diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4 b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4 index 8cf376907f0881..414d2d5043a590 100644 --- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4 +++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisLexer.g4 @@ -714,9 +714,6 @@ BRACKETED_COMMENT ; -FROM_DUAL - : 'FROM' WS+ 'DUAL' -> channel(HIDDEN); - WS : [ \r\n\t]+ -> channel(HIDDEN) ; diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 index 2f895bc8cbd2cc..ae2103dbd8c267 100644 --- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 +++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 @@ -1253,7 +1253,8 @@ whereClause ; fromClause - : FROM relations + : FROM DUAL #fromDual + | FROM relations #fromRelations ; // For PL-SQL 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 72f7ce3d99ca9a..eaeca5669f3699 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 @@ -217,7 +217,8 @@ import org.apache.doris.nereids.DorisParser.ExpressionWithEofContext; import org.apache.doris.nereids.DorisParser.ExpressionWithOrderContext; import org.apache.doris.nereids.DorisParser.FixedPartitionDefContext; -import org.apache.doris.nereids.DorisParser.FromClauseContext; +import org.apache.doris.nereids.DorisParser.FromDualContext; +import org.apache.doris.nereids.DorisParser.FromRelationsContext; import org.apache.doris.nereids.DorisParser.FunctionArgumentsContext; import org.apache.doris.nereids.DorisParser.FunctionIdentifierContext; import org.apache.doris.nereids.DorisParser.GroupConcatContext; @@ -1878,8 +1879,8 @@ public LogicalPlan visitUpdate(UpdateContext ctx) { ) ); query = withTableAlias(query, ctx.tableAlias()); - if (ctx.fromClause() != null) { - query = withRelations(query, ctx.fromClause().relations().relation()); + if (ctx.fromClause() instanceof FromRelationsContext) { + query = withRelations(query, ((FromRelationsContext) ctx.fromClause()).relations().relation()); } query = withFilter(query, Optional.ofNullable(ctx.whereClause())); String tableAlias = null; @@ -2479,11 +2480,11 @@ public LogicalPlan visitRegularQuerySpecification(RegularQuerySpecificationConte SelectClauseContext selectCtx = ctx.selectClause(); LogicalPlan selectPlan; LogicalPlan relation; - if (ctx.fromClause() == null) { + if (ctx.fromClause() == null || ctx.fromClause() instanceof FromDualContext) { relation = new LogicalOneRowRelation(StatementScopeIdGenerator.newRelationId(), ImmutableList.of(new Alias(Literal.of(0)))); } else { - relation = visitFromClause(ctx.fromClause()); + relation = visitFromRelations((FromRelationsContext) ctx.fromClause()); } if (ctx.intoClause() != null && !ConnectContext.get().isRunProcedure()) { throw new ParseException("Only procedure supports insert into variables", selectCtx); @@ -3616,7 +3617,7 @@ public LogicalPlan visitRelation(RelationContext ctx) { } @Override - public LogicalPlan visitFromClause(FromClauseContext ctx) { + public LogicalPlan visitFromRelations(FromRelationsContext ctx) { return ParserUtils.withOrigin(ctx, () -> visitRelations(ctx.relations())); } diff --git a/regression-test/data/query_p0/dual/dual.out b/regression-test/data/query_p0/dual/dual.out index fee517f8f2cb06..fa14b5f70a735d 100644 --- a/regression-test/data/query_p0/dual/dual.out +++ b/regression-test/data/query_p0/dual/dual.out @@ -63,6 +63,21 @@ -- !sql -- 1 +-- !sql -- +1 2 + +-- !sql -- +2 + +-- !sql -- +1 + +-- !sql -- +1 + +-- !sql -- +3 + -- !sql -- 1 1 diff --git a/regression-test/suites/query_p0/dual/dual.groovy b/regression-test/suites/query_p0/dual/dual.groovy index 868fdce895be32..373b6c108f91ad 100644 --- a/regression-test/suites/query_p0/dual/dual.groovy +++ b/regression-test/suites/query_p0/dual/dual.groovy @@ -42,6 +42,36 @@ suite('dual') { qt_sql 'select a from (select 1 as a from dual union all select 2 as a from dual) u' qt_sql 'select row_number() over (order by 1) from dual;' + // Test CTE with name starting with 'dual' - should not be confused with FROM DUAL + qt_sql ''' + with dualtbl as ( + select 1 as col1, 2 as col2 + ) + select col1, col2 from dualtbl + ''' + + qt_sql ''' + with dualtbl as ( + select 1 as a + ), + dual2 as ( + select a + 1 as b from dualtbl + ) + select * from dual2 + ''' + + // Test subquery alias starting with 'dual' + qt_sql 'select * from (select 1 as x) dualtbl' + qt_sql 'select dualtbl.x from (select 1 as x) dualtbl' + + // Multiple CTEs referencing each other with dual-prefix names + qt_sql ''' + with duala as (select 1 as v), + dualb as (select v + 1 as v from duala), + dualc as (select v + 1 as v from dualb) + select * from dualc + ''' + // Dropping and creating a table named 'dual' to test behavior when dual is a real table sql 'drop table if exists `dual`' sql '''