Read the source and its declared rule
The original export contract contains a, b only. A view built with SELECT * changes its shape when internal is added to the underlying table. Corrected view names a, b and keeps the internal value outside this export, including when there are no records.
| Record | Input JSON |
|---|---|
| Base input | {"records":[{"a":"A","b":2,"internal":"secret"}]} |
Compare the actual paths
The bad export leaks a third column secret. Correct output retains two declared columns; column metadata is checked beside rows so an empty result cannot hide the schema change.
| Path | Output JSON |
|---|---|
| Misleading path | {"columns":["a","b","internal"],"records":[{"a":"A","b":2,"internal":"secret"}]} |
| Corrected path | {"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);
}
Check boundaries before the next load
A null internal value is still excluded. Empty data retains the two-column export definition.
| Case | Input JSON | Expected output JSON |
|---|---|---|
| Null internal value remains excluded | {"records":[{"a":"A","b":0,"internal":null}]} | {"columns":["a","b"],"records":[{"a":"A","b":0}]} |
| Empty data with explicit shape | {"records":[]} | {"columns":["a","b"],"records":[]} |
Does an empty export prove its column contract stayed unchanged?
Show the worked answer
No. Inspect the columns too. Corrected empty output still declares exactly a, b.