docs: PLAN 附录 F 记录 A9/A10/A15 完成情况与三值逻辑根治细节
This commit is contained in:
+55
-7
@@ -710,10 +710,56 @@ connection-manager/integrations/migration)两份完整报告,以及测试质
|
||||
| `074afd3` | **A8 + LSM 读自洽** | 行所有权写进 `IStorageEngine` 契约(读返副本/写收副本)+ `cloneRow`;Memory/KVStore/Hybrid/Aria 全部生效(改返回值不再改库、索引不再失配);**LSM 读取自洽**:未命中即回源 + CRC 校验,消除"缓存未命中 = 静默丢数据"(实测小缓存下 300 行只回 59 行);缓存上限语义修正(超大 SSTable 常驻) |
|
||||
| `89243ef` | **A3/A4 SAVEPOINT** | 新增 `SAVEPOINT_ROLLBACK` WAL 记录(复用 `data` 字段,格式不变);恢复按**事务内**下标窗口重放 → 已回滚的行不再复活;事务结束清空 savepoint 并校验归属 → 陈旧 savepoint 不再静默吞写入 |
|
||||
| `765df80` | **A1/A2 约束** | UPDATE 阶段 1 增加批内新主键互查(四引擎)→ 不再静默丢行;ALTER ADD UNIQUE 真正建索引并做存量校验(复用 `createIndex`),Memory 侧"重启静默丢行"随之消失 |
|
||||
| `674da6b` | **A9/A10** | `ChangeNotifierEngine` 引擎装饰器:INSERT/UPDATE/DELETE/CLEAR/DDL 统一派发变更事件,`db.subscribe` 对本地写入生效(此前只在跨标签页广播时触发);关联子查询的 `$col` 绑定补上外层别名剥离与**递归进入子查询**(`WHERE id IN (SELECT ... WHERE o.user_id = u.id)` 此前静默空结果) |
|
||||
| `4ab04df` | **A15 三值逻辑(PB-2)** | 见下节"三值逻辑根治" |
|
||||
|
||||
**测试规模**:1304 → **1334 通过 / 79 套件**;新增 `tests/v080-savepoint.test.ts`、`tests/v080-atomicity.test.ts`、`tests/version-contract.test.ts`、`tests/helpers/storage-harness.test.ts`。
|
||||
### 三值逻辑根治(A15 / PB-2,提交 `4ab04df`)
|
||||
|
||||
**验证状态**:`typecheck`(src + tests)、`lint` 全部为零错误。
|
||||
**根因**:`matchWhere`(布尔版,自带 `matchField`)与三值求值器**并存**,同一条 SQL
|
||||
的语义取决于走哪个函数;更严重的是字段级 `$or` 递归进了"where 子句级"求值器。
|
||||
|
||||
**三类实测静默错值**(修复前的实际返回):
|
||||
|
||||
| SQL | 修复前 | 修复后 | 机制 |
|
||||
|---|---|---|---|
|
||||
| `WHERE n = NULL`(n 列含 NULL 行) | 命中 NULL 行 | 空集 | `===` 比较 `null === null` 为真 |
|
||||
| `WHERE n != NULL` | 所有非 NULL 行 | 空集 | 同上取反 |
|
||||
| `WHERE s NOT LIKE 'x'`(含 NULL 行) | NULL 行被判真 | 仅真正不匹配的行 | 布尔取反未传播 UNKNOWN |
|
||||
| `WHERE n NOT BETWEEN 1 AND 2` | **恒空集** | 真正的越界行 | 字段级 `$or` 子项 `{ $lt: 1 }` 被当成"查询列 `$lt`" |
|
||||
|
||||
**根治方式(取消第二套实现,而非打补丁)**:
|
||||
|
||||
1. `query/where-matcher.ts` 重写为**唯一一个递归求值器**,同时理解 where 子句级
|
||||
(键是列名 / 逻辑连接词)与操作符级(键是 `$gt` 之类)。区别由**位置**承载,
|
||||
不再由另一个函数承载;行上下文随求值上下文下传,`$col` 在任意嵌套深度都能解析。
|
||||
`matchWhere` 退化为"三值结果是否恰为 TRUE",并新增 `matchWhereThreeValued`。
|
||||
2. `sql/parser.ts`:`IS NULL` / `IS NOT NULL` 生成 `$isNull` / `$isNotNull` **谓词**
|
||||
(此前与 `= NULL` / `!= NULL` 共用 `$eq: null` / `$ne: null`,调用方无法表达区别);
|
||||
`BETWEEN` 生成真正的范围条件(此前把同一对象同时当操作符对象与操作数);
|
||||
`NOT BETWEEN` 展开为 `$or: [{ $lt }, { $gt }]`。
|
||||
3. `query/executor.ts` 新增 `enginePreFilter`:逐行求值谓词(`$col` / `$exists` /
|
||||
CASE 键)必须整体移出引擎层 —— 引擎无外层行上下文,会把它们判 UNKNOWN 并把
|
||||
**所有行**过滤掉,逐行求值再正确也无行可算(这正是 `WHERE t.x = t.y` 静默空结果的
|
||||
第二层原因)。粒度按连接词决定:`$and` 成员可单独移除;`$or` / `$not` 成员一旦
|
||||
移除就改变结果集(漏行 / 多行),故整条放弃下推。
|
||||
|
||||
**契约变更**(旧测试编码了错误语义,已按 SQL 标准改正并在测试内注明理由):
|
||||
|
||||
- `{ $eq: null }` 不再命中 NULL 行(`= NULL` 恒 UNKNOWN)→ 需 NULL 匹配时用 `$isNull`;
|
||||
- `IN` 列表含 NULL:`x IN (NULL, 'a')` 只命中 `'a'`(`null = NULL` 为 UNKNOWN),
|
||||
未命中的非 NULL 行因 UNKNOWN 同样不保留;
|
||||
- 引擎层(`MemoryEngine.find` / `AriaEngine.find`)与 Query Builder 路径同步适用。
|
||||
|
||||
**验证**:新增 `tests/v080-sql-three-valued.test.ts` —— 26 条 SQL 语义矩阵 ×
|
||||
4 引擎(memory/disk/hybrid/aria)+ UPDATE/DELETE 写路径,共 **104 断言**。
|
||||
|
||||
**测试规模**:1304 → **1458 通过 / 83 套件**;新增 `tests/v080-savepoint.test.ts`、
|
||||
`tests/v080-atomicity.test.ts`、`tests/v080-subscribe.test.ts`、
|
||||
`tests/v080-correlated.test.ts`、`tests/v080-sql-three-valued.test.ts`、
|
||||
`tests/version-contract.test.ts`、`tests/helpers/storage-harness.test.ts`。
|
||||
|
||||
**验证状态**:`typecheck`(src + tests)、`lint` 全部为零错误;Aria 生产负载
|
||||
(10 万行 / OPFS / KV 后端崩溃恢复)全绿。
|
||||
|
||||
### 实施过程中的额外发现(方案未列出,已一并修复)
|
||||
|
||||
@@ -728,11 +774,11 @@ connection-manager/integrations/migration)两份完整报告,以及测试质
|
||||
|
||||
| 优先级 | 项目 | 状态 |
|
||||
|---|---|---|
|
||||
| P0 | A9 `subscribe()` 对本地写入不触发 | 待做 |
|
||||
| P0 | A10 `t.x = t.y` 与关联 `IN (SELECT ...)` 静默空结果 | 待做 |
|
||||
| ~~P0~~ | ~~A9 `subscribe()` 对本地写入不触发~~ | ✅ `674da6b` |
|
||||
| ~~P0~~ | ~~A10 `t.x = t.y` 与关联 `IN (SELECT ...)` 静默空结果~~ | ✅ `674da6b` + `4ab04df`(enginePreFilter) |
|
||||
| ~~P1~~ | ~~A15 三值逻辑(PB-2 统一 `sqlCompare`)~~ | ✅ `4ab04df` |
|
||||
| P1 | A12 `maxLength`/`min`/`max` 在 memory/disk/hybrid 失效(PB-1 统一校验) | 待做 |
|
||||
| P1 | A13/A14 自引用外键失效、ON UPDATE CASCADE 只一层 | 待做 |
|
||||
| P1 | A15 三值逻辑(PB-2 统一 `sqlEquals/sqlCompare`) | 待做 |
|
||||
| P1 | A17 INSERT 未知列静默丢弃 | 待做 |
|
||||
| P1 | A22/A23/A25/A26/A27/A29 查询层剩余项 | 待做 |
|
||||
| P1 | A35/A36/A37 JOIN NULL 键、派生表别名、未限定列名 | 待做 |
|
||||
@@ -740,7 +786,9 @@ connection-manager/integrations/migration)两份完整报告,以及测试质
|
||||
| P1 | A40/A41 Aria `close()` 活跃事务守卫、`dropTable` DDL 原子性 | 待做 |
|
||||
| P0 | **PB-6 存储单一提交点(manifest)** | 待做 |
|
||||
| — | PC-2 e2e 真崩溃注入(CDP `Page.crash`) | 待做 |
|
||||
| — | PC-4/PC-5 契约测试 + 属性测试 | 部分(跨引擎参数化已用于 A1/A2) |
|
||||
| — | PC-4/PC-5 契约测试 + 属性测试 | 部分(跨引擎参数化已用于 A1/A2/A15) |
|
||||
| — | PG 文档与站点全量同步 | 待做 |
|
||||
|
||||
**下一步建议**:A9/A10(静默错误结果,P0)→ PB-1/PB-2(统一校验与三值逻辑,一次消除 A12/A15/A35 整类)→ PB-4(统一表达式求值,消除 A10 与"未解析即 false"整类)→ 存储层 A38~A41 → PB-6 → PC-2 → PG。
|
||||
**下一步建议**:PB-1 统一校验 choke point(一次消除 A12/A17 与 A1 剩余分支)→
|
||||
A35/A36/A37(JOIN 与派生表,同属"未限定/未解析即静默"一整类)→ 查询层剩余
|
||||
A22~A29 → 存储层 A38~A41 → PB-6 → PC-2 → PG。
|
||||
|
||||
Reference in New Issue
Block a user