@Override
    protected Scope visitJoin(Join node, Optional<Scope> scope) {
      JoinCriteria criteria = node.getCriteria().orElse(null);

      joinConditionCheck(criteria);

      Scope left = process(node.getLeft(), scope);
      Scope right = process(node.getRight(), scope);

      if (criteria instanceof JoinUsing) {
        return analyzeJoinUsing(node, ((JoinUsing) criteria).getColumns(), scope, left, right);
      }

      Scope output =
          createAndAssignScope(
              node, scope, left.getRelationType().joinWith(right.getRelationType()));

      if (node.getType() == Join.Type.CROSS || node.getType() == Join.Type.IMPLICIT) {
        return output;
      }
      if (criteria instanceof JoinOn) {
        boolean isAsofJoin = criteria instanceof AsofJoinOn;

        Expression expression = ((JoinOn) criteria).getExpression();
        if (expression != null) {
          verifyNoAggregateWindowOrGroupingFunctions(expression, "JOIN clause");

          // Need to register coercions in case when join criteria requires coercion (e.g. join on
          // char(1) = char(2))
          // Correlations are only currently support in the join criteria for INNER joins
          ExpressionAnalysis expressionAnalysis =
              analyzeExpression(
                  expression,
                  output,
                  node.getType() == INNER && !isAsofJoin
                      ? CorrelationSupport.ALLOWED
                      : CorrelationSupport.DISALLOWED);
          Type clauseType = expressionAnalysis.getType(expression);
          if (!clauseType.equals(BOOLEAN)) {
            //          if (!clauseType.equals(UNKNOWN)) {
            //            throw semanticException(
            //                TYPE_MISMATCH,
            //                expression,
            //                "JOIN ON clause must evaluate to a boolean: actual type %s",
            //                clauseType);
            //          }
            throw new SemanticException(
                String.format(
                    "JOIN ON clause must evaluate to a boolean: actual type %s", clauseType));
            // coerce expression to boolean
            //          analysis.addCoercion(expression, BOOLEAN, false);
          }

          if (!isAsofJoin) {
            analysis.recordSubqueries(node, expressionAnalysis);
          }
        }

        if (isAsofJoin) {
          // The asofExpression must be ComparisonExpression, it has been checked in AstBuilder
          ComparisonExpression asofExpression =
              (ComparisonExpression) ((AsofJoinOn) criteria).getAsofExpression();

          verifyNoAggregateWindowOrGroupingFunctions(asofExpression, "JOIN clause");

          // ASOF Join does not support Correlation
          ExpressionAnalysis expressionAnalysis =
              analyzeExpression(asofExpression, output, CorrelationSupport.DISALLOWED);
          Type clauseType = expressionAnalysis.getType(asofExpression);
          if (!clauseType.equals(BOOLEAN)) {
            throw new SemanticException(
                String.format(
                    "ASOF main JOIN expression must evaluate to a boolean: actual type %s",
                    clauseType));
          }

          clauseType = expressionAnalysis.getType(asofExpression.getLeft());
          if (!clauseType.equals(TimestampType.TIMESTAMP)) {
            throw new SemanticException(
                String.format(
                    "left child type of ASOF main JOIN expression must be TIMESTAMP: actual type %s",
                    clauseType));
          }

          clauseType = expressionAnalysis.getType(asofExpression.getRight());
          if (!clauseType.equals(TimestampType.TIMESTAMP)) {
            throw new SemanticException(
                String.format(
                    "right child type of ASOF main JOIN expression must be TIMESTAMP: actual type %s",
                    clauseType));
          }
        }
        analysis.setJoinCriteria(node, expression);
      } else {
        throw new UnsupportedOperationException(
            "Unsupported join criteria: " + criteria.getClass().getName());
      }

      return output;
    }
Check if there is Columns function in expression, and verify they are same if there are multi
Column functions.

@param expression input expression
@return if there is Columns function in expression
@throws SemanticException if there are multi Columns functions but different
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java