Skip to content

Parser: fix exponential parse time on chains of (x)-> and nested POSITION calls - #18

Open
moshap-firebolt wants to merge 2 commits into
firebolt/v0.63.0-patchesfrom
moshap/fix-lambda-arrow-and-position-blowup
Open

moshap-firebolt wants to merge 2 commits into
firebolt/v0.63.0-patchesfrom
moshap/fix-lambda-arrow-and-position-blowup

Conversation

@moshap-firebolt

@moshap-firebolt moshap-firebolt commented Oct 14, 2026 •

Copy link
Copy Markdown

Two speculative parses that roll back and retry the same tail, so each level doubles the work:

Both shapes now parse in about 2ms. New tests: parse_lambda_arrow_chain_no_exponential_blowup and parse_lambda_vs_arrow_operator_dispatch, plus apache#2619's own tests. The full suite, fmt and clippy are clean.

🤖 Generated with Claude Code


Note

Medium Risk
Touches core expression parsing dispatch; memoization is designed to preserve fallback behavior but wrong cache keys could change how ambiguous SQL is classified.

Overview
Fixes exponential parse time on two speculative-parse patterns by memoizing failed attempts at the same token index (and parser state, so CONNECT BY / PRIOR behavior stays correct).

For lambda arrow syntax in dialects that support it, chained (x)->(x)->... with an invalid tail no longer re-tries the same speculative lambda parse on every binary--> fallback. Failed positions are recorded in failed_lambda_positions and short-circuit to the same Ok(None) path as a fresh failure.

For POSITION(... IN ...), nested POSITION(POSITION(...)) no longer re-parses the IN form on every outer function-call fallback. Failures are cached in failed_position_syntax_positions before delegating to ordinary parse_function.

Adds regression tests for time bounds on deep chains and for unchanged AST dispatch (lambda vs arrow operator, POSITION special form vs function call).

Reviewed by Cursor Bugbot for commit 8ffe445. Bugbot is set up for automated code reviews on this repo. Configure here.

fmcmac and others added 2 commits October 14, 2026 10:44
)

(cherry picked from commit e790703)

Conflict: this branch keeps its own `IN (` cache (a set of positions)
rather than upstream's later map, so only the POSITION field is added.
Dropped parse_position_in_connect_by_list_after_state_change: its SQL
starts an IN list with a parenthesized subquery, which needs upstream
apache#2586 and fails to parse here with or without this change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
In dialects with lambda functions, `try_parse_lambda` speculatively parses
`(params) -> body` and rolls back if the body fails. The fallback parses
`(x)` as a nested expression and `->` as the binary `Arrow` operator, whose
right side starts at the same `(` and re-attempts the identical lambda
parse, so each level of `(x)->(x)->...` costs twice the one below it.
`SELECT (x)->...1 +` with 16 arrows took 98ms and doubles per arrow.

Memoize the failed positions, as `parse_in` and `parse_table_factor`
already do for the same shape. A cached failure yields the same fallback
as re-running the parse, so behaviour is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants