Java Code Examples for org.apache.calcite.rel.core.Join#copy()

The following examples show how to use org.apache.calcite.rel.core.Join#copy() . 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: JoinCommuteRule.java    From Bats with Apache License 2.0 6 votes vote down vote up
/**
 * Returns a relational expression with the inputs switched round. Does not
 * modify <code>join</code>. Returns null if the join cannot be swapped (for
 * example, because it is an outer join).
 *
 * @param join              join to be swapped
 * @param swapOuterJoins    whether outer joins should be swapped
 * @param relBuilder        Builder for relational expressions
 * @return swapped join if swapping possible; else null
 */
public static RelNode swap(Join join, boolean swapOuterJoins, RelBuilder relBuilder) {
    final JoinRelType joinType = join.getJoinType();
    if (!swapOuterJoins && joinType != JoinRelType.INNER) {
        return null;
    }
    final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
    final RelDataType leftRowType = join.getLeft().getRowType();
    final RelDataType rightRowType = join.getRight().getRowType();
    final VariableReplacer variableReplacer = new VariableReplacer(rexBuilder, leftRowType, rightRowType);
    final RexNode oldCondition = join.getCondition();
    RexNode condition = variableReplacer.go(oldCondition);

    // NOTE jvs 14-Mar-2006: We preserve attribute semiJoinDone after the
    // swap. This way, we will generate one semijoin for the original
    // join, and one for the swapped join, and no more. This
    // doesn't prevent us from seeing any new combinations assuming
    // that the planner tries the desired order (semijoins after swaps).
    Join newJoin = join.copy(join.getTraitSet(), condition, join.getRight(), join.getLeft(), joinType.swap(),
            join.isSemiJoinDone());
    final List<RexNode> exps = RelOptUtil.createSwappedJoinExprs(newJoin, join, true);
    return relBuilder.push(newJoin).project(exps, join.getRowType().getFieldNames()).build();
}
 
Example 2
Source File: AbstractJoinExtractFilterRule.java    From calcite with Apache License 2.0 5 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Join join = call.rel(0);

  if (join.getJoinType() != JoinRelType.INNER) {
    return;
  }

  if (join.getCondition().isAlwaysTrue()) {
    return;
  }

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

  final RelBuilder builder = call.builder();

  // NOTE jvs 14-Mar-2006:  See JoinCommuteRule for why we
  // preserve attribute semiJoinDone here.

  final RelNode cartesianJoin =
      join.copy(
          join.getTraitSet(),
          builder.literal(true),
          join.getLeft(),
          join.getRight(),
          join.getJoinType(),
          join.isSemiJoinDone());

  builder.push(cartesianJoin)
      .filter(join.getCondition());

  call.transformTo(builder.build());
}
 
Example 3
Source File: JoinAddRedundantSemiJoinRule.java    From Bats with Apache License 2.0 5 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  Join origJoinRel = call.rel(0);
  if (origJoinRel.isSemiJoinDone()) {
    return;
  }

  // can't process outer joins using semijoins
  if (origJoinRel.getJoinType() != JoinRelType.INNER) {
    return;
  }

  // determine if we have a valid join condition
  final JoinInfo joinInfo = origJoinRel.analyzeCondition();
  if (joinInfo.leftKeys.size() == 0) {
    return;
  }

  RelNode semiJoin =
      SemiJoin.create(origJoinRel.getLeft(),
          origJoinRel.getRight(),
          origJoinRel.getCondition(),
          joinInfo.leftKeys,
          joinInfo.rightKeys);

  RelNode newJoinRel =
      origJoinRel.copy(
          origJoinRel.getTraitSet(),
          origJoinRel.getCondition(),
          semiJoin,
          origJoinRel.getRight(),
          JoinRelType.INNER,
          true);

  call.transformTo(newJoinRel);
}
 
