Java Code Examples for org.apache.calcite.rel.logical.LogicalJoin#getJoinType()

The following examples show how to use org.apache.calcite.rel.logical.LogicalJoin#getJoinType() . 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: Lattice.java    From Bats with Apache License 2.0 6 votes vote down vote up
private static boolean populate(List<RelNode> nodes, List<int[][]> tempLinks,
    RelNode rel) {
  if (nodes.isEmpty() && rel instanceof LogicalProject) {
    return populate(nodes, tempLinks, ((LogicalProject) rel).getInput());
  }
  if (rel instanceof TableScan) {
    nodes.add(rel);
    return true;
  }
  if (rel instanceof LogicalJoin) {
    LogicalJoin join = (LogicalJoin) rel;
    if (join.getJoinType() != JoinRelType.INNER) {
      throw new RuntimeException("only inner join allowed, but got "
          + join.getJoinType());
    }
    populate(nodes, tempLinks, join.getLeft());
    populate(nodes, tempLinks, join.getRight());
    for (RexNode rex : RelOptUtil.conjunctions(join.getCondition())) {
      tempLinks.add(grab(nodes, rex));
    }
    return true;
  }
  throw new RuntimeException("Invalid node type "
      + rel.getClass().getSimpleName() + " in lattice query");
}
 
Example 2
Source File: Lattice.java    From calcite with Apache License 2.0 6 votes vote down vote up
private static boolean populate(List<RelNode> nodes, List<int[][]> tempLinks,
    RelNode rel) {
  if (nodes.isEmpty() && rel instanceof LogicalProject) {
    return populate(nodes, tempLinks, ((LogicalProject) rel).getInput());
  }
  if (rel instanceof TableScan) {
    nodes.add(rel);
    return true;
  }
  if (rel instanceof LogicalJoin) {
    LogicalJoin join = (LogicalJoin) rel;
    if (join.getJoinType().isOuterJoin()) {
      throw new RuntimeException("only non nulls-generating join allowed, but got "
          + join.getJoinType());
    }
    populate(nodes, tempLinks, join.getLeft());
    populate(nodes, tempLinks, join.getRight());
    for (RexNode rex : RelOptUtil.conjunctions(join.getCondition())) {
      tempLinks.add(grab(nodes, rex));
    }
    return true;
  }
  throw new RuntimeException("Invalid node type "
      + rel.getClass().getSimpleName() + " in lattice query");
}
 
Example 3
Source File: FlinkSemiAntiJoinProjectTransposeRule.java    From flink with Apache License 2.0 6 votes vote down vote up
@Override
public boolean matches(RelOptRuleCall call) {
	LogicalJoin join = call.rel(0);
	LogicalProject project = call.rel(1);

	// only accept SEMI/ANTI join
	JoinRelType joinType = join.getJoinType();
	if (joinType != JoinRelType.SEMI && joinType != JoinRelType.ANTI) {
		return false;
	}
	// all expressions in Project should be RexInputRef
	for (RexNode p : project.getProjects()) {
		if (!(p instanceof RexInputRef)) {
			return false;
		}
	}
	return true;
}
 
Example 4
Source File: CopyWithCluster.java    From dremio-oss with Apache License 2.0 6 votes vote down vote up
@Override
public RelNode visit(LogicalJoin join) {
  // to the best of my knowledge join.systemFieldList is always empty
  Preconditions.checkState(join.getSystemFieldList().isEmpty(), "join.systemFieldList is not empty!");

  final RelNode left = join.getLeft().accept(this);
  final RelNode right = join.getRight().accept(this);

  return new LogicalJoin(
    cluster,
    copyOf(join.getTraitSet()),
    left,
    right,
    copyOf(join.getCondition()),
    join.getVariablesSet(),
    join.getJoinType(),
    join.isSemiJoinDone(),
    ImmutableList.<RelDataTypeField>of()
  );
}
 
Example 5
Source File: FlinkSemiAntiJoinProjectTransposeRule.java    From flink with Apache License 2.0 6 votes vote down vote up
@Override
public boolean matches(RelOptRuleCall call) {
	LogicalJoin join = call.rel(0);
	LogicalProject project = call.rel(1);

	// only accept SEMI/ANTI join
	JoinRelType joinType = join.getJoinType();
	if (joinType != JoinRelType.SEMI && joinType != JoinRelType.ANTI) {
		return false;
	}
	// all expressions in Project should be RexInputRef
	for (RexNode p : project.getProjects()) {
		if (!(p instanceof RexInputRef)) {
			return false;
		}
	}
	return true;
}
 
