Skip to content

Commit eb28848

Browse files
authored
Support UNION (ALL) BY NAME syntax (#915)
1 parent c454518 commit eb28848

File tree

4 files changed

+164
-3
lines changed

4 files changed

+164
-3
lines changed

src/ast/query.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,10 @@ impl fmt::Display for SetExpr {
110110
} => {
111111
write!(f, "{left} {op}")?;
112112
match set_quantifier {
113-
SetQuantifier::All | SetQuantifier::Distinct => write!(f, " {set_quantifier}")?,
113+
SetQuantifier::All
114+
| SetQuantifier::Distinct
115+
| SetQuantifier::ByName
116+
| SetQuantifier::AllByName => write!(f, " {set_quantifier}")?,
114117
SetQuantifier::None => write!(f, "{set_quantifier}")?,
115118
}
116119
write!(f, " {right}")?;
@@ -148,6 +151,8 @@ impl fmt::Display for SetOperator {
148151
pub enum SetQuantifier {
149152
All,
150153
Distinct,
154+
ByName,
155+
AllByName,
151156
None,
152157
}
153158

@@ -156,6 +161,8 @@ impl fmt::Display for SetQuantifier {
156161
match self {
157162
SetQuantifier::All => write!(f, "ALL"),
158163
SetQuantifier::Distinct => write!(f, "DISTINCT"),
164+
SetQuantifier::ByName => write!(f, "BY NAME"),
165+
SetQuantifier::AllByName => write!(f, "ALL BY NAME"),
159166
SetQuantifier::None => write!(f, ""),
160167
}
161168
}

src/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ define_keywords!(
384384
MSCK,
385385
MULTISET,
386386
MUTATION,
387+
NAME,
387388
NANOSECOND,
388389
NANOSECONDS,
389390
NATIONAL,

src/parser.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5331,8 +5331,14 @@ impl<'a> Parser<'a> {
53315331
pub fn parse_set_quantifier(&mut self, op: &Option<SetOperator>) -> SetQuantifier {
53325332
match op {
53335333
Some(SetOperator::Union) => {
5334-
if self.parse_keyword(Keyword::ALL) {
5335-
SetQuantifier::All
5334+
if self.parse_keywords(&[Keyword::BY, Keyword::NAME]) {
5335+
SetQuantifier::ByName
5336+
} else if self.parse_keyword(Keyword::ALL) {
5337+
if self.parse_keywords(&[Keyword::BY, Keyword::NAME]) {
5338+
SetQuantifier::AllByName
5339+
} else {
5340+
SetQuantifier::All
5341+
}
53365342
} else if self.parse_keyword(Keyword::DISTINCT) {
53375343
SetQuantifier::Distinct
53385344
} else {

tests/sqlparser_duckdb.rs

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,150 @@ fn test_create_table_macro() {
129129
};
130130
assert_eq!(expected, macro_);
131131
}
132+
133+
#[test]
134+
fn test_select_union_by_name() {
135+
let ast = duckdb().verified_query("SELECT * FROM capitals UNION BY NAME SELECT * FROM weather");
136+
let expected = Box::<SetExpr>::new(SetExpr::SetOperation {
137+
op: SetOperator::Union,
138+
set_quantifier: SetQuantifier::ByName,
139+
left: Box::<SetExpr>::new(SetExpr::Select(Box::new(Select {
140+
distinct: None,
141+
top: None,
142+
projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions {
143+
opt_exclude: None,
144+
opt_except: None,
145+
opt_rename: None,
146+
opt_replace: None,
147+
})],
148+
into: None,
149+
from: vec![TableWithJoins {
150+
relation: TableFactor::Table {
151+
name: ObjectName(vec![Ident {
152+
value: "capitals".to_string(),
153+
quote_style: None,
154+
}]),
155+
alias: None,
156+
args: None,
157+
with_hints: vec![],
158+
},
159+
joins: vec![],
160+
}],
161+
lateral_views: vec![],
162+
selection: None,
163+
group_by: vec![],
164+
cluster_by: vec![],
165+
distribute_by: vec![],
166+
sort_by: vec![],
167+
having: None,
168+
named_window: vec![],
169+
qualify: None,
170+
}))),
171+
right: Box::<SetExpr>::new(SetExpr::Select(Box::new(Select {
172+
distinct: None,
173+
top: None,
174+
projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions {
175+
opt_exclude: None,
176+
opt_except: None,
177+
opt_rename: None,
178+
opt_replace: None,
179+
})],
180+
into: None,
181+
from: vec![TableWithJoins {
182+
relation: TableFactor::Table {
183+
name: ObjectName(vec![Ident {
184+
value: "weather".to_string(),
185+
quote_style: None,
186+
}]),
187+
alias: None,
188+
args: None,
189+
with_hints: vec![],
190+
},
191+
joins: vec![],
192+
}],
193+
lateral_views: vec![],
194+
selection: None,
195+
group_by: vec![],
196+
cluster_by: vec![],
197+
distribute_by: vec![],
198+
sort_by: vec![],
199+
having: None,
200+
named_window: vec![],
201+
qualify: None,
202+
}))),
203+
});
204+
205+
assert_eq!(ast.body, expected);
206+
207+
let ast =
208+
duckdb().verified_query("SELECT * FROM capitals UNION ALL BY NAME SELECT * FROM weather");
209+
let expected = Box::<SetExpr>::new(SetExpr::SetOperation {
210+
op: SetOperator::Union,
211+
set_quantifier: SetQuantifier::AllByName,
212+
left: Box::<SetExpr>::new(SetExpr::Select(Box::new(Select {
213+
distinct: None,
214+
top: None,
215+
projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions {
216+
opt_exclude: None,
217+
opt_except: None,
218+
opt_rename: None,
219+
opt_replace: None,
220+
})],
221+
into: None,
222+
from: vec![TableWithJoins {
223+
relation: TableFactor::Table {
224+
name: ObjectName(vec![Ident {
225+
value: "capitals".to_string(),
226+
quote_style: None,
227+
}]),
228+
alias: None,
229+
args: None,
230+
with_hints: vec![],
231+
},
232+
joins: vec![],
233+
}],
234+
lateral_views: vec![],
235+
selection: None,
236+
group_by: vec![],
237+
cluster_by: vec![],
238+
distribute_by: vec![],
239+
sort_by: vec![],
240+
having: None,
241+
named_window: vec![],
242+
qualify: None,
243+
}))),
244+
right: Box::<SetExpr>::new(SetExpr::Select(Box::new(Select {
245+
distinct: None,
246+
top: None,
247+
projection: vec![SelectItem::Wildcard(WildcardAdditionalOptions {
248+
opt_exclude: None,
249+
opt_except: None,
250+
opt_rename: None,
251+
opt_replace: None,
252+
})],
253+
into: None,
254+
from: vec![TableWithJoins {
255+
relation: TableFactor::Table {
256+
name: ObjectName(vec![Ident {
257+
value: "weather".to_string(),
258+
quote_style: None,
259+
}]),
260+
alias: None,
261+
args: None,
262+
with_hints: vec![],
263+
},
264+
joins: vec![],
265+
}],
266+
lateral_views: vec![],
267+
selection: None,
268+
group_by: vec![],
269+
cluster_by: vec![],
270+
distribute_by: vec![],
271+
sort_by: vec![],
272+
having: None,
273+
named_window: vec![],
274+
qualify: None,
275+
}))),
276+
});
277+
assert_eq!(ast.body, expected);
278+
}

0 commit comments

Comments
 (0)