先核來源與已訂規則
本例完整批次先列子 1,後列父 1。已約定目標外鍵可在單一明確交易內延後檢查。即時檢查會先拒子行;延後只是改核對時點,最後仍須有父。
| 紀錄 | 輸入 JSON |
|---|---|
| 原例輸入 | {"transaction":true,"parentPresent":true} |
比較兩條實際路徑
更正完整批次可提交兩行;父仍缺,COMMIT 便錯 787,例子再回復,兩行均不留已提交狀態。
| 路徑 | 輸出 JSON |
|---|---|
| 錯誤路徑 | {"error":{"code":787},"parents":[],"children":[]} |
| 更正路徑 | {"error":null,"parents":[{"id":1}],"children":[{"id":1,"parent_id":1}]} |
import {DatabaseSync} from 'node:sqlite';
import {TextDecoder} from 'node:util';
import {inflateSync} from 'node:zlib';
import {posix} from 'node:path';
import {createHash} from 'node:crypto';
const rows=(db,sql)=>db.prepare(sql).all().map(r=>({...r}));
const failed=(action)=>{try{action();return null;}catch(e){return {code:e.errcode??e.code??e.name};}};
import assert from 'node:assert/strict';
const run=function run(i,corrected=true){
const db=new DatabaseSync(':memory:');try{db.exec('PRAGMA foreign_keys=ON;CREATE TABLE parent(id INTEGER PRIMARY KEY);CREATE TABLE child(id INTEGER PRIMARY KEY,parent_id INTEGER REFERENCES parent(id)'+(corrected?' DEFERRABLE INITIALLY DEFERRED':'')+');');
if(i.transaction)db.exec('BEGIN;');
const error=failed(()=>{db.exec('INSERT INTO child VALUES(1,1);');if(i.parentPresent)db.exec('INSERT INTO parent VALUES(1);');if(i.transaction)db.exec('COMMIT;');});
if(error&&i.transaction)db.exec('ROLLBACK;');
return {error,parents:rows(db,'SELECT * FROM parent'),children:rows(db,'SELECT * FROM child')};
}finally{db.close();}
};
const cases=[{"label":"misleading base","input":{"transaction":true,"parentPresent":true},"expected":{"error":{"code":787},"parents":[],"children":[]},"corrected":false},{"label":"corrected base","input":{"transaction":true,"parentPresent":true},"expected":{"error":null,"parents":[{"id":1}],"children":[{"id":1,"parent_id":1}]},"corrected":true},{"label":"missing parent at commit","input":{"transaction":true,"parentPresent":false},"expected":{"error":{"code":787},"parents":[],"children":[]},"corrected":true},{"label":"no deferred window outside transaction","input":{"transaction":false,"parentPresent":true},"expected":{"error":{"code":787},"parents":[],"children":[]},"corrected":true}];
for(const test of cases){
const actual=run(test.input,test.corrected);
assert.deepEqual(actual,test.expected,test.label);
console.log(test.label,actual);
}
下次載入前核邊界
最後仍欠父,提交會失敗;沒有明確交易,子行沒有延後空間,即時失敗。
| 情況 | 輸入 JSON | 預期輸出 JSON |
|---|---|---|
| 提交時仍欠父 | {"transaction":true,"parentPresent":false} | {"error":{"code":787},"parents":[],"children":[]} |
| 沒有明確交易空間 | {"transaction":false,"parentPresent":true} | {"error":{"code":787},"parents":[],"children":[]} |
延後檢查是否代表提交後可繼續欠父?
查看參考答案
不是,提交時父 1 必須在。延後只容許交易內暫時使用本例載入次序。