release: v0.3.2 — 质量加固 + SQL扩展 + 表达式 + 并发同步
CI / test (18.x) (push) Failing after 5m11s
CI / test (20.x) (push) Failing after 5m8s
CI / test (22.x) (push) Successful in 9m58s
CI / test (24.x) (push) Successful in 9m56s

v0.2.6 质量加固:
- 修复 AriaEngine 二级索引 SSTable 互相覆盖(命名空间隔离)
- 修复 LSM 多版本读取顺序错误 + MergeIterator 取最新来源
- 重写 LZ4 压缩器(往返一致性 + 缓冲区溢出)
- sstableCache LRU 上限 + 预加载兜底(BufferPool 配置生效)
- 修复 React/Vue 集成 import type 运行时 bug + exports 子路径
- 新增 38 个测试(LZ4往返/Crypto/集成), 删除伪测试

v0.3.0 SQL 功能扩展:
- 多语句 parseAll + 事务语句 BEGIN/COMMIT/ROLLBACK
- INSERT INTO ... SELECT + UNION/UNION ALL + EXISTS 关联子查询
- CREATE/DROP INDEX 五引擎实现 + 别名 WHERE 修复
- benchmark 页面 + 36 个新测试

v0.3.1 表达式与性能:
- CASE WHEN 表达式(SELECT 列/WHERE/聚合)
- JOIN + 关联子查询逐行绑定
- WAL 批量组提交(写放大 O(N)→O(1))
- 修复 pending frozen 可见性 + flush 缓存竞争

