Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions packages/objectql/src/engine-cascade-delete.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -282,10 +282,16 @@ describe('cascadeDeleteRelations — required FK escalates set_null → restrict
it('[#9625] refuses a required MULTI-VALUE lookup even when member removal would leave the set non-empty', async () => {
// The escalation runs before the multi-value branch and keys on
// `required` alone, so the other live member does not save the delete.
// Pinned as CURRENT behaviour, deliberately not changed here: `[]`
// still satisfies `required` in the record validator (#9476), so the
// blanket refusal is what stops an emptied required set landing
// silently.
// Pinned as CURRENT behaviour, deliberately not changed here: the
// refusal lands before the member-removal write runs, so what reaches
// the caller is `DELETE_RESTRICTED` about the `acct` it asked to
// delete, not a `required` error naming a field on `roster`. This
// comment used to add that `[]` still satisfied `required` in the
// record validator, making the refusal the only thing stopping an
// emptied required set from landing silently — #9476 has landed and
// `[]` is rejected there now, so that clause is gone. The assertions
// below never rested on it: they pin the 409 envelope and the
// untouched set.
const a = await engine.insert('acct', { name: 'Acme' });
const b = await engine.insert('acct', { name: 'Beta' });
const r = await engine.insert('roster', { accounts: [a.id, b.id] });
Expand Down
14 changes: 11 additions & 3 deletions packages/objectql/src/engine.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -10450,9 +10450,17 @@ export class ObjectQL implements IObjectQLEngine {
// even when the child's set holds other members and member removal
// would leave it non-empty — a state the #9447 ruling accepts.
// Measured, pinned as current behaviour, and carded separately rather
// than changed here: today `[]` still satisfies `required` in the
// record validator (#9476), so this blanket refusal is what keeps an
// emptied required set from landing silently.
// than changed here. What justifies refusing is the paragraph above,
// not the validator's tolerance: the escalation refuses THIS relation
// before its own set_null write runs, so the caller is told
// `DELETE_RESTRICTED` about the record it asked to delete, instead of
// the child's own `required` 400 — which names a field that is not on
// that record's object at all. The predecessor of this comment rested
// it on `[]` still satisfying `required` in the record validator,
// which made this refusal the only thing between an emptied required
// set and a silent write; #9476 landed and `[]` is rejected there now
// too, so the refusal is no longer that last guard. It is the one that
// fires early, against the right record.
if (behavior === 'set_null' && fdef.required === true) {
behavior = 'restrict';
}
Expand Down
Loading