Commit a47f6d3
fix(compiler): scope-aware column resolution in subqueries (#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>1 parent a3b0cfd commit a47f6d3
9 files changed
Lines changed: 277 additions & 10 deletions
File tree
- internal
- compiler
- endtoend/testdata/subquery_scope_4251/postgresql/pgx/v5
- go
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | | - | |
34 | | - | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
35 | 37 | | |
36 | 38 | | |
37 | 39 | | |
| |||
139 | 141 | | |
140 | 142 | | |
141 | 143 | | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
142 | 160 | | |
143 | 161 | | |
144 | 162 | | |
| |||
186 | 204 | | |
187 | 205 | | |
188 | 206 | | |
189 | | - | |
| 207 | + | |
190 | 208 | | |
191 | 209 | | |
192 | 210 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
215 | 215 | | |
216 | 216 | | |
217 | 217 | | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
218 | 222 | | |
219 | 223 | | |
220 | 224 | | |
| |||
581 | 585 | | |
582 | 586 | | |
583 | 587 | | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
584 | 592 | | |
585 | 593 | | |
586 | 594 | | |
| |||
636 | 644 | | |
637 | 645 | | |
638 | 646 | | |
| 647 | + | |
| 648 | + | |
| 649 | + | |
| 650 | + | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
| 668 | + | |
| 669 | + | |
| 670 | + | |
| 671 | + | |
| 672 | + | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
| 680 | + | |
| 681 | + | |
| 682 | + | |
| 683 | + | |
| 684 | + | |
| 685 | + | |
| 686 | + | |
| 687 | + | |
| 688 | + | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 44 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 10 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
Lines changed: 8 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
Lines changed: 13 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
0 commit comments