Skip to content

Commit e174e14

Browse files
authored
[SIG-43218] support for snowflake EXCLUDE wildcard modifier (#11)
* support for snowflake EXCLUDE wildcard modifier * revert unintended change to EXCEPT * add test case for REPLACE wildcard modifier
1 parent 5ec623e commit e174e14

9 files changed

Lines changed: 116 additions & 39 deletions

File tree

src/ast/query.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ pub enum SelectItem {
228228
Wildcard {
229229
prefix: Option<ObjectName>,
230230
except: Vec<Ident>,
231+
exclude: Vec<Ident>, // Snowflake's flavor of EXCEPT
231232
replace: Vec<(Expr, Ident)>,
232233
},
233234
}
@@ -240,6 +241,7 @@ impl fmt::Display for SelectItem {
240241
SelectItem::Wildcard {
241242
prefix,
242243
except,
244+
exclude,
243245
replace,
244246
} => {
245247
if let Some(pre) = prefix {
@@ -257,6 +259,19 @@ impl fmt::Display for SelectItem {
257259
write!(f, ")")?;
258260
}
259261
delim = "";
262+
if !exclude.is_empty() {
263+
write!(f, " EXCLUDE ")?;
264+
if exclude.len() == 1 {
265+
write!(f, "{}", exclude[0])?;
266+
} else {
267+
write!(f, "(")?;
268+
for col in exclude {
269+
write!(f, "{}{}", delim, col)?;
270+
delim = ", ";
271+
}
272+
write!(f, ")")?;
273+
}
274+
}
260275
if !replace.is_empty() {
261276
write!(f, " REPLACE (")?;
262277
for &(ref expr, ref alias) in replace.iter() {

src/dialect/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ define_keywords!(
201201
ESCAPE,
202202
EVERY,
203203
EXCEPT,
204+
EXCLUDE,
204205
EXEC,
205206
EXECUTE,
206207
EXISTS,

src/parser.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub enum ParserError {
2828
}
2929

3030
pub type WildcardExcept = Vec<Ident>;
31+
pub type WildcardExclude = Vec<Ident>;
3132
pub type WildcardReplace = Vec<(Expr, Ident)>;
3233

3334
// Use `Parser::expected` instead, if possible
@@ -2871,17 +2872,19 @@ impl<'a> Parser<'a> {
28712872
pub fn parse_select_item(&mut self) -> Result<SelectItem, ParserError> {
28722873
let expr = self.parse_expr()?;
28732874
if let Expr::Wildcard = expr {
2874-
let (except, replace) = self.parse_wildcard_modifiers()?;
2875+
let (except, exclude, replace) = self.parse_wildcard_modifiers()?;
28752876
Ok(SelectItem::Wildcard {
28762877
prefix: None,
28772878
except,
2879+
exclude,
28782880
replace,
28792881
})
28802882
} else if let Expr::QualifiedWildcard(prefix) = expr {
2881-
let (except, replace) = self.parse_wildcard_modifiers()?;
2883+
let (except, exclude, replace) = self.parse_wildcard_modifiers()?;
28822884
Ok(SelectItem::Wildcard {
28832885
prefix: Some(ObjectName(prefix)),
28842886
except,
2887+
exclude,
28852888
replace,
28862889
})
28872890
} else {
@@ -2908,7 +2911,7 @@ impl<'a> Parser<'a> {
29082911

29092912
pub fn parse_wildcard_modifiers(
29102913
&mut self,
2911-
) -> Result<(WildcardExcept, WildcardReplace), ParserError> {
2914+
) -> Result<(WildcardExcept, WildcardExclude, WildcardReplace), ParserError> {
29122915
let except = if self.parse_keyword(Keyword::EXCEPT) {
29132916
self.expect_token(&Token::LParen)?;
29142917
let aliases = self.parse_comma_separated(Parser::parse_identifier)?;
@@ -2917,15 +2920,26 @@ impl<'a> Parser<'a> {
29172920
} else {
29182921
vec![]
29192922
};
2920-
let replace = if self.parse_keyword(Keyword::EXCEPT) {
2923+
let exclude = if self.parse_keyword(Keyword::EXCLUDE) {
2924+
if self.consume_token(&Token::LParen) {
2925+
let aliases = self.parse_comma_separated(Parser::parse_identifier)?;
2926+
self.expect_token(&Token::RParen)?;
2927+
aliases
2928+
} else {
2929+
vec![self.parse_identifier()?]
2930+
}
2931+
} else {
2932+
vec![]
2933+
};
2934+
let replace = if self.parse_keyword(Keyword::REPLACE) {
29212935
self.expect_token(&Token::LParen)?;
29222936
let replace = self.parse_comma_separated(Parser::parse_replace_item)?;
29232937
self.expect_token(&Token::RParen)?;
29242938
replace
29252939
} else {
29262940
vec![]
29272941
};
2928-
Ok((except, replace))
2942+
Ok((except, exclude, replace))
29292943
}
29302944

29312945
/// Parse an expression, optionally followed by ASC or DESC (used in ORDER BY)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
SELECT
2+
* EXCEPT (order_id)
3+
FROM
4+
orders;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
SELECT
2+
table_a.* EXCLUDE foo,
3+
table_b.* EXCLUDE (bar, baz)
4+
FROM
5+
table_a,
6+
table_b
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
SELECT
2+
* REPLACE ('DEPT-' || department_id AS department_id)
3+
FROM
4+
departments

tests/sqlparser_common.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ fn parse_select_wildcard() {
273273
&SelectItem::Wildcard {
274274
prefix: None,
275275
except: vec![],
276+
exclude: vec![],
276277
replace: vec![]
277278
},
278279
only(&select.projection)
@@ -284,6 +285,7 @@ fn parse_select_wildcard() {
284285
&SelectItem::Wildcard {
285286
prefix: Some(ObjectName(vec![Ident::new("foo")])),
286287
except: vec![],
288+
exclude: vec![],
287289
replace: vec![]
288290
},
289291
only(&select.projection)
@@ -298,6 +300,7 @@ fn parse_select_wildcard() {
298300
Ident::new("mytable"),
299301
])),
300302
except: vec![],
303+
exclude: vec![],
301304
replace: vec![]
302305
},
303306
only(&select.projection)

tests/sqlparser_regression.rs

Lines changed: 59 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,50 +15,75 @@
1515
use sqlparser::dialect::GenericDialect;
1616
use sqlparser::parser::Parser;
1717

18-
macro_rules! tpch_tests {
19-
($($name:ident: $value:expr,)*) => {
20-
const QUERIES: &[&str] = &[
21-
$(include_str!(concat!("queries/tpch/", $value, ".sql"))),*
22-
];
18+
macro_rules! regression_tests {
19+
(
20+
path: $path:literal,
21+
tests: [$(
22+
$(#[$attr:ident])?
23+
$name:ident: $file:expr,
24+
)*] $(,)?
25+
) => {
2326
$(
2427

2528
#[test]
29+
$(#[$attr])?
2630
fn $name() {
2731
let dialect = GenericDialect {};
2832

29-
let res = Parser::parse_sql(&dialect, QUERIES[$value -1]);
30-
// Ignore 6.sql and 22.sql
31-
if $value != 6 && $value != 22 {
32-
assert!(res.is_ok());
33+
let stmts = Parser::parse_sql(&dialect, include_str!(concat!($path, $file, ".sql")))
34+
.unwrap_or_else(|err| panic!("{}", err));
35+
36+
// check that each statement still parses after being rendered
37+
for stmt in stmts {
38+
assert!(Parser::parse_sql(&dialect, &dbg!(stmt.to_string())).is_ok());
3339
}
3440
}
3541
)*
3642
}
3743
}
3844

39-
tpch_tests! {
40-
tpch_1: 1,
41-
tpch_2: 2,
42-
tpch_3: 3,
43-
tpch_4: 4,
44-
tpch_5: 5,
45-
tpch_6: 6,
46-
tpch_7: 7,
47-
tpch_8: 8,
48-
tpch_9: 9,
49-
tpch_10: 10,
50-
tpch_11: 11,
51-
tpch_12: 12,
52-
tpch_13: 13,
53-
tpch_14: 14,
54-
tpch_15: 15,
55-
tpch_16: 16,
56-
tpch_17: 17,
57-
tpch_18: 18,
58-
tpch_19: 19,
59-
tpch_20: 20,
60-
tpch_21: 21,
61-
tpch_22: 22,
62-
tpch_23: 23,
63-
tpch_24: 24,
45+
regression_tests! {
46+
path: "queries/tpch/",
47+
tests: [
48+
tpch_1: "1",
49+
tpch_2: "2",
50+
tpch_3: "3",
51+
tpch_4: "4",
52+
tpch_5: "5",
53+
tpch_6: "6",
54+
tpch_7: "7",
55+
tpch_8: "8",
56+
tpch_9: "9",
57+
tpch_10: "10",
58+
tpch_11: "11",
59+
tpch_12: "12",
60+
tpch_13: "13",
61+
tpch_14: "14",
62+
tpch_15: "15",
63+
tpch_16: "16",
64+
tpch_17: "17",
65+
tpch_18: "18",
66+
tpch_19: "19",
67+
tpch_20: "20",
68+
tpch_21: "21",
69+
#[ignore]
70+
tpch_22: "22",
71+
tpch_23: "23",
72+
tpch_24: "24",
73+
],
74+
}
75+
76+
regression_tests! {
77+
path: "queries/snowflake/",
78+
tests: [
79+
snowflake_wildcard_modifiers_exclude: "wildcard_modifiers_exclude",
80+
snowflake_wildcard_modifiers_replace: "wildcard_modifiers_replace",
81+
],
82+
}
83+
84+
regression_tests! {
85+
path: "queries/bigquery/",
86+
tests: [
87+
bigquery_wildcard_modifiers_except: "wildcard_modifiers_except",
88+
],
6489
}

tests/sqlparser_sigma.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,6 +1428,7 @@ fn parse_complicated_sql() {
14281428
quote_style: None,
14291429
}])),
14301430
except: vec![],
1431+
exclude: vec![],
14311432
replace: vec![],
14321433
},
14331434
ExprWithAlias {
@@ -2044,6 +2045,7 @@ fn parse_complicated_sql() {
20442045
SelectItem::Wildcard {
20452046
prefix: None,
20462047
except: vec![],
2048+
exclude: vec![],
20472049
replace: vec![],
20482050
},
20492051
ExprWithAlias {
@@ -2120,6 +2122,7 @@ fn parse_complicated_sql() {
21202122
quote_style: None,
21212123
}])),
21222124
except: vec![],
2125+
exclude: vec![],
21232126
replace: vec![],
21242127
},
21252128
ExprWithAlias {
@@ -2640,6 +2643,7 @@ fn parse_complicated_sql() {
26402643
quote_style: None,
26412644
}])),
26422645
except: vec![],
2646+
exclude: vec![],
26432647
replace: vec![],
26442648
},
26452649
ExprWithAlias {
@@ -2764,6 +2768,7 @@ fn parse_complicated_sql() {
27642768
quote_style: None,
27652769
}])),
27662770
except: vec![],
2771+
exclude: vec![],
27672772
replace: vec![],
27682773
},
27692774
ExprWithAlias {

0 commit comments

Comments
 (0)