This is an automated email from the ASF dual-hosted git repository.

JackieTien97 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 8fad78466f6 Implement GROUP BY ALL functionality (#17937)
8fad78466f6 is described below

commit 8fad78466f6e72930c140aed43a856674f10d2cb
Author: Zhao Xinqi <[email protected]>
AuthorDate: Tue Jun 16 14:12:22 2026 +0800

    Implement GROUP BY ALL functionality (#17937)
---
 .../it/query/recent/IoTDBGroupByAllTableIT.java    | 238 +++++++++++++++++++++
 .../relational/analyzer/AggregationAnalyzer.java   |  84 +++++++-
 .../relational/analyzer/StatementAnalyzer.java     |  28 ++-
 .../plan/relational/sql/parser/AstBuilder.java     |  16 +-
 .../plan/relational/sql/ast/GroupBy.java           |  26 ++-
 .../sql/util/CommonQuerySqlFormatter.java          |   9 +-
 .../db/relational/grammar/sql/RelationalSql.g4     |   3 +-
 7 files changed, 385 insertions(+), 19 deletions(-)

diff --git 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBGroupByAllTableIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBGroupByAllTableIT.java
new file mode 100644
index 00000000000..da543952a34
--- /dev/null
+++ 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBGroupByAllTableIT.java
@@ -0,0 +1,238 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.relational.it.query.recent;
+
+import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.TableClusterIT;
+import org.apache.iotdb.itbase.category.TableLocalStandaloneIT;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import static org.apache.iotdb.db.it.utils.TestUtils.prepareTableData;
+import static org.apache.iotdb.db.it.utils.TestUtils.tableAssertTestFail;
+import static org.apache.iotdb.db.it.utils.TestUtils.tableResultSetEqualTest;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({TableLocalStandaloneIT.class, TableClusterIT.class})
+public class IoTDBGroupByAllTableIT {
+  protected static final String DATABASE_NAME = "test";
+
+  protected static final String[] createSqls =
+      new String[] {
+        "CREATE DATABASE " + DATABASE_NAME,
+        "USE " + DATABASE_NAME,
+        "CREATE TABLE table1(device_id STRING TAG, s1 DOUBLE FIELD, x INT32 
FIELD, y INT32 FIELD)",
+        "INSERT INTO table1(time, device_id, s1, x, y) VALUES (1, 'd1', 20.0, 
1, 10)",
+        "INSERT INTO table1(time, device_id, s1, x, y) VALUES (2, 'd1', 30.0, 
2, 20)",
+        "INSERT INTO table1(time, device_id, s1, x, y) VALUES (3, 'd2', 20.0, 
1, 30)",
+        "INSERT INTO table1(time, device_id, s1, x, y) VALUES (4, 'd2', 40.0, 
3, 40)",
+        "FLUSH"
+      };
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    EnvFactory.getEnv().initClusterEnvironment();
+    prepareTableData(createSqls);
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    EnvFactory.getEnv().cleanClusterEnvironment();
+  }
+
+  @Test
+  public void testBasicInference() {
+    String[] expectedHeader = new String[] {"device_id", "_col1"};
+    String[] retArray = new String[] {"d1,25.0,", "d2,30.0,"};
+
+    tableResultSetEqualTest(
+        "SELECT device_id, avg(s1) FROM table1 GROUP BY ALL ORDER BY 
device_id",
+        expectedHeader,
+        retArray,
+        DATABASE_NAME);
+
+    expectedHeader = new String[] {"d", "avg_s1"};
+    retArray = new String[] {"d1,25.0,", "d2,30.0,"};
+    tableResultSetEqualTest(
+        "SELECT device_id AS d, avg(s1) AS avg_s1 FROM table1 GROUP BY ALL 
ORDER BY d",
+        expectedHeader,
+        retArray,
+        DATABASE_NAME);
+  }
+
+  @Test
+  public void testExpressionAndConstantInference() {
+    String[] expectedHeader = new String[] {"_col0", "_col1"};
+    String[] retArray = new String[] {"false,2,", "true,2,"};
+
+    tableResultSetEqualTest(
+        "SELECT s1 > 25, count(*) FROM table1 GROUP BY ALL ORDER BY 1",
+        expectedHeader,
+        retArray,
+        DATABASE_NAME);
+
+    retArray = new String[] {"factory_01,4,"};
+    tableResultSetEqualTest(
+        "SELECT 'factory_01', count(*) FROM table1 GROUP BY ALL",
+        expectedHeader,
+        retArray,
+        DATABASE_NAME);
+
+    retArray = new String[] {"100,4,"};
+    tableResultSetEqualTest(
+        "SELECT 100, count(*) FROM table1 GROUP BY ALL", expectedHeader, 
retArray, DATABASE_NAME);
+
+    expectedHeader = new String[] {"device_id", "_col1", "_col2"};
+    retArray = new String[] {"d1,1,25.0,", "d2,1,30.0,"};
+    tableResultSetEqualTest(
+        "SELECT device_id, 1, avg(s1) FROM table1 GROUP BY ALL ORDER BY 
device_id",
+        expectedHeader,
+        retArray,
+        DATABASE_NAME);
+  }
+
+  @Test
+  public void testAggregateExpressionsAreSkipped() {
+    String[] expectedHeader = new String[] {"_col0", "_col1"};
+    String[] retArray = new String[] {"7.0,7.0,"};
+
+    tableResultSetEqualTest(
+        "SELECT sum(x), abs(sum(x)) FROM table1 GROUP BY ALL",
+        expectedHeader,
+        retArray,
+        DATABASE_NAME);
+
+    expectedHeader = new String[] {"_col0"};
+    retArray = new String[] {"27.5,"};
+    tableResultSetEqualTest(
+        "SELECT avg(s1) FROM table1 GROUP BY ALL", expectedHeader, retArray, 
DATABASE_NAME);
+  }
+
+  @Test
+  public void testMixedAggregateExpressionValidation() {
+    tableAssertTestFail(
+        "SELECT s1 + avg(y) FROM table1 GROUP BY ALL",
+        "must be an aggregate expression or appear in GROUP BY clause",
+        DATABASE_NAME);
+
+    String[] expectedHeader = new String[] {"s1", "_col1"};
+    String[] retArray = new String[] {"20.0,40.0,", "30.0,50.0,", 
"40.0,80.0,"};
+
+    tableResultSetEqualTest(
+        "SELECT s1, s1 + avg(y) FROM table1 GROUP BY ALL ORDER BY s1",
+        expectedHeader,
+        retArray,
+        DATABASE_NAME);
+  }
+
+  @Test
+  public void testSelectAllAfterExpansion() {
+    String[] expectedHeader = new String[] {"time", "device_id", "s1", "x", 
"y", "_col5"};
+    String[] retArray =
+        new String[] {
+          "1970-01-01T00:00:00.001Z,d1,20.0,1,10,1,",
+          "1970-01-01T00:00:00.002Z,d1,30.0,2,20,1,",
+          "1970-01-01T00:00:00.003Z,d2,20.0,1,30,1,",
+          "1970-01-01T00:00:00.004Z,d2,40.0,3,40,1,"
+        };
+
+    tableResultSetEqualTest(
+        "SELECT *, count(*) FROM table1 GROUP BY ALL ORDER BY time",
+        expectedHeader,
+        retArray,
+        DATABASE_NAME);
+  }
+
+  @Test
+  public void testWindowFunctionValidation() {
+    String[] expectedHeader = new String[] {"device_id", "_col1", "_col2"};
+    String[] retArray = new String[] {"d1,1,25.0,", "d2,1,30.0,"};
+
+    tableResultSetEqualTest(
+        "SELECT device_id, count(*) OVER (PARTITION BY device_id), avg(s1) 
FROM table1 GROUP BY ALL ORDER BY device_id",
+        expectedHeader,
+        retArray,
+        DATABASE_NAME);
+
+    expectedHeader = new String[] {"device_id", "s1"};
+    retArray = new String[] {"d1,20.0,", "d2,20.0,", "d1,30.0,", "d2,40.0,"};
+    tableResultSetEqualTest(
+        "SELECT device_id, s1 FROM table1 GROUP BY ALL ORDER BY rank() OVER 
(ORDER BY s1), device_id",
+        expectedHeader,
+        retArray,
+        DATABASE_NAME);
+
+    tableAssertTestFail(
+        "SELECT device_id, rank() OVER (ORDER BY s1), avg(s1) FROM table1 
GROUP BY ALL",
+        "ORDER BY expression 's1' must be an aggregate expression or appear in 
GROUP BY clause",
+        DATABASE_NAME);
+
+    tableAssertTestFail(
+        "SELECT device_id, count(*) OVER (PARTITION BY s1), avg(s1) FROM 
table1 GROUP BY ALL",
+        "PARTITION BY expression 's1' must be an aggregate expression or 
appear in GROUP BY clause",
+        DATABASE_NAME);
+
+    tableAssertTestFail(
+        "SELECT device_id, avg(s1) FROM table1 GROUP BY ALL ORDER BY rank() 
OVER (ORDER BY s1)",
+        "ORDER BY expression 's1' must be an aggregate expression or appear in 
GROUP BY clause",
+        DATABASE_NAME);
+
+    tableAssertTestFail(
+        "SELECT sum(count(*) OVER (PARTITION BY device_id)) FROM table1 GROUP 
BY ALL",
+        "Cannot nest window functions inside aggregation",
+        DATABASE_NAME);
+  }
+
+  @Test
+  public void testWindowFrameValidation() {
+    tableAssertTestFail(
+        "SELECT device_id, count(*) OVER (PARTITION BY device_id ORDER BY 
device_id ROWS BETWEEN x PRECEDING AND CURRENT ROW), avg(s1) FROM table1 GROUP 
BY ALL",
+        "Window frame start must be an aggregate expression or appear in GROUP 
BY clause",
+        DATABASE_NAME);
+
+    tableAssertTestFail(
+        "SELECT device_id, count(*) OVER (PARTITION BY device_id ORDER BY 
device_id ROWS BETWEEN CURRENT ROW AND x FOLLOWING), avg(s1) FROM table1 GROUP 
BY ALL",
+        "Window frame end must be an aggregate expression or appear in GROUP 
BY clause",
+        DATABASE_NAME);
+  }
+
+  @Test
+  public void testIllegalAutoGroupByCombination() {
+    tableAssertTestFail(
+        "SELECT device_id, count(*) FROM table1 GROUP BY ALL, device_id",
+        "GROUP BY ALL cannot be combined with explicit grouping elements",
+        DATABASE_NAME);
+
+    tableAssertTestFail(
+        "SELECT device_id, count(*) FROM table1 GROUP BY ALL, 
ROLLUP(device_id)",
+        "GROUP BY ALL cannot be combined with explicit grouping elements",
+        DATABASE_NAME);
+
+    tableAssertTestFail(
+        "SELECT device_id, count(*) FROM table1 GROUP BY CUBE(device_id), ALL",
+        "GROUP BY ALL cannot be combined with explicit grouping elements",
+        DATABASE_NAME);
+  }
+}
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AggregationAnalyzer.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AggregationAnalyzer.java
index d083c478fac..46787078230 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AggregationAnalyzer.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AggregationAnalyzer.java
@@ -51,9 +51,13 @@ import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.QuantifiedCo
 import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Row;
 import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SearchedCaseExpression;
 import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SimpleCaseExpression;