Example 4
Source File: AbstractJoinExtractFilterRule.java    From Bats with Apache License 2.0 5 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Join join = call.rel(0);

  if (join.getJoinType() != JoinRelType.INNER) {
    return;
  }

  if (join.getCondition().isAlwaysTrue()) {
    return;
  }

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

  final RelBuilder builder = call.builder();

  // NOTE jvs 14-Mar-2006:  See JoinCommuteRule for why we
  // preserve attribute semiJoinDone here.

  final RelNode cartesianJoin =
      join.copy(
          join.getTraitSet(),
          builder.literal(true),
          join.getLeft(),
          join.getRight(),
          join.getJoinType(),
          join.isSemiJoinDone());

  builder.push(cartesianJoin)
      .filter(join.getCondition());

  call.transformTo(builder.build());
}
 
Example 5
Source File: JoinAddRedundantSemiJoinRule.java    From calcite with Apache License 2.0 5 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  Join origJoinRel = call.rel(0);
  if (origJoinRel.isSemiJoinDone()) {
    return;
  }

  // can't process outer joins using semijoins
  if (origJoinRel.getJoinType() != JoinRelType.INNER) {
    return;
  }

  // determine if we have a valid join condition
  final JoinInfo joinInfo = origJoinRel.analyzeCondition();
  if (joinInfo.leftKeys.size() == 0) {
    return;
  }

  RelNode semiJoin =
      LogicalJoin.create(origJoinRel.getLeft(),
          origJoinRel.getRight(),
          ImmutableList.of(),
          origJoinRel.getCondition(),
          ImmutableSet.of(),
          JoinRelType.SEMI);

  RelNode newJoinRel =
      origJoinRel.copy(
          origJoinRel.getTraitSet(),
          origJoinRel.getCondition(),
          semiJoin,
          origJoinRel.getRight(),
          JoinRelType.INNER,
          true);

  call.transformTo(newJoinRel);
}
 
Example 6
Source File: JoinCommuteRule.java    From calcite with Apache License 2.0 5 votes vote down vote up
/**
 * Returns a relational expression with the inputs switched round. Does not
 * modify <code>join</code>. Returns null if the join cannot be swapped (for
 * example, because it is an outer join).
 *
 * @param join              join to be swapped
 * @param swapOuterJoins    whether outer joins should be swapped
 * @param relBuilder        Builder for relational expressions
 * @return swapped join if swapping possible; else null
 */
public static RelNode swap(Join join, boolean swapOuterJoins,
    RelBuilder relBuilder) {
  final JoinRelType joinType = join.getJoinType();
  if (!swapOuterJoins && joinType != JoinRelType.INNER) {
    return null;
  }
  final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
  final RelDataType leftRowType = join.getLeft().getRowType();
  final RelDataType rightRowType = join.getRight().getRowType();
  final VariableReplacer variableReplacer =
      new VariableReplacer(rexBuilder, leftRowType, rightRowType);
  final RexNode oldCondition = join.getCondition();
  RexNode condition = variableReplacer.apply(oldCondition);

  // NOTE jvs 14-Mar-2006: We preserve attribute semiJoinDone after the
  // swap.  This way, we will generate one semijoin for the original
  // join, and one for the swapped join, and no more.  This
  // doesn't prevent us from seeing any new combinations assuming
  // that the planner tries the desired order (semijoins after swaps).
  Join newJoin =
      join.copy(join.getTraitSet(), condition, join.getRight(),
          join.getLeft(), joinType.swap(), join.isSemiJoinDone());
  final List<RexNode> exps =
      RelOptUtil.createSwappedJoinExprs(newJoin, join, true);
  return relBuilder.push(newJoin)
      .project(exps, join.getRowType().getFieldNames())
      .build();
}
 
