diff --git a/CHANGELOG.md b/CHANGELOG.md index f9b86ec..1bf923b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,66 @@ All notable changes to MetonaSqlark will be documented in this file. +## [0.4.1] - 2026-08-08 + +### Added +- **AriaEngine 外键级联删除** — 对齐 Memory 引擎:`CASCADE`(递归多层)、`SET NULL`、`RESTRICT`(抛 `FOREIGN_KEY_VIOLATION`),含二级索引清理、WAL 记录、事务内可提交/回滚、环路保护 +- **`AriaEngine.clearAll()`** — 重置数据库(清空后端存储/LSM/WAL/MVCC/二级索引),实例可继续使用;供演示页刷新场景与 API 用户使用 +- **演示页引擎切换器** — 右上角 `⚡ Memory` / `🌲 Aria` 一键切换,Aria 模式每次加载 `clearAll()` 保证演示确定性;aria 预设文案更新(数据库即 Aria) + +### Changed +- 版本号升至 v0.4.1 +- 测试 880 → **894**(46 → 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,重启后列定义回退。新增引擎级 `alterTable`(`IStorageEngine` 可选接口):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 +- **流式查询 API** — `db.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 LAST** — `ORDER 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_TABLE` 而 `CREATE_TABLE` 无条件恢复 schema,删表后崩溃重启表+数据全部复活。v0.4.0 新增 `applyDropTableRecovery()` 回放删除 schema + 清除 LSM 残留 +- **WAL 恢复后不截断** — 每次重启重复回放 + WAL 无限膨胀。恢复完成将回放数据落盘后 `wal.checkpoint()` 截断 +- **MemoryEngine update 不维护索引(P0)** — `update()` 后唯一约束可被绕过、按新值索引查询丢行。`removeIndexEntries()` 在 update/delete/级联删除/SET NULL 前清理旧值索引 +- **Aria 事务内读不到自己写入的行** — `update/delete/count/clear` 的 `getAllRows()` 不合并 `txnSnapshot`,事务内先 insert 再 update/delete 失效;统一 `mergeTxnSnapshot()` +- **Aria TRUNCATE 不写 WAL** — `clear()` 崩溃恢复后丢失;事务内清空走快照删除标记,提交生效 +- **WAL 大小阈值在 full 模式永不触发** — `getBufferedBytes()` 跟踪真实字节数替代缓冲计数估算 +- **rollback 后二级索引残留** — 事务内直接写入索引 LSM,回滚后对受影响表全量重建索引 +- **Savepoint 回滚 MVCC 不一致** — `discardVersions()` 清理版本链保留事务登记 +- **PK 范围查询字符串算术 bug** — `$gt/$gte/$lt/$lte` 用 `Number(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 diff --git a/README.md b/README.md index c8a2268..98fb1cd 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # MetonaSqlark
-
+
-
-
+
+