Example 6
Source File: ElasticsearchJoinRule.java    From dk-fitting with Apache License 2.0 6 votes vote down vote up
public RelNode convert(RelNode relNode) {
    LogicalJoin join = (LogicalJoin) relNode;
    List<RelNode> newInputs = new ArrayList<RelNode>();
    for (RelNode input : join.getInputs()) {
        if (!(input.getConvention().getName().equals(ElasticsearchRelNode.CONVENTION.getName()))) {
            input =
                    convert(
                            input,
                            input.getTraitSet()
                                    .replace(ElasticsearchRelNode.CONVENTION));
        }
        newInputs.add(input);
    }
    final RelOptCluster cluster = join.getCluster();
    final RelTraitSet traitSet =
            join.getTraitSet().replace(ElasticsearchRelNode.CONVENTION);
    final RelNode left = newInputs.get(0);
    final RelNode right = newInputs.get(1);

    return new ElasticsearchJoin(join.getCluster(), traitSet, left, right,
            join.getCondition(), join.getJoinType());
}
 
Example 7
Source File: ElasticsearchJoinRule.java    From dk-fitting with Apache License 2.0 6 votes vote down vote up
public RelNode convert(RelNode relNode) {
    LogicalJoin join = (LogicalJoin) relNode;
    List<RelNode> newInputs = new ArrayList<RelNode>();
    for (RelNode input : join.getInputs()) {
        if (!(input.getConvention().getName().equals(ElasticsearchRelNode.CONVENTION.getName()))) {
            input =
                    convert(
                            input,
                            input.getTraitSet()
                                    .replace(ElasticsearchRelNode.CONVENTION));
        }
        newInputs.add(input);
    }
    final RelOptCluster cluster = join.getCluster();
    final RelTraitSet traitSet =
            join.getTraitSet().replace(ElasticsearchRelNode.CONVENTION);
    final RelNode left = newInputs.get(0);
    final RelNode right = newInputs.get(1);

    return new ElasticsearchJoin(join.getCluster(), traitSet, left, right,
            join.getCondition(), join.getJoinType());
}
 
Example 8
Source File: Bindables.java    From calcite with Apache License 2.0 5 votes vote down vote up
public RelNode convert(RelNode rel) {
  final LogicalJoin join = (LogicalJoin) rel;
  final BindableConvention out = BindableConvention.INSTANCE;
  final RelTraitSet traitSet = join.getTraitSet().replace(out);
  return new BindableJoin(rel.getCluster(), traitSet,
      convert(join.getLeft(),
          join.getLeft().getTraitSet()
              .replace(BindableConvention.INSTANCE)),
      convert(join.getRight(),
          join.getRight().getTraitSet()
              .replace(BindableConvention.INSTANCE)),
      join.getCondition(), join.getVariablesSet(), join.getJoinType());
}
 
Example 9
Source File: SubQueryDecorrelator.java    From flink with Apache License 2.0 5 votes vote down vote up
@Override
public RelNode visit(LogicalJoin join) {
	switch (join.getJoinType()) {
		case LEFT:
			checkCorConditionOfInput(join.getRight());
			break;
		case RIGHT:
			checkCorConditionOfInput(join.getLeft());
			break;
		case FULL:
			checkCorConditionOfInput(join.getLeft());
			checkCorConditionOfInput(join.getRight());
			break;
		default:
			break;
	}

	final boolean hasSubQuery = RexUtil.SubQueryFinder.find(join.getCondition()) != null;
	try {
		if (!corNodeStack.isEmpty()) {
			mapSubQueryNodeToCorSet.put(join, corNodeStack.peek().getVariablesSet());
		}
		if (hasSubQuery) {
			corNodeStack.push(join);
		}
		checkCorCondition(join);
		join.getCondition().accept(rexVisitor(join));
	} finally {
		if (hasSubQuery) {
			corNodeStack.pop();
		}
	}
	visitChild(join, 0, join.getLeft());
	visitChild(join, 1, join.getRight());
	return join;
}
 
