in aios/sql/iquan/java/iquan_core/src/main/java/com/taobao/search/iquan/core/rel/rules/logical/calcite/IquanFilterJoinRule.java [156:339]
protected void perform(RelOptRuleCall call, Filter filter, Join join) {
final List<RexNode> deterministicJoinFilters = Lists.<RexNode>newArrayList();
final ImmutableList.Builder<RexNode> nondeterministicJoinFiltersBuilder =
ImmutableList.<RexNode>builder();
for (RexNode expr : RelOptUtil.conjunctions(join.getCondition())) {
if (RexUtil.isDeterministic(expr)) {
deterministicJoinFilters.add(expr);
} else {
nondeterministicJoinFiltersBuilder.add(expr);
}
}
final List<RexNode> nondeterministicJoinFilters =
nondeterministicJoinFiltersBuilder.build();
final List<RexNode> origDeterministicJoinFilters =
ImmutableList.copyOf(deterministicJoinFilters);
// If there is only the joinRel,
// make sure it does not match a cartesian product joinRel
// (with "true" condition), otherwise this rule will be applied
// again on the new cartesian product joinRel.
if (filter == null && deterministicJoinFilters.isEmpty()) {
return;
}
final List<RexNode> deterministicAboveFilters = Lists.<RexNode>newArrayList();
final ImmutableList.Builder<RexNode> nondeterministicAboveFiltersBuilder =
ImmutableList.<RexNode>builder();
if (filter != null) {
for (RexNode expr : RelOptUtil.conjunctions(filter.getCondition())) {
if (RexUtil.isDeterministic(expr)) {
deterministicAboveFilters.add(expr);
} else {
nondeterministicAboveFiltersBuilder.add(expr);
}
}
}
final List<RexNode> nondeterministicAboveFilters =
nondeterministicAboveFiltersBuilder.build();
final ImmutableList<RexNode> origDeterministicAboveFilters =
ImmutableList.copyOf(deterministicAboveFilters);
// Simplify Outer Joins
JoinRelType joinType = join.getJoinType();
if (smart
&& !origDeterministicAboveFilters.isEmpty()
&& join.getJoinType() != JoinRelType.INNER) {
joinType = RelOptUtil.simplifyJoin(join, origDeterministicAboveFilters, joinType);
}
final List<RexNode> leftFilters = new ArrayList<>();
final List<RexNode> rightFilters = new ArrayList<>();
// TODO - add logic to derive additional filters. E.g., from
// (t1.a = 1 AND t2.a = 2) OR (t1.b = 3 AND t2.b = 4), you can
// derive table filters:
// (t1.a = 1 OR t1.b = 3)
// (t2.a = 2 OR t2.b = 4)
// Try to push down above filters. These are typically where clause
// filters. They can be pushed down if they are not on the NULL
// generating side.
boolean filterPushed = false;
if (RelOptUtil.classifyFilters(
join,
deterministicAboveFilters,
joinType,
(joinType == JoinRelType.INNER),
!joinType.generatesNullsOnLeft(),
!joinType.generatesNullsOnRight(),
deterministicJoinFilters,
leftFilters,
rightFilters)) {
filterPushed = true;
}
// Move join filters up if needed
validateJoinFilters(deterministicAboveFilters, deterministicJoinFilters, join, joinType);
// If no filter got pushed after validate, reset filterPushed flag
if (leftFilters.isEmpty()
&& rightFilters.isEmpty()
&& deterministicJoinFilters.size() == origDeterministicJoinFilters.size()) {
if (com.google.common.collect.Sets.newHashSet(deterministicJoinFilters)
.equals(com.google.common.collect.Sets.newHashSet(origDeterministicJoinFilters))) {
filterPushed = false;
}
}
boolean isAntiJoin = join.getJoinType().equals(JoinRelType.ANTI);
// Try to push down filters in ON clause. A ON clause filter can only be
// pushed down if it does not affect the non-matching set, i.e. it is
// not on the side which is preserved.
// A ON clause filter of anti-join can not be pushed down.
if (!isAntiJoin && RelOptUtil.classifyFilters(
join,
deterministicJoinFilters,
joinType,
false,
!joinType.generatesNullsOnRight(),
!joinType.generatesNullsOnLeft(),
deterministicJoinFilters,
leftFilters,
rightFilters)) {
filterPushed = true;
}
// if nothing actually got pushed and there is nothing leftover,
// then this rule is a no-op
if ((!filterPushed
&& joinType == join.getJoinType())
|| (deterministicJoinFilters.isEmpty()
&& leftFilters.isEmpty()
&& rightFilters.isEmpty())) {
return;
}
// create Filters on top of the children if any filters were
// pushed to them
final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
final RelBuilder relBuilder = call.builder();
final RelNode leftRel =
relBuilder.push(join.getLeft()).filter(leftFilters).build();
final RelNode rightRel =
relBuilder.push(join.getRight()).filter(rightFilters).build();
// create the new join node referencing the new children and
// containing its new join filters (if there are any)
final ImmutableList<RelDataType> fieldTypes =
ImmutableList.<RelDataType>builder()
.addAll(RelOptUtil.getFieldTypeList(leftRel.getRowType()))
.addAll(RelOptUtil.getFieldTypeList(rightRel.getRowType())).build();
final List<RexNode> leftJoinFilters = ImmutableList.<RexNode>builder()
.addAll(deterministicJoinFilters)
.addAll(nondeterministicJoinFilters)
.build();
final RexNode joinFilter =
RexUtil.composeConjunction(rexBuilder,
RexUtil.fixUp(rexBuilder, leftJoinFilters, fieldTypes),
false);
// If nothing actually got pushed and there is nothing leftover,
// then this rule is a no-op
if (joinFilter.isAlwaysTrue()
&& leftFilters.isEmpty()
&& rightFilters.isEmpty()
&& joinType == join.getJoinType()) {
return;
}
RelNode newJoinRel =
join.copy(
join.getTraitSet(),
joinFilter,
leftRel,
rightRel,
joinType,
join.isSemiJoinDone());
call.getPlanner().onCopy(join, newJoinRel);
if (!leftFilters.isEmpty()) {
call.getPlanner().onCopy(filter, leftRel);
}
if (!rightFilters.isEmpty()) {
call.getPlanner().onCopy(filter, rightRel);
}
relBuilder.push(newJoinRel);
// Create a project on top of the join if some of the columns have become
// NOT NULL due to the join-type getting stricter.
relBuilder.convert(join.getRowType(), false);
final List<RexNode> leftAboveFilters = ImmutableList.<RexNode>builder()
.addAll(deterministicAboveFilters)
.addAll(nondeterministicAboveFilters)
.build();
// create a FilterRel on top of the join if needed
relBuilder.filter(
RexUtil.fixUp(rexBuilder, leftAboveFilters,
RelOptUtil.getFieldTypeList(relBuilder.peek().getRowType())));
call.transformTo(relBuilder.build());
}