Skip to content

Parse a parenthesed LATERAL FROM item - #2887

Open
edeadington wants to merge 1 commit into
JSQLParser:masterfrom
edeadington:fix/parenthesed-lateral-from-item
Open

edeadington wants to merge 1 commit into
JSQLParser:masterfrom
edeadington:fix/parenthesed-lateral-from-item

Conversation

@edeadington

@edeadington edeadington commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Current problem

A LATERAL FROM item no longer parses when it is wrapped in parentheses:

SELECT * FROM a, (LATERAL FLATTEN(input => a.items)) f
-- Encountered: <OPENING_BRACKET> / "(", at line 1, column 18

It is the parenthesis that fails rather than anything inside it, so this is not specific to Snowflake's FLATTEN — (LATERAL (SELECT 1 FROM b)) f fails at the same position. Unparenthesed LATERAL is unaffected, as are other parenthesed FROM items such as (a JOIN b ON a.id = b.id) and (SELECT 1 FROM b) x.

FromItem() reaches ParenthesedFromItem() through LOOKAHEAD({ isParenthesedFromItemAhead() }), and that predicate declines whenever the token after ( is LATERAL. No other alternative accepts the input, so the parse fails at the opening parenthesis.

if (k2 == K_LATERAL) return false; // handled by LateralSubSelect

When it was introduced

In 5.4. 2d2ee4b replaced the syntactic LOOKAHEAD(ParenthesedFromItem()) with this two-token predicate as part of the lookahead performance work. The syntactic lookahead attempted the parse and succeeded; the predicate has to decide from two tokens, and decides wrongly here. 5.0 through 5.3 parse all of the above.

The comment's premise does not hold. Its single alternative begins at K_LATERAL, so it can never match input beginning at ( — it cannot be the handler the comment defers to. It is also the only branch of the predicate naming a rule that cannot start with (: ParenthesedSelect, the ( TABLE ... ) query form and SubImport all genuinely do, so those lines stay as they are.

With the line removed, ParenthesedFromItem() is tried, consumes the (, and the inner LATERAL item is parsed by the ordinary FromItem() recursion — which is where LateralSubSelect does apply. That is the pre-5.4 behaviour.

testParenthesedLateralFlat and testParenthesedLateral cover the table-function and subselect forms, both round-tripped through the deparser. The full suite passes and JavaCC reports the same warning count as master.

isParenthesedFromItemAhead() declines ParenthesedFromItem when the token
after "(" is LATERAL, on the basis that LateralSubSelect handles it.
LateralSubSelect begins at <K_LATERAL>, so it can never match input that
begins at "(", and no later alternative accepts it either. Every
parenthesed LATERAL item therefore fails at the opening parenthesis with
"Encountered: <OPENING_BRACKET>".

Remove the guard so ParenthesedFromItem is tried, as it was before
2d2ee4b replaced the syntactic LOOKAHEAD(ParenthesedFromItem()) with
this two-token predicate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@edeadington
edeadington marked this pull request as ready for review November 2, 2026 14:26

This branch has not been deployed

No deployments
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.

1 participant