Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
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 +1247 to +1251
Copy link
Copy Markdown
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
Copy Markdown
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 @@ -130,6 +130,61 @@ public StatementMemorySource visitCountDevice(
node.getTsBlock(context.getAnalysis()), node.getDataSetHeader());
}

@Override
public StatementMemorySource visitDescribeQuery(
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DescribeQuery node,
TableModelStatementMemorySourceContext context) {

Comment on lines +138 to +141
java.util.List<org.apache.iotdb.commons.schema.column.ColumnHeader> 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));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

you should read more about duckdb's definition, column_name and column_type should be the column headers. And their type should be STRING.

And these constants should be defined in ColumnHeaderConstant class


org.apache.iotdb.db.queryengine.common.header.DatasetHeader datasetHeader =
new org.apache.iotdb.db.queryengine.common.header.DatasetHeader(columnHeaders, true);

java.util.List<String> columnNames = new java.util.ArrayList<>();
java.util.List<String> columnTypes = new java.util.ArrayList<>();

context
.getAnalysis()
.getOutputDescriptor()
.getVisibleFields()
.forEach(
field -> {
columnNames.add(field.getName().orElse("unknown"));
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));

for (int i = 0; i < columnNames.size(); i++) {
builder.getTimeColumnBuilder().writeLong(0);

builder
.getColumnBuilder(0)
.writeBinary(
new org.apache.tsfile.utils.Binary(
columnNames.get(i).getBytes(java.nio.charset.StandardCharsets.UTF_8)));
builder
.getColumnBuilder(1)
.writeBinary(
new org.apache.tsfile.utils.Binary(
columnTypes.get(i).getBytes(java.nio.charset.StandardCharsets.UTF_8)));

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();
}
Comment on lines +860 to +863
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.

Avoid System.out.println in the analyzer path. This will write to stdout for every DESCRIBE and can be noisy in production; use the project logger at an appropriate level or store the extracted column metadata in Analysis for the planner/operator to consume instead of printing.

Copilot uses AI. Check for mistakes.

Comment on lines +859 to +864
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.

visitDescribeQuery calls analysis.getOutputDescriptor() after analyzing the inner query, but the analysis scope is set on node.getQuery() (see visitQuery), not on the DescribeQuery statement node. This will throw when Analysis tries to resolve the root scope. Use analysis.getOutputDescriptor(node.getQuery()) (or the returned scope) and also assign a scope to the DescribeQuery node if it is the root statement.

Suggested change
RelationType outputDescriptor = analysis.getOutputDescriptor();
for (Field field : outputDescriptor.getVisibleFields()) {
String name = field.getName().orElse("unknown");
String type = field.getType().toString();
System.out.println("DESCRIBE_DEBUG: Column=" + name + ", Type=" + type);
}
// Retrieve output descriptor for the analyzed inner query
RelationType outputDescriptor = analysis.getOutputDescriptor(node.getQuery());
for (Field field : outputDescriptor.getVisibleFields()) {
String name = field.getName().orElse("unknown");
String type = field.getType().toString();
System.out.println("DESCRIBE_DEBUG: Column=" + name + ", Type=" + type);
}
// Assign a scope to the DescribeQuery node when it is the root statement
if (!context.isPresent()) {
analysis.setScope(node, scope);
}

Copilot uses AI. Check for mistakes.
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 @@ -449,6 +449,10 @@ protected R visitDescribeTable(DescribeTable node, C context) {
return visitStatement(node, context);
}

protected R visitDescribeQuery(DescribeQuery node, C context) {
return visitStatement(node, context);
}
Comment on lines +448 to +450

protected R visitSetProperties(SetProperties node, C context) {
return visitStatement(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;
}
}
Copy link
Copy Markdown
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 @@ -914,6 +914,7 @@ queryStatement
: query #statementDefault
| EXPLAIN query #explain
| EXPLAIN ANALYZE VERBOSE? query #explainAnalyze
| DESCRIBE QUERY query #describeQuery
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.

DESCRIBE QUERY is added to the grammar, but there is no corresponding AST builder override (e.g., in AstBuilder) to construct a DescribeQuery node. With the default ANTLR visitor behavior, this alternative will likely return the inner query AST directly, causing DESCRIBE QUERY ... to execute the query instead of describing it. Add an AstBuilder#visitDescribeQuery(...) that wraps the parsed query in the new DescribeQuery statement.

Suggested change
| DESCRIBE QUERY query #describeQuery

Copilot uses AI. Check for mistakes.
;

query
Expand Down
Loading