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

The following examples show how to use org.apache.calcite.rel.core.Join#getLeft() . 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: RelMdSize.java    From Bats with Apache License 2.0 6 votes vote down vote up
private List<Double> averageJoinColumnSizes(Join rel, RelMetadataQuery mq,
    boolean semijoin) {
  final RelNode left = rel.getLeft();
  final RelNode right = rel.getRight();
  final List<Double> lefts = mq.getAverageColumnSizes(left);
  final List<Double> rights =
      semijoin ? null : mq.getAverageColumnSizes(right);
  if (lefts == null && rights == null) {
    return null;
  }
  final int fieldCount = rel.getRowType().getFieldCount();
  Double[] sizes = new Double[fieldCount];
  if (lefts != null) {
    lefts.toArray(sizes);
  }
  if (rights != null) {
    final int leftCount = left.getRowType().getFieldCount();
    for (int i = 0; i < rights.size(); i++) {
      sizes[leftCount + i] = rights.get(i);
    }
  }
  return ImmutableNullableList.copyOf(sizes);
}
 
Example 2
Source File: LoptOptimizeJoinRule.java    From calcite with Apache License 2.0 6 votes vote down vote up
/**
 * Determines whether a join is a removable self-join. It is if it's an
 * inner join between identical, simple factors and the equality portion of
 * the join condition consists of the same set of unique keys.
 *
 * @param joinRel the join
 *
 * @return true if the join is removable
 */
public static boolean isRemovableSelfJoin(Join joinRel) {
  final RelNode left = joinRel.getLeft();
  final RelNode right = joinRel.getRight();

  if (joinRel.getJoinType().isOuterJoin()) {
    return false;
  }

  // Make sure the join is between the same simple factor
  final RelMetadataQuery mq = joinRel.getCluster().getMetadataQuery();
  final RelOptTable leftTable = mq.getTableOrigin(left);
  if (leftTable == null) {
    return false;
  }
  final RelOptTable rightTable = mq.getTableOrigin(right);
  if (rightTable == null) {
    return false;
  }
  if (!leftTable.getQualifiedName().equals(rightTable.getQualifiedName())) {
    return false;
  }

  // Determine if the join keys are identical and unique
  return areSelfJoinKeysUnique(mq, left, right, joinRel.getCondition());
}
 
Example 3
Source File: RelMdSize.java    From calcite with Apache License 2.0 6 votes vote down vote up
private List<Double> averageJoinColumnSizes(Join rel, RelMetadataQuery mq) {
  boolean semiOrAntijoin = !rel.getJoinType().projectsRight();
  final RelNode left = rel.getLeft();
  final RelNode right = rel.getRight();
  final List<Double> lefts = mq.getAverageColumnSizes(left);
  final List<Double> rights =
      semiOrAntijoin ? null : mq.getAverageColumnSizes(right);
  if (lefts == null && rights == null) {
    return null;
  }
  final int fieldCount = rel.getRowType().getFieldCount();
  Double[] sizes = new Double[fieldCount];
  if (lefts != null) {
    lefts.toArray(sizes);
  }
  if (rights != null) {
    final int leftCount = left.getRowType().getFieldCount();
    for (int i = 0; i < rights.size(); i++) {
      sizes[leftCount + i] = rights.get(i);
    }
  }
  return ImmutableNullableList.copyOf(sizes);
}
 
Example 4
Source File: JoinRule.java    From dremio-oss with Apache License 2.0 6 votes vote down vote up
@Override
public void onMatch(RelOptRuleCall call) {
  final Join join = call.rel(0);
  final RelNode left = join.getLeft();
  final RelNode right = join.getRight();
  final RelNode convertedLeft = convertIfNecessary(left);
  final RelNode convertedRight = convertIfNecessary(right);
  final RelBuilder builder = factory.create(join.getCluster(), null);

  builder.pushAll(ImmutableList.of(convertedLeft, convertedRight));
  builder.join(join.getJoinType(), join.getCondition());

  final RelNode newJoin = builder.build();
  if(newJoin != null) {
    call.transformTo(newJoin);
  }
}
 