+import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SortItem;
 import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.SubqueryExpression;
 import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Trim;
 import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.WhenClause;
+import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Window;
+import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.WindowFrame;
+import 
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.WindowSpecification;
 import org.apache.iotdb.db.i18n.DataNodeQueryMessages;
 import org.apache.iotdb.db.queryengine.plan.relational.planner.ScopeAware;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AstVisitor;
@@ -73,11 +77,13 @@ import static 
com.google.common.collect.ImmutableSet.toImmutableSet;
 import static java.lang.String.format;
 import static java.util.Objects.requireNonNull;
 import static 
org.apache.iotdb.db.queryengine.plan.relational.analyzer.ExpressionTreeUtils.extractAggregateFunctions;
+import static 
org.apache.iotdb.db.queryengine.plan.relational.analyzer.ExpressionTreeUtils.extractWindowExpressions;
 import static 
org.apache.iotdb.db.queryengine.plan.relational.analyzer.ExpressionTreeUtils.isAggregationFunction;
 import static 
org.apache.iotdb.db.queryengine.plan.relational.analyzer.ScopeReferenceExtractor.getReferencesToScope;
 import static 
org.apache.iotdb.db.queryengine.plan.relational.analyzer.ScopeReferenceExtractor.hasReferencesToScope;
 import static 
