fix(compiler): scope-aware column resolution in subqueries (#4251)#4457
Open
luongs3 wants to merge 1 commit into
Open
fix(compiler): scope-aware column resolution in subqueries (#4251)#4457luongs3 wants to merge 1 commit into
luongs3 wants to merge 1 commit into
Conversation
…4251) When resolving an unqualified column reference inside a subquery, sqlc's analyzer matched against every table in scope across the whole query, producing a spurious "column reference ... is ambiguous" error for queries that real PostgreSQL resolves unambiguously via lexical scope. Example from the issue: SELECT * FROM t1 WHERE id IN ( SELECT t1_id FROM t2 WHERE id = $1 ); Real Postgres binds the outer "id" to t1.id and the inner "id" to t2.id by lexical scope. sqlc was flattening every FROM-clause RangeVar in the whole query into one search list and triggering "ambiguous" on the inner id. Two small changes: * internal/compiler/find_params.go — when paramSearch.Visit enters a SelectStmt whose FROM clause has exactly one RangeVar, capture it as the current scope (paramSearch.rangeVar). The walker propagates this through the returned visitor, so ParamRefs encountered in the same SelectStmt's WHERE/GROUP/etc. inherit the inner scope. The exactly-one guard ensures we don't silently pick a winner when the FROM clause is genuinely multi-table at the same level — those cases must keep iterating every table so true ambiguity (e.g. "SELECT t1.id FROM t1, t2 WHERE id = $1") still errors. * internal/compiler/resolve.go — when resolving an unqualified column, if no alias is given but ref.rv points to an in-scope table that actually contains the column, narrow the search to that table only. If the column is absent from the inner table, fall back to the full search list so correlated-subquery references to an outer column continue to work. Tests: * internal/compiler/resolve_test.go — new unit test covering narrowToInnermostScope across nil-rv, column-in-inner-scope (narrow), column-absent (fall back), and unknown-table (fall back). * internal/endtoend/testdata/subquery_scope_4251/ — end-to-end fixture reproducing the exact query from the issue under postgresql/pgx/v5; generated code asserted against the golden files. Verified locally: * /tmp/repro4251 generate now returns EXIT=0 (was EXIT=1 with 'relation "id" ambiguous'). * SELECT t1.id FROM t1, t2 WHERE id = $1 still correctly errors with 'ambiguous'. * Correlated subquery (SELECT SUM(total) FROM orders WHERE customer_id = c.id) still resolves cleanly. * go test ./internal/compiler/... ./internal/sql/... ./internal/engine/... passes; gofmt -l and go vet clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9780d84 to
a47f6d3
Compare
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.
Fixes #4251.
Problem
When resolving an unqualified column reference inside a subquery, sqlc's analyzer matched against every table in scope across the whole query, producing a spurious
column reference "id" is ambiguouserror for queries that real PostgreSQL resolves unambiguously via lexical scope.The exact repro from the issue:
Real Postgres binds the outer
idtot1.idand the inneridtot2.idby lexical scope. sqlc was flattening every FROM-clause RangeVar in the whole query into one search list and triggering "ambiguous" on the inner id.Fix
Two small changes:
internal/compiler/find_params.go— whenparamSearch.Visitenters aSelectStmtwhose FROM clause has exactly oneRangeVar, capture it as the current scope. The walker propagates this through the returned visitor, soParamRefs in that SelectStmt's WHERE/GROUP inherit the inner scope.The exactly-one guard ensures we don't silently pick a winner when the FROM is genuinely multi-table at the same level — true ambiguity (e.g.
SELECT t1.id FROM t1, t2 WHERE id = $1) must still error.internal/compiler/resolve.go— when resolving an unqualified column, if no alias is given butref.rvpoints to an in-scope table that actually contains the column, narrow the search to that table only. If the column is absent from the inner table, fall back to the full search list so correlated-subquery references to an outer column continue to work.Tests
internal/compiler/resolve_test.go— new unit test coveringnarrowToInnermostScopeacross nil-rv, column-in-inner-scope (narrow), column-absent (fall back), and unknown-table (fall back).internal/endtoend/testdata/subquery_scope_4251/— end-to-end fixture reproducing the exact query under postgresql/pgx/v5; the generated code is asserted against golden files.Verified locally
SELECT t1.id FROM t1, t2 WHERE id = $1still errors with "ambiguous" — no regression.go test ./internal/compiler/... ./internal/sql/... ./internal/engine/...passes;gofmt -landgo vetclean on the touched files.