Example 10
Source File: SubQueryDecorrelator.java    From flink with Apache License 2.0 5 votes vote down vote up
@Override
public RelNode visit(LogicalJoin join) {
	switch (join.getJoinType()) {
		case LEFT:
			checkCorConditionOfInput(join.getRight());
			break;
		case RIGHT:
			checkCorConditionOfInput(join.getLeft());
			break;
		case FULL:
			checkCorConditionOfInput(join.getLeft());
			checkCorConditionOfInput(join.getRight());
			break;
		default:
			break;
	}

	final boolean hasSubQuery = RexUtil.SubQueryFinder.find(join.getCondition()) != null;
	try {
		if (!corNodeStack.isEmpty()) {
			mapSubQueryNodeToCorSet.put(join, corNodeStack.peek().getVariablesSet());
		}
		if (hasSubQuery) {
			corNodeStack.push(join);
		}
		checkCorCondition(join);
		join.getCondition().accept(rexVisitor(join));
	} finally {
		if (hasSubQuery) {
			corNodeStack.pop();
		}
	}
	visitChild(join, 0, join.getLeft());
	visitChild(join, 1, join.getRight());
	return join;
}
 
Example 11
Source File: DrillJoinRelBase.java    From Bats with Apache License 2.0 5 votes vote down vote up
@Override
public double estimateRowCount(RelMetadataQuery mq) {
  if (this.condition.isAlwaysTrue()) {
    return joinRowFactor * this.getLeft().estimateRowCount(mq) * this.getRight().estimateRowCount(mq);
  }

  int[] joinFields = new int[2];

  LogicalJoin jr = LogicalJoin.create(this.getLeft(), this.getRight(), this.getCondition(),
          this.getVariablesSet(), this.getJoinType());

  if (!DrillRelOptUtil.guessRows(this)         //Statistics present for left and right side of the join
      && jr.getJoinType() == JoinRelType.INNER
      && DrillRelOptUtil.analyzeSimpleEquiJoin((Join)jr, joinFields)) {
    ImmutableBitSet leq = ImmutableBitSet.of(joinFields[0]);
    ImmutableBitSet req = ImmutableBitSet.of(joinFields[1]);

    Double ldrc = mq.getDistinctRowCount(this.getLeft(), leq, null);
    Double rdrc = mq.getDistinctRowCount(this.getRight(), req, null);

    Double lrc = mq.getRowCount(this.getLeft());
    Double rrc = mq.getRowCount(this.getRight());

    if (ldrc != null && rdrc != null && lrc != null && rrc != null) {
      // Join cardinality = (lrc * rrc) / Math.max(ldrc, rdrc). Avoid overflow by dividing earlier
      return (lrc / Math.max(ldrc, rdrc)) * rrc;
    }
  }

  return joinRowFactor * Math.max(
      mq.getRowCount(this.getLeft()),
      mq.getRowCount(this.getRight()));
}
 
Example 12
Source File: RelDecorrelator.java    From Bats with Apache License 2.0 5 votes vote down vote up
/**
 * Pulls project above the join from its RHS input. Enforces nullability
 * for join output.
 *
 * @param join          Join
 * @param project       Original project as the right-hand input of the join
 * @param nullIndicatorPos Position of null indicator
 * @return the subtree with the new Project at the root
 */
private RelNode projectJoinOutputWithNullability(LogicalJoin join, LogicalProject project, int nullIndicatorPos) {
    final RelDataTypeFactory typeFactory = join.getCluster().getTypeFactory();
    final RelNode left = join.getLeft();
    final JoinRelType joinType = join.getJoinType();

    RexInputRef nullIndicator = RexBuilder.getRexFactory().makeInputRef(nullIndicatorPos, typeFactory
            .createTypeWithNullability(join.getRowType().getFieldList().get(nullIndicatorPos).getType(), true));

    // now create the new project
    List<Pair<RexNode, String>> newProjExprs = new ArrayList<>();

    // project everything from the LHS and then those from the original
    // projRel
    List<RelDataTypeField> leftInputFields = left.getRowType().getFieldList();

    for (int i = 0; i < leftInputFields.size(); i++) {
        newProjExprs.add(RexInputRef.of2(i, leftInputFields));
    }

    // Marked where the projected expr is coming from so that the types will
    // become nullable for the original projections which are now coming out
    // of the nullable side of the OJ.
    boolean projectPulledAboveLeftCorrelator = joinType.generatesNullsOnRight();

    for (Pair<RexNode, String> pair : project.getNamedProjects()) {
        RexNode newProjExpr = removeCorrelationExpr(pair.left, projectPulledAboveLeftCorrelator, nullIndicator);

        newProjExprs.add(Pair.of(newProjExpr, pair.right));
    }

    return relBuilder.push(join).projectNamed(Pair.left(newProjExprs), Pair.right(newProjExprs), true).build();
}
 
