Skip to content

Commit 85a49a6

Browse files
committed
fix: clear error for qualified column names in EXCLUDE clause
When a user writes `f.* EXCLUDE (f.col)` instead of `f.* EXCLUDE (col)`, the parser previously consumed only the table qualifier (e.g. `f`) as the identifier and then hit the `.` unexpectedly, producing a confusing error like "Expected: `,` or `)`, found `.`". This commit detects the qualified-name pattern in `parse_optional_select_item_exclude` and returns an actionable error: EXCLUDE does not support qualified column names, use a plain identifier instead (e.g. EXCLUDE (account_canonical_id)) Applies to both the single-column (`EXCLUDE col`) and multi-column (`EXCLUDE (col1, col2)`) forms. Fixes repro: `SELECT f.* EXCLUDE (f.account_canonical_id, f.amount) FROM t AS f`
1 parent 982068e commit 85a49a6

2 files changed

Lines changed: 55 additions & 1 deletion

File tree

src/parser/mod.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17901,11 +17901,32 @@ impl<'a> Parser<'a> {
1790117901
) -> Result<Option<ExcludeSelectItem>, ParserError> {
1790217902
let opt_exclude = if self.parse_keyword(Keyword::EXCLUDE) {
1790317903
if self.consume_token(&Token::LParen) {
17904-
let columns = self.parse_comma_separated(|parser| parser.parse_identifier())?;
17904+
let columns = self.parse_comma_separated(|parser| {
17905+
let ident = parser.parse_identifier()?;
17906+
if parser.peek_token_ref().token == Token::Period {
17907+
return parser_err!(
17908+
format!(
17909+
"EXCLUDE does not support qualified column names, \
17910+
use a plain identifier instead (e.g. EXCLUDE ({ident}))"
17911+
),
17912+
parser.peek_token_ref().span.start
17913+
);
17914+
}
17915+
Ok(ident)
17916+
})?;
1790517917
self.expect_token(&Token::RParen)?;
1790617918
Some(ExcludeSelectItem::Multiple(columns))
1790717919
} else {
1790817920
let column = self.parse_identifier()?;
17921+
if self.peek_token_ref().token == Token::Period {
17922+
return parser_err!(
17923+
format!(
17924+
"EXCLUDE does not support qualified column names, \
17925+
use a plain identifier instead (e.g. EXCLUDE {column})"
17926+
),
17927+
self.peek_token_ref().span.start
17928+
);
17929+
}
1790917930
Some(ExcludeSelectItem::Single(column))
1791017931
}
1791117932
} else {

tests/sqlparser_common.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17395,6 +17395,39 @@ fn test_select_exclude() {
1739517395
);
1739617396
}
1739717397

17398+
#[test]
17399+
fn test_select_exclude_qualified_names() {
17400+
// EXCLUDE should only accept plain identifiers, not qualified names like `f.col`.
17401+
// Verify that the parser produces a clear, actionable error message.
17402+
let dialects = all_dialects_where(|d| d.supports_select_wildcard_exclude());
17403+
17404+
// Qualified name in multi-column EXCLUDE list: f.* EXCLUDE (f.col)
17405+
let err = dialects
17406+
.parse_sql_statements("SELECT f.* EXCLUDE (f.account_canonical_id) FROM t AS f")
17407+
.err()
17408+
.unwrap();
17409+
assert!(
17410+
err.to_string()
17411+
.contains("EXCLUDE does not support qualified column names"),
17412+
"unexpected error: {err}"
17413+
);
17414+
17415+
// Qualified name in single-column EXCLUDE: * EXCLUDE f.col
17416+
let err = dialects
17417+
.parse_sql_statements("SELECT * EXCLUDE f.col FROM t AS f")
17418+
.err()
17419+
.unwrap();
17420+
assert!(
17421+
err.to_string()
17422+
.contains("EXCLUDE does not support qualified column names"),
17423+
"unexpected error: {err}"
17424+
);
17425+
17426+
// Valid forms must still parse successfully.
17427+
dialects.verified_only_select("SELECT f.* EXCLUDE (account_canonical_id) FROM t AS f");
17428+
dialects.verified_only_select("SELECT f.* EXCLUDE (col1, col2) FROM t AS f");
17429+
}
17430+
1739817431
#[test]
1739917432
fn test_no_semicolon_required_between_statements() {
1740017433
let sql = r#"

0 commit comments

Comments
 (0)