public RelationPlan planJoin()

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);
  }