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 c2aff94c4617c4..23af3ac02e971b 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 @@ -692,9 +692,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 783b0f5f6d85c4..55270771dfd39a 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 @@ -1222,7 +1222,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 171d6ed137bf6a..7c981bc1c5570e 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 @@ -111,7 +111,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.GroupingElementContext; import org.apache.doris.nereids.DorisParser.GroupingSetContext; import org.apache.doris.nereids.DorisParser.HavingClauseContext; @@ -1026,8 +1027,8 @@ public LogicalPlan visitUpdate(UpdateContext ctx) { LogicalPlan query = LogicalPlanBuilderAssistant.withCheckPolicy(new UnboundRelation( StatementScopeIdGenerator.newRelationId(), visitMultipartIdentifier(ctx.tableName))); 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; @@ -1452,7 +1453,7 @@ public LogicalPlan visitRegularQuerySpecification(RegularQuerySpecificationConte SelectClauseContext selectCtx = ctx.selectClause(); LogicalPlan selectPlan; LogicalPlan relation; - if (ctx.fromClause() == null) { + if (ctx.fromClause() == null || ctx.fromClause() instanceof FromDualContext) { SelectColumnClauseContext columnCtx = selectCtx.selectColumnClause(); if (columnCtx.EXCEPT() != null) { throw new ParseException("select-except cannot be used in one row relation", selectCtx); @@ -1460,7 +1461,7 @@ public LogicalPlan visitRegularQuerySpecification(RegularQuerySpecificationConte 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); @@ -2429,7 +2430,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 964ca49f3ab2c1..5fc41bf923a71c 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 '''