This is an automated email from the ASF dual-hosted git repository.
morrySnow pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new fb0b0c5358e [refactor](parser) Dispatch parser statements by first
token (#66944)
fb0b0c5358e is described below
commit fb0b0c5358e341286c30d7af67a6b996a9d50b1d
Author: morrySnow <[email protected]>
AuthorDate: Mon Aug 31 23:53:30 2026 +0800
[refactor](parser) Dispatch parser statements by first token (#66944)
### What problem does this PR solve?
Problem Summary: `statementBase` previously grouped unrelated statement
families into one ANTLR adaptive prediction decision. This PR splits
cross-prefix families and dispatches them through first-token-specific
rules, changing generated `statementBase()` from one `adaptivePredict`
call to a direct `LA(1)` switch. It also removes the redundant
`supported` prefix from statement grammar rules and generated
Context/visitor names; dispatcher rules use the `*StatementDispatch`
suffix where the natural statement name is already occupied. All
concrete grammar alternatives and FE behavior are preserved. Parent CST
Context compatibility is intentionally out of scope because the
standalone parser CST is not a public API.
Shared-host JMH rounds were rejected because they did not pass the
predeclared noise gate, so this PR does not claim an unverified
wall-clock improvement.
---
.../doris/nereids/parser/LogicalPlanBuilder.java | 10 +-
.../parser/LogicalPlanBuilderForEncryption.java | 8 +-
.../parser/ParseInsertPartitionSpecTest.java | 29 +-
fe/fe-sql-parser/README.md | 8 +-
.../antlr4/org/apache/doris/nereids/DorisParser.g4 | 300 +++++++++++++++------
.../doris/sqlparser/StatementBaseDispatchTest.java | 160 +++++++++++
6 files changed, 397 insertions(+), 118 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index b27aa93a45f..8b7126d730a 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -465,7 +465,6 @@ import
org.apache.doris.nereids.DorisParser.StructLiteralContext;
import org.apache.doris.nereids.DorisParser.SubqueryContext;
import org.apache.doris.nereids.DorisParser.SubqueryExpressionContext;
import org.apache.doris.nereids.DorisParser.SubstringContext;
-import org.apache.doris.nereids.DorisParser.SupportedUnsetStatementContext;
import org.apache.doris.nereids.DorisParser.SwitchCatalogContext;
import org.apache.doris.nereids.DorisParser.SyncContext;
import org.apache.doris.nereids.DorisParser.SystemVariableContext;
@@ -479,6 +478,7 @@ import
org.apache.doris.nereids.DorisParser.TypeConstructorContext;
import org.apache.doris.nereids.DorisParser.UninstallPluginContext;
import org.apache.doris.nereids.DorisParser.UnitIdentifierContext;
import org.apache.doris.nereids.DorisParser.UnlockTablesContext;
+import org.apache.doris.nereids.DorisParser.UnsetStatementContext;
import org.apache.doris.nereids.DorisParser.UnsupportedStartTransactionContext;
import org.apache.doris.nereids.DorisParser.UpdateAssignmentContext;
import org.apache.doris.nereids.DorisParser.UpdateAssignmentSeqContext;
@@ -1254,7 +1254,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
? Maps.newHashMap(visitPropertyClause(ctx.jobProperties)) :
Maps.newHashMap();
String comment =
visitCommentSpec(ctx.commentSpec());
- String executeSql = ctx.supportedDmlStatement() == null ? "" :
getOriginSql(ctx.supportedDmlStatement());
+ String executeSql = ctx.dmlStatement() == null ? "" :
getOriginSql(ctx.dmlStatement());
JobFromToClauseContext jobFromToClauseCtx = ctx.jobFromToClause();
String sourceType = null;
String targetDb = null;
@@ -1273,7 +1273,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
return new CreateJobCommand(createJobInfo);
}
- private void checkJobNameKey(String key, String keyFormat,
DorisParser.SupportedJobStatementContext parseContext) {
+ private void checkJobNameKey(String key, String keyFormat,
ParserRuleContext parseContext) {
if (key.isEmpty() || !key.equalsIgnoreCase(keyFormat)) {
throw new ParseException(keyFormat + " should be: '" + keyFormat +
"'", parseContext);
}
@@ -1283,7 +1283,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
public LogicalPlan visitAlterJob(DorisParser.AlterJobContext ctx) {
Map<String, String> properties = ctx.propertyClause() != null
? Maps.newHashMap(visitPropertyClause(ctx.propertyClause())) :
Maps.newHashMap();
- String executeSql = ctx.supportedDmlStatement() != null ?
getOriginSql(ctx.supportedDmlStatement()) : "";
+ String executeSql = ctx.dmlStatement() != null ?
getOriginSql(ctx.dmlStatement()) : "";
String sourceType = null;
String targetDb = null;
Map<String, String> sourceProperties = Maps.newHashMap();
@@ -5568,7 +5568,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
}
@Override
- public LogicalPlan
visitSupportedUnsetStatement(SupportedUnsetStatementContext ctx) {
+ public LogicalPlan visitUnsetStatement(UnsetStatementContext ctx) {
if (ctx.DEFAULT() != null && ctx.STORAGE() != null && ctx.VAULT() !=
null) {
return new UnsetDefaultStorageVaultCommand();
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderForEncryption.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderForEncryption.java
index 006750dbe5d..1e074463d94 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderForEncryption.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilderForEncryption.java
@@ -227,8 +227,8 @@ public class LogicalPlanBuilderForEncryption extends
LogicalPlanBuilder {
// create job select tvf
@Override
public LogicalPlan
visitCreateScheduledJob(DorisParser.CreateScheduledJobContext ctx) {
- if (ctx.supportedDmlStatement() instanceof InsertTableContext) {
- visitInsertTable((InsertTableContext) ctx.supportedDmlStatement());
+ if (ctx.dmlStatement() instanceof InsertTableContext) {
+ visitInsertTable((InsertTableContext) ctx.dmlStatement());
} else if (ctx.jobFromToClause() != null) {
JobFromToClauseContext jobFromToClauseContext =
ctx.jobFromToClause();
encryptProperty(visitPropertyItemList(jobFromToClauseContext.sourceProperties),
@@ -242,8 +242,8 @@ public class LogicalPlanBuilderForEncryption extends
LogicalPlanBuilder {
// alter job select tvf
@Override
public LogicalPlan visitAlterJob(DorisParser.AlterJobContext ctx) {
- if (ctx.supportedDmlStatement() instanceof InsertTableContext) {
- visitInsertTable((InsertTableContext) ctx.supportedDmlStatement());
+ if (ctx.dmlStatement() instanceof InsertTableContext) {
+ visitInsertTable((InsertTableContext) ctx.dmlStatement());
} else if (ctx.jobFromToClause() != null) {
JobFromToClauseContext jobFromToClauseContext =
ctx.jobFromToClause();
encryptProperty(visitPropertyItemList(jobFromToClauseContext.sourceProperties),
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/ParseInsertPartitionSpecTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/ParseInsertPartitionSpecTest.java
index 487a224bc8a..70a3d0e6375 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/ParseInsertPartitionSpecTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/ParseInsertPartitionSpecTest.java
@@ -23,7 +23,6 @@ import
org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
import org.apache.doris.qe.ConnectContext;
import com.google.common.collect.Maps;
-import org.antlr.v4.runtime.ParserRuleContext;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
@@ -81,30 +80,12 @@ public class ParseInsertPartitionSpecTest {
}
/**
- * Helper method to parse SQL and extract PartitionSpecContext using
reflection.
+ * Helper method to parse SQL and extract PartitionSpecContext.
*/
- private Object parsePartitionSpec(String insertSql) throws Exception {
- // Use NereidsParser.toAst() to parse the SQL and get the AST
- ParserRuleContext tree = NereidsParser.toAst(
- insertSql, DorisParser::singleStatement);
-
- // The tree is a SingleStatementContext, which contains a
StatementContext
- // which contains a StatementBaseContext which contains an
InsertTableContext
- // Use reflection to navigate the AST structure
- Method getChildMethod = ParserRuleContext.class.getMethod("getChild",
int.class);
-
- // Get statement from singleStatement (index 0)
- Object statement = getChildMethod.invoke(tree, 0);
-
- // Get statementBase from statement (index 0)
- Object statementBase = getChildMethod.invoke(statement, 0);
-
- // Get insertTable from statementBase (index 0)
- Object insertTableCtx = getChildMethod.invoke(statementBase, 0);
-
- // Get partitionSpec() from insertTableCtx using the method
- Method partitionSpecMethod =
insertTableCtx.getClass().getMethod("partitionSpec");
- return partitionSpecMethod.invoke(insertTableCtx);
+ private DorisParser.PartitionSpecContext parsePartitionSpec(String
insertSql) {
+ DorisParser.InsertTableContext insertTableContext =
(DorisParser.InsertTableContext) NereidsParser.toAst(
+ insertSql, DorisParser::dmlStatement);
+ return insertTableContext.partitionSpec();
}
@Test
diff --git a/fe/fe-sql-parser/README.md b/fe/fe-sql-parser/README.md
index 73d2d509d46..3cab071b33f 100644
--- a/fe/fe-sql-parser/README.md
+++ b/fe/fe-sql-parser/README.md
@@ -385,8 +385,8 @@ import org.antlr.v4.runtime.tree.ParseTreeWalker;
public class DropGuardListener extends DorisParserBaseListener {
@Override
- public void
enterSupportedDropStatement(DorisParser.SupportedDropStatementContext ctx) {
- throw new SecurityException("DROP statements are not allowed: " +
ctx.getText());
+ public void enterDropTable(DorisParser.DropTableContext ctx) {
+ throw new SecurityException("DROP TABLE statements are not allowed: "
+ ctx.getText());
}
}
@@ -411,8 +411,8 @@ public class AuditListener extends DorisParserBaseListener {
@Override public void enterDelete(DorisParser.DeleteContext ctx) {
writes.add("DELETE " + ctx.tableName.getText());
}
- @Override public void
enterSupportedDropStatement(DorisParser.SupportedDropStatementContext ctx) {
- writes.add("DROP " + ctx.getText());
+ @Override public void enterDropTable(DorisParser.DropTableContext ctx) {
+ writes.add("DROP TABLE " + ctx.name.getText());
}
}
```
diff --git
a/fe/fe-sql-parser/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
b/fe/fe-sql-parser/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
index 42ac02d145e..2176dd3fb84 100644
--- a/fe/fe-sql-parser/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
+++ b/fe/fe-sql-parser/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
@@ -46,33 +46,97 @@ statement
;
statementBase
+ : queryOrDmlStatement
+ | createStatementDispatch
+ | alterStatementDispatch
+ | dropStatementDispatch
+ | showStatementDispatch
+ | pauseStatement
+ | resumeStatement
+ | cancelStatementDispatch
+ | refreshStatementDispatch
+ | killStatementDispatch
+ | buildStatement
+ | syncLoadStatement
+ | stopLoadStatement
+ | cleanStatement
+ | setStatement
+ | unsetStatement
+ | recoverStatement
+ | adminStatement
+ | useStatement
+ | analyzeStatsStatement
+ | transactionStatement
+ | grantRevokeStatement
+ ;
+
+queryOrDmlStatement
: explain? query outFileClause? #statementDefault
- | supportedDmlStatement #supportedDmlStatementAlias
- | supportedCreateStatement #supportedCreateStatementAlias
- | supportedAlterStatement #supportedAlterStatementAlias
- | materializedViewStatement #materializedViewStatementAlias
- | supportedJobStatement #supportedJobStatementAlias
- | constraintStatement #constraintStatementAlias
- | supportedCleanStatement #supportedCleanStatementAlias
- | supportedDescribeStatement #supportedDescribeStatementAlias
- | supportedDropStatement #supportedDropStatementAlias
- | supportedSetStatement #supportedSetStatementAlias
- | supportedUnsetStatement #supportedUnsetStatementAlias
- | supportedRefreshStatement #supportedRefreshStatementAlias
- | supportedShowStatement #supportedShowStatementAlias
- | supportedLoadStatement #supportedLoadStatementAlias
- | supportedCancelStatement #supportedCancelStatementAlias
- | supportedRecoverStatement #supportedRecoverStatementAlias
- | supportedAdminStatement #supportedAdminStatementAlias
- | supportedUseStatement #supportedUseStatementAlias
- | supportedOtherStatement #supportedOtherStatementAlias
- | supportedKillStatement #supportedKillStatementAlias
- | supportedStatsStatement #supportedStatsStatementAlias
- | supportedTransactionStatement #supportedTransactionStatementAlias
- | supportedGrantRevokeStatement #supportedGrantRevokeStatementAlias
- ;
-
-materializedViewStatement
+ | dmlStatement #dmlStatementAlias
+ | describeStatement #describeStatementAlias
+ | otherStatement #otherStatementAlias
+ | loadDmlStatement #loadStatementAlias
+ ;
+
+createStatementDispatch
+ : createStatement
+ | createMaterializedViewStatement
+ | createJobStatement
+ | createLoadStatement
+ ;
+
+alterStatementDispatch
+ : alterStatement
+ | alterMaterializedViewStatement
+ | alterJobStatement
+ | alterConstraintStatement
+ | alterStatsStatement
+ ;
+
+dropStatementDispatch
+ : dropStatement
+ | dropMaterializedViewStatement
+ | dropJobStatement
+ | dropStatsStatement
+ ;
+
+showStatementDispatch
+ : showStatement
+ | showMaterializedViewStatement
+ | showConstraintStatement
+ | showLoadStatement
+ | showStatsStatement
+ ;
+
+pauseStatement
+ : pauseMaterializedViewStatement
+ | pauseJobStatement
+ | pauseLoadStatement
+ ;
+
+resumeStatement
+ : resumeMaterializedViewStatement
+ | resumeJobStatement
+ | resumeLoadStatement
+ ;
+
+cancelStatementDispatch
+ : cancelMaterializedViewStatement
+ | cancelJobStatement
+ | cancelStatement
+ ;
+
+refreshStatementDispatch
+ : refreshMaterializedViewStatement
+ | refreshStatement
+ ;
+
+killStatementDispatch
+ : killStatement
+ | killStatsStatement
+ ;
+
+createMaterializedViewStatement
: CREATE MATERIALIZED VIEW (IF NOT EXISTS)? mvName=multipartIdentifier
(LEFT_PAREN cols=simpleColumnDefs RIGHT_PAREN)? buildMode?
(REFRESH refreshMethod? refreshTrigger?)?
@@ -83,17 +147,38 @@ materializedViewStatement
(BUCKETS (INTEGER_VALUE | AUTO))?)?
propertyClause?
AS? query
#createMTMV
- | REFRESH MATERIALIZED VIEW mvName=multipartIdentifier (partitionSpec |
COMPLETE | AUTO) #refreshMTMV
- | ALTER MATERIALIZED VIEW mvName=multipartIdentifier ((RENAME
renameNewName=multipartIdentifier)
+ ;
+
+refreshMaterializedViewStatement
+ : REFRESH MATERIALIZED VIEW mvName=multipartIdentifier (partitionSpec |
COMPLETE | AUTO) #refreshMTMV
+ ;
+
+alterMaterializedViewStatement
+ : ALTER MATERIALIZED VIEW mvName=multipartIdentifier ((RENAME
renameNewName=multipartIdentifier)
| (REFRESH (refreshMethod | refreshTrigger | refreshMethod
refreshTrigger))
| REPLACE WITH MATERIALIZED VIEW replaceNewName=identifier
propertyClause?
| (SET LEFT_PAREN fileProperties=propertyItemList RIGHT_PAREN))
#alterMTMV
- | DROP MATERIALIZED VIEW (IF EXISTS)? mvName=multipartIdentifier
+ ;
+
+dropMaterializedViewStatement
+ : DROP MATERIALIZED VIEW (IF EXISTS)? mvName=multipartIdentifier
(ON tableName=multipartIdentifier)?
#dropMV
- | PAUSE MATERIALIZED VIEW JOB ON mvName=multipartIdentifier
#pauseMTMV
- | RESUME MATERIALIZED VIEW JOB ON mvName=multipartIdentifier
#resumeMTMV
- | CANCEL MATERIALIZED VIEW TASK taskId=INTEGER_VALUE ON
mvName=multipartIdentifier #cancelMTMVTask
- | SHOW CREATE MATERIALIZED VIEW mvName=multipartIdentifier
#showCreateMTMV
+ ;
+
+pauseMaterializedViewStatement
+ : PAUSE MATERIALIZED VIEW JOB ON mvName=multipartIdentifier
#pauseMTMV
+ ;
+
+resumeMaterializedViewStatement
+ : RESUME MATERIALIZED VIEW JOB ON mvName=multipartIdentifier
#resumeMTMV
+ ;
+
+cancelMaterializedViewStatement
+ : CANCEL MATERIALIZED VIEW TASK taskId=INTEGER_VALUE ON
mvName=multipartIdentifier #cancelMTMVTask
+ ;
+
+showMaterializedViewStatement
+ : SHOW CREATE MATERIALIZED VIEW mvName=multipartIdentifier
#showCreateMTMV
;
jobFromToClause
@@ -101,7 +186,7 @@ jobFromToClause
TO DATABASE targetDb=identifier (LEFT_PAREN
targetProperties=propertyItemList RIGHT_PAREN)?
;
-supportedJobStatement
+createJobStatement
: CREATE JOB label=multipartIdentifier jobProperties=propertyClause?
ON (STREAMING | SCHEDULE(
(EVERY timeInterval=INTEGER_VALUE timeUnit=identifier
@@ -112,29 +197,49 @@ supportedJobStatement
)
)
commentSpec?
- (jobFromToClause | DO supportedDmlStatement )
#createScheduledJob
- | PAUSE JOB WHERE (jobNameKey=identifier) EQ (jobNameValue=STRING_LITERAL)
#pauseJob
- | ALTER JOB (jobName=multipartIdentifier)
- (propertyClause | supportedDmlStatement | propertyClause
supportedDmlStatement
- | jobFromToClause | propertyClause jobFromToClause)
#alterJob
- | DROP JOB (IF EXISTS)? WHERE (jobNameKey=identifier) EQ
(jobNameValue=STRING_LITERAL)
#dropJob
- | RESUME JOB WHERE (jobNameKey=identifier) EQ (jobNameValue=STRING_LITERAL)
#resumeJob
- | CANCEL TASK WHERE (jobNameKey=identifier) EQ
(jobNameValue=STRING_LITERAL) AND (taskIdKey=identifier) EQ
(taskIdValue=INTEGER_VALUE) #cancelJobTask
+ (jobFromToClause | DO dmlStatement )
#createScheduledJob
;
-constraintStatement
+
+pauseJobStatement
+ : PAUSE JOB WHERE (jobNameKey=identifier) EQ (jobNameValue=STRING_LITERAL)
#pauseJob
+ ;
+
+alterJobStatement
+ : ALTER JOB (jobName=multipartIdentifier)
+ (propertyClause | dmlStatement | propertyClause dmlStatement
+ | jobFromToClause | propertyClause jobFromToClause)
#alterJob
+ ;
+
+dropJobStatement
+ : DROP JOB (IF EXISTS)? WHERE (jobNameKey=identifier) EQ
(jobNameValue=STRING_LITERAL)
#dropJob
+ ;
+
+resumeJobStatement
+ : RESUME JOB WHERE (jobNameKey=identifier) EQ
(jobNameValue=STRING_LITERAL)
#resumeJob
+ ;
+
+cancelJobStatement
+ : CANCEL TASK WHERE (jobNameKey=identifier) EQ
(jobNameValue=STRING_LITERAL)
+ AND (taskIdKey=identifier) EQ (taskIdValue=INTEGER_VALUE)
#cancelJobTask
+ ;
+
+alterConstraintStatement
: ALTER TABLE table=multipartIdentifier
ADD CONSTRAINT constraintName=errorCapturingIdentifier
constraint
#addConstraint
| ALTER TABLE table=multipartIdentifier
DROP CONSTRAINT constraintName=errorCapturingIdentifier
#dropConstraint
- | SHOW CONSTRAINTS FROM table=multipartIdentifier
#showConstraint
+ ;
+
+showConstraintStatement
+ : SHOW CONSTRAINTS FROM table=multipartIdentifier
#showConstraint
;
optSpecBranch
: ATSIGN BRANCH LEFT_PAREN name=identifier RIGHT_PAREN
;
-supportedDmlStatement
+dmlStatement
: explain? cte? INSERT INTO tvfName=identifier
LEFT_PAREN tvfProperties=propertyItemList RIGHT_PAREN
(WITH LABEL labelName=identifier)?
@@ -188,7 +293,7 @@ mergeNotMatchedClause
INSERT cols=identifierList? VALUES rowConstructor
;
-supportedCreateStatement
+createStatement
: CREATE (EXTERNAL | TEMPORARY)? TABLE (IF NOT EXISTS)?
name=multipartIdentifier
((ctasCols=identifierList)? | (LEFT_PAREN columnDefs (COMMA
indexDefs)? COMMA? RIGHT_PAREN))
(ENGINE EQ engine=identifier)?
@@ -233,8 +338,6 @@ supportedCreateStatement
USING LEFT_PAREN booleanExpression RIGHT_PAREN
#createRowPolicy
| CREATE STORAGE POLICY (IF NOT EXISTS)?
name=identifier properties=propertyClause?
#createStoragePolicy
- | BUILD INDEX (name=identifier)? ON tableName=multipartIdentifier
- partitionSpec?
#buildIndex
| CREATE INDEX (IF NOT EXISTS)? name=identifier
ON tableName=multipartIdentifier identifierList
(USING indexType=(BLOOMFILTER | NGRAM_BF | INVERTED | ANN))?
@@ -284,13 +387,18 @@ supportedCreateStatement
name=identifier properties=propertyClause?
#createIndexNormalizer
;
+buildStatement
+ : BUILD INDEX (name=identifier)? ON tableName=multipartIdentifier
+ partitionSpec?
#buildIndex
+ ;
+
dictionaryColumnDefs:
dictionaryColumnDef (COMMA dictionaryColumnDef)*;
dictionaryColumnDef:
colName = identifier columnType = (KEY | VALUE) ;
-supportedAlterStatement
+alterStatement
: ALTER SYSTEM alterSystemClause
#alterSystem
| ALTER VIEW name=multipartIdentifier
(MODIFY commentSpec |
@@ -345,7 +453,7 @@ supportedAlterStatement
passwordOption requireClause? commentSpec?
#alterUser
;
-supportedDropStatement
+dropStatement
: DROP CATALOG RECYCLE BIN WHERE idType=STRING_LITERAL EQ id=INTEGER_VALUE
#dropCatalogRecycleBin
| DROP ENCRYPTKEY (IF EXISTS)? name=multipartIdentifier
#dropEncryptkey
| DROP ROLE (IF EXISTS)? name=identifierOrText
#dropRole
@@ -381,7 +489,7 @@ supportedDropStatement
| DROP STREAM (IF EXISTS)? name=multipartIdentifier FORCE?
#dropStream
;
-supportedShowStatement
+showStatement
: SHOW statementScope? VARIABLES wildWhere?
#showVariables
| SHOW AUTHORS
#showAuthors
| SHOW ALTER TABLE (ROLLUP | (MATERIALIZED VIEW) | COLUMN)
@@ -504,19 +612,37 @@ supportedShowStatement
| SHOW CREATE STREAM name=multipartIdentifier
#showCreateStream
;
-supportedLoadStatement
+syncLoadStatement
: SYNC
#sync
- | SHOW CREATE LOAD FOR label=multipartIdentifier
#showCreateLoad
- | createRoutineLoad
#createRoutineLoadAlias
- | LOAD mysqlDataDesc
+ ;
+
+createLoadStatement
+ : createRoutineLoad
#createRoutineLoadAlias
+ ;
+
+loadDmlStatement
+ : LOAD mysqlDataDesc
(PROPERTIES LEFT_PAREN properties=propertyItemList RIGHT_PAREN)?
(commentSpec)?
#mysqlLoad
- | SHOW ALL? CREATE ROUTINE LOAD FOR label=multipartIdentifier
#showCreateRoutineLoad
- | PAUSE ROUTINE LOAD FOR label=multipartIdentifier
#pauseRoutineLoad
+ ;
+
+pauseLoadStatement
+ : PAUSE ROUTINE LOAD FOR label=multipartIdentifier
#pauseRoutineLoad
| PAUSE ALL ROUTINE LOAD
#pauseAllRoutineLoad
- | RESUME ROUTINE LOAD FOR label=multipartIdentifier
#resumeRoutineLoad
+ ;
+
+resumeLoadStatement
+ : RESUME ROUTINE LOAD FOR label=multipartIdentifier
#resumeRoutineLoad
| RESUME ALL ROUTINE LOAD
#resumeAllRoutineLoad
- | STOP ROUTINE LOAD FOR label=multipartIdentifier
#stopRoutineLoad
+ ;
+
+stopLoadStatement
+ : STOP ROUTINE LOAD FOR label=multipartIdentifier
#stopRoutineLoad
+ ;
+
+showLoadStatement
+ : SHOW CREATE LOAD FOR label=multipartIdentifier
#showCreateLoad
+ | SHOW ALL? CREATE ROUTINE LOAD FOR label=multipartIdentifier
#showCreateRoutineLoad
| SHOW ALL? ROUTINE LOAD ((FOR label=multipartIdentifier) | (LIKE
STRING_LITERAL)?) #showRoutineLoad
| SHOW ROUTINE LOAD TASK ((FOR label=multipartIdentifier)
| (((FROM | IN) database=identifier)? wildWhere?))
#showRoutineLoadTask
@@ -527,12 +653,12 @@ supportedLoadStatement
| SHOW INVERTED INDEX NORMALIZER
#showIndexNormalizer
;
-supportedKillStatement
+killStatement
: KILL (CONNECTION)? INTEGER_VALUE
#killConnection
| KILL QUERY (INTEGER_VALUE | STRING_LITERAL)
#killQuery
;
-supportedOtherStatement
+otherStatement
: HELP mark=identifierOrText
#help
| UNLOCK TABLES
#unlockTables
| INSTALL PLUGIN FROM source=identifierOrText properties=propertyClause?
#installPlugin
@@ -618,7 +744,7 @@ importColumnDesc
| LEFT_PAREN name=identifier (EQ booleanExpression)? RIGHT_PAREN
;
-supportedRefreshStatement
+refreshStatement
: REFRESH CATALOG name=identifier propertyClause?
#refreshCatalog
| REFRESH DATABASE name=multipartIdentifier propertyClause?
#refreshDatabase
| REFRESH TABLE name=multipartIdentifier
#refreshTable
@@ -626,7 +752,7 @@ supportedRefreshStatement
| REFRESH LDAP (ALL | (FOR user=identifierOrText))?
#refreshLdap
;
-supportedCleanStatement
+cleanStatement
: CLEAN ALL PROFILE
#cleanAllProfile
| CLEAN LABEL label=identifier? (FROM | IN) database=identifier
#cleanLabel
| CLEAN QUERY STATS ((FOR database=identifier)
@@ -634,7 +760,7 @@ supportedCleanStatement
| CLEAN ALL QUERY STATS
#cleanAllQueryStats
;
-supportedCancelStatement
+cancelStatement
: CANCEL LOAD ((FROM | IN) database=identifier)? wildWhere?
#cancelLoad
| CANCEL EXPORT ((FROM | IN) database=identifier)? wildWhere?
#cancelExport
| CANCEL WARM UP JOB wildWhere?
#cancelWarmUpJob
@@ -650,7 +776,7 @@ supportedCancelStatement
(COMMA jobIds+=INTEGER_VALUE)* RIGHT_PAREN)?
#cancelAlterTable
;
-supportedAdminStatement
+adminStatement
: ADMIN SHOW REPLICA DISTRIBUTION FROM baseTableRef
#adminShowReplicaDistribution
| ADMIN REBALANCE DISK (ON LEFT_PAREN backends+=STRING_LITERAL
(COMMA backends+=STRING_LITERAL)* RIGHT_PAREN)?
#adminRebalanceDisk
@@ -690,7 +816,7 @@ roleMappingRuleClause
RIGHT_PAREN
;
-supportedRecoverStatement
+recoverStatement
: RECOVER DATABASE name=identifier id=INTEGER_VALUE? (AS
alias=identifier)? #recoverDatabase
| RECOVER TABLE name=multipartIdentifier
id=INTEGER_VALUE? (AS alias=identifier)?
#recoverTable
@@ -708,13 +834,13 @@ wildWhere
| WHERE expression
;
-supportedTransactionStatement
+transactionStatement
: BEGIN (WITH LABEL identifier?)?
#transactionBegin
| COMMIT WORK? (AND NO? CHAIN)? (NO? RELEASE)?
#transcationCommit
| ROLLBACK WORK? (AND NO? CHAIN)? (NO? RELEASE)?
#transactionRollback
;
-supportedGrantRevokeStatement
+grantRevokeStatement
: GRANT privilegeList ON multipartIdentifierOrAsterisk
TO (userIdentify | ROLE identifierOrText)
#grantTablePrivilege
| GRANT privilegeList ON
@@ -882,7 +1008,7 @@ fromRollup
: FROM rollup=identifier
;
-supportedStatsStatement
+showStatsStatement
: SHOW AUTO? ANALYZE (jobId=INTEGER_VALUE | tableName=multipartIdentifier)?
(WHERE (stateKey=identifier) EQ (stateValue=STRING_LITERAL))?
#showAnalyze
| SHOW QUEUED ANALYZE JOBS tableName=multipartIdentifier?
@@ -892,25 +1018,37 @@ supportedStatsStatement
| SHOW COLUMN CACHED? STATS tableName=multipartIdentifier
columnList=identifierList? partitionSpec?
#showColumnStats
| SHOW ANALYZE TASK STATUS jobId=INTEGER_VALUE
#showAnalyzeTask
- | ANALYZE DATABASE name=multipartIdentifier
+ | SHOW INDEX STATS tableName=multipartIdentifier indexId=identifier
#showIndexStats
+ | SHOW TABLE STATS tableName=multipartIdentifier
+ partitionSpec? columnList=identifierList?
#showTableStats
+ | SHOW TABLE STATS tableId=INTEGER_VALUE
#showTableStats
+ ;
+
+analyzeStatsStatement
+ : ANALYZE DATABASE name=multipartIdentifier
(WITH analyzeProperties)* propertyClause?
#analyzeDatabase
| ANALYZE TABLE name=multipartIdentifier partitionSpec?
columns=identifierList? (WITH analyzeProperties)* propertyClause?
#analyzeTable
- | ALTER TABLE name=multipartIdentifier SET STATS
+ ;
+
+alterStatsStatement
+ : ALTER TABLE name=multipartIdentifier SET STATS
LEFT_PAREN propertyItemList RIGHT_PAREN partitionSpec?
#alterTableStats
| ALTER TABLE name=multipartIdentifier (INDEX indexName=identifier)?
MODIFY COLUMN columnName=identifier
SET STATS LEFT_PAREN propertyItemList RIGHT_PAREN partitionSpec?
#alterColumnStats
- | SHOW INDEX STATS tableName=multipartIdentifier indexId=identifier
#showIndexStats
- | DROP STATS tableName=multipartIdentifier
+ ;
+
+dropStatsStatement
+ : DROP STATS tableName=multipartIdentifier
columns=identifierList? partitionSpec?
#dropStats
| DROP CACHED STATS tableName=multipartIdentifier
#dropCachedStats
| DROP EXPIRED STATS
#dropExpiredStats
- | KILL ANALYZE jobId=INTEGER_VALUE
#killAnalyzeJob
| DROP ANALYZE JOB INTEGER_VALUE
#dropAnalyzeJob
- | SHOW TABLE STATS tableName=multipartIdentifier
- partitionSpec? columnList=identifierList?
#showTableStats
- | SHOW TABLE STATS tableId=INTEGER_VALUE
#showTableStats
+ ;
+
+killStatsStatement
+ : KILL ANALYZE jobId=INTEGER_VALUE
#killAnalyzeJob
;
analyzeProperties
@@ -979,7 +1117,7 @@ dataTypeList
: dataType (COMMA dataType)*
;
-supportedSetStatement
+setStatement
: SET (optionWithType | optionWithoutType)
(COMMA (optionWithType | optionWithoutType))*
#setOptions
| SET identifier AS DEFAULT STORAGE VAULT
#setDefaultStorageVault
@@ -1021,12 +1159,12 @@ isolationLevel
: ISOLATION LEVEL ((READ UNCOMMITTED) | (READ COMMITTED) | (REPEATABLE
READ) | (SERIALIZABLE))
;
-supportedUnsetStatement
+unsetStatement
: UNSET statementScope? VARIABLE (ALL | identifier)
| UNSET DEFAULT STORAGE VAULT
;
-supportedUseStatement
+useStatement
: SWITCH catalog=identifier
#switchCatalog
| USE (catalog=identifier DOT)? database=identifier
#useDatabase
| USE ((catalog=identifier DOT)? database=identifier)? ATSIGN
cluster=identifier #useCloudCluster
@@ -1037,7 +1175,7 @@ stageAndPattern
(LEFT_PAREN pattern=STRING_LITERAL RIGHT_PAREN)?
;
-supportedDescribeStatement
+describeStatement
: explainCommand FUNCTION tvfName=identifier LEFT_PAREN
(properties=propertyItemList)? RIGHT_PAREN tableAlias
#describeTableValuedFunction
| explainCommand multipartIdentifier ALL
#describeTableAll
diff --git
a/fe/fe-sql-parser/src/test/java/org/apache/doris/sqlparser/StatementBaseDispatchTest.java
b/fe/fe-sql-parser/src/test/java/org/apache/doris/sqlparser/StatementBaseDispatchTest.java
new file mode 100644
index 00000000000..13eb8dd45f8
--- /dev/null
+++
b/fe/fe-sql-parser/src/test/java/org/apache/doris/sqlparser/StatementBaseDispatchTest.java
@@ -0,0 +1,160 @@
+// 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.doris.sqlparser;
+
+import org.apache.doris.nereids.DorisParser.MultiStatementsContext;
+import org.apache.doris.nereids.DorisParser.SingleStatementContext;
+import org.apache.doris.nereids.exceptions.ParseException;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+class StatementBaseDispatchTest {
+ private final DorisSqlParser parser = new DorisSqlParser();
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("statementFamilies")
+ void parsesEveryStatementFamily(String family, String sql) {
+ assertParses(sql);
+ }
+
+ private static Stream<Arguments> statementFamilies() {
+ return Stream.of(
+ Arguments.of("query", "SELECT 1"),
+ Arguments.of("dml", "INSERT INTO t SELECT 1"),
+ Arguments.of("create", "CREATE DATABASE db"),
+ Arguments.of("alter", "ALTER TABLE t RENAME t2"),
+ Arguments.of("materialized view", "CREATE MATERIALIZED VIEW mv
AS SELECT 1"),
+ Arguments.of("job", "PAUSE JOB WHERE jobName = 'job'"),
+ Arguments.of("constraint", "SHOW CONSTRAINTS FROM t"),
+ Arguments.of("clean", "CLEAN ALL PROFILE"),
+ Arguments.of("describe", "DESCRIBE t"),
+ Arguments.of("drop", "DROP TABLE t"),
+ Arguments.of("set", "SET x = 1"),
+ Arguments.of("unset", "UNSET VARIABLE x"),
+ Arguments.of("refresh", "REFRESH TABLE t"),
+ Arguments.of("show", "SHOW TABLES"),
+ Arguments.of("load", "SYNC"),
+ Arguments.of("cancel", "CANCEL LOAD"),
+ Arguments.of("recover", "RECOVER TABLE t"),
+ Arguments.of("admin", "ADMIN CLEAN TRASH"),
+ Arguments.of("use", "USE db"),
+ Arguments.of("other", "HELP 'SHOW'"),
+ Arguments.of("kill", "KILL 1"),
+ Arguments.of("stats", "ANALYZE TABLE t"),
+ Arguments.of("transaction", "BEGIN"),
+ Arguments.of("grant/revoke", "GRANT ALL ON db.t TO
'user'@'%'"));
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("sharedPrefixStatements")
+ void parsesSharedFirstTokenStatements(String branch, String sql) {
+ assertParses(sql);
+ }
+
+ private static Stream<Arguments> sharedPrefixStatements() {
+ return Stream.of(
+ Arguments.of("CREATE ddl", "CREATE DATABASE db"),
+ Arguments.of("CREATE mv", "CREATE MATERIALIZED VIEW mv AS
SELECT 1"),
+ Arguments.of("CREATE job",
+ "CREATE JOB db.job ON SCHEDULE AT '2026-01-01
00:00:00' DO INSERT INTO t SELECT 1"),
+ Arguments.of("CREATE load",
+ "CREATE ROUTINE LOAD db.job ON t PROPERTIES
(\"max_batch_interval\" = \"10\") "
+ + "FROM KAFKA (\"kafka_broker_list\" =
\"localhost:9092\")"),
+ Arguments.of("ALTER ddl", "ALTER TABLE t RENAME t2"),
+ Arguments.of("ALTER mv", "ALTER MATERIALIZED VIEW mv RENAME
mv2"),
+ Arguments.of("ALTER job", "ALTER JOB db.job PROPERTIES (\"k\"
= \"v\")"),
+ Arguments.of("ALTER constraint", "ALTER TABLE t ADD CONSTRAINT
pk PRIMARY KEY (id)"),
+ Arguments.of("ALTER stats", "ALTER TABLE t SET STATS
(\"row_count\" = \"1\")"),
+ Arguments.of("SHOW command", "SHOW TABLES"),
+ Arguments.of("SHOW mv", "SHOW CREATE MATERIALIZED VIEW mv"),
+ Arguments.of("SHOW constraint", "SHOW CONSTRAINTS FROM t"),
+ Arguments.of("SHOW load", "SHOW ROUTINE LOAD"),
+ Arguments.of("SHOW stats", "SHOW ANALYZE"),
+ Arguments.of("DROP ddl", "DROP TABLE t"),
+ Arguments.of("DROP mv", "DROP MATERIALIZED VIEW mv"),
+ Arguments.of("DROP job", "DROP JOB WHERE jobName = 'job'"),
+ Arguments.of("DROP stats", "DROP STATS t"),
+ Arguments.of("PAUSE mv", "PAUSE MATERIALIZED VIEW JOB ON mv"),
+ Arguments.of("PAUSE job", "PAUSE JOB WHERE jobName = 'job'"),
+ Arguments.of("PAUSE load", "PAUSE ROUTINE LOAD FOR db.job"),
+ Arguments.of("RESUME mv", "RESUME MATERIALIZED VIEW JOB ON
mv"),
+ Arguments.of("RESUME job", "RESUME JOB WHERE jobName = 'job'"),
+ Arguments.of("RESUME load", "RESUME ROUTINE LOAD FOR db.job"),
+ Arguments.of("CANCEL mv", "CANCEL MATERIALIZED VIEW TASK 1 ON
mv"),
+ Arguments.of("CANCEL job", "CANCEL TASK WHERE jobName = 'job'
AND taskId = 1"),
+ Arguments.of("CANCEL command", "CANCEL LOAD"),
+ Arguments.of("REFRESH mv", "REFRESH MATERIALIZED VIEW mv
COMPLETE"),
+ Arguments.of("REFRESH command", "REFRESH TABLE t"),
+ Arguments.of("KILL connection", "KILL 1"),
+ Arguments.of("KILL analyze", "KILL ANALYZE 1"));
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("delayedDispatchStatements")
+ void parsesStatementsWhoseBranchNeedsMoreLookahead(String branch, String
sql) {
+ assertParses(sql);
+ }
+
+ private static Stream<Arguments> delayedDispatchStatements() {
+ return Stream.of(
+ Arguments.of("EXPLAIN query", "EXPLAIN SELECT 1"),
+ Arguments.of("EXPLAIN dml", "EXPLAIN INSERT INTO t SELECT 1"),
+ Arguments.of("WITH query", "WITH c AS (SELECT 1) SELECT * FROM
c"),
+ Arguments.of("WITH dml", "WITH c AS (SELECT 1) INSERT INTO t
SELECT * FROM c"),
+ Arguments.of("parenthesized query", "(SELECT 1)"),
+ Arguments.of("query outfile", "SELECT 1 INTO OUTFILE
'file:///tmp/result'"),
+ Arguments.of("warm-up explain", "EXPLAIN WARM UP SELECT * FROM
t"),
+ Arguments.of("describe", "DESC t"));
+ }
+
+ @ParameterizedTest(name = "truncated: {0}")
+ @MethodSource("truncatedStatements")
+ void rejectsTruncatedSharedPrefixesAtEndOfInput(String sql) {
+ ParseException exception =
Assertions.assertThrows(ParseException.class, () -> parser.parseStatement(sql));
+ Assertions.assertTrue(exception.getMessage().contains("line 1, pos " +
sql.length()), exception::getMessage);
+ }
+
+ private static Stream<String> truncatedStatements() {
+ return Stream.of("CREATE", "ALTER TABLE", "SHOW", "DROP", "ADMIN",
"CANCEL", "REFRESH", "KILL");
+ }
+
+ @Test
+ void parsesMultiStatementsWithCommentsAndExtraSemicolons() {
+ String sql = "; /* leading */ SELECT 1;; CREATE DATABASE db; --
comment\n SHOW TABLES;;";
+ MultiStatementsContext context = parser.parseStatements(sql);
+ Assertions.assertEquals(3, context.statement().size());
+ }
+
+ @Test
+ void rejectsInvalidMiddleStatement() {
+ String sql = "SELECT 1; CREATE; SHOW TABLES";
+ ParseException exception =
Assertions.assertThrows(ParseException.class, () ->
parser.parseStatements(sql));
+ Assertions.assertTrue(exception.getMessage().contains("line 1, pos
16"), exception::getMessage);
+ }
+
+ private void assertParses(String sql) {
+ SingleStatementContext context = parser.parseStatement(sql);
+ Assertions.assertNotNull(context.statement());
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]