private Analysis.GroupingSetAnalysis analyzeGroupBy(
        QuerySpecification node, Scope scope, List<Expression> outputExpressions) {
      if (node.getGroupBy().isPresent()) {
        ImmutableList.Builder<List<Set<FieldId>>> cubes = ImmutableList.builder();
        ImmutableList.Builder<List<Set<FieldId>>> rollups = ImmutableList.builder();
        ImmutableList.Builder<List<Set<FieldId>>> sets = ImmutableList.builder();
        ImmutableList.Builder<Expression> complexExpressions = ImmutableList.builder();
        ImmutableList.Builder<Expression> groupingExpressions = ImmutableList.builder();
        FunctionCall gapFillColumn = null;
        ImmutableList.Builder<Expression> gapFillGroupingExpressions = ImmutableList.builder();

        checkGroupingSetsCount(node.getGroupBy().get());
        for (GroupingElement groupingElement : node.getGroupBy().get().getGroupingElements()) {
          if (groupingElement instanceof SimpleGroupBy) {
            for (Expression column : groupingElement.getExpressions()) {
              // simple GROUP BY expressions allow ordinals or arbitrary expressions
              if (column instanceof LongLiteral) {
                long ordinal = ((LongLiteral) column).getParsedValue();
                if (ordinal < 1 || ordinal > outputExpressions.size()) {
                  throw new SemanticException(
                      String.format("GROUP BY position %s is not in select list", ordinal));
                }

                column = outputExpressions.get(toIntExact(ordinal - 1));
                verifyNoAggregateWindowOrGroupingFunctions(column, "GROUP BY clause");
              } else {
                verifyNoAggregateWindowOrGroupingFunctions(column, "GROUP BY clause");
                analyzeExpression(column, scope);
              }

              ResolvedField field = analysis.getColumnReferenceFields().get(NodeRef.of(column));
              if (field != null) {
                sets.add(ImmutableList.of(ImmutableSet.of(field.getFieldId())));
              } else {
                analysis.recordSubqueries(node, analyzeExpression(column, scope));
                complexExpressions.add(column);
              }

              if (isDateBinGapFill(column)) {
                if (gapFillColumn != null) {
                  throw new SemanticException("multiple date_bin_gapfill calls not allowed");
                }
                gapFillColumn = (FunctionCall) column;
              } else {
                gapFillGroupingExpressions.add(column);
              }

              groupingExpressions.add(column);
            }
          } else if (groupingElement instanceof GroupingSets) {
            GroupingSets element = (GroupingSets) groupingElement;
            for (Expression column : groupingElement.getExpressions()) {
              analyzeExpression(column, scope);
              if (!analysis.getColumnReferences().contains(NodeRef.of(column))) {
                throw new SemanticException(
                    String.format("GROUP BY expression must be a column reference: %s", column));
              }

              groupingExpressions.add(column);
            }

            List<Set<FieldId>> groupingSets =
                element.getSets().stream()
                    .map(
                        set ->
                            set.stream()
                                .map(NodeRef::of)
                                .map(analysis.getColumnReferenceFields()::get)
                                .map(ResolvedField::getFieldId)
                                .collect(toImmutableSet()))
                    .collect(toImmutableList());

            switch (element.getType()) {
              case CUBE:
                cubes.add(groupingSets);
                break;
              case ROLLUP:
                rollups.add(groupingSets);
                break;
              case EXPLICIT:
                sets.add(groupingSets);
                break;
            }
          }
        }

        List<Expression> expressions = groupingExpressions.build();
        for (Expression expression : expressions) {
          Type type = analysis.getType(expression);
          if (!type.isComparable()) {
            throw new SemanticException(
                String.format(
                    "%s is not comparable, and therefore cannot be used in GROUP BY", type));
          }
        }

        Analysis.GroupingSetAnalysis groupingSets =
            new Analysis.GroupingSetAnalysis(
                expressions,
                cubes.build(),
                rollups.build(),
                sets.build(),
                complexExpressions.build());
        analysis.setGroupingSets(node, groupingSets);
        if (gapFillColumn != null) {
          analysis.setGapFill(node, gapFillColumn);
          analysis.setGapFillGroupingKeys(node, gapFillGroupingExpressions.build());
        }
        return groupingSets;
      }

      Analysis.GroupingSetAnalysis result =
          new Analysis.GroupingSetAnalysis(
              ImmutableList.of(),
              ImmutableList.of(),
              ImmutableList.of(),
              ImmutableList.of(),
              ImmutableList.of());

      if (hasAggregates(node) || node.getHaving().isPresent()) {
        analysis.setGroupingSets(node, result);
      }

      return result;
    }
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