private Scope analyzeJoinUsing(
        Join node, List<Identifier> columns, Optional<Scope> scope, Scope left, Scope right) {
      List<Field> joinFields = new ArrayList<>();

      List<Integer> leftJoinFields = new ArrayList<>();
      List<Integer> rightJoinFields = new ArrayList<>();

      Set<Identifier> seen = new HashSet<>();
      for (Identifier column : columns) {
        if (!seen.add(column)) {
          throw new SemanticException(
              String.format(
                  "Column '%s' appears multiple times in USING clause", column.getValue()));
        }

        ResolvedField leftField =
            left.tryResolveField(column)
                .orElseThrow(
                    () ->
                        new SemanticException(
                            String.format(
                                "Column '%s' is missing from left side of join",
                                column.getValue())));
        ResolvedField rightField =
            right
                .tryResolveField(column)
                .orElseThrow(
                    () ->
                        new SemanticException(
                            String.format(
                                "Column '%s' is missing from right side of join",
                                column.getValue())));

        // ensure a comparison operator exists for the given types (applying coercions if necessary)
        //        try {
        //          metadata.resolveOperator(OperatorType.EQUAL, ImmutableList.of(
        //              leftField.getType(), rightField.getType()));
        //        } catch (OperatorNotFoundException e) {
        //          throw semanticException(TYPE_MISMATCH, column, e, "%s", e.getMessage());
        //        }
        if (leftField.getType() != rightField.getType()) {
          throw new SemanticException(
              String.format(
                  "Column Types of left and right side are different: left is %s, right is %s",
                  leftField.getType(), rightField.getType()));
        }

        analysis.addTypes(ImmutableMap.of(NodeRef.of(column), leftField.getType()));

        joinFields.add(
            Field.newUnqualified(
                column.getValue(), leftField.getType(), leftField.getColumnCategory()));

        leftJoinFields.add(leftField.getRelationFieldIndex());
        rightJoinFields.add(rightField.getRelationFieldIndex());

        recordColumnAccess(leftField.getField());
        recordColumnAccess(rightField.getField());
      }

      ImmutableList.Builder<Field> outputs = ImmutableList.builder();
      outputs.addAll(joinFields);

      ImmutableList.Builder<Integer> leftFields = ImmutableList.builder();
      for (int i = 0; i < left.getRelationType().getAllFieldCount(); i++) {
        if (!leftJoinFields.contains(i)) {
          outputs.add(left.getRelationType().getFieldByIndex(i));
          leftFields.add(i);
        }
      }

      ImmutableList.Builder<Integer> rightFields = ImmutableList.builder();
      for (int i = 0; i < right.getRelationType().getAllFieldCount(); i++) {
        if (!rightJoinFields.contains(i)) {
          outputs.add(right.getRelationType().getFieldByIndex(i));
          rightFields.add(i);
        }
      }

      analysis.setJoinUsing(
          node,
          new Analysis.JoinUsingAnalysis(
              leftJoinFields, rightJoinFields, leftFields.build(), rightFields.build()));

      return createAndAssignScope(node, scope, new RelationType(outputs.build()));
    }
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