Example 7
Source File: FlinkProjectJoinTransposeRule.java    From flink with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
	Project origProj = call.rel(0);
	final Join join = call.rel(1);

	if (!join.getJoinType().projectsRight()) {
		return; // TODO: support SEMI/ANTI join later
	}
	// locate all fields referenced in the projection and join condition;
	// determine which inputs are referenced in the projection and
	// join condition; if all fields are being referenced and there are no
	// special expressions, no point in proceeding any further
	PushProjector pushProject =
			new PushProjector(
					origProj,
					join.getCondition(),
					join,
					preserveExprCondition,
					call.builder());
	if (pushProject.locateAllRefs()) {
		return;
	}

	// create left and right projections, projecting only those
	// fields referenced on each side
	RelNode leftProjRel =
			pushProject.createProjectRefsAndExprs(
					join.getLeft(),
					true,
					false);
	RelNode rightProjRel =
			pushProject.createProjectRefsAndExprs(
					join.getRight(),
					true,
					true);

	// convert the join condition to reference the projected columns
	RexNode newJoinFilter = null;
	int[] adjustments = pushProject.getAdjustments();
	if (join.getCondition() != null) {
		List<RelDataTypeField> projJoinFieldList = new ArrayList<>();
		projJoinFieldList.addAll(
				join.getSystemFieldList());
		projJoinFieldList.addAll(
				leftProjRel.getRowType().getFieldList());
		projJoinFieldList.addAll(
				rightProjRel.getRowType().getFieldList());
		newJoinFilter =
				pushProject.convertRefsAndExprs(
						join.getCondition(),
						projJoinFieldList,
						adjustments);
	}

	// create a new join with the projected children
	Join newJoinRel =
			join.copy(
					join.getTraitSet(),
					newJoinFilter,
					leftProjRel,
					rightProjRel,
					join.getJoinType(),
					join.isSemiJoinDone());

	// put the original project on top of the join, converting it to
	// reference the modified projection list
	RelNode topProject =
			pushProject.createNewProject(newJoinRel, adjustments);

	call.transformTo(topProject);
}
 
Example 8
Source File: JoinAssociateRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
public void onMatch(final RelOptRuleCall call) {
  final Join topJoin = call.rel(0);
  final Join bottomJoin = call.rel(1);
  final RelNode relA = bottomJoin.getLeft();
  final RelNode relB = bottomJoin.getRight();
  final RelSubset relC = call.rel(2);
  final RelOptCluster cluster = topJoin.getCluster();
  final RexBuilder rexBuilder = cluster.getRexBuilder();

  if (relC.getConvention() != relA.getConvention()) {
    // relC could have any trait-set. But if we're matching say
    // EnumerableConvention, we're only interested in enumerable subsets.
    return;
  }

  //        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(0, aCount);
  final ImmutableBitSet bBitSet =
      ImmutableBitSet.range(aCount, aCount + bCount);

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

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

  // Goal is to transform to
  //
  //       newTopJoin
  //        /     \
  //       A   newBottomJoin
  //               /    \
  //              B      C

  // Split the condition of topJoin and bottomJoin into a conjunctions. A
  // condition can be pushed down if it does not use columns from A.
  final List<RexNode> top = new ArrayList<>();
  final List<RexNode> bottom = new ArrayList<>();
  JoinPushThroughJoinRule.split(topJoin.getCondition(), aBitSet, top, bottom);
  JoinPushThroughJoinRule.split(bottomJoin.getCondition(), aBitSet, top,
      bottom);

  // Mapping for moving conditions from topJoin or bottomJoin to
  // newBottomJoin.
  // target: | B | C      |
  // source: | A       | B | C      |
  final Mappings.TargetMapping bottomMapping =
      Mappings.createShiftMapping(
          aCount + bCount + cCount,
          0, aCount, bCount,
          bCount, aCount + bCount, cCount);
  final List<RexNode> newBottomList =
      new RexPermuteInputsShuttle(bottomMapping, relB, relC)
          .visitList(bottom);
  RexNode newBottomCondition =
      RexUtil.composeConjunction(rexBuilder, newBottomList);

  final Join newBottomJoin =
      bottomJoin.copy(bottomJoin.getTraitSet(), newBottomCondition, relB,
          relC, JoinRelType.INNER, false);

  // Condition for newTopJoin consists of pieces from bottomJoin and topJoin.
  // Field ordinals do not need to be changed.
  RexNode newTopCondition = RexUtil.composeConjunction(rexBuilder, top);
  @SuppressWarnings("SuspiciousNameCombination")
  final Join newTopJoin =
      topJoin.copy(topJoin.getTraitSet(), newTopCondition, relA,
          newBottomJoin, JoinRelType.INNER, false);

  call.transformTo(newTopJoin);
}
 
