Java Code Examples for org.apache.calcite.plan.RelOptUtil#splitJoinCondition()

The following examples show how to use org.apache.calcite.plan.RelOptUtil#splitJoinCondition() . 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: DrillFilterJoinRules.java    From Bats with Apache License 2.0 6 votes vote down vote up
public boolean apply(Join join, JoinRelType joinType, RexNode exp) {
  if (joinType != JoinRelType.INNER) {
    return true;  // In OUTER join, we could not pull-up the filter.
                  // All we can do is keep the filter with JOIN, and
                  // then decide whether the filter could be pushed down
                  // into LEFT/RIGHT.
  }

  List<RexNode> tmpLeftKeys = new ArrayList<>();
  List<RexNode> tmpRightKeys = new ArrayList<>();
  List<RelDataTypeField> sysFields = new ArrayList<>();
  List<Integer> filterNulls = new ArrayList<>();

  RexNode remaining = RelOptUtil.splitJoinCondition(sysFields, join.getLeft(), join.getRight(),
      exp, tmpLeftKeys, tmpRightKeys, filterNulls, null);

  return remaining.isAlwaysTrue();
}
 
Example 2
Source File: RexTransformerTest.java    From calcite with Apache License 2.0 6 votes vote down vote up
/** Test case for
 * <a href="https://issues.apache.org/jira/browse/CALCITE-833">[CALCITE-833]
 * RelOptUtil.splitJoinCondition attempts to split a Join-Condition which
 * has a remaining condition</a>. */
@Test void testSplitJoinCondition() {
  final String sql = "select *\n"
      + "from emp a\n"
      + "INNER JOIN dept b\n"
      + "ON CAST(a.empno AS int) <> b.deptno";

  final RelNode relNode = toRel(sql);
  final LogicalProject project = (LogicalProject) relNode;
  final LogicalJoin join = (LogicalJoin) project.getInput(0);
  final List<RexNode> leftJoinKeys = new ArrayList<>();
  final List<RexNode> rightJoinKeys = new ArrayList<>();
  final ArrayList<RelDataTypeField> sysFieldList = new ArrayList<>();
  final RexNode remaining = RelOptUtil.splitJoinCondition(sysFieldList,
      join.getInputs().get(0),
      join.getInputs().get(1),
      join.getCondition(),
      leftJoinKeys,
      rightJoinKeys,
      null,
      null);

  assertThat(remaining.toString(), is("<>($0, $9)"));
  assertThat(leftJoinKeys.isEmpty(), is(true));
  assertThat(rightJoinKeys.isEmpty(), is(true));
}
 
Example 3
Source File: PigJoin.java    From calcite with Apache License 2.0 6 votes vote down vote up
/**
 * Constructs a Pig JOIN statement in the form of
 * <pre>
 * {@code
 * A = JOIN A BY f1 LEFT OUTER, B BY f2;
 * }
 * </pre>
 * Only supports simple equi-joins with single column on both sides of
 * <code>=</code>.
 */
private String getPigJoinStatement(Implementor implementor) {
  if (!getCondition().isA(SqlKind.EQUALS)) {
    throw new IllegalArgumentException("Only equi-join are supported");
  }
  List<RexNode> operands = ((RexCall) getCondition()).getOperands();
  if (operands.size() != 2) {
    throw new IllegalArgumentException("Only equi-join are supported");
  }
  List<Integer> leftKeys = new ArrayList<>(1);
  List<Integer> rightKeys = new ArrayList<>(1);
  List<Boolean> filterNulls = new ArrayList<>(1);
  RelOptUtil.splitJoinCondition(getLeft(), getRight(), getCondition(), leftKeys, rightKeys,
      filterNulls);

  String leftRelAlias = implementor.getPigRelationAlias((PigRel) getLeft());
  String rightRelAlias = implementor.getPigRelationAlias((PigRel) getRight());
  String leftJoinFieldName = implementor.getFieldName((PigRel) getLeft(), leftKeys.get(0));
  String rightJoinFieldName = implementor.getFieldName((PigRel) getRight(), rightKeys.get(0));

  return implementor.getPigRelationAlias((PigRel) getLeft()) + " = JOIN " + leftRelAlias + " BY "
      + leftJoinFieldName + ' ' + getPigJoinType() + ", " + rightRelAlias + " BY "
      + rightJoinFieldName + ';';
}
 
