Java Code Examples for org.apache.calcite.plan.RelOptRuleCall#builder()

The following examples show how to use org.apache.calcite.plan.RelOptRuleCall#builder() . 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: FilterMergeCrule.java    From dremio-oss with Apache License 2.0 6 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Filter topFilter = call.rel(0);
  final Filter bottomFilter = call.rel(1);

  // use RexPrograms to merge the two FilterRels into a single program
  // so we can convert the two LogicalFilter conditions to directly
  // reference the bottom LogicalFilter's child
  RexBuilder rexBuilder = topFilter.getCluster().getRexBuilder();
  RexProgram bottomProgram = createProgram(bottomFilter);
  RexProgram topProgram = createProgram(topFilter);

  RexProgram mergedProgram = RexProgramBuilder.mergePrograms(topProgram, bottomProgram, rexBuilder);

  RexNode newCondition = mergedProgram.expandLocalRef(mergedProgram.getCondition());

  final RelBuilder relBuilder = call.builder();
  relBuilder.push(bottomFilter.getInput()).filter(newCondition);

  call.transformTo(relBuilder.build());
}
 
Example 2
Source File: FlinkSemiAntiJoinProjectTransposeRule.java    From flink with Apache License 2.0 6 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
	LogicalJoin join = call.rel(0);
	LogicalProject project = call.rel(1);

	// convert the semi/anti join condition to reflect the LHS with the project
	// pulled up
	RexNode newCondition = adjustCondition(project, join);
	Join newJoin = LogicalJoin.create(
			project.getInput(), join.getRight(), newCondition, join.getVariablesSet(), join.getJoinType());

	// Create the new projection. Note that the projection expressions
	// are the same as the original because they only reference the LHS
	// of the semi/anti join and the semi/anti join only projects out the LHS
	final RelBuilder relBuilder = call.builder();
	relBuilder.push(newJoin);
	relBuilder.project(project.getProjects(), project.getRowType().getFieldNames());

	call.transformTo(relBuilder.build());
}
 
Example 3
Source File: CalcSplitRule.java    From Bats with Apache License 2.0 5 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Calc calc = call.rel(0);
  final Pair<ImmutableList<RexNode>, ImmutableList<RexNode>> projectFilter =
      calc.getProgram().split();
  final RelBuilder relBuilder = call.builder();
  relBuilder.push(calc.getInput());
  relBuilder.filter(projectFilter.right);
  relBuilder.project(projectFilter.left, calc.getRowType().getFieldNames());
  call.transformTo(relBuilder.build());
}
 
Example 4
Source File: JoinCommuteRule.java    From Bats with Apache License 2.0 5 votes vote down vote up
@Override
public void onMatch(final RelOptRuleCall call) {
    Join join = call.rel(0);

    if (!join.getSystemFieldList().isEmpty()) {
        // FIXME Enable this rule for joins with system fields
        return;
    }

    final RelNode swapped = swap(join, this.swapOuter, call.builder());
    if (swapped == null) {
        return;
    }

    // The result is either a Project or, if the project is trivial, a
    // raw Join.
    final Join newJoin = swapped instanceof Join ? (Join) swapped : (Join) swapped.getInput(0);

    call.transformTo(swapped);

    // We have converted join='a join b' into swapped='select
    // a0,a1,a2,b0,b1 from b join a'. Now register that project='select
    // b0,b1,a0,a1,a2 from (select a0,a1,a2,b0,b1 from b join a)' is the
    // same as 'b join a'. If we didn't do this, the swap join rule
    // would fire on the new join, ad infinitum.
    final RelBuilder relBuilder = call.builder();
    final List<RexNode> exps = RelOptUtil.createSwappedJoinExprs(newJoin, join, false);
    relBuilder.push(swapped).project(exps, newJoin.getRowType().getFieldNames());

    call.getPlanner().ensureRegistered(relBuilder.build(), newJoin);
}
 