Example 9
Source File: SortJoinTransposeRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Sort sort = call.rel(0);
  final Join join = call.rel(1);

  // We create a new sort operator on the corresponding input
  final RelNode newLeftInput;
  final RelNode newRightInput;
  final RelMetadataQuery mq = call.getMetadataQuery();
  if (join.getJoinType() == JoinRelType.LEFT) {
    // If the input is already sorted and we are not reducing the number of tuples,
    // we bail out
    if (RelMdUtil.checkInputForCollationAndLimit(mq, join.getLeft(),
        sort.getCollation(), sort.offset, sort.fetch)) {
      return;
    }
    newLeftInput = sort.copy(sort.getTraitSet(), join.getLeft(), sort.getCollation(),
        sort.offset, sort.fetch);
    newRightInput = join.getRight();
  } else {
    final RelCollation rightCollation =
        RelCollationTraitDef.INSTANCE.canonize(
            RelCollations.shift(sort.getCollation(),
                -join.getLeft().getRowType().getFieldCount()));
    // If the input is already sorted and we are not reducing the number of tuples,
    // we bail out
    if (RelMdUtil.checkInputForCollationAndLimit(mq, join.getRight(),
        rightCollation, sort.offset, sort.fetch)) {
      return;
    }
    newLeftInput = join.getLeft();
    newRightInput = sort.copy(sort.getTraitSet().replace(rightCollation),
        join.getRight(), rightCollation, sort.offset, sort.fetch);
  }
  // We copy the join and the top sort operator
  final RelNode joinCopy = join.copy(join.getTraitSet(), join.getCondition(), newLeftInput,
      newRightInput, join.getJoinType(), join.isSemiJoinDone());
  final RelNode sortCopy = sort.copy(sort.getTraitSet(), joinCopy, sort.getCollation(),
      sort.offset, sort.fetch);

  call.transformTo(sortCopy);
}
 
