Java Code Examples for org.apache.calcite.rel.core.Union#getInputs()

The following examples show how to use org.apache.calcite.rel.core.Union#getInputs() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: RelMdPercentageOriginalRows.java    From calcite with Apache License 2.0 5 votes vote down vote up
public Double getPercentageOriginalRows(Union rel, RelMetadataQuery mq) {
  double numerator = 0.0;
  double denominator = 0.0;

  // Ignore rel.isDistinct() because it's the same as an aggregate.

  // REVIEW jvs 28-Mar-2006: The original Broadbase formula was broken.
  // It was multiplying percentage into the numerator term rather than
  // than dividing it out of the denominator term, which would be OK if
  // there weren't summation going on.  Probably the cause of the error
  // was the desire to avoid division by zero, which I don't know how to
  // handle so I punt, meaning we return a totally wrong answer in the
  // case where a huge table has been completely filtered away.

  for (RelNode input : rel.getInputs()) {
    Double rowCount = mq.getRowCount(input);
    if (rowCount == null) {
      continue;
    }
    Double percentage = mq.getPercentageOriginalRows(input);
    if (percentage == null) {
      continue;
    }
    if (percentage != 0.0) {
      denominator += rowCount / percentage;
      numerator += rowCount;
    }
  }

  return quotientForPercentage(numerator, denominator);
}
 
Example 2
Source File: RelMdDistinctRowCount.java    From Bats with Apache License 2.0 5 votes vote down vote up
public Double getDistinctRowCount(Union rel, RelMetadataQuery mq,
    ImmutableBitSet groupKey, RexNode predicate) {
  double rowCount = 0.0;
  int[] adjustments = new int[rel.getRowType().getFieldCount()];
  RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
  for (RelNode input : rel.getInputs()) {
    // convert the predicate to reference the types of the union child
    RexNode modifiedPred;
    if (predicate == null) {
      modifiedPred = null;
    } else {
      modifiedPred =
          predicate.accept(
              new RelOptUtil.RexInputConverter(
                  rexBuilder,
                  null,
                  input.getRowType().getFieldList(),
                  adjustments));
    }
    Double partialRowCount =
        mq.getDistinctRowCount(input, groupKey, modifiedPred);
    if (partialRowCount == null) {
      return null;
    }
    rowCount += partialRowCount;
  }
  return rowCount;
}
 
Example 3
Source File: RelMdSize.java    From Bats with Apache License 2.0 5 votes vote down vote up
public List<Double> averageColumnSizes(Union rel, RelMetadataQuery mq) {
  final int fieldCount = rel.getRowType().getFieldCount();
  List<List<Double>> inputColumnSizeList = new ArrayList<>();
  for (RelNode input : rel.getInputs()) {
    final List<Double> inputSizes = mq.getAverageColumnSizes(input);
    if (inputSizes != null) {
      inputColumnSizeList.add(inputSizes);
    }
  }
  switch (inputColumnSizeList.size()) {
  case 0:
    return null; // all were null
  case 1:
    return inputColumnSizeList.get(0); // all but one were null
  }
  final ImmutableNullableList.Builder<Double> sizes =
      ImmutableNullableList.builder();
  int nn = 0;
  for (int i = 0; i < fieldCount; i++) {
    double d = 0d;
    int n = 0;
    for (List<Double> inputColumnSizes : inputColumnSizeList) {
      Double d2 = inputColumnSizes.get(i);
      if (d2 != null) {
        d += d2;
        ++n;
        ++nn;
      }
    }
    sizes.add(n > 0 ? d / n : null);
  }
  if (nn == 0) {
    return null; // all columns are null
  }
  return sizes.build();
}
 
Example 4
Source File: RelMdUtil.java    From Bats with Apache License 2.0 5 votes vote down vote up
/** Returns an estimate of the number of rows returned by a {@link Union}
 * (before duplicates are eliminated). */
public static double getUnionAllRowCount(RelMetadataQuery mq, Union rel) {
  double rowCount = 0;
  for (RelNode input : rel.getInputs()) {
    rowCount += mq.getRowCount(input);
  }
  return rowCount;
}
 