v0.3.2 并发:
- CASE WHEN 用于 WHERE/聚合 + JOIN 哈希连接
- 多标签页同步(multiTabSync + BroadcastChannel)
- IndexedDB schema 持久化(reopen 后表结构恢复)
- 修复 where-matcher 顶层 $not
- 修复 CJS 产物 .js 被 ESM 解析(exports 空) — .cjs 后缀 + exports 修正
- 836 测试 / 44 套件 / 81.0% 覆盖率
This commit is contained in:
thzxx
2026-08-08 10:41:30 +08:00
parent 3ae7d6e8fb
commit d544501e1c
77 changed files with 29007 additions and 20395 deletions
+361 -374
View File
@@ -1,374 +1,361 @@
/**
* v0.2.5 修复验证测试
* 验证所有 P0/P1/P2 修复点
*/
import { VERSION } from '../src/constants';
import { MetonaSqlark } from '../src/core';
import { AriaEngine } from '../src/engine/aria/index';
import { MemoryEngine } from '../src/engine/memory';
import { parse } from '../src/sql/parser';
import { QueryExecutor } from '../src/query/executor';
import { WAL } from '../src/engine/aria/wal/log';
import { SSTableReader } from '../src/engine/aria/index/sstable';
import { BloomFilter } from '../src/engine/aria/index/bloom';
import { CryptoManager } from '../src/engine/aria/crypto';
import { PluginManager } from '../src/plugin/index';
import type { SSTableMeta } from '../src/engine/aria/types';
import type { MetonaPlugin } from '../src/constants';
import { createSchema } from '../src/table/schema';
// ---------------------------------------------------------------------------
// P0-1: 版本号统一
// ---------------------------------------------------------------------------
describe('[v0.2.5] P0-1: 版本号统一', () => {
test('VERSION 常量为 0.2.5', () => {
expect(VERSION).toBe('0.2.5');
});
});
// ---------------------------------------------------------------------------
// P0-2: AriaEngine OPFS 后端映射修复
// ---------------------------------------------------------------------------
describe('[v0.2.5] P0-2: AriaEngine OPFS 后端映射', () => {
test('mode=aria + diskEngine=opfs 时应使用 opfs 后端', () => {
const db = new MetonaSqlark({ name: 'test-opfs-map', mode: 'aria', diskEngine: 'opfs' });
// 不实际 open(需要浏览器环境),只验证 createEngine 逻辑
// 通过 getEngine 在 init 后检查
expect(db).toBeDefined();
});
test('mode=aria + diskEngine=indexeddb 时应使用 indexeddb 后端', () => {
const db = new MetonaSqlark({ name: 'test-idb-map', mode: 'aria', diskEngine: 'indexeddb' });
expect(db).toBeDefined();
});
});
// ---------------------------------------------------------------------------
// P0-3: _onError 接入执行路径
// ---------------------------------------------------------------------------
describe('[v0.2.5] P0-3: _onError 接入执行路径', () => {
test('query 失败时调用 onError 回调', async () => {
const errors: Error[] = [];
const db = new MetonaSqlark({
name: 'test-onerror',
mode: 'memory',
onError: (e) => errors.push(e),
});
await db.init();
await db.defineTable('users', {
id: { type: 'string', primaryKey: true },
name: { type: 'string' },
});
// 故意执行不存在的表查询
await expect(db.query('SELECT * FROM nonexistent')).rejects.toThrow();
expect(errors.length).toBeGreaterThan(0);
await db.close();
});
test('defineTable 失败时调用 onError', async () => {
const errors: Error[] = [];
const db = new MetonaSqlark({
name: 'test-onerror2',
mode: 'memory',
onError: (e) => errors.push(e),
});
await db.init();
await db.defineTable('dup', {
id: { type: 'string', primaryKey: true },
});
// 重复创建
await expect(db.defineTable('dup', {
id: { type: 'string', primaryKey: true },
})).rejects.toThrow();
expect(errors.length).toBeGreaterThan(0);
await db.close();
});
});
// ---------------------------------------------------------------------------
// P0-4: maxRowsPerQuery 生效
// ---------------------------------------------------------------------------
describe('[v0.2.5] P0-4: maxRowsPerQuery 生效', () => {
test('结果集被截断为 maxRowsPerQuery', async () => {
const engine = new MemoryEngine();
await engine.open('test-maxrows', 1);
await engine.createTable(createSchema('items', {
id: { type: 'string', primaryKey: true },
val: { type: 'number' },
}));
// 插入 100 行
for (let i = 0; i < 100; i++) {
await engine.insert('items', [{ id: `item${i}`, val: i }]);
}
const executor = new QueryExecutor(engine, 10); // maxRowsPerQuery=10
const stmt = parse('SELECT * FROM items');
const result = await executor.execute(stmt) as Record<string, unknown>[];
expect(result.length).toBe(10); // 截断为 10 行
await engine.close();
});
test('maxRowsPerQuery=0 表示不限制', async () => {
const engine = new MemoryEngine();
await engine.open('test-nolimit', 1);
await engine.createTable(createSchema('items', {
id: { type: 'string', primaryKey: true },
}));
for (let i = 0; i < 50; i++) {
await engine.insert('items', [{ id: `i${i}` }]);
}
const executor = new QueryExecutor(engine, 0);
const stmt = parse('SELECT * FROM items');
const result = await executor.execute(stmt) as Record<string, unknown>[];
expect(result.length).toBe(50);
await engine.close();
});
});
// ---------------------------------------------------------------------------
// P0-5: WAL full 模式真正同步
// ---------------------------------------------------------------------------
describe('[v0.2.5] P0-5: WAL full 模式同步', () => {
test('append 在 full 模式下是 async 且可 await', async () => {
let appendCount = 0;
const wal = new WAL({
append: async (_data: Uint8Array) => { appendCount++; },
readAll: async () => new Uint8Array(0),
truncate: async () => {},
exists: async () => false,
}, true, 'full');
// append 现在返回 Promise
await wal.append({
type: 1, // INSERT
txnId: 0,
tableName: 'test',
key: 'k1',
data: { v: 1 },
} as any);
expect(appendCount).toBe(1);
});
});
// ---------------------------------------------------------------------------
// P0-6: PluginManager.install 传 db 实例
// ---------------------------------------------------------------------------
describe('[v0.2.5] P0-6: PluginManager.install 传 db 实例', () => {
test('register 传 db 给 install', () => {
let receivedDb: unknown = null;
const plugin: MetonaPlugin = {
name: 'test-plugin',
install: (db) => { receivedDb = db; },
destroy: () => {},
};
const pm = new PluginManager();
const fakeDb = { name: 'fake' };
pm.register(plugin, fakeDb);
expect(receivedDb).toBe(fakeDb);
});
});
// ---------------------------------------------------------------------------
// P1-7: SSTableReader 二分查找统一
// ---------------------------------------------------------------------------
describe('[v0.2.5] P1-7: SSTableReader 二分查找', () => {
test('rangeScan 使用二分查找正确定位', () => {
// 构建一个 SSTable 手工
const { SSTableBuilder } = require('../src/engine/aria/index/sstable_builder');
const builder = new SSTableBuilder(4096);
// 添加足够多的条目以形成多个 block
for (let i = 0; i < 100; i++) {
const key = `key${String(i).padStart(5, '0')}`;
builder.add(key, { data: `value${i}` });
}
const { sstableData } = builder.build();
const meta: SSTableMeta = {
id: 1,
level: 0,
minKey: 'key00000',
maxKey: 'key00099',
blockCount: 1,
totalSize: sstableData.byteLength,
bloomData: null,
};
const reader = new SSTableReader(sstableData, meta);
// 精确查找
const result = reader.get('key00050');
expect(result).not.toBeNull();
expect((result as any).data).toBe('value50');
// 范围扫描
const collected: string[] = [];
reader.rangeScan('key00010', 'key00020', (k) => collected.push(k));
expect(collected.length).toBeGreaterThan(0);
expect(collected[0]).toBe('key00010');
});
});
// ---------------------------------------------------------------------------
// P1-8: SQL 注入防护 — 表名校验
// ---------------------------------------------------------------------------
describe('[v0.2.5] P1-8: SQL 注入防护', () => {
test('表名校验正则表达式正确', () => {
// 验证正则逻辑本身
const validName = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
expect(validName.test('users')).toBe(true);
expect(validName.test('user_table')).toBe(true);
expect(validName.test('_private')).toBe(true);
expect(validName.test('Table1')).toBe(true);
// 非法表名
expect(validName.test('users; DROP TABLE')).toBe(false);
expect(validName.test('1table')).toBe(false);
expect(validName.test('user.name')).toBe(false);
expect(validName.test('user name')).toBe(false);
expect(validName.test("'; DROP TABLE users; --")).toBe(false);
});
});
// ---------------------------------------------------------------------------
// P1-9: crypto 实例化
// ---------------------------------------------------------------------------
describe('[v0.2.5] P1-9: CryptoManager 实例化', () => {
test('CryptoManager 可以独立实例化', () => {
const cm1 = new CryptoManager();
const cm2 = new CryptoManager();
expect(cm1.enabled).toBe(false);
expect(cm2.enabled).toBe(false);
// 两个实例互不影响
expect(cm1).not.toBe(cm2);
});
});
// ---------------------------------------------------------------------------
// P2-14: ALTER TABLE 语法
// ---------------------------------------------------------------------------
describe('[v0.2.5] P2-14: ALTER TABLE', () => {
test('解析 ALTER TABLE ADD COLUMN', () => {
const stmt = parse('ALTER TABLE users ADD COLUMN email VARCHAR(255) UNIQUE');
expect(stmt.type).toBe('ALTER_TABLE');
expect((stmt as any).name).toBe('users');
expect((stmt as any).action).toBe('ADD');
expect((stmt as any).column.name).toBe('email');
});
test('解析 ALTER TABLE DROP COLUMN', () => {
const stmt = parse('ALTER TABLE users DROP COLUMN email');
expect(stmt.type).toBe('ALTER_TABLE');
expect((stmt as any).action).toBe('DROP');
expect((stmt as any).column.name).toBe('email');
});
test('执行 ALTER TABLE ADD COLUMN', async () => {
const engine = new MemoryEngine();
await engine.open('test-alter', 1);
await engine.createTable(createSchema('users', {
id: { type: 'string', primaryKey: true },
name: { type: 'string' },
}));
const executor = new QueryExecutor(engine);
const stmt = parse('ALTER TABLE users ADD COLUMN email VARCHAR(255)');
await executor.execute(stmt);
const schema = await engine.getTableSchema('users');
expect(schema!.columns.email).toBeDefined();
await engine.close();
});
test('执行 ALTER TABLE DROP COLUMN', async () => {
const engine = new MemoryEngine();
await engine.open('test-alter-drop', 1);
await engine.createTable(createSchema('users', {
id: { type: 'string', primaryKey: true },
name: { type: 'string' },
email: { type: 'string' },
}));
const executor = new QueryExecutor(engine);
const stmt = parse('ALTER TABLE users DROP COLUMN email');
await executor.execute(stmt);
const schema = await engine.getTableSchema('users');
expect(schema!.columns.email).toBeUndefined();
await engine.close();
});
});
// ---------------------------------------------------------------------------
// P2-15: TRUNCATE TABLE 语法
// ---------------------------------------------------------------------------
describe('[v0.2.5] P2-15: TRUNCATE TABLE', () => {
test('解析 TRUNCATE TABLE', () => {
const stmt = parse('TRUNCATE TABLE users');
expect(stmt.type).toBe('TRUNCATE_TABLE');
expect((stmt as any).name).toBe('users');
});
test('执行 TRUNCATE TABLE 清空数据', async () => {
const engine = new MemoryEngine();
await engine.open('test-truncate', 1);
await engine.createTable(createSchema('items', {
id: { type: 'string', primaryKey: true },
}));
await engine.insert('items', [
{ id: 'a' }, { id: 'b' }, { id: 'c' },
]);
const executor = new QueryExecutor(engine);
const stmt = parse('TRUNCATE TABLE items');
await executor.execute(stmt);
const rows = await engine.find('items', { table: 'items' });
expect(rows.length).toBe(0);
await engine.close();
});
});
// ---------------------------------------------------------------------------
// P2-12: WAL 大小阈值接入 checkpoint
// ---------------------------------------------------------------------------
describe('[v0.2.5] P2-12: WAL 大小阈值', () => {
test('CheckpointManager 接收 walSizeThreshold 参数', () => {
const { CheckpointManager } = require('../src/engine/aria/wal/checkpoint');
const fakeLsm = { flush: async () => {} };
const fakeWal = { flush: async () => {}, checkpoint: async () => {}, getBufferedCount: () => 0 };
const cm = new CheckpointManager(fakeLsm, fakeWal, null, 1000, 1024);
expect(cm).toBeDefined();
expect(cm.getOpCount()).toBe(0);
});
});
// ---------------------------------------------------------------------------
// P2-13: compactLevelSync 接口公开化
// ---------------------------------------------------------------------------
describe('[v0.2.5] P2-13: compactLevel public', () => {
test('LSM.compactLevel 是 public 方法', () => {
const { LSM } = require('../src/engine/aria/index/lsm');
const lsm = new LSM({
sstableStore: {
save: async () => {}, load: async () => null, delete: async () => {},
allocateId: async () => 1, listMeta: async () => [], saveMeta: async () => {}, deleteMeta: async () => {},
},
});
expect(typeof lsm.compactLevel).toBe('function');
});
});
/**
* v0.2.5 修复验证测试
* 验证所有 P0/P1/P2 修复点
*/
import { VERSION } from '../src/constants';
import { MetonaSqlark } from '../src/core';
import { AriaEngine } from '../src/engine/aria/index';
import { MemoryEngine } from '../src/engine/memory';
import { parse } from '../src/sql/parser';
import { QueryExecutor } from '../src/query/executor';
import { WAL } from '../src/engine/aria/wal/log';
import { SSTableReader } from '../src/engine/aria/index/sstable';
import { BloomFilter } from '../src/engine/aria/index/bloom';
import { CryptoManager } from '../src/engine/aria/crypto';
import { PluginManager } from '../src/plugin/index';
import type { SSTableMeta } from '../src/engine/aria/types';
import type { MetonaPlugin } from '../src/constants';
import { createSchema } from '../src/table/schema';
// ---------------------------------------------------------------------------
// P0-1: 版本号统一
// ---------------------------------------------------------------------------
describe('[v0.2.5] P0-1: 版本号统一', () => {
test('VERSION 常量为当前版本(0.3.2', () => {
expect(VERSION).toBe('0.3.2');
});
});
// ---------------------------------------------------------------------------
// P0-2: AriaEngine OPFS 后端映射修复
// ---------------------------------------------------------------------------
describe('[v0.2.5] P0-2: AriaEngine OPFS 后端映射', () => {
test('mode=aria + diskEngine=opfs 时应使用 opfs 后端', () => {
const db = new MetonaSqlark({ name: 'test-opfs-map', mode: 'aria', diskEngine: 'opfs' });
// 不实际 open(需要浏览器环境),只验证 createEngine 逻辑
// 通过 getEngine 在 init 后检查
expect(db).toBeDefined();
});
test('mode=aria + diskEngine=indexeddb 时应使用 indexeddb 后端', () => {
const db = new MetonaSqlark({ name: 'test-idb-map', mode: 'aria', diskEngine: 'indexeddb' });
expect(db).toBeDefined();
});
});
// ---------------------------------------------------------------------------
// P0-3: _onError 接入执行路径
// ---------------------------------------------------------------------------
describe('[v0.2.5] P0-3: _onError 接入执行路径', () => {
test('query 失败时调用 onError 回调', async () => {
const errors: Error[] = [];
const db = new MetonaSqlark({
name: 'test-onerror',
mode: 'memory',
onError: (e) => errors.push(e),
});
await db.init();
await db.defineTable('users', {
id: { type: 'string', primaryKey: true },
name: { type: 'string' },
});
// 故意执行不存在的表查询
await expect(db.query('SELECT * FROM nonexistent')).rejects.toThrow();
expect(errors.length).toBeGreaterThan(0);
await db.close();
});
test('defineTable 失败时调用 onError', async () => {
const errors: Error[] = [];
const db = new MetonaSqlark({
name: 'test-onerror2',
mode: 'memory',
onError: (e) => errors.push(e),
});
await db.init();
await db.defineTable('dup', {
id: { type: 'string', primaryKey: true },
});
// 重复创建
await expect(db.defineTable('dup', {
id: { type: 'string', primaryKey: true },
})).rejects.toThrow();
expect(errors.length).toBeGreaterThan(0);
await db.close();
});
});
// ---------------------------------------------------------------------------
// P0-4: maxRowsPerQuery 生效
// ---------------------------------------------------------------------------
describe('[v0.2.5] P0-4: maxRowsPerQuery 生效', () => {
test('结果集被截断为 maxRowsPerQuery', async () => {
const engine = new MemoryEngine();
await engine.open('test-maxrows', 1);
await engine.createTable(createSchema('items', {
id: { type: 'string', primaryKey: true },
val: { type: 'number' },
}));
// 插入 100 行
for (let i = 0; i < 100; i++) {
await engine.insert('items', [{ id: `item${i}`, val: i }]);
}
const executor = new QueryExecutor(engine, 10); // maxRowsPerQuery=10
const stmt = parse('SELECT * FROM items');
const result = await executor.execute(stmt) as Record<string, unknown>[];
expect(result.length).toBe(10); // 截断为 10 行
await engine.close();
});
test('maxRowsPerQuery=0 表示不限制', async () => {
const engine = new MemoryEngine();
await engine.open('test-nolimit', 1);
await engine.createTable(createSchema('items', {
id: { type: 'string', primaryKey: true },
}));
for (let i = 0; i < 50; i++) {
await engine.insert('items', [{ id: `i${i}` }]);
}
const executor = new QueryExecutor(engine, 0);
const stmt = parse('SELECT * FROM items');
const result = await executor.execute(stmt) as Record<string, unknown>[];
expect(result.length).toBe(50);
await engine.close();
});
});
// ---------------------------------------------------------------------------
// P0-5: WAL full 模式真正同步
// ---------------------------------------------------------------------------
describe('[v0.2.5] P0-5: WAL full 模式同步', () => {
test('append 在 full 模式下是 async 且可 await', async () => {
let appendCount = 0;
const wal = new WAL({
append: async (_data: Uint8Array) => { appendCount++; },
readAll: async () => new Uint8Array(0),
truncate: async () => {},
exists: async () => false,
}, true, 'full');
// append 现在返回 Promise
await wal.append({
type: 1, // INSERT
txnId: 0,
tableName: 'test',
key: 'k1',
data: { v: 1 },
} as any);
expect(appendCount).toBe(1);
});
});
// ---------------------------------------------------------------------------
// P0-6: PluginManager.install 传 db 实例
// ---------------------------------------------------------------------------
describe('[v0.2.5] P0-6: PluginManager.install 传 db 实例', () => {
test('register 传 db 给 install', () => {
let receivedDb: unknown = null;
const plugin: MetonaPlugin = {
name: 'test-plugin',
install: (db) => { receivedDb = db; },
destroy: () => {},
};
const pm = new PluginManager();
const fakeDb = { name: 'fake' };
pm.register(plugin, fakeDb);
expect(receivedDb).toBe(fakeDb);
});
});
// ---------------------------------------------------------------------------
// P1-7: SSTableReader 二分查找统一
// ---------------------------------------------------------------------------
describe('[v0.2.5] P1-7: SSTableReader 二分查找', () => {
test('rangeScan 使用二分查找正确定位', () => {
// 构建一个 SSTable 手工
const { SSTableBuilder } = require('../src/engine/aria/index/sstable_builder');
const builder = new SSTableBuilder(4096);
// 添加足够多的条目以形成多个 block
for (let i = 0; i < 100; i++) {
const key = `key${String(i).padStart(5, '0')}`;
builder.add(key, { data: `value${i}` });
}
const { sstableData } = builder.build();
const meta: SSTableMeta = {
id: 1,
level: 0,
minKey: 'key00000',
maxKey: 'key00099',
blockCount: 1,
totalSize: sstableData.byteLength,
bloomData: null,
};
const reader = new SSTableReader(sstableData, meta);
// 精确查找
const result = reader.get('key00050');
expect(result).not.toBeNull();
expect((result as any).data).toBe('value50');
// 范围扫描
const collected: string[] = [];
reader.rangeScan('key00010', 'key00020', (k) => collected.push(k));
expect(collected.length).toBeGreaterThan(0);
expect(collected[0]).toBe('key00010');
});
});
// ---------------------------------------------------------------------------
// P1-8: SQL 注入防护 — 表名校验
// 真实覆盖位置:tests/integrations/react.test.tsuseTable
// tests/integrations/vue.test.tsuseSqlarkTable
// 通过真实 hooks 调用触发 validateTableName,验证非法表名抛错。
// 注:v0.2.6 移除此处"复制正则自测"的伪测试(未触达真实代码)。
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// P1-9: crypto 实例化
// ---------------------------------------------------------------------------
describe('[v0.2.5] P1-9: CryptoManager 实例化', () => {
test('CryptoManager 可以独立实例化', () => {
const cm1 = new CryptoManager();
const cm2 = new CryptoManager();
expect(cm1.enabled).toBe(false);
expect(cm2.enabled).toBe(false);
// 两个实例互不影响
expect(cm1).not.toBe(cm2);
});
});
// ---------------------------------------------------------------------------
// P2-14: ALTER TABLE 语法
// ---------------------------------------------------------------------------
describe('[v0.2.5] P2-14: ALTER TABLE', () => {
test('解析 ALTER TABLE ADD COLUMN', () => {
const stmt = parse('ALTER TABLE users ADD COLUMN email VARCHAR(255) UNIQUE');
expect(stmt.type).toBe('ALTER_TABLE');
expect((stmt as any).name).toBe('users');
expect((stmt as any).action).toBe('ADD');
expect((stmt as any).column.name).toBe('email');
});
test('解析 ALTER TABLE DROP COLUMN', () => {
const stmt = parse('ALTER TABLE users DROP COLUMN email');
expect(stmt.type).toBe('ALTER_TABLE');
expect((stmt as any).action).toBe('DROP');
expect((stmt as any).column.name).toBe('email');
});
test('执行 ALTER TABLE ADD COLUMN', async () => {
const engine = new MemoryEngine();
await engine.open('test-alter', 1);
await engine.createTable(createSchema('users', {
id: { type: 'string', primaryKey: true },
name: { type: 'string' },
}));
const executor = new QueryExecutor(engine);
const stmt = parse('ALTER TABLE users ADD COLUMN email VARCHAR(255)');
await executor.execute(stmt);
const schema = await engine.getTableSchema('users');
expect(schema!.columns.email).toBeDefined();
await engine.close();
});
test('执行 ALTER TABLE DROP COLUMN', async () => {
const engine = new MemoryEngine();
await engine.open('test-alter-drop', 1);
await engine.createTable(createSchema('users', {
id: { type: 'string', primaryKey: true },
name: { type: 'string' },
email: { type: 'string' },
}));
const executor = new QueryExecutor(engine);
const stmt = parse('ALTER TABLE users DROP COLUMN email');
await executor.execute(stmt);
const schema = await engine.getTableSchema('users');
expect(schema!.columns.email).toBeUndefined();
await engine.close();
});
});
// ---------------------------------------------------------------------------
// P2-15: TRUNCATE TABLE 语法
// ---------------------------------------------------------------------------
describe('[v0.2.5] P2-15: TRUNCATE TABLE', () => {
test('解析 TRUNCATE TABLE', () => {
const stmt = parse('TRUNCATE TABLE users');
expect(stmt.type).toBe('TRUNCATE_TABLE');
expect((stmt as any).name).toBe('users');
});
test('执行 TRUNCATE TABLE 清空数据', async () => {
const engine = new MemoryEngine();
await engine.open('test-truncate', 1);
await engine.createTable(createSchema('items', {
id: { type: 'string', primaryKey: true },
}));
await engine.insert('items', [
{ id: 'a' }, { id: 'b' }, { id: 'c' },
]);
const executor = new QueryExecutor(engine);
const stmt = parse('TRUNCATE TABLE items');
await executor.execute(stmt);
const rows = await engine.find('items', { table: 'items' });
expect(rows.length).toBe(0);
await engine.close();
});
});
// ---------------------------------------------------------------------------
// P2-12: WAL 大小阈值接入 checkpoint
// ---------------------------------------------------------------------------
describe('[v0.2.5] P2-12: WAL 大小阈值', () => {
test('CheckpointManager 接收 walSizeThreshold 参数', () => {
const { CheckpointManager } = require('../src/engine/aria/wal/checkpoint');
const fakeLsm = { flush: async () => {} };
const fakeWal = { flush: async () => {}, checkpoint: async () => {}, getBufferedCount: () => 0 };
const cm = new CheckpointManager(fakeLsm, fakeWal, null, 1000, 1024);
expect(cm).toBeDefined();
expect(cm.getOpCount()).toBe(0);
});
});
// ---------------------------------------------------------------------------
// P2-13: compactLevelSync 接口公开化
// ---------------------------------------------------------------------------
describe('[v0.2.5] P2-13: compactLevel public', () => {
test('LSM.compactLevel 是 public 方法', () => {
const { LSM } = require('../src/engine/aria/index/lsm');
const lsm = new LSM({
sstableStore: {
save: async () => {}, load: async () => null, delete: async () => {},
allocateId: async () => 1, listMeta: async () => [], saveMeta: async () => {}, deleteMeta: async () => {},
},
});
expect(typeof lsm.compactLevel).toBe('function');
});
});