Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/ast/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1018,13 +1018,13 @@ pub enum ExcludeSelectItem {
/// ```plaintext
/// <col_name>
/// ```
Single(Ident),
Single(ObjectName),
/// Multiple column names inside parenthesis.
/// # Syntax
/// ```plaintext
/// (<col_name>, <col_name>, ...)
/// ```
Multiple(Vec<Ident>),
Multiple(Vec<ObjectName>),
}

impl fmt::Display for ExcludeSelectItem {
Expand Down
4 changes: 2 additions & 2 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1849,8 +1849,8 @@ impl Spanned for IlikeSelectItem {
impl Spanned for ExcludeSelectItem {
fn span(&self) -> Span {
match self {
ExcludeSelectItem::Single(ident) => ident.span,
ExcludeSelectItem::Multiple(vec) => union_spans(vec.iter().map(|i| i.span)),
ExcludeSelectItem::Single(name) => name.span(),
ExcludeSelectItem::Multiple(vec) => union_spans(vec.iter().map(|i| i.span())),
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17952,11 +17952,13 @@ impl<'a> Parser<'a> {
) -> Result<Option<ExcludeSelectItem>, ParserError> {
let opt_exclude = if self.parse_keyword(Keyword::EXCLUDE) {
if self.consume_token(&Token::LParen) {
let columns = self.parse_comma_separated(|parser| parser.parse_identifier())?;
let columns = self.parse_comma_separated(|parser| {
parser.parse_object_name(false)
})?;
self.expect_token(&Token::RParen)?;
Some(ExcludeSelectItem::Multiple(columns))
} else {
let column = self.parse_identifier()?;
let column = self.parse_object_name(false)?;
Some(ExcludeSelectItem::Single(column))
}
} else {
Expand Down
47 changes: 41 additions & 6 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17320,7 +17320,9 @@ fn test_select_exclude() {
SelectItem::Wildcard(WildcardAdditionalOptions { opt_exclude, .. }) => {
assert_eq!(
*opt_exclude,
Some(ExcludeSelectItem::Single(Ident::new("c1")))
Some(ExcludeSelectItem::Single(ObjectName::from(Ident::new(
"c1"
))))
);
}
_ => unreachable!(),
Expand All @@ -17333,8 +17335,8 @@ fn test_select_exclude() {
assert_eq!(
*opt_exclude,
Some(ExcludeSelectItem::Multiple(vec![
Ident::new("c1"),
Ident::new("c2")
ObjectName::from(Ident::new("c1")),
ObjectName::from(Ident::new("c2")),
]))
);
}
Expand All @@ -17345,7 +17347,9 @@ fn test_select_exclude() {
SelectItem::Wildcard(WildcardAdditionalOptions { opt_exclude, .. }) => {
assert_eq!(
*opt_exclude,
Some(ExcludeSelectItem::Single(Ident::new("c1")))
Some(ExcludeSelectItem::Single(ObjectName::from(Ident::new(
"c1"
))))
);
}
_ => unreachable!(),
Expand All @@ -17367,7 +17371,9 @@ fn test_select_exclude() {
}
assert_eq!(
select.exclude,
Some(ExcludeSelectItem::Single(Ident::new("c1")))
Some(ExcludeSelectItem::Single(ObjectName::from(Ident::new(
"c1"
))))
);

let dialects = all_dialects_where(|d| {
Expand All @@ -17378,7 +17384,9 @@ fn test_select_exclude() {
SelectItem::Wildcard(WildcardAdditionalOptions { opt_exclude, .. }) => {
assert_eq!(
*opt_exclude,
Some(ExcludeSelectItem::Single(Ident::new("c1")))
Some(ExcludeSelectItem::Single(ObjectName::from(Ident::new(
"c1"
))))
);
}
_ => unreachable!(),
Expand Down Expand Up @@ -17415,6 +17423,33 @@ fn test_select_exclude() {
);
}

#[test]
fn test_select_exclude_qualified_names() {
// EXCLUDE should accept qualified names like `f.col` parsed as ObjectName.
let dialects = all_dialects_where(|d| d.supports_select_wildcard_exclude());

// Qualified name in multi-column EXCLUDE list: f.* EXCLUDE (f.col1, f.col2)
let select = dialects.verified_only_select(
"SELECT f.* EXCLUDE (f.account_canonical_id, f.amount) FROM t AS f",
);
match &select.projection[0] {
SelectItem::QualifiedWildcard(_, WildcardAdditionalOptions { opt_exclude, .. }) => {
assert_eq!(
*opt_exclude,
Some(ExcludeSelectItem::Multiple(vec![
ObjectName::from(vec![Ident::new("f"), Ident::new("account_canonical_id")]),
ObjectName::from(vec![Ident::new("f"), Ident::new("amount")]),
]))
);
}
_ => unreachable!(),
}

// Plain identifiers must still parse successfully.
dialects.verified_only_select("SELECT f.* EXCLUDE (account_canonical_id) FROM t AS f");
dialects.verified_only_select("SELECT f.* EXCLUDE (col1, col2) FROM t AS f");
}

#[test]
fn test_no_semicolon_required_between_statements() {
let sql = r#"
Expand Down
12 changes: 8 additions & 4 deletions tests/sqlparser_duckdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,9 @@ fn column_defs(statement: Statement) -> Vec<ColumnDef> {
fn test_select_wildcard_with_exclude() {
let select = duckdb().verified_only_select("SELECT * EXCLUDE (col_a) FROM data");
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
opt_exclude: Some(ExcludeSelectItem::Multiple(vec![Ident::new("col_a")])),
opt_exclude: Some(ExcludeSelectItem::Multiple(vec![ObjectName::from(
Ident::new("col_a"),
)])),
..Default::default()
});
assert_eq!(expected, select.projection[0]);
Expand All @@ -166,7 +168,9 @@ fn test_select_wildcard_with_exclude() {
let expected = SelectItem::QualifiedWildcard(
SelectItemQualifiedWildcardKind::ObjectName(ObjectName::from(vec![Ident::new("name")])),
WildcardAdditionalOptions {
opt_exclude: Some(ExcludeSelectItem::Single(Ident::new("department_id"))),
opt_exclude: Some(ExcludeSelectItem::Single(ObjectName::from(Ident::new(
"department_id",
)))),
..Default::default()
},
);
Expand All @@ -176,8 +180,8 @@ fn test_select_wildcard_with_exclude() {
.verified_only_select("SELECT * EXCLUDE (department_id, employee_id) FROM employee_table");
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
opt_exclude: Some(ExcludeSelectItem::Multiple(vec![
Ident::new("department_id"),
Ident::new("employee_id"),
ObjectName::from(Ident::new("department_id")),
ObjectName::from(Ident::new("employee_id")),
])),
..Default::default()
});
Expand Down
16 changes: 11 additions & 5 deletions tests/sqlparser_snowflake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1474,7 +1474,9 @@ fn snowflake_and_generic() -> TestedDialects {
fn test_select_wildcard_with_exclude() {
let select = snowflake_and_generic().verified_only_select("SELECT * EXCLUDE (col_a) FROM data");
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
opt_exclude: Some(ExcludeSelectItem::Multiple(vec![Ident::new("col_a")])),
opt_exclude: Some(ExcludeSelectItem::Multiple(vec![ObjectName::from(
Ident::new("col_a"),
)])),
..Default::default()
});
assert_eq!(expected, select.projection[0]);
Expand All @@ -1484,7 +1486,9 @@ fn test_select_wildcard_with_exclude() {
let expected = SelectItem::QualifiedWildcard(
SelectItemQualifiedWildcardKind::ObjectName(ObjectName::from(vec![Ident::new("name")])),
WildcardAdditionalOptions {
opt_exclude: Some(ExcludeSelectItem::Single(Ident::new("department_id"))),
opt_exclude: Some(ExcludeSelectItem::Single(ObjectName::from(Ident::new(
"department_id",
)))),
..Default::default()
},
);
Expand All @@ -1494,8 +1498,8 @@ fn test_select_wildcard_with_exclude() {
.verified_only_select("SELECT * EXCLUDE (department_id, employee_id) FROM employee_table");
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
opt_exclude: Some(ExcludeSelectItem::Multiple(vec![
Ident::new("department_id"),
Ident::new("employee_id"),
ObjectName::from(Ident::new("department_id")),
ObjectName::from(Ident::new("employee_id")),
])),
..Default::default()
});
Expand Down Expand Up @@ -1580,7 +1584,9 @@ fn test_select_wildcard_with_exclude_and_rename() {
let select = snowflake_and_generic()
.verified_only_select("SELECT * EXCLUDE col_z RENAME col_a AS col_b FROM data");
let expected = SelectItem::Wildcard(WildcardAdditionalOptions {
opt_exclude: Some(ExcludeSelectItem::Single(Ident::new("col_z"))),
opt_exclude: Some(ExcludeSelectItem::Single(ObjectName::from(Ident::new(
"col_z",
)))),
opt_rename: Some(RenameSelectItem::Single(IdentWithAlias {
ident: Ident::new("col_a"),
alias: Ident::new("col_b"),
Expand Down
Loading