Skip to content

feat(analysis): analyze multi-row and SELECT-sourced inserts - #81

Merged
omer-cengel merged 1 commit into
masterfrom
feature/multi-row-and-select-sourced-inserts
Oct 14, 2026
Merged

omer-cengel merged 1 commit into
masterfrom
feature/multi-row-and-select-sourced-inserts

Conversation

@omer-cengel

Copy link
Copy Markdown
Member

Summary

An INSERT can now write several VALUES rows or the rows of a SELECT, with every placeholder typed from the column it is written into. Bulk inserts such as INSERT INTO memberships (project_id, user_id) SELECT :projectId, unnest(:userIds::bigint[]) and multi-row inserts now compile without extra casts, where previously they were rejected.

Changes

  • Several VALUES rows pair with the column list by position; a named placeholder repeated across rows is one parameter bound at each position. VALUES rows written inside parentheses at any depth are accepted.
  • INSERT ... SELECT, parenthesized or not and with or without FROM, pairs its items with the column list by position. A placeholder item is typed and named by its target column, a cast item is named by its target column, direct columns resolve against the SELECT's own sources, and * / t.* count one item per source column.
  • The insert's SELECT is analyzed like any other analyzed nested read: its join ON, WHERE, LIMIT, and OFFSET placeholders are typed by their clause rules, CTEs and subquery sources are read, and the insert target is not in scope.
  • WITH clauses written after the column list are analyzed, on a SELECT and on a VALUES source, as well as before the INSERT.
  • ON CONFLICT and RETURNING are accepted on every accepted row source.
  • New diagnostics: INSERT column and value counts must match. for a row or a SELECT of the wrong width, and INSERT requires parenthesized VALUES rows or a SELECT. for VALUES ROW(...), a bare value element, and DEFAULT VALUES; a set operation as the row source keeps the set-operation diagnostic.
  • Analyzer, generated-source compilation, and PostgreSQL integration tests cover the new shapes; the query documentation and the unsupported-query catalogue are updated.

Scope and non-goals

  • UPDATE ... FROM and DELETE ... USING remain rejected.
  • Set operations, recursive or data-modifying CTEs, LATERAL, JDBC batch execution, and COPY remain unsupported.
  • RETURNING item rules, conflict-clause scopes, and the rules of other nested SELECTs are unchanged; a scalar subquery in an insert SELECT item is still typed only by casts.
  • Executable SQL is passed through unchanged, and parameters still bind in textual order.

An INSERT now accepts several VALUES rows, written inside parentheses at
any depth, or a SELECT, with or without FROM, and pairs each value or item
with the column list by position. A placeholder is typed and named by its
target column, so INSERT ... SELECT :projectId, unnest(:userIds::bigint[])
compiles. The SELECT is analyzed as a nested read that sees no target,
WITH clauses after the column list are analyzed, and both sources accept
ON CONFLICT and RETURNING. Rows that are not parenthesized, DEFAULT VALUES,
count mismatches, and set operations are rejected with diagnostics.
@omer-cengel
omer-cengel merged commit 1f0210b into master Oct 14, 2026
7 checks passed
@omer-cengel
omer-cengel deleted the feature/multi-row-and-select-sourced-inserts branch October 14, 2026 17:49
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