Example 10
Source File: SortJoinCopyRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Sort sort = call.rel(0);
  final Join join = call.rel(1);
  final RelMetadataQuery metadataQuery = call.getMetadataQuery();

  final RelNode newLeftInput;
  final RelNode newRightInput;

  final List<RelFieldCollation> leftFieldCollation = new ArrayList<>();
  final List<RelFieldCollation> rightFieldCollation = new ArrayList<>();

  // Decompose sort collations into left and right collations
  for (RelFieldCollation relFieldCollation : sort.getCollation().getFieldCollations()) {
    if (relFieldCollation.getFieldIndex() >= join.getLeft().getRowType().getFieldCount()) {
      rightFieldCollation.add(relFieldCollation);
    } else {
      leftFieldCollation.add(relFieldCollation);
    }
  }

  // Add sort to new left node only if sort collations
  // contained fields from left table
  if (leftFieldCollation.isEmpty()) {
    newLeftInput = join.getLeft();
  } else {
    final RelCollation leftCollation = RelCollationTraitDef.INSTANCE.canonize(
        RelCollations.of(leftFieldCollation));
    // If left table already sorted don't add a sort
    if (RelMdUtil.checkInputForCollationAndLimit(
        metadataQuery,
        join.getLeft(),
        leftCollation,
        null,
        null)) {
      newLeftInput = join.getLeft();
    } else {
      newLeftInput = sort.copy(
          sort.getTraitSet().replaceIf(
              RelCollationTraitDef.INSTANCE,
              () -> leftCollation),
          join.getLeft(),
          leftCollation,
          null,
          null);
    }
  }
  // Add sort to new right node only if sort collations
  // contained fields from right table
  if (rightFieldCollation.isEmpty()) {
    newRightInput = join.getRight();
  } else {
    final RelCollation rightCollation = RelCollationTraitDef.INSTANCE.canonize(
        RelCollations.shift(
            RelCollations.of(rightFieldCollation),
            -join.getLeft().getRowType().getFieldCount()));
    // If right table already sorted don't add a sort
    if (RelMdUtil.checkInputForCollationAndLimit(
        metadataQuery,
        join.getRight(),
        rightCollation,
        null,
        null)) {
      newRightInput = join.getRight();
    } else {
      newRightInput = sort.copy(
          sort.getTraitSet().replaceIf(
              RelCollationTraitDef.INSTANCE,
              () -> rightCollation),
          join.getRight(),
          rightCollation,
          null,
          null);
    }
  }
  // If no change was made no need to apply the rule
  if (newLeftInput == join.getLeft() && newRightInput == join.getRight()) {
    return;
  }

  final RelNode joinCopy = join.copy(
      join.getTraitSet(),
      join.getCondition(),
      newLeftInput,
      newRightInput,
      join.getJoinType(),
      join.isSemiJoinDone());
  final RelNode sortCopy = sort.copy(
      sort.getTraitSet(),
      joinCopy,
      sort.getCollation(),
      sort.offset,
      sort.fetch);

  call.transformTo(sortCopy);
}
 
Example 11
Source File: JoinPushThroughJoinRule.java    From calcite 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 12
Source File: ProjectJoinTransposeRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  Project origProj = call.rel(0);
  final Join join = call.rel(1);

  // Normalize the join condition so we don't end up misidentified expanded
  // form of IS NOT DISTINCT FROM as PushProject also visit the filter condition
  // and push down expressions.
  RexNode joinFilter = join.getCondition().accept(new RexShuttle() {
    @Override public RexNode visitCall(RexCall rexCall) {
      final RexNode node = super.visitCall(rexCall);
      if (!(node instanceof RexCall)) {
        return node;
      }
      return RelOptUtil.collapseExpandedIsNotDistinctFromExpr((RexCall) node,
          call.builder().getRexBuilder());
    }
  });

  // locate all fields referenced in the projection and join condition;
  // determine which inputs are referenced in the projection and
  // join condition; if all fields are being referenced and there are no
  // special expressions, no point in proceeding any further
  PushProjector pushProject =
      new PushProjector(
          origProj,
          joinFilter,
          join,
          preserveExprCondition,
          call.builder());
  if (pushProject.locateAllRefs()) {
    return;
  }

  // create left and right projections, projecting only those
  // fields referenced on each side
  RelNode leftProjRel =
      pushProject.createProjectRefsAndExprs(
          join.getLeft(),
          true,
          false);
  RelNode rightProjRel =
      pushProject.createProjectRefsAndExprs(
          join.getRight(),
          true,
          true);

  // convert the join condition to reference the projected columns
  RexNode newJoinFilter = null;
  int[] adjustments = pushProject.getAdjustments();
  if (joinFilter != null) {
    List<RelDataTypeField> projJoinFieldList = new ArrayList<>();
    projJoinFieldList.addAll(
        join.getSystemFieldList());
    projJoinFieldList.addAll(
        leftProjRel.getRowType().getFieldList());
    projJoinFieldList.addAll(
        rightProjRel.getRowType().getFieldList());
    newJoinFilter =
        pushProject.convertRefsAndExprs(
            joinFilter,
            projJoinFieldList,
            adjustments);
  }

  // create a new join with the projected children
  Join newJoinRel =
      join.copy(
          join.getTraitSet(),
          newJoinFilter,
          leftProjRel,
          rightProjRel,
          join.getJoinType(),
          join.isSemiJoinDone());

  // put the original project on top of the join, converting it to
  // reference the modified projection list
  RelNode topProject =
      pushProject.createNewProject(newJoinRel, adjustments);

  call.transformTo(topProject);
}
 