Example 5
Source File: StreamRules.java    From Bats with Apache License 2.0 6 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Delta delta = call.rel(0);
  Util.discard(delta);
  final Join join = call.rel(1);
  final RelNode left = join.getLeft();
  final RelNode right = join.getRight();

  final LogicalDelta rightWithDelta = LogicalDelta.create(right);
  final LogicalJoin joinL = LogicalJoin.create(left, rightWithDelta,
      join.getCondition(), join.getVariablesSet(), join.getJoinType(),
      join.isSemiJoinDone(),
      ImmutableList.copyOf(join.getSystemFieldList()));

  final LogicalDelta leftWithDelta = LogicalDelta.create(left);
  final LogicalJoin joinR = LogicalJoin.create(leftWithDelta, right,
      join.getCondition(), join.getVariablesSet(), join.getJoinType(),
      join.isSemiJoinDone(),
      ImmutableList.copyOf(join.getSystemFieldList()));

  List<RelNode> inputsToUnion = new ArrayList<>();
  inputsToUnion.add(joinL);
  inputsToUnion.add(joinR);

  final LogicalUnion newNode = LogicalUnion.create(inputsToUnion, true);
  call.transformTo(newNode);
}
 
Example 6
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 7
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 8
Source File: StreamRules.java    From calcite with Apache License 2.0 5 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Delta delta = call.rel(0);
  Util.discard(delta);
  final Join join = call.rel(1);
  final RelNode left = join.getLeft();
  final RelNode right = join.getRight();

  final LogicalDelta rightWithDelta = LogicalDelta.create(right);
  final LogicalJoin joinL = LogicalJoin.create(left,
      rightWithDelta,
      join.getHints(),
      join.getCondition(),
      join.getVariablesSet(),
      join.getJoinType(),
      join.isSemiJoinDone(),
      ImmutableList.copyOf(join.getSystemFieldList()));

  final LogicalDelta leftWithDelta = LogicalDelta.create(left);
  final LogicalJoin joinR = LogicalJoin.create(leftWithDelta,
      right,
      join.getHints(),
      join.getCondition(),
      join.getVariablesSet(),
      join.getJoinType(),
      join.isSemiJoinDone(),
      ImmutableList.copyOf(join.getSystemFieldList()));

  List<RelNode> inputsToUnion = new ArrayList<>();
  inputsToUnion.add(joinL);
  inputsToUnion.add(joinR);

  final LogicalUnion newNode = LogicalUnion.create(inputsToUnion, true);
  call.transformTo(newNode);
}
 
Example 9
Source File: RelMdAllPredicates.java    From Bats with Apache License 2.0 4 votes vote down vote up
/**
 * Add the Join condition to the list obtained from the input.
 */
public RelOptPredicateList getAllPredicates(Join join, RelMetadataQuery mq) {
  if (join.getJoinType() != JoinRelType.INNER) {
    // We cannot map origin of this expression.
    return null;
  }

  final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
  final RexNode pred = join.getCondition();

  final Multimap<List<String>, RelTableRef> qualifiedNamesToRefs = HashMultimap.create();
  RelOptPredicateList newPreds = RelOptPredicateList.EMPTY;
  for (RelNode input : join.getInputs()) {
    final RelOptPredicateList inputPreds = mq.getAllPredicates(input);
    if (inputPreds == null) {
      // Bail out
      return null;
    }
    // Gather table references
    final Set<RelTableRef> tableRefs = mq.getTableReferences(input);
    if (input == join.getLeft()) {
      // Left input references remain unchanged
      for (RelTableRef leftRef : tableRefs) {
        qualifiedNamesToRefs.put(leftRef.getQualifiedName(), leftRef);
      }
      newPreds = newPreds.union(rexBuilder, inputPreds);
    } else {
      // Right input references might need to be updated if there are table name
      // clashes with left input
      final Map<RelTableRef, RelTableRef> currentTablesMapping = new HashMap<>();
      for (RelTableRef rightRef : tableRefs) {
        int shift = 0;
        Collection<RelTableRef> lRefs = qualifiedNamesToRefs.get(
            rightRef.getQualifiedName());
        if (lRefs != null) {
          shift = lRefs.size();
        }
        currentTablesMapping.put(rightRef,
            RelTableRef.of(rightRef.getTable(), shift + rightRef.getEntityNumber()));
      }
      final List<RexNode> updatedPreds = Lists.newArrayList(
          Iterables.transform(inputPreds.pulledUpPredicates,
              e -> RexUtil.swapTableReferences(rexBuilder, e,
                  currentTablesMapping)));
      newPreds = newPreds.union(rexBuilder,
          RelOptPredicateList.of(rexBuilder, updatedPreds));
    }
  }

  // Extract input fields referenced by Join condition
  final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<>();
  final RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(inputExtraFields);
  pred.accept(inputFinder);
  final ImmutableBitSet inputFieldsUsed = inputFinder.inputBitSet.build();

  // Infer column origin expressions for given references
  final Map<RexInputRef, Set<RexNode>> mapping = new LinkedHashMap<>();
  for (int idx : inputFieldsUsed) {
    final RexInputRef inputRef = RexInputRef.of(idx, join.getRowType().getFieldList());
    final Set<RexNode> originalExprs = mq.getExpressionLineage(join, inputRef);
    if (originalExprs == null) {
      // Bail out
      return null;
    }
    final RexInputRef ref = RexInputRef.of(idx, join.getRowType().getFieldList());
    mapping.put(ref, originalExprs);
  }

  // Replace with new expressions and return union of predicates
  final Set<RexNode> allExprs =
      RelMdExpressionLineage.createAllPossibleExpressions(rexBuilder, pred, mapping);
  if (allExprs == null) {
    return null;
  }
  return newPreds.union(rexBuilder, RelOptPredicateList.of(rexBuilder, allExprs));
}
 
Example 10
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 11
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 12
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 13
Source File: RelDecorrelator.java    From calcite 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(
    Join join,
    Project 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 14
Source File: RelMdColumnUniqueness.java    From Bats with Apache License 2.0 4 votes vote down vote up
public Boolean areColumnsUnique(Join rel, RelMetadataQuery mq,
    ImmutableBitSet columns, boolean ignoreNulls) {
  if (columns.cardinality() == 0) {
    return false;
  }

  final RelNode left = rel.getLeft();
  final RelNode right = rel.getRight();

  // Divide up the input column mask into column masks for the left and
  // right sides of the join
  final Pair<ImmutableBitSet, ImmutableBitSet> leftAndRightColumns =
      splitLeftAndRightColumns(rel.getLeft().getRowType().getFieldCount(),
          columns);
  final ImmutableBitSet leftColumns = leftAndRightColumns.left;
  final ImmutableBitSet rightColumns = leftAndRightColumns.right;

  // If the original column mask contains columns from both the left and
  // right hand side, then the columns are unique if and only if they're
  // unique for their respective join inputs
  Boolean leftUnique = mq.areColumnsUnique(left, leftColumns, ignoreNulls);
  Boolean rightUnique = mq.areColumnsUnique(right, rightColumns, ignoreNulls);
  if ((leftColumns.cardinality() > 0)
      && (rightColumns.cardinality() > 0)) {
    if ((leftUnique == null) || (rightUnique == null)) {
      return null;
    } else {
      return leftUnique && rightUnique;
    }
  }

  // If we're only trying to determine uniqueness for columns that
  // originate from one join input, then determine if the equijoin
  // columns from the other join input are unique.  If they are, then
  // the columns are unique for the entire join if they're unique for
  // the corresponding join input, provided that input is not null
  // generating.
  final JoinInfo joinInfo = rel.analyzeCondition();
  if (leftColumns.cardinality() > 0) {
    if (rel.getJoinType().generatesNullsOnLeft()) {
      return false;
    }
    Boolean rightJoinColsUnique =
        mq.areColumnsUnique(right, joinInfo.rightSet(), ignoreNulls);
    if ((rightJoinColsUnique == null) || (leftUnique == null)) {
      return null;
    }
    return rightJoinColsUnique && leftUnique;
  } else if (rightColumns.cardinality() > 0) {
    if (rel.getJoinType().generatesNullsOnRight()) {
      return false;
    }
    Boolean leftJoinColsUnique =
        mq.areColumnsUnique(left, joinInfo.leftSet(), ignoreNulls);
    if ((leftJoinColsUnique == null) || (rightUnique == null)) {
      return null;
    }
    return leftJoinColsUnique && rightUnique;
  }

  throw new AssertionError();
}
 
Example 15
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 16
Source File: RelMdUniqueKeys.java    From Bats with Apache License 2.0 4 votes vote down vote up
public Set<ImmutableBitSet> getUniqueKeys(Join rel, RelMetadataQuery mq,
    boolean ignoreNulls) {
  final RelNode left = rel.getLeft();
  final RelNode right = rel.getRight();

  // first add the different combinations of concatenated unique keys
  // from the left and the right, adjusting the right hand side keys to
  // reflect the addition of the left hand side
  //
  // NOTE zfong 12/18/06 - If the number of tables in a join is large,
  // the number of combinations of unique key sets will explode.  If
  // that is undesirable, use RelMetadataQuery.areColumnsUnique() as
  // an alternative way of getting unique key information.

  final Set<ImmutableBitSet> retSet = new HashSet<>();
  final Set<ImmutableBitSet> leftSet = mq.getUniqueKeys(left, ignoreNulls);
  Set<ImmutableBitSet> rightSet = null;

  final Set<ImmutableBitSet> tmpRightSet = mq.getUniqueKeys(right, ignoreNulls);
  int nFieldsOnLeft = left.getRowType().getFieldCount();

  if (tmpRightSet != null) {
    rightSet = new HashSet<>();
    for (ImmutableBitSet colMask : tmpRightSet) {
      ImmutableBitSet.Builder tmpMask = ImmutableBitSet.builder();
      for (int bit : colMask) {
        tmpMask.set(bit + nFieldsOnLeft);
      }
      rightSet.add(tmpMask.build());
    }

    if (leftSet != null) {
      for (ImmutableBitSet colMaskRight : rightSet) {
        for (ImmutableBitSet colMaskLeft : leftSet) {
          retSet.add(colMaskLeft.union(colMaskRight));
        }
      }
    }
  }

  // locate the columns that participate in equijoins
  final JoinInfo joinInfo = rel.analyzeCondition();

  // determine if either or both the LHS and RHS are unique on the
  // equijoin columns
  final Boolean leftUnique =
      mq.areColumnsUnique(left, joinInfo.leftSet(), ignoreNulls);
  final Boolean rightUnique =
      mq.areColumnsUnique(right, joinInfo.rightSet(), ignoreNulls);

  // if the right hand side is unique on its equijoin columns, then we can
  // add the unique keys from left if the left hand side is not null
  // generating
  if ((rightUnique != null)
      && rightUnique
      && (leftSet != null)
      && !(rel.getJoinType().generatesNullsOnLeft())) {
    retSet.addAll(leftSet);
  }

  // same as above except left and right are reversed
  if ((leftUnique != null)
      && leftUnique
      && (rightSet != null)
      && !(rel.getJoinType().generatesNullsOnRight())) {
    retSet.addAll(rightSet);
  }

  return retSet;
}
 
Example 17
Source File: RelMdUniqueKeys.java    From calcite with Apache License 2.0 4 votes vote down vote up
public Set<ImmutableBitSet> getUniqueKeys(Join rel, RelMetadataQuery mq,
    boolean ignoreNulls) {
  if (!rel.getJoinType().projectsRight()) {
    // only return the unique keys from the LHS since a semijoin only
    // returns the LHS
    return mq.getUniqueKeys(rel.getLeft(), ignoreNulls);
  }

  final RelNode left = rel.getLeft();
  final RelNode right = rel.getRight();

  // first add the different combinations of concatenated unique keys
  // from the left and the right, adjusting the right hand side keys to
  // reflect the addition of the left hand side
  //
  // NOTE zfong 12/18/06 - If the number of tables in a join is large,
  // the number of combinations of unique key sets will explode.  If
  // that is undesirable, use RelMetadataQuery.areColumnsUnique() as
  // an alternative way of getting unique key information.

  final Set<ImmutableBitSet> retSet = new HashSet<>();
  final Set<ImmutableBitSet> leftSet = mq.getUniqueKeys(left, ignoreNulls);
  Set<ImmutableBitSet> rightSet = null;

  final Set<ImmutableBitSet> tmpRightSet = mq.getUniqueKeys(right, ignoreNulls);
  int nFieldsOnLeft = left.getRowType().getFieldCount();

  if (tmpRightSet != null) {
    rightSet = new HashSet<>();
    for (ImmutableBitSet colMask : tmpRightSet) {
      ImmutableBitSet.Builder tmpMask = ImmutableBitSet.builder();
      for (int bit : colMask) {
        tmpMask.set(bit + nFieldsOnLeft);
      }
      rightSet.add(tmpMask.build());
    }

    if (leftSet != null) {
      for (ImmutableBitSet colMaskRight : rightSet) {
        for (ImmutableBitSet colMaskLeft : leftSet) {
          retSet.add(colMaskLeft.union(colMaskRight));
        }
      }
    }
  }

  // locate the columns that participate in equijoins
  final JoinInfo joinInfo = rel.analyzeCondition();

  // determine if either or both the LHS and RHS are unique on the
  // equijoin columns
  final Boolean leftUnique =
      mq.areColumnsUnique(left, joinInfo.leftSet(), ignoreNulls);
  final Boolean rightUnique =
      mq.areColumnsUnique(right, joinInfo.rightSet(), ignoreNulls);

  // if the right hand side is unique on its equijoin columns, then we can
  // add the unique keys from left if the left hand side is not null
  // generating
  if ((rightUnique != null)
      && rightUnique
      && (leftSet != null)
      && !(rel.getJoinType().generatesNullsOnLeft())) {
    retSet.addAll(leftSet);
  }

  // same as above except left and right are reversed
  if ((leftUnique != null)
      && leftUnique
      && (rightSet != null)
      && !(rel.getJoinType().generatesNullsOnRight())) {
    retSet.addAll(rightSet);
  }

  return retSet;
}
 
Example 18
Source File: RelMdColumnUniqueness.java    From calcite with Apache License 2.0 4 votes vote down vote up
public Boolean areColumnsUnique(Join rel, RelMetadataQuery mq,
    ImmutableBitSet columns, boolean ignoreNulls) {
  columns = decorateWithConstantColumnsFromPredicates(columns, rel, mq);
  if (columns.cardinality() == 0) {
    return false;
  }

  final RelNode left = rel.getLeft();
  final RelNode right = rel.getRight();

  // Semi or anti join should ignore uniqueness of the right input.
  if (!rel.getJoinType().projectsRight()) {
    return mq.areColumnsUnique(left, columns, ignoreNulls);
  }

  // Divide up the input column mask into column masks for the left and
  // right sides of the join
  final Pair<ImmutableBitSet, ImmutableBitSet> leftAndRightColumns =
      splitLeftAndRightColumns(rel.getLeft().getRowType().getFieldCount(),
          columns);
  final ImmutableBitSet leftColumns = leftAndRightColumns.left;
  final ImmutableBitSet rightColumns = leftAndRightColumns.right;

  // for FULL OUTER JOIN if columns contain column from both inputs it is not
  // guaranteed that the result will be unique
  if (!ignoreNulls && rel.getJoinType() == JoinRelType.FULL
      && leftColumns.cardinality() > 0 && rightColumns.cardinality() > 0) {
    return false;
  }

  // If the original column mask contains columns from both the left and
  // right hand side, then the columns are unique if and only if they're
  // unique for their respective join inputs
  Boolean leftUnique = mq.areColumnsUnique(left, leftColumns, ignoreNulls);
  Boolean rightUnique = mq.areColumnsUnique(right, rightColumns, ignoreNulls);
  if ((leftColumns.cardinality() > 0)
      && (rightColumns.cardinality() > 0)) {
    if ((leftUnique == null) || (rightUnique == null)) {
      return null;
    } else {
      return leftUnique && rightUnique;
    }
  }

  // If we're only trying to determine uniqueness for columns that
  // originate from one join input, then determine if the equijoin
  // columns from the other join input are unique.  If they are, then
  // the columns are unique for the entire join if they're unique for
  // the corresponding join input, provided that input is not null
  // generating.
  final JoinInfo joinInfo = rel.analyzeCondition();
  if (leftColumns.cardinality() > 0) {
    if (rel.getJoinType().generatesNullsOnLeft()) {
      return false;
    }
    Boolean rightJoinColsUnique =
        mq.areColumnsUnique(right, joinInfo.rightSet(), ignoreNulls);
    if ((rightJoinColsUnique == null) || (leftUnique == null)) {
      return null;
    }
    return rightJoinColsUnique && leftUnique;
  } else if (rightColumns.cardinality() > 0) {
    if (rel.getJoinType().generatesNullsOnRight()) {
      return false;
    }
    Boolean leftJoinColsUnique =
        mq.areColumnsUnique(left, joinInfo.leftSet(), ignoreNulls);
    if ((leftJoinColsUnique == null) || (rightUnique == null)) {
      return null;
    }
    return leftJoinColsUnique && rightUnique;
  }

  throw new AssertionError();
}
 
Example 19
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 20
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());
}