Example 4
Source File: JoinRelBase.java    From dremio-oss with Apache License 2.0 6 votes vote down vote up
protected JoinRelBase(RelOptCluster cluster, RelTraitSet traits, RelNode left, RelNode right, RexNode condition,
                      JoinRelType joinType, ImmutableBitSet projectedFields) {
  super(cluster, traits, left, right, condition, CorrelationId.setOf(Collections.emptySet()), joinType);
  leftKeys = Lists.newArrayList();
  rightKeys = Lists.newArrayList();
  filterNulls = Lists.newArrayList();

  remaining = RelOptUtil.splitJoinCondition(left, right, condition, leftKeys, rightKeys, filterNulls);
  joinCategory = getJoinCategory(condition, leftKeys, rightKeys, filterNulls, remaining);

  this.projectedFields = projectedFields;
  if (projectedFields != null) {
    List<RelDataType> fields = getRowType().getFieldList().stream().map(RelDataTypeField::getType).collect(Collectors.toList());
    List<String> names = ImmutableList.copyOf(getRowType().getFieldNames());
    inputRowType = cluster.getTypeFactory().createStructType(fields, names);
    rowType = JoinUtils.rowTypeFromProjected(left, right, getRowType(), projectedFields, cluster.getTypeFactory());
  }
}
 
Example 5
Source File: ElasticsearchJoin.java    From dk-fitting with Apache License 2.0 6 votes vote down vote up
public void implement(Implementor implementor) {
    implementor.visitChild(0, getLeft());
    implementor.visitChild(0, getRight());
    if (!getCondition().isA(SqlKind.EQUALS)) {
        throw new IllegalArgumentException("Only equi-join are supported");
    }
    List<RexNode> operands = ((RexCall) getCondition()).getOperands();
    if (operands.size() != 2) {
        throw new IllegalArgumentException("Only equi-join are supported");
    }
    List<Integer> leftKeys = new ArrayList<Integer>(1);
    List<Integer> rightKeys = new ArrayList<Integer>(1);
    List<Boolean> filterNulls = new ArrayList<Boolean>(1);
    RexNode rexNode = RelOptUtil.splitJoinCondition(getLeft(), getRight(), getCondition(), leftKeys, rightKeys,
            filterNulls);
    String leftRelAlias = implementor.getElasticsearchRelationAlias((ElasticsearchRelNode) getLeft());
    String rightRelAlias = implementor.getElasticsearchRelationAlias((ElasticsearchRelNode) getRight());
    String leftJoinFieldName = implementor.getFieldName((ElasticsearchRelNode) getLeft(), leftKeys.get(0));
    String rightJoinFieldName = implementor.getFieldName((ElasticsearchRelNode) getRight(), rightKeys.get(0));
    String result = implementor.getElasticsearchRelationAlias((ElasticsearchRelNode) getLeft())
            + " = JOIN " + leftRelAlias + " BY "+ leftJoinFieldName + ' ' + getElasticsearchJoinType() + ", " +
            rightRelAlias + " BY "+ rightJoinFieldName + ';';
    System.out.println("implementor = " + result);
}
 