Example 13
Source File: JoinCalcTransposeRule.java    From quark with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Join joinRel = call.rel(0);
  final RelNode otherNode;
  final Calc calc;

  final RelNode leftJoinChild;
  final RelNode rightJoinChild;

  if (call.rel(1) instanceof Calc) {
    otherNode = call.rel(2);
    calc = call.rel(1);
    rightJoinChild = otherNode;
    leftJoinChild = calc.getInput();
  } else {
    otherNode = call.rel(1);
    calc = call.rel(2);
    rightJoinChild = calc.getInput();
    leftJoinChild = otherNode;
  }
  /**
   * Currently not supporting calc which doesnot
   * project star (all the columns of input)
   * or has aggregates.
   */
  if (!isStar(calc.getProgram())
      || calc.getProgram().containsAggs()) {
    return;
  }

  final List<RelDataTypeField> origFields =
      calc.getRowType().getFieldList();
  final int[] adjustments = new int[calc.getProgram().getExprCount()];
  if (rightJoinChild == calc.getInput()) {
    int offset = leftJoinChild.getRowType().getFieldList().size();
    for (int i = 0; i < origFields.size(); i++) {
      adjustments[i] = offset;
    }
  }
  Join newJoinRel =
      joinRel.copy(joinRel.getTraitSet(), joinRel.getCondition(),
          leftJoinChild, rightJoinChild, joinRel.getJoinType(),
          joinRel.isSemiJoinDone());

  RexProgramBuilder topProgramBuilder =
      new RexProgramBuilder(
          joinRel.getRowType(),
          joinRel.getCluster().getRexBuilder());
  topProgramBuilder.addIdentity();
  final RelOptUtil.RexInputConverter rexInputConverter =
      new RelOptUtil.RexInputConverter(calc.getCluster().getRexBuilder(),
          origFields,
          joinRel.getRowType().getFieldList(),
          adjustments);
  if (calc.getProgram().getCondition() != null) {
    RexNode cond =
        calc.getProgram().expandLocalRef(calc.getProgram().getCondition());
    final RexLocalRef rexLocalRef =
        topProgramBuilder.addExpr(cond.accept(rexInputConverter));
    topProgramBuilder.addCondition(rexLocalRef);
  }
  Calc newCalcRel =
      calc.copy(calc.getTraitSet(), newJoinRel, topProgramBuilder.getProgram());

  call.transformTo(newCalcRel);
}
 
Example 14
Source File: FlinkProjectJoinTransposeRule.java    From flink with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
	Project origProj = call.rel(0);
	final Join join = call.rel(1);

	if (!join.getJoinType().projectsRight()) {
		return; // TODO: support SEMI/ANTI join later
	}
	// locate all fields referenced in the projection and join condition;
	// determine which inputs are referenced in the projection and
	// join condition; if all fields are being referenced and there are no
	// special expressions, no point in proceeding any further
	PushProjector pushProject =
			new PushProjector(
					origProj,
					join.getCondition(),
					join,
					preserveExprCondition,
					call.builder());
	if (pushProject.locateAllRefs()) {
		return;
	}

	// create left and right projections, projecting only those
	// fields referenced on each side
	RelNode leftProjRel =
			pushProject.createProjectRefsAndExprs(
					join.getLeft(),
					true,
					false);
	RelNode rightProjRel =
			pushProject.createProjectRefsAndExprs(
					join.getRight(),
					true,
					true);

	// convert the join condition to reference the projected columns
	RexNode newJoinFilter = null;
	int[] adjustments = pushProject.getAdjustments();
	if (join.getCondition() != null) {
		List<RelDataTypeField> projJoinFieldList = new ArrayList<>();
		projJoinFieldList.addAll(
				join.getSystemFieldList());
		projJoinFieldList.addAll(
				leftProjRel.getRowType().getFieldList());
		projJoinFieldList.addAll(
				rightProjRel.getRowType().getFieldList());
		newJoinFilter =
				pushProject.convertRefsAndExprs(
						join.getCondition(),
						projJoinFieldList,
						adjustments);
	}

	// create a new join with the projected children
	Join newJoinRel =
			join.copy(
					join.getTraitSet(),
					newJoinFilter,
					leftProjRel,
					rightProjRel,
					join.getJoinType(),
					join.isSemiJoinDone());

	// put the original project on top of the join, converting it to
	// reference the modified projection list
	RelNode topProject =
			pushProject.createNewProject(newJoinRel, adjustments);

	call.transformTo(topProject);
}
 