Example 5
Source File: RelDecorrelator.java    From Bats with Apache License 2.0 5 votes vote down vote up
@Override
public void onMatch(RelOptRuleCall call) {
    LogicalAggregate singleAggregate = call.rel(0);
    LogicalProject project = call.rel(1);
    LogicalAggregate aggregate = call.rel(2);

    // check singleAggRel is single_value agg
    if ((!singleAggregate.getGroupSet().isEmpty()) || (singleAggregate.getAggCallList().size() != 1)
            || !(singleAggregate.getAggCallList().get(0)
                    .getAggregation() instanceof SqlSingleValueAggFunction)) {
        return;
    }

    // check projRel only projects one expression
    // check this project only projects one expression, i.e. scalar
    // sub-queries.
    List<RexNode> projExprs = project.getProjects();
    if (projExprs.size() != 1) {
        return;
    }

    // check the input to project is an aggregate on the entire input
    if (!aggregate.getGroupSet().isEmpty()) {
        return;
    }

    // singleAggRel produces a nullable type, so create the new
    // projection that casts proj expr to a nullable type.
    final RelBuilder relBuilder = call.builder();
    final RelDataType type = relBuilder.getTypeFactory().createTypeWithNullability(projExprs.get(0).getType(),
            true);
    final RexNode cast = relBuilder.getRexBuilder().makeCast(type, projExprs.get(0));
    relBuilder.push(aggregate).project(cast);
    call.transformTo(relBuilder.build());
}
 
Example 6
Source File: FilterMergeRule.java    From Bats with Apache License 2.0 5 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Filter topFilter = call.rel(0);
  final Filter bottomFilter = call.rel(1);

  // use RexPrograms to merge the two FilterRels into a single program
  // so we can convert the two LogicalFilter conditions to directly
  // reference the bottom LogicalFilter's child
  RexBuilder rexBuilder = topFilter.getCluster().getRexBuilder();
  RexProgram bottomProgram = createProgram(bottomFilter);
  RexProgram topProgram = createProgram(topFilter);

  RexProgram mergedProgram =
      RexProgramBuilder.mergePrograms(
          topProgram,
          bottomProgram,
          rexBuilder);

  RexNode newCondition =
      mergedProgram.expandLocalRef(
          mergedProgram.getCondition());

  final RelBuilder relBuilder = call.builder();
  relBuilder.push(bottomFilter.getInput())
      .filter(newCondition);

  call.transformTo(relBuilder.build());
}
 
Example 7
Source File: FilterSetOpTransposeRule.java    From calcite with Apache License 2.0 5 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  Filter filterRel = call.rel(0);
  SetOp setOp = call.rel(1);

  RexNode condition = filterRel.getCondition();

  // create filters on top of each setop child, modifying the filter
  // condition to reference each setop child
  RexBuilder rexBuilder = filterRel.getCluster().getRexBuilder();
  final RelBuilder relBuilder = call.builder();
  List<RelDataTypeField> origFields =
      setOp.getRowType().getFieldList();
  int[] adjustments = new int[origFields.size()];
  final List<RelNode> newSetOpInputs = new ArrayList<>();
  for (RelNode input : setOp.getInputs()) {
    RexNode newCondition =
        condition.accept(
            new RelOptUtil.RexInputConverter(
                rexBuilder,
                origFields,
                input.getRowType().getFieldList(),
                adjustments));
    newSetOpInputs.add(relBuilder.push(input).filter(newCondition).build());
  }

  // create a new setop whose children are the filters created above
  SetOp newSetOp =
      setOp.copy(setOp.getTraitSet(), newSetOpInputs);

  call.transformTo(newSetOp);
}
 
