Files
MetonaSqlark/CHANGELOG.md
T
thzxx 05e6823bf1
CI / test (22.x) (push) Successful in 24m26s
CI / e2e (push) Successful in 10m0s
CI / test (18.x) (push) Successful in 27m14s
CI / test (20.x) (push) Failing after 1h19m9s
CI / test (24.x) (push) Successful in 37m49s
fix: v0.7.2 语句级原子性 + 事务 DDL 拒绝 + 约束/绑定硬化 — 6 项修复 + 43 回归 + CI 重型套件串行
- UPDATE 语句级部分提交(P1,四引擎):两阶段全量预检后执行,批内唯一互查,
  任何一行失败整句不执行(aria 场景 WAL 与内存不再错位)
- 事务内 ALTER/CREATE INDEX/DROP INDEX 残留(P1):Memory/KVStore 显式拒绝
  (对齐 Aria),createTable/dropTable 保持可回滚
- SET NULL 级联绕过 required 约束(P1):预检阶段整体拒绝 FOREIGN_KEY_VIOLATION
- bindParameters 注释误判(P2):行注释/块注释中的 ? 与引号不再参与绑定
- 未闭合字符串静默接受 → lexer 抛 PARSE_ERROR;未知 where 操作符抛 QUERY_ERROR
- UPDATE undefined 覆盖列值 → 语义化为不更新(null 仍置空)
- Hybrid 写穿透非原子(P1):磁盘失败自动重载内存对齐磁盘再抛原错误
- CI:Run tests 拆常规并行 + 重型串行(runInBand),重型测试超时余量提升,
  性能护栏 kv 120→240s / opfs 150→300s(仍拦截悬崖回归)
- 测试 1155 → 1198(74 套件),覆盖率 89.82% 保持
2026-08-13 15:31:16 +08:00

72 KiB
Raw Blame History

Changelog

All notable changes to MetonaSqlark will be documented in this file.

[0.7.2] - 2026-08-13

语句级原子性 / 事务 DDL 语义统一 / 约束与绑定硬化

深度审计第五阶段:修复 UPDATE 语句级部分提交、事务内 DDL 回滚残留、 SET NULL 级联绕过 required 约束、参数绑定注释误判等 6 项问题, Hybrid 写穿透失败补偿,四引擎语义对齐。