Example 15
Source File: JoinAssociateRule.java    From Bats with Apache License 2.0 4 votes vote down vote up
public void onMatch(final RelOptRuleCall call) {
  final Join topJoin = call.rel(0);
  final Join bottomJoin = call.rel(1);
  final RelNode relA = bottomJoin.getLeft();
  final RelNode relB = bottomJoin.getRight();
  final RelSubset relC = call.rel(2);
  final RelOptCluster cluster = topJoin.getCluster();
  final RexBuilder rexBuilder = cluster.getRexBuilder();

  if (relC.getConvention() != relA.getConvention()) {
    // relC could have any trait-set. But if we're matching say
    // EnumerableConvention, we're only interested in enumerable subsets.
    return;
  }

  //        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(0, aCount);
  final ImmutableBitSet bBitSet =
      ImmutableBitSet.range(aCount, aCount + bCount);

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

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

  // Goal is to transform to
  //
  //       newTopJoin
  //        /     \
  //       A   newBottomJoin
  //               /    \
  //              B      C

  // Split the condition of topJoin and bottomJoin into a conjunctions. A
  // condition can be pushed down if it does not use columns from A.
  final List<RexNode> top = new ArrayList<>();
  final List<RexNode> bottom = new ArrayList<>();
  JoinPushThroughJoinRule.split(topJoin.getCondition(), aBitSet, top, bottom);
  JoinPushThroughJoinRule.split(bottomJoin.getCondition(), aBitSet, top,
      bottom);

  // Mapping for moving conditions from topJoin or bottomJoin to
  // newBottomJoin.
  // target: | B | C      |
  // source: | A       | B | C      |
  final Mappings.TargetMapping bottomMapping =
      Mappings.createShiftMapping(
          aCount + bCount + cCount,
          0, aCount, bCount,
          bCount, aCount + bCount, cCount);
  final List<RexNode> newBottomList = new ArrayList<>();
  new RexPermuteInputsShuttle(bottomMapping, relB, relC)
      .visitList(bottom, newBottomList);
  RexNode newBottomCondition =
      RexUtil.composeConjunction(rexBuilder, newBottomList);

  final Join newBottomJoin =
      bottomJoin.copy(bottomJoin.getTraitSet(), newBottomCondition, relB,
          relC, JoinRelType.INNER, false);

  // Condition for newTopJoin consists of pieces from bottomJoin and topJoin.
  // Field ordinals do not need to be changed.
  RexNode newTopCondition = RexUtil.composeConjunction(rexBuilder, top);
  @SuppressWarnings("SuspiciousNameCombination")
  final Join newTopJoin =
      topJoin.copy(topJoin.getTraitSet(), newTopCondition, relA,
          newBottomJoin, JoinRelType.INNER, false);

  call.transformTo(newTopJoin);
}
 
