Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1239,6 +1240,11 @@ explain
: EXPLAIN (ANALYZE VERBOSE?)? selectStatement?
;

// ---- Describe
describe
: DESCRIBE selectStatement
;

Comment on lines +1243 to +1247
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we only need to support it in table model, this g4 file is for tree model

// Set System To readonly/running/error
setSystemStatus
: SET SYSTEM TO (READONLY|RUNNING) (ON (LOCAL | CLUSTER))?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ private ExecutionResult retry() {
}

private boolean skipExecute() {
return analysis.canSkipExecute(context);
return analysis.isFailed() || analysis.isFinishQueryAfterAnalyze();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do this change?

}

private void constructResultForMemorySource() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -130,6 +135,50 @@ public StatementMemorySource visitCountDevice(
node.getTsBlock(context.getAnalysis()), node.getDataSetHeader());
}

@Override
public StatementMemorySource visitDescribeQuery(
final DescribeQuery node, final TableModelStatementMemorySourceContext context) {

List<ColumnHeader> 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<String> columnNames = new ArrayList<>();
List<String> 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<String> mergeExplainResults(
Map<NodeRef<Table>, Pair<Integer, List<String>>> cteExplainResults,
List<String> mainExplainResult) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -850,6 +851,21 @@ protected Scope visitExplain(Explain node, Optional<Scope> context) {
return visitQuery((Query) node.getStatement(), context);
}

@Override
protected Scope visitDescribeQuery(DescribeQuery node, Optional<Scope> 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<Scope> context) {
queryContext.setExplainType(ExplainType.EXPLAIN_ANALYZE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Apache license header looks malformed: it currently reads Version 2.0 (the followed by License"); (missing the opening quote before License). Please fix the header to match the standard ... (the "License"); ... form used elsewhere in the codebase.

Suggested change
* License"); you may not use this file except in compliance
* "License"); you may not use this file except in compliance

Copilot uses AI. Check for mistakes.
* 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, C> R accept(AstVisitor<R, C> visitor, C context) {
return visitor.visitDescribeQuery(this, context);
}

@Override
public List<Node> getChildren() {
return ImmutableList.of(query);
}

@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Comment on lines +58 to +59
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

equals uses single-line if statements without braces (e.g., if (this == obj) return true;). This violates the repo Checkstyle NeedBraces rule and will fail style checks. Add braces for these conditionals.

Suggested change
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}

Copilot uses AI. Check for mistakes.
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1711,6 +1712,13 @@ private void appendTableFunctionArguments(List<TableFunctionArgument> 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();
Expand Down
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changes to this class is unrelated to this feature, don't include them.

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
}
Comment on lines +289 to +298
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<ColumnHeader> lastQueryColumnHeaders =
ImmutableList.of(
new ColumnHeader(TIMESERIES, TSDataType.TEXT),
Expand Down Expand Up @@ -708,14 +711,14 @@ private ColumnHeaderConstant() {

public static final List<ColumnHeader> 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<ColumnHeader> 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));
Expand Down
Loading