Example 13
Source File: FlinkSemiAntiJoinFilterTransposeRule.java    From flink with Apache License 2.0 4 votes vote down vote up
@Override
public boolean matches(RelOptRuleCall call) {
	LogicalJoin join = call.rel(0);
	return join.getJoinType() == JoinRelType.SEMI || join.getJoinType() == JoinRelType.ANTI;
}
 
Example 14
Source File: JoinTranslator.java    From samza with Apache License 2.0 4 votes vote down vote up
private void validateJoinQuery(LogicalJoin join, JoinInputNode.InputType inputTypeOnLeft,
    JoinInputNode.InputType inputTypeOnRight) {
  JoinRelType joinRelType = join.getJoinType();

  if (joinRelType.compareTo(JoinRelType.INNER) != 0 && joinRelType.compareTo(JoinRelType.LEFT) != 0
      && joinRelType.compareTo(JoinRelType.RIGHT) != 0) {
    throw new SamzaException("Query with only INNER and LEFT/RIGHT OUTER join are supported.");
  }

  boolean isTablePosOnLeft = inputTypeOnLeft != JoinInputNode.InputType.STREAM;
  boolean isTablePosOnRight = inputTypeOnRight != JoinInputNode.InputType.STREAM;

  if (!isTablePosOnLeft && !isTablePosOnRight) {
    throw new SamzaException("Invalid query with both sides of join being denoted as 'stream'. "
        + "Stream-stream join is not yet supported. " + dumpRelPlanForNode(join));
  }

  if (isTablePosOnLeft && isTablePosOnRight) {
    throw new SamzaException("Invalid query with both sides of join being denoted as 'table'. " +
        dumpRelPlanForNode(join));
  }

  if (joinRelType.compareTo(JoinRelType.LEFT) == 0 && isTablePosOnLeft) {
    throw new SamzaException("Invalid query for outer left join. Left side of the join should be a 'stream' and "
        + "right side of join should be a 'table'. " + dumpRelPlanForNode(join));
  }

  if (joinRelType.compareTo(JoinRelType.RIGHT) == 0 && isTablePosOnRight) {
    throw new SamzaException("Invalid query for outer right join. Left side of the join should be a 'table' and "
        + "right side of join should be a 'stream'. " + dumpRelPlanForNode(join));
  }

  final List<RexNode> conjunctionList = new ArrayList<>();
  decomposeAndValidateConjunction(join.getCondition(), conjunctionList);

  if (conjunctionList.isEmpty()) {
    throw new SamzaException("Query results in a cross join, which is not supported. Please optimize the query."
        + " It is expected that the joins should include JOIN ON operator in the sql query.");
  }
  //TODO Not sure why we can not allow literal as part of the join condition will revisit this in another scope
  conjunctionList.forEach(rexNode -> rexNode.accept(new RexShuttle() {
    @Override
    public RexNode visitLiteral(RexLiteral literal) {
      throw new SamzaException(
          "Join Condition can not allow literal " + literal.toString() + " join node" + join.getDigest());
    }
  }));
  final JoinInputNode.InputType rootTableInput = isTablePosOnRight ? inputTypeOnRight : inputTypeOnLeft;
  if (rootTableInput.compareTo(JoinInputNode.InputType.REMOTE_TABLE) != 0) {
    // it is not a remote table all is good we do not have to validate the project on key Column
    return;
  }

  /*
  For remote Table we need to validate The join Condition and The project that is above remote table scan.
   - As of today Filter need to be exactly one equi-join using the __key__ column (see SAMZA-2554)
   - The Project on the top of the remote table has to contain only simple input references to any of the column used in the join.
  */

  // First let's collect the ref of columns used by the join condition.
  List<RexInputRef> refCollector = new ArrayList<>();
  join.getCondition().accept(new RexShuttle() {
    @Override
    public RexNode visitInputRef(RexInputRef inputRef) {
      refCollector.add(inputRef);
      return inputRef;
    }
  });
  // start index of the Remote table within the Join Row
  final int tableStartIndex = isTablePosOnRight ? join.getLeft().getRowType().getFieldCount() : 0;
  // end index of the Remote table withing the Join Row
  final int tableEndIndex =
      isTablePosOnRight ? join.getRowType().getFieldCount() : join.getLeft().getRowType().getFieldCount();

  List<Integer> tableRefsIdx = refCollector.stream()
      .map(x -> x.getIndex())
      .filter(x -> tableStartIndex <= x && x < tableEndIndex) // collect all the refs form table side
      .map(x -> x - tableStartIndex) // re-adjust the offset
      .sorted()
      .collect(Collectors.toList()); // we have a list with all the input from table side with 0 based index.

  // Validate the Condition must contain a ref to remote table primary key column.

  if (conjunctionList.size() != 1 || tableRefsIdx.size() != 1) {
    //TODO We can relax this by allowing another filter to be evaluated post lookup see SAMZA-2554
    throw new SamzaException(
        "Invalid query for join condition must contain exactly one predicate for remote table on __key__ column "
            + dumpRelPlanForNode(join));
  }

  // Validate the Project, follow each input and ensure that it is a simple ref with no rexCall in the way.
  if (!isValidRemoteJoinRef(tableRefsIdx.get(0), isTablePosOnRight ? join.getRight() : join.getLeft())) {
    throw new SamzaException("Invalid query for join condition can not have an expression and must be reference "
        + SamzaSqlRelMessage.KEY_NAME + " column " + dumpRelPlanForNode(join));
  }
}
 