Example 16
Source File: SortJoinTransposeRule.java    From Bats with Apache License 2.0 4 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Sort sort = call.rel(0);
  final Join join = call.rel(1);

  // We create a new sort operator on the corresponding input
  final RelNode newLeftInput;
  final RelNode newRightInput;
  final RelMetadataQuery mq = call.getMetadataQuery();
  if (join.getJoinType() == JoinRelType.LEFT) {
    // If the input is already sorted and we are not reducing the number of tuples,
    // we bail out
    if (RelMdUtil.checkInputForCollationAndLimit(mq, join.getLeft(),
        sort.getCollation(), sort.offset, sort.fetch)) {
      return;
    }
    newLeftInput = sort.copy(sort.getTraitSet(), join.getLeft(), sort.getCollation(),
        sort.offset, sort.fetch);
    newRightInput = join.getRight();
  } else {
    final RelCollation rightCollation =
        RelCollationTraitDef.INSTANCE.canonize(
            RelCollations.shift(sort.getCollation(),
                -join.getLeft().getRowType().getFieldCount()));
    // If the input is already sorted and we are not reducing the number of tuples,
    // we bail out
    if (RelMdUtil.checkInputForCollationAndLimit(mq, join.getRight(),
        rightCollation, sort.offset, sort.fetch)) {
      return;
    }
    newLeftInput = join.getLeft();
    newRightInput = sort.copy(sort.getTraitSet().replace(rightCollation),
        join.getRight(), rightCollation, sort.offset, sort.fetch);
  }
  // We copy the join and the top sort operator
  final RelNode joinCopy = join.copy(join.getTraitSet(), join.getCondition(), newLeftInput,
      newRightInput, join.getJoinType(), join.isSemiJoinDone());
  final RelNode sortCopy = sort.copy(sort.getTraitSet(), joinCopy, sort.getCollation(),
      sort.offset, sort.fetch);

  call.transformTo(sortCopy);
}
 
Example 17
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 18
Source File: ProjectJoinTransposeRule.java    From Bats with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  Project origProj = call.rel(0);
  final Join join = call.rel(1);

  if (join instanceof SemiJoin) {
    return; // TODO: support SemiJoin
  }
  // locate all fields referenced in the projection and join condition;
  // determine which inputs are referenced in the projection and
  // join condition; if all fields are being referenced and there are no
  // special expressions, no point in proceeding any further
  PushProjector pushProject =
      new PushProjector(
          origProj,
          join.getCondition(),
          join,
          preserveExprCondition,
          call.builder());
  if (pushProject.locateAllRefs()) {
    return;
  }

  // create left and right projections, projecting only those
  // fields referenced on each side
  RelNode leftProjRel =
      pushProject.createProjectRefsAndExprs(
          join.getLeft(),
          true,
          false);
  RelNode rightProjRel =
      pushProject.createProjectRefsAndExprs(
          join.getRight(),
          true,
          true);

  // convert the join condition to reference the projected columns
  RexNode newJoinFilter = null;
  int[] adjustments = pushProject.getAdjustments();
  if (join.getCondition() != null) {
    List<RelDataTypeField> projJoinFieldList = new ArrayList<>();
    projJoinFieldList.addAll(
        join.getSystemFieldList());
    projJoinFieldList.addAll(
        leftProjRel.getRowType().getFieldList());
    projJoinFieldList.addAll(
        rightProjRel.getRowType().getFieldList());
    newJoinFilter =
        pushProject.convertRefsAndExprs(
            join.getCondition(),
            projJoinFieldList,
            adjustments);
  }

  // create a new join with the projected children
  Join newJoinRel =
      join.copy(
          join.getTraitSet(),
          newJoinFilter,
          leftProjRel,
          rightProjRel,
          join.getJoinType(),
          join.isSemiJoinDone());

  // put the original project on top of the join, converting it to
  // reference the modified projection list
  RelNode topProject =
      pushProject.createNewProject(newJoinRel, adjustments);

  call.transformTo(topProject);
}