Example 5
Source File: RelMdSelectivity.java    From Bats with Apache License 2.0 5 votes vote down vote up
public Double getSelectivity(Union rel, RelMetadataQuery mq,
    RexNode predicate) {
  if ((rel.getInputs().size() == 0) || (predicate == null)) {
    return 1.0;
  }

  double sumRows = 0.0;
  double sumSelectedRows = 0.0;
  int[] adjustments = new int[rel.getRowType().getFieldCount()];
  RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
  for (RelNode input : rel.getInputs()) {
    Double nRows = mq.getRowCount(input);
    if (nRows == null) {
      return null;
    }

    // convert the predicate to reference the types of the union child
    RexNode modifiedPred =
        predicate.accept(
            new RelOptUtil.RexInputConverter(
                rexBuilder,
                null,
                input.getRowType().getFieldList(),
                adjustments));
    double sel = mq.getSelectivity(input, modifiedPred);

    sumRows += nRows;
    sumSelectedRows += nRows * sel;
  }

  if (sumRows < 1.0) {
    sumRows = 1.0;
  }
  return sumSelectedRows / sumRows;
}
 
Example 6
Source File: RelMdPopulationSize.java    From Bats with Apache License 2.0 5 votes vote down vote up
public Double getPopulationSize(Union rel, RelMetadataQuery mq,
    ImmutableBitSet groupKey) {
  double population = 0.0;
  for (RelNode input : rel.getInputs()) {
    Double subPop = mq.getPopulationSize(input, groupKey);
    if (subPop == null) {
      return null;
    }
    population += subPop;
  }
  return population;
}
 
Example 7
Source File: RelMdRowCount.java    From calcite with Apache License 2.0 5 votes vote down vote up
public Double getRowCount(Union rel, RelMetadataQuery mq) {
  double rowCount = 0.0;
  for (RelNode input : rel.getInputs()) {
    Double partialRowCount = mq.getRowCount(input);
    if (partialRowCount == null) {
      return null;
    }
    rowCount += partialRowCount;
  }
  if (!rel.all) {
    rowCount *= 0.5;
  }
  return rowCount;
}
 
Example 8
Source File: RelMdMinRowCount.java    From Bats with Apache License 2.0 5 votes vote down vote up
public Double getMinRowCount(Union rel, RelMetadataQuery mq) {
  double rowCount = 0.0;
  for (RelNode input : rel.getInputs()) {
    Double partialRowCount = mq.getMinRowCount(input);
    if (partialRowCount != null) {
      rowCount += partialRowCount;
    }
  }
  return rowCount;
}
 
Example 9
Source File: OLAPUnionRule.java    From kylin with Apache License 2.0 5 votes vote down vote up
@Override
public RelNode convert(RelNode rel) {
    final Union union = (Union) rel;
    final RelTraitSet traitSet = union.getTraitSet().replace(OLAPRel.CONVENTION);
    final List<RelNode> inputs = union.getInputs();
    return new OLAPUnionRel(rel.getCluster(), traitSet, convertList(inputs, OLAPRel.CONVENTION), union.all);
}
 
Example 10
Source File: RelMdPercentageOriginalRows.java    From Bats with Apache License 2.0 5 votes vote down vote up
public Double getPercentageOriginalRows(Union rel, RelMetadataQuery mq) {
  double numerator = 0.0;
  double denominator = 0.0;

  // Ignore rel.isDistinct() because it's the same as an aggregate.

  // REVIEW jvs 28-Mar-2006: The original Broadbase formula was broken.
  // It was multiplying percentage into the numerator term rather than
  // than dividing it out of the denominator term, which would be OK if
  // there weren't summation going on.  Probably the cause of the error
  // was the desire to avoid division by zero, which I don't know how to
  // handle so I punt, meaning we return a totally wrong answer in the
  // case where a huge table has been completely filtered away.

  for (RelNode input : rel.getInputs()) {
    Double rowCount = mq.getRowCount(input);
    if (rowCount == null) {
      continue;
    }
    Double percentage = mq.getPercentageOriginalRows(input);
    if (percentage == null) {
      continue;
    }
    if (percentage != 0.0) {
      denominator += rowCount / percentage;
      numerator += rowCount;
    }
  }

  return quotientForPercentage(numerator, denominator);
}
 
