@@ -0,0 +1,627 @@
/**
* v0.7.3 回归测试 — 深度审计第六阶段修复
*
* 1. 索引列 IS NULL 恒空(Memory/KVStore/Hybrid)
* 2. delete RESTRICT 预检前误删索引
* 3. insert 语句级部分提交(PK/unique 批内重复)
* 4. Aria insert 批内 PK 重复部分提交
* 5. queryStream 子查询静默空结果
* 6. ALTER DROP 索引列残留
* 7. CREATE UNIQUE INDEX 存量重复数据
* 8. SELECT * 混别名列投影
* 9. INSERT hooks 列映射
* 10. KVStore insert 持久化 validated 行
*/
import { MetonaSqlark } from '../src/core' ;
import { MemoryEngine } from '../src/engine/memory' ;
describe ( 'v0.7.3: 索引列 IS NULL(三引擎对齐)' , ( ) = > {
test . each ( [
[ 'memory' , { mode : 'memory' } as const ] ,
[ 'disk' , { mode : 'disk' , diskEngine : 'memory' } as const ] ,
[ 'hybrid' , { mode : 'hybrid' , diskEngine : 'memory' } as const ] ,
] ) ( '%s: 索引列 IS NULL 返回 null 行' , async ( _label , cfg ) = > {
const db = await MetonaSqlark . create ( { name : ` v073-isnull- ${ _label } ` , . . . cfg } ) ;
await db . defineTable ( 'users' , {
id : { type : 'string' , primaryKey : true } ,
email : { type : 'string' , index : true } ,
} ) ;
await db . query ( "INSERT INTO users VALUES ('1', NULL), ('2', 'a@b.c')" ) ;
const rows = await db . query ( 'SELECT * FROM users WHERE email IS NULL' ) ;
expect ( rows ) . toHaveLength ( 1 ) ;
expect ( ( rows [ 0 ] as Record < string , unknown > ) . id ) . toBe ( '1' ) ;
await db . close ( ) ;
} ) ;
test . each ( [
[ 'memory' , { mode : 'memory' } as const ] ,
[ 'disk' , { mode : 'disk' , diskEngine : 'memory' } as const ] ,
] ) ( '%s: 索引列 $eq: null( Query Builder)不走索引短路' , async ( _label , cfg ) = > {
const db = await MetonaSqlark . create ( { name : ` v073-isnull-qb- ${ _label } ` , . . . cfg } ) ;
await db . defineTable ( 'users' , {
id : { type : 'string' , primaryKey : true } ,
email : { type : 'string' , unique : true } ,
} ) ;
await db . query ( "INSERT INTO users VALUES ('1', NULL), ('2', 'a@b.c')" ) ;
const rows = await db . table ( 'users' ) . select ( [ 'id' ] ) . where ( { email : { $eq : null } } ) . execute ( ) ;
expect ( rows ) . toHaveLength ( 1 ) ;
await db . close ( ) ;
} ) ;
test ( 'aria: IS NULL 回归护栏(v0.6.2 已修)' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-isnull-aria' , mode : 'aria' , diskEngine : 'memory' } ) ;
await db . defineTable ( 'users' , {
id : { type : 'string' , primaryKey : true } ,
email : { type : 'string' , index : true } ,
} ) ;
await db . query ( "INSERT INTO users VALUES ('1', NULL), ('2', 'a@b.c')" ) ;
const rows = await db . query ( 'SELECT * FROM users WHERE email IS NULL' ) ;
expect ( rows ) . toHaveLength ( 1 ) ;
await db . close ( ) ;
} ) ;
} ) ;
describe ( 'v0.7.3: delete RESTRICT 预检不破坏索引' , ( ) = > {
test ( 'memory: RESTRICT 抛错后唯一约束与索引查询保持有效' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-del-restrict' , mode : 'memory' } ) ;
await db . defineTable ( 'users' , {
id : { type : 'string' , primaryKey : true } ,
email : { type : 'string' , unique : true , index : true } ,
} ) ;
await db . defineTable ( 'orders' , {
id : { type : 'string' , primaryKey : true } ,
user_id : { type : 'string' , references : 'users.id' , onDelete : 'RESTRICT' } ,
} ) ;
await db . query ( "INSERT INTO users VALUES ('u1', 'x@x.x')" ) ;
await db . query ( "INSERT INTO orders VALUES ('o1', 'u1')" ) ;
let threw = false ;
try { await db . query ( "DELETE FROM users WHERE id = 'u1'" ) ; } catch { threw = true ; }
expect ( threw ) . toBe ( true ) ;
// 行仍在
expect ( await db . query ( 'SELECT * FROM users' ) ) . toHaveLength ( 1 ) ;
// 唯一约束仍有效
let uniqueThrew = false ;
try { await db . query ( "INSERT INTO users VALUES ('u2', 'x@x.x')" ) ; } catch { uniqueThrew = true ; }
expect ( uniqueThrew ) . toBe ( true ) ;
// 索引查询仍能命中
const viaIndex = await db . query ( "SELECT * FROM users WHERE email = 'x@x.x'" ) ;
expect ( viaIndex ) . toHaveLength ( 1 ) ;
await db . close ( ) ;
} ) ;
test ( 'memory: 多行匹配删除 RESTRICT 失败 → 全部行索引完好' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-del-restrict-multi' , mode : 'memory' } ) ;
await db . defineTable ( 'users' , {
id : { type : 'string' , primaryKey : true } ,
tag : { type : 'string' , index : true } ,
} ) ;
await db . defineTable ( 'orders' , {
id : { type : 'string' , primaryKey : true } ,
user_id : { type : 'string' , references : 'users.id' , onDelete : 'RESTRICT' } ,
} ) ;
await db . query ( "INSERT INTO users VALUES ('u1', 't1'), ('u2', 't2')" ) ;
await db . query ( "INSERT INTO orders VALUES ('o1', 'u1')" ) ;
let threw = false ;
try { await db . query ( 'DELETE FROM users' ) ; } catch { threw = true ; }
expect ( threw ) . toBe ( true ) ;
expect ( await db . query ( 'SELECT * FROM users' ) ) . toHaveLength ( 2 ) ;
expect ( await db . query ( "SELECT * FROM users WHERE tag = 't1'" ) ) . toHaveLength ( 1 ) ;
expect ( await db . query ( "SELECT * FROM users WHERE tag = 't2'" ) ) . toHaveLength ( 1 ) ;
await db . close ( ) ;
} ) ;
test ( 'disk: RESTRICT 抛错后索引保持(与 memory 同路径)' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-del-restrict-disk' , mode : 'disk' , diskEngine : 'memory' } ) ;
await db . defineTable ( 'users' , {
id : { type : 'string' , primaryKey : true } ,
email : { type : 'string' , unique : true , index : true } ,
} ) ;
await db . defineTable ( 'orders' , {
id : { type : 'string' , primaryKey : true } ,
user_id : { type : 'string' , references : 'users.id' , onDelete : 'RESTRICT' } ,
} ) ;
await db . query ( "INSERT INTO users VALUES ('u1', 'x@x.x')" ) ;
await db . query ( "INSERT INTO orders VALUES ('o1', 'u1')" ) ;
let threw = false ;
try { await db . query ( "DELETE FROM users WHERE id = 'u1'" ) ; } catch { threw = true ; }
expect ( threw ) . toBe ( true ) ;
const viaIndex = await db . query ( "SELECT * FROM users WHERE email = 'x@x.x'" ) ;
expect ( viaIndex ) . toHaveLength ( 1 ) ;
await db . close ( ) ;
} ) ;
} ) ;
describe ( 'v0.7.3: insert 语句级原子性' , ( ) = > {
test . each ( [
[ 'memory' , { mode : 'memory' } as const ] ,
[ 'disk' , { mode : 'disk' , diskEngine : 'memory' } as const ] ,
[ 'hybrid' , { mode : 'hybrid' , diskEngine : 'memory' } as const ] ,
] ) ( '%s: 批内主键重复 → 整句不执行' , async ( _label , cfg ) = > {
const db = await MetonaSqlark . create ( { name : ` v073-ins-atomic-pk- ${ _label } ` , . . . cfg } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } } ) ;
let threw = false ;
try { await db . query ( "INSERT INTO users VALUES ('a'), ('a')" ) ; } catch { threw = true ; }
expect ( threw ) . toBe ( true ) ;
expect ( await db . query ( 'SELECT * FROM users' ) ) . toHaveLength ( 0 ) ;
await db . close ( ) ;
} ) ;
test . each ( [
[ 'memory' , { mode : 'memory' } as const ] ,
[ 'disk' , { mode : 'disk' , diskEngine : 'memory' } as const ] ,
[ 'hybrid' , { mode : 'hybrid' , diskEngine : 'memory' } as const ] ,
] ) ( '%s: 批内唯一冲突 → 整句不执行' , async ( _label , cfg ) = > {
const db = await MetonaSqlark . create ( { name : ` v073-ins-atomic-uq- ${ _label } ` , . . . cfg } ) ;
await db . defineTable ( 'users' , {
id : { type : 'string' , primaryKey : true } ,
email : { type : 'string' , unique : true } ,
} ) ;
let threw = false ;
try {
await db . query ( "INSERT INTO users VALUES ('1', 'a@b.c'), ('2', 'a@b.c')" ) ;
} catch { threw = true ; }
expect ( threw ) . toBe ( true ) ;
expect ( await db . query ( 'SELECT * FROM users' ) ) . toHaveLength ( 0 ) ;
await db . close ( ) ;
} ) ;
test . each ( [
[ 'memory' , { mode : 'memory' } as const ] ,
[ 'disk' , { mode : 'disk' , diskEngine : 'memory' } as const ] ,
] ) ( '%s: 第 N 行撞已有主键 → 整句不执行(含前 N-1 行)' , async ( _label , cfg ) = > {
const db = await MetonaSqlark . create ( { name : ` v073-ins-atomic-exist- ${ _label } ` , . . . cfg } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } } ) ;
await db . query ( "INSERT INTO users VALUES ('a')" ) ;
let threw = false ;
try { await db . query ( "INSERT INTO users VALUES ('b'), ('a')" ) ; } catch { threw = true ; }
expect ( threw ) . toBe ( true ) ;
const rows = await db . query ( 'SELECT * FROM users' ) ;
expect ( rows ) . toHaveLength ( 1 ) ;
expect ( ( rows [ 0 ] as Record < string , unknown > ) . id ) . toBe ( 'a' ) ;
await db . close ( ) ;
} ) ;
test ( 'aria: 批内主键重复 → 整句不执行(此前部分提交 + WAL 不一致)' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-ins-atomic-aria' , mode : 'aria' , diskEngine : 'memory' } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } } ) ;
let threw = false ;
try { await db . query ( "INSERT INTO users VALUES ('a'), ('a')" ) ; } catch { threw = true ; }
expect ( threw ) . toBe ( true ) ;
expect ( await db . query ( 'SELECT * FROM users' ) ) . toHaveLength ( 0 ) ;
await db . close ( ) ;
} ) ;
test ( 'aria: 事务内批内主键重复 → 整句不执行且快照干净' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-ins-atomic-aria-tx' , mode : 'aria' , diskEngine : 'memory' } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } } ) ;
await db . query ( 'BEGIN' ) ;
let threw = false ;
try { await db . query ( "INSERT INTO users VALUES ('a'), ('a')" ) ; } catch { threw = true ; }
expect ( threw ) . toBe ( true ) ;
await db . query ( 'COMMIT' ) ;
expect ( await db . query ( 'SELECT * FROM users' ) ) . toHaveLength ( 0 ) ;
await db . close ( ) ;
} ) ;
test ( 'aria: 批内唯一冲突整批不落库回归护栏(v0.6.2)' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-ins-atomic-aria-uq' , mode : 'aria' , diskEngine : 'memory' } ) ;
await db . defineTable ( 'users' , {
id : { type : 'string' , primaryKey : true } ,
email : { type : 'string' , unique : true } ,
} ) ;
let threw = false ;
try {
await db . query ( "INSERT INTO users VALUES ('1', 'a@b.c'), ('2', 'a@b.c')" ) ;
} catch { threw = true ; }
expect ( threw ) . toBe ( true ) ;
expect ( await db . query ( 'SELECT * FROM users' ) ) . toHaveLength ( 0 ) ;
await db . close ( ) ;
} ) ;
} ) ;
describe ( 'v0.7.3: queryStream 子查询回退物化' , ( ) = > {
test . each ( [
[ 'memory' , { mode : 'memory' } as const ] ,
[ 'aria' , { mode : 'aria' , diskEngine : 'memory' } as const ] ,
] ) ( '%s: IN 子查询流式查询返回正确结果(回退物化)' , async ( _label , cfg ) = > {
const db = await MetonaSqlark . create ( { name : ` v073-stream-sub- ${ _label } ` , . . . cfg } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } } ) ;
await db . defineTable ( 'orders' , { id : { type : 'string' , primaryKey : true } , user_id : { type : 'string' } } ) ;
await db . query ( "INSERT INTO users VALUES ('1'), ('2')" ) ;
await db . query ( "INSERT INTO orders VALUES ('o1', '1')" ) ;
const collected : Record < string , unknown > [ ] = [ ] ;
const n = await db . queryStream ( 'SELECT * FROM users WHERE id IN (SELECT user_id FROM orders)' , ( r ) = > collected . push ( r ) ) ;
expect ( n ) . toBe ( 1 ) ;
expect ( collected ) . toHaveLength ( 1 ) ;
expect ( collected [ 0 ] . id ) . toBe ( '1' ) ;
await db . close ( ) ;
} ) ;
test ( 'memory: EXISTS 关联子查询流式查询回退物化' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-stream-exists' , mode : 'memory' } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } } ) ;
await db . defineTable ( 'orders' , { id : { type : 'string' , primaryKey : true } , user_id : { type : 'string' } } ) ;
await db . query ( "INSERT INTO users VALUES ('1'), ('2')" ) ;
await db . query ( "INSERT INTO orders VALUES ('o1', '1')" ) ;
const collected : Record < string , unknown > [ ] = [ ] ;
await db . queryStream (
'SELECT * FROM users u WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id)' ,
( r ) = > collected . push ( r ) ,
) ;
expect ( collected ) . toHaveLength ( 1 ) ;
await db . close ( ) ;
} ) ;
test ( 'memory: 简单查询仍走引擎流式路径(未误回退)' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-stream-simple' , mode : 'memory' } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } } ) ;
await db . query ( "INSERT INTO users VALUES ('1'), ('2')" ) ;
const collected : Record < string , unknown > [ ] = [ ] ;
const n = await db . queryStream ( "SELECT * FROM users WHERE id = '1'" , ( r ) = > collected . push ( r ) ) ;
expect ( n ) . toBe ( 1 ) ;
expect ( collected ) . toHaveLength ( 1 ) ;
await db . close ( ) ;
} ) ;
test ( 'memory: WHERE 列引用($col)流式查询回退物化且不抛错' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-stream-colref' , mode : 'memory' } ) ;
await db . defineTable ( 't1' , { id : { type : 'string' , primaryKey : true } , x : { type : 'number' } , y : { type : 'number' } } ) ;
await db . query ( "INSERT INTO t1 VALUES ('1', 5, 5), ('2', 10, 3)" ) ;
// t1.x = t1.y 解析为 $col 列引用 —— 引擎层 matchWhere 无 $col 匹配分支,
// 此前流式路径会抛 QUERY_ERROR/静默过滤;v0.7.3 回退物化,结果与 query() 一致
const viaQuery = await db . query ( 'SELECT * FROM t1 WHERE t1.x = t1.y' ) ;
const collected : Record < string , unknown > [ ] = [ ] ;
await db . queryStream ( 'SELECT * FROM t1 WHERE t1.x = t1.y' , ( r ) = > collected . push ( r ) ) ;
expect ( collected ) . toHaveLength ( ( viaQuery as Record < string , unknown > [ ] ) . length ) ;
await db . close ( ) ;
} ) ;
} ) ;
describe ( 'v0.7.3: ALTER DROP 索引列清理' , ( ) = > {
test . each ( [
[ 'memory' , { mode : 'memory' } as const ] ,
[ 'disk' , { mode : 'disk' , diskEngine : 'memory' } as const ] ,
] ) ( '%s: DROP 索引列后无旧索引短路(新增行可见)' , async ( _label , cfg ) = > {
const db = await MetonaSqlark . create ( { name : ` v073-alter-drop-idx- ${ _label } ` , . . . cfg } ) ;
await db . defineTable ( 'users' , {
id : { type : 'string' , primaryKey : true } ,
email : { type : 'string' , index : true } ,
} ) ;
await db . query ( "INSERT INTO users VALUES ('1', 'a@b.c')" ) ;
await db . query ( 'ALTER TABLE users DROP COLUMN email' ) ;
await db . query ( "INSERT INTO users VALUES ('2')" ) ;
expect ( await db . query ( 'SELECT * FROM users' ) ) . toHaveLength ( 2 ) ;
// 已删列不再存在于 schema;查询该列应报错而非走旧索引(executor 层不会到达)
const rows = await db . query ( 'SELECT * FROM users WHERE id = \'2\'' ) ;
expect ( rows ) . toHaveLength ( 1 ) ;
await db . close ( ) ;
} ) ;
test ( 'memory: DROP 非索引列不影响其他列索引' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-alter-drop-plain' , mode : 'memory' } ) ;
await db . defineTable ( 'users' , {
id : { type : 'string' , primaryKey : true } ,
name : { type : 'string' } ,
email : { type : 'string' , index : true } ,
} ) ;
await db . query ( "INSERT INTO users VALUES ('1', 'Alice', 'a@b.c')" ) ;
await db . query ( 'ALTER TABLE users DROP COLUMN name' ) ;
const viaIndex = await db . query ( "SELECT * FROM users WHERE email = 'a@b.c'" ) ;
expect ( viaIndex ) . toHaveLength ( 1 ) ;
await db . close ( ) ;
} ) ;
} ) ;
describe ( 'v0.7.3: CREATE UNIQUE INDEX 存量唯一性' , ( ) = > {
test . each ( [
[ 'memory' , { mode : 'memory' } as const ] ,
[ 'aria' , { mode : 'aria' , diskEngine : 'memory' } as const ] ,
] ) ( '%s: 存量重复数据 → 抛 UNIQUE_VIOLATION 且无半初始化索引' , async ( _label , cfg ) = > {
const db = await MetonaSqlark . create ( { name : ` v073-uqidx-dup- ${ _label } ` , . . . cfg } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } , email : { type : 'string' } } ) ;
await db . query ( "INSERT INTO users VALUES ('1', 'a@b.c'), ('2', 'a@b.c')" ) ;
let threw = false ;
let code = '' ;
try { await db . query ( 'CREATE UNIQUE INDEX idx_e ON users (email)' ) ; } catch ( e ) {
threw = true ;
code = ( e as { code? : string } ) . code ? ? '' ;
}
expect ( threw ) . toBe ( true ) ;
expect ( code ) . toBe ( 'UNIQUE_VIOLATION' ) ;
// 失败后列标志未落:普通 CREATE INDEX 仍可建立
await db . query ( 'CREATE INDEX idx_e ON users (email)' ) ;
const viaIndex = await db . query ( "SELECT * FROM users WHERE email = 'a@b.c'" ) ;
expect ( viaIndex ) . toHaveLength ( 2 ) ;
await db . close ( ) ;
} ) ;
test . each ( [
[ 'memory' , { mode : 'memory' } as const ] ,
[ 'aria' , { mode : 'aria' , diskEngine : 'memory' } as const ] ,
] ) ( '%s: 存量数据唯一 → 建索引成功且唯一约束生效' , async ( _label , cfg ) = > {
const db = await MetonaSqlark . create ( { name : ` v073-uqidx-ok- ${ _label } ` , . . . cfg } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } , email : { type : 'string' } } ) ;
await db . query ( "INSERT INTO users VALUES ('1', 'a@b.c'), ('2', 'b@b.c')" ) ;
await db . query ( 'CREATE UNIQUE INDEX idx_e ON users (email)' ) ;
let threw = false ;
try { await db . query ( "INSERT INTO users VALUES ('3', 'a@b.c')" ) ; } catch { threw = true ; }
expect ( threw ) . toBe ( true ) ;
expect ( await db . query ( 'SELECT * FROM users' ) ) . toHaveLength ( 2 ) ;
await db . close ( ) ;
} ) ;
} ) ;
describe ( 'v0.7.3: SELECT * 混别名列投影' , ( ) = > {
test ( 'memory: SELECT *, name AS nick 保留全部列 + 别名列' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-star-alias' , mode : 'memory' } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } , name : { type : 'string' } , age : { type : 'number' } } ) ;
await db . query ( "INSERT INTO users VALUES ('1', 'Alice', 30)" ) ;
const rows = await db . query ( 'SELECT *, name AS nick FROM users' ) ;
expect ( rows ) . toHaveLength ( 1 ) ;
const row = rows [ 0 ] as Record < string , unknown > ;
expect ( row . id ) . toBe ( '1' ) ;
expect ( row . name ) . toBe ( 'Alice' ) ;
expect ( row . age ) . toBe ( 30 ) ;
expect ( row . nick ) . toBe ( 'Alice' ) ;
await db . close ( ) ;
} ) ;
test ( 'memory: 纯 SELECT * 行为不变(原行引用键集合)' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-star-only' , mode : 'memory' } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } , name : { type : 'string' } } ) ;
await db . query ( "INSERT INTO users VALUES ('1', 'Alice')" ) ;
const rows = await db . query ( 'SELECT * FROM users' ) ;
expect ( Object . keys ( rows [ 0 ] as Record < string , unknown > ) . sort ( ) ) . toEqual ( [ 'id' , 'name' ] ) ;
await db . close ( ) ;
} ) ;
test ( 'memory: SELECT *, 常量列混合' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-star-const' , mode : 'memory' } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } } ) ;
await db . query ( "INSERT INTO users VALUES ('1')" ) ;
const rows = await db . query ( "SELECT *, 'lit' AS c FROM users" ) ;
const row = rows [ 0 ] as Record < string , unknown > ;
expect ( row . id ) . toBe ( '1' ) ;
expect ( row . c ) . toBe ( 'lit' ) ;
await db . close ( ) ;
} ) ;
} ) ;
describe ( 'v0.7.3: INSERT hooks 列映射' , ( ) = > {
test ( 'SQL 省略列名时 beforeInsert 收到 schema 列名映射' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-hooks-insert' , mode : 'memory' } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } , name : { type : 'string' } } ) ;
let seenRows : unknown = null ;
db . on ( 'beforeInsert' , async ( rows : unknown ) = > { seenRows = rows ; } ) ;
await db . query ( "INSERT INTO users VALUES ('1', 'Alice')" ) ;
const rows = seenRows as Record < string , unknown > [ ] ;
expect ( rows ) . toHaveLength ( 1 ) ;
expect ( rows [ 0 ] . id ) . toBe ( '1' ) ;
expect ( rows [ 0 ] . name ) . toBe ( 'Alice' ) ;
await db . close ( ) ;
} ) ;
test ( 'SQL 显式列名时 hooks 行键按显式列名' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-hooks-insert-cols' , mode : 'memory' } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } , name : { type : 'string' } } ) ;
let seenRows : unknown = null ;
db . on ( 'beforeInsert' , async ( rows : unknown ) = > { seenRows = rows ; } ) ;
await db . query ( "INSERT INTO users (name, id) VALUES ('Alice', '1')" ) ;
const rows = seenRows as Record < string , unknown > [ ] ;
expect ( rows [ 0 ] . id ) . toBe ( '1' ) ;
expect ( rows [ 0 ] . name ) . toBe ( 'Alice' ) ;
await db . close ( ) ;
} ) ;
} ) ;
describe ( 'v0.7.3: KVStore insert 持久化 validated 行' , ( ) = > {
test ( 'default 值与列投影落盘(跨实例恢复一致)' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-kv-validated' , mode : 'disk' , diskEngine : 'memory' } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } , age : { type : 'number' , default : 18 } } ) ;
await db . query ( "INSERT INTO users VALUES ('1')" ) ;
await db . close ( ) ;
const db2 = await MetonaSqlark . create ( { name : 'v073-kv-validated' , mode : 'disk' , diskEngine : 'memory' } ) ;
const rows = await db2 . query ( 'SELECT * FROM users' ) ;
expect ( rows ) . toHaveLength ( 1 ) ;
expect ( rows [ 0 ] ) . toEqual ( { id : '1' , age : 18 } ) ;
await db2 . close ( ) ;
} ) ;
test ( 'schema 外列不持久化' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-kv-extra-col' , mode : 'disk' , diskEngine : 'memory' } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } } ) ;
await db . table ( 'users' ) . insert ( { id : '1' , junk : 'x' } as never ) ;
await db . close ( ) ;
const db2 = await MetonaSqlark . create ( { name : 'v073-kv-extra-col' , mode : 'disk' , diskEngine : 'memory' } ) ;
const rows = await db2 . query ( 'SELECT * FROM users' ) ;
expect ( Object . keys ( rows [ 0 ] as Record < string , unknown > ) . sort ( ) ) . toEqual ( [ 'id' ] ) ;
await db2 . close ( ) ;
} ) ;
test ( 'MemoryEngine.getRow 暴露 validated 行' , async ( ) = > {
const engine = new MemoryEngine ( ) ;
await engine . open ( 'v073-getrow' , 1 ) ;
await engine . createTable ( { name : 'users' , columns : { id : { type : 'string' , primaryKey : true } , age : { type : 'number' , default : 18 } } } ) ;
await engine . insert ( 'users' , [ { id : '1' } ] ) ;
const row = engine . getRow ( 'users' , '1' ) ;
expect ( row ) . toEqual ( { id : '1' , age : 18 } ) ;
expect ( engine . getRow ( 'users' , 'nope' ) ) . toBeNull ( ) ;
expect ( engine . getRow ( 'missing' , '1' ) ) . toBeNull ( ) ;
await engine . close ( ) ;
} ) ;
} ) ;
describe ( 'v0.7.3: WAL BEGIN/ROLLBACK 写失败窗口' , ( ) = > {
async function createAria ( name : string ) : Promise < { db : MetonaSqlark ; walStore : { append : ( d : Uint8Array ) = > Promise < void > } } > {
const db = await MetonaSqlark . create ( { name , mode : 'aria' , diskEngine : 'memory' } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } } ) ;
const engine = db . getEngine ( ) as unknown as { wal : { store : { append : ( d : Uint8Array ) = > Promise < void > } } } ;
return { db , walStore : engine.wal.store } ;
}
test ( 'BEGIN 记录写失败 → 事务状态不泄漏(可重试)' , async ( ) = > {
const { db , walStore } = await createAria ( 'v073-wal-begin-fail' ) ;
const orig = walStore . append . bind ( walStore ) ;
walStore . append = async ( ) = > { throw new Error ( 'wal boom' ) ; } ;
await expect ( db . query ( 'BEGIN' ) ) . rejects . toThrow ( ) ;
walStore . append = orig ;
// 事务状态未泄漏:可正常开始并提交新事务
await db . query ( 'BEGIN' ) ;
await db . query ( "INSERT INTO users VALUES ('1')" ) ;
await db . query ( 'COMMIT' ) ;
expect ( await db . query ( 'SELECT * FROM users' ) ) . toHaveLength ( 1 ) ;
await db . close ( ) ;
} ) ;
test ( 'ROLLBACK 记录写失败 → 事务仍活跃(可重试回滚,不复活数据)' , async ( ) = > {
const { db , walStore } = await createAria ( 'v073-wal-rollback-fail' ) ;
await db . query ( 'BEGIN' ) ;
await db . query ( "INSERT INTO users VALUES ('1')" ) ;
const orig = walStore . append . bind ( walStore ) ;
walStore . append = async ( ) = > { throw new Error ( 'wal boom' ) ; } ;
// ROLLBACK WAL 记录先写失败 → 内存未回滚、事务仍活跃
await expect ( db . query ( 'ROLLBACK' ) ) . rejects . toThrow ( ) ;
walStore . append = orig ;
// 重试回滚成功,数据未提交
await db . query ( 'ROLLBACK' ) ;
expect ( await db . query ( 'SELECT * FROM users' ) ) . toHaveLength ( 0 ) ;
await db . close ( ) ;
} ) ;
} ) ;
describe ( 'v0.7.3: aria $in 批级预加载' , ( ) = > {
test ( '多值 IN(含重复值/未命中值)索引查询正确' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-in-batch' , mode : 'aria' , diskEngine : 'memory' } ) ;
await db . defineTable ( 'users' , {
id : { type : 'string' , primaryKey : true } ,
tag : { type : 'string' , index : true } ,
} ) ;
for ( let i = 0 ; i < 50 ; i ++ ) {
await db . query ( 'INSERT INTO users VALUES (?, ?)' , [ String ( i ) , ` t ${ i % 5 } ` ] ) ;
}
// flush 使索引/主数据 SSTable 化(预加载路径真实生效)
const engine = db . getEngine ( ) as unknown as { lsm : { flush ( ) : Promise < void > } ; secondaryIndexes : Map < string , { flush ( ) : Promise < void > } > } ;
await engine . lsm . flush ( ) ;
for ( const idxLsm of engine . secondaryIndexes . values ( ) ) await idxLsm . flush ( ) ;
const rows = await db . query ( "SELECT * FROM users WHERE tag IN ('t1', 't2', 't1', 't9')" ) ;
expect ( rows ) . toHaveLength ( 20 ) ;
await db . close ( ) ;
} ) ;
test ( 'IN 与 $and 组合条件结果正确(索引子集 + 全条件过滤)' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-in-and' , mode : 'aria' , diskEngine : 'memory' } ) ;
await db . defineTable ( 'users' , {
id : { type : 'string' , primaryKey : true } ,
tag : { type : 'string' , index : true } ,
age : { type : 'number' } ,
} ) ;
for ( let i = 0 ; i < 20 ; i ++ ) {
await db . query ( 'INSERT INTO users VALUES (?, ?, ?)' , [ String ( i ) , ` t ${ i % 4 } ` , i ] ) ;
}
const rows = await db . query ( "SELECT * FROM users WHERE tag IN ('t1', 't2') AND age >= 10" ) ;
expect ( rows ) . toHaveLength ( 5 ) ;
await db . close ( ) ;
} ) ;
} ) ;
describe ( 'v0.7.3: $and 等值条件下推' , ( ) = > {
test . each ( [
[ 'memory' , { mode : 'memory' } as const ] ,
[ 'aria' , { mode : 'aria' , diskEngine : 'memory' } as const ] ,
] ) ( '%s: 多条件 AND 查询结果正确' , async ( _label , cfg ) = > {
const db = await MetonaSqlark . create ( { name : ` v073-and-push- ${ _label } ` , . . . cfg } ) ;
await db . defineTable ( 'users' , {
id : { type : 'string' , primaryKey : true } ,
tag : { type : 'string' , index : true } ,
age : { type : 'number' } ,
} ) ;
await db . query ( "INSERT INTO users VALUES ('1', 'a', 10), ('2', 'a', 20), ('3', 'b', 10)" ) ;
const rows = await db . query ( "SELECT * FROM users WHERE tag = 'a' AND age = 10" ) ;
expect ( rows ) . toHaveLength ( 1 ) ;
expect ( ( rows [ 0 ] as Record < string , unknown > ) . id ) . toBe ( '1' ) ;
await db . close ( ) ;
} ) ;
test . each ( [
[ 'memory' , { mode : 'memory' } as const ] ,
[ 'aria' , { mode : 'aria' , diskEngine : 'memory' } as const ] ,
] ) ( '%s: EXPLAIN 识别 $and 嵌套索引条件' , async ( _label , cfg ) = > {
const db = await MetonaSqlark . create ( { name : ` v073-and-explain- ${ _label } ` , . . . cfg } ) ;
await db . defineTable ( 'users' , {
id : { type : 'string' , primaryKey : true } ,
tag : { type : 'string' , index : true } ,
age : { type : 'number' } ,
} ) ;
await db . query ( "INSERT INTO users VALUES ('1', 'a', 10)" ) ;
// 索引列在 $and 嵌套中 → 递归识别 index:tag
const plan1 = await db . query ( "EXPLAIN SELECT * FROM users WHERE age > 5 AND tag = 'a'" ) ;
expect ( ( plan1 as Record < string , unknown > ) . usingIndex ) . toBe ( 'index:tag' ) ;
// 主键在 $and 嵌套中 → pk
const plan2 = await db . query ( "EXPLAIN SELECT * FROM users WHERE age > 5 AND id = '1'" ) ;
expect ( ( plan2 as Record < string , unknown > ) . usingIndex ) . toBe ( 'pk' ) ;
await db . close ( ) ;
} ) ;
} ) ;
describe ( 'v0.7.3: SELECT 常量列 SQL 标准转义' , ( ) = > {
test ( "SELECT 'O''Brien' 还原为 O'Brien" , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-escape' , mode : 'memory' } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } } ) ;
await db . query ( "INSERT INTO users VALUES ('1')" ) ;
const rows = await db . query ( "SELECT 'O''Brien' AS name FROM users" ) ;
expect ( ( rows [ 0 ] as Record < string , unknown > ) . name ) . toBe ( "O'Brien" ) ;
await db . close ( ) ;
} ) ;
test ( "无表查询 SELECT 'a''b' AS x 转义还原" , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-escape-notable' , mode : 'memory' } ) ;
const rows = await db . query ( "SELECT 'a''b' AS x" ) ;
expect ( ( rows [ 0 ] as Record < string , unknown > ) . x ) . toBe ( "a'b" ) ;
await db . close ( ) ;
} ) ;
test ( "SELECT *, 'x''y' AS c 混合投影转义" , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-escape-star' , mode : 'memory' } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } } ) ;
await db . query ( "INSERT INTO users VALUES ('1')" ) ;
const rows = await db . query ( "SELECT *, 'x''y' AS c FROM users" ) ;
const row = rows [ 0 ] as Record < string , unknown > ;
expect ( row . id ) . toBe ( '1' ) ;
expect ( row . c ) . toBe ( "x'y" ) ;
await db . close ( ) ;
} ) ;
} ) ;
describe ( 'v0.7.3: ANALYZE 统计二级索引' , ( ) = > {
test ( 'aria: 统计含二级索引 LSM( sstableCount/memtableSize 汇总)' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-analyze' , mode : 'aria' , diskEngine : 'memory' } ) ;
await db . defineTable ( 'users' , {
id : { type : 'string' , primaryKey : true } ,
email : { type : 'string' , index : true } ,
tag : { type : 'string' , index : true } ,
} ) ;
for ( let i = 0 ; i < 30 ; i ++ ) {
await db . query ( 'INSERT INTO users VALUES (?, ?, ?)' , [ String ( i ) , ` e ${ i } @x.x ` , ` t ${ i % 3 } ` ] ) ;
}
const engine = db . getEngine ( ) as unknown as {
lsm : { flush ( ) : Promise < void > } ;
secondaryIndexes : Map < string , { flush ( ) : Promise < void > } > ;
} ;
await engine . lsm . flush ( ) ;
for ( const idxLsm of engine . secondaryIndexes . values ( ) ) await idxLsm . flush ( ) ;
const stats = await db . query ( 'ANALYZE users' ) ;
expect ( ( stats as Record < string , unknown > ) . rowCount ) . toBe ( 30 ) ;
expect ( typeof ( stats as Record < string , unknown > ) . indexDepth ) . toBe ( 'number' ) ;
expect ( ( stats as Record < string , unknown > ) . sstableCount ) . toBeGreaterThanOrEqual ( 1 ) ;
expect ( typeof ( stats as Record < string , unknown > ) . memtableSize ) . toBe ( 'number' ) ;
await db . close ( ) ;
} ) ;
test ( 'memory: ANALYZE 不支持抛 NOT_SUPPORTED(护栏)' , async ( ) = > {
const db = await MetonaSqlark . create ( { name : 'v073-analyze-memory' , mode : 'memory' } ) ;
await db . defineTable ( 'users' , { id : { type : 'string' , primaryKey : true } } ) ;
await expect ( db . query ( 'ANALYZE users' ) ) . rejects . toMatchObject ( { code : 'NOT_SUPPORTED' } ) ;
await db . close ( ) ;
} ) ;
} ) ;