Skip to content

fix(parse): resolve correlated subqueries in strict mode - #351

Merged
tiagolauer merged 1 commit into
masterfrom
fix/273-correlated-subquery
Aug 2, 2026
Merged

fix(parse): resolve correlated subqueries in strict mode#351
tiagolauer merged 1 commit into
masterfrom
fix/273-correlated-subquery

Conversation

@tiagolauer

Copy link
Copy Markdown
Owner

Fixes #273.

The bug

StrictQuery<DB, 'select (select count(*) from posts where posts.user_id = users.id) as post_count from users'>
// QueryTypeError<'unknown alias: users'>[]

That is the README's own scalar-subquery example. The lateral form was worse, because the inner error object replaced the derived row and the surfaced message pointed at the wrong thing:

StrictQuery<DB, 'select u.name, p.top_id from users u join lateral (select max(id) as top_id from posts where posts.user_id = u.id) p on true'>
// QueryTypeError<'unknown column: top_id'>[]

Not in the documented limitations — and CONTRIBUTING.md:68 claimed the README documented it, which it never did.

The fix

ScalarSubqueryType and BuildDerivedSourceMap ran the inner query through InferRowWith with only the inner query's own sources. Both now pass the enclosing FROM list down, and the WHERE scan consults it.

As a fallback, not as an addition to the scope. SQL resolves an inner name first and looks outward only when it is not there; merging the two lists would have invented an ambiguous column for every name the two levels share (select (select max(id) from posts) from users has an id on both sides). When the outer lookup fails too, the inner message is reported — the reference was meant for the inner query.

The second half of the issue is fixed too: a derived table that fails to type now surfaces its own error, instead of the outer query reporting the alias's columns as unknown. tests/where-strict.test-d.ts had that misdirection pinned (unknown column: id for a typo named naem); it now pins the real message.

Verification

tests/correlated-subquery.test-d.ts, ten cases. Five red on master, plus the updated lock:

tests/correlated-subquery.test-d.ts(17,3): error TS2344: Type 'false' does not satisfy the constraint 'true'.
tests/correlated-subquery.test-d.ts(27,3): ... (37,3), (47,3), (69,3)
tests/where-strict.test-d.ts(146,3): ...
  • the README example, the same through an alias, a correlated LATERAL join, a correlated derived table
  • a typo inside a correlated subquery still errors, and now names nope rather than the alias
  • a standalone query is not given an outer scope it does not have: select ... where posts.user_id = u.id on its own is still unknown alias: u

Controls: uncorrelated scalar subquery and uncorrelated lateral join unchanged, loose mode unchanged.

tsc --noEmit clean, 192 runtime tests pass, budget 199,660 (+7,174, 100% of the ceiling — it stays under).

    StrictQuery<DB, 'select (select count(*) from posts where posts.user_id = users.id) as post_count from users'>
    // was QueryTypeError<'unknown alias: users'>[]

That is the README's own scalar-subquery example. ScalarSubqueryType and
BuildDerivedSourceMap ran the inner query through InferRowWith with only the
inner query's sources, so any correlated reference had nothing to resolve
against. The restriction is not in the documented limitations, and
CONTRIBUTING.md claimed the README documented it, which it never did.

The outer sources are a fallback, not an addition to the scope. SQL resolves
an inner name first and looks outward only when it is not there, so merging
the two lists would have invented an `ambiguous column` for every name the
two levels share. When the outer lookup fails too, the inner message is what
gets reported - the reference was meant for the inner query.

A derived table that fails to type now surfaces its own error as well. It
used to replace its row with the error object, and the outer query then
reported the alias's columns as unknown, pointing at the wrong line; the
existing lock in tests/where-strict.test-d.ts pinned that misdirection and
is updated to the real message.

Fixes #273

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tiagolauer
tiagolauer force-pushed the fix/273-correlated-subquery branch from 61f55b9 to 97e6afa Compare August 2, 2026 13:04
@tiagolauer
tiagolauer merged commit b1b19ea into master Aug 2, 2026
12 checks passed
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.

Strict mode rejects correlated subqueries, including the README example

1 participant