Skip to content

Commit 936af64

Browse files
authored
support EXPLAIN and EXPLAIN ANALYZE for EXECUTE prepared statement (#17318)
1 parent 9f790e1 commit 936af64

4 files changed

Lines changed: 59 additions & 21 deletions

File tree

integration-test/src/test/java/org/apache/iotdb/relational/it/insertquery/IoTDBInsertQueryIT.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,8 @@ public void testExplain() throws SQLException {
459459
Assert.assertTrue(
460460
e.getMessage(),
461461
e.getMessage()
462-
.contains("700: line 1:9: mismatched input 'INSERT'. Expecting: 'ANALYZE', <query>"));
462+
.contains(
463+
"700: line 1:9: mismatched input 'INSERT'. Expecting: 'ANALYZE', 'EXECUTE', <query>"));
463464
}
464465

465466
try {
@@ -471,7 +472,7 @@ public void testExplain() throws SQLException {
471472
e.getMessage(),
472473
e.getMessage()
473474
.contains(
474-
"700: line 1:17: mismatched input 'INSERT'. Expecting: 'VERBOSE', <query>"));
475+
"700: line 1:17: mismatched input 'INSERT'. Expecting: 'EXECUTE', 'VERBOSE', <query>"));
475476
}
476477
}
477478

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@
8686
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropTable;
8787
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Execute;
8888
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ExecuteImmediate;
89+
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Explain;
90+
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ExplainAnalyze;
8991
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Expression;
9092
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ExtendRegion;
9193
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Flush;
@@ -689,39 +691,59 @@ private IQueryExecution createQueryExecutionForTableModel(
689691
List<Expression> parameters = Collections.emptyList();
690692
Map<NodeRef<Parameter>, Expression> parameterLookup = Collections.emptyMap();
691693

692-
if (statement instanceof Execute) {
693-
Execute executeStatement = (Execute) statement;
694+
// Unwrap Explain/ExplainAnalyze to check for inner Execute/ExecuteImmediate
695+
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Statement innerStatement = statement;
696+
if (statement instanceof Explain) {
697+
innerStatement = ((Explain) statement).getStatement();
698+
} else if (statement instanceof ExplainAnalyze) {
699+
innerStatement = ((ExplainAnalyze) statement).getStatement();
700+
}
701+
702+
if (innerStatement instanceof Execute) {
703+
Execute executeStatement = (Execute) innerStatement;
694704
String statementName = executeStatement.getStatementName().getValue();
695705

696-
// Get prepared statement from session (contains cached AST)
697706
PreparedStatementInfo preparedInfo = clientSession.getPreparedStatement(statementName);
698707
if (preparedInfo == null) {
699708
throw new SemanticException(
700709
String.format("Prepared statement '%s' does not exist", statementName));
701710
}
702711

703-
// Use cached AST
704-
statementToUse = preparedInfo.getSql();
705-
706-
// Bind parameters: create parameterLookup map
707-
// Note: bindParameters() internally validates parameter count
712+
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Statement resolvedSql =
713+
preparedInfo.getSql();
708714
parameterLookup =
709-
ParameterExtractor.bindParameters(statementToUse, executeStatement.getParameters());
715+
ParameterExtractor.bindParameters(resolvedSql, executeStatement.getParameters());
710716
parameters = new ArrayList<>(executeStatement.getParameters());
711717

712-
} else if (statement instanceof ExecuteImmediate) {
713-
ExecuteImmediate executeImmediateStatement = (ExecuteImmediate) statement;
718+
if (statement instanceof Explain) {
719+
statementToUse = new Explain(resolvedSql);
720+
} else if (statement instanceof ExplainAnalyze) {
721+
statementToUse = new ExplainAnalyze(resolvedSql, ((ExplainAnalyze) statement).isVerbose());
722+
} else {
723+
statementToUse = resolvedSql;
724+
}
725+
726+
} else if (innerStatement instanceof ExecuteImmediate) {
727+
ExecuteImmediate executeImmediateStatement = (ExecuteImmediate) innerStatement;
714728

715-
// EXECUTE IMMEDIATE needs to parse SQL first
716729
String sql = executeImmediateStatement.getSqlString();
717730
List<Literal> literalParameters = executeImmediateStatement.getParameters();
718731

719-
statementToUse = sqlParser.createStatement(sql, clientSession.getZoneId(), clientSession);
732+
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Statement resolvedSql =
733+
sqlParser.createStatement(sql, clientSession.getZoneId(), clientSession);
720734

721735
if (!literalParameters.isEmpty()) {
722-
parameterLookup = ParameterExtractor.bindParameters(statementToUse, literalParameters);
736+
parameterLookup = ParameterExtractor.bindParameters(resolvedSql, literalParameters);
723737
parameters = new ArrayList<>(literalParameters);
724738
}
739+
740+
if (statement instanceof Explain) {
741+
statementToUse = new Explain(resolvedSql);
742+
} else if (statement instanceof ExplainAnalyze) {
743+
statementToUse = new ExplainAnalyze(resolvedSql, ((ExplainAnalyze) statement).isVerbose());
744+
} else {
745+
statementToUse = resolvedSql;
746+
}
725747
}
726748

727749
if (statement instanceof WrappedInsertStatement) {

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1790,13 +1790,28 @@ public Node visitStatementDefault(RelationalSqlParser.StatementDefaultContext ct
17901790

17911791
@Override
17921792
public Node visitExplain(RelationalSqlParser.ExplainContext ctx) {
1793-
return new Explain(getLocation(ctx), (Statement) visit(ctx.query()));
1793+
Statement innerStatement;
1794+
if (ctx.query() != null) {
1795+
innerStatement = (Statement) visit(ctx.query());
1796+
} else if (ctx.executeStatement() != null) {
1797+
innerStatement = (Statement) visit(ctx.executeStatement());
1798+
} else {
1799+
innerStatement = (Statement) visit(ctx.executeImmediateStatement());
1800+
}
1801+
return new Explain(getLocation(ctx), innerStatement);
17941802
}
17951803

17961804
@Override
17971805
public Node visitExplainAnalyze(RelationalSqlParser.ExplainAnalyzeContext ctx) {
1798-
return new ExplainAnalyze(
1799-
getLocation(ctx), ctx.VERBOSE() != null, (Statement) visit(ctx.query()));
1806+
Statement innerStatement;
1807+
if (ctx.query() != null) {
1808+
innerStatement = (Statement) visit(ctx.query());
1809+
} else if (ctx.executeStatement() != null) {
1810+
innerStatement = (Statement) visit(ctx.executeStatement());
1811+
} else {
1812+
innerStatement = (Statement) visit(ctx.executeImmediateStatement());
1813+
}
1814+
return new ExplainAnalyze(getLocation(ctx), ctx.VERBOSE() != null, innerStatement);
18001815
}
18011816

18021817
// ********************** author expressions ********************

iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -911,8 +911,8 @@ deallocateStatement
911911
// ------------------------------------------- Query Statement ---------------------------------------------------------
912912
queryStatement
913913
: query #statementDefault
914-
| EXPLAIN query #explain
915-
| EXPLAIN ANALYZE VERBOSE? query #explainAnalyze
914+
| EXPLAIN (query | executeStatement | executeImmediateStatement) #explain
915+
| EXPLAIN ANALYZE VERBOSE? (query | executeStatement | executeImmediateStatement) #explainAnalyze
916916
;
917917

918918
query

0 commit comments

Comments
 (0)