Example 6
Source File: ElasticsearchJoin.java    From dk-fitting with Apache License 2.0 6 votes vote down vote up
public void implement(Implementor implementor) {
    implementor.visitChild(0, getLeft());
    implementor.visitChild(0, getRight());
    if (!getCondition().isA(SqlKind.EQUALS)) {
        throw new IllegalArgumentException("Only equi-join are supported");
    }
    List<RexNode> operands = ((RexCall) getCondition()).getOperands();
    if (operands.size() != 2) {
        throw new IllegalArgumentException("Only equi-join are supported");
    }
    List<Integer> leftKeys = new ArrayList<Integer>(1);
    List<Integer> rightKeys = new ArrayList<Integer>(1);
    List<Boolean> filterNulls = new ArrayList<Boolean>(1);
    RexNode rexNode = RelOptUtil.splitJoinCondition(getLeft(), getRight(), getCondition(), leftKeys, rightKeys,
            filterNulls);
    String leftRelAlias = implementor.getElasticsearchRelationAlias((ElasticsearchRelNode) getLeft());
    String rightRelAlias = implementor.getElasticsearchRelationAlias((ElasticsearchRelNode) getRight());
    String leftJoinFieldName = implementor.getFieldName((ElasticsearchRelNode) getLeft(), leftKeys.get(0));
    String rightJoinFieldName = implementor.getFieldName((ElasticsearchRelNode) getRight(), rightKeys.get(0));
    String result = implementor.getElasticsearchRelationAlias((ElasticsearchRelNode) getLeft())
            + " = JOIN " + leftRelAlias + " BY "+ leftJoinFieldName + ' ' + getElasticsearchJoinType() + ", " +
            rightRelAlias + " BY "+ rightJoinFieldName + ';';
    System.out.println("implementor = " + result);
}
 
Example 7
Source File: JoinUtils.java    From Bats with Apache License 2.0 6 votes vote down vote up
public static JoinCategory getJoinCategory(RelNode left, RelNode right, RexNode condition,
    List<Integer> leftKeys, List<Integer> rightKeys, List<Boolean> filterNulls) {
  if (condition.isAlwaysTrue()) {
    return JoinCategory.CARTESIAN;
  }
  leftKeys.clear();
  rightKeys.clear();
  filterNulls.clear();
  RexNode remaining = RelOptUtil.splitJoinCondition(left, right, condition, leftKeys, rightKeys, filterNulls);

  if (!remaining.isAlwaysTrue() || (leftKeys.size() == 0 || rightKeys.size() == 0) ) {
    // for practical purposes these cases could be treated as inequality
    return JoinCategory.INEQUALITY;
  }
  return JoinCategory.EQUALITY;
}
 
Example 8
Source File: JoinInfo.java    From Bats with Apache License 2.0 6 votes vote down vote up
/** Creates a {@code JoinInfo} by analyzing a condition. */
public static JoinInfo of(RelNode left, RelNode right, RexNode condition) {
  final List<Integer> leftKeys = new ArrayList<>();
  final List<Integer> rightKeys = new ArrayList<>();
  final List<Boolean> filterNulls = new ArrayList<>();
  RexNode remaining =
      RelOptUtil.splitJoinCondition(left, right, condition, leftKeys,
          rightKeys, filterNulls);
  if (remaining.isAlwaysTrue()) {
    return new EquiJoinInfo(ImmutableIntList.copyOf(leftKeys),
        ImmutableIntList.copyOf(rightKeys));
  } else {
    return new NonEquiJoinInfo(ImmutableIntList.copyOf(leftKeys),
        ImmutableIntList.copyOf(rightKeys), remaining);
  }
}
 
Example 9
Source File: JoinFilterCanonicalizationRule.java    From dremio-oss with Apache License 2.0 5 votes vote down vote up
/**
 * Create a join operator with a canonicalized version of {@code joinCondition}
 *
 * @param builder
 * @param joinType
 * @param joinCondition
 * @param left
 * @param right
 * @return the new join operator, or {@code null} if {@code joinCondition} hasn't changed.
 */
private RelNode canonicalizeJoinCondition(
    RelBuilder builder,
    JoinRelType joinType,
    RexNode joinCondition,
    RelNode left,
    RelNode right) {
  final List<Integer> leftKeys = Lists.newArrayList();
  final List<Integer> rightKeys = Lists.newArrayList();
  final List<Boolean> filterNulls = Lists.newArrayList();

  final RexNode remaining = RelOptUtil.splitJoinCondition(left, right, joinCondition, leftKeys, rightKeys, filterNulls);

  // Create a normalized join condition
  final RexNode newPartialJoinCondition = buildJoinCondition(builder.getRexBuilder(), left.getRowType(), right.getRowType(), leftKeys, rightKeys, filterNulls);
  // Add the remaining filter condition
  final RexNode newJoinCondition = RelOptUtil.andJoinFilters(builder.getRexBuilder(), newPartialJoinCondition, remaining);

  // terminate if the same condition as previously
  if (RexUtil.eq(joinCondition, newJoinCondition)) {
    return null;
  }

  builder.pushAll(ImmutableList.of(left, right));
  builder.join(joinType, newJoinCondition);

  return builder.build();
}
 
