Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 5 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7614,6 +7614,10 @@ pub enum FunctionArgExpr {
QualifiedWildcard(ObjectName),
/// An unqualified `*` wildcard.
Wildcard,
/// An unqualified `*` wildcard with additional options, e.g. `* EXCLUDE(col)`.
///
/// Used in Snowflake to support expressions like `HASH(* EXCLUDE(col))`.
WildcardWithOptions(WildcardAdditionalOptions),
}

impl From<Expr> for FunctionArgExpr {
Expand All @@ -7632,6 +7636,7 @@ impl fmt::Display for FunctionArgExpr {
FunctionArgExpr::Expr(expr) => write!(f, "{expr}"),
FunctionArgExpr::QualifiedWildcard(prefix) => write!(f, "{prefix}.*"),
FunctionArgExpr::Wildcard => f.write_str("*"),
FunctionArgExpr::WildcardWithOptions(opts) => write!(f, "*{opts}"),
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2128,6 +2128,7 @@ impl Spanned for FunctionArg {
///
/// Missing spans:
/// - [FunctionArgExpr::Wildcard]
/// - [FunctionArgExpr::WildcardWithOptions]
impl Spanned for FunctionArgExpr {
fn span(&self) -> Span {
match self {
Expand All @@ -2136,6 +2137,7 @@ impl Spanned for FunctionArgExpr {
union_spans(object_name.0.iter().map(|i| i.span()))
}
FunctionArgExpr::Wildcard => Span::empty(),
FunctionArgExpr::WildcardWithOptions(_) => Span::empty(),
}
}
}
Expand Down
21 changes: 20 additions & 1 deletion src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17559,7 +17559,26 @@ impl<'a> Parser<'a> {
if let Some(arg) = arg {
return Ok(arg);
}
Ok(FunctionArg::Unnamed(self.parse_wildcard_expr()?.into()))
let wildcard_expr = self.parse_wildcard_expr()?;
let arg_expr: FunctionArgExpr = match wildcard_expr {
Expr::Wildcard(ref token) if self.dialect.supports_select_wildcard_exclude() => {
// Support `* EXCLUDE(col1, col2, ...)` inside function calls (e.g. Snowflake's
// `HASH(* EXCLUDE(col))`). Parse the options the same way SELECT items do.
let opts = self.parse_wildcard_additional_options(token.0.clone())?;
if opts.opt_exclude.is_some()
|| opts.opt_except.is_some()
|| opts.opt_replace.is_some()
|| opts.opt_rename.is_some()
|| opts.opt_ilike.is_some()
{
FunctionArgExpr::WildcardWithOptions(opts)
} else {
wildcard_expr.into()
}
}
other => other.into(),
};
Ok(FunctionArg::Unnamed(arg_expr))
}

fn parse_function_named_arg_operator(&mut self) -> Result<FunctionArgOperator, ParserError> {
Expand Down
16 changes: 16 additions & 0 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18563,3 +18563,19 @@ fn parse_array_subscript() {
dialects.verified_stmt("SELECT arr[1][2]");
dialects.verified_stmt("SELECT arr[:][:]");
}

#[test]
fn test_wildcard_func_arg() {
// Wildcard (*) and wildcard with EXCLUDE as a function argument.
// Documented for Snowflake's HASH function but parsed for any dialect that
// supports the wildcard-EXCLUDE select syntax.
let dialects = all_dialects_where(|d| d.supports_select_wildcard_exclude());

// Wildcard with EXCLUDE — canonical form has a space before the parenthesised column list.
dialects.one_statement_parses_to(
"SELECT HASH(* EXCLUDE(col1)) FROM t",
"SELECT HASH(* EXCLUDE (col1)) FROM t",
);
dialects.verified_expr("HASH(* EXCLUDE (col1))");
dialects.verified_expr("HASH(* EXCLUDE (col1, col2))");
}
Loading