Repository navigation
Conversation
`DELETE FROM t ROWS (q)` deletes `t INTERSECT ALL q`: each row of `q` removes at most one copy of a matching table row, and rows of `q` that the table does not hold are ignored. This lets users delete the excess copies of duplicated rows, which `WHERE (...) IN (subquery)` cannot express because it deletes every copy. The query is planned like the source of `INSERT ... SELECT`, including assignment casts to the table's column types, and the INTERSECT ALL lowering moves into a reusable `HirRelationExpr::intersect_all`. The syntax is gated behind the `enable_delete_rows` feature flag. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
QA LLM Review1. MEDIUM -- Assignment casts let
|
Assignment casts round or truncate, so a query row that equals no table row could round onto one and delete it. Implicit casts are the ones INTERSECT ALL applies when unifying the query with the table's types, so the statement deletes exactly t INTERSECT ALL q whenever it plans, and lossy or narrowing conversions are rejected at plan time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Fixed in b9245bc: the query's columns now take implicit casts only, the casts INTERSECT ALL applies when unifying, so the statement deletes exactly Posted by Claude Code. |
Adds
DELETE FROM t ROWS (<query>), which removes the rows a query returns, with multiplicity.DELETE ... WHEREonly takes per-row predicates, andWHERE (...) IN (subquery)removes every copy of a duplicated row instead of the excess count.The statement deletes
t INTERSECT ALL q, so it removes at most as many copies of a row as the table holds and cannot drive multiplicities negative. The query's columns take implicit casts only, the ones INTERSECT ALL applies, because assignment casts round and would match rows the query does not contain. Planning reuses the INTERSECT ALL lowering, and execution reuses the read-then-write path. The form is gated behindenable_delete_rows, which is off by default and on in CI.rowsstill parses as a table alias unlessROWS (is followed by the start of a query.Standard SQL has no multiset delete. PostgreSQL expresses one through physical row identity, joining numbered copies on
ctidand deletingWHERE ctid IN (...), with CTEs only organizing the query. Materialize tables have no row identity, so identical copies are indistinguishable and any content predicate removes all of them or none. GivingWHERE (...) IN (q)count semantics would change the meaning of an existing standard query, hence a separate clause.🤖 Generated with Claude Code