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

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

commit 232f7f4a033c6ca098f447f377dc6403005ea9ea
Author: Weihao Li <[email protected]>
AuthorDate: Wed Apr 1 09:48:50 2026 +0800

    implement
    
    Signed-off-by: Weihao Li <[email protected]>
---
 .../iotdb/relational/it/schema/IoTDBTableIT.java   | 91 ++++++++++++++++++++++
 .../common/header/DatasetHeaderFactory.java        |  4 +
 .../TableModelStatementMemorySourceVisitor.java    | 42 ++++++++++
 .../relational/analyzer/StatementAnalyzer.java     |  7 ++
 .../relational/planner/TableLogicalPlanner.java    |  8 +-
 .../plan/relational/planner/node/OutputNode.java   |  9 +++
 .../plan/relational/sql/ast/AstVisitor.java        |  4 +
 .../sql/ast/DefaultTraversalVisitor.java           |  6 ++
 .../plan/relational/sql/ast/DescribeQuery.java     | 86 ++++++++++++++++++++
 .../plan/relational/sql/parser/AstBuilder.java     |  6 ++
 .../schema/column/ColumnHeaderConstant.java        |  5 ++
 .../db/relational/grammar/sql/RelationalSql.g4     |  5 ++
 12 files changed, 267 insertions(+), 6 deletions(-)

diff --git 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java
index b335278321c..d81cceab58a 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java
@@ -53,6 +53,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
+import static 
org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.describeQueryColumnHeaders;
 import static 
org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.describeTableColumnHeaders;
 import static 
org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.describeTableDetailsColumnHeaders;
 import static 
org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.showDBColumnHeaders;
@@ -435,6 +436,96 @@ public class IoTDBTableIT {
         assertEquals(columnNames.length, cnt);
       }
 