Example 8
Source File: RelDecorrelator.java    From flink with Apache License 2.0 5 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  LogicalAggregate singleAggregate = call.rel(0);
  LogicalProject project = call.rel(1);
  LogicalAggregate aggregate = call.rel(2);

  // check singleAggRel is single_value agg
  if ((!singleAggregate.getGroupSet().isEmpty())
      || (singleAggregate.getAggCallList().size() != 1)
      || !(singleAggregate.getAggCallList().get(0).getAggregation()
      instanceof SqlSingleValueAggFunction)) {
    return;
  }

  // check projRel only projects one expression
  // check this project only projects one expression, i.e. scalar
  // sub-queries.
  List<RexNode> projExprs = project.getProjects();
  if (projExprs.size() != 1) {
    return;
  }

  // check the input to project is an aggregate on the entire input
  if (!aggregate.getGroupSet().isEmpty()) {
    return;
  }

  // singleAggRel produces a nullable type, so create the new
  // projection that casts proj expr to a nullable type.
  final RelBuilder relBuilder = call.builder();
  final RelDataType type =
      relBuilder.getTypeFactory()
          .createTypeWithNullability(projExprs.get(0).getType(), true);
  final RexNode cast =
      relBuilder.getRexBuilder().makeCast(type, projExprs.get(0));
  relBuilder.push(aggregate)
      .project(cast);
  call.transformTo(relBuilder.build());
}
 
Example 9
Source File: SemiJoinProjectTransposeRule.java    From Bats with Apache License 2.0 5 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  SemiJoin semiJoin = call.rel(0);
  LogicalProject project = call.rel(1);

  // Convert the LHS semi-join keys to reference the child projection
  // expression; all projection expressions must be RexInputRefs,
  // otherwise, we wouldn't have created this semi-join.
  final List<Integer> newLeftKeys = new ArrayList<>();
  final List<Integer> leftKeys = semiJoin.getLeftKeys();
  final List<RexNode> projExprs = project.getProjects();
  for (int leftKey : leftKeys) {
    RexInputRef inputRef = (RexInputRef) projExprs.get(leftKey);
    newLeftKeys.add(inputRef.getIndex());
  }

  // convert the semijoin condition to reflect the LHS with the project
  // pulled up
  RexNode newCondition = adjustCondition(project, semiJoin);

  SemiJoin newSemiJoin =
      SemiJoin.create(project.getInput(), semiJoin.getRight(), newCondition,
          ImmutableIntList.copyOf(newLeftKeys), semiJoin.getRightKeys());

  // Create the new projection.  Note that the projection expressions
  // are the same as the original because they only reference the LHS
  // of the semijoin and the semijoin only projects out the LHS
  final RelBuilder relBuilder = call.builder();
  relBuilder.push(newSemiJoin);
  relBuilder.project(projExprs, project.getRowType().getFieldNames());

  call.transformTo(relBuilder.build());
}
 
Example 10
Source File: UnionToDistinctRule.java    From calcite with Apache License 2.0 5 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Union union = call.rel(0);
  final RelBuilder relBuilder = call.builder();
  relBuilder.pushAll(union.getInputs());
  relBuilder.union(true, union.getInputs().size());
  relBuilder.distinct();
  call.transformTo(relBuilder.build());
}
 
Example 11
Source File: AggregateValuesRule.java    From calcite with Apache License 2.0 5 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Aggregate aggregate = call.rel(0);
  final Values values = call.rel(1);
  Util.discard(values);
  final RelBuilder relBuilder = call.builder();
  final RexBuilder rexBuilder = relBuilder.getRexBuilder();

  final List<RexLiteral> literals = new ArrayList<>();
  for (final AggregateCall aggregateCall : aggregate.getAggCallList()) {
    switch (aggregateCall.getAggregation().getKind()) {
    case COUNT:
    case SUM0:
      literals.add(
          (RexLiteral) rexBuilder.makeLiteral(
              BigDecimal.ZERO, aggregateCall.getType(), false));
      break;

    case MIN:
    case MAX:
    case SUM:
      literals.add(rexBuilder.makeNullLiteral(aggregateCall.getType()));
      break;

    default:
      // Unknown what this aggregate call should do on empty Values. Bail out to be safe.
      return;
    }
  }

  call.transformTo(
      relBuilder.values(ImmutableList.of(literals), aggregate.getRowType())
          .build());

  // New plan is absolutely better than old plan.
  call.getPlanner().prune(aggregate);
}
 
