in iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/RelationPlanner.java [426:670]
public RelationPlan planJoin(
Expression criteria,
Expression asofCriteria,
Join.Type type,
Scope scope,
RelationPlan leftPlan,
RelationPlan rightPlan,
Analysis.SubqueryAnalysis subqueries) {
// NOTE: symbols must be in the same order as the outputDescriptor
List<Symbol> outputSymbols =
ImmutableList.<Symbol>builder()
.addAll(leftPlan.getFieldMappings())
.addAll(rightPlan.getFieldMappings())
.build();
PlanBuilder leftPlanBuilder =
newPlanBuilder(leftPlan, analysis).withScope(scope, outputSymbols);
PlanBuilder rightPlanBuilder =
newPlanBuilder(rightPlan, analysis).withScope(scope, outputSymbols);
ImmutableList.Builder<JoinNode.EquiJoinClause> equiClauses = ImmutableList.builder();
Optional<JoinNode.AsofJoinClause> asofJoinClause = Optional.empty();
List<Expression> complexJoinExpressions = new ArrayList<>();
List<Expression> postInnerJoinConditions = new ArrayList<>();
RelationType left = leftPlan.getDescriptor();
RelationType right = rightPlan.getDescriptor();
if (type != CROSS && type != IMPLICIT) {
List<Expression> leftComparisonExpressions = new ArrayList<>();
List<Expression> rightComparisonExpressions = new ArrayList<>();
List<ComparisonExpression.Operator> joinConditionComparisonOperators = new ArrayList<>();
if (asofCriteria != null) {
Expression firstExpression = ((ComparisonExpression) asofCriteria).getLeft();
Expression secondExpression = ((ComparisonExpression) asofCriteria).getRight();
ComparisonExpression.Operator comparisonOperator =
((ComparisonExpression) asofCriteria).getOperator();
Set<QualifiedName> firstDependencies =
SymbolsExtractor.extractNames(firstExpression, analysis.getColumnReferences());
Set<QualifiedName> secondDependencies =
SymbolsExtractor.extractNames(secondExpression, analysis.getColumnReferences());
if (firstDependencies.stream().allMatch(left::canResolve)
&& secondDependencies.stream().allMatch(right::canResolve)) {
leftComparisonExpressions.add(firstExpression);
rightComparisonExpressions.add(secondExpression);
joinConditionComparisonOperators.add(comparisonOperator);
} else if (firstDependencies.stream().allMatch(right::canResolve)
&& secondDependencies.stream().allMatch(left::canResolve)) {
leftComparisonExpressions.add(secondExpression);
rightComparisonExpressions.add(firstExpression);
joinConditionComparisonOperators.add(comparisonOperator.flip());
} else {
// the case when we mix symbols from both left and right join side on either side of
// condition.
throw new SemanticException(
format("Complex ASOF main join expression [%s] is not supported", asofCriteria));
}
}
if (criteria != null) {
for (Expression conjunct : extractPredicates(LogicalExpression.Operator.AND, criteria)) {
if (!isEqualComparisonExpression(conjunct) && type != INNER) {
complexJoinExpressions.add(conjunct);
continue;
}
Set<QualifiedName> dependencies =
SymbolsExtractor.extractNames(conjunct, analysis.getColumnReferences());
if (dependencies.stream().allMatch(left::canResolve)
|| dependencies.stream().allMatch(right::canResolve)) {
// If the conjunct can be evaluated entirely with the inputs on either side of the join,
// add
// it to the list complex expressions and let the optimizers figure out how to push it
// down later.
complexJoinExpressions.add(conjunct);
} else if (conjunct instanceof ComparisonExpression) {
Expression firstExpression = ((ComparisonExpression) conjunct).getLeft();
Expression secondExpression = ((ComparisonExpression) conjunct).getRight();
ComparisonExpression.Operator comparisonOperator =
((ComparisonExpression) conjunct).getOperator();
Set<QualifiedName> firstDependencies =
SymbolsExtractor.extractNames(firstExpression, analysis.getColumnReferences());
Set<QualifiedName> secondDependencies =
SymbolsExtractor.extractNames(secondExpression, analysis.getColumnReferences());
if (firstDependencies.stream().allMatch(left::canResolve)
&& secondDependencies.stream().allMatch(right::canResolve)) {
leftComparisonExpressions.add(firstExpression);
rightComparisonExpressions.add(secondExpression);
joinConditionComparisonOperators.add(comparisonOperator);
} else if (firstDependencies.stream().allMatch(right::canResolve)
&& secondDependencies.stream().allMatch(left::canResolve)) {
leftComparisonExpressions.add(secondExpression);
rightComparisonExpressions.add(firstExpression);
joinConditionComparisonOperators.add(comparisonOperator.flip());
} else {
// the case when we mix symbols from both left and right join side on either side of
// condition.
complexJoinExpressions.add(conjunct);
}
} else {
complexJoinExpressions.add(conjunct);
}
}
}
// leftPlanBuilder = subqueryPlanner.handleSubqueries(leftPlanBuilder,
// leftComparisonExpressions, subqueries);
// rightPlanBuilder = subqueryPlanner.handleSubqueries(rightPlanBuilder,
// rightComparisonExpressions, subqueries);
// Add projections for join criteria
leftPlanBuilder =
leftPlanBuilder.appendProjections(
leftComparisonExpressions, symbolAllocator, queryContext);
rightPlanBuilder =
rightPlanBuilder.appendProjections(
rightComparisonExpressions, symbolAllocator, queryContext);
QueryPlanner.PlanAndMappings leftCoercions =
coerce(
leftPlanBuilder, leftComparisonExpressions, analysis, idAllocator, symbolAllocator);
leftPlanBuilder = leftCoercions.getSubPlan();
QueryPlanner.PlanAndMappings rightCoercions =
coerce(
rightPlanBuilder, rightComparisonExpressions, analysis, idAllocator, symbolAllocator);
rightPlanBuilder = rightCoercions.getSubPlan();
for (int i = 0; i < leftComparisonExpressions.size(); i++) {
if (asofCriteria != null && i == 0) {
Symbol leftSymbol = leftCoercions.get(leftComparisonExpressions.get(i));
Symbol rightSymbol = rightCoercions.get(rightComparisonExpressions.get(i));
asofJoinClause =
Optional.of(
new JoinNode.AsofJoinClause(
joinConditionComparisonOperators.get(i), leftSymbol, rightSymbol));
continue;
}
if (joinConditionComparisonOperators.get(i) == ComparisonExpression.Operator.EQUAL) {
Symbol leftSymbol = leftCoercions.get(leftComparisonExpressions.get(i));
Symbol rightSymbol = rightCoercions.get(rightComparisonExpressions.get(i));
equiClauses.add(new JoinNode.EquiJoinClause(leftSymbol, rightSymbol));
} else {
postInnerJoinConditions.add(
new ComparisonExpression(
joinConditionComparisonOperators.get(i),
leftCoercions.get(leftComparisonExpressions.get(i)).toSymbolReference(),
rightCoercions.get(rightComparisonExpressions.get(i)).toSymbolReference()));
}
}
}
PlanNode root =
new JoinNode(
idAllocator.genPlanNodeId(),
mapJoinType(type),
leftPlanBuilder.getRoot(),
rightPlanBuilder.getRoot(),
equiClauses.build(),
asofJoinClause,
leftPlanBuilder.getRoot().getOutputSymbols(),
rightPlanBuilder.getRoot().getOutputSymbols(),
Optional.empty(),
Optional.empty());
if (type != INNER) {
for (Expression complexExpression : complexJoinExpressions) {
Set<QualifiedName> dependencies =
SymbolsExtractor.extractNamesNoSubqueries(
complexExpression, analysis.getColumnReferences());
// This is for handling uncorreled subqueries. Correlated subqueries are not currently
// supported and are dealt with
// during analysis.
// Make best effort to plan the subquery in the branch of the join involving the other
// inputs to the expression.
// E.g.,
// t JOIN u ON t.x = (...) get's planned on the t side
// t JOIN u ON t.x = (...) get's planned on the u side
// t JOIN u ON t.x + u.x = (...) get's planned on an arbitrary side
if (dependencies.stream().allMatch(left::canResolve)) {
// leftPlanBuilder = subqueryPlanner.handleSubqueries(leftPlanBuilder, complexExpression,
// subqueries);
} else {
// rightPlanBuilder = subqueryPlanner.handleSubqueries(rightPlanBuilder,
// complexExpression, subqueries);
}
}
}
TranslationMap translationMap =
new TranslationMap(
Optional.empty(),
scope,
analysis,
outputSymbols,
new PlannerContext(new TableMetadataImpl(), new InternalTypeManager()))
.withAdditionalMappings(leftPlanBuilder.getTranslations().getMappings())
.withAdditionalMappings(rightPlanBuilder.getTranslations().getMappings());
if (type != INNER && !complexJoinExpressions.isEmpty()) {
root =
new JoinNode(
idAllocator.genPlanNodeId(),
mapJoinType(type),
leftPlanBuilder.getRoot(),
rightPlanBuilder.getRoot(),
equiClauses.build(),
asofJoinClause,
leftPlanBuilder.getRoot().getOutputSymbols(),
rightPlanBuilder.getRoot().getOutputSymbols(),
Optional.of(
IrUtils.and(
complexJoinExpressions.stream()
.map(e -> coerceIfNecessary(analysis, e, translationMap.rewrite(e)))
.collect(Collectors.toList()))),
Optional.empty());
}
if (type == INNER) {
// rewrite all the other conditions using output symbols from left + right plan node.
PlanBuilder rootPlanBuilder = new PlanBuilder(translationMap, root);
// rootPlanBuilder = subqueryPlanner.handleSubqueries(rootPlanBuilder, complexJoinExpressions,
// subqueries);
for (Expression expression : complexJoinExpressions) {
postInnerJoinConditions.add(
coerceIfNecessary(analysis, expression, rootPlanBuilder.rewrite(expression)));
}
root = rootPlanBuilder.getRoot();
Expression postInnerJoinCriteria;
if (!postInnerJoinConditions.isEmpty()) {
postInnerJoinCriteria = IrUtils.and(postInnerJoinConditions);
root = new FilterNode(idAllocator.genPlanNodeId(), root, postInnerJoinCriteria);
}
}
return new RelationPlan(root, scope, outputSymbols, outerContext);
}