Example 15
Source File: FlinkSemiAntiJoinFilterTransposeRule.java    From flink with Apache License 2.0 4 votes vote down vote up
@Override
public boolean matches(RelOptRuleCall call) {
	LogicalJoin join = call.rel(0);
	return join.getJoinType() == JoinRelType.SEMI || join.getJoinType() == JoinRelType.ANTI;
}
 
Example 16
Source File: RelDecorrelator.java    From flink with Apache License 2.0 4 votes vote down vote up
/**
 * Pulls project above the join from its RHS input. Enforces nullability
 * for join output.
 *
 * @param join          Join
 * @param project       Original project as the right-hand input of the join
 * @param nullIndicatorPos Position of null indicator
 * @return the subtree with the new Project at the root
 */
private RelNode projectJoinOutputWithNullability(
    LogicalJoin join,
    LogicalProject project,
    int nullIndicatorPos) {
  final RelDataTypeFactory typeFactory = join.getCluster().getTypeFactory();
  final RelNode left = join.getLeft();
  final JoinRelType joinType = join.getJoinType();

  RexInputRef nullIndicator =
      new RexInputRef(
          nullIndicatorPos,
          typeFactory.createTypeWithNullability(
              join.getRowType().getFieldList().get(nullIndicatorPos)
                  .getType(),
              true));

  // now create the new project
  List<Pair<RexNode, String>> newProjExprs = new ArrayList<>();

  // project everything from the LHS and then those from the original
  // projRel
  List<RelDataTypeField> leftInputFields =
      left.getRowType().getFieldList();

  for (int i = 0; i < leftInputFields.size(); i++) {
    newProjExprs.add(RexInputRef.of2(i, leftInputFields));
  }

  // Marked where the projected expr is coming from so that the types will
  // become nullable for the original projections which are now coming out
  // of the nullable side of the OJ.
  boolean projectPulledAboveLeftCorrelator =
      joinType.generatesNullsOnRight();

  for (Pair<RexNode, String> pair : project.getNamedProjects()) {
    RexNode newProjExpr =
        removeCorrelationExpr(
            pair.left,
            projectPulledAboveLeftCorrelator,
            nullIndicator);

    newProjExprs.add(Pair.of(newProjExpr, pair.right));
  }

  return relBuilder.push(join)
      .projectNamed(Pair.left(newProjExprs), Pair.right(newProjExprs), true)
      .build();
}
 
Example 17
Source File: RelDecorrelator.java    From flink with Apache License 2.0 4 votes vote down vote up
/**
 * Pulls project above the join from its RHS input. Enforces nullability
 * for join output.
 *
 * @param join          Join
 * @param project       Original project as the right-hand input of the join
 * @param nullIndicatorPos Position of null indicator
 * @return the subtree with the new Project at the root
 */