Example 11
Source File: RelMdPopulationSize.java    From calcite with Apache License 2.0 5 votes vote down vote up
public Double getPopulationSize(Union rel, RelMetadataQuery mq,
    ImmutableBitSet groupKey) {
  double population = 0.0;
  for (RelNode input : rel.getInputs()) {
    Double subPop = mq.getPopulationSize(input, groupKey);
    if (subPop == null) {
      return null;
    }
    population += subPop;
  }
  return population;
}
 
Example 12
Source File: OLAPUnionRule.java    From kylin-on-parquet-v2 with Apache License 2.0 5 votes vote down vote up
@Override
public RelNode convert(RelNode rel) {
    final Union union = (Union) rel;
    final RelTraitSet traitSet = union.getTraitSet().replace(OLAPRel.CONVENTION);
    final List<RelNode> inputs = union.getInputs();
    return new OLAPUnionRel(rel.getCluster(), traitSet, convertList(inputs, OLAPRel.CONVENTION), union.all);
}
 
Example 13
Source File: UnionPullUpConstantsRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Union union = call.rel(0);

  final RexBuilder rexBuilder = union.getCluster().getRexBuilder();
  final RelMetadataQuery mq = call.getMetadataQuery();
  final RelOptPredicateList predicates = mq.getPulledUpPredicates(union);
  if (predicates == null) {
    return;
  }

  final Map<Integer, RexNode> constants = new HashMap<>();
  for (Map.Entry<RexNode, RexNode> e : predicates.constantMap.entrySet()) {
    if (e.getKey() instanceof RexInputRef) {
      constants.put(((RexInputRef) e.getKey()).getIndex(), e.getValue());
    }
  }

  // None of the expressions are constant. Nothing to do.
  if (constants.isEmpty()) {
    return;
  }

  // Create expressions for Project operators before and after the Union
  List<RelDataTypeField> fields = union.getRowType().getFieldList();
  List<RexNode> topChildExprs = new ArrayList<>();
  List<String> topChildExprsFields = new ArrayList<>();
  List<RexNode> refs = new ArrayList<>();
  ImmutableBitSet.Builder refsIndexBuilder = ImmutableBitSet.builder();
  for (RelDataTypeField field : fields) {
    final RexNode constant = constants.get(field.getIndex());
    if (constant != null) {
      topChildExprs.add(constant);
      topChildExprsFields.add(field.getName());
    } else {
      final RexNode expr = rexBuilder.makeInputRef(union, field.getIndex());
      topChildExprs.add(expr);
      topChildExprsFields.add(field.getName());
      refs.add(expr);
      refsIndexBuilder.set(field.getIndex());
    }
  }
  ImmutableBitSet refsIndex = refsIndexBuilder.build();

  // Update top Project positions
  final Mappings.TargetMapping mapping =
      RelOptUtil.permutation(refs, union.getInput(0).getRowType()).inverse();
  topChildExprs = RexUtil.apply(mapping, topChildExprs);

  // Create new Project-Union-Project sequences
  final RelBuilder relBuilder = call.builder();
  for (RelNode input : union.getInputs()) {
    List<Pair<RexNode, String>> newChildExprs = new ArrayList<>();
    for (int j : refsIndex) {
      newChildExprs.add(
          Pair.of(rexBuilder.makeInputRef(input, j),
              input.getRowType().getFieldList().get(j).getName()));
    }
    if (newChildExprs.isEmpty()) {
      // At least a single item in project is required.
      newChildExprs.add(
          Pair.of(topChildExprs.get(0), topChildExprsFields.get(0)));
    }
    // Add the input with project on top
    relBuilder.push(input);
    relBuilder.project(Pair.left(newChildExprs), Pair.right(newChildExprs));
  }
  relBuilder.union(union.all, union.getInputs().size());
  // Create top Project fixing nullability of fields
  relBuilder.project(topChildExprs, topChildExprsFields);
  relBuilder.convert(union.getRowType(), false);

  call.transformTo(relBuilder.build());
}
 
