You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit cbc268d
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: .changeset/27004-predicate-delete-walk-as-set.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,8 +9,8 @@ Metadata-protocol: none
9
9
10
10
-**Before.** The predicate delete ran the by-id referential walk once per matched row, so its cost was matched rows times referencing relations: 1,001 rows of an object three relations point at took 4,004 dependents probes, about 1.7 s on SQLite.
11
11
-**Now.** One walk, rooted at the whole matched set (a by-id delete is a set of one). Each referencing relation is probed once per 500 matched rows with `$in` (a multiple lookup ORs each id's `$contains`), and each row is then judged and written as before: `restrict` refuses, `cascade` deletes (each child the predicate did not match through its own delete, hooks and all), `set_null` clears, a multiple lookup loses only the deleted members and an emptied set is written as `[]`. The same 1,001-row delete takes 12 probes.
12
-
-**A by-id delete's probes, too.** Its own record is still probed with that record's filter, and each cascaded child's delete still probes its one record. The reads made before the first write changed: collecting the rows the cascade removes now probes the records found at one depth together (per object and relation, from the root's children down), and the check for an authored `restrict` that the delete's own cascade would override probes together the records that ask one relation. Two or more records are asked with one `$in` (on a multiple lookup, the OR of each id's `$contains`) where each used to get its own probe; a lone record keeps its own filter. Deleting a parent with two cascading children therefore reads the grandchildren with one `$in`, where it used to send two equality probes. A driver answering these probes now receives `$in`; a batch with a single-valued row that names none of its ids by `String()` is asked again one id at a time.
13
-
-**A matched row another matched row's cascade reaches** (an object's `cascade` reference to itself, with parent and child both matched). The cascade no longer deletes it through its own delete; the predicate delete removes it with the other matched rows. The count `engine.delete` returns, which a flow's `delete_record` hands on as its `result`, now includes that row, where it used to leave it out, and the row's `beforeDelete` and `afterDelete` hooks fire once, as a matched row, where they used to fire a second time from the cascade's delete.
12
+
-**A by-id delete's probes, too.** Its own record is still probed with that record's filter, and each cascaded child's delete still probes its one record. The reads made before the first write changed: collecting the rows the cascade removes now probes the records found at one depth together (per object and relation, from the root's children down), and the check for an authored `restrict` that the delete's own cascade would override probes together the records that ask one relation. Two or more records are asked together with `$in` (on a multiple lookup, the OR of each id's `$contains`) where each used to get its own probe; a lone record keeps its own filter. Deleting a parent with two children on one cascading relation therefore probes each of the children's own cascading relations once with `$in`, where it used to send one probe per child. A driver answering these probes now receives `$in`; a batch with a single-valued row that names none of its ids by `String()` is asked again one id at a time.
13
+
-**A matched row another matched row's cascade reaches** (an object's `cascade` reference to itself, with parent and child both matched). The cascade no longer deletes it through a nested delete of its own; the predicate delete removes it with the other matched rows. The count `engine.delete` returns, which a flow's `delete_record` hands on as its `result`, now includes that row, where it used to leave it out, and the row's `beforeDelete` and `afterDelete` hooks fire once, as a matched row, where they used to fire a second time from the cascade's delete.
14
14
-**Which refusal is reported when more than one applies.** The row-by-row walk finished one matched row's relations before it started the next row, so it reported the first matched row that refused. The set walk asks each relation about every matched row before it moves to the next relation, so it reports the first relation that refuses, naming the first matched row that relation refuses for; and it checks every matched row for an authored `restrict` its own cascade would override before it walks the first relation.
15
15
-**Unchanged.** A refusal's code and wording: it still names one record and counts the rows that refuse over that record (for a required multiple lookup, the rows the removal would empty). The atomic and split plans, the 500-id `$in` delete batches, and the roll-up recompute after the delete.
16
16
-**Who sees it.** A bulk delete that removes rows holding each other: a matched row that another matched row holds is in the delete's own set, so it neither refuses nor is cleared first (the rule a cascade already applies to the rows it deletes). A holder outside the matched rows still refuses.
0 commit comments