Example 12
Source File: RelDecorrelator.java    From flink with Apache License 2.0 5 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  LogicalAggregate singleAggregate = call.rel(0);
  LogicalProject project = call.rel(1);
  LogicalAggregate aggregate = call.rel(2);

  // check singleAggRel is single_value agg
  if ((!singleAggregate.getGroupSet().isEmpty())
      || (singleAggregate.getAggCallList().size() != 1)
      || !(singleAggregate.getAggCallList().get(0).getAggregation()
      instanceof SqlSingleValueAggFunction)) {
    return;
  }

  // check projRel only projects one expression
  // check this project only projects one expression, i.e. scalar
  // sub-queries.
  List<RexNode> projExprs = project.getProjects();
  if (projExprs.size() != 1) {
    return;
  }

  // check the input to project is an aggregate on the entire input
  if (!aggregate.getGroupSet().isEmpty()) {
    return;
  }

  // singleAggRel produces a nullable type, so create the new
  // projection that casts proj expr to a nullable type.
  final RelBuilder relBuilder = call.builder();
  final RelDataType type =
      relBuilder.getTypeFactory()
          .createTypeWithNullability(projExprs.get(0).getType(), true);
  final RexNode cast =
      relBuilder.getRexBuilder().makeCast(type, projExprs.get(0));
  relBuilder.push(aggregate)
      .project(cast);
  call.transformTo(relBuilder.build());
}
 
Example 13
Source File: AggregateUnionAggregateRule.java    From Bats with Apache License 2.0 5 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Aggregate topAggRel = call.rel(0);
  final Union union = call.rel(1);

  // If distincts haven't been removed yet, defer invoking this rule
  if (!union.all) {
    return;
  }

  final RelBuilder relBuilder = call.builder();
  final Aggregate bottomAggRel;
  if (call.rel(3) instanceof Aggregate) {
    // Aggregate is the second input
    bottomAggRel = call.rel(3);
    relBuilder.push(call.rel(2))
        .push(call.rel(3).getInput(0));
  } else if (call.rel(2) instanceof Aggregate) {
    // Aggregate is the first input
    bottomAggRel = call.rel(2);
    relBuilder.push(call.rel(2).getInput(0))
        .push(call.rel(3));
  } else {
    return;
  }

  // Only pull up aggregates if they are there just to remove distincts
  if (!topAggRel.getAggCallList().isEmpty()
      || !bottomAggRel.getAggCallList().isEmpty()) {
    return;
  }

  relBuilder.union(true);
  relBuilder.aggregate(relBuilder.groupKey(topAggRel.getGroupSet()),
      topAggRel.getAggCallList());
  call.transformTo(relBuilder.build());
}
 
Example 14
Source File: ProjectJoinJoinRemoveRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Project project = call.rel(0);
  final Join topJoin = call.rel(1);
  final Join bottomJoin = call.rel(2);
  int leftBottomChildSize = bottomJoin.getLeft().getRowType().getFieldCount();

  // Check whether the project uses columns in the right input of bottom join.
  for (RexNode expr: project.getProjects()) {
    if (RelOptUtil.InputFinder.bits(expr).asList().stream().anyMatch(
        i -> i >= leftBottomChildSize
            && i < bottomJoin.getRowType().getFieldCount())) {
      return;
    }
  }

  // Check whether the top join uses columns in the right input of bottom join.
  final List<Integer> leftKeys = new ArrayList<>();
  RelOptUtil.splitJoinCondition(topJoin.getLeft(), topJoin.getRight(),
      topJoin.getCondition(), leftKeys, new ArrayList<>(),
      new ArrayList<>());
  if (leftKeys.stream().anyMatch(s -> s >= leftBottomChildSize)) {
    return;
  }

  // Check whether left join keys in top join and bottom join are equal.
  final List<Integer> leftChildKeys = new ArrayList<>();
  final List<Integer> rightChildKeys = new ArrayList<>();
  RelOptUtil.splitJoinCondition(bottomJoin.getLeft(), bottomJoin.getRight(),
      bottomJoin.getCondition(), leftChildKeys, rightChildKeys,
      new ArrayList<>());
  if (!leftKeys.equals(leftChildKeys)) {
    return;
  }

  // Make sure that right keys of bottom join are unique.
  final ImmutableBitSet.Builder columns = ImmutableBitSet.builder();
  rightChildKeys.forEach(key -> columns.set(key));
  final RelMetadataQuery mq = call.getMetadataQuery();
  if (!mq.areColumnsUnique(bottomJoin.getRight(), columns.build())) {
    return;
  }

  int offset = bottomJoin.getRight().getRowType().getFieldCount();
  final RelBuilder relBuilder = call.builder();

  final RexNode condition = RexUtil.shift(topJoin.getCondition(),
      leftBottomChildSize, -offset);
  final RelNode join = relBuilder.push(bottomJoin.getLeft())
      .push(topJoin.getRight())
      .join(topJoin.getJoinType(), condition)
      .build();

  final List<RexNode> newExprs = project.getProjects().stream()
      .map(expr -> RexUtil.shift(expr, leftBottomChildSize, -offset))
      .collect(Collectors.toList());
  relBuilder.push(join).project(newExprs);
  call.transformTo(relBuilder.build());
}
 
