diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBDescribeTableIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBDescribeTableIT.java new file mode 100644 index 0000000000000..8d63dfb12c204 --- /dev/null +++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBDescribeTableIT.java @@ -0,0 +1,32 @@ +package org.apache.iotdb.relational.it.query.recent; + +import org.apache.iotdb.it.env.EnvFactory; +import org.apache.iotdb.itbase.category.TableLocalStandaloneIT; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.Statement; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +@Category({TableLocalStandaloneIT.class}) +public class IoTDBDescribeTableIT { + @Test + public void testDescribeTable() { + try (Connection connection = EnvFactory.getEnv().getConnection("TABLE"); + Statement statement = connection.createStatement()) { + statement.execute("CREATE DATABASE test_db"); + statement.execute("USE test_db"); + statement.execute("CREATE TABLE t1 (id1 TAG, s1 INT32)"); + try (ResultSet resultSet = statement.executeQuery("DESCRIBE t1")) { + ResultSetMetaData metaData = resultSet.getMetaData(); + assertEquals("column_name", metaData.getColumnName(1).toLowerCase()); + assertEquals("column_type", metaData.getColumnName(2).toLowerCase()); + } + } catch (Exception e) { + fail(e.getMessage()); + } + } +} \ No newline at end of file diff --git a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 index 2308d3b81b7d2..52a2816e448bb 100644 --- a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 +++ b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 @@ -90,6 +90,7 @@ dclStatement utilityStatement : flush | clearCache | setConfiguration | settle | startRepairData | stopRepairData | explain + | describe // <--- Ye line add karo | setSystemStatus | showVersion | showFlushInfo | showLockInfo | showQueryResource | showQueries | showCurrentTimestamp | killQuery | grantWatermarkEmbedding | revokeWatermarkEmbedding | loadConfiguration | loadTimeseries | loadFile @@ -1239,6 +1240,11 @@ explain : EXPLAIN (ANALYZE VERBOSE?)? selectStatement? ; +// ---- Describe +describe + : DESCRIBE selectStatement + ; + // Set System To readonly/running/error setSystemStatus : SET SYSTEM TO (READONLY|RUNNING) (ON (LOCAL | CLUSTER))? diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/QueryExecution.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/QueryExecution.java index c63937996b922..77267b369f05f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/QueryExecution.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/QueryExecution.java @@ -252,7 +252,7 @@ private ExecutionResult retry() { } private boolean skipExecute() { - return analysis.canSkipExecute(context); + return analysis.isFailed() || analysis.isFinishQueryAfterAnalyze(); } private void constructResultForMemorySource() { 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 932d941979223..f75179d9a93eb 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 @@ -7,7 +7,7 @@ * "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 + * 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 @@ -34,16 +34,21 @@ 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.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; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -130,6 +135,50 @@ public StatementMemorySource visitCountDevice( node.getTsBlock(context.getAnalysis()), node.getDataSetHeader()); } + @Override + public StatementMemorySource visitDescribeQuery( + final DescribeQuery node, final TableModelStatementMemorySourceContext context) { + + List columnHeaders = new ArrayList<>(); + // Jackie's requested lowercase headers + columnHeaders.add(new ColumnHeader("column_name", TSDataType.TEXT)); + columnHeaders.add(new ColumnHeader("column_type", TSDataType.TEXT)); + + DatasetHeader datasetHeader = new DatasetHeader(columnHeaders, true); + + List columnNames = new ArrayList<>(); + List columnTypes = new ArrayList<>(); + + context + .getAnalysis() + .getOutputDescriptor() + .getVisibleFields() + .forEach( + field -> { + columnNames.add(field.getName().orElse("unknown")); + columnTypes.add(field.getType().toString()); + }); + + TsBlockBuilder builder = new TsBlockBuilder(Arrays.asList(TSDataType.TEXT, TSDataType.TEXT)); + + for (int i = 0; i < columnNames.size(); i++) { + builder.getTimeColumnBuilder().writeLong(0); + + // Jackie's Fix: No more long paths, just simple class names + builder + .getColumnBuilder(0) + .writeBinary(new Binary(columnNames.get(i), TSFileConfig.STRING_CHARSET)); + + builder + .getColumnBuilder(1) + .writeBinary(new Binary(columnTypes.get(i), TSFileConfig.STRING_CHARSET)); + + builder.declarePosition(); + } + + return new StatementMemorySource(builder.build(), datasetHeader); + } + private List mergeExplainResults( Map, Pair>> cteExplainResults, List mainExplainResult) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analysis.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analysis.java index 4a0fe9daa570c..c0497b431423b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analysis.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analysis.java @@ -257,6 +257,7 @@ public class Analysis implements IAnalysis { private boolean emptyDataSource = false; private boolean isQuery = false; + private boolean isDescribe = false; // SqlParser is needed during query planning phase for executing uncorrelated scalar subqueries // in advance (predicate folding). The planner needs to parse and execute these subqueries @@ -1552,6 +1553,14 @@ public void setInsert(Insert insert) { this.insert = insert; } + public void setDescribe(boolean isDescribe) { + this.isDescribe = isDescribe; + } + + public boolean isDescribe() { + return isDescribe; + } + public Insert getInsert() { return insert; } 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 bc6d54c37e7fa..0c89a05566267 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.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; @@ -850,6 +851,21 @@ protected Scope visitExplain(Explain node, Optional context) { return visitQuery((Query) node.getStatement(), context); } + @Override + protected Scope visitDescribeQuery(DescribeQuery node, Optional context) { + analysis.setFinishQueryAfterAnalyze(true); + Scope scope = visitQuery(node.getQuery(), context); + + RelationType outputDescriptor = analysis.getOutputDescriptor(); + for (Field field : outputDescriptor.getVisibleFields()) { + String name = field.getName().orElse("unknown"); + String type = field.getType().toString(); + } + + analysis.setDescribe(true); + return scope; + } + @Override protected Scope visitExplainAnalyze(ExplainAnalyze node, Optional context) { queryContext.setExplainType(ExplainType.EXPLAIN_ANALYZE); 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 13925ae392029..38c684d2e3ac6 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 @@ -445,7 +445,7 @@ protected R visitRemoveConfigNode(RemoveConfigNode node, C context) { return visitStatement(node, context); } - protected R visitDescribeTable(DescribeTable node, C context) { + protected R visitDescribeQuery(DescribeQuery node, C context) { return visitStatement(node, context); } @@ -545,6 +545,11 @@ protected R visitGroupBy(GroupBy node, C context) { return visitNode(node, context); } + protected R visitDescribeTable( + org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DescribeTable node, C context) { + return visitStatement(node, context); + } + protected R visitGroupingElement(GroupingElement node, C context) { return visitNode(node, context); } 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 0000000000000..e715a8a1056e1 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/DescribeQuery.java @@ -0,0 +1,81 @@ +/* + * 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 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(NodeLocation location, Query query) { + super(requireNonNull(location, "location is null")); + this.query = requireNonNull(query, "query is null"); + } + + public Query getQuery() { + return query; + } + + @Override + public R accept(AstVisitor visitor, C context) { + return visitor.visitDescribeQuery(this, context); + } + + @Override + public List getChildren() { + return ImmutableList.of(query); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + DescribeQuery that = (DescribeQuery) obj; + return Objects.equals(query, that.query); + } + + @Override + public int hashCode() { + return Objects.hash(query); + } + + @Override + public String toString() { + return "DescribeQuery{" + "query=" + query + '}'; + } + + @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/util/SqlFormatter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/SqlFormatter.java index 0c1d862a886cc..cbc32a2318c3d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/SqlFormatter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/SqlFormatter.java @@ -34,6 +34,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateTopic; 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.DescribeTable; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropColumn; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropFunction; @@ -1711,6 +1712,13 @@ private void appendTableFunctionArguments(List arguments, } } + @Override + protected Void visitDescribeTable(DescribeTable node, Integer indent) { + builder.append("DESCRIBE "); + builder.append(node.getTable().toString()); + return null; + } + @Override public Void visitTableArgument(TableFunctionTableArgument node, Integer indent) { Relation relation = node.getTable(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/datastructure/tv/ElasticSerializableTVList.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/datastructure/tv/ElasticSerializableTVList.java index f0d4e3dd174eb..790e5cedaf517 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/datastructure/tv/ElasticSerializableTVList.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/datastructure/tv/ElasticSerializableTVList.java @@ -25,6 +25,7 @@ import org.apache.tsfile.block.column.Column; import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.column.TimeColumn; import org.apache.tsfile.utils.Binary; import java.io.IOException; @@ -183,16 +184,16 @@ public void putColumn(Column timeColumn, Column valueColumn) throws IOException consumed = total; if (begin == 0) { // No need to copy if the columns do not split - insertedTimeColumn = timeColumn; + insertedTimeColumn = ensureTimeColumn(timeColumn); insertedValueColumn = valueColumn; } else { - insertedTimeColumn = timeColumn.getRegionCopy(begin, consumed); + insertedTimeColumn = ensureTimeColumn(timeColumn.getRegionCopy(begin, consumed)); insertedValueColumn = valueColumn.getRegionCopy(begin, consumed); } } else { consumed = internalTVListCapacity - pointCount % internalTVListCapacity; // Construct sub-regions - insertedTimeColumn = timeColumn.getRegionCopy(begin, consumed); + insertedTimeColumn = ensureTimeColumn(timeColumn.getRegionCopy(begin, consumed)); insertedValueColumn = valueColumn.getRegionCopy(begin, consumed); } @@ -284,4 +285,15 @@ public SerializableTVList get(int targetIndex) throws IOException { return internalTVList.get(targetIndex); } } + + private Column ensureTimeColumn(Column column) { + if (column instanceof TimeColumn) { + return column; + } + long[] times = new long[column.getPositionCount()]; + for (int i = 0; i < times.length; i++) { + times[i] = column.getLong(i); + } + return new TimeColumn(column.getPositionCount(), times); + } } 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 55fecde7c3db1..e1d97ae9dcdce 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 @@ -349,6 +349,9 @@ private ColumnHeaderConstant() { public static final String SHOW_CONFIGURATIONS_DEFAULT_VALUE = "default_value"; public static final String SHOW_CONFIGURATIONS_DESCRIPTION = "description"; + public static final String COLUMN_NAME_DUCKDB = "column_name"; + public static final String COLUMN_TYPE_DUCKDB = "column_type"; + public static final List lastQueryColumnHeaders = ImmutableList.of( new ColumnHeader(TIMESERIES, TSDataType.TEXT), @@ -708,14 +711,14 @@ private ColumnHeaderConstant() { public static final List describeTableColumnHeaders = ImmutableList.of( - new ColumnHeader(COLUMN_NAME, TSDataType.TEXT), - new ColumnHeader(COLUMN_DATA_TYPE, TSDataType.TEXT), + new ColumnHeader(COLUMN_NAME_DUCKDB, TSDataType.TEXT), + new ColumnHeader(COLUMN_TYPE_DUCKDB, TSDataType.TEXT), new ColumnHeader(COLUMN_CATEGORY, TSDataType.TEXT)); public static final List describeTableDetailsColumnHeaders = ImmutableList.of( - new ColumnHeader(COLUMN_NAME, TSDataType.TEXT), - new ColumnHeader(COLUMN_DATA_TYPE, TSDataType.TEXT), + new ColumnHeader(COLUMN_NAME_DUCKDB, TSDataType.TEXT), + new ColumnHeader(COLUMN_TYPE_DUCKDB, TSDataType.TEXT), new ColumnHeader(COLUMN_CATEGORY, TSDataType.TEXT), new ColumnHeader(STATUS, TSDataType.TEXT), new ColumnHeader(COMMENT, TSDataType.TEXT)); diff --git "a/potless\357\200\272apply" "b/potless\357\200\272apply" new file mode 100644 index 0000000000000..fcd187564c588 --- /dev/null +++ "b/potless\357\200\272apply" @@ -0,0 +1,222 @@ +warning: in the working copy of 'integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBDescribeTableIT.java', CRLF will be replaced by LF the next time Git touches it +diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBDescribeTableIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBDescribeTableIT.java +index e69de29bb2..8d63dfb12c 100644 +--- a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBDescribeTableIT.java ++++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBDescribeTableIT.java +@@ -0,0 +1,32 @@ ++package org.apache.iotdb.relational.it.query.recent; ++ ++import org.apache.iotdb.it.env.EnvFactory; ++import org.apache.iotdb.itbase.category.TableLocalStandaloneIT; ++import org.junit.Test; ++import org.junit.experimental.categories.Category; ++import java.sql.Connection; ++import java.sql.ResultSet; ++import java.sql.ResultSetMetaData; ++import java.sql.Statement; ++import static org.junit.Assert.assertEquals; ++import static org.junit.Assert.fail; ++ ++@Category({TableLocalStandaloneIT.class}) ++public class IoTDBDescribeTableIT { ++ @Test ++ public void testDescribeTable() { ++ try (Connection connection = EnvFactory.getEnv().getConnection("TABLE"); ++ Statement statement = connection.createStatement()) { ++ statement.execute("CREATE DATABASE test_db"); ++ statement.execute("USE test_db"); ++ statement.execute("CREATE TABLE t1 (id1 TAG, s1 INT32)"); ++ try (ResultSet resultSet = statement.executeQuery("DESCRIBE t1")) { ++ ResultSetMetaData metaData = resultSet.getMetaData(); ++ assertEquals("column_name", metaData.getColumnName(1).toLowerCase()); ++ assertEquals("column_type", metaData.getColumnName(2).toLowerCase()); ++ } ++ } catch (Exception e) { ++ fail(e.getMessage()); ++ } ++ } ++} +\ No newline at end of file +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 b3ee230d2e..f75179d9a9 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 +@@ -7,7 +7,7 @@ + * "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 ++ * 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 +@@ -34,16 +34,21 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.distribute.TableD + 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.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; ++import java.util.Arrays; + import java.util.Collections; + import java.util.List; + import java.util.Map; +@@ -132,23 +137,17 @@ public class TableModelStatementMemorySourceVisitor +  + @Override + public StatementMemorySource visitDescribeQuery( +- org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DescribeQuery node, +- TableModelStatementMemorySourceContext context) { ++ final DescribeQuery node, final TableModelStatementMemorySourceContext context) { +  +- java.util.List columnHeaders = +- new java.util.ArrayList<>(); +- columnHeaders.add( +- new org.apache.iotdb.commons.schema.column.ColumnHeader( +- "Column", org.apache.tsfile.enums.TSDataType.TEXT)); +- columnHeaders.add( +- new org.apache.iotdb.commons.schema.column.ColumnHeader( +- "Type", org.apache.tsfile.enums.TSDataType.TEXT)); ++ List columnHeaders = new ArrayList<>(); ++ // Jackie's requested lowercase headers ++ columnHeaders.add(new ColumnHeader("column_name", TSDataType.TEXT)); ++ columnHeaders.add(new ColumnHeader("column_type", TSDataType.TEXT)); +  +- org.apache.iotdb.db.queryengine.common.header.DatasetHeader datasetHeader = +- new org.apache.iotdb.db.queryengine.common.header.DatasetHeader(columnHeaders, true); ++ DatasetHeader datasetHeader = new DatasetHeader(columnHeaders, true); +  +- java.util.List columnNames = new java.util.ArrayList<>(); +- java.util.List columnTypes = new java.util.ArrayList<>(); ++ List columnNames = new ArrayList<>(); ++ List columnTypes = new ArrayList<>(); +  + context + .getAnalysis() +@@ -160,24 +159,19 @@ public class TableModelStatementMemorySourceVisitor + columnTypes.add(field.getType().toString()); + }); +  +- org.apache.tsfile.read.common.block.TsBlockBuilder builder = +- new org.apache.tsfile.read.common.block.TsBlockBuilder( +- java.util.Arrays.asList( +- org.apache.tsfile.enums.TSDataType.TEXT, org.apache.tsfile.enums.TSDataType.TEXT)); ++ TsBlockBuilder builder = new TsBlockBuilder(Arrays.asList(TSDataType.TEXT, TSDataType.TEXT)); +  + for (int i = 0; i < columnNames.size(); i++) { + builder.getTimeColumnBuilder().writeLong(0); +  ++ // Jackie's Fix: No more long paths, just simple class names + builder + .getColumnBuilder(0) +- .writeBinary( +- new org.apache.tsfile.utils.Binary( +- columnNames.get(i).getBytes(java.nio.charset.StandardCharsets.UTF_8))); ++ .writeBinary(new Binary(columnNames.get(i), TSFileConfig.STRING_CHARSET)); ++ + builder + .getColumnBuilder(1) +- .writeBinary( +- new org.apache.tsfile.utils.Binary( +- columnTypes.get(i).getBytes(java.nio.charset.StandardCharsets.UTF_8))); ++ .writeBinary(new Binary(columnTypes.get(i), TSFileConfig.STRING_CHARSET)); +  + builder.declarePosition(); + } +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 154e1d0a01..38c684d2e3 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 +@@ -445,10 +445,6 @@ public abstract class AstVisitor { + return visitStatement(node, context); + } +  +- protected R visitDescribeTable(DescribeTable node, C context) { +- return visitStatement(node, context); +- } +- + protected R visitDescribeQuery(DescribeQuery node, C context) { + return visitStatement(node, context); + } +@@ -549,6 +545,11 @@ public abstract class AstVisitor { + return visitNode(node, context); + } +  ++ protected R visitDescribeTable( ++ org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DescribeTable node, C context) { ++ return visitStatement(node, context); ++ } ++ + protected R visitGroupingElement(GroupingElement node, C context) { + return visitNode(node, context); + } +diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/SqlFormatter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/SqlFormatter.java +index 0c1d862a88..cbc32a2318 100644 +--- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/SqlFormatter.java ++++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/SqlFormatter.java +@@ -34,6 +34,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateTable; + import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateTopic; + 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.DescribeTable; + import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropColumn; + import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropDB; + import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropFunction; +@@ -1711,6 +1712,13 @@ public final class SqlFormatter { + } + } +  ++ @Override ++ protected Void visitDescribeTable(DescribeTable node, Integer indent) { ++ builder.append("DESCRIBE "); ++ builder.append(node.getTable().toString()); ++ return null; ++ } ++ + @Override + public Void visitTableArgument(TableFunctionTableArgument node, Integer indent) { + Relation relation = node.getTable(); +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 55fecde7c3..e1d97ae9dc 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 +@@ -349,6 +349,9 @@ public class ColumnHeaderConstant { + public static final String SHOW_CONFIGURATIONS_DEFAULT_VALUE = "default_value"; + public static final String SHOW_CONFIGURATIONS_DESCRIPTION = "description"; +  ++ public static final String COLUMN_NAME_DUCKDB = "column_name"; ++ public static final String COLUMN_TYPE_DUCKDB = "column_type"; ++ + public static final List lastQueryColumnHeaders = + ImmutableList.of( + new ColumnHeader(TIMESERIES, TSDataType.TEXT), +@@ -708,14 +711,14 @@ public class ColumnHeaderConstant { +  + public static final List describeTableColumnHeaders = + ImmutableList.of( +- new ColumnHeader(COLUMN_NAME, TSDataType.TEXT), +- new ColumnHeader(COLUMN_DATA_TYPE, TSDataType.TEXT), ++ new ColumnHeader(COLUMN_NAME_DUCKDB, TSDataType.TEXT), ++ new ColumnHeader(COLUMN_TYPE_DUCKDB, TSDataType.TEXT), + new ColumnHeader(COLUMN_CATEGORY, TSDataType.TEXT)); +  + public static final List describeTableDetailsColumnHeaders = + ImmutableList.of( +- new ColumnHeader(COLUMN_NAME, TSDataType.TEXT), +- new ColumnHeader(COLUMN_DATA_TYPE, TSDataType.TEXT), ++ new ColumnHeader(COLUMN_NAME_DUCKDB, TSDataType.TEXT), ++ new ColumnHeader(COLUMN_TYPE_DUCKDB, TSDataType.TEXT), + new ColumnHeader(COLUMN_CATEGORY, TSDataType.TEXT), + new ColumnHeader(STATUS, TSDataType.TEXT), + new ColumnHeader(COMMENT, TSDataType.TEXT));