Read the source and its declared rule
The original target quantity 2 must remain positive and non-NULL. MigrationV2 attempts its supplied replacement. Updating the ledger toV2 before BEGIN persists that marker even when a later−1 update fails. Corrected code changes both only inside one transaction and rolls back on error.
| Record | Input JSON |
|---|---|
| Base input | {"nextQuantity":-1} |
Compare the actual paths
Failed corrected update leaves actual quantity 2 and ledgerV1. The bad split state is quantity 2 with ledgerV2, misrepresenting a completed migration.
| Path | Output JSON |
|---|---|
| Misleading path | {"error":{"code":275},"version":"V2","records":[{"quantity":2}]} |
| Corrected path | {"error":{"code":275},"version":"V1","records":[{"quantity":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 ledger(version TEXT);INSERT INTO ledger VALUES('V1');CREATE TABLE target(quantity INTEGER NOT NULL CHECK(quantity>0));INSERT INTO target VALUES(2);");
if(!corrected)db.exec("UPDATE ledger SET version='V2';");db.exec('BEGIN;');
const error=failed(()=>{db.prepare('UPDATE target SET quantity=?').run(i.nextQuantity);if(corrected)db.exec("UPDATE ledger SET version='V2';");db.exec('COMMIT;');});
if(error)db.exec('ROLLBACK;');return {error,version:db.prepare('SELECT version FROM ledger').get().version,records:rows(db,'SELECT * FROM target')};
}finally{db.close();}
};
const cases=[{"label":"misleading base","input":{"nextQuantity":-1},"expected":{"error":{"code":275},"version":"V2","records":[{"quantity":2}]},"corrected":false},{"label":"corrected base","input":{"nextQuantity":-1},"expected":{"error":{"code":275},"version":"V1","records":[{"quantity":2}]},"corrected":true},{"label":"successful data and ledger commit","input":{"nextQuantity":4},"expected":{"error":null,"version":"V2","records":[{"quantity":4}]},"corrected":true},{"label":"required target value missing","input":{"nextQuantity":null},"expected":{"error":{"code":1299},"version":"V1","records":[{"quantity":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);
}
Check boundaries before the next load
Valid replacement 4 commits withV2. Missing replacementnull fails 1299 and retainsV1/2.
| Case | Input JSON | Expected output JSON |
|---|---|---|
| Data and version both commit | {"nextQuantity":4} | {"error":null,"version":"V2","records":[{"quantity":4}]} |
| Missing required replacement | {"nextQuantity":null} | {"error":{"code":1299},"version":"V1","records":[{"quantity":2}]} |
After the−1 failure, which version marker is truthful?
Show the worked answer
V1. NoV2 data effect committed. Keep V1 and quantity 2 together until a successful migration transaction.