Fixed

  • UPDATE 语句级部分提交(P1Memory/Aria/KVStore/Hybrid — 单条 UPDATE 匹配 多行时,第 N 行唯一约束/校验失败抛错,前 N-1 行已写入(aria 场景其 WAL 记录 随 appendBatch 一起丢失,内存与 WAL 进一步不一致)。改为两阶段:先全量预检 (validateRow + 批内唯一互查 + 索引唯一查 + 主键冲突 + 外键 RESTRICT/SET NULL 预检),任何一行失败整句不执行,后统一执行
  • 批内唯一互查缺失 — 两行在同一语句中更新到同一新唯一值:索引尚未反映本语句 变更,逐行检查相互看不见 → 绕过唯一约束。两阶段预检增加批内 Set 互查
  • 事务内 DDL 回滚残留(P1BEGIN; ALTER ADD COLUMN; ROLLBACK 后新列残留 Memory/KVStore 事务快照对 schema 是浅拷贝,alterTable 直改共享 columns 对象)。 Memory/KVStore 的 ALTER TABLE / CREATE INDEX / DROP INDEX 在事务内显式拒绝 NOT_SUPPORTED,与 AriaEngine 对齐);createTable/dropTable 保持可回滚
  • SET NULL 级联绕过 required 约束(P1 — 外键列 required: true 时 delete 级联 与 update 主键变更级联静默写入 null(不经过 validateRow)。预检阶段整体拒绝 (FOREIGN_KEY_VIOLATION),Memory/Aria 双引擎对齐
  • bindParameters 注释误判(P2-- comment ? 中的 ? 计入占位符(参数错位 PARAM_ERROR);注释中的单引号触发 "Unterminated string literal"。词法扫描感知 行注释与块注释(其中 ?/引号不参与绑定与字符串状态机)
  • 未闭合字符串静默接受SELECT 'abc 此前解析成功(错误结果),lexer 现抛 PARSE_ERROR
  • 未知 where 操作符静默全匹配(P3)$betwen 等未实现/拼错操作符此前 default: return true(所有行匹配、过滤形同虚设)。现抛 QUERY_ERROR
  • UPDATE undefined 覆盖列值update({ col: undefined }) 此前把 undefined 写入 行(列键丢失)。undefined 现语义化为"不更新该列"(保留旧值),null 仍显式置空 (四引擎统一;KVStore 主键 undefined 不再误触主键变更路径)
  • Hybrid write-through 非原子(P1 — 磁盘写失败时内存已写入(重启丢数据且错误 已抛)。insert/update/delete/createTable/dropTable/alterTable/clear/createIndex/ dropIndex 磁盘失败后自动从磁盘重载内存(内存=磁盘对齐),再抛原始错误; 事务路径由双引擎快照回滚保证不受影响

Changed

  • 测试 1155 → 119874 套件,+43 个 v0.7.2 回归);新增 tests/v072-fixes.test.ts (语句原子性 ×6 / 批内互查 ×3 / 事务 DDL ×6 / SET NULL 约束 ×5 / 注释感知 ×6 / 未闭合字符串 ×2 / 未知操作符 ×2 / undefined 语义 ×9 / Hybrid 补偿 ×4
  • CONTRIBUTING.md 项目结构同步(移除已删除的 indexeddb/opfs 引擎与 utils.ts

[0.7.1] - 2026-08-13

API 修复 / 防御统一 / 工程质量

深度审计第四阶段:修复 README 示例 API 缺失、未 open 防护不一致、 事务错误掩盖、原型污染防护,lint 清零。

Fixed

  • MetonaSqlark.create 静态工厂缺失(API/文档不一致) — create 此前仅存在于 api 对象与 window 挂载,README/CONTRIBUTING/site 全部示例的 MetonaSqlark.create({...}) 在 ESM/Node 下是 undefinedTypeError)。 类增加静态 create(与 connect/disconnect 同入口风格),独立 create 函数委托
  • close 未初始化崩溃new MetonaSqlark(cfg).close()init 失败/未调用)时 this.engine undefined → TypeError。close 增加防御
  • AriaEngine 未 open 防护不一致 — hasTable/getTableNames/getTableSchema 未检查 DB_NOT_OPEN(返回空而非报错,与 KVStoreEngine 及自身其余方法不一致)。 统一 ensureOpen
  • 事务回滚失败掩盖原始错误 — rollbackTransaction 抛错会替换掉真正导致事务 失败的异常(定位困难)。回滚失败吞掉,保留原始错误
  • __proto__ 列名原型污染防护 — SQL CREATE TABLE 的列名 __proto__ 在普通 对象上触发原型 setter 静默丢列。Executor 列映射改用 Object.create(null) (键可见),schema 校验层显式拒绝该列名(SCHEMA_ERROR
  • lint 清零 — 移除 5 处未使用导入(backend/file_manager/kvstore_engine/parser

Changed

  • 测试 1147 → 115573 套件,+8 个 v0.7.1 回归);新增 tests/v071-hardening.test.ts 行覆盖率 89.8%lint 0 error 0 warning

[0.7.0] - 2026-08-13

参数化查询 / 事务性能 / 语义硬化

深度审计第三阶段:能力补全(参数化查询)、大库事务性能、 复合主键语义陷阱显式化、EXPLAIN 真实索引信息。

Added

  • 参数化查询db.query(sql, params) 位置参数(?):
    • 绑定在词法层完成(仅替换字符串字面量之外的 ?'' 转义与原样保留)
    • 值按 SQL 字面量安全编码(字符串 '' 转义 / 数字 / 布尔 / NULL), 注入防护从根上成立('x'; DROP TABLE users; -- 作为字面量原样入库)
    • 参数数量不匹配 → PARAM_ERROR;对象/数组参数显式拒绝(SQL 方言无 json 字面量,防静默类型错配);NaN/Infinity → NULL
  • EXPLAIN 真实索引信息usingIndex 从 'auto' 占位升级为真实命中判定: 主键条件 → pk、索引/唯一列条件 → index:col、无索引 → none (引擎无关启发式:按 schema 标记判定,与各引擎 tryIndexLookup 路径一致)

Changed

  • KVStoreEngine 事务增量 flush(性能) — commit 时此前对每个 dirty 表整表 diff(大表事务改 1 行也 O(表大小))。现在行级变更追踪:普通 insert/update/delete 仅写事务内改动行(单次全表扫描 + 变更集合过滤);主键变更/级联影响表整表 diff 兜底;事务内 clear/drop 只删 KV 行;clear 后写入自动切换兜底。实测:1000 行表 事务改 1 行 commit 日志仅 1 条记录 1 个条目(此前 1000 条目)
  • 移除每次 commit 的强制全量 checkpoint — KVStore 按日志阈值自动 checkpoint (日志重放保证崩溃恢复),close() 时统一 checkpoint 截断(重开更快); 大库高频事务不再 O(库大小)
  • 复合主键显式拒绝(语义陷阱) — 此前 schema 允许多个 primaryKey 但引擎 静默取第一个(其余标记失效)。createSchema 校验期抛 SCHEMA_ERROR (含明确指引);ALTER TABLE ADD 主键列同样防护(防绕过建表校验)。 复合主键列入 v0.8 路线图

Fixed

  • 事务级联影响表漏写 — delete/update 的级联影响表加入 txFullTables 但未加入 txDirtyTables → commit 循环不处理 → 级联改动(SET NULL 等)不落盘

Changed(测试)

  • 测试 1126 → 114772 套件,+21 个 v0.7.0 回归);新增 tests/v070-features.test.ts (复合主键拒绝 ×3 / 参数化 ×7 / 事务增量 flush ×8 / EXPLAIN ×2

[0.6.3] - 2026-08-13

原子性 / 一致性 / 资源治理

深度审计第二阶段:兑现"单记录真原子"宣称、补齐 WAL 失败可见性、 修复级联/索引残留/内存泄漏类问题。

Fixed

  • KVStore 混合写非单记录原子(P2,宣称失实) — delete/update 主键变更等路径 putMany 与 deleteMany 分两次调用 = 两条日志记录:崩溃在两条记录之间 → 新旧行并存 (主键变更产生重复行 / SET NULL 重写与父行删除不一致)。新增 KVStore.writeBatch put+delete 编码进同一条日志记录,真全有或全无),KVStoreEngine 全部混合写路径 update/delete/dropTable/alterTable/clear/commitTransaction)统一走 writeBatch
  • WAL full 模式写入失败吞错(P2) — append/appendBatch 失败仅 console.warn 内存已提交而 WAL 缺失,崩溃即丢且调用方无感知。现在失败直接抛出(batch 模式 flush 早已抛错,行为对齐)
  • MemoryEngine SET NULL 级联索引残留(P2 — 手动 pks.get(v)?.delete(pk) 遗留 空 Set → checkUniqueness 对旧值永久误报 UNIQUE_VIOLATION(外键列带 unique 约束时)。 统一复用 removeIndexEntries(空 Set 正确清理)+ updateIndexes
  • delete 级联部分执行(P2 — 多行删除时第 N 行 RESTRICT 抛错 → 前 N-1 行的级联 子行已删除、父行未删(无事务下数据不一致)。改为两阶段:先对全部待删行做 RESTRICT 预检(沿 CASCADE 链递归),任何违规则整体拒绝;Memory/Aria 双引擎对齐
  • BufferPool 驱逐不清理 pages MapP2 — 驱逐仅清 LRU 链表,pages Map 保留全部 历史页面 → 内存无限增长(256 页 ≈ 1MB 预算形同虚设)。EvictionManager 增加 onRemove 回调,驱逐时同步从 pages Map 移除(容量约束真实生效,重载仍正确)
  • MVCC 已提交版本不清理(P2 — commit 仅标记 committedversionStore 随写入量 无限增长(行数据双份常驻)。快照读取已移除(v0.5.1),版本链仅作事务内 undo: commit 时直接清理本事务版本(rollback/discardVersions 语义不变,gc 保留未提交链裁剪)
  • LSM.flush 重复入链(P2 — freezeMemtable 会把同一张 immutable 再次入链 → 重复 SSTable2 行 flush 出 3 个文件);且 frozenMemtables 在链执行前被清空 → 并发读短暂看不到冻结数据。修复:入链后立即置空 immutable(防重复), frozenMemtables 保持到链排空后再清理
  • rollbackToSavepoint 不重建二级索引(P2 — 事务内直写索引 LSM,savepoint 回滚 只还原快照 → savepoint 之后的过期索引条目残留。回滚后重建受影响表索引

Changed

  • 测试 1114 → 112671 套件,+12 个 v0.6.3 回归);新增 tests/v063-fixes.test.ts; MVCC 语义测试同步改写(commit 清理版本、gc 裁剪未提交链)

[0.6.2] - 2026-08-13

深度审计修复(数据正确性专项)

全源码通读 + 针对性实验验证,修复 6 个测试盲区中的数据丢失/查询错误/约束缺失问题。

Fixed

  • KVStoreEngine 数值主键 update 丢行(P0 — 非主键更新路径把受影响主键 String() 化后按 where { [pkCol]: pk } 回查内存行,数值型主键(123 !== "123")不命中 → 行被误判删除 → 重启后该行永久丢失。改为单次全表扫描 + 受影响集合过滤(同时消除 O(N×M) 逐主键回查开销), 字符串/数值主键语义统一
  • update 主键变更撞已有主键静默覆盖(P0) — MemoryEngine / AriaEngine 更新主键为目标已 存在值时静默覆盖另一行(数据丢失)。现在抛 DUPLICATE_KEY(与 insert 语义对齐), 事务内路径(txnSnapshot + 主 LSM 双查)同样拦截
  • Aria 二级索引范围查询边界算法错误(P1)$gt/$gte/$lt/$lteNumber(v)±1 构造边界 key:小数数值($gt:2 → "3:",漏 2.5)与字符串("NaN:" 前缀错位,数字/大写开头值被漏) 静默丢数据。改为全索引扫描 + 行级 matchWhere 过滤(与主键范围路径同方案),边界语义与 where-matcher 完全一致;新增小数/字符串/大写/数字开头/整数 flush 前后一致性回归
  • Aria 索引列 IS NULL 返回空(P1$eq: null 走索引路径时 String(null)="null" 查找 返回空并短路全表扫描 → 索引列 IS NULL 恒空。null 等值/含 null 的 IN 列表不再走索引 (回退全表扫描),与全表扫描语义一致
  • AriaEngine unique 约束未强制(P1 — 此前 insert/update 仅检查主键重复,唯一列重复值 被静默接受(MemoryEngine 已强制)。补齐:insert 整批预检(批内互查 + 索引 LSM 前缀扫描, 失败整批不落库);update 排除自身旧索引条目后检查;null 不受唯一约束(对齐 Memory); 新增 LSM.prefetchPrefixRanges(批量前缀范围预加载,一次 drainChain 避免逐行性能悬崖)
  • 非主键 update 索引旧值残留 — update 时旧行未传给 updateSecondaryIndexes(仅主键变更 时传),索引 LSM 旧条目累积 → 唯一性检查误报 / 索引存储膨胀。统一传旧行清理旧值
  • EXPLAIN 写语句产生真实副作用(P2)EXPLAIN DELETE/UPDATE/INSERT 此前真实执行语句 (删/改数据)。现在仅 SELECT 类执行(只读),UPDATE/DELETE 用 count 估算影响行数, INSERT/DDL 仅输出计划

Changed

  • 测试 1092 → 111470 套件,+22 个 v0.6.2 回归);新增 tests/v062-fixes.test.ts (数值主键持久化 ×3 / 主键碰撞 ×3 / 索引范围 ×4 / IS NULL ×3 / unique ×7 / EXPLAIN ×2

[0.6.1] - 2026-08-10

生产可用性深度审查(异常场景)

以"生产可用"标准全面审查:修复级联环无限递归、多表事务原子性缺口、 统一未 open 防护,并新增 27 个异常场景测试。

Fixed

  • 页面 id 崩溃回退导致静默丢数据(P0) — FileManager allocatePageIds 在分配页面后、 saveMeta 持久化 nextPageId 前崩溃 → __aria_meta 停留在旧水位 → 恢复时页面 id 复用 (覆盖崩溃前旧页面)→ 恢复期 compaction 按 meta.pageIds 删除"旧"SSTable 时误删被复用的 新数据页面 → 崩溃恢复后静默丢 75%(kv 后端 2 万行 + 加密 + 双索引场景复现 5228/20000)。 修复:init 以"现存最大页面 id + 1"为准(单调不回退,绝不复用已存在页面)
  • LSM.flush 剩余数据与 compaction 并发写 metaP0flush() 对 immutable/memtable 的剩余落盘直接 await(不在链上),与链上 compaction 并发写 SSTable metacompaction 产物 saveMeta 后,memtable flush 的 saveMeta 读到中间态列表(含 compaction 产物)→ 覆盖产物 引用 → compaction 产物变孤儿 → 主表/索引数据静默丢失(kv 后端优雅关闭后重开丢 75%)。 修复:剩余落盘挂链串行(memtable flush 在 compaction 之后),meta 无竞态
  • 生产矩阵审计(13 个组合全量验收):后端(opfs/kv/memory)× 特性(页面化/加密/压缩/ WAL 开关/同步模式)× 功能(CRUD/双索引/事务/主键变更/级联删除/崩溃恢复)—— 新增 tests/engine/aria-matrix-audit.test.ts,暴露并修复上述 2 个 P0
  • 二级索引范围扫描漏读尾部数据(P0 — SSTable 索引块记录的是"块内最后一个 key" builder 约定),读端 locateBlockLE 却按块首 key 语义二分:范围查询 endKey 落在 某块尾部时,下一块(尾 key 越界但首 key 仍在范围内)被排除 → 二级索引条件查询 静默丢数据(5 万行写入查 tag='t5' 丢 106~771 条,主表全扫描完整)。 修复:rangeScan 的 endBlockIdx 多扫一个块(条目级过滤兜底,不丢不错); 新增 4 个 SSTable 边界回归(跨前缀尾部/精确块尾/超出全量/多前缀一致性)
    • 重建 3 个二级索引大数据量回归(5 万行/高频小批量/多索引列,含崩溃恢复)
  • checkpoint 未落盘二级索引(P1 — checkpoint 只 flush 主 LSM,截断 WAL 后崩溃时 索引 memtable 未落盘、WAL 为空跳过重建 → 二级索引静默丢失最后一批条目。 checkpoint 的 flushAll 同步 flush 全部二级索引 LSM
  • kv 后端页面化缺失pageStorage 默认只对 opfs 生效;kv 后端整 value SSTable 4MB)超过 1MB 页面缓存时每次读取全量重载。kv 后端同样启用页面化, 读放大消除;SharedMemoryBackend 改为 chunk 列表(append O(1)+ 惰性拼接缓存
  • MemoryEngine 级联环无限递归(P0 — A→B→A 循环引用 + CASCADE 删除导致栈溢出 RangeError: Maximum call stack size exceeded)—— MemoryEngine.cascadeDelete 无环路保护, AriaEngine 已有 visited 保护(v0.4.1),Memory 侧缺失。新增 visited 集合(表:主键), 与 Aria 语义对齐;A→B→A 环 + 深链 A→B→C→D 均有回归测试
  • KVStore 快照损坏水位 bugP0 — baseSeq = max(metaSeq, snapshotSeq) 在快照损坏 回退全量日志重放时,meta.seq(最后一次 checkpoint 水位)错误跳过日志中 checkpoint 后 的有效记录 → 静默丢数据。水位只信任快照内嵌 seq
  • 多表事务 commit 非原子(P1 — 事务 commit 逐表 flush:中途失败/崩溃 → 部分表已提交。 重构为全部 dirty 表合并单次 putMany/deleteMany(一条日志记录 = 真原子)
  • 级联更新/删除非原子(P1 — update 主键变更/delete 的级联影响表逐表 diff 分多次写。 重构 collectTableDiff 合并到单次原子写(主表 + 全部级联表同一条日志记录)
  • 未 open 防护不一致 — getTableNames/hasTable/getTableSchema 未检查 DB_NOT_OPEN (返回空而非报错,与其余方法不一致)。统一 ensureOpen
  • 测试盲区 — 快照损坏测试此前篡改未生效(close 后介质不可读),从未真正覆盖损坏路径; 修复并新增 metaSeq 超前回归测试
  • 批量插入性能悬崖(P1 — insert 循环内逐行 lsm.prefetchKeys([key]) 每行 await drainChain() 排空后台链:后台 compaction 在链上数秒时每行阻塞数秒 → 10 万级插入从 5ms/批暴跌到 811s/批(kv 后端 10 万行共 353s)。 修复:批级预加载本批全部 PK(一次 drainChain),循环内 lsm.get 由 memtable/flush 产物(自动入缓存)兜底,完整性与批量语义不变。 实测:kv 后端 10 万行 353s → 12.5s28 倍),opfs 后端 25s 新增 kv/opfs 双后端 10 万行回归(性能护栏 + 索引完整 + 崩溃恢复)

Added

  • AriaEngine 可选自研 KVStore 后端storageBackend: 'kv' / diskEngine: 'kv'):
    • aria 完全跑在自研存储栈上(KVStore 日志+快照),不再依赖浏览器 OPFS APINode/测试用 SharedMemory 介质)
    • KVStore 新增 APPEND 日志操作类型:aria WAL 分片追加 O(chunk) 高效,恢复按序拼接,checkpoint 快照含最终值
    • writeMany/deleteMany 经 KVStore 单日志记录真原子(此前 OPFS 逐文件写)
    • aria 崩溃恢复 = KVStore 双层恢复 + WAL 重放;二级索引/页面化兼容(kv 后端 SSTable 整 value 存储)
    • 10 个 aria+kv 集成测试(后端单元/CRUD 持久化/WAL 追加恢复/checkpoint 混合/索引/大数据崩溃/高层 API 事务级联)
  • 27 个生产异常场景测试(tests/production-abnormal.test.ts):
    • KVStore 边界:空 value/空 key、1MB 大 value、中文/emoji/冒号/超长 key、未 open、 重复 open/close 幂等、clear 后写、1 万小 key、写失败后继续可用
    • KVStoreEngine:未 open 全操作、重复 close、不存在表、事务嵌套 TX_ACTIVE、 无事务 TX_NONE、commit 失败回滚(磁盘无部分提交)、多表事务原子性、级联环、 深链级联、主键变更+索引一致性、空表操作、5000 行批量删除持久化
    • SharedMemoryBackend 全分支:append 拼接、writeMany/deleteMany、close 后安全、跨实例共享
    • 日志解析损坏分支:onCorrupt false 停止、keyLen 越界记录丢弃
  • AriaEngine 级联环/深链回归测试(与 Memory 对齐)

Changed

  • 测试 1022 → 1049(65 套件);行覆盖率 89.15% → 89.33%
  • 事务/级联写入路径:逐表 flush → 单条日志记录原子(性能与一致性双提升)

[0.6.0] - 2026-08-10

里程碑:完全移除 IndexedDB,自研 KV 事务存储引擎

达成"彻底删除 IndexedDB + 自研类似 IndexedDB 的存储后端"目标: KVStore 引擎在 OPFS 之上实现多 key 原子事务(IndexedDB 的核心能力), disk 模式全面切换,旧库一键迁移。

Added

  • KVStore 自研 KV 事务引擎src/engine/kvstore/):
    • 多 key 原子写:putMany/deleteMany = 单条日志记录(单文件 COW 原子追加)→ 崩溃全有或全无
    • 持久化与崩溃恢复:快照(checkpoint)+ 追加日志,两阶段恢复(快照水位跳过)
    • 自愈:快照损坏回退全量日志重放;日志损坏截断至损坏处;repair() 清理
    • 容错时序:checkpoint = 写快照 → 写 meta → 清空日志(meta 先于截断,任何崩溃窗口不丢数据)
    • 写操作与 checkpoint 串行队列(无交错窗口);标准 CRC-32 全程校验
    • 介质层:OPFS(浏览器)/ SharedMemoryBackendNode/测试,跨实例共享模拟持久化)
  • KVStoreEnginedisk 模式,替代 IndexedDBEngine + OPFSEngine):
    • 内存热路径 + KVStore 原子持久化;读 O(1),写增量/整表 diff 分级
    • 事务:内存快照 + commit 原子 flush(受影响表),事务内 DDL 支持(schema 一并持久化)
    • 外键级联(CASCADE/SET NULL/RESTRICT)持久化、主键变更、二级索引跨重启恢复
    • reload() 支持多标签页同步重载(Hybrid 场景)
  • 迁移工具 migrateFromIndexedDB():一次性把旧 IndexedDB 库(schema/索引/行)导入新引擎; 旧库不存在/aria 私有格式(SSTable/WAL)明确报错;目标库已有表跳过不覆盖
  • 可靠性强化
    • 10 万 key 写入+checkpoint+重开全量验证(928ms
    • 5 万混合操作+崩溃模拟零丢失
    • e2e 新增写入中途崩溃 / checkpoint 前后崩溃注入 / KVStoreEngine 真实环境持久化-索引-事务(12 用例)
  • MetonaSqlark 配置diskEngine 类型收敛为 'opfs' | 'memory'(默认 'opfs'

Removed

  • src/engine/indexeddb.ts840 行 IndexedDBEngine)—— 版本冲突/blocked 重试/onversionchange/flushToIDB 全部消失
  • src/engine/opfs.ts375 行 OPFSEngine)—— 能力被 KVStoreEngine 覆盖
  • IndexedDBBackendaria 后端)—— aria storageBackend 收敛为 'opfs' | 'memory'
  • 26 个测试文件的 fake-indexeddb 依赖改造为 KVStore SharedMemory / OPFS mock

Fixed

  • Hybrid 多标签页重载失效KVStoreEngine 读内存 vs 磁盘引擎读穿透)—— 新增 reload() 从介质重载
  • 事务内 DDL schema 不持久化—— commit 时统一 persistSchema(重启后表结构完整)
  • repair 缓存掩盖损坏—— 先清 BufferPool 再校验(缓存中"完好页面"不再掩盖磁盘损坏)
  • dropInvalidSSTable 孤儿页面—— 先删数据文件再删 meta(页面化删除依赖 pageIds 定位)

Changed

  • 测试 1009 → 102264 套件;+58 新增 kvstore/kvstore-engine/压力/迁移测试,-46 删除/改造 IDB 测试)
  • 行覆盖率 87.25% → 89.13%
  • 浏览器支持收敛到 OPFS 矩阵(Chrome/Edge 102+、Firefox 111+、Safari 15.2+);Node 用内存介质
  • fake-indexeddb 保留为 devDependency(仅迁移工具测试用)

[0.5.1] - 2026-08-10

深度审查:消除假实现/半成品/死代码

对全部源码做系统性审查,修复"宣称存在但从未接线"的假实现、补齐无入口的半成品、 删除无引用的死代码。

Added(假实现接线)

  • CRUD 生命周期钩子真实接线 — 此前 beforeInsert/afterInsert/beforeUpdate/afterUpdate/beforeDelete/afterDelete 六个钩子仅存在于类型与文档宣称("14 种钩子"),从未被任何路径触发。现已在两条路径真实触发:
    • Table APItable().insert/insertMany/update().execute()/delete().execute()
    • SQLdb.query("INSERT/UPDATE/DELETE ...") 参数与文档一致(beforeInsert: rows[]、afterUpdate: query, updates, count 等);事务路径不触发(Table 无 hooks 回调,与现状一致)
  • EXPLAIN SQL 入口 — 此前 executor 有 EXPLAIN 分支但 parser 无法解析(无 token),只能手工构造 AST 测试。 补齐 EXPLAIN <statement> 语法(tokens/lexer/parser),任意引擎可用
  • ANALYZE / REINDEX / VACUUM SQL 入口 — 此前仅有 AriaEngine 方法((engine as any) 才能调用),README 宣称能力但用户无法触发。 补齐 ANALYZE [TABLE] name / REINDEX [TABLE] name / VACUUM 语法;不支持的引擎抛 NOT_SUPPORTED
  • SAVEPOINT SQL 入口 — 补齐 SAVEPOINT name / ROLLBACK TO [SAVEPOINT] name / RELEASE [SAVEPOINT] name ROLLBACK [TRANSACTION] 保持原语义
  • db.backup() 公共方法 — 引擎级 backup(MVCC 一致性视图)此前无公共入口;Aria 走引擎级,其余引擎回退 exportAll

Removed(死代码)

  • src/utils.ts 整文件 — generateId/escapeHTML/deepMerge/debounce/throttle/isBrowser 在 src 内零引用 (v0.0.1 遗留,仅测试锁定);连同 tests/utils.test.ts 删除
  • estimateQueryCost(未接线的"查询优化器") — 实现与测试存在但从未接入查询路径(find 用 tryIndexLookup 硬编码),删除消除"优化器存在但没用"假象
  • MVCC 读侧全套 — readVersion/getLatestCommittedVersions/clearTable/getActiveTxnCount/isActive/getActiveWriteKeys 引擎事务读取走 txnSnapshotMVCC 仅作 undo log),读侧实现从未执行。删除并改写相关测试为验证版本链清理/GC 行为
  • 统计/辅助死方法 — EvictionManager.getAllPages/getSize/popLRU/getCapacity、BufferPool.getCachedPageCount/getDirtyPageCount/getCapacity/newPage、 WAL.isEnabled/getLSN、CheckpointManager.forceCheckpoint/setInterval/getOpCount、LSM.isInitialized/getCacheSize/getCacheLimit、 SSTableReader.getMeta/getIndexBlockCount/getStoredChecksum、BloomFilter.getBitSize/getInsertedCount、MemTable.contains、 FileManager.allocateTableRootPage/createEmptyPage、OPFSEngine.loadTableIntoMemory、QueryExecutor.setMaxRowsPerQuery、 ConnectionManager.getRefCount
  • 类型/常量死代码 — STORAGE_MODES/DISK_ENGINES、ColumnEncoding/DEFAULT_BLOCK_SIZE/DataBlock、CallbackEntrySource、StatementType、AggregateExpression、SubqueryExpression
  • Crypto 全局兼容层 — initCrypto/encryptPage/decryptPage/closeCrypto/isCryptoEnabledv0.5.0 后引擎走 EncryptedBackend 实例,全局单例无消费者);删除过时的"全局加密落盘"测试(aria-encryption.test.ts 已覆盖等价路径)