Example 14
Source File: RelMdExpressionLineage.java    From calcite with Apache License 2.0 4 votes vote down vote up
/**
 * Expression lineage from {@link Union}.
 *
 * <p>For Union operator, we might be able to extract multiple origins for the
 * references in the given expression.
 */
public Set<RexNode> getExpressionLineage(Union rel, RelMetadataQuery mq,
    RexNode outputExpression) {
  final RexBuilder rexBuilder = rel.getCluster().getRexBuilder();

  // Extract input fields referenced by expression
  final ImmutableBitSet inputFieldsUsed = extractInputRefs(outputExpression);

  // Infer column origin expressions for given references
  final Multimap<List<String>, RelTableRef> qualifiedNamesToRefs = HashMultimap.create();
  final Map<RexInputRef, Set<RexNode>> mapping = new LinkedHashMap<>();
  for (RelNode input : rel.getInputs()) {
    // Gather table references
    final Map<RelTableRef, RelTableRef> currentTablesMapping = new HashMap<>();
    final Set<RelTableRef> tableRefs = mq.getTableReferences(input);
    if (tableRefs == null) {
      // Bail out
      return null;
    }
    for (RelTableRef tableRef : tableRefs) {
      int shift = 0;
      Collection<RelTableRef> lRefs = qualifiedNamesToRefs.get(
          tableRef.getQualifiedName());
      if (lRefs != null) {
        shift = lRefs.size();
      }
      currentTablesMapping.put(tableRef,
          RelTableRef.of(tableRef.getTable(), shift + tableRef.getEntityNumber()));
    }
    // Map references
    for (int idx : inputFieldsUsed) {
      final RexInputRef inputRef = RexInputRef.of(idx, input.getRowType().getFieldList());
      final Set<RexNode> originalExprs = mq.getExpressionLineage(input, inputRef);
      if (originalExprs == null) {
        // Bail out
        return null;
      }
      // References might need to be updated
      final RexInputRef ref = RexInputRef.of(idx, rel.getRowType().getFieldList());
      final Set<RexNode> updatedExprs =
          originalExprs.stream()
              .map(e ->
                  RexUtil.swapTableReferences(rexBuilder, e,
                      currentTablesMapping))
              .collect(Collectors.toSet());
      final Set<RexNode> set = mapping.get(ref);
      if (set != null) {
        set.addAll(updatedExprs);
      } else {
        mapping.put(ref, updatedExprs);
      }
    }
    // Add to existing qualified names
    for (RelTableRef newRef : currentTablesMapping.values()) {
      qualifiedNamesToRefs.put(newRef.getQualifiedName(), newRef);
    }
  }

  // Return result
  return createAllPossibleExpressions(rexBuilder, outputExpression, mapping);
}
 
Example 15
Source File: JoinUnionTransposeRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Join join = call.rel(0);
  final Union unionRel;
  RelNode otherInput;
  boolean unionOnLeft;
  if (call.rel(1) instanceof Union) {
    unionRel = call.rel(1);
    otherInput = call.rel(2);
    unionOnLeft = true;
  } else {
    otherInput = call.rel(1);
    unionRel = call.rel(2);
    unionOnLeft = false;
  }
  if (!unionRel.all) {
    return;
  }
  if (!join.getVariablesSet().isEmpty()) {
    return;
  }
  // The UNION ALL cannot be on the null generating side
  // of an outer join (otherwise we might generate incorrect
  // rows for the other side for join keys which lack a match
  // in one or both branches of the union)
  if (unionOnLeft) {
    if (join.getJoinType().generatesNullsOnLeft()) {
      return;
    }
  } else {
    if (join.getJoinType().generatesNullsOnRight()
        || !join.getJoinType().projectsRight()) {
      return;
    }
  }
  List<RelNode> newUnionInputs = new ArrayList<>();
  for (RelNode input : unionRel.getInputs()) {
    RelNode joinLeft;
    RelNode joinRight;
    if (unionOnLeft) {
      joinLeft = input;
      joinRight = otherInput;
    } else {
      joinLeft = otherInput;
      joinRight = input;
    }
    newUnionInputs.add(
        join.copy(
            join.getTraitSet(),
            join.getCondition(),
            joinLeft,
            joinRight,
            join.getJoinType(),
            join.isSemiJoinDone()));
  }
  final SetOp newUnionRel =
      unionRel.copy(unionRel.getTraitSet(), newUnionInputs, true);
  call.transformTo(newUnionRel);
}
 
