parser: keep parenthesized set-op operand and bind trailing ORDER BY/LIMIT to the whole set#49
Merged
Conversation
…LIMIT to the whole set
Two set-operation parsing defects (parser robustness audit):
1. A parenthesized right operand was silently dropped. parse_setop_operand only
accepted a bare SELECT keyword, so `SELECT 1 UNION (SELECT 2)` returned just
`SELECT 1` and abandoned `UNION (SELECT 2)` as trailing tokens. It now also
accepts a parenthesized query block `( <query> )` (which may itself be a set
operation and keeps its own ORDER BY / LIMIT), consuming the matching ')'.
2. A trailing ORDER BY / LIMIT bound to the right branch instead of the whole
set operation. The right operand (parsed via parse_select_stmt in
in_setop_rhs_ mode) consumed the ORDER BY / LIMIT before the set-op-rhs early
return, so `SELECT 1 UNION SELECT 2 ORDER BY 1` attached ORDER BY to the RHS
SELECT and then failed validation. The ORDER BY / LIMIT parsing is factored
into a lambda, skipped for bare set-op operands, and re-run once after folding
so it attaches to the combined set-op node. A plain SELECT is unaffected (it
consumes its clauses on the first pass; the post-fold pass is a no-op).
Known remaining gap (separate, more invasive entry-point change; tracked as a
follow-up): a LEADING parenthesized operand `(SELECT 1) UNION (SELECT 2)` still
errors, because parse_select_stmt's entry does not yet accept a leading '('.
Tests (test_precedence_regression): parenthesized right operand keeps both
branches; trailing ORDER BY and ORDER BY + LIMIT attach to the UNION node; a
plain SELECT still attaches its own ORDER BY / LIMIT. Full suite 37/37 green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FyKDAbMWGwiZq4iJx3imsg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Two set-operation parsing defects (parser robustness audit):
Parenthesized right operand silently dropped.
parse_setop_operandonly accepted a bareSELECTkeyword, soSELECT 1 UNION (SELECT 2)returned justSELECT 1and abandonedUNION (SELECT 2)as trailing tokens. It now also accepts a parenthesized query block( <query> )— which may itself be a set operation and keeps its ownORDER BY/LIMIT— consuming the matching).Trailing
ORDER BY/LIMITbound to the right branch, not the whole set. The right operand (parsed viaparse_select_stmtinin_setop_rhs_mode) consumed theORDER BY/LIMITbefore the set-op-rhs early return, soSELECT 1 UNION SELECT 2 ORDER BY 1attachedORDER BYto the RHSSELECTand then failed validation. TheORDER BY/LIMITparsing is factored into a lambda, skipped for bare set-op operands, and re-run once after folding so it attaches to the combined set-op node. A plainSELECTis unaffected (consumes its clauses on the first pass; the post-fold pass is a no-op).Before / after
SELECT 1 UNION (SELECT 2)— before:SelectStmt(1)(4 trailing tokens); after:UnionStmt → [SelectStmt(1), SelectStmt(2)].SELECT 1 UNION SELECT 2 ORDER BY 1— before:PARSE ERROR: validation failed; after:UnionStmt → [SelectStmt(1), SelectStmt(2), OrderByClause].Known remaining gap (follow-up)
A leading parenthesized operand
(SELECT 1) UNION (SELECT 2)still errors —parse_select_stmt's entry point doesn't yet accept a leading(. That's a separate, more invasive change and is tracked as a follow-up; this PR is scoped to the two confirmed silent-drop / mis-parse findings.Tests
test_precedence_regression: parenthesized right operand keeps both branches; trailingORDER BYandORDER BY + LIMITattach to theUNIONnode; a plainSELECTstill attaches its ownORDER BY/LIMIT(regression guard). Full suite 37/37 green.Completes the parser-layer set-operation robustness (CASE #46,
IS DISTINCT FROM#47,IN#48 already up/merged).