先核來源與已訂規則
本例節點先 A→B,再根 B,目標是即時自外鍵。B 雖在完整輸入,寫 A 時尚未在目標。已准兩步路徑先建可空連結行,再改參考;完整交易才提交。
| 紀錄 | 輸入 JSON |
|---|---|
| 原例輸入 | {"nodes":[{"id":"A","parent":"B"},{"id":"B","parent":null}]} |
比較兩條實際路徑
更正目標有 A→B 及根 B。若批次根本缺 B,後段 UPDATE 真會錯 787,回復也移除暫建行。
| 路徑 | 輸出 JSON |
|---|---|
| 錯誤路徑 | {"error":{"code":787},"nodes":[]} |
| 更正路徑 | {"error":null,"nodes":[{"id":"A","parent_id":"B"},{"id":"B","parent_id":null}]} |
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 node(id TEXT PRIMARY KEY,parent_id TEXT REFERENCES node(id));BEGIN;');
const error=failed(()=>{for(const n of i.nodes)db.prepare('INSERT INTO node VALUES(?,?)').run(n.id,corrected?null:n.parent);
if(corrected)for(const n of i.nodes)db.prepare('UPDATE node SET parent_id=? WHERE id=?').run(n.parent,n.id);
db.exec('COMMIT;');});
if(error)db.exec('ROLLBACK;');return {error,nodes:rows(db,'SELECT * FROM node ORDER BY id')};
}finally{db.close();}
};
const cases=[{"label":"misleading base","input":{"nodes":[{"id":"A","parent":"B"},{"id":"B","parent":null}]},"expected":{"error":{"code":787},"nodes":[]},"corrected":false},{"label":"corrected base","input":{"nodes":[{"id":"A","parent":"B"},{"id":"B","parent":null}]},"expected":{"error":null,"nodes":[{"id":"A","parent_id":"B"},{"id":"B","parent_id":null}]},"corrected":true},{"label":"missing later reference rolls back allocations","input":{"nodes":[{"id":"A","parent":"B"}]},"expected":{"error":{"code":787},"nodes":[]},"corrected":true},{"label":"independent root","input":{"nodes":[{"id":"A","parent":null}]},"expected":{"error":null,"nodes":[{"id":"A","parent_id":null}]},"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);
}
下次載入前核邊界
欠 B 時已提交目標留空;獨立根 A 可連 null 父提交。
| 情況 | 輸入 JSON | 預期輸出 JSON |
|---|---|---|
| 後列父紀錄實際欠缺 | {"nodes":[{"id":"A","parent":"B"}]} | {"error":{"code":787},"nodes":[]} |
| 獨立根紀錄 | {"nodes":[{"id":"A","parent":null}]} | {"error":null,"nodes":[{"id":"A","parent_id":null}]} |
暫時分配好行,參考未核過,可算載入完成嗎?
查看參考答案
不可,欠 B 會令參考更新失敗,全部暫建行都回復。參考齊才提交。