Skip to content

Commit 49bdb5c

Browse files
Snowflake: parse EXCLUDE column list as ObjectName to support qualified names (apache#2244)
Co-authored-by: Yoav Cohen <59807311+yoavcloud@users.noreply.github.com>
1 parent a3cfcac commit 49bdb5c

File tree

6 files changed

+66
-21
lines changed

6 files changed

+66
-21
lines changed

src/ast/query.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,13 +1018,13 @@ pub enum ExcludeSelectItem {
10181018
/// ```plaintext
10191019
/// <col_name>
10201020
/// ```
1021-
Single(Ident),
1021+
Single(ObjectName),
10221022
/// Multiple column names inside parenthesis.
10231023
/// # Syntax
10241024
/// ```plaintext
10251025
/// (<col_name>, <col_name>, ...)
10261026
/// ```
1027-
Multiple(Vec<Ident>),
1027+
Multiple(Vec<ObjectName>),
10281028
}
10291029

10301030
impl fmt::Display for ExcludeSelectItem {

src/ast/spans.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1849,8 +1849,8 @@ impl Spanned for IlikeSelectItem {
18491849
impl Spanned for ExcludeSelectItem {
18501850
fn span(&self) -> Span {
18511851
match self {
1852-
ExcludeSelectItem::Single(ident) => ident.span,
1853-
ExcludeSelectItem::Multiple(vec) => union_spans(vec.iter().map(|i| i.span)),
1852+
ExcludeSelectItem::Single(name) => name.span(),
1853+
ExcludeSelectItem::Multiple(vec) => union_spans(vec.iter().map(|i| i.span())),
18541854
}
18551855
}
18561856
}

src/parser/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17951,11 +17951,12 @@ impl<'a> Parser<'a> {
1795117951
) -> Result<Option<ExcludeSelectItem>, ParserError> {
1795217952
let opt_exclude = if self.parse_keyword(Keyword::EXCLUDE) {
1795317953
if self.consume_token(&Token::LParen) {
17954-
let columns = self.parse_comma_separated(|parser| parser.parse_identifier())?;
17954+
let columns =
17955+
self.parse_comma_separated(|parser| parser.parse_object_name(false))?;
1795517956
self.expect_token(&Token::RParen)?;
1795617957
Some(ExcludeSelectItem::Multiple(columns))
1795717958
} else {
17958-
let column = self.parse_identifier()?;
17959+
let column = self.parse_object_name(false)?;
1795917960
Some(ExcludeSelectItem::Single(column))
1796017961
}
1796117962
} else {

tests/sqlparser_common.rs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17343,7 +17343,9 @@ fn test_select_exclude() {
1734317343
SelectItem::Wildcard(WildcardAdditionalOptions { opt_exclude, .. }) => {
1734417344
assert_eq!(
1734517345
*opt_exclude,
17346-
Some(ExcludeSelectItem::Single(Ident::new("c1")))
17346+
Some(ExcludeSelectItem::Single(ObjectName::from(Ident::new(
17347+
"c1"
17348+
))))
1734717349
);
1734817350
}
1734917351
_ => unreachable!(),
@@ -17356,8 +17358,8 @@ fn test_select_exclude() {
1735617358
assert_eq!(
1735717359
*opt_exclude,
1735817360
Some(ExcludeSelectItem::Multiple(vec![
17359-
Ident::new("c1"),
17360-
Ident::new("c2")
17361+
ObjectName::from(Ident::new("c1")),
17362+
ObjectName::from(Ident::new("c2")),
1736117363
]))
1736217364
);
1736317365
}
@@ -17368,7 +17370,9 @@ fn test_select_exclude() {
1736817370
SelectItem::Wildcard(WildcardAdditionalOptions { opt_exclude, .. }) => {
1736917371
assert_eq!(
1737017372
*opt_exclude,
17371-
Some(ExcludeSelectItem::Single(Ident::new("c1")))
17373+
Some(ExcludeSelectItem::Single(ObjectName::from(Ident::new(
17374+
"c1"
17375+
))))
1737217376
);
1737317377
}
1737417378
_ => unreachable!(),
@@ -17390,7 +17394,9 @@ fn test_select_exclude() {
1739017394
}
1739117395
assert_eq!(
1739217396
select.exclude,
17393-
Some(ExcludeSelectItem::Single(Ident::new("c1")))
17397+
Some(ExcludeSelectItem::Single(ObjectName::from(Ident::new(
17398+
"c1"
17399+
))))
1739417400
);
1739517401

1739617402
let dialects = all_dialects_where(|d| {
@@ -17401,7 +17407,9 @@ fn test_select_exclude() {
1740117407
SelectItem::Wildcard(WildcardAdditionalOptions { opt_exclude, .. }) => {
1740217408
assert_eq!(
1740317409
*opt_exclude,
17404-
Some(ExcludeSelectItem::Single(Ident::new("c1")))
17410+
Some(ExcludeSelectItem::Single(ObjectName::from(Ident::new(
17411+
"c1"
17412+
))))
1740517413
);
1740617414
}
1740717415
_ => unreachable!(),
@@ -17438,6 +17446,32 @@ fn test_select_exclude() {
1743817446
);
1743917447
}
1744017448

17449+
#[test]
17450+
fn test_select_exclude_qualified_names() {
17451+
// EXCLUDE should accept qualified names like `f.col` parsed as ObjectName.
17452+
let dialects = all_dialects_where(|d| d.supports_select_wildcard_exclude());
17453+
17454+
// Qualified name in multi-column EXCLUDE list: f.* EXCLUDE (f.col1, f.col2)
17455+
let select = dialects
17456+
.verified_only_select("SELECT f.* EXCLUDE (f.account_canonical_id, f.amount) FROM t AS f");
17457+
match &select.projection[0] {
17458+
SelectItem::QualifiedWildcard(_, WildcardAdditionalOptions { opt_exclude, .. }) => {
17459+
assert_eq!(
17460+
*opt_exclude,
17461+
Some(ExcludeSelectItem::Multiple(vec![
17462+
ObjectName::from(vec![Ident::new("f"), Ident::new("account_canonical_id")]),
17463+
ObjectName::from(vec![Ident::new("f"), Ident::new("amount")]),
17464+
]))
17465+
);
17466+
}
17467+
_ => unreachable!(),
17468+
}
17469+
17470+
// Plain identifiers must still parse successfully.
17471+
dialects.verified_only_select("SELECT f.* EXCLUDE (account_canonical_id) FROM t AS f");
17472+
dialects.verified_only_select("SELECT f.* EXCLUDE (col1, col2) FROM t AS f");
17473+
}
17474+
1744117475
#[test]
1744217476
fn test_no_semicolon_required_between_statements() {
1744317477
let sql = r#"

tests/sqlparser_duckdb.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,9 @@ fn column_defs(statement: Statement) -> Vec<ColumnDef> {
156156
fn test_select_wildcard_with_exclude() {
157157
let select = duckdb().verified_only_select("SELECT * EXCLUDE (col_a) FROM data");
158158
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
159-
opt_exclude: Some(ExcludeSelectItem::Multiple(vec![Ident::new("col_a")])),
159+
opt_exclude: Some(ExcludeSelectItem::Multiple(vec![ObjectName::from(
160+
Ident::new("col_a"),
161+
)])),
160162
..Default::default()
161163
});
162164
assert_eq!(expected, select.projection[0]);
@@ -166,7 +168,9 @@ fn test_select_wildcard_with_exclude() {
166168
let expected = SelectItem::QualifiedWildcard(
167169
SelectItemQualifiedWildcardKind::ObjectName(ObjectName::from(vec![Ident::new("name")])),
168170
WildcardAdditionalOptions {
169-
opt_exclude: Some(ExcludeSelectItem::Single(Ident::new("department_id"))),
171+
opt_exclude: Some(ExcludeSelectItem::Single(ObjectName::from(Ident::new(
172+
"department_id",
173+
)))),
170174
..Default::default()
171175
},
172176
);
@@ -176,8 +180,8 @@ fn test_select_wildcard_with_exclude() {
176180
.verified_only_select("SELECT * EXCLUDE (department_id, employee_id) FROM employee_table");
177181
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
178182
opt_exclude: Some(ExcludeSelectItem::Multiple(vec![
179-
Ident::new("department_id"),
180-
Ident::new("employee_id"),
183+
ObjectName::from(Ident::new("department_id")),
184+
ObjectName::from(Ident::new("employee_id")),
181185
])),
182186
..Default::default()
183187
});

tests/sqlparser_snowflake.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1474,7 +1474,9 @@ fn snowflake_and_generic() -> TestedDialects {
14741474
fn test_select_wildcard_with_exclude() {
14751475
let select = snowflake_and_generic().verified_only_select("SELECT * EXCLUDE (col_a) FROM data");
14761476
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
1477-
opt_exclude: Some(ExcludeSelectItem::Multiple(vec![Ident::new("col_a")])),
1477+
opt_exclude: Some(ExcludeSelectItem::Multiple(vec![ObjectName::from(
1478+
Ident::new("col_a"),
1479+
)])),
14781480
..Default::default()
14791481
});
14801482
assert_eq!(expected, select.projection[0]);
@@ -1484,7 +1486,9 @@ fn test_select_wildcard_with_exclude() {
14841486
let expected = SelectItem::QualifiedWildcard(
14851487
SelectItemQualifiedWildcardKind::ObjectName(ObjectName::from(vec![Ident::new("name")])),
14861488
WildcardAdditionalOptions {
1487-
opt_exclude: Some(ExcludeSelectItem::Single(Ident::new("department_id"))),
1489+
opt_exclude: Some(ExcludeSelectItem::Single(ObjectName::from(Ident::new(
1490+
"department_id",
1491+
)))),
14881492
..Default::default()
14891493
},
14901494
);
@@ -1494,8 +1498,8 @@ fn test_select_wildcard_with_exclude() {
14941498
.verified_only_select("SELECT * EXCLUDE (department_id, employee_id) FROM employee_table");
14951499
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
14961500
opt_exclude: Some(ExcludeSelectItem::Multiple(vec![
1497-
Ident::new("department_id"),
1498-
Ident::new("employee_id"),
1501+
ObjectName::from(Ident::new("department_id")),
1502+
ObjectName::from(Ident::new("employee_id")),
14991503
])),
15001504
..Default::default()
15011505
});
@@ -1580,7 +1584,9 @@ fn test_select_wildcard_with_exclude_and_rename() {
15801584
let select = snowflake_and_generic()
15811585
.verified_only_select("SELECT * EXCLUDE col_z RENAME col_a AS col_b FROM data");
15821586
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
1583-
opt_exclude: Some(ExcludeSelectItem::Single(Ident::new("col_z"))),
1587+
opt_exclude: Some(ExcludeSelectItem::Single(ObjectName::from(Ident::new(
1588+
"col_z",
1589+
)))),
15841590
opt_rename: Some(RenameSelectItem::Single(IdentWithAlias {
15851591
ident: Ident::new("col_a"),
15861592
alias: Ident::new("col_b"),

0 commit comments

Comments
 (0)