Example 10
Source File: JoinUtils.java    From Bats with Apache License 2.0 5 votes vote down vote up
/**
   * Check if the given RelNode contains any Cartesian join.
   * Return true if find one. Otherwise, return false.
   *
   * @param relNode     the RelNode to be inspected.
   * @param leftKeys    a list used for the left input into the join which has
   *                    equi-join keys. It can be empty or not (but not null),
   *                    this method will clear this list before using it.
   * @param rightKeys   a list used for the right input into the join which has
   *                    equi-join keys. It can be empty or not (but not null),
   *                    this method will clear this list before using it.
   * @param filterNulls The join key positions for which null values will not
   *                    match.
   * @return            Return true if the given relNode contains Cartesian join.
   *                    Otherwise, return false
   */
public static boolean checkCartesianJoin(RelNode relNode, List<Integer> leftKeys, List<Integer> rightKeys, List<Boolean> filterNulls) {
  if (relNode instanceof Join) {
    leftKeys.clear();
    rightKeys.clear();

    Join joinRel = (Join) relNode;
    RelNode left = joinRel.getLeft();
    RelNode right = joinRel.getRight();

    RexNode remaining = RelOptUtil.splitJoinCondition(left, right, joinRel.getCondition(), leftKeys, rightKeys, filterNulls);
    if (joinRel.getJoinType() == JoinRelType.INNER) {
      if (leftKeys.isEmpty() || rightKeys.isEmpty()) {
        return true;
      }
    } else {
      if (!remaining.isAlwaysTrue() || leftKeys.isEmpty() || rightKeys.isEmpty()) {
        return true;
      }
    }
  }

  for (RelNode child : relNode.getInputs()) {
    if (checkCartesianJoin(child, leftKeys, rightKeys, filterNulls)) {
      return true;
    }
  }

  return false;
}
 
Example 11
Source File: JoinUtils.java    From dremio-oss with Apache License 2.0 5 votes vote down vote up
/**
   * Check if the given RelNode contains any Cartesian join.
   * Return true if find one. Otherwise, return false.
   *
   * @param relNode   the RelNode to be inspected.
   * @param leftKeys  a list used for the left input into the join which has
   *                  equi-join keys. It can be empty or not (but not null),
   *                  this method will clear this list before using it.
   * @param rightKeys a list used for the right input into the join which has
   *                  equi-join keys. It can be empty or not (but not null),
   *                  this method will clear this list before using it.
   * @param filterNulls   The join key positions for which null values will not
   *                      match. null values only match for the "is not distinct
   *                      from" condition.
   * @return          Return true if the given relNode contains Cartesian join.
   *                  Otherwise, return false
   */
public static boolean checkCartesianJoin(RelNode relNode, List<Integer> leftKeys, List<Integer> rightKeys, List<Boolean> filterNulls) {
  if (relNode instanceof Join) {
    leftKeys.clear();
    rightKeys.clear();

    Join joinRel = (Join) relNode;
    RelNode left = joinRel.getLeft();
    RelNode right = joinRel.getRight();

    RexNode remaining = RelOptUtil.splitJoinCondition(left, right, joinRel.getCondition(), leftKeys, rightKeys, filterNulls);
    if(joinRel.getJoinType() == JoinRelType.INNER) {
      if(leftKeys.isEmpty() || rightKeys.isEmpty()) {
        return true;
      }
    } else {
      if(!remaining.isAlwaysTrue() || leftKeys.isEmpty() || rightKeys.isEmpty()) {
        return true;
      }
    }
  }

  for (RelNode child : relNode.getInputs()) {
    if(checkCartesianJoin(child, leftKeys, rightKeys, filterNulls)) {
      return true;
    }
  }

  return false;
}
 
