fix: v0.7.3 数据正确性与边界窗口收尾 — INSERT 语句级原子(三引擎+Aria PK 批内重复)/ 索引列 IS NULL 恒空 / delete RESTRICT 破坏索引 / queryStream 子查询静默空结果 / ALTER DROP 索引残留 / UNIQUE INDEX 存量校验 / SELECT * 别名投影 / WAL BEGIN/ROLLBACK 事务边界 / aria $in 与级联重复扫描性能 / $and 等值下推 / ANALYZE 索引统计 / React-Vue hooks 生命周期 / 迁移主键兜底 + 58 回归
CI / test (18.x) (push) Successful in 17m43s
CI / test (22.x) (push) Successful in 13m45s
CI / test (20.x) (push) Successful in 15m33s
CI / test (24.x) (push) Successful in 24m46s
CI / e2e (push) Successful in 52s

This commit is contained in:
thzxx
2026-08-14 22:53:46 +08:00
parent f8f8d1b2ff
commit 50468b9b0e
29 changed files with 2374 additions and 650 deletions
+88
View File
@@ -2,6 +2,94 @@
All notable changes to MetonaSqlark will be documented in this file.
## [0.7.3] - 2026-08-14
### INSERT 语句级原子性补全 / 索引一致性 / 流式查询回退安全
> 深度审计第六阶段:修复 INSERT 语句级部分提交(三引擎 + Aria PK 批内重复)、
> 索引列 IS NULL 恒空、delete RESTRICT 预检破坏索引、queryStream 子查询静默空结果
> 等 10 项问题,四引擎语义对齐。
### Fixed
- **INSERT 语句级部分提交(P1Memory/KVStore/Hybrid** — 第 N 行主键重复/唯一冲突
抛错时前 N-1 行已提交(v0.7.2 只修了 UPDATEINSERT 漏修)。改为两阶段:
先全量预检(主键批内 Set 互查 + 索引查 + 唯一批内互查),任何一行失败整句不执行
- **Aria insert 批内主键重复部分提交(P1)** — PK 重复检查在写入循环内:第 N 行
重复抛错时前 N-1 行已 put LSM 且其 WAL 记录随 appendBatch 一起丢失 → 部分提交 +
内存/WAL 不一致。PK 检查移入批预检阶段(与 v0.6.2 的 unique 预检同一阶段)
- **索引列 IS NULL 恒空(P1Memory/KVStore/Hybrid** — `tryIndexLookup`
`colIndex.get(null)` 恒 undefined → `return []` 短路全表扫描(AriaEngine v0.6.2
已修,Memory 漏修)。null/undefined 条件跳过索引路径回退全表扫描
- **delete RESTRICT 预检破坏索引(P1Memory/KVStore/Hybrid** — `removeIndexEntries`
在收集阶段(RESTRICT 预检前)执行:预检抛错时行未删但索引条目已删 → 唯一约束
永久失效 + 索引查询丢行。索引清理移到预检通过之后(与 AriaEngine 对齐)
- **queryStream 子查询静默空结果(P1** — WHERE 含 `$subquery`/`$exists`/`$col`
未回退物化:引擎层 matchWhere 的 `$in` 遇未解析的 `$subquery` 对象返回 false →
所有行被静默过滤(`$col` 会抛 QUERY_ERROR)。streamable 判定增加递归检测,
回退物化路径(resolveSubqueries 正确解析)
- **ALTER DROP 索引列残留(P2Memory/KVStore** — 删列只删 schema.columns
indexes Map 中该列条目残留 → 查询已删列走旧索引(不含新行)→ 结果不完整。
DROP 时同步清理索引 Map(对齐 AriaEngine cleanupTableIndexes
- **CREATE UNIQUE INDEX 存量重复数据静默成功(P2)** — 回填不校验存量唯一性
SQLite 语义应报错)。Memory/Aria 回填时检查重复 → 抛 `UNIQUE_VIOLATION`
失败路径清理半初始化索引(标志未落、索引 Map/LSM 移除),保持原子语义
- **`SELECT *, col AS alias` 解析与投影(P3** — 此前 parser 的 '*' 独占分支使
`SELECT *, name AS nick` 直接 PARSE_ERRORexecutor 侧 columns[0]==='*' 不投影。
parser 支持 '*' 后接列列表;projectRow 以原行全部列为基、其余表达式覆盖/追加
- **INSERT hooks 行键错位(P3** — SQL 省略列名时 beforeInsert/afterInsert 收到
数字键行(与 executor 写入的 schema 列名行不一致)。hooks 行映射对齐 executor
(省略列名时按 schema 列顺序)
- **KVStoreEngine insert 持久化原始行(P3)** — 非事务路径 `JSON.stringify(row)`
写入参原始对象:default 值不落盘、schema 外列被持久化。改为持久化内存中
validated 行(MemoryEngine 新增 `getRow()`O(rows) 无性能回退)
### Changed
- 测试 1198 → **1256**75 套件,+58 个 v0.7.3 回归);新增 `tests/v073-fixes.test.ts`
IS NULL ×6 / RESTRICT 索引 ×3 / INSERT 原子 ×11 / queryStream ×4 / ALTER DROP ×2 /
UNIQUE INDEX ×4 / SELECT * ×3 / hooks ×2 / KVStore validated ×3 / getRow ×1 /
WAL 失败窗口 ×2 / aria $in ×2 / $and 下推 ×4 / 常量转义 ×3 / ANALYZE ×2
+ React config 重建 ×1 + Vue 卸载 ×2 + 迁移无主键 ×2
- 行覆盖率 89.8% → **90.0%**
### Fixed(审计第二阶段:边界窗口 / 性能 / 生态收尾)
- **WAL BEGIN 写失败事务泄漏(P3** — full 模式 BEGIN 记录写失败时 currentTxnId 已设置
→ TX_ACTIVE 永久泄漏(后续无法开始新事务)。失败回滚 mvcc 登记与快照后重抛,
调用方可重试
- **WAL ROLLBACK 顺序(P3** — 内存先回滚、ROLLBACK 记录后写:full 模式写失败时
崩溃重放无 ROLLBACK 记录 → 已回滚事务的数据复活。改为先持久化 ROLLBACK 再回滚内存
(与 commitTransaction 的"WAL 领先内存"对齐):写失败 → 事务仍活跃可重试,
崩溃重放看到 ROLLBACK 记录不复活数据
- **aria $in 逐值 drainChain 性能悬崖(P2** — 索引 `$in` 逐值 indexScanToRows
每个值一次 prefetchRange + prefetchKeys(各一次 drainChain 排空后台链),
compaction 长耗时时 N 倍放大(与 v0.6.1 修的 insert 批量预加载同类)。
批级预加载全部值索引范围 + 主表行各一次,循环内同步 rangeScan/get
- **update 主键变更级联重复全表扫描(P2 性能)** — applyUpdateCascade /
applyForeignKeyUpdateRules 的阶段 1 RESTRICT 扫描与两阶段预检
checkUpdateRestrict / checkForeignKeyUpdateRestrict)完全重复,删除冗余扫描
- **多条件 AND 永不走索引(P2)** — `WHERE a AND b` 解析为顶层 $andMemory/Aria
的 tryIndexLookup 只查顶层键 → 永远全表扫描,索引形同虚设。递归展开 $and 等值
条件下推($or/$not 保守跳过,命中后全条件 matchWhere 过滤,子集语义安全);
EXPLAIN usingIndex 同步递归识别 $and 嵌套(与引擎行为对齐)
- **ANALYZE 统计不含二级索引(P3** — indexDepth/sstableCount/memtableSize 只统计
主 LSM,多索引表严重低估。汇总该表全部二级索引 LSM
- **Vue useSqlarkDatabase 卸载不 closeP3)** — 组件卸载后实例永不关闭(连接/锁/
后端句柄泄漏)。onUnmounted 时 close(失败不阻塞卸载)
- **React useDatabase config 变更不生效(P3** — initRef 只建一次,配置更新永不
重建且旧实例残留。以 config 序列化指纹为依赖:变更时 cleanup 关闭旧实例再重建
close 幂等,未完成 init 亦可安全关闭)
- **migrateFromIndexedDB 无 id 列旧库迁移中断(P3)** — 推断 schema 无主键 →
createSchema 抛 SCHEMA_ERROR 中断整个迁移。第一个非 json 列兜底为主键;
全 json 列无可用主键 → 跳过该表(skippedTables)不中断
- **SELECT 常量列 SQL 标准 '' 转义未还原(P3** — `SELECT 'O''Brien'` 输出
`O''Brien`projectRow 只处理反斜杠转义)。`''` 还原为 `'`(常量列与别名常量两处)
- **死代码清理** — 移除 AriaEngine.getWALEstimatedSizeCheckpointManager 自带
真实缓冲字节估算,此方法无调用者)
---
## [0.7.2] - 2026-08-13
### 语句级原子性 / 事务 DDL 语义统一 / 约束与绑定硬化