diff --git a/src/ast/query.rs b/src/ast/query.rs index fe650e6b..5f4c289d 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -110,7 +110,10 @@ impl fmt::Display for SetExpr { } => { write!(f, "{left} {op}")?; match set_quantifier { - SetQuantifier::All | SetQuantifier::Distinct => write!(f, " {set_quantifier}")?, + SetQuantifier::All + | SetQuantifier::Distinct + | SetQuantifier::ByName + | SetQuantifier::AllByName => write!(f, " {set_quantifier}")?, SetQuantifier::None => write!(f, "{set_quantifier}")?, } write!(f, " {right}")?; @@ -148,6 +151,8 @@ impl fmt::Display for SetOperator { pub enum SetQuantifier { All, Distinct, + ByName, + AllByName, None, } @@ -156,6 +161,8 @@ impl fmt::Display for SetQuantifier { match self { SetQuantifier::All => write!(f, "ALL"), SetQuantifier::Distinct => write!(f, "DISTINCT"), + SetQuantifier::ByName => write!(f, "BY NAME"), + SetQuantifier::AllByName => write!(f, "ALL BY NAME"), SetQuantifier::None => write!(f, ""), } } diff --git a/src/keywords.rs b/src/keywords.rs index 32556c6d..80c605dd 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -384,6 +384,7 @@ define_keywords!( MSCK, MULTISET, MUTATION, + NAME, NANOSECOND, NANOSECONDS, NATIONAL, diff --git a/src/parser.rs b/src/parser.rs index d9b85fcc..4d331ce0 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -5331,8 +5331,14 @@ impl<'a> Parser<'a> { pub fn parse_set_quantifier(&mut self, op: &Option) -> SetQuantifier { match op { Some(SetOperator::Union) => { - if self.parse_keyword(Keyword::ALL) { - SetQuantifier::All + if self.parse_keywords(&[Keyword::BY, Keyword::NAME]) { + SetQuantifier::ByName + } else if self.parse_keyword(Keyword::ALL) { + if self.parse_keywords(&[Keyword::BY, Keyword::NAME]) { + SetQuantifier::AllByName + } else { + SetQuantifier::All + } } else if self.parse_keyword(Keyword::DISTINCT) { SetQuantifier::Distinct } else { diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index fabb9790..83b1e537 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -129,3 +129,150 @@ fn test_create_table_macro() { }; assert_eq!(expected, macro_); } + +#[test] +fn test_select_union_by_name() { + let ast = duckdb().verified_query("SELECT * FROM capitals UNION BY NAME SELECT * FROM weather"); + let expected = Box::::new(SetExpr::SetOperation { + op: SetOperator::Union, + set_quantifier: SetQuantifier::ByName, + left: Box::::new(SetExpr::Select(Box::new(Select { + distinct: None, + top: None, + projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions { + opt_exclude: None, + opt_except: None, + opt_rename: None, + opt_replace: None, + })], + into: None, + from: vec![TableWithJoins { + relation: TableFactor::Table { + name: ObjectName(vec![Ident { + value: "capitals".to_string(), + quote_style: None, + }]), + alias: None, + args: None, + with_hints: vec![], + }, + joins: vec![], + }], + lateral_views: vec![], + selection: None, + group_by: vec![], + cluster_by: vec![], + distribute_by: vec![], + sort_by: vec![], + having: None, + named_window: vec![], + qualify: None, + }))), + right: Box::::new(SetExpr::Select(Box::new(Select { + distinct: None, + top: None, + projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions { + opt_exclude: None, + opt_except: None, + opt_rename: None, + opt_replace: None, + })], + into: None, + from: vec![TableWithJoins { + relation: TableFactor::Table { + name: ObjectName(vec![Ident { + value: "weather".to_string(), + quote_style: None, + }]), + alias: None, + args: None, + with_hints: vec![], + }, + joins: vec![], + }], + lateral_views: vec![], + selection: None, + group_by: vec![], + cluster_by: vec![], + distribute_by: vec![], + sort_by: vec![], + having: None, + named_window: vec![], + qualify: None, + }))), + }); + + assert_eq!(ast.body, expected); + + let ast = + duckdb().verified_query("SELECT * FROM capitals UNION ALL BY NAME SELECT * FROM weather"); + let expected = Box::::new(SetExpr::SetOperation { + op: SetOperator::Union, + set_quantifier: SetQuantifier::AllByName, + left: Box::::new(SetExpr::Select(Box::new(Select { + distinct: None, + top: None, + projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions { + opt_exclude: None, + opt_except: None, + opt_rename: None, + opt_replace: None, + })], + into: None, + from: vec![TableWithJoins { + relation: TableFactor::Table { + name: ObjectName(vec![Ident { + value: "capitals".to_string(), + quote_style: None, + }]), + alias: None, + args: None, + with_hints: vec![], + }, + joins: vec![], + }], + lateral_views: vec![], + selection: None, + group_by: vec![], + cluster_by: vec![], + distribute_by: vec![], + sort_by: vec![], + having: None, + named_window: vec![], + qualify: None, + }))), + right: Box::::new(SetExpr::Select(Box::new(Select { + distinct: None, + top: None, + projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions { + opt_exclude: None, + opt_except: None, + opt_rename: None, + opt_replace: None, + })], + into: None, + from: vec![TableWithJoins { + relation: TableFactor::Table { + name: ObjectName(vec![Ident { + value: "weather".to_string(), + quote_style: None, + }]), + alias: None, + args: None, + with_hints: vec![], + }, + joins: vec![], + }], + lateral_views: vec![], + selection: None, + group_by: vec![], + cluster_by: vec![], + distribute_by: vec![], + sort_by: vec![], + having: None, + named_window: vec![], + qualify: None, + }))), + }); + assert_eq!(ast.body, expected); +}