Example 16
Source File: JoinUnionTransposeRule.java    From Bats with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Join join = call.rel(0);
  final Union unionRel;
  RelNode otherInput;
  boolean unionOnLeft;
  if (call.rel(1) instanceof Union) {
    unionRel = call.rel(1);
    otherInput = call.rel(2);
    unionOnLeft = true;
  } else {
    otherInput = call.rel(1);
    unionRel = call.rel(2);
    unionOnLeft = false;
  }
  if (!unionRel.all) {
    return;
  }
  if (!join.getVariablesSet().isEmpty()) {
    return;
  }
  // The UNION ALL cannot be on the null generating side
  // of an outer join (otherwise we might generate incorrect
  // rows for the other side for join keys which lack a match
  // in one or both branches of the union)
  if (unionOnLeft) {
    if (join.getJoinType().generatesNullsOnLeft()) {
      return;
    }
  } else {
    if (join.getJoinType().generatesNullsOnRight()) {
      return;
    }
  }
  List<RelNode> newUnionInputs = new ArrayList<>();
  for (RelNode input : unionRel.getInputs()) {
    RelNode joinLeft;
    RelNode joinRight;
    if (unionOnLeft) {
      joinLeft = input;
      joinRight = otherInput;
    } else {
      joinLeft = otherInput;
      joinRight = input;
    }
    newUnionInputs.add(
        join.copy(
            join.getTraitSet(),
            join.getCondition(),
            joinLeft,
            joinRight,
            join.getJoinType(),
            join.isSemiJoinDone()));
  }
  final SetOp newUnionRel =
      unionRel.copy(unionRel.getTraitSet(), newUnionInputs, true);
  call.transformTo(newUnionRel);
}
 
Example 17
Source File: AggregateUnionTransposeRule.java    From Bats with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  Aggregate aggRel = call.rel(0);
  Union union = call.rel(1);

  if (!union.all) {
    // This transformation is only valid for UNION ALL.
    // Consider t1(i) with rows (5), (5) and t2(i) with
    // rows (5), (10), and the query
    // select sum(i) from (select i from t1) union (select i from t2).
    // The correct answer is 15.  If we apply the transformation,
    // we get
    // select sum(i) from
    // (select sum(i) as i from t1) union (select sum(i) as i from t2)
    // which yields 25 (incorrect).
    return;
  }

  int groupCount = aggRel.getGroupSet().cardinality();

  List<AggregateCall> transformedAggCalls =
      transformAggCalls(
          aggRel.copy(aggRel.getTraitSet(), aggRel.getInput(), false,
              aggRel.getGroupSet(), null, aggRel.getAggCallList()),
          groupCount, aggRel.getAggCallList());
  if (transformedAggCalls == null) {
    // we've detected the presence of something like AVG,
    // which we can't handle
    return;
  }

  // create corresponding aggregates on top of each union child
  final RelBuilder relBuilder = call.builder();
  int transformCount = 0;
  final RelMetadataQuery mq = call.getMetadataQuery();
  for (RelNode input : union.getInputs()) {
    boolean alreadyUnique =
        RelMdUtil.areColumnsDefinitelyUnique(mq, input,
            aggRel.getGroupSet());

    relBuilder.push(input);
    if (!alreadyUnique) {
      ++transformCount;
      relBuilder.aggregate(relBuilder.groupKey(aggRel.getGroupSet()),
          aggRel.getAggCallList());
    }
  }

  if (transformCount == 0) {
    // none of the children could benefit from the push-down,
    // so bail out (preventing the infinite loop to which most
    // planners would succumb)
    return;
  }

  // create a new union whose children are the aggregates created above
  relBuilder.union(true, union.getInputs().size());
  relBuilder.aggregate(
      relBuilder.groupKey(aggRel.getGroupSet(), aggRel.getGroupSets()),
      transformedAggCalls);
  call.transformTo(relBuilder.build());
}
 