Example 12
Source File: ApexRelNode.java    From attic-apex-malhar with Apache License 2.0 5 votes vote down vote up
@Override
public RelInfo visit(RelContext context, RelNode node, List<RelInfo> inputStreams)
{
  Join join = (Join)node;
  if (inputStreams.size() != 2) {
    throw new UnsupportedOperationException("Join is a BiRel");
  }

  if ((join.getJoinType() == JoinRelType.FULL) || (join.getJoinType() == JoinRelType.LEFT) ||
      (join.getJoinType() == JoinRelType.RIGHT)) {
    throw new UnsupportedOperationException("Outer joins are not supported");
  }

  final List<Integer> leftKeys = new ArrayList<>();
  final List<Integer> rightKeys = new ArrayList<>();

  RexNode remaining =
      RelOptUtil.splitJoinCondition(join.getLeft(), join.getRight(), join.getCondition(), leftKeys, rightKeys);

  if (leftKeys.size() != rightKeys.size()) {
    throw new RuntimeException("Unexpected condition reached. Left and right condition count should be same");
  }

  if (leftKeys.size() == 0) {
    throw new UnsupportedOperationException("Theta joins are not supported.");
  }

  RelInfo relInfo = addInnerJoinOperator(join, leftKeys, rightKeys, context);

  if (!remaining.isAlwaysTrue()) {
    relInfo = addJoinFilter(join, remaining, relInfo, context);
  }

  return relInfo;
}
 
Example 13
Source File: DrillFilterJoinRules.java    From Bats with Apache License 2.0 5 votes vote down vote up
public boolean apply(Join join, JoinRelType joinType, RexNode exp) {
  if (joinType != JoinRelType.INNER) {
    return true;
  }

  List<Integer> tmpLeftKeys = new ArrayList<>();
  List<Integer> tmpRightKeys = new ArrayList<>();
  List<Boolean> filterNulls = new ArrayList<>();

  RexNode remaining =
      RelOptUtil.splitJoinCondition(join.getLeft(), join.getRight(), exp, tmpLeftKeys, tmpRightKeys, filterNulls);

  return remaining.isAlwaysTrue();
}
 
Example 14
Source File: JoinInfo.java    From calcite with Apache License 2.0 5 votes vote down vote up
/** Creates a {@code JoinInfo} by analyzing a condition. */
public static JoinInfo of(RelNode left, RelNode right, RexNode condition) {
  final List<Integer> leftKeys = new ArrayList<>();
  final List<Integer> rightKeys = new ArrayList<>();
  final List<Boolean> filterNulls = new ArrayList<>();
  final List<RexNode> nonEquiList = new ArrayList<>();
  RelOptUtil.splitJoinCondition(left, right, condition, leftKeys, rightKeys,
      filterNulls, nonEquiList);
  return new JoinInfo(ImmutableIntList.copyOf(leftKeys),
      ImmutableIntList.copyOf(rightKeys), ImmutableList.copyOf(nonEquiList));
}
 
