Skip to content

Commit 0f77138

Browse files
Parser: fix exponential parse time on named-arg chains
1 parent feb12da commit 0f77138

1 file changed

Lines changed: 29 additions & 17 deletions

File tree

src/parser/mod.rs

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18357,19 +18357,8 @@ impl<'a> Parser<'a> {
1835718357

1835818358
/// Parse a single function argument, handling named and unnamed variants.
1835918359
pub fn parse_function_args(&mut self) -> Result<FunctionArg, ParserError> {
18360-
let arg = if self.dialect.supports_named_fn_args_with_expr_name() {
18361-
self.maybe_parse(|p| {
18362-
let name = p.parse_expr()?;
18363-
let operator = p.parse_function_named_arg_operator()?;
18364-
let arg = p.parse_wildcard_expr()?.into();
18365-
Ok(FunctionArg::ExprNamed {
18366-
name,
18367-
arg,
18368-
operator,
18369-
})
18370-
})?
18371-
} else {
18372-
self.maybe_parse(|p| {
18360+
if !self.dialect.supports_named_fn_args_with_expr_name() {
18361+
if let Some(arg) = self.maybe_parse(|p| {
1837318362
let name = p.parse_identifier()?;
1837418363
let operator = p.parse_function_named_arg_operator()?;
1837518364
let arg = p.parse_wildcard_expr()?.into();
@@ -18378,12 +18367,35 @@ impl<'a> Parser<'a> {
1837818367
arg,
1837918368
operator,
1838018369
})
18381-
})?
18382-
};
18383-
if let Some(arg) = arg {
18384-
return Ok(arg);
18370+
})? {
18371+
return Ok(arg);
18372+
}
1838518373
}
18374+
18375+
// Parse the leading expression once, then speculatively parse the
18376+
// named-arg tail `<op> <expr>`. The previous implementation also
18377+
// speculated on the name itself via `maybe_parse(parse_expr ...)`,
18378+
// which produced ~2^N work on chains like
18379+
// `SELECT Y\n.foo(t--\n.foo(t--\n...` because rollback re-walked
18380+
// deeply nested function calls. Same family of bug as #2344.
1838618381
let wildcard_expr = self.parse_wildcard_expr()?;
18382+
if self.dialect.supports_named_fn_args_with_expr_name()
18383+
&& !matches!(wildcard_expr, Expr::Wildcard(_))
18384+
{
18385+
if let Some((operator, arg)) = self.maybe_parse(|p| {
18386+
Ok((
18387+
p.parse_function_named_arg_operator()?,
18388+
p.parse_wildcard_expr()?,
18389+
))
18390+
})? {
18391+
return Ok(FunctionArg::ExprNamed {
18392+
name: wildcard_expr,
18393+
arg: arg.into(),
18394+
operator,
18395+
});
18396+
}
18397+
}
18398+
1838718399
let arg_expr: FunctionArgExpr = match wildcard_expr {
1838818400
Expr::Wildcard(ref token) if self.dialect.supports_select_wildcard_exclude() => {
1838918401
// Support `* EXCLUDE(col1, col2, ...)` inside function calls (e.g. Snowflake's

0 commit comments

Comments
 (0)