Example 15
Source File: JoinPushThroughJoinRule.java    From Bats with Apache License 2.0 4 votes vote down vote up
/**
 * Similar to {@link #onMatch}, but swaps the upper sibling with the left
 * of the two lower siblings, rather than the right.
 */
private void onMatchLeft(RelOptRuleCall call) {
  final Join topJoin = call.rel(0);
  final Join bottomJoin = call.rel(1);
  final RelNode relC = call.rel(2);
  final RelNode relA = bottomJoin.getLeft();
  final RelNode relB = bottomJoin.getRight();
  final RelOptCluster cluster = topJoin.getCluster();

  //        topJoin
  //        /     \
  //   bottomJoin  C
  //    /    \
  //   A      B

  final int aCount = relA.getRowType().getFieldCount();
  final int bCount = relB.getRowType().getFieldCount();
  final int cCount = relC.getRowType().getFieldCount();
  final ImmutableBitSet aBitSet = ImmutableBitSet.range(aCount);

  // becomes
  //
  //        newTopJoin
  //        /        \
  //   newBottomJoin  A
  //    /    \
  //   C      B

  // If either join is not inner, we cannot proceed.
  // (Is this too strict?)
  if (topJoin.getJoinType() != JoinRelType.INNER
      || bottomJoin.getJoinType() != JoinRelType.INNER) {
    return;
  }

  // Split the condition of topJoin into a conjunction. Each of the
  // parts that does not use columns from A can be pushed down.
  final List<RexNode> intersecting = new ArrayList<>();
  final List<RexNode> nonIntersecting = new ArrayList<>();
  split(topJoin.getCondition(), aBitSet, intersecting, nonIntersecting);

  // If there's nothing to push down, it's not worth proceeding.
  if (nonIntersecting.isEmpty()) {
    return;
  }

  // Split the condition of bottomJoin into a conjunction. Each of the
  // parts that use columns from A will need to be pulled up.
  final List<RexNode> bottomIntersecting = new ArrayList<>();
  final List<RexNode> bottomNonIntersecting = new ArrayList<>();
  split(
      bottomJoin.getCondition(), aBitSet, bottomIntersecting,
      bottomNonIntersecting);

  // target: | C      | B |
  // source: | A       | B | C      |
  final Mappings.TargetMapping bottomMapping =
      Mappings.createShiftMapping(
          aCount + bCount + cCount,
          cCount, aCount, bCount,
          0, aCount + bCount, cCount);
  final List<RexNode> newBottomList = new ArrayList<>();
  new RexPermuteInputsShuttle(bottomMapping, relC, relB)
      .visitList(nonIntersecting, newBottomList);
  new RexPermuteInputsShuttle(bottomMapping, relC, relB)
      .visitList(bottomNonIntersecting, newBottomList);
  final RexBuilder rexBuilder = cluster.getRexBuilder();
  RexNode newBottomCondition =
      RexUtil.composeConjunction(rexBuilder, newBottomList);
  final Join newBottomJoin =
      bottomJoin.copy(bottomJoin.getTraitSet(), newBottomCondition, relC,
          relB, bottomJoin.getJoinType(), bottomJoin.isSemiJoinDone());

  // target: | C      | B | A       |
  // source: | A       | B | C      |
  final Mappings.TargetMapping topMapping =
      Mappings.createShiftMapping(
          aCount + bCount + cCount,
          cCount + bCount, 0, aCount,
          cCount, aCount, bCount,
          0, aCount + bCount, cCount);
  final List<RexNode> newTopList = new ArrayList<>();
  new RexPermuteInputsShuttle(topMapping, newBottomJoin, relA)
      .visitList(intersecting, newTopList);
  new RexPermuteInputsShuttle(topMapping, newBottomJoin, relA)
      .visitList(bottomIntersecting, newTopList);
  RexNode newTopCondition =
      RexUtil.composeConjunction(rexBuilder, newTopList);
  @SuppressWarnings("SuspiciousNameCombination")
  final Join newTopJoin =
      topJoin.copy(topJoin.getTraitSet(), newTopCondition, newBottomJoin,
          relA, topJoin.getJoinType(), topJoin.isSemiJoinDone());

  final RelBuilder relBuilder = call.builder();
  relBuilder.push(newTopJoin);
  relBuilder.project(relBuilder.fields(topMapping));
  call.transformTo(relBuilder.build());
}
 