Example 15
Source File: RelMdRowCount.java    From dremio-oss with Apache License 2.0 4 votes vote down vote up
public static double estimateRowCount(Join rel, RelMetadataQuery mq) {
  double rightJoinFactor = 1.0;

  RexNode condition = rel.getCondition();
  if (condition.isAlwaysTrue()) {
    // Cartesian join is only supported for NLJ. If join type is right, make it more expensive
    if (rel.getJoinType() == JoinRelType.RIGHT) {
      rightJoinFactor = 2.0;
    }
    return RelMdUtil.getJoinRowCount(mq, rel, condition) * rightJoinFactor;
  }

  final PlannerSettings plannerSettings = PrelUtil.getPlannerSettings(rel.getCluster().getPlanner());
  double filterMinSelectivityEstimateFactor = plannerSettings == null ?
    PlannerSettings.DEFAULT_FILTER_MIN_SELECTIVITY_ESTIMATE_FACTOR :
    plannerSettings.getFilterMinSelectivityEstimateFactor();
  double filterMaxSelectivityEstimateFactor = plannerSettings == null ?
    PlannerSettings.DEFAULT_FILTER_MAX_SELECTIVITY_ESTIMATE_FACTOR :
    plannerSettings.getFilterMaxSelectivityEstimateFactor();

  final RexNode remaining;
  if (rel instanceof JoinRelBase) {
    remaining = ((JoinRelBase) rel).getRemaining();
  } else {
    remaining = RelOptUtil.splitJoinCondition(rel.getLeft(), rel.getRight(), condition, new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
  }

  double selectivity = mq.getSelectivity(rel, remaining);
  if (!remaining.isAlwaysFalse()) {
    // Cap selectivity at filterMinSelectivityEstimateFactor unless it is always FALSE
    if (selectivity < filterMinSelectivityEstimateFactor) {
      selectivity = filterMinSelectivityEstimateFactor;
    }
  }

  if (!remaining.isAlwaysTrue()) {
    // Cap selectivity at filterMaxSelectivityEstimateFactor unless it is always TRUE
    if (selectivity > filterMaxSelectivityEstimateFactor) {
      selectivity = filterMaxSelectivityEstimateFactor;
    }
    // Make right join more expensive for inequality join condition (logical phase)
    if (rel.getJoinType() == JoinRelType.RIGHT) {
      rightJoinFactor = 2.0;
    }
  }

  return selectivity * Math.max(mq.getRowCount(rel.getLeft()), mq.getRowCount(rel.getRight())) * rightJoinFactor;
}
 
Example 16
Source File: JoinNormalizationRule.java    From dremio-oss with Apache License 2.0 4 votes vote down vote up
/**
 * Attempt to create a new join with a canonicalized join expression, and a possible filter
 * on top
 * @param builder
 * @param join
 * @return a new join tree (or same as original argument if no change)
 */
private RelNode getNewJoinCondition(RelBuilder builder, Join join) {
  final List<Integer> leftKeys = Lists.newArrayList();
  final List<Integer> rightKeys = Lists.newArrayList();
  final List<Boolean> filterNulls = Lists.newArrayList();

  final RexNode remaining = RelOptUtil.splitJoinCondition(join.getLeft(), join.getRight(), join.getCondition(), leftKeys, rightKeys, filterNulls);
  final boolean hasEquiJoins = leftKeys.size() == rightKeys.size() && leftKeys.size() > 0 ;
  final JoinRelType joinType = join.getJoinType();

  // If join has no equi-join condition, do not transform
  if (!hasEquiJoins) {
    return join;
  }

  // Create a new partial condition for the equi-join
  final RexNode partialCondition = JoinFilterCanonicalizationRule.buildJoinCondition(
      builder.getRexBuilder(),
      join.getLeft().getRowType(),
      join.getRight().getRowType(),
      leftKeys,
      rightKeys,
      filterNulls);

  // We do not know how to add filter for non-INNER joins (see DRILL-1337)
  if (joinType != JoinRelType.INNER) {
    final RexNode newJoinCondition = RexUtil.composeConjunction(builder.getRexBuilder(), ImmutableList.of(partialCondition, remaining), false);
    if (RexUtil.eq(join.getCondition(), newJoinCondition)) {
      // Condition is the same, do not create a new rel
      return join;
    }
    builder.pushAll(ImmutableList.of(join.getLeft(), join.getRight()));
    builder.join(joinType, newJoinCondition);

    return builder.build();
  }

  // Check if join condition has changed if pure equi-join
  if (remaining.isAlwaysTrue() && RexUtil.eq(join.getCondition(), partialCondition)) {
    return join;
  }

  // Return the new join with a filter on top
  builder.pushAll(ImmutableList.of(join.getLeft(), join.getRight()));
  builder.join(joinType, partialCondition);
  builder.filter(remaining);
  return builder.build();
}
 
Example 17
Source File: ProjectJoinRemoveRule.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 join = call.rel(1);
  final boolean isLeftJoin = join.getJoinType() == JoinRelType.LEFT;
  int lower = isLeftJoin
      ? join.getLeft().getRowType().getFieldCount() - 1 : 0;
  int upper = isLeftJoin
      ? join.getRowType().getFieldCount()
      : join.getLeft().getRowType().getFieldCount();

  // Check whether the project uses columns whose index is between
  // lower(included) and upper(excluded).
  for (RexNode expr: project.getProjects()) {
    if (RelOptUtil.InputFinder.bits(expr).asList().stream().anyMatch(
        i -> i >= lower && i < upper)) {
      return;
    }
  }

  final List<Integer> leftKeys = new ArrayList<>();
  final List<Integer> rightKeys = new ArrayList<>();
  RelOptUtil.splitJoinCondition(join.getLeft(), join.getRight(),
      join.getCondition(), leftKeys, rightKeys,
      new ArrayList<>());

  final List<Integer> joinKeys = isLeftJoin ? rightKeys : leftKeys;
  final ImmutableBitSet.Builder columns = ImmutableBitSet.builder();
  joinKeys.forEach(key -> columns.set(key));

  final RelMetadataQuery mq = call.getMetadataQuery();
  if (!mq.areColumnsUnique(isLeftJoin ? join.getRight() : join.getLeft(),
      columns.build())) {
    return;
  }

  RelNode node;
  if (isLeftJoin) {
    node = project
        .copy(project.getTraitSet(), join.getLeft(), project.getProjects(),
            project.getRowType());
  } else {
    final int offset = join.getLeft().getRowType().getFieldCount();
    final List<RexNode> newExprs = project.getProjects().stream()
        .map(expr -> RexUtil.shift(expr, -offset))
        .collect(Collectors.toList());
    node = project.copy(project.getTraitSet(), join.getRight(), newExprs,
        project.getRowType());
  }
  call.transformTo(node);
}
 