Example 18
Source File: UnionPullUpConstantsRule.java    From Bats with Apache License 2.0 4 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Union union = call.rel(0);

  final int count = union.getRowType().getFieldCount();
  if (count == 1) {
    // No room for optimization since we cannot create an empty Project
    // operator. If we created a Project with one column, this rule would
    // cycle.
    return;
  }

  final RexBuilder rexBuilder = union.getCluster().getRexBuilder();
  final RelMetadataQuery mq = call.getMetadataQuery();
  final RelOptPredicateList predicates = mq.getPulledUpPredicates(union);
  if (predicates == null) {
    return;
  }

  final Map<Integer, RexNode> constants = new HashMap<>();
  for (Map.Entry<RexNode, RexNode> e : predicates.constantMap.entrySet()) {
    if (e.getKey() instanceof RexInputRef) {
      constants.put(((RexInputRef) e.getKey()).getIndex(), e.getValue());
    }
  }

  // None of the expressions are constant. Nothing to do.
  if (constants.isEmpty()) {
    return;
  }

  // Create expressions for Project operators before and after the Union
  List<RelDataTypeField> fields = union.getRowType().getFieldList();
  List<RexNode> topChildExprs = new ArrayList<>();
  List<String> topChildExprsFields = new ArrayList<>();
  List<RexNode> refs = new ArrayList<>();
  ImmutableBitSet.Builder refsIndexBuilder = ImmutableBitSet.builder();
  for (RelDataTypeField field : fields) {
    final RexNode constant = constants.get(field.getIndex());
    if (constant != null) {
      topChildExprs.add(constant);
      topChildExprsFields.add(field.getName());
    } else {
      final RexNode expr = rexBuilder.makeInputRef(union, field.getIndex());
      topChildExprs.add(expr);
      topChildExprsFields.add(field.getName());
      refs.add(expr);
      refsIndexBuilder.set(field.getIndex());
    }
  }
  ImmutableBitSet refsIndex = refsIndexBuilder.build();

  // Update top Project positions
  final Mappings.TargetMapping mapping =
      RelOptUtil.permutation(refs, union.getInput(0).getRowType()).inverse();
  topChildExprs = ImmutableList.copyOf(RexUtil.apply(mapping, topChildExprs));

  // Create new Project-Union-Project sequences
  final RelBuilder relBuilder = call.builder();
  for (RelNode input : union.getInputs()) {
    List<Pair<RexNode, String>> newChildExprs = new ArrayList<>();
    for (int j : refsIndex) {
      newChildExprs.add(
          Pair.of(rexBuilder.makeInputRef(input, j),
              input.getRowType().getFieldList().get(j).getName()));
    }
    if (newChildExprs.isEmpty()) {
      // At least a single item in project is required.
      newChildExprs.add(
          Pair.of(topChildExprs.get(0), topChildExprsFields.get(0)));
    }
    // Add the input with project on top
    relBuilder.push(input);
    relBuilder.project(Pair.left(newChildExprs), Pair.right(newChildExprs));
  }
  relBuilder.union(union.all, union.getInputs().size());
  // Create top Project fixing nullability of fields
  relBuilder.project(topChildExprs, topChildExprsFields);
  relBuilder.convert(union.getRowType(), false);

  call.transformTo(relBuilder.build());
}
 
