release: v0.3.2 — 质量加固 + SQL扩展 + 表达式 + 并发同步
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:
+129
-129
@@ -1,129 +1,129 @@
|
||||
/**
|
||||
* AriaEngine SSTable Builder + Reader 单元测试
|
||||
*/
|
||||
import { SSTableBuilder } from '../../src/engine/aria/index/sstable_builder';
|
||||
import { SSTableReader } from '../../src/engine/aria/index/sstable';
|
||||
import type { SSTableMeta } from '../../src/engine/aria/types';
|
||||
|
||||
// ===================================================================
|
||||
// SSTable Builder + Reader
|
||||
// ===================================================================
|
||||
describe('AriaEngine — SSTable Builder + Reader', () => {
|
||||
const makeMeta = (data: Uint8Array): SSTableMeta => ({
|
||||
id: 1, level: 0, minKey: '', maxKey: '\uffff',
|
||||
blockCount: 1, totalSize: data.byteLength, bloomData: null,
|
||||
});
|
||||
|
||||
it('构建单条目 SSTable 并精确读取', () => {
|
||||
const builder = new SSTableBuilder(4096);
|
||||
builder.add('key1', { name: 'Alice', age: 30 });
|
||||
const { sstableData } = builder.build();
|
||||
|
||||
const reader = new SSTableReader(sstableData, makeMeta(sstableData));
|
||||
const result = reader.get('key1');
|
||||
expect(result).not.toBeNull();
|
||||
expect(result!.name).toBe('Alice');
|
||||
expect(result!.age).toBe(30);
|
||||
});
|
||||
|
||||
it('构建多条 SSTable 并全部读取', () => {
|
||||
const builder = new SSTableBuilder(4096);
|
||||
const items: [string, Record<string, unknown>][] = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const key = `user-${String(i).padStart(3, '0')}`;
|
||||
const value = { idx: i, name: `User${i}` };
|
||||
items.push([key, value]);
|
||||
builder.add(key, value);
|
||||
}
|
||||
const { sstableData } = builder.build();
|
||||
|
||||
const reader = new SSTableReader(sstableData, makeMeta(sstableData));
|
||||
for (const [key, value] of items) {
|
||||
const result = reader.get(key);
|
||||
expect(result).not.toBeNull();
|
||||
expect(result!.idx).toBe(value.idx);
|
||||
}
|
||||
});
|
||||
|
||||
it('get — 不存在的 key 返回 null', () => {
|
||||
const builder = new SSTableBuilder(4096);
|
||||
builder.add('a', { v: 1 });
|
||||
const { sstableData } = builder.build();
|
||||
|
||||
const reader = new SSTableReader(sstableData, makeMeta(sstableData));
|
||||
expect(reader.get('nonexistent')).toBeNull();
|
||||
});
|
||||
|
||||
it('rangeScan — 范围查询', () => {
|
||||
const builder = new SSTableBuilder(4096);
|
||||
for (let i = 0; i < 20; i++) {
|
||||
builder.add(`k-${String(i).padStart(2, '0')}`, { v: i });
|
||||
}
|
||||
const { sstableData } = builder.build();
|
||||
|
||||
const reader = new SSTableReader(sstableData, makeMeta(sstableData));
|
||||
const results: [string, Record<string, unknown>][] = [];
|
||||
reader.rangeScan('k-05', 'k-10', (k, v) => results.push([k, v]));
|
||||
expect(results).toHaveLength(6);
|
||||
expect(results[0][0]).toBe('k-05');
|
||||
expect(results[results.length - 1][0]).toBe('k-10');
|
||||
});
|
||||
|
||||
it('scanAll — 遍历所有条目', () => {
|
||||
const builder = new SSTableBuilder(4096);
|
||||
const count = 50;
|
||||
for (let i = 0; i < count; i++) {
|
||||
builder.add(`item-${i}`, { idx: i });
|
||||
}
|
||||
const { sstableData } = builder.build();
|
||||
|
||||
const reader = new SSTableReader(sstableData, makeMeta(sstableData));
|
||||
const items: [string, Record<string, unknown>][] = [];
|
||||
reader.scanAll((k, v) => items.push([k, v]));
|
||||
expect(items).toHaveLength(count);
|
||||
});
|
||||
|
||||
it('getIndexBlockCount 返回索引块数', () => {
|
||||
const builder = new SSTableBuilder(256); // 小 block size 触发多个 block
|
||||
for (let i = 0; i < 100; i++) {
|
||||
builder.add(`k-${i}`, { data: 'x'.repeat(50) });
|
||||
}
|
||||
const { sstableData } = builder.build();
|
||||
|
||||
const reader = new SSTableReader(sstableData, makeMeta(sstableData));
|
||||
expect(reader.getIndexBlockCount()).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
it('边界 — 空 SSTable 不抛异常', () => {
|
||||
const builder = new SSTableBuilder(4096);
|
||||
const { sstableData } = builder.build();
|
||||
|
||||
const reader = new SSTableReader(sstableData, makeMeta(sstableData));
|
||||
expect(reader.get('any')).toBeNull();
|
||||
const results: [string, Record<string, unknown>][] = [];
|
||||
reader.scanAll((k, v) => results.push([k, v]));
|
||||
expect(results).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('带特殊字符的 key', () => {
|
||||
const builder = new SSTableBuilder(4096);
|
||||
// 必须按键排序添加(按 ASCII 排序:空格 < 短横 < 点号)
|
||||
builder.add('key with space', { v: 3 });
|
||||
builder.add('key-with-dash', { v: 1 });
|
||||
builder.add('key.with.dot', { v: 2 });
|
||||
const { sstableData } = builder.build();
|
||||
|
||||
const reader = new SSTableReader(sstableData, makeMeta(sstableData));
|
||||
expect(reader.get('key-with-dash')!.v).toBe(1);
|
||||
expect(reader.get('key.with.dot')!.v).toBe(2);
|
||||
expect(reader.get('key with space')!.v).toBe(3);
|
||||
});
|
||||
|
||||
it('getEntryCount 返回正确条目数', () => {
|
||||
const builder = new SSTableBuilder(4096);
|
||||
builder.add('a', { v: 1 });
|
||||
builder.add('b', { v: 2 });
|
||||
builder.add('c', { v: 3 });
|
||||
expect(builder.getEntryCount()).toBe(3);
|
||||
});
|
||||
});
|
||||
/**
|
||||
* AriaEngine SSTable Builder + Reader 单元测试
|
||||
*/
|
||||
import { SSTableBuilder } from '../../src/engine/aria/index/sstable_builder';
|
||||
import { SSTableReader } from '../../src/engine/aria/index/sstable';
|
||||
import type { SSTableMeta } from '../../src/engine/aria/types';
|
||||
|
||||
// ===================================================================
|
||||
// SSTable Builder + Reader
|
||||
// ===================================================================
|
||||
describe('AriaEngine — SSTable Builder + Reader', () => {
|
||||
const makeMeta = (data: Uint8Array): SSTableMeta => ({
|
||||
id: 1, level: 0, minKey: '', maxKey: '\uffff',
|
||||
blockCount: 1, totalSize: data.byteLength, bloomData: null,
|
||||
});
|
||||
|
||||
it('构建单条目 SSTable 并精确读取', () => {
|
||||
const builder = new SSTableBuilder(4096);
|
||||
builder.add('key1', { name: 'Alice', age: 30 });
|
||||
const { sstableData } = builder.build();
|
||||
|
||||
const reader = new SSTableReader(sstableData, makeMeta(sstableData));
|
||||
const result = reader.get('key1');
|
||||
expect(result).not.toBeNull();
|
||||
expect(result!.name).toBe('Alice');
|
||||
expect(result!.age).toBe(30);
|
||||
});
|
||||
|
||||
it('构建多条 SSTable 并全部读取', () => {
|
||||
const builder = new SSTableBuilder(4096);
|
||||
const items: [string, Record<string, unknown>][] = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const key = `user-${String(i).padStart(3, '0')}`;
|
||||
const value = { idx: i, name: `User${i}` };
|
||||
items.push([key, value]);
|
||||
builder.add(key, value);
|
||||
}
|
||||
const { sstableData } = builder.build();
|
||||
|
||||
const reader = new SSTableReader(sstableData, makeMeta(sstableData));
|
||||
for (const [key, value] of items) {
|
||||
const result = reader.get(key);
|
||||
expect(result).not.toBeNull();
|
||||
expect(result!.idx).toBe(value.idx);
|
||||
}
|
||||
});
|
||||
|
||||
it('get — 不存在的 key 返回 null', () => {
|
||||
const builder = new SSTableBuilder(4096);
|
||||
builder.add('a', { v: 1 });
|
||||
const { sstableData } = builder.build();
|
||||
|
||||
const reader = new SSTableReader(sstableData, makeMeta(sstableData));
|
||||
expect(reader.get('nonexistent')).toBeNull();
|
||||
});
|
||||
|
||||
it('rangeScan — 范围查询', () => {
|
||||
const builder = new SSTableBuilder(4096);
|
||||
for (let i = 0; i < 20; i++) {
|
||||
builder.add(`k-${String(i).padStart(2, '0')}`, { v: i });
|
||||
}
|
||||
const { sstableData } = builder.build();
|
||||
|
||||
const reader = new SSTableReader(sstableData, makeMeta(sstableData));
|
||||
const results: [string, Record<string, unknown>][] = [];
|
||||
reader.rangeScan('k-05', 'k-10', (k, v) => results.push([k, v]));
|
||||
expect(results).toHaveLength(6);
|
||||
expect(results[0][0]).toBe('k-05');
|
||||
expect(results[results.length - 1][0]).toBe('k-10');
|
||||
});
|
||||
|
||||
it('scanAll — 遍历所有条目', () => {
|
||||
const builder = new SSTableBuilder(4096);
|
||||
const count = 50;
|
||||
for (let i = 0; i < count; i++) {
|
||||
builder.add(`item-${i}`, { idx: i });
|
||||
}
|
||||
const { sstableData } = builder.build();
|
||||
|
||||
const reader = new SSTableReader(sstableData, makeMeta(sstableData));
|
||||
const items: [string, Record<string, unknown>][] = [];
|
||||
reader.scanAll((k, v) => items.push([k, v]));
|
||||
expect(items).toHaveLength(count);
|
||||
});
|
||||
|
||||
it('getIndexBlockCount 返回索引块数', () => {
|
||||
const builder = new SSTableBuilder(256); // 小 block size 触发多个 block
|
||||
for (let i = 0; i < 100; i++) {
|
||||
builder.add(`k-${i}`, { data: 'x'.repeat(50) });
|
||||
}
|
||||
const { sstableData } = builder.build();
|
||||
|
||||
const reader = new SSTableReader(sstableData, makeMeta(sstableData));
|
||||
expect(reader.getIndexBlockCount()).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
it('边界 — 空 SSTable 不抛异常', () => {
|
||||
const builder = new SSTableBuilder(4096);
|
||||
const { sstableData } = builder.build();
|
||||
|
||||
const reader = new SSTableReader(sstableData, makeMeta(sstableData));
|
||||
expect(reader.get('any')).toBeNull();
|
||||
const results: [string, Record<string, unknown>][] = [];
|
||||
reader.scanAll((k, v) => results.push([k, v]));
|
||||
expect(results).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('带特殊字符的 key', () => {
|
||||
const builder = new SSTableBuilder(4096);
|
||||
// 必须按键排序添加(按 ASCII 排序:空格 < 短横 < 点号)
|
||||
builder.add('key with space', { v: 3 });
|
||||
builder.add('key-with-dash', { v: 1 });
|
||||
builder.add('key.with.dot', { v: 2 });
|
||||
const { sstableData } = builder.build();
|
||||
|
||||
const reader = new SSTableReader(sstableData, makeMeta(sstableData));
|
||||
expect(reader.get('key-with-dash')!.v).toBe(1);
|
||||
expect(reader.get('key.with.dot')!.v).toBe(2);
|
||||
expect(reader.get('key with space')!.v).toBe(3);
|
||||
});
|
||||
|
||||
it('getEntryCount 返回正确条目数', () => {
|
||||
const builder = new SSTableBuilder(4096);
|
||||
builder.add('a', { v: 1 });
|
||||
builder.add('b', { v: 2 });
|
||||
builder.add('c', { v: 3 });
|
||||
expect(builder.getEntryCount()).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user