Skip to content

feat(analysis): type predicate subquery placeholders from columns - #80

Merged
omer-cengel merged 1 commit into
masterfrom
feature/predicate-subqueries-typed-from-columns
Oct 12, 2026
Merged

omer-cengel merged 1 commit into
masterfrom
feature/predicate-subqueries-typed-from-columns

Conversation

@omer-cengel

Copy link
Copy Markdown
Member

Summary

Placeholders inside EXISTS, NOT EXISTS, [NOT] IN (SELECT ...), and scalar comparison subqueries are now typed and named from columns, so membership checks and correlated filters compile without casts. A predicate subquery is analyzed as a nested read that sees its own sources first and then each enclosing query's sources, as PostgreSQL resolves correlated references.

Changes

  • Analyze predicate subqueries wherever the WHERE rules reach them through AND, OR, NOT, and parentheses: a read's WHERE, a join ON condition, an UPDATE or DELETE WHERE, an ON CONFLICT target predicate, a DO UPDATE predicate, and the WHERE of another analyzed nested query.
  • Resolve columns through a scope chain: unqualified columns resolve in the innermost level that has them and are ambiguous only within one level; qualified columns use the innermost source of that name; a join condition sees only the sources up to its joined source. An unknown column names every visible source from the innermost level outward.
  • Descend into NOT, so NOT (id = :id) types its placeholder.
  • Put a write's WITH clauses in scope of the predicate subqueries of its WHERE and conflict predicates.
  • Accept SELECT 1, wildcards, and columns of unmapped types in a predicate subquery's select list, while still rejecting an unknown direct column there; a set operation inside a predicate subquery gets the set-operation diagnostic.
  • Keep binding order as the textual order of placeholders across all levels; a named placeholder repeated across levels is one parameter.
  • Add analyzer tests for each form and location, generated-source compilation tests, and a PostgreSQL test of an uncast membership check and a correlated NOT EXISTS filter; update the query documentation.

Scope and non-goals

  • = ANY (SELECT ...), IN ((SELECT ...)), a bare boolean subquery, and subqueries in a projection, GROUP BY, HAVING, ORDER BY, a written value, RETURNING, or a function argument keep their existing unanalyzed handling, where only a cast types a placeholder.
  • A placeholder compared with a subquery, such as :x = (SELECT ...), is not typed from it.
  • A WITH body or FROM subquery nested inside a predicate subquery still sees no enclosing source.
  • Generated code structure, the runtime, and executable SQL are unchanged.

EXISTS, NOT EXISTS, [NOT] IN (SELECT ...), and comparison subqueries in a
WHERE, a join ON condition, a write's WHERE, and a conflict predicate are
analyzed as nested reads. Their placeholders are typed and named from
columns of their own sources and of each enclosing query, so they no
longer need a cast. The WHERE rules also descend into NOT, and a write's
WITH clauses are in scope of its predicate subqueries.
@omer-cengel
omer-cengel merged commit aac4232 into master Oct 12, 2026
7 checks passed
@omer-cengel
omer-cengel deleted the feature/predicate-subqueries-typed-from-columns branch October 12, 2026 19:25
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