diff --git a/src/parser/mod.rs b/src/parser/mod.rs index b2fa3b169..1e633008b 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1222,6 +1222,17 @@ impl<'a> Parser<'a> { Token::Mul => { return Ok(Expr::Wildcard(AttachedToken(next_token))); } + // Handle parenthesized wildcard: (*) + Token::LParen => { + let [maybe_mul, maybe_rparen] = self.peek_tokens_ref(); + if maybe_mul.token == Token::Mul && maybe_rparen.token == Token::RParen { + let mul_token = self.next_token(); // consume Mul + self.next_token(); // consume RParen + return Ok(Expr::Wildcard(AttachedToken(mul_token))); + } + // Not a (*), fall through to reset index and call parse_expr + self.prev_token(); + } _ => (), }; diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index ccad67e39..83c321e07 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -17905,3 +17905,22 @@ fn test_parse_set_session_authorization() { })) ); } + +#[test] +fn parse_select_parenthesized_wildcard() { + // Test SELECT DISTINCT(*) which uses a parenthesized wildcard + // The parentheses are syntactic sugar and get normalized to just * + let sql = "SELECT DISTINCT (*) FROM table1"; + let canonical = "SELECT DISTINCT * FROM table1"; + let select = all_dialects().verified_only_select_with_canonical(sql, canonical); + assert_eq!(select.distinct, Some(Distinct::Distinct)); + assert_eq!(select.projection.len(), 1); + assert!(matches!(select.projection[0], SelectItem::Wildcard(_))); + + // Also test without spaces: SELECT DISTINCT(*) + let sql_no_spaces = "SELECT DISTINCT(*) FROM table1"; + let select2 = all_dialects().verified_only_select_with_canonical(sql_no_spaces, canonical); + assert_eq!(select2.distinct, Some(Distinct::Distinct)); + assert_eq!(select2.projection.len(), 1); + assert!(matches!(select2.projection[0], SelectItem::Wildcard(_))); +}