From 899c08bf0407e16bfab0ed921df8a119b02d0d1b Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Wed, 25 Feb 2026 12:15:15 +0100 Subject: [PATCH] Only parse FROM identifier in CTE if using Hive --- src/parser/mod.rs | 2 +- tests/sqlparser_common.rs | 123 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index bb11d79c2..b4bdd79e0 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -13958,7 +13958,7 @@ impl<'a> Parser<'a> { closing_paren_token: closing_paren_token.into(), } }; - if self.parse_keyword(Keyword::FROM) { + if dialect_of!(self is HiveDialect) && self.parse_keyword(Keyword::FROM) { cte.from = Some(self.parse_identifier()?); } Ok(cte) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index a3b5404d3..8c7595f6c 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -16073,6 +16073,129 @@ fn test_select_from_first() { } } +#[test] +fn test_select_from_first_with_cte() { + let dialects = all_dialects_where(|d| d.supports_from_first_select()); + let q = "WITH test AS (FROM t SELECT a) FROM test SELECT 1"; + + let ast = dialects.verified_query(q); + + let expected = Query { + with: Some(With { + with_token: AttachedToken::empty(), + recursive: false, + cte_tables: vec![Cte { + alias: TableAlias { + explicit: false, + name: Ident { + value: "test".to_string(), + quote_style: None, + span: Span::empty(), + }, + columns: vec![], + }, + query: Box::new(Query { + with: None, + body: Box::new(SetExpr::Select(Box::new(Select { + select_token: AttachedToken::empty(), + optimizer_hints: vec![], + distinct: None, + select_modifiers: None, + top: None, + projection: vec![SelectItem::UnnamedExpr(Expr::Identifier(Ident { + value: "a".to_string(), + quote_style: None, + span: Span::empty(), + }))], + exclude: None, + top_before_distinct: false, + into: None, + from: vec![TableWithJoins { + relation: table_from_name(ObjectName::from(vec![Ident { + value: "t".to_string(), + quote_style: None, + span: Span::empty(), + }])), + joins: vec![], + }], + lateral_views: vec![], + prewhere: None, + selection: None, + group_by: GroupByExpr::Expressions(vec![], vec![]), + cluster_by: vec![], + distribute_by: vec![], + sort_by: vec![], + having: None, + named_window: vec![], + window_before_qualify: false, + qualify: None, + value_table_mode: None, + connect_by: vec![], + flavor: SelectFlavor::FromFirst, + }))), + order_by: None, + limit_clause: None, + fetch: None, + locks: vec![], + for_clause: None, + settings: None, + format_clause: None, + pipe_operators: vec![], + }), + from: None, + materialized: None, + closing_paren_token: AttachedToken::empty(), + }], + }), + body: Box::new(SetExpr::Select(Box::new(Select { + select_token: AttachedToken::empty(), + optimizer_hints: vec![], + distinct: None, + select_modifiers: None, + top: None, + projection: vec![SelectItem::UnnamedExpr(Expr::Value(ValueWithSpan { + value: test_utils::number("1"), + span: Span::empty(), + }))], + exclude: None, + top_before_distinct: false, + into: None, + from: vec![TableWithJoins { + relation: table_from_name(ObjectName::from(vec![Ident { + value: "test".to_string(), + quote_style: None, + span: Span::empty(), + }])), + joins: vec![], + }], + lateral_views: vec![], + prewhere: None, + selection: None, + group_by: GroupByExpr::Expressions(vec![], vec![]), + cluster_by: vec![], + distribute_by: vec![], + sort_by: vec![], + having: None, + named_window: vec![], + window_before_qualify: false, + qualify: None, + value_table_mode: None, + connect_by: vec![], + flavor: SelectFlavor::FromFirst, + }))), + order_by: None, + limit_clause: None, + fetch: None, + locks: vec![], + for_clause: None, + settings: None, + format_clause: None, + pipe_operators: vec![], + }; + assert_eq!(expected, ast); + assert_eq!(ast.to_string(), q); +} + #[test] fn test_geometric_unary_operators() { // Number of points in path or polygon