Example 16
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 17
Source File: ProjectSetOpTransposeRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  LogicalProject origProj = call.rel(0);
  SetOp setOp = call.rel(1);

  // cannot push project past a distinct
  if (!setOp.all) {
    return;
  }

  // locate all fields referenced in the projection
  PushProjector pushProject =
      new PushProjector(
          origProj, null, setOp, preserveExprCondition, call.builder());
  pushProject.locateAllRefs();

  List<RelNode> newSetOpInputs = new ArrayList<>();
  int[] adjustments = pushProject.getAdjustments();

  final RelNode node;
  if (RexOver.containsOver(origProj.getProjects(), null)) {
    // should not push over past setop but can push its operand down.
    for (RelNode input : setOp.getInputs()) {
      Project p = pushProject.createProjectRefsAndExprs(input, true, false);
      // make sure that it is not a trivial project to avoid infinite loop.
      if (p.getRowType().equals(input.getRowType())) {
        return;
      }
      newSetOpInputs.add(p);
    }
    SetOp newSetOp =
        setOp.copy(setOp.getTraitSet(), newSetOpInputs);
    node = pushProject.createNewProject(newSetOp, adjustments);
  } else {
    // push some expressions below the setop; this
    // is different from pushing below a join, where we decompose
    // to try to keep expensive expressions above the join,
    // because UNION ALL does not have any filtering effect,
    // and it is the only operator this rule currently acts on
    setOp.getInputs().forEach(input ->
        newSetOpInputs.add(
            pushProject.createNewProject(
                pushProject.createProjectRefsAndExprs(
                    input, true, false), adjustments)));
    node = setOp.copy(setOp.getTraitSet(), newSetOpInputs);
  }

  call.transformTo(node);
}
 