+      columnNames = new String[] {"region_id", "_col1"};
+      dataTypes = new String[] {"STRING", "FLOAT"};
+      try (final ResultSet resultSet =
+          statement.executeQuery(
+              "describe select region_id, max(temperature) from test1.table1 
group by region_id")) {
+        int cnt = 0;
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(describeQueryColumnHeaders.size(), 
metaData.getColumnCount());
+        for (int i = 0; i < describeQueryColumnHeaders.size(); i++) {
+          assertEquals(
+              describeQueryColumnHeaders.get(i).getColumnName(), 
metaData.getColumnName(i + 1));
+        }
+        while (resultSet.next()) {
+          assertEquals(columnNames[cnt], resultSet.getString(1));
+          assertEquals(dataTypes[cnt], resultSet.getString(2));
+          cnt++;
+        }
+        assertEquals(columnNames.length, cnt);
+      }
+
+      // describe query: last + group by
+      columnNames = new String[] {"last_time", "last_temp"};
+      dataTypes = new String[] {"TIMESTAMP", "FLOAT"};
+      try (final ResultSet resultSet =
+          statement.executeQuery(
+              "describe select last(time) as last_time, last(temperature) as 
last_temp "
+                  + "from test1.table1")) {
+        int cnt = 0;
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(describeQueryColumnHeaders.size(), 
metaData.getColumnCount());
+        for (int i = 0; i < describeQueryColumnHeaders.size(); i++) {
+          assertEquals(
+              describeQueryColumnHeaders.get(i).getColumnName(), 
metaData.getColumnName(i + 1));
+        }
+        while (resultSet.next()) {
+          assertEquals(columnNames[cnt], resultSet.getString(1));
+          assertEquals(dataTypes[cnt], resultSet.getString(2));
+          cnt++;
+        }
+        assertEquals(columnNames.length, cnt);
+      }
+
+      // describe query: date_bin + group by + aggregate
+      columnNames = new String[] {"bucket", "region_id", "max_t"};
+      dataTypes = new String[] {"TIMESTAMP", "STRING", "FLOAT"};
+      try (final ResultSet resultSet =
+          statement.executeQuery(
+              "describe select date_bin(5s, time) as bucket, region_id, 
max(temperature) as max_t "
+                  + "from test1.table1 group by 1, 2")) {
+        int cnt = 0;
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(describeQueryColumnHeaders.size(), 
metaData.getColumnCount());
+        for (int i = 0; i < describeQueryColumnHeaders.size(); i++) {
+          assertEquals(
+              describeQueryColumnHeaders.get(i).getColumnName(), 
metaData.getColumnName(i + 1));
+        }
+        while (resultSet.next()) {
+          assertEquals(columnNames[cnt], resultSet.getString(1));
+          assertEquals(dataTypes[cnt], resultSet.getString(2));
+          cnt++;
+        }
+        assertEquals(columnNames.length, cnt);
+      }
+
+      // describe query: raw row scan (no aggregation)
+      columnNames =
+          new String[] {
+            "time", "region_id", "plant_id", "device_id", "model", 
"temperature", "humidity"
+          };
+      dataTypes =
+          new String[] {"TIMESTAMP", "STRING", "STRING", "STRING", "STRING", 
"FLOAT", "DOUBLE"};
+      try (final ResultSet resultSet =
+          statement.executeQuery(
+              "describe select time, region_id, plant_id, device_id, model, 
temperature, humidity "
+                  + "from test1.table1 where region_id = 'r1'")) {
+        int cnt = 0;
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(describeQueryColumnHeaders.size(), 
metaData.getColumnCount());
+        for (int i = 0; i < describeQueryColumnHeaders.size(); i++) {
+          assertEquals(
+              describeQueryColumnHeaders.get(i).getColumnName(), 
metaData.getColumnName(i + 1));
+        }
+        while (resultSet.next()) {
+          assertEquals(columnNames[cnt], resultSet.getString(1));
+          assertEquals(dataTypes[cnt], resultSet.getString(2));
+          cnt++;
+        }
+        assertEquals(columnNames.length, cnt);
+      }
+
       statement.execute(
           "insert into table2(region_id, plant_id, color, temperature, speed) 
values(1, 1, 1, 1, 1)");
 
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeaderFactory.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeaderFactory.java
index 18f15eea8f3..c28c57b3607 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeaderFactory.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeaderFactory.java
@@ -251,6 +251,10 @@ public class DatasetHeaderFactory {
     return new DatasetHeader(ColumnHeaderConstant.describeTableColumnHeaders, 
true);
   }
 
+  public static DatasetHeader getDescribeQueryHeader() {
+    return new DatasetHeader(ColumnHeaderConstant.describeQueryColumnHeaders, 
true);
+  }
+
   public static DatasetHeader getDescribeTableDetailsHeader() {
     return new 
DatasetHeader(ColumnHeaderConstant.describeTableDetailsColumnHeaders, true);
   }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/memory/TableModelStatementMemorySourceVisitor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/memory/TableModelStatementMemorySourceVisitor.java
index 932d9419792..3d39758a83b 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/memory/TableModelStatementMemorySourceVisitor.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/memory/TableModelStatementMemorySourceVisitor.java
@@ -21,26 +21,35 @@ package 
org.apache.iotdb.db.queryengine.plan.execution.memory;
 
 import org.apache.iotdb.commons.conf.IoTDBConstant;
 import org.apache.iotdb.commons.schema.column.ColumnHeader;
+import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant;
 import org.apache.iotdb.db.queryengine.common.header.DatasetHeader;
+import org.apache.iotdb.db.queryengine.common.header.DatasetHeaderFactory;
 import org.apache.iotdb.db.queryengine.plan.Coordinator;
 import org.apache.iotdb.db.queryengine.plan.planner.LocalExecutionPlanner;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanGraphPrinter;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
+import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Field;
 import org.apache.iotdb.db.queryengine.plan.relational.analyzer.NodeRef;
+import org.apache.iotdb.db.queryengine.plan.relational.analyzer.RelationType;
 import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolAllocator;
 import 
org.apache.iotdb.db.queryengine.plan.relational.planner.TableLogicalPlanner;
 import 
org.apache.iotdb.db.queryengine.plan.relational.planner.distribute.TableDistributedPlanGenerator;
 import 
org.apache.iotdb.db.queryengine.plan.relational.planner.distribute.TableDistributedPlanner;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AstVisitor;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDevice;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DescribeQuery;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Explain;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Node;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDevice;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Table;
+import 
org.apache.iotdb.db.queryengine.plan.relational.type.InternalTypeManager;
 
+import org.apache.tsfile.common.conf.TSFileConfig;
 import org.apache.tsfile.enums.TSDataType;
 import org.apache.tsfile.read.common.block.TsBlock;
+import org.apache.tsfile.read.common.block.TsBlockBuilder;
+import org.apache.tsfile.utils.Binary;
 import org.apache.tsfile.utils.Pair;
 
 import java.util.ArrayList;
@@ -54,6 +63,7 @@ import static 
org.apache.iotdb.commons.conf.IoTDBConstant.MAIN_QUERY;
 import static 
org.apache.iotdb.db.queryengine.common.header.DatasetHeader.EMPTY_HEADER;
 import static 
org.apache.iotdb.db.queryengine.execution.warnings.WarningCollector.NOOP;
 import static 
org.apache.iotdb.db.queryengine.plan.execution.memory.StatementMemorySourceVisitor.getStatementMemorySource;
+import static 
org.apache.iotdb.db.queryengine.plan.relational.planner.node.OutputNode.buildFallbackColumnName;
 
 public class TableModelStatementMemorySourceVisitor
     extends AstVisitor<StatementMemorySource, 
TableModelStatementMemorySourceContext> {
@@ -116,6 +126,38 @@ public class TableModelStatementMemorySourceVisitor
     return getStatementMemorySource(header, lines);
   }
 
+  @Override
+  public StatementMemorySource visitDescribeQuery(
+      final DescribeQuery node, final TableModelStatementMemorySourceContext 
context) {
+    final DatasetHeader datasetHeader = 
DatasetHeaderFactory.getDescribeQueryHeader();
+    final TsBlockBuilder builder =
+        new TsBlockBuilder(
+            ColumnHeaderConstant.describeQueryColumnHeaders.stream()
+                .map(ColumnHeader::getColumnType)
+                .collect(java.util.stream.Collectors.toList()));
+    final RelationType outputDescriptor =
+        context.getAnalysis().getOutputDescriptor(node.getQuery());
+    int columnNumber = 0;
+    for (final Field field : outputDescriptor.getVisibleFields()) {
+      String columnName = field.getName().orElse(null);
+      if (columnName == null) {
+        final String originColumnName = 
field.getOriginColumnName().orElse(null);
+        columnName = buildFallbackColumnName(columnNumber, originColumnName);
+      }
+      builder.getTimeColumnBuilder().writeLong(0L);
+      builder.getColumnBuilder(0).writeBinary(new Binary(columnName, 
TSFileConfig.STRING_CHARSET));
+      builder
+          .getColumnBuilder(1)
+          .writeBinary(
+              new Binary(
+                  InternalTypeManager.getTSDataType(field.getType()).name(),
+                  TSFileConfig.STRING_CHARSET));
+      builder.declarePosition();
+      columnNumber++;
+    }
+    return new StatementMemorySource(builder.build(), datasetHeader);
+  }
+
   @Override
   public StatementMemorySource visitShowDevice(
       final ShowDevice node, final TableModelStatementMemorySourceContext 
context) {
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 bc6d54c37e7..c840f69a589 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
@@ -80,6 +80,7 @@ import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateView;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Delete;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DeleteDevice;
 import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DereferenceExpression;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DescribeQuery;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DescribeTable;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropColumn;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropDB;
@@ -468,6 +469,12 @@ public class StatementAnalyzer {
       throw new SemanticException("Describe Table statement is not supported 
yet.");
     }
 
+    @Override
+    protected Scope visitDescribeQuery(final DescribeQuery node, final 
Optional<Scope> context) {
+      analysis.setFinishQueryAfterAnalyze();
+      return visitQuery(node.getQuery(), context);
+    }
+
     @Override
     protected Scope visitSetProperties(final SetProperties node, final 
Optional<Scope> context) {
       validateProperties(node.getProperties(), context);
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableLogicalPlanner.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableLogicalPlanner.java
index 2d7ed174d2a..840be940928 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableLogicalPlanner.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableLogicalPlanner.java
@@ -103,7 +103,7 @@ import static 
org.apache.iotdb.db.queryengine.metric.QueryPlanCostMetricSet.LOGI
 import static 
org.apache.iotdb.db.queryengine.metric.QueryPlanCostMetricSet.LOGICAL_PLAN_OPTIMIZE;
 import static 
org.apache.iotdb.db.queryengine.plan.relational.metadata.MetadataUtil.createQualifiedObjectName;
 import static 
org.apache.iotdb.db.queryengine.plan.relational.planner.QueryPlanner.visibleFields;
-import static 
org.apache.iotdb.db.queryengine.plan.relational.planner.node.OutputNode.COLUMN_NAME_PREFIX;
+import static 
org.apache.iotdb.db.queryengine.plan.relational.planner.node.OutputNode.buildFallbackColumnName;
 import static 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDevice.COUNT_DEVICE_HEADER_STRING;
 import static 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDevice.getDeviceColumnHeaderList;
 import static 
org.apache.iotdb.db.queryengine.plan.relational.type.InternalTypeManager.getTSDataType;
@@ -341,12 +341,8 @@ public class TableLogicalPlanner {
 
         if (name == null) {
           String originColumnName = field.getOriginColumnName().orElse(null);
-          StringBuilder stringBuilder = new 
StringBuilder(COLUMN_NAME_PREFIX).append(columnNumber);
           // process of expr with Column, we record originColumnName in Field 
when analyze
-          if (originColumnName != null) {
-            stringBuilder.append('_').append(originColumnName);
-          }
-          name = stringBuilder.toString();
+          name = buildFallbackColumnName(columnNumber, originColumnName);
         }
 
         names.add(name);
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/OutputNode.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/OutputNode.java
index d9d166e7116..ea13b2cb04f 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/OutputNode.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/OutputNode.java
@@ -48,6 +48,15 @@ public class OutputNode extends SingleChildProcessNode {
 
   public static final String COLUMN_NAME_PREFIX = "_col";
 
+  public static String buildFallbackColumnName(
+      final int columnNumber, final String originColumnName) {
+    final StringBuilder builderName = new 
StringBuilder(COLUMN_NAME_PREFIX).append(columnNumber);
+    if (originColumnName != null) {
+      builderName.append('_').append(originColumnName);
+    }
+    return builderName.toString();
+  }
+
   public OutputNode(
       PlanNodeId id, PlanNode child, List<String> columnNames, List<Symbol> 
outputSymbols) {
     super(id, child);
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java
index 13925ae3920..154e1d0a01a 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java
@@ -449,6 +449,10 @@ public abstract class AstVisitor<R, C> {
     return visitStatement(node, context);
   }
 
+  protected R visitDescribeQuery(DescribeQuery node, C context) {
+    return visitStatement(node, context);
+  }
+
   protected R visitSetProperties(SetProperties node, C context) {
     return visitStatement(node, context);
   }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DefaultTraversalVisitor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DefaultTraversalVisitor.java
index f22af4b020e..acd3329bdbe 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DefaultTraversalVisitor.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DefaultTraversalVisitor.java
@@ -92,6 +92,12 @@ public abstract class DefaultTraversalVisitor<C> extends 
AstVisitor<Void, C> {
     return null;
   }
 
+  @Override
+  protected Void visitDescribeQuery(final DescribeQuery node, final C context) 
{
+    process(node.getQuery(), context);
+    return null;
+  }
+
   @Override
   protected Void visitWith(With node, C context) {
     for (WithQuery query : node.getQueries()) {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DescribeQuery.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DescribeQuery.java
new file mode 100644
index 00000000000..d3410852c9a
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DescribeQuery.java
@@ -0,0 +1,86 @@
+/*
+ * 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.db.queryengine.plan.relational.sql.ast;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.tsfile.utils.RamUsageEstimator;
+
+import java.util.List;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static java.util.Objects.requireNonNull;
+
+public class DescribeQuery extends Statement {
+
+  private static final long INSTANCE_SIZE =
+      RamUsageEstimator.shallowSizeOfInstance(DescribeQuery.class);
+
+  private final Query query;
+
+  public DescribeQuery(final NodeLocation location, final Query query) {
+    super(requireNonNull(location, "location is null"));
+    this.query = requireNonNull(query, "query is null");
+  }
+
+  public Query getQuery() {
+    return query;
+  }
+
+  @Override
+  public <R, C> R accept(final AstVisitor<R, C> visitor, final C context) {
+    return visitor.visitDescribeQuery(this, context);
+  }
+
+  @Override
+  public List<Node> getChildren() {
+    return ImmutableList.of(query);
+  }
+
+  @Override
+  public boolean equals(final Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null || getClass() != obj.getClass()) {
+      return false;
+    }
+    final DescribeQuery that = (DescribeQuery) obj;
+    return Objects.equals(query, that.query);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(query);
+  }
+
+  @Override
+  public String toString() {
+    return toStringHelper(this).add("query", query).toString();
+  }
+
+  @Override
+  public long ramBytesUsed() {
+    long size = INSTANCE_SIZE;
+    size += 
AstMemoryEstimationHelper.getEstimatedSizeOfNodeLocation(getLocationInternal());
+    size += 
AstMemoryEstimationHelper.getEstimatedSizeOfAccountableObject(query);
+    return size;
+  }
+}
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 70dc79b6adb..5f7cabf3810 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
@@ -76,6 +76,7 @@ import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Deallocate;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Delete;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DeleteDevice;
 import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DereferenceExpression;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DescribeQuery;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DescribeTable;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DoubleLiteral;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropColumn;
@@ -483,6 +484,11 @@ public class AstBuilder extends 
RelationalSqlBaseVisitor<Node> {
         getLocation(ctx), getQualifiedName(ctx.table), 
Objects.nonNull(ctx.DETAILS()), null);
   }
 
+  @Override
+  public Node visitDescQueryStatement(final 
RelationalSqlParser.DescQueryStatementContext ctx) {
+    return new DescribeQuery(getLocation(ctx), (Query) visit(ctx.query()));
+  }
+
   @Override
   public Node visitRenameTable(final RelationalSqlParser.RenameTableContext 
ctx) {
     return new RenameTable(
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java
index 33f56cdefe9..893c68a3e97 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java
@@ -732,6 +732,11 @@ public class ColumnHeaderConstant {
           new ColumnHeader(COLUMN_DATA_TYPE, TSDataType.TEXT),
           new ColumnHeader(COLUMN_CATEGORY, TSDataType.TEXT));
 
+  public static final List<ColumnHeader> describeQueryColumnHeaders =
+      ImmutableList.of(
+          new ColumnHeader(COLUMN_NAME, TSDataType.TEXT),
+          new ColumnHeader(COLUMN_DATA_TYPE, TSDataType.TEXT));
+
   public static final List<ColumnHeader> describeTableDetailsColumnHeaders =
       ImmutableList.of(
           new ColumnHeader(COLUMN_NAME, TSDataType.TEXT),
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 291aa1ea1ae..9e0bd09416a 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
@@ -58,6 +58,7 @@ statement
     | dropTableStatement
     | showTableStatement
     | descTableStatement
+    | descQueryStatement
     | alterTableStatement
     | commentStatement
     | showCreateTableStatement
@@ -255,6 +256,10 @@ descTableStatement
     : (DESC | DESCRIBE) table=qualifiedName (DETAILS)?
     ;
 
+descQueryStatement
+    : (DESC | DESCRIBE) query
+    ;
+
 alterTableStatement
     : ALTER TABLE (IF EXISTS)? from=qualifiedName RENAME TO to=identifier      
                          #renameTable
     | ALTER TABLE (IF EXISTS)? tableName=qualifiedName ADD COLUMN (IF NOT 
EXISTS)? column=columnDefinition                #addColumn

Reply via email to