org.apache.iotdb.db.queryengine.plan.relational.analyzer.ScopeReferenceExtractor.isFieldFromScope;
 import static 
org.apache.iotdb.db.queryengine.plan.relational.planner.ScopeAware.scopeAwareKey;
+import static 
org.apache.iotdb.db.queryengine.plan.relational.utils.NodeUtils.getSortItemsFromOrderBy;
 
 /** Checks whether an expression is constant with respect to the group */
 class AggregationAnalyzer {
@@ -288,19 +294,85 @@ class AggregationAnalyzer {
     @Override
     public Boolean visitFunctionCall(FunctionCall node, Void context) {
       if (isAggregationFunction(node.getName().toString())) {
-        List<FunctionCall> aggregateFunctions = 
extractAggregateFunctions(node.getArguments());
+        if (node.getWindow().isEmpty()) {
+          List<FunctionCall> aggregateFunctions = 
extractAggregateFunctions(node.getArguments());
+          List<Expression> windowExpressions = 
extractWindowExpressions(node.getArguments());
+
+          if (!aggregateFunctions.isEmpty()) {
+            throw new SemanticException(
+                String.format(
+                    "Cannot nest aggregations inside aggregation '%s': %s",
+                    node.getName(), aggregateFunctions));
+          }
+
+          if (!windowExpressions.isEmpty()) {
+            throw new SemanticException(
+                String.format(
+                    "Cannot nest window functions inside aggregation '%s': %s",
+                    node.getName(), windowExpressions));
+          }
+
+          return true;
+        }
+      } else {
+        // Reject FILTER for non-aggregation functions when FunctionCall 
supports FILTER.
+        // Reject ORDER BY for non-aggregation functions when FunctionCall 
supports function-level
+        // ORDER BY.
+      }
+
+      if (node.getWindow().isPresent()) {
+        Window window = node.getWindow().get();
+        if (window instanceof WindowSpecification windowSpecification
+            && !process(windowSpecification, context)) {
+          return false;
+        }
+      }
+
+      return node.getArguments().stream().allMatch(expression -> 
process(expression, context));
+    }
 
-        if (!aggregateFunctions.isEmpty()) {
+    @Override
+    public Boolean visitWindowSpecification(WindowSpecification node, Void 
context) {
+      for (Expression expression : node.getPartitionBy()) {
+        if (!process(expression, context)) {
           throw new SemanticException(
               String.format(
-                  "Cannot nest aggregations inside aggregation '%s': %s",
-                  node.getName(), aggregateFunctions));
+                  "PARTITION BY expression '%s' must be an aggregate 
expression or appear in GROUP BY clause",
+                  expression));
         }
+      }
 
-        return true;
+      for (SortItem sortItem : getSortItemsFromOrderBy(node.getOrderBy())) {
+        Expression expression = sortItem.getSortKey();
+        if (!process(expression, context)) {
+          throw new SemanticException(
+              String.format(
+                  "ORDER BY expression '%s' must be an aggregate expression or 
appear in GROUP BY clause",
+                  expression));
+        }
       }
 
-      return node.getArguments().stream().allMatch(expression -> 
process(expression, context));
+      if (node.getFrame().isPresent()) {
+        process(node.getFrame().get(), context);
+      }
+
+      return true;
+    }
+
+    @Override
+    public Boolean visitWindowFrame(WindowFrame node, Void context) {
+      if (node.getStart().getValue().isPresent()
+          && !process(node.getStart().getValue().get(), context)) {
+        throw new SemanticException(
+            "Window frame start must be an aggregate expression or appear in 
GROUP BY clause");
+      }
+      if (node.getEnd().isPresent()
+          && node.getEnd().get().getValue().isPresent()
+          && !process(node.getEnd().get().getValue().get(), context)) {
+        throw new SemanticException(
+            "Window frame end must be an aggregate expression or appear in 
GROUP BY clause");
+      }
+      return true;
     }
 
     @Override
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java
index 35285f77bd0..3634debb6d8 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java
@@ -2773,12 +2773,23 @@ public class StatementAnalyzer {
         FunctionCall gapFillColumn = null;
         ImmutableList.Builder<Expression> gapFillGroupingExpressions = 
ImmutableList.builder();
 
-        checkGroupingSetsCount(node.getGroupBy().get());
-        for (GroupingElement groupingElement : 
node.getGroupBy().get().getGroupingElements()) {
+        GroupBy groupBy = node.getGroupBy().get();
+        List<Expression> allGroupByExpressions = ImmutableList.of();
+        List<GroupingElement> groupingElements;
+
+        if (groupBy.isAll()) {
+          allGroupByExpressions = 
inferAllGroupByExpressions(outputExpressions);
+          groupingElements = ImmutableList.of(new 
SimpleGroupBy(allGroupByExpressions));
+        } else {
+          checkGroupingSetsCount(groupBy);
+          groupingElements = groupBy.getGroupingElements();
+        }
+
+        for (GroupingElement groupingElement : groupingElements) {
           if (groupingElement instanceof SimpleGroupBy) {
             for (Expression column : groupingElement.getExpressions()) {
               // simple GROUP BY expressions allow ordinals or arbitrary 
expressions
-              if (column instanceof LongLiteral) {
+              if (!groupBy.isAll() && column instanceof LongLiteral) {
                 long ordinal = ((LongLiteral) column).getParsedValue();
                 if (ordinal < 1 || ordinal > outputExpressions.size()) {
                   throw new SemanticException(
@@ -2919,6 +2930,17 @@ public class StatementAnalyzer {
           .orElse(expression);
     }
 
+    private List<Expression> inferAllGroupByExpressions(List<Expression> 
outputExpressions) {
+      ImmutableList.Builder<Expression> groupingExpressions = 
ImmutableList.builder();
+      for (Expression expression : outputExpressions) {
+        if (extractAggregateFunctions(ImmutableList.of(expression)).isEmpty()
+            && extractWindowFunctions(ImmutableList.of(expression)).isEmpty()) 
{
+          groupingExpressions.add(expression);
+        }
+      }
+      return groupingExpressions.build();
+    }
+
     private boolean isDateBinGapFill(Expression column) {
       return column instanceof FunctionCall
           && DATE_BIN
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java
index ab2909e5e3f..b3c375129df 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java
@@ -2635,10 +2635,24 @@ public class AstBuilder extends 
RelationalSqlBaseVisitor<Node> {
   }
 
   @Override
-  public Node visitGroupBy(RelationalSqlParser.GroupByContext ctx) {
+  public Node visitAllGroupBy(RelationalSqlParser.AllGroupByContext ctx) {
+    return new GroupBy(getLocation(ctx), false, true, ImmutableList.of());
+  }
+
+  @Override
+  public Node visitExplicitGroupBy(RelationalSqlParser.ExplicitGroupByContext 
ctx) {
+    if (ctx.setQuantifier() == null && ctx.groupingElement().size() > 1) {
+      for (RelationalSqlParser.GroupingElementContext element : 
ctx.groupingElement()) {
+        if (element.getText().equalsIgnoreCase("ALL")) {
+          throw new SemanticException(
+              "GROUP BY ALL cannot be combined with explicit grouping 
elements");
+        }
+      }
+    }
     return new GroupBy(
         getLocation(ctx),
         isDistinct(ctx.setQuantifier()),
+        false,
         visit(ctx.groupingElement(), GroupingElement.class));
   }
 
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/GroupBy.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/GroupBy.java
index 9a22e842434..2421684aa95 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/GroupBy.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/ast/GroupBy.java
@@ -33,18 +33,26 @@ public class GroupBy extends Node {
   private static final long INSTANCE_SIZE = 
RamUsageEstimator.shallowSizeOfInstance(GroupBy.class);
 
   private final boolean isDistinct;
+  private final boolean isAll;
   private final List<GroupingElement> groupingElements;
 
   public GroupBy(boolean isDistinct, List<GroupingElement> groupingElements) {
-    super(null);
-    this.isDistinct = isDistinct;
-    this.groupingElements = 
ImmutableList.copyOf(requireNonNull(groupingElements));
+    this(null, isDistinct, false, groupingElements);
   }
 
   public GroupBy(
       NodeLocation location, boolean isDistinct, List<GroupingElement> 
groupingElements) {
-    super(requireNonNull(location, "location is null"));
+    this(location, isDistinct, false, groupingElements);
+  }
+
+  public GroupBy(
+      NodeLocation location,
+      boolean isDistinct,
+      boolean isAll,
+      List<GroupingElement> groupingElements) {
+    super(location);
     this.isDistinct = isDistinct;
+    this.isAll = isAll;
     this.groupingElements = 
ImmutableList.copyOf(requireNonNull(groupingElements));
   }
 
@@ -52,6 +60,10 @@ public class GroupBy extends Node {
     return isDistinct;
   }
 
+  public boolean isAll() {
+    return isAll;
+  }
+
   public List<GroupingElement> getGroupingElements() {
     return groupingElements;
   }
@@ -76,18 +88,20 @@ public class GroupBy extends Node {
     }
     GroupBy groupBy = (GroupBy) o;
     return isDistinct == groupBy.isDistinct
+        && isAll == groupBy.isAll
         && Objects.equals(groupingElements, groupBy.groupingElements);
   }
 
   @Override
   public int hashCode() {
-    return Objects.hash(isDistinct, groupingElements);
+    return Objects.hash(isDistinct, isAll, groupingElements);
   }
 
   @Override
   public String toString() {
     return toStringHelper(this)
         .add("isDistinct", isDistinct)
+        .add("isAll", isAll)
         .add("groupingElements", groupingElements)
         .toString();
   }
@@ -98,7 +112,7 @@ public class GroupBy extends Node {
       return false;
     }
 
-    return isDistinct == ((GroupBy) other).isDistinct;
+    return isDistinct == ((GroupBy) other).isDistinct && isAll == ((GroupBy) 
other).isAll;
   }
 
   @Override
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/util/CommonQuerySqlFormatter.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/util/CommonQuerySqlFormatter.java
index 7bb325fbc63..0ce928a957e 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/util/CommonQuerySqlFormatter.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/plan/relational/sql/util/CommonQuerySqlFormatter.java
@@ -404,14 +404,19 @@ public class CommonQuerySqlFormatter implements 
CommonQueryAstVisitor<Void, Inte
 
     node.getGroupBy()
         .ifPresent(
-            groupBy ->
+            groupBy -> {
+              if (groupBy.isAll()) {
+                append(indent, "GROUP BY ALL").append('\n');
+              } else {
                 append(
                         indent,
                         "GROUP BY "
                             + (groupBy.isDistinct() ? " DISTINCT " : "")
                             + 
org.apache.iotdb.commons.queryengine.plan.relational.sql.util
                                 
.ExpressionFormatter.formatGroupBy(groupBy.getGroupingElements()))
-                    .append('\n'));
+                    .append('\n');
+              }
+            });
 
     node.getHaving()
         .ifPresent(having -> append(indent, "HAVING " + 
formatExpression(having)).append('\n'));
diff --git 
a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
 
b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
index 301f9c0589c..4222754cdb9 100644
--- 
a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
+++ 
b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
@@ -1055,7 +1055,8 @@ fromFirstQuerySpecification
     ;
 
 groupBy
-    : setQuantifier? groupingElement (',' groupingElement)*
+    : ALL                                                                      
                   #allGroupBy
+    | setQuantifier? groupingElement (',' groupingElement)*                    
                   #explicitGroupBy
     ;
 
 groupingElement

Reply via email to