Example 18
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 19
Source File: AggregateJoinJoinRemoveRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
@Override public void onMatch(RelOptRuleCall call) {
  final Aggregate aggregate = call.rel(0);
  final Join topJoin = call.rel(1);
  final Join bottomJoin = call.rel(2);
  int leftBottomChildSize = bottomJoin.getLeft().getRowType()
      .getFieldCount();

  // Check whether the aggregate uses columns in the right input of
  // bottom join.
  final Set<Integer> allFields = RelOptUtil.getAllFields(aggregate);
  if (allFields.stream().anyMatch(i -> i >= leftBottomChildSize
      && i < bottomJoin.getRowType().getFieldCount())) {
    return;
  }

  if (aggregate.getAggCallList().stream().anyMatch(aggregateCall ->
      !aggregateCall.isDistinct())) {
    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<>();
  RelOptUtil.splitJoinCondition(bottomJoin.getLeft(), bottomJoin.getRight(),
      bottomJoin.getCondition(), leftChildKeys, new ArrayList<>(),
      new ArrayList<>());
  if (!leftKeys.equals(leftChildKeys)) {
    return;
  }

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

  final Map<Integer, Integer> map = new HashMap<>();
  allFields.forEach(
      index ->
          map.put(index,
              index < leftBottomChildSize ? index : index - offset));
  final ImmutableBitSet groupSet = aggregate.getGroupSet().permute(map);

  final ImmutableList.Builder<AggregateCall> aggCalls =
      ImmutableList.builder();
  final int sourceCount = aggregate.getInput().getRowType().getFieldCount();
  final Mappings.TargetMapping targetMapping =
      Mappings.target(map, sourceCount, sourceCount);
  aggregate.getAggCallList().forEach(
      aggregateCall ->
          aggCalls.add(aggregateCall.transform(targetMapping)));

  RelNode newAggregate = relBuilder.push(join)
      .aggregate(relBuilder.groupKey(groupSet), aggCalls.build())
      .build();

  call.transformTo(newAggregate);
}