先核來源與已訂規則
本例只准從 V1 且欄 a、b,把 b 改 count。另一分支雖附同名版本,卻是 a、c。盲 ALTER 會因缺 b 失敗;更正先查 table_info,結構不符就停,不更改。
| 紀錄 | 輸入 JSON |
|---|---|
| 原例輸入 | {"branch":"AC","version":"V1"} |
比較兩條實際路徑
更正指出 schema-precondition,不把來源值當成錯。版本未知,即使欄看似符合,仍要暫停。
| 路徑 | 輸出 JSON |
|---|---|
| 錯誤路徑 | {"error":{"code":1},"columns":["a","c"],"records":[{"a":"A","c":2}]} |
| 更正路徑 | {"error":"schema-precondition","columns":["a","c"],"records":[{"a":"A","c":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,'+(i.branch==='AB'?'b':'c')+' INTEGER);');db.prepare('INSERT INTO target VALUES(?,?)').run('A',2);
const columns=()=>db.prepare('PRAGMA table_info(target)').all().map(c=>c.name);let error=null;
if(corrected&&(i.version!=='V1'||!columns().includes('b')))error=i.version===null?'unknown-version':'schema-precondition';
else error=failed(()=>db.exec('ALTER TABLE target RENAME COLUMN b TO count;'));
return {error,columns:columns(),records:rows(db,'SELECT * FROM target')};
}finally{db.close();}
};
const cases=[{"label":"misleading base","input":{"branch":"AC","version":"V1"},"expected":{"error":{"code":1},"columns":["a","c"],"records":[{"a":"A","c":2}]},"corrected":false},{"label":"corrected base","input":{"branch":"AC","version":"V1"},"expected":{"error":"schema-precondition","columns":["a","c"],"records":[{"a":"A","c":2}]},"corrected":true},{"label":"matching actual predecessor","input":{"branch":"AB","version":"V1"},"expected":{"error":null,"columns":["a","count"],"records":[{"a":"A","count":2}]},"corrected":true},{"label":"unknown version even with similar columns","input":{"branch":"AB","version":null},"expected":{"error":"unknown-version","columns":["a","b"],"records":[{"a":"A","b":2}]},"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);
}
下次載入前核邊界
正好 a、b/V1 可成功改名;版本 null 則保 a、b 不動。
| 情況 | 輸入 JSON | 預期輸出 JSON |
|---|---|---|
| 真正前置分支相符 | {"branch":"AB","version":"V1"} | {"error":null,"columns":["a","count"],"records":[{"a":"A","count":2}]} |
| 前置版本未知 | {"branch":"AB","version":null} | {"error":"unknown-version","columns":["a","b"],"records":[{"a":"A","b":2}]} |
標 V1,是否已證明此分支有 b 欄?
查看參考答案
不是,要查真正欄清單。本例 a、c 暫停,照原結構留。