Example 19
Source File: AggregateUnionTransposeRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  Aggregate aggRel = call.rel(0);
  Union union = call.rel(1);

  if (!union.all) {
    // This transformation is only valid for UNION ALL.
    // Consider t1(i) with rows (5), (5) and t2(i) with
    // rows (5), (10), and the query
    // select sum(i) from (select i from t1) union (select i from t2).
    // The correct answer is 15.  If we apply the transformation,
    // we get
    // select sum(i) from
    // (select sum(i) as i from t1) union (select sum(i) as i from t2)
    // which yields 25 (incorrect).
    return;
  }

  int groupCount = aggRel.getGroupSet().cardinality();

  List<AggregateCall> transformedAggCalls =
      transformAggCalls(
          aggRel.copy(aggRel.getTraitSet(), aggRel.getInput(),
              aggRel.getGroupSet(), null, aggRel.getAggCallList()),
          groupCount, aggRel.getAggCallList());
  if (transformedAggCalls == null) {
    // we've detected the presence of something like AVG,
    // which we can't handle
    return;
  }

  // create corresponding aggregates on top of each union child
  final RelBuilder relBuilder = call.builder();
  int transformCount = 0;
  final RelMetadataQuery mq = call.getMetadataQuery();
  for (RelNode input : union.getInputs()) {
    boolean alreadyUnique =
        RelMdUtil.areColumnsDefinitelyUnique(mq, input,
            aggRel.getGroupSet());

    relBuilder.push(input);
    if (!alreadyUnique) {
      ++transformCount;
      relBuilder.aggregate(relBuilder.groupKey(aggRel.getGroupSet()),
          aggRel.getAggCallList());
    }
  }

  if (transformCount == 0) {
    // none of the children could benefit from the push-down,
    // so bail out (preventing the infinite loop to which most
    // planners would succumb)
    return;
  }

  // create a new union whose children are the aggregates created above
  relBuilder.union(true, union.getInputs().size());
  relBuilder.aggregate(
      relBuilder.groupKey(aggRel.getGroupSet(),
          (Iterable<ImmutableBitSet>) aggRel.getGroupSets()),
      transformedAggCalls);
  call.transformTo(relBuilder.build());
}
 
Example 20
Source File: RelMdExpressionLineage.java    From Bats with Apache License 2.0 4 votes vote down vote up
/**
 * Expression lineage from {@link Union}.
 *
 * <p>For Union operator, we might be able to extract multiple origins for the
 * references in the given expression.
 */
public Set<RexNode> getExpressionLineage(Union rel, RelMetadataQuery mq,
    RexNode outputExpression) {
  final RexBuilder rexBuilder = rel.getCluster().getRexBuilder();

  // Extract input fields referenced by expression
  final ImmutableBitSet inputFieldsUsed = extractInputRefs(outputExpression);

  // Infer column origin expressions for given references
  final Multimap<List<String>, RelTableRef> qualifiedNamesToRefs = HashMultimap.create();
  final Map<RexInputRef, Set<RexNode>> mapping = new LinkedHashMap<>();
  for (RelNode input : rel.getInputs()) {
    // Gather table references
    final Map<RelTableRef, RelTableRef> currentTablesMapping = new HashMap<>();
    final Set<RelTableRef> tableRefs = mq.getTableReferences(input);
    if (tableRefs == null) {
      // Bail out
      return null;
    }
    for (RelTableRef tableRef : tableRefs) {
      int shift = 0;
      Collection<RelTableRef> lRefs = qualifiedNamesToRefs.get(
          tableRef.getQualifiedName());
      if (lRefs != null) {
        shift = lRefs.size();
      }
      currentTablesMapping.put(tableRef,
          RelTableRef.of(tableRef.getTable(), shift + tableRef.getEntityNumber()));
    }
    // Map references
    for (int idx : inputFieldsUsed) {
      final RexInputRef inputRef = RexInputRef.of(idx, input.getRowType().getFieldList());
      final Set<RexNode> originalExprs = mq.getExpressionLineage(input, inputRef);
      if (originalExprs == null) {
        // Bail out
        return null;
      }
      // References might need to be updated
      final RexInputRef ref = RexInputRef.of(idx, rel.getRowType().getFieldList());
      final Set<RexNode> updatedExprs =
          originalExprs.stream()
              .map(e ->
                  RexUtil.swapTableReferences(rexBuilder, e,
                      currentTablesMapping))
              .collect(Collectors.toSet());
      final Set<RexNode> set = mapping.get(ref);
      if (set != null) {
        set.addAll(updatedExprs);
      } else {
        mapping.put(ref, updatedExprs);
      }
    }
    // Add to existing qualified names
    for (RelTableRef newRef : currentTablesMapping.values()) {
      qualifiedNamesToRefs.put(newRef.getQualifiedName(), newRef);
    }
  }

  // Return result
  return createAllPossibleExpressions(rexBuilder, outputExpression, mapping);
}