Changed

  • 测试 1022 → 1009(62 套件;删除 13 个死代码测试 + 新增 22 个 hooks/维护语句测试);行覆盖率 86.68% → 87.25%
  • README 补维护语句表(EXPLAIN/ANALYZE/REINDEX/VACUUM/SAVEPOINT SQL 入口)与 db.backup() API
  • 14 个生命周期钩子现在全部真实触发(site/docs.html 与 CONTRIBUTING 宣称变为事实)

[0.5.0] - 2026-08-10

里程碑:存储后端生产级硬化(自研 OPFS 后端全面落地)

本次版本对 AriaEngine 存储层做全链路生产级改造:修复"宣称有校验和/加密/页面式存储但实际未实现"的失实实现, WAL 重构为分片文件(消除多文件原子性与文件爆炸问题)、SSTable 页面化物理存储(BufferPool/FileManager 真正接入)、 多标签页独占锁、repair 自愈增强,并引入真实 Chromium e2e 验证。

Added

  • 全库 AES-256-GCM 加密(真实接线)AriaEngineConfig.encryption.password
    • EncryptedBackend 装饰器在 backend 层透明加解密(WAL/SSTable/Schema/元数据全覆盖,每个 value 独立随机 IV + GCM 认证)
    • PBKDF2-SHA256100000 迭代)派生密钥,salt + verifier 持久化于 __aria_keymeta(密码错误 → ARIA_DECRYPT_ERROR
    • 加密开关一致性检测:明文旧库 + 加密配置 → ARIA_ENCRYPT_CONFIG_ERROR;加密库 + 无密码 → ARIA_ENCRYPT_REQUIRED
    • clearAll 保留密钥元数据(库身份保留,密码不变)
  • WAL 分片存储重构SegmentedWALStore__wal_%06d.bin):
    • 取代"每条记录一个 key"旧结构:分片阈值 4MB 自动切换,文件数量可控
    • WAL 序号(LSN)内嵌记录字节流,移除 __wal_count 键 → append 单文件原子写(createWritable COW
    • 空洞检测:分片序号不连续 → 空洞之后保守丢弃(仅可能来自 truncate 部分完成,数据已落盘,无害)
    • 旧格式 __wal_N + __wal_count 兼容:读取迁移重放,checkpoint 统一清空
  • OPFS 后端 v2
    • append() 真追加:createWritable({ keepExistingData }) + seekO(chunk) 无需读旧内容
    • 写队列健壮性:单次任务失败不中断队列链(错误返回给调用方),close 等待队列排空
    • open 自动清理崩溃残留临时文件(.crswap/.tmp
    • 原子性文档化:单文件 createWritable 为 copy-on-write 原子替换
  • SSTable 页面化物理存储(BufferPool/FileManager 真正接入)
    • PageSSTableStoreSSTable 切分为 4KB 页面,经 FileManager 分配 pageId、BufferPool LRU 缓存
    • save 语义 = 已持久化(逐页 flushPage 落盘后才返回,WAL checkpoint 截断安全)
    • 页面 ID 列表经 SSTableMeta.pageIds 持久化;旧 meta(无 pageIds)自动走整 value 读取(兼容)
    • OPFS 后端默认启用(pageStorage 配置可覆盖);FileManager 批量分配 allocatePageIds(一次 meta 持久化)
    • 读取缺失页面返回 null(此前返回空页会静默掩盖页面丢失,自愈路径依赖 null 触发清理)
  • 多标签页独占锁 — Web Locks APIChrome 69+ / Firefox 96+ / Safari 15.4+):
    • ifAvailable 模式:第二个标签页打开同一库 → 抛 ARIA_LOCKED(不排队挂起)
    • 打开失败自动释放锁(不泄漏);close 等待锁真正归还
    • 不支持 Web Locks 的环境降级为无锁 + 告警(如实文档化)
  • repair() 自愈增强 — 孤儿页面清理(meta 未引用的 pg_ 文件)、OPFS 残留临时文件清理、WAL 空洞截断落地
  • LZ4 格式 v2 — 压缩流前置 4 字节原始大小头:解压不再依赖外部估算(高压缩率数据下 buf.length*2 估算不足会截断),损坏头显式抛错
  • SSTable 真实 CRC-32 校验和 — footer checksum 字段从写死 0 升级为整文件标准 CRC32(checksum=0 识别旧版文件跳过校验,兼容旧库);打开校验/compaction 加载/运行期预加载三处均校验,损坏文件自动清理自愈
  • WAL 标准 CRC32 — 记录校验从弱滚动校验升级为标准 CRC32;双算法探测(新算法失败再试旧算法)兼容旧 WAL 记录
  • 配置透传DatabaseConfig.ariaMetonaSqlark.create() 可配置 walSyncMode/checkpointInterval/encryption/pageStorage/compression 等 AriaEngine 专属参数
  • Playwright e2e(真实 Chromium + OPFS — 7 用例:持久化(刷新保留)、关闭重开、崩溃恢复(WAL 重放)、多标签页锁、加密密码往返、页面化端到端 + 索引查询、repair 自愈;npm run test:e2e

Fixed

  • 加密此前从未接线(严重失实) — crypto 模块全局单例从未被引擎调用、salt 不持久化重启无法解密;README 宣称的 AES-GCM 实际不可用。改为 EncryptedBackend 装饰器 + keymeta 持久化 + 密码验证
  • SSTable checksum 写死 0(严重失实) — 数据腐坏(bit flip)完全无法检测。升级为真实 CRC-32,损坏文件打开自愈
  • OPFS writeMany 无原子性(P0 — WAL 记录 + count 多键写入非原子,记录空洞导致后续记录字节错位解析失败。分片重构后 WAL append 单文件原子写 + 空洞检测截断根治
  • OPFS 高频写入文件爆炸(P1 — 每条 WAL 记录一个文件,8 万条记录 = 8 万次 createWritable。分片存储 + 真追加根治
  • LZ4 高压缩率解压截断(P1buf.length * 2 估算原始大小,压缩率 >50% 时截断(SSTable 魔数在文件末尾被丢 → 整库数据丢失)。格式 v2 头部自描述根治
  • OPFS 写队列失败永久中断(P1 — 队列任务抛错后链 rejected,后续所有写挂起。单次失败不中断链
  • OPFS close 不等挂起写(P1 — close 立即置空目录句柄,挂起写静默丢失。close 等待队列排空
  • Web Locks 方法解构 this 绑定丢失(e2e 发现)navigator.locks.request 解构后调用抛 Illegal invocation。改用 .bind(lockManager) 保持 this
  • 页面化 + 崩溃窗口数据丢失(压力测试发现) — 修复前页面化路径在模拟崩溃测试中丢失已确认写入;根治后 500 随机操作 + 崩溃重放零丢失

Changed

  • AriaEngineConfig 新增 encryption / pageStorageDatabaseConfig 新增 aria 透传
  • 页面层精简:删除从未接入主路径的 slot/tuple 模块(假"页面式存储"实现),仅保留生产代码实际使用的 initPageHeader/createPage;架构宣称如实更新(4KB 页面承载 SSTable 字节切片,非行级 Slotted Page
  • LZ4 压缩格式不向后兼容(v0.2.6 已声明 compression 数据需重新导入,本次格式 v2 同理)
  • 测试 958 → 1022(62 套件);行覆盖率 84.2% → 86.68%;新增 6 套件:crc32 / aria-checksum / aria-wal-crc / aria-wal-segment / aria-opfs-backend / aria-page-store / aria-encryption / aria-locks / aria-repair-hardening / aria-page-core
  • 新增 Playwright e2e 基建(npm run test:e2e7 用例真实 Chromium

[0.4.4] - 2026-08-09

Fixed

  • SSTable 编码缺陷导致大内容写入崩溃(P0) — AriaEngine 保存大段中文内容(如 300KB 笔记)→ close 构建 SSTable 崩溃 → 半写文件 → 下次打开 ARIA_OPEN_ERROR
    • 块大小估算用 UTF-16 码元而非 UTF-8 字节computeBlockSizejson.length(中文 1 字 = 1 码元)估算,实际写入 UTF-8 字节(中文 1 字 = 3 字节)→ 300KB 中文实际 900KB → 缓冲区低估 → Uint8Array.set 越界崩溃。改为 TextEncoder 预编码,全部按字节长度计算(切块、块大小、索引块、总大小)
    • valueLen/keyLen 字段 u16 截断 — 长度字段 u16 上限 64KB,大 value 被截断 → 线性格式整体错乱。格式 v2magic "SSTC")升级为 u32 长度字段
    • 大 value 块切分 — 单条 value 超过块大小(4096)时独立成块(基于字节估算正确切分)
    • 格式兼容 — reader 支持 v1"SSTB"u16)与 v2"SSTC"u32)双格式:旧库 v1 文件仍可读(小 value 场景无缺陷),新写入用 v2,旧库数据不丢失
    • 连带受益:中文主键 key、大内容二级索引列(索引 key 含列值)同样支持 u32 长度字段

Changed

  • 测试 950 → 95852 套件),新增 tests/v045-hardening.test.ts8 条:300KB 中文 builder/端到端跨重启、>64KB value、大 value 独立成块、中文 key、v1 兼容、v2 魔数)
  • 版本号升至 v0.4.4

[0.4.3] - 2026-08-09

Fixed

  • close 后后台 compaction 吞错并在 backend 关闭后执行(P0)scheduleCompactsetTimeout(0) 调度:close 时未触发的定时器在 backend.close() 后执行,用已关闭的存储写(错误被吞);close 后立即 reopen 时旧闭包引用新 backend 交叉污染。改为 compaction 直接挂 flushChain 串行执行,close 的 drainChain() 排空全部级联任务后再关闭存储
  • 后台 flush/compaction 失败完全静默(P0 — 吞错防死锁的同时记录 lastBackgroundError,显式 flush()/close() 时抛 ARIA_BACKGROUND_ERROR(消费一次),不再永久不可见
  • prefetch 与 compaction 竞态丢数据(P0 — 后台任务在链上动态增长,prefetchRange/prefetchKeys 单次 await flushChain 后 compaction 仍可能合并 levels,扫描时新 meta 缓存未命中 → 跳块丢数据(缓存驱逐场景 UPDATE/DELETE 少删行)。改为等待链稳定(drainChain)再预加载
  • commitTransaction 提交顺序(P1 — 先合并快照后写 WAL COMMIT:WAL 写失败时内存已提交但持久化缺失,崩溃重启丢失。改为先持久化 WAL COMMIT 再合并快照(WAL 始终领先,崩溃恢复一致)
  • OPFS close 不等挂起写(P1 — 写操作整体串行入队(内存写 + 快照 + 文件持久化),close/repair/clearAll 先等队列排空再释放目录句柄,避免 close 后挂起写读旧数据
  • IndexedDBEngine close 活跃事务防御 — close 时自动回滚活跃事务(避免对已关闭连接操作报错)

Changed

  • 测试 944 → 95051 套件),新增 tests/v044-hardening.test.ts(6 条:close 后台任务排空、reopen 无污染、后台失败可见、链不卡死、WAL 提交顺序、OPFS close 等待)
  • 版本号升至 v0.4.3

[0.4.2] - 2026-08-09

Fixed

  • AriaEngine 残缺 SSTable 导致打开崩溃(P0) — 异常退出后下次打开抛原生 RangeError: offset is out of bounds,库打不开只能删库重建。三处根治:
    • SSTableReader 读路径防御:块偏移/大小越界时跳过该块(get/rangeScan/scanAll 返回空或继续),索引块/块内条目解析全程边界检查,不再抛 RangeError
    • 打开时完整性校验:LSM.init() 校验 meta 引用的文件存在、长度 ≥ 头、可解析,残缺的忽略并清理 meta/文件(自愈,不阻塞打开)
    • WAL 原子写入:IndexedDBBackend 新增 writeMany/deleteMany(单事务批量写删),WAL 记录与 count 计数同事务提交,中断整体回滚不留半写
  • IndexedDB 版本管理缺陷导致重启必报 VersionErrorP0 — 建表每张版本号 +1 而 open()config.version,重启后版本过低直接失败。open() 遇 VersionError 自动用无版本参数探测库当前实际版本并以该版本重开
  • create({ version: 0 }) 抛原生 TypeErrorP0 — 版本 < 1 统一归一化为 1,不再直接炸 indexedDB.open(name, 0)
  • WAL 写丢失(P1 — 连续快速写入 200 条 close+重开实测丢 4 条。WAL 恢复不再依赖 count 计数,改为扫描全部 __wal_* 键(数值排序拼接),count 键只作序号分配器;配合原子写入彻底消除 count/记录竞态
  • AriaEngine.close() 不 checkpointWAL 无限重放(P1 — close 前补 wal.checkpoint() 截断,下次打开不再重放已落盘的历史记录
  • 引擎内部错误不包装 DatabaseErrorP1 — AriaEngine open() 整体 try/catch 包装为 ARIA_OPEN_ERRORIndexedDBEngine 打开路径统一 IDB_OPEN_ERROR/IDB_VERSION_ERROR/IDB_BLOCKED,应用层可拿 code 分类处理
  • migrateTo 版本门槛与引擎版本脱节(P2 — 迁移版本持久化到库内(IStorageEngine.getMeta/setMeta 可选接口,Aria 存 backend、IndexedDB 复用 __metona_schema store、OPFS 存元数据文件、Hybrid 委托磁盘引擎),重启后从持久化版本继续执行,不重跑不跳跑
  • IndexedDB onblocked 直接 rejectP2 — 改为等待后重试(阻塞解除后 success 仍触发,仅超时判失败,并防止连接泄漏),多次重试仍阻塞才抛 IDB_BLOCKED
  • 缺少官方崩溃恢复/自愈接口(P2 — 新增 db.repair()(校验清理损坏数据并恢复一致性)与 db.clearAll()(清空全部数据与表结构,保留库),全部引擎(Aria/IndexedDB/OPFS/Memory/Hybrid)实现,无需删库重建
  • IndexedDB __metona_schema store 缺失时元数据无法落盘open() 时确保 schema/meta store 存在(缺失则一次版本升级创建)

Fixed(深度审计第二轮)

  • AriaEngine 二级索引跨重启丢失(严重) — 重开只恢复 schema 不恢复索引 LSM:索引查询静默回退全表、createIndex 因 schema 标记已存在而直接 return → 索引永久缺失;且 WAL 崩溃恢复只回放主 LSM,最后一批写入的索引缺失 → 索引查询丢行。三处根治:open 时按 schema 标记重建索引 LSM(数据已持久化直接加载)、close 时同步落盘全部索引 LSM、WAL 恢复完成后全量重建索引
  • AriaEngine Compaction 依赖缓存丢数据(严重)compactLevelAsync 仅从 LRU 缓存读 SSTable,缓存未命中(单文件超缓存上限被驱逐、频繁 flush)时跳过全部文件并从 levels 移除 → 运行中数据全部不可见(重启才恢复)。改为从存储兜底加载;无有效数据时将文件放回 levels 不删除
  • AriaEngine flush/compaction 失败卡死写路径(严重) — flushChain 链上任务抛错后永久 rejected,后续所有 flush/compaction 挂起。链上任务统一吞错恢复(记录告警,链继续)
  • ALTER TABLE 在 IndexedDB/Hybrid/OPFS 引擎不持久化 — v0.4.1 只给 Aria 加了引擎级 alterTable,其余引擎走通用路径只改内存 schema 引用:重启后 ADD/DROP 全部回退,DROP 的行数据也残留。为 IndexedDB(持久化 schema + 重写存储行)/ Hybrid(双引擎)/ OPFS / Memory 统一实现引擎级 alterTable
  • IndexedDB 事务内 DDL 崩溃(严重) — 事务内建表 commit 时 IDB 无对应 store 报错;事务内删表 IDB store 残留 → 重启幽灵表。flushToIDB 提交时 diff 内存表与 IDB store:缺失的创建(含 schema 持久化)、多余的删除(含 schema 记录清理);commit 顺序调整为"先刷盘后提交内存快照",失败可回滚
  • Hybrid 内存/磁盘引擎共享 schema 引用污染reloadMemoryFromDisk 把磁盘引擎的 schema 对象直接存入内存引擎,任一引擎 ALTER 都改到对方。MemoryEngine.createTable 深拷贝 schema(全引擎受益)
  • Hybrid commit 失败路径错误掩盖 — 磁盘已提交后内存提交失败时调 diskEngine.rollbackTransaction() 抛 TX_NONE 掩盖原错误;如实上报磁盘已提交状态
  • AriaEngine.close 运行期状态残留 — close 后 MVCC/事务快照/savepoint 残留,重开后 beginTransaction 报 TX_ACTIVE。close 统一清理
  • VACUUM 对 2-3 个文件层级不压缩compactLevel public 门槛与内部自动调度门槛混用(内部 4 / VACUUM 期望 2)。分离参数化

Fixed(生产就绪审计第三轮)

  • Aria 事务进行中 checkpoint 截断 WAL(严重,P0 — checkpointtick/forceCheckpoint)在活跃事务中途执行时 truncate WALBEGIN/INSERT 记录被截断,事务 COMMIT 后崩溃恢复丢失整个事务数据。CheckpointManager 包装 WAL:事务活跃时跳过截断(close/repair/clearAll 仍正常截断)
  • Aria dropTable / DROP_TABLE 恢复不清理二级索引(P1) — 索引 LSM 孤儿残留,重建同名表后旧索引数据污染新表(按旧值索引查询返回不匹配行)。新增 cleanupTableIndexesdropTable、崩溃恢复、ALTER DROP 索引列统一清理
  • Aria ALTER TABLE DROP 索引列残留(P1 — 删列但索引 LSM 保留,后续同名列索引数据脏
  • Aria freezeMemtable 阈值逐次衰减(P2 — 新 memtable 用旧表已用大小当阈值,手动 flush 后阈值塌缩 → 频繁小文件。改回配置阈值
  • Aria 事务中 DDL 静默不一致(P2) — 事务快照只覆盖行数据,createTable/dropTable/alterTable 无法回滚;与 Memory/IndexedDB 可回滚行为不一致 → 显式抛 NOT_SUPPORTED(不再静默)
  • ON UPDATE 外键级联未实现(P2,声称支持但静默忽略) — 实现 ON UPDATE CASCADE/SET NULL/RESTRICTMemory + Aria 双引擎):更新主键时级联更新/拒绝,含二级索引与 WAL 记录,先全量 RESTRICT 检查再执行防部分修改;update 同时支持主键变更(旧键删除 + 新键落表)
  • MVCC commit/rollback 全库版本遍历(P2 性能) — 大表事务 commit O(全库版本数)。按事务记录写入的 key 精准清理(O(写入数))
  • OPFS 空表重启消失 + schema/索引丢失(P1 — 表 schema 持久化到元数据文件:createTable/dropTable/alterTable/createIndex 同步;重启从 schema 恢复(空表保留、主键/索引标记/约束完整),无 schema 的旧库回退数据推断(兼容)
  • OPFS dropTable 不清理 schema 记录(P1 — 重启恢复幽灵表;删表同步清理
  • OPFS 并发写验证 — 审计确认内存写同步、持久化快照总是最新(单标签页无丢更新竞态),新增并发写回归测试锁定行为
  • VACUUM 返回值语义gcVersions 返回活跃事务数改为全局提交序列号

Changed

  • 测试 894 → 94450 套件),新增 tests/v042-fixes.test.ts23 条问题清单回归)、tests/v042-hardening.test.ts12 条深度审计回归)与 tests/v043-hardening.test.ts(15 条生产就绪回归:事务×checkpoint、DDL 索引清理、OPFS 持久化、memtable 阈值、红黑树 5000 次随机压力、onUpdate 级联)
  • 版本号升至 v0.4.2

[0.4.1] - 2026-08-08

Added

  • AriaEngine 外键级联删除 — 对齐 Memory 引擎:CASCADE(递归多层)、SET NULLRESTRICT(抛 FOREIGN_KEY_VIOLATION),含二级索引清理、WAL 记录、事务内可提交/回滚、环路保护
  • AriaEngine.clearAll() — 重置数据库(清空后端存储/LSM/WAL/MVCC/二级索引),实例可继续使用;供演示页刷新场景与 API 用户使用
  • 演示页引擎切换器 — 右上角 ⚡ Memory / 🌲 Aria 一键切换,Aria 模式每次加载 clearAll() 保证演示确定性;aria 预设文案更新(数据库即 Aria)

Changed

  • 版本号升至 v0.4.1
  • 测试 880 → 89446 → 47 套件)
  • AriaEngine 级联行为与 MemoryEngine 对齐:SET NULL 不影响删除返回行数

Fixed

  • JOIN 主表 WHERE 不下推SELECT ... FROM orders o JOIN users u ... WHERE o.user_id = '1' 的 WHERE 只在 JOIN 后过滤,二级索引形同虚设。v0.4.1 将主表前缀等值条件下推到引擎(extractPushableWhere),索引真正生效
  • DROP INDEX 不存在的索引静默成功 — Memory/Aria 引擎 dropIndex 对无索引列静默返回,改为抛 INDEX_NOT_FOUND
  • Memory 索引查找不支持 $eq 对象形式 — SQL 解析器生成的 { $eq: value } 等值条件不走哈希索引(仅简单值走),补 $eq 分支
  • 演示页预设数据被前序预设污染 — 连续点击预设时,cascade(删除 Alice/重建 orders)、truncate、alter 等会修改种子数据,后续预设(如 index)查询返回空。loadPreset 点击预设前自动重置数据库到初始状态,每个预设可预期演示
  • Aria ALTER TABLE 不生效(严重)DROP COLUMN 仅改 schema 引用,find() 返回行副本无法就地删除,存储行残留已删列值(SELECT * 仍显示);且 ALTER 不持久化 schema,重启后列定义回退。新增引擎级 alterTableIStorageEngine 可选接口):Aria 重写主 LSM 移除列值 + persistSchemas + WAL UPDATE 记录,崩溃恢复后 schema 与行一致
  • 裸布尔列条件不支持(严重)WHERE done / CASE WHEN done THEN 解析失败(无比较运算符即抛错),条件被忽略导致 CASE 恒走 ELSE。parser 支持裸列真值判断({ col: { $eq: true } }
  • Aria $in 返回重复行 — IN 子查询含重复值(如 Alice 两条订单)时 PK/二级索引 $in 逐值查找重复 push,按主键去重

[0.4.0] - 2026-08-08

Added

  • 流式查询 APIdb.queryStream(sql, onRow) / db.table(name).stream(onRow) / IStorageEngine.findStream()
    • Aria 引擎走 LSM rangeScanLazy 惰性扫描,逐行回调不物化结果数组(大表友好)
    • Memory/Hybrid/OPFS 逐行迭代;IndexedDB 批量读入后逐行回调(保持接口一致)
    • 支持 WHERE/LIMIT/OFFSET/列投影;JOIN/GROUP BY/UNION/聚合/ORDER BY 自动回退物化
    • 用户 async 回调自动回退物化(同步扫描无法 await,避免吞 Promise
  • 多列 ON 哈希连接 — 复合等值条件(ON a.tenant = b.tenant AND a.key = b.key)走哈希连接(含顶层 $and 展开),任一右列为索引/主键即可;不适用自动回退嵌套循环
  • FROM 子查询(派生表)SELECT ... FROM (SELECT ...) AS alias WHERE ...,子查询结果作为行源,支持 WHERE/ORDER BY/LIMIT 与 JOIN 组合
  • COUNT(DISTINCT col) — 聚合去重(COUNT/SUM/AVG/MIN/MAX 均支持),任意类型去重
  • NULLS FIRST / NULLS LASTORDER BY col ASC NULLS FIRST 等,NULL 位置固定不受升降序反转
  • 普通列别名SELECT name AS n / SELECT name n(此前仅聚合/CASE 支持别名),别名可用于 ORDER BY
  • 无表查询与字符串常量列SELECT 1 / SELECT 'lit' AS x(无需 FROM),'' 转义与常量列投影

Fixed

  • AriaEngine 崩溃恢复后已删除表/数据复活(严重,P0) — WAL 回放忽略 DROP_TABLECREATE_TABLE 无条件恢复 schema,删表后崩溃重启表+数据全部复活。v0.4.0 新增 applyDropTableRecovery() 回放删除 schema + 清除 LSM 残留
  • WAL 恢复后不截断 — 每次重启重复回放 + WAL 无限膨胀。恢复完成将回放数据落盘后 wal.checkpoint() 截断
  • MemoryEngine update 不维护索引(P0update() 后唯一约束可被绕过、按新值索引查询丢行。removeIndexEntries() 在 update/delete/级联删除/SET NULL 前清理旧值索引
  • Aria 事务内读不到自己写入的行update/delete/count/cleargetAllRows() 不合并 txnSnapshot,事务内先 insert 再 update/delete 失效;统一 mergeTxnSnapshot()
  • Aria TRUNCATE 不写 WALclear() 崩溃恢复后丢失;事务内清空走快照删除标记,提交生效
  • WAL 大小阈值在 full 模式永不触发getBufferedBytes() 跟踪真实字节数替代缓冲计数估算
  • rollback 后二级索引残留 — 事务内直接写入索引 LSM,回滚后对受影响表全量重建索引
  • Savepoint 回滚 MVCC 不一致discardVersions() 清理版本链保留事务登记
  • PK 范围查询字符串算术 bug$gt/$gte/$lt/$lteNumber(v)+1 构造 key 对字符串 PK 失效;改为主 LSM 前缀扫描 + 条件过滤;主键列不再建冗余二级索引(PK $in 走多次精确查找)
  • SQL 字符串 '' 标准转义 — 双引号转义此前从未生效(lexer 条件先于转义分支退出);重写 readString
  • INSERT INTO ... SELECT 位置错位(严重) — 源行 validateRow 跳过 undefined 值导致行键缺失/乱序(如 ALTER 加列后省略列插入的行),Object.values 位置映射把 age 数字填进 email 列报类型错。改为按源表 schema 列顺序/SELECT 列列表对齐,缺列不填
  • ORDER BY / GROUP BY 表前缀列ORDER BY o.amount / GROUP BY u.name 解析失败(Expected ';' ... got "."),parser 支持 table.column 引用,executor 非 JOIN 路径剥离前缀
  • 关联 EXISTS 绑定失效(严重)SELECT u.name ... WHERE EXISTS (SELECT 1 FROM o WHERE o.user_id = u.id) 返回空:引擎按 SELECT 列投影后外层行缺 id 键,$col 绑定为 null。关联子查询路径强制 plan.columns=['*'] 取完整行;非 JOIN 路径 SELECT 列带表前缀(u.name)统一剥离
  • HAVING 标量子查询失效(静默返回空)HAVING SUM(o.amount) > (SELECT AVG(amount) FROM orders)$subquery 未解析 + 聚合表达式键(SUM(o.amount))与别名键(spent)不匹配。HAVING 先 resolveSubqueries,GROUP BY 结果维护「表达式键→别名键」映射做归一化

Changed

  • 版本号升至 v0.4.0
  • 测试 837 → 876(44 → 46 套件),覆盖率 81.1% → 81.5%Lines 84.7%

[0.3.2] - 2026-08-08

Added

  • CASE WHEN 用于 WHEREWHERE CASE WHEN cond THEN a ELSE b END = 'x' 逐行求值(含 NOT/AND/OR 组合)
  • CASE WHEN 用于聚合SUM(CASE WHEN age > 18 THEN 1 ELSE 0 END)COUNT/AVG/MIN/MAX(CASE...)GROUP BY + CASE 非聚合列
  • JOIN 哈希连接 — 等值 ON + 右表主键/索引列时,收集左表连接值 → 一次 $in 查询 → 哈希映射匹配,替代嵌套循环(INNER/LEFT JOINO(N+M));不适用自动回退
  • 多标签页同步multiTabSync: true 配置 + BroadcastChannel
    • SQL 写语句(INSERT/UPDATE/DELETE/DDL)自动广播表变更
    • Table API 写入(insert/insertMany/update/delete/clear/drop)自动广播
    • 其他标签页收到 external 变更事件(订阅者可见)+ Hybrid 内存自动重载
    • db.broadcastChange(table) 手动广播
  • IndexedDB schema 持久化__metona_schema store 保存完整列定义,reopen(页面刷新/重连)后恢复;旧数据回退为"主键 + 索引 + 样例推断"

Fixed

  • IndexedDB reopen 后 schema 丢失(严重) — schema 仅存内存缓存,close 后重开连接表结构即丢失(INSERT 列映射截断、UPDATE 全列失效)。v0.3.2 持久化 + 重建
  • where-matcher 顶层 $not 不生效NOT (expr) 解析为顶层 { $not } 但被当作字段匹配,始终返回 true
  • CASE 剥离后空 $not 恒假 — 引擎层执行 NOT(true) 过滤掉所有行

Changed

  • 版本号升至 v0.3.2

[0.3.1] - 2026-08-08

Added

  • CASE WHEN 表达式(SELECT 列)CASE WHEN cond THEN value [WHEN ...] [ELSE value] END [AS alias]
    • 多 WHEN 按顺序匹配、ELSE 缺省返回 null
    • THEN/ELSE 值支持字面量(字符串/数字/布尔/null)与列引用
    • 条件支持 AND/OR/比较运算符/IS NULL 等完整 WHERE 语法
    • 引擎层返回原始行供求值(避免投影丢失条件列),与普通列混合投影
  • JOIN + 关联子查询 — JOIN 结果的 WHERE 支持 EXISTS (SELECT ... WHERE o2.x = u.y)$col 引用外层行,逐行绑定求值
  • WAL 批量组提交WAL.appendBatch() 合并多条记录为一次底层写入;引擎 insert/update/delete 批量记录后一次落盘(写放大从 O(N) 降为 O(1))
  • parseWhereCondition() 导出 — 独立条件表达式解析(CASE WHEN 求值内部使用)

Fixed

  • LSM pending frozen 可见性 — 异步 flush 链期间,中间冻结的 MemTable 仅存在于闭包中,读取路径不可见导致数据缺失。新增 frozenMemtables 列表,get/rangeScan/getAllEntries 遍历所有 pending frozen(新→旧)
  • flush 缓存裁剪与预加载竞争 — flush 完成时的 LRU 裁剪会驱逐 prefetch 刚加载的 SSTable,导致扫描静默丢数据。prefetchRange/prefetchKeys 先等待 flush 链完成再加载
  • CASE 值解析顺序true/false/null 字面量被误判为列引用

Changed

  • 版本号升至 v0.3.1

[0.3.0] - 2026-08-08

Added

  • SQL 多语句支持parseAll() 解析分号分隔的多条语句,db.query() 顺序执行并返回最后一条结果(parse() 保持单语句兼容)
  • 事务语句(TCLBEGIN / COMMIT / ROLLBACK(支持可选 TRANSACTION 关键字),直接驱动引擎层事务,五引擎可用
  • INSERT INTO ... SELECT — 支持 INSERT INTO t (cols) SELECT ...(含 WHERE 过滤、列映射)
  • UNION / UNION ALL — 支持去重合并与链式组合(A UNION B UNION C),右侧结果按位置投影到左侧列结构
  • CREATE INDEX / DROP INDEX — 动态二级索引:
    • CREATE [UNIQUE] INDEX idx ON table (col) / DROP INDEX idx ON table (col)
    • 五引擎实现:Memory 哈希索引 / IndexedDB 版本升级建 idx_col / OPFS 委托 / Hybrid 双写 / Aria 动态 LSM 索引
    • Aria 主键索引受保护(不可 DROP);CREATE INDEX 对已有数据立即建索引(回填),后续写入自动同步
  • EXISTS / NOT EXISTS — 支持关联子查询(WHERE EXISTS (SELECT 1 FROM o WHERE o.user_id = u.id)),逐行绑定外层上下文求值
  • 别名 WHERE 修复WHERE u.age > 20(主表别名前缀)此前不工作,v0.3.0 字段名规范化(JOIN 路径不受影响)
  • SELECT 1 常量列 — 数字字面量可作为 SELECT 列(EXISTS 子查询常见用法)
  • benchmark 页面site/benchmark.html 浏览器内实测 Memory / Aria 引擎 × 1K/10K/50K 行的 INSERT/PK 查询/索引查询/UPDATE/DELETE 吞吐(ops/sec
  • parseAll / 新 AST 类型导出SelectUnionStatement / CreateIndexStatement / DropIndexStatement / BeginTransactionStatement

Changed

  • 版本号升至 v0.3.0

Fixed

  • NOT EXISTS 解析丢 EXISTS token 导致 parse 失败

[0.2.6] - 2026-08-08

Fixed

  • AriaEngine 二级索引 SSTable 互相覆盖(严重) — 主 LSM 与二级索引 LSM 此前共享同一 sstableStoreid 空间 + meta 列表),allocateId() 返回 Date.now() 且各 LSM 从同一 meta 恢复 nextSSTableId,高频写入时 id 冲突导致索引数据文件覆盖主数据、meta 与数据错配。v0.2.6 按命名空间隔离(文件前缀 + meta key + 独立 id 序列),索引 LSM 各持独立 store
  • LSM 多版本读取顺序错误(严重)levels[] 数组按 push 追加(新文件在尾部),但 get()/rangeScan 从头遍历(旧文件优先),同一 key 跨多次 flush 更新后读到旧值;MergeIterator 去重只取"先出堆者",不保证最新来源。v0.2.6 改为"数组头部即最新"(unshift + 按 id 降序加载),MergeIterator 显式取 sourceIndex 最小者
  • LZ4 往返不一致 — ① 匹配长度超过 19 字节被截断但 si 跳过全部匹配,解压丢数据;② matchField=0 的组合 token 与纯字面量 token 格式歧义;③ 字面量累积超过 15 字节后组合 token 永远无法输出;④ 输出缓冲区 maxOut 不足导致越界写入;⑤ 压缩失败时返回原样 input,解压端无法区分。v0.2.6 重写压缩器(长匹配分段、纯字面量 token 语义明确、动态上限)
  • React/Vue 集成运行时 ReferenceErroruseDatabase/useSqlarkDatabaseimport type 导入 MetonaSqlark 但运行时 new MetonaSqlark()@ts-nocheck 掩盖此 bug,实际使用必炸。改为 value import
  • sstableCache 无限增长 — SSTable 缓存无内存上限且 init() 预加载全部数据,大库 OOM。v0.2.6 引入 LRU 容量上限(bufferPoolPages × pageSize),查询前异步预加载兜底(修复缓存未命中静默返回 null 丢数据的隐患)
  • integrations 子路径缺失 — README 宣称 metona-sqlark/reactmetona-sqlark/vue 可用但 exports 未声明,现已补齐(react/vue 为 peer dependencyrollup external

Changed

  • Buffer Pool 配置真正生效bufferPoolPages 从无效配置变为 SSTable 缓存容量上限(默认 256 页 ≈ 1MB 可控内存)
  • LZ4 格式变更 — 旧版本压缩数据与新解压器不兼容,使用 compression: true 的用户需重新导入数据
  • SSTable 持久化串行化 — flush/compaction 改为串行链(flushChain),id 分配与持久化顺序一致,消除 fire-and-forget 竞态
  • 文档数据修正 — 测试 761(41 套件)、覆盖率 81.1%(此前 91.0% 为排除 Aria 模块的陈旧数据)、体积 ~105KB / gzip ~27KB

Added

  • AriaEngine 缓存内存上限测试 — 9 个测试:LRU 上限约束、驱逐后全表/PK/索引查询完整性、UPDATE/DELETE 全量作用、写路径上限、命名空间隔离回归、多版本回归、tombstone 回归
  • LZ4 往返一致性测试 — 6 个往返测试(重复/文本/随机/边界长度/15 字节边界/长匹配)
  • CryptoManager 加解密测试 — 7 个测试(往返/错误密码/不同 salt/实例隔离/全局兼容层/4KB 页面)
  • React/Vue hooks 集成测试 — 16 个测试(jest.mock 零依赖:mount 执行、错误路径、refresh、注入防护、数据库初始化)
  • jest.setup.js WebCrypto polyfill — jsdom 环境补齐 crypto.subtle 支持 AES-GCM

[0.2.5] - 2026-07-29

Fixed

  • 版本号统一constants.ts VERSION 从 '0.2.0' 更新为 '0.2.5',修正 index.ts/utils.ts/CONTRIBUTING.md 中过时的版本注释和数据
  • AriaEngine OPFS 后端映射core.tsmode: 'aria' + diskEngine: 'opfs' 时实际使用 Memory 后端的 bug 已修复
  • _onError 接入执行路径query()/defineTable()/dropTable()/transaction()/importTable() 的 catch 路径现在调用 _onError 全局错误回调
  • maxRowsPerQuery 生效QueryExecutor 构造时接收 maxRowsPerQuery 参数,SELECT 结果在返回前截断
  • WAL full 模式真正同步WAL.append() 改为 asyncfull 模式下 await this.store.append() 真正等待写入完成,不再 fire-and-forget
  • PluginManager.install 传 db 实例register() 增加可选 db 参数,core.ts 初始化时传入 this,插件可获取 db 引用

Changed

  • SSTableReader 二分查找统一locateBlockGE/locateBlockLE 从线性扫描改为二分查找,rangeScan 性能在大型 SSTable 下不再退化
  • crypto 实例化 — 全局状态改为 CryptoManager 类,每个 AriaEngine 实例可拥有独立加密配置,保留全局函数向后兼容
  • compactLevelSync 接口公开化 — LSM 新增 public compactLevel() 方法,vacuum() 不再使用 as any 绕过 private 访问
  • WAL 大小阈值接入 checkpointCheckpointManager 接收 walSizeThreshold 参数,WAL 缓冲超阈值时自动触发 checkpoint

Added

  • ALTER TABLE 语法 — 支持 ALTER TABLE ... ADD COLUMN / DROP COLUMN(含可选 COLUMN 关键字)
  • TRUNCATE TABLE 语法 — 支持 TRUNCATE TABLE name 快速清空表数据
  • MVCC 接入读写路径 — 事务内 insert/update/delete 调用 mvcc.writeVersion/mvcc.deleteVersion,版本链作为 undo log
  • IndexedDB 索引利用IndexedDBEngine.find 等值查询时优先使用 IDB 索引(idx_col 命名约定),避免全量 getAll
  • SQL 注入防护 — React useTable / Vue useSqlarkTable 增加表名合法性校验(/^[a-zA-Z_][a-zA-Z0-9_]*$/

[0.2.4] - 2026-07-27

Added

  • 二级索引 — 每列可独立维护 LSM Tree 索引,$eq/$in/$gt/$lt 走索引 O(log n)
  • MVCC 接入引擎 — MVCCManager 正式投产,替换 ad-hoc txnSnapshot
  • MVCC 自动 GC — 每 10 次 checkpoint 自动回收过旧版本(保留最新 100 个)
  • Bloom Filter 序列化 — 写入 SSTable footer + 读取时加载 + 查询时 probe 快速否定
  • WAL 大小阈值walSizeThreshold 配置(默认 16MB),超阈值强制 checkpoint
  • 内存预算maxMemoryMB 配置(默认 64MB

Changed

  • WAL 默认同步模式walSyncMode'batch' 改为 'full',消除 crash 丢数据风险
  • 查询优化tryIndexLookup 扩展支持非 PK 列索引查找

[0.2.3] - 2026-07-27

Fixed

  • RB-Tree fixDelete 完整实现 — 补全标准红黑树删除修复,保证 O(log n)
  • LSM SSTable 缓存预热init() 预加载所有 SSTable,消除 cache miss
  • OPFS 数据恢复open() 自动从 OPFS 文件加载已有表数据到内存
  • Aria WAL 事务恢复 — 两阶段恢复:仅回放已提交事务,未提交数据不回放
  • LZ4 格式修复 — 重写 token 格式,消除中间字面量 token 歧义

[0.2.2] - 2026-07-27

Added

  • OPFS 自研存储后端 — AriaEngine 新增 OPFSBackend,纯浏览器文件系统 API,零 IndexedDB 依赖
    • 每个 key 对应一个二进制文件,存储在 navigator.storage.getDirectory()
    • 支持 read/write/delete/list/exists/clear 完整接口
    • 页面文件(4KB)、WAL 日志、Schema、SSTable 全部存储为独立文件
    • { dbName }/pg_1, { dbName }/__wal_0, { dbName }/__aria_schemas ...

Changed

  • AriaEngineConfig.storageBackend 已支持 'opfs'AriaEngine.open() 自动选择 OPFSBackend

[0.2.1] - 2026-07-27

Fixed

  • WAL CRC 校验验证 — 恢复时计算并验证 CRC32,损坏记录自动跳过并告警
  • Hybrid 引擎提交顺序 — commit 先写磁盘再写内存,磁盘失败回滚内存,消除数据不一致风险
  • RESTRICT 外键行为修正 — 检测到引用行时抛出 FOREIGN_KEY_VIOLATION,不再静默保留孤儿
  • SSTable rangeScan 边界保护 — 空索引或 startBlockIdx > endBlockIdx 时安全返回

Added

  • ColumnDef 约束激活maxLength/min/max 约束在 checkFieldType 中正式生效
  • 查询结果上限DatabaseConfig.maxRowsPerQuery(默认 0 不限制),防止超大结果集 OOM
  • onError 全局回调 — 配置中的 onError 回调实际接入 CRUD 异常路径
  • debug 调试模式DatabaseConfig.debug: true 输出 [MetonaSqlark:name] 前缀的结构化日志
  • 浏览器兼容性声明 — README 增加 Chrome 80+/Firefox 80+/Safari 14+/Edge 80+ 支持矩阵

Changed

  • MemoryEngine RESTRICT 外键行为:从"不级联保留孤儿"改为"禁止删除抛异常"
  • Hybrid 引擎 commitTransaction 磁盘优先于内存
  • DB_DEFAULTS 新增 maxRowsPerQuery: 0debug: false

[0.2.0] - 2026-07-27

Added

  • AriaEngine 自研存储引擎 — 基于 LSM-Tree 的页面式存储引擎,19 个新模块,~3500 行 TypeScript
    • 页面格式层 (page/): 4KB Slotted Page 编解码、Tuple 二进制序列化、CRC32 校验
    • Buffer Pool (buffer/): LRU 页面缓存 + 驱逐策略,可控内存占用
    • LSM-Tree 索引 (index/): MemTable (红黑树) + 多级 SSTable + Leveled Compaction
    • Bloom Filter (index/bloom.ts): FNV-1a + Murmur 双哈希,快速键否定判定
    • SSTable 构建器/读取器 (index/sstable_builder.ts, sstable.ts): 二分查找 + 范围扫描
    • Merge Iterator (index/merge_iterator.ts): 最小堆多路归并,去重保留最新值
    • WAL (wal/): 二进制日志格式 (LSN/type/txnId/table/key/json/CRC) + Checkpoint 管理
    • MVCC (transaction/mvcc.ts): 版本链 + 快照隔离 + GC
    • 存储后端 (store/): IndexedDB / Memory 双后端抽象
    • LZ4 压缩 (compression/lz4.ts): 简易页面级压缩
  • mode: 'aria' — 新增存储模式,可通过 MetonaSqlark.create({ mode: 'aria' }) 激活
  • Schema 持久化 — 表结构自动保存到 __aria_schemas,重启自动恢复
  • SSTable 元数据管理 — SSTable 索引信息持久化,启动时自动扫描加载
  • 事务感知 CRUD — insert/update/delete 在事务中缓冲到 snapshotcommit 批量写入 LSM
  • 244 个 AriaEngine 专项测试 — 覆盖生命周期/表管理/CRUD/事务/持久化/SQL 集成/页面格式/LSM/压缩

Changed

  • StorageMode 类型新增 'aria'
  • STORAGE_MODES 数组新增 'aria'
  • createEngine() 支持 mode: 'aria' 分支
  • 测试从 318 → 526,套件从 20 → 27
  • 新增 7 个模块级测试文件:aria-pagearia-indexaria-sstablearia-bufferaria-wal-mvccaria-compressaria
  • LRUList 修复 size 追踪 bug
  • WAL 存储改为按记录独立 key(避免拼接缓冲区越界)
  • CheckpointManager 解耦 BufferPool 依赖

Fixed

  • LZ4 压缩无限循环 — 字面量分支在发现匹配后回退导致 litLen=0 死循环,CI 卡死根因
  • SSTableReader.get() 自比较 bug — 参数 key 被循环变量遮蔽导致永远返回第一条
  • CheckpointManager 测试 null 引用 — 改为 Mock 对象避免 TypeError 导致进程无法退出
  • IndexedDB 持久化测试 — 替换为 Memory Backend 验证,消除 fake-indexeddb timer 堆积
  • LSM.flush() 不必要 setTimeout — 替换为 Promise.resolve(),消除额外 timer 延迟

[0.1.14] - 2026-07-26

Fixed

  • IndexedDB 事务原子性: flushToIDB 改为单 IDB 事务包裹 clear+insert,消除崩溃丢数据风险
  • 多标签页冲突: open() 添加 onversionchange 监听,其他标签页升级版本时自动关闭过期连接
  • Memory 引擎幂等: open() 重复调用安全无副作用
  • 关闭时清理 onversionchange 监听器,防止内存泄漏

[0.1.13] - 2026-07-26

Added

  • 事务回滚机制: IStorageEngine 新增 beginTransaction/commitTransaction/rollbackTransaction 接口
    • MemoryEngine: 快照式回滚(深拷贝 tables/schemas/indexes
    • IndexedDBEngine: 延迟写入策略(事务中仅写内存,commit 批量刷 IDB)
    • OPFSEngine: 快照式回滚(commit 批量写文件)
    • HybridEngine: 同时代理内存+磁盘引擎事务
  • 子查询支持: SQL Parser + Executor 支持 IN (SELECT ...)op (SELECT ...) 子查询
    • AST 新增 SubqueryExpression 类型
    • Parser 在 IN 和比较运算符后检测子查询
    • Executor 新增 resolveSubqueries() 递归解析,自动执行子查询并替换为具体值
  • 外键级联操作: ColumnDef 新增 onDelete/onUpdate 选项
    • 支持 CASCADE(递归级联删除)、SET NULLRESTRICT
    • SQL Parser 解析 REFERENCES table(col) ON DELETE CASCADE ON UPDATE CASCADE
    • MemoryEngine 内置 cascadeDelete() 递归级联逻辑
  • 连接池管理: MetonaSqlark.connect() 静态方法
    • 同名数据库复用已打开实例,引用计数管理
    • db.disconnect() 释放连接,归零自动 close
    • MetonaSqlark.disconnectAll() 强制关闭所有连接

Changed

  • TransactionManager.execute() 调用引擎层 begin/commit/rollback 实现真正原子性
  • IndexedDBEngine CRUD 事务感知:活跃事务中延迟 IDB 写入
  • ASTColumnDef / astColumnToColumnDef 支持 references/onDelete/onUpdate 透传

Fixed

  • IndexedDBEngine 事务中 find/count 从内存缓存读取(保证读到未提交变更)
  • SQL Parser parseColumnDef 循环条件扩展,避免 REFERENCES 语法解析中断

[0.1.12] - 2026-07-26

Added

  • MeSqlark 别名导出,与 MetonaSqlark 完全等价
  • metona-sqlark.cjs.js CommonJS 构建产物
  • 完善的 where-matcher.ts 消除 220+ 行重复代码
  • $col 列引用支持 JOIN ON 条件
  • $and/$or 嵌套支持(字段级 + 顶层)
  • $not 逻辑非操作符
  • $like 正则缓存加速
  • projectColumns 列投影支持 table.column 格式
  • DISTINCT 去重支持(O(n) 时间,列值拼接优化)
  • 测试覆盖率提升至 93.46%(264 测试/15 套件)

Changed

  • where-matcher 统一 MemoryEngine / IndexedDBEngine / Executor 的 WHERE 逻辑
  • JOIN ON 匹配支持 $col 语法
  • LIKE 正则改为缓存式编译
  • Executor DISTINCT 用列值拼接代替 JSON.stringify
  • JOIN 嵌套循环连接优化:避免 ON 时对象扩散

Fixed

  • IndexedDBEngine update/delete 方法正确同步内存缓存
  • OPFSEngine getTableNames 兼容 entries() 返回值
  • SQL Parser 正确处理字符串转义字符
  • SQL Parser 正确处理表别名(无 AS 关键字)
  • SQL Parser 解析 IS NULL / IS NOT NULL / NOT LIKE
  • peekTokenIs 方法正确暴露给语法分析
  • 多处边界条件空值/假值处理

[0.1.11] - 2026-07-25

Added

  • React 集成 hooks: useQuery, useTable, useDatabase
  • Vue 集成 composables: useSqlarkQuery, useSqlarkTable, useSqlarkDatabase
  • 发布订阅系统: subscribe(), emit()
  • 数据迁移系统: addMigration(), migrateTo()
  • 导入导出: exportTable(), exportAll(), importTable()
  • 完整 SQL 解析器: Lexer + Parser(递归下降)
  • SQL 支持: SELECT, INSERT, UPDATE, DELETE, CREATE TABLE, DROP TABLE
  • JOIN 支持: INNER, LEFT, RIGHT, CROSS
  • GROUP BY + HAVING + 聚合函数 (COUNT, SUM, AVG, MIN, MAX)
  • DISTINCT, ORDER BY, LIMIT, OFFSET
  • WHERE 条件: AND, OR, NOT, IN, LIKE, IS NULL
  • Query Builder 链式 API: select().where().orderBy().limit().execute()
  • 插件系统: 14 种生命周期钩子 + PluginManager
  • Metadata 系统: ColumnDef 完整约束 (type/primaryKey/required/unique/index/default/references/maxLength/min/max)

Changed

  • 架构重构为分层设计:Engine → QueryExecutor → Table/QueryBuilder → SQL Parser → Public API
  • AST 作为统一中间表示,SQL 和 QueryBuilder 行为完全一致
  • 测试框架完善:core / edge / groupby / join / query-system / sql / table / engine / transaction / plugin / hybrid 全覆盖

[0.0.1] - 2026-07-24

Added

  • 初始项目骨架
  • TypeScript 配置(严格模式)
  • Rollup 构建(UMD / ESM / CJS / min / .d.ts
  • Jest + jsdom 测试环境
  • ESLint + @typescript-eslint
  • Gitea Actions CI 工作流
  • 示例站点(index / demo / docs
  • 构建脚本 build.sh