先核來源與已訂規則
本例匯出只約定 a、b。視圖若用 SELECT *,底表加 internal 後形狀也改。更正明列 a、b,內部值不入此匯出;即使沒有紀錄,欄形狀也要一樣。
| 紀錄 | 輸入 JSON |
|---|---|
| 原例輸入 | {"records":[{"a":"A","b":2,"internal":"secret"}]} |
比較兩條實際路徑
錯匯出多了第三欄 secret;更正仍兩個約定欄。行以外也核欄metadata,空結果才不會掩蓋結構變動。
| 路徑 | 輸出 JSON |
|---|---|
| 錯誤路徑 | {"columns":["a","b","internal"],"records":[{"a":"A","b":2,"internal":"secret"}]} |
| 更正路徑 | {"columns":["a","b"],"records":[{"a":"A","b":2}]} |
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('CREATE TABLE target(a TEXT,b INTEGER);CREATE VIEW export AS SELECT '+(corrected?'a,b':'*')+' FROM target;');
db.exec('ALTER TABLE target ADD COLUMN internal TEXT;');
for(const r of i.records)db.prepare('INSERT INTO target VALUES(?,?,?)').run(r.a,r.b,r.internal);
const stmt=db.prepare('SELECT * FROM export');return {columns:stmt.columns().map(c=>c.name),records:stmt.all().map(r=>({...r}))};
}finally{db.close();}
};
const cases=[{"label":"misleading base","input":{"records":[{"a":"A","b":2,"internal":"secret"}]},"expected":{"columns":["a","b","internal"],"records":[{"a":"A","b":2,"internal":"secret"}]},"corrected":false},{"label":"corrected base","input":{"records":[{"a":"A","b":2,"internal":"secret"}]},"expected":{"columns":["a","b"],"records":[{"a":"A","b":2}]},"corrected":true},{"label":"null internal value remains excluded","input":{"records":[{"a":"A","b":0,"internal":null}]},"expected":{"columns":["a","b"],"records":[{"a":"A","b":0}]},"corrected":true},{"label":"empty dataset still has export shape","input":{"records":[]},"expected":{"columns":["a","b"],"records":[]},"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);
}
下次載入前核邊界
內部值 null 也不入匯出;資料空,匯出定義仍兩欄。
| 情況 | 輸入 JSON | 預期輸出 JSON |
|---|---|---|
| 內部空值也排除 | {"records":[{"a":"A","b":0,"internal":null}]} | {"columns":["a","b"],"records":[{"a":"A","b":0}]} |
| 資料空但欄形狀明確 | {"records":[]} | {"columns":["a","b"],"records":[]} |
匯出沒有行,是否已證明欄定義沒變?
查看參考答案
不是,也要查欄清單。更正空輸出仍明訂只有 a、b。