private RelNode projectJoinOutputWithNullability(
    LogicalJoin join,
    LogicalProject project,
    int nullIndicatorPos) {
  final RelDataTypeFactory typeFactory = join.getCluster().getTypeFactory();
  final RelNode left = join.getLeft();
  final JoinRelType joinType = join.getJoinType();

  RexInputRef nullIndicator =
      new RexInputRef(
          nullIndicatorPos,
          typeFactory.createTypeWithNullability(
              join.getRowType().getFieldList().get(nullIndicatorPos)
                  .getType(),
              true));

  // now create the new project
  List<Pair<RexNode, String>> newProjExprs = new ArrayList<>();

  // project everything from the LHS and then those from the original
  // projRel
  List<RelDataTypeField> leftInputFields =
      left.getRowType().getFieldList();

  for (int i = 0; i < leftInputFields.size(); i++) {
    newProjExprs.add(RexInputRef.of2(i, leftInputFields));
  }

  // Marked where the projected expr is coming from so that the types will
  // become nullable for the original projections which are now coming out
  // of the nullable side of the OJ.
  boolean projectPulledAboveLeftCorrelator =
      joinType.generatesNullsOnRight();

  for (Pair<RexNode, String> pair : project.getNamedProjects()) {
    RexNode newProjExpr =
        removeCorrelationExpr(
            pair.left,
            projectPulledAboveLeftCorrelator,
            nullIndicator);

    newProjExprs.add(Pair.of(newProjExpr, pair.right));
  }

  return relBuilder.push(join)
      .projectNamed(Pair.left(newProjExprs), Pair.right(newProjExprs), true)
      .build();
}
 
Example 18
Source File: PigRules.java    From calcite with Apache License 2.0 4 votes vote down vote up
public RelNode convert(RelNode rel) {
  final LogicalJoin join = (LogicalJoin) rel;
  final RelTraitSet traitSet = join.getTraitSet().replace(PigRel.CONVENTION);
  return new PigJoin(join.getCluster(), traitSet, join.getLeft(), join.getRight(),
      join.getCondition(), join.getJoinType());
}
 
Example 19
Source File: EnumerableMergeJoinRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
@Override public RelNode convert(RelNode rel) {
  LogicalJoin join = (LogicalJoin) rel;
  final JoinInfo info = join.analyzeCondition();
  if (!EnumerableMergeJoin.isMergeJoinSupported(join.getJoinType())) {
    // EnumerableMergeJoin only supports certain join types.
    return null;
  }
  if (info.pairs().size() == 0) {
    // EnumerableMergeJoin CAN support cartesian join, but disable it for now.
    return null;
  }
  final List<RelNode> newInputs = new ArrayList<>();
  final List<RelCollation> collations = new ArrayList<>();
  int offset = 0;
  for (Ord<RelNode> ord : Ord.zip(join.getInputs())) {
    RelTraitSet traits = ord.e.getTraitSet()
        .replace(EnumerableConvention.INSTANCE);
    if (!info.pairs().isEmpty()) {
      final List<RelFieldCollation> fieldCollations = new ArrayList<>();
      for (int key : info.keys().get(ord.i)) {
        fieldCollations.add(
            new RelFieldCollation(key, RelFieldCollation.Direction.ASCENDING,
                RelFieldCollation.NullDirection.LAST));
      }
      final RelCollation collation = RelCollations.of(fieldCollations);
      collations.add(RelCollations.shift(collation, offset));
      traits = traits.replace(collation);
    }
    newInputs.add(convert(ord.e, traits));
    offset += ord.e.getRowType().getFieldCount();
  }
  final RelNode left = newInputs.get(0);
  final RelNode right = newInputs.get(1);
  final RelOptCluster cluster = join.getCluster();
  RelNode newRel;

  RelTraitSet traitSet = join.getTraitSet()
      .replace(EnumerableConvention.INSTANCE);
  if (!collations.isEmpty()) {
    traitSet = traitSet.replace(collations);
  }
  // Re-arrange condition: first the equi-join elements, then the non-equi-join ones (if any);
  // this is not strictly necessary but it will be useful to avoid spurious errors in the
  // unit tests when verifying the plan.
  final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
  final RexNode equi = info.getEquiCondition(left, right, rexBuilder);
  final RexNode condition;
  if (info.isEqui()) {
    condition = equi;
  } else {
    final RexNode nonEqui = RexUtil.composeConjunction(rexBuilder, info.nonEquiConditions);
    condition = RexUtil.composeConjunction(rexBuilder, Arrays.asList(equi, nonEqui));
  }
  newRel = new EnumerableMergeJoin(cluster,
      traitSet,
      left,
      right,
      condition,
      join.getVariablesSet(),
      join.getJoinType());
  return newRel;
}