Example 18
Source File: ProjectFilterTransposeRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Project origProject;
  final Filter filter;
  if (call.rels.length >= 2) {
    origProject = call.rel(0);
    filter = call.rel(1);
  } else {
    origProject = null;
    filter = call.rel(0);
  }
  final RelNode input = filter.getInput();
  final RexNode origFilter = filter.getCondition();

  if ((origProject != null)
      && RexOver.containsOver(origProject.getProjects(), null)) {
    // Cannot push project through filter if project contains a windowed
    // aggregate -- it will affect row counts. Abort this rule
    // invocation; pushdown will be considered after the windowed
    // aggregate has been implemented. It's OK if the filter contains a
    // windowed aggregate.
    return;
  }

  if ((origProject != null)
      && origProject.getRowType().isStruct()
      && origProject.getRowType().getFieldList().stream()
        .anyMatch(RelDataTypeField::isDynamicStar)) {
    // The PushProjector would change the plan:
    //
    //    prj(**=[$0])
    //    : - filter
    //        : - scan
    //
    // to form like:
    //
    //    prj(**=[$0])                    (1)
    //    : - filter                      (2)
    //        : - prj(**=[$0], ITEM= ...) (3)
    //            :  - scan
    // This new plan has more cost that the old one, because of the new
    // redundant project (3), if we also have FilterProjectTransposeRule in
    // the rule set, it will also trigger infinite match of the ProjectMergeRule
    // for project (1) and (3).
    return;
  }

  final RelBuilder builder = call.builder();
  final RelNode topProject;
  if (origProject != null && (wholeProject || wholeFilter)) {
    builder.push(input);

    final Set<RexNode> set = new LinkedHashSet<>();
    final RelOptUtil.InputFinder refCollector = new RelOptUtil.InputFinder();

    if (wholeFilter) {
      set.add(filter.getCondition());
    } else {
      filter.getCondition().accept(refCollector);
    }
    if (wholeProject) {
      set.addAll(origProject.getProjects());
    } else {
      refCollector.visitEach(origProject.getProjects());
    }

    // Build a list with inputRefs, in order, first, then other expressions.
    final List<RexNode> list = new ArrayList<>();
    final ImmutableBitSet refs = refCollector.build();
    for (RexNode field : builder.fields()) {
      if (refs.get(((RexInputRef) field).getIndex()) || set.contains(field)) {
        list.add(field);
      }
    }
    set.removeAll(list);
    list.addAll(set);
    builder.project(list);
    final Replacer replacer = new Replacer(list, builder);
    builder.filter(replacer.visit(filter.getCondition()));
    builder.project(replacer.visitList(origProject.getProjects()),
        origProject.getRowType().getFieldNames());
    topProject = builder.build();
  } else {
    // The traditional mode of operation of this rule: push down field
    // references. The effect is similar to RelFieldTrimmer.
    final PushProjector pushProjector =
        new PushProjector(origProject, origFilter, input,
            preserveExprCondition, builder);
    topProject = pushProjector.convertProject(null);
  }

  if (topProject != null) {
    call.transformTo(topProject);
  }
}
 
Example 19
Source File: FilterProjectTransposeRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Filter filter = call.rel(0);
  final Project project = call.rel(1);

  if (RexOver.containsOver(project.getProjects(), null)) {
    // In general a filter cannot be pushed below a windowing calculation.
    // Applying the filter before the aggregation function changes
    // the results of the windowing invocation.
    //
    // When the filter is on the PARTITION BY expression of the OVER clause
    // it can be pushed down. For now we don't support this.
    return;
  }
  // convert the filter to one that references the child of the project
  RexNode newCondition =
      RelOptUtil.pushPastProject(filter.getCondition(), project);

  final RelBuilder relBuilder = call.builder();
  RelNode newFilterRel;
  if (copyFilter) {
    final RelNode input = project.getInput();
    final RelTraitSet traitSet = filter.getTraitSet()
        .replaceIfs(RelCollationTraitDef.INSTANCE,
            () -> Collections.singletonList(
                    input.getTraitSet().getTrait(RelCollationTraitDef.INSTANCE)))
        .replaceIfs(RelDistributionTraitDef.INSTANCE,
            () -> Collections.singletonList(
                    input.getTraitSet().getTrait(RelDistributionTraitDef.INSTANCE)));
    newCondition = RexUtil.removeNullabilityCast(relBuilder.getTypeFactory(), newCondition);
    newFilterRel = filter.copy(traitSet, input, newCondition);
  } else {
    newFilterRel =
        relBuilder.push(project.getInput()).filter(newCondition).build();
  }

  RelNode newProjRel =
      copyProject
          ? project.copy(project.getTraitSet(), newFilterRel,
              project.getProjects(), project.getRowType())
          : relBuilder.push(newFilterRel)
              .project(project.getProjects(), project.getRowType().getFieldNames())
              .build();

  call.transformTo(newProjRel);
}
 
Example 20
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());
}