in iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java [658:858]
public PlanNode visitJoin(JoinNode node, RewriteContext context) {
Expression inheritedPredicate =
context.inheritedPredicate != null ? context.inheritedPredicate : TRUE_LITERAL;
// See if we can rewrite outer joins in terms of a plain inner join
node = tryNormalizeToOuterToInnerJoin(node, inheritedPredicate);
Expression leftEffectivePredicate = TRUE_LITERAL;
// effectivePredicateExtractor.extract(session, node.getLeftChild(), types, typeAnalyzer);
Expression rightEffectivePredicate = TRUE_LITERAL;
// effectivePredicateExtractor.extract(session, node.getRightChild(), types, typeAnalyzer);
Expression joinPredicate = extractJoinPredicate(node);
Expression leftPredicate;
Expression rightPredicate;
Expression postJoinPredicate;
Expression newJoinPredicate;
switch (node.getJoinType()) {
case INNER:
JoinUtils.InnerJoinPushDownResult innerJoinPushDownResult =
processInnerJoin(
metadata,
inheritedPredicate,
leftEffectivePredicate,
rightEffectivePredicate,
joinPredicate,
node.getLeftChild().getOutputSymbols(),
node.getRightChild().getOutputSymbols());
leftPredicate = innerJoinPushDownResult.getLeftPredicate();
rightPredicate = innerJoinPushDownResult.getRightPredicate();
postJoinPredicate = innerJoinPushDownResult.getPostJoinPredicate();
newJoinPredicate = innerJoinPushDownResult.getJoinPredicate();
break;
case LEFT:
JoinUtils.OuterJoinPushDownResult leftOuterJoinPushDownResult =
processLimitedOuterJoin(
metadata,
inheritedPredicate,
leftEffectivePredicate,
rightEffectivePredicate,
joinPredicate,
node.getLeftChild().getOutputSymbols(),
node.getRightChild().getOutputSymbols());
leftPredicate = leftOuterJoinPushDownResult.getOuterJoinPredicate();
rightPredicate = leftOuterJoinPushDownResult.getInnerJoinPredicate();
postJoinPredicate = leftOuterJoinPushDownResult.getPostJoinPredicate();
newJoinPredicate = leftOuterJoinPushDownResult.getJoinPredicate();
break;
case FULL:
leftPredicate = TRUE_LITERAL;
rightPredicate = TRUE_LITERAL;
postJoinPredicate = inheritedPredicate;
newJoinPredicate = joinPredicate;
break;
default:
throw new IllegalArgumentException(
"Unsupported join type in predicate push down: " + node.getJoinType().name());
}
// newJoinPredicate = simplifyExpression(newJoinPredicate);
// Create identity projections for all existing symbols
Assignments.Builder leftProjections = Assignments.builder();
leftProjections.putAll(
node.getLeftChild().getOutputSymbols().stream()
.collect(toImmutableMap(key -> key, Symbol::toSymbolReference)));
Assignments.Builder rightProjections = Assignments.builder();
rightProjections.putAll(
node.getRightChild().getOutputSymbols().stream()
.collect(toImmutableMap(key -> key, Symbol::toSymbolReference)));
// Create new projections for the new join clauses
List<JoinNode.EquiJoinClause> equiJoinClauses = new ArrayList<>();
ImmutableList.Builder<Expression> joinFilterBuilder = ImmutableList.builder();
for (Expression conjunct : extractConjuncts(newJoinPredicate)) {
if (joinEqualityExpressionOnOneColumn(conjunct, node)) {
ComparisonExpression equality = (ComparisonExpression) conjunct;
boolean alignedComparison =
new HashSet<>(node.getLeftChild().getOutputSymbols())
.containsAll(extractUnique(equality.getLeft()));
Expression leftExpression = alignedComparison ? equality.getLeft() : equality.getRight();
Expression rightExpression = alignedComparison ? equality.getRight() : equality.getLeft();
Symbol leftSymbol = symbolForExpression(leftExpression);
if (!node.getLeftChild().getOutputSymbols().contains(leftSymbol)) {
leftProjections.put(leftSymbol, leftExpression);
}
Symbol rightSymbol = symbolForExpression(rightExpression);
if (!node.getRightChild().getOutputSymbols().contains(rightSymbol)) {
rightProjections.put(rightSymbol, rightExpression);
}
equiJoinClauses.add(new JoinNode.EquiJoinClause(leftSymbol, rightSymbol));
} else {
if (node.getJoinType() != INNER) {
throw new SemanticException(ONLY_SUPPORT_EQUI_JOIN);
}
joinFilterBuilder.add(conjunct);
}
}
PlanNode leftSource;
PlanNode rightSource;
boolean equiJoinClausesUnmodified =
ImmutableSet.copyOf(equiJoinClauses).equals(ImmutableSet.copyOf(node.getCriteria()));
if (!equiJoinClausesUnmodified) {
leftSource =
new ProjectNode(queryId.genPlanNodeId(), node.getLeftChild(), leftProjections.build())
.accept(this, new RewriteContext(leftPredicate));
rightSource =
new ProjectNode(queryId.genPlanNodeId(), node.getRightChild(), rightProjections.build())
.accept(this, new RewriteContext(rightPredicate));
} else {
leftSource = node.getLeftChild().accept(this, new RewriteContext(leftPredicate));
rightSource = node.getRightChild().accept(this, new RewriteContext(rightPredicate));
}
Cardinality leftCardinality = extractCardinality(leftSource);
Cardinality rightCardinality = extractCardinality(rightSource);
if (leftCardinality.isAtMostScalar() || rightCardinality.isAtMostScalar()) {
// if cardinality of left or right equals to 1, use NestedLoopJoin
equiJoinClauses.forEach(
equiJoinClause -> joinFilterBuilder.add(equiJoinClause.toExpression()));
equiJoinClauses.clear();
}
List<Expression> joinFilter = joinFilterBuilder.build();
Optional<Expression> newJoinFilter = Optional.of(combineConjuncts(joinFilter));
if (TRUE_LITERAL.equals(newJoinFilter.get())) {
newJoinFilter = Optional.empty();
}
if (node.getJoinType() == INNER && newJoinFilter.isPresent()
// && equiJoinClauses.isEmpty()
) {
// if we do not have any equi conjunct we do not pushdown non-equality condition into
// inner join, so we plan execution as nested-loops-join followed by filter instead
// hash join.
postJoinPredicate = combineConjuncts(postJoinPredicate, newJoinFilter.get());
newJoinFilter = Optional.empty();
}
boolean filtersEquivalent =
newJoinFilter.isPresent() == node.getFilter().isPresent()
&& (!newJoinFilter.isPresent()
// || areExpressionsEquivalent(newJoinFilter.get(), node.getFilter().get());
);
PlanNode output = node;
if (leftSource != node.getLeftChild()
|| rightSource != node.getRightChild()
|| !filtersEquivalent
|| !equiJoinClausesUnmodified) {
// this branch is always executed in current version
leftSource =
new ProjectNode(
queryContext.getQueryId().genPlanNodeId(), leftSource, leftProjections.build());
rightSource =
new ProjectNode(
queryContext.getQueryId().genPlanNodeId(), rightSource, rightProjections.build());
output =
new JoinNode(
node.getPlanNodeId(),
node.getJoinType(),
leftSource,
rightSource,
equiJoinClauses,
node.getAsofCriteria(),
leftSource.getOutputSymbols(),
rightSource.getOutputSymbols(),
newJoinFilter,
node.isSpillable());
}
JoinNode outputJoinNode = (JoinNode) output;
if (!((JoinNode) output).isCrossJoin()) {
// inner join or full join, use MergeSortJoinNode
appendSortNodeForMergeSortJoin(outputJoinNode);
}
if (!TRUE_LITERAL.equals(postJoinPredicate)) {
output =
new FilterNode(
queryContext.getQueryId().genPlanNodeId(), outputJoinNode, postJoinPredicate);
}
if (!node.getOutputSymbols().equals(output.getOutputSymbols())) {
output =
new ProjectNode(
queryContext.getQueryId().genPlanNodeId(),
output,
Assignments.identity(node.getOutputSymbols()));
}
return output;
}