private Scope computeAndAssignOutputScope(
        QuerySpecification node, Optional<Scope> scope, Scope sourceScope) {
      ImmutableList.Builder<Field> outputFields = ImmutableList.builder();

      for (SelectItem item : node.getSelect().getSelectItems()) {
        if (item instanceof AllColumns) {
          AllColumns allColumns = (AllColumns) item;
          List<Field> fields = analysis.getSelectAllResultFields(allColumns);
          checkNotNull(fields, "output fields is null for select item %s", item);
          for (int i = 0; i < fields.size(); i++) {
            Field field = fields.get(i);

            Optional<String> name;
            if (!allColumns.getAliases().isEmpty()) {
              name = Optional.of(allColumns.getAliases().get(i).getCanonicalValue());
            } else {
              name = field.getName();
            }

            Field newField =
                Field.newUnqualified(
                    name,
                    field.getType(),
                    field.getColumnCategory(),
                    field.getOriginTable(),
                    field.getOriginColumnName(),
                    false);
            analysis.addSourceColumns(newField, analysis.getSourceColumns(field));
            outputFields.add(newField);
          }
        } else if (item instanceof SingleColumn) {
          SingleColumn column = (SingleColumn) item;
          Expression expression = column.getExpression();

          // process Columns
          List<Expression> expandedExpressions = column.getExpandedExpressions();
          if (expandedExpressions != null) {
            for (int i = 0; i < expandedExpressions.size(); i++) {
              expression = expandedExpressions.get(i);

              // Different from process of normal SingleColumn, alias has been processed when
              // expanded Columns in analyzeSelect, so we needn't process alias here.
              Optional<String> field = Optional.empty();
              Optional<QualifiedObjectName> originTable = Optional.empty();
              // Put accordingColumnName into originColumn to rename expr in OutputNode if necessary
              Optional<String> originColumn = Optional.of(column.getAccordingColumnNames().get(i));
              QualifiedName name = null;

              if (expression instanceof Identifier) {
                name = QualifiedName.of(((Identifier) expression).getValue());
              }

              if (name != null) {
                Field matchingField = null;
                try {
                  matchingField = analysis.getResolvedField(expression).getField();
                } catch (IllegalArgumentException e) {
                  List<Field> matchingFields = sourceScope.getRelationType().resolveFields(name);
                  if (!matchingFields.isEmpty()) {
                    matchingField = matchingFields.get(0);
                  }
                }
                if (matchingField != null) {
                  originTable = matchingField.getOriginTable();
                }

                // expression is Identifier, the name of field is original column name
                field = originColumn;
              }

              boolean aliased = column.getAlias().isPresent();
              Field newField =
                  Field.newUnqualified(
                      aliased ? originColumn : field,
                      analysis.getType(expression),
                      TsTableColumnCategory.FIELD,
                      originTable,
                      originColumn,
                      aliased);
              // outputExpressions to look up the type
              if (originTable.isPresent()) {
                analysis.addSourceColumns(
                    newField,
                    ImmutableSet.of(
                        new Analysis.SourceColumn(
                            originTable.get(),
                            originColumn.orElseThrow(
                                () -> new NoSuchElementException("No value present")))));
              } else {
                analysis.addSourceColumns(
                    newField, analysis.getExpressionSourceColumns(expression));
              }
              outputFields.add(newField);
            }
            continue;
          }

          Optional<Identifier> field = column.getAlias();

          Optional<QualifiedObjectName> originTable = Optional.empty();
          Optional<String> originColumn = Optional.empty();
          QualifiedName name = null;

          if (expression instanceof Identifier) {
            name = QualifiedName.of(((Identifier) expression).getValue());
          } else if (expression instanceof DereferenceExpression) {
            name = getQualifiedName((DereferenceExpression) expression);
          }

          if (name != null) {
            Field matchingField = null;
            try {
              matchingField = analysis.getResolvedField(expression).getField();
            } catch (IllegalArgumentException e) {
              List<Field> matchingFields = sourceScope.getRelationType().resolveFields(name);
              if (!matchingFields.isEmpty()) {
                matchingField = matchingFields.get(0);
              }
            }
            if (matchingField != null) {
              originTable = matchingField.getOriginTable();
              originColumn = matchingField.getOriginColumnName();
            }
          }

          if (!field.isPresent() && (name != null)) {
            field = Optional.of(getLast(name.getOriginalParts()));
          }

          Field newField =
              Field.newUnqualified(
                  field.map(Identifier::getValue),
                  analysis.getType(expression),
                  TsTableColumnCategory.FIELD,
                  originTable,
                  originColumn,
                  column.getAlias().isPresent()); // TODO don't use analysis as a side-channel. Use
          // outputExpressions to look up the type
          if (originTable.isPresent()) {
            analysis.addSourceColumns(
                newField,
                ImmutableSet.of(
                    new Analysis.SourceColumn(
                        originTable.get(),
                        originColumn.orElseThrow(
                            () -> new NoSuchElementException("No value present")))));
          } else {
            analysis.addSourceColumns(newField, analysis.getExpressionSourceColumns(expression));
          }
          outputFields.add(newField);
        } else {
          throw new IllegalArgumentException(
              "Unsupported SelectItem type: " + item.getClass().getName());
        }
      }

      return createAndAssignScope(node, scope, outputFields.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