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

The following examples show how to use org.apache.calcite.rel.core.Join#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: LoptOptimizeJoinRule.java    From Bats 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() != JoinRelType.INNER) {
    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 2
Source File: AbstractJoinExtractFilterRule.java    From calcite with Apache License 2.0 5 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Join join = call.rel(0);

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

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

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

  final RelBuilder builder = call.builder();

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

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

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

  call.transformTo(builder.build());
}
 
Example 3
Source File: JoinToCorrelateRule.java    From Bats with Apache License 2.0 5 votes vote down vote up
public boolean matches(RelOptRuleCall call) {
  Join join = call.rel(0);
  switch (join.getJoinType()) {
  case INNER:
  case LEFT:
    return true;
  case FULL:
  case RIGHT:
    return false;
  default:
    throw Util.unexpected(join.getJoinType());
  }
}
 
Example 4
Source File: EnumerableBatchNestedLoopJoinRule.java    From calcite with Apache License 2.0 5 votes vote down vote up
@Override public boolean matches(RelOptRuleCall call) {
  Join join = call.rel(0);
  JoinRelType joinType = join.getJoinType();
  return joinType == JoinRelType.INNER
      || joinType == JoinRelType.LEFT
      || joinType == JoinRelType.ANTI
      || joinType == JoinRelType.SEMI;
}
 
Example 5
Source File: JoinToMultiJoinRule.java    From calcite with Apache License 2.0 5 votes vote down vote up
/**
 * Combines the join filters from the left and right inputs (if they are
 * MultiJoinRels) with the join filter in the joinrel into a single AND'd
 * join filter, unless the inputs correspond to null generating inputs in an
 * outer join
 *
 * @param joinRel join rel
 * @param left    left child of the join
 * @param right   right child of the join
 * @return combined join filters AND-ed together
 */
private List<RexNode> combineJoinFilters(
    Join joinRel,
    RelNode left,
    RelNode right) {
  JoinRelType joinType = joinRel.getJoinType();

  // AND the join condition if this isn't a left or right outer join;
  // in those cases, the outer join condition is already tracked
  // separately
  final List<RexNode> filters = new ArrayList<>();
  if ((joinType != JoinRelType.LEFT) && (joinType != JoinRelType.RIGHT)) {
    filters.add(joinRel.getCondition());
  }
  if (canCombine(left, joinType.generatesNullsOnLeft())) {
    filters.add(((MultiJoin) left).getJoinFilter());
  }
  // Need to adjust the RexInputs of the right child, since
  // those need to shift over to the right
  if (canCombine(right, joinType.generatesNullsOnRight())) {
    MultiJoin multiJoin = (MultiJoin) right;
    filters.add(
        shiftRightFilter(joinRel, left, multiJoin,
            multiJoin.getJoinFilter()));
  }

  return filters;
}
 
Example 6
Source File: JoinToMultiJoinRule.java    From Bats with Apache License 2.0 5 votes vote down vote up
/**
 * Combines the join filters from the left and right inputs (if they are
 * MultiJoinRels) with the join filter in the joinrel into a single AND'd
 * join filter, unless the inputs correspond to null generating inputs in an
 * outer join
 *
 * @param joinRel join rel
 * @param left    left child of the join
 * @param right   right child of the join
 * @return combined join filters AND-ed together
 */
private List<RexNode> combineJoinFilters(
    Join joinRel,
    RelNode left,
    RelNode right) {
  JoinRelType joinType = joinRel.getJoinType();

  // AND the join condition if this isn't a left or right outer join;
  // in those cases, the outer join condition is already tracked
  // separately
  final List<RexNode> filters = new ArrayList<>();
  if ((joinType != JoinRelType.LEFT) && (joinType != JoinRelType.RIGHT)) {
    filters.add(joinRel.getCondition());
  }
  if (canCombine(left, joinType.generatesNullsOnLeft())) {
    filters.add(((MultiJoin) left).getJoinFilter());
  }
  // Need to adjust the RexInputs of the right child, since
  // those need to shift over to the right
  if (canCombine(right, joinType.generatesNullsOnRight())) {
    MultiJoin multiJoin = (MultiJoin) right;
    filters.add(
        shiftRightFilter(joinRel, left, multiJoin,
            multiJoin.getJoinFilter()));
  }

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

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

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

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

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

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

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

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

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

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

  call.transformTo(newJoinRel);
}
 
Example 9
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 10
Source File: JdbcRules.java    From calcite with Apache License 2.0 5 votes vote down vote up
/**
 * Converts a {@code Join} into a {@code JdbcJoin}.
 *
 * @param join Join operator to convert
 * @param convertInputTraits Whether to convert input to {@code join}'s
 *                            JDBC convention
 * @return A new JdbcJoin
 */
public RelNode convert(Join join, boolean convertInputTraits) {
  final List<RelNode> newInputs = new ArrayList<>();
  for (RelNode input : join.getInputs()) {
    if (convertInputTraits && input.getConvention() != getOutTrait()) {
      input =
          convert(input,
              input.getTraitSet().replace(out));
    }
    newInputs.add(input);
  }
  if (convertInputTraits && !canJoinOnCondition(join.getCondition())) {
    return null;
  }
  try {
    return new JdbcJoin(
        join.getCluster(),
        join.getTraitSet().replace(out),
        newInputs.get(0),
        newInputs.get(1),
        join.getCondition(),
        join.getVariablesSet(),
        join.getJoinType());
  } catch (InvalidRelException e) {
    LOGGER.debug(e.toString());
    return null;
  }
}
 
Example 11
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 12
Source File: FlinkJoinToMultiJoinRule.java    From flink with Apache License 2.0 4 votes vote down vote up
/**
 * Combines the outer join conditions and join types from the left and right
 * join inputs. If the join itself is either a left or right outer join,
 * then the join condition corresponding to the join is also set in the
 * position corresponding to the null-generating input into the join. The
 * join type is also set.
 *
 * @param joinRel        join rel
 * @param combinedInputs the combined inputs to the join
 * @param left           left child of the joinrel
 * @param right          right child of the joinrel
 * @param joinSpecs      the list where the join types and conditions will be
 *                       copied
 */
private void combineOuterJoins(
		Join joinRel,
		List<RelNode> combinedInputs,
		RelNode left,
		RelNode right,
		List<Pair<JoinRelType, RexNode>> joinSpecs) {
	JoinRelType joinType = joinRel.getJoinType();
	boolean leftCombined =
			canCombine(left, joinType.generatesNullsOnLeft());
	boolean rightCombined =
			canCombine(right, joinType.generatesNullsOnRight());
	switch (joinType) {
		case LEFT:
			if (leftCombined) {
				copyOuterJoinInfo(
						(MultiJoin) left,
						joinSpecs,
						0,
						null,
						null);
			} else {
				joinSpecs.add(Pair.of(JoinRelType.INNER, (RexNode) null));
			}
			joinSpecs.add(Pair.of(joinType, joinRel.getCondition()));
			break;
		case RIGHT:
			joinSpecs.add(Pair.of(joinType, joinRel.getCondition()));
			if (rightCombined) {
				copyOuterJoinInfo(
						(MultiJoin) right,
						joinSpecs,
						left.getRowType().getFieldCount(),
						right.getRowType().getFieldList(),
						joinRel.getRowType().getFieldList());
			} else {
				joinSpecs.add(Pair.of(JoinRelType.INNER, (RexNode) null));
			}
			break;
		default:
			if (leftCombined) {
				copyOuterJoinInfo(
						(MultiJoin) left,
						joinSpecs,
						0,
						null,
						null);
			} else {
				joinSpecs.add(Pair.of(JoinRelType.INNER, (RexNode) null));
			}
			if (rightCombined) {
				copyOuterJoinInfo(
						(MultiJoin) right,
						joinSpecs,
						left.getRowType().getFieldCount(),
						right.getRowType().getFieldList(),
						joinRel.getRowType().getFieldList());
			} else {
				joinSpecs.add(Pair.of(JoinRelType.INNER, (RexNode) null));
			}
	}
}
 
Example 13
Source File: JoinPushThroughJoinRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
/**
 * Similar to {@link #onMatch}, but swaps the upper sibling with the left
 * of the two lower siblings, rather than the right.
 */
private void onMatchLeft(RelOptRuleCall call) {
  final Join topJoin = call.rel(0);
  final Join bottomJoin = call.rel(1);
  final RelNode relC = call.rel(2);
  final RelNode relA = bottomJoin.getLeft();
  final RelNode relB = bottomJoin.getRight();
  final RelOptCluster cluster = topJoin.getCluster();

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

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

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

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

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

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

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

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

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

  final RelBuilder relBuilder = call.builder();
  relBuilder.push(newTopJoin);
  relBuilder.project(relBuilder.fields(topMapping));
  call.transformTo(relBuilder.build());
}
 
Example 14
Source File: AggregateJoinRemoveRule.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 join = call.rel(1);
  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 aggregate uses columns whose index is between
  // lower(included) and upper(excluded).
  final Set<Integer> allFields = RelOptUtil.getAllFields(aggregate);
  if (allFields.stream().anyMatch(i -> i >= lower && i < upper)) {
    return;
  }

  if (aggregate.getAggCallList().stream().anyMatch(
      aggregateCall -> !aggregateCall.isDistinct())) {
    return;
  }

  RelNode node;
  if (isLeftJoin) {
    node = aggregate.copy(aggregate.getTraitSet(), join.getLeft(),
        aggregate.getGroupSet(), aggregate.getGroupSets(),
        aggregate.getAggCallList());
  } else {
    final Map<Integer, Integer> map = new HashMap<>();
    allFields.forEach(index -> map.put(index, index - upper));
    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)));

    final RelBuilder relBuilder = call.builder();
    node = relBuilder.push(join.getRight())
        .aggregate(relBuilder.groupKey(groupSet), aggCalls.build())
        .build();
  }
  call.transformTo(node);
}
 
Example 15
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 16
Source File: AggregateJoinTransposeRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
private boolean isJoinSupported(final Join join, final Aggregate aggregate) {
  return join.getJoinType() == JoinRelType.INNER || aggregate.getAggCallList().isEmpty();
}
 
Example 17
Source File: SemiJoinRule.java    From Bats with Apache License 2.0 4 votes vote down vote up
protected void perform(RelOptRuleCall call, Project project,
    Join join, RelNode left, Aggregate aggregate) {
  final RelOptCluster cluster = join.getCluster();
  final RexBuilder rexBuilder = cluster.getRexBuilder();
  if (project != null) {
    final ImmutableBitSet bits =
        RelOptUtil.InputFinder.bits(project.getProjects(), null);
    final ImmutableBitSet rightBits =
        ImmutableBitSet.range(left.getRowType().getFieldCount(),
            join.getRowType().getFieldCount());
    if (bits.intersects(rightBits)) {
      return;
    }
  }
  final JoinInfo joinInfo = join.analyzeCondition();
  if (!joinInfo.rightSet().equals(
      ImmutableBitSet.range(aggregate.getGroupCount()))) {
    // Rule requires that aggregate key to be the same as the join key.
    // By the way, neither a super-set nor a sub-set would work.
    return;
  }
  if (!joinInfo.isEqui()) {
    return;
  }
  final RelBuilder relBuilder = call.builder();
  relBuilder.push(left);
  switch (join.getJoinType()) {
  case INNER:
    final List<Integer> newRightKeyBuilder = new ArrayList<>();
    final List<Integer> aggregateKeys = aggregate.getGroupSet().asList();
    for (int key : joinInfo.rightKeys) {
      newRightKeyBuilder.add(aggregateKeys.get(key));
    }
    final ImmutableIntList newRightKeys = ImmutableIntList.copyOf(newRightKeyBuilder);
    relBuilder.push(aggregate.getInput());
    final RexNode newCondition =
        RelOptUtil.createEquiJoinCondition(relBuilder.peek(2, 0),
            joinInfo.leftKeys, relBuilder.peek(2, 1), newRightKeys,
            rexBuilder);
    relBuilder.semiJoin(newCondition);
    break;

  case LEFT:
    // The right-hand side produces no more than 1 row (because of the
    // Aggregate) and no fewer than 1 row (because of LEFT), and therefore
    // we can eliminate the semi-join.
    break;

  default:
    throw new AssertionError(join.getJoinType());
  }
  if (project != null) {
    relBuilder.project(project.getProjects(), project.getRowType().getFieldNames());
  }
  call.transformTo(relBuilder.build());
}
 
Example 18
Source File: JoinToMultiJoinRule.java    From calcite with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Join origJoin = call.rel(0);
  final RelNode left = call.rel(1);
  final RelNode right = call.rel(2);

  // combine the children MultiJoin inputs into an array of inputs
  // for the new MultiJoin
  final List<ImmutableBitSet> projFieldsList = new ArrayList<>();
  final List<int[]> joinFieldRefCountsList = new ArrayList<>();
  final List<RelNode> newInputs =
      combineInputs(
          origJoin,
          left,
          right,
          projFieldsList,
          joinFieldRefCountsList);

  // combine the outer join information from the left and right
  // inputs, and include the outer join information from the current
  // join, if it's a left/right outer join
  final List<Pair<JoinRelType, RexNode>> joinSpecs = new ArrayList<>();
  combineOuterJoins(
      origJoin,
      newInputs,
      left,
      right,
      joinSpecs);

  // pull up the join filters from the children MultiJoinRels and
  // combine them with the join filter associated with this LogicalJoin to
  // form the join filter for the new MultiJoin
  List<RexNode> newJoinFilters = combineJoinFilters(origJoin, left, right);

  // add on the join field reference counts for the join condition
  // associated with this LogicalJoin
  final ImmutableMap<Integer, ImmutableIntList> newJoinFieldRefCountsMap =
      addOnJoinFieldRefCounts(newInputs,
          origJoin.getRowType().getFieldCount(),
          origJoin.getCondition(),
          joinFieldRefCountsList);

  List<RexNode> newPostJoinFilters =
      combinePostJoinFilters(origJoin, left, right);

  final RexBuilder rexBuilder = origJoin.getCluster().getRexBuilder();
  RelNode multiJoin =
      new MultiJoin(
          origJoin.getCluster(),
          newInputs,
          RexUtil.composeConjunction(rexBuilder, newJoinFilters),
          origJoin.getRowType(),
          origJoin.getJoinType() == JoinRelType.FULL,
          Pair.right(joinSpecs),
          Pair.left(joinSpecs),
          projFieldsList,
          newJoinFieldRefCountsMap,
          RexUtil.composeConjunction(rexBuilder, newPostJoinFilters, true));

  call.transformTo(multiJoin);
}
 
Example 19
Source File: JoinToMultiJoinRule.java    From Bats with Apache License 2.0 4 votes vote down vote up
/**
 * Combines the outer join conditions and join types from the left and right
 * join inputs. If the join itself is either a left or right outer join,
 * then the join condition corresponding to the join is also set in the
 * position corresponding to the null-generating input into the join. The
 * join type is also set.
 *
 * @param joinRel        join rel
 * @param combinedInputs the combined inputs to the join
 * @param left           left child of the joinrel
 * @param right          right child of the joinrel
 * @param joinSpecs      the list where the join types and conditions will be
 *                       copied
 */
private void combineOuterJoins(
    Join joinRel,
    List<RelNode> combinedInputs,
    RelNode left,
    RelNode right,
    List<Pair<JoinRelType, RexNode>> joinSpecs) {
  JoinRelType joinType = joinRel.getJoinType();
  boolean leftCombined =
      canCombine(left, joinType.generatesNullsOnLeft());
  boolean rightCombined =
      canCombine(right, joinType.generatesNullsOnRight());
  switch (joinType) {
  case LEFT:
    if (leftCombined) {
      copyOuterJoinInfo(
          (MultiJoin) left,
          joinSpecs,
          0,
          null,
          null);
    } else {
      joinSpecs.add(Pair.of(JoinRelType.INNER, (RexNode) null));
    }
    joinSpecs.add(Pair.of(joinType, joinRel.getCondition()));
    break;
  case RIGHT:
    joinSpecs.add(Pair.of(joinType, joinRel.getCondition()));
    if (rightCombined) {
      copyOuterJoinInfo(
          (MultiJoin) right,
          joinSpecs,
          left.getRowType().getFieldCount(),
          right.getRowType().getFieldList(),
          joinRel.getRowType().getFieldList());
    } else {
      joinSpecs.add(Pair.of(JoinRelType.INNER, (RexNode) null));
    }
    break;
  default:
    if (leftCombined) {
      copyOuterJoinInfo(
          (MultiJoin) left,
          joinSpecs,
          0,
          null,
          null);
    } else {
      joinSpecs.add(Pair.of(JoinRelType.INNER, (RexNode) null));
    }
    if (rightCombined) {
      copyOuterJoinInfo(
          (MultiJoin) right,
          joinSpecs,
          left.getRowType().getFieldCount(),
          right.getRowType().getFieldList(),
          joinRel.getRowType().getFieldList());
    } else {
      joinSpecs.add(Pair.of(JoinRelType.INNER, (RexNode) null));
    }
  }
}
 
Example 20
Source File: JoinToMultiJoinRule.java    From Bats with Apache License 2.0 4 votes vote down vote up
public void onMatch(RelOptRuleCall call) {
  final Join origJoin = call.rel(0);
  final RelNode left = call.rel(1);
  final RelNode right = call.rel(2);

  // combine the children MultiJoin inputs into an array of inputs
  // for the new MultiJoin
  final List<ImmutableBitSet> projFieldsList = new ArrayList<>();
  final List<int[]> joinFieldRefCountsList = new ArrayList<>();
  final List<RelNode> newInputs =
      combineInputs(
          origJoin,
          left,
          right,
          projFieldsList,
          joinFieldRefCountsList);

  // combine the outer join information from the left and right
  // inputs, and include the outer join information from the current
  // join, if it's a left/right outer join
  final List<Pair<JoinRelType, RexNode>> joinSpecs = new ArrayList<>();
  combineOuterJoins(
      origJoin,
      newInputs,
      left,
      right,
      joinSpecs);

  // pull up the join filters from the children MultiJoinRels and
  // combine them with the join filter associated with this LogicalJoin to
  // form the join filter for the new MultiJoin
  List<RexNode> newJoinFilters = combineJoinFilters(origJoin, left, right);

  // add on the join field reference counts for the join condition
  // associated with this LogicalJoin
  final ImmutableMap<Integer, ImmutableIntList> newJoinFieldRefCountsMap =
      addOnJoinFieldRefCounts(newInputs,
          origJoin.getRowType().getFieldCount(),
          origJoin.getCondition(),
          joinFieldRefCountsList);

  List<RexNode> newPostJoinFilters =
      combinePostJoinFilters(origJoin, left, right);

  final RexBuilder rexBuilder = origJoin.getCluster().getRexBuilder();
  RelNode multiJoin =
      new MultiJoin(
          origJoin.getCluster(),
          newInputs,
          RexUtil.composeConjunction(rexBuilder, newJoinFilters),
          origJoin.getRowType(),
          origJoin.getJoinType() == JoinRelType.FULL,
          Pair.right(joinSpecs),
          Pair.left(joinSpecs),
          projFieldsList,
          newJoinFieldRefCountsMap,
          RexUtil.composeConjunction(rexBuilder, newPostJoinFilters, true));

  call.transformTo(multiJoin);
}