fix(P0): v0.6.2 数据正确性专项 — 深度审计 6 项修复 + 22 回归
- KVStoreEngine 数值主键 update 丢行(P0):String 化主键回查不命中 → 误删 KV 行 (重启丢数据);改为单次全表扫描 + 受影响集合过滤(兼消 O(N×M) 回查开销) - update 主键撞已有主键静默覆盖(P0,Memory/Aria):抛 DUPLICATE_KEY,事务路径同拦截 - Aria 二级索引范围查询边界算法错误(P1):Number(v)±1 构造 key 漏小数/字符串数据; 改全索引扫描 + matchWhere 过滤,边界语义统一 - Aria 索引列 IS NULL 返回空(P1):null 等值/含 null 的 IN 不走索引(回退全表) - AriaEngine unique 约束未强制(P1):insert 整批预检(批内互查+索引扫描,失败整批 不落库)+ update 排除自身旧条目检查;新增 LSM.prefetchPrefixRanges 批量预加载 - 非主键 update 索引旧值残留:统一传旧行清理(消除唯一性误报与索引膨胀) - EXPLAIN 写语句产生真实副作用(P2):仅 SELECT 执行,UPDATE/DELETE 用 count 估算 测试 1092 → 1114(70 套件);行覆盖率 89.6%;版本 0.6.2
This commit is contained in:
@@ -294,15 +294,22 @@ export class KVStoreEngine implements IStorageEngine {
|
||||
deletes.push(...diff.deletes);
|
||||
}
|
||||
} else {
|
||||
// 增量重写受影响行
|
||||
for (const pk of affected) {
|
||||
const row = await this.memory.find(tableName, { table: tableName, where: { [pkCol]: pk } });
|
||||
if (row.length > 0) {
|
||||
puts[this.rowKey(tableName, pk)] = enc(JSON.stringify(row[0]));
|
||||
} else {
|
||||
deletes.push(this.rowKey(tableName, pk));
|
||||
// v0.6.2-fix(P0): 受影响主键经 String() 化后按 `where { [pkCol]: pk }` 回查内存行,
|
||||
// 数值型主键(123 !== "123")不命中 → 行被误判删除 → 重启丢数据。
|
||||
// 改为单次全表扫描 + 受影响集合过滤(同时消除此前 O(N×M) 逐主键回查开销)。
|
||||
const affectedSet = new Set(affected);
|
||||
const allRows = await this.memory.find(tableName, { table: tableName });
|
||||
for (const row of allRows) {
|
||||
const pkStr = String(row[pkCol]);
|
||||
if (affectedSet.has(pkStr)) {
|
||||
puts[this.rowKey(tableName, pkStr)] = enc(JSON.stringify(row));
|
||||
affectedSet.delete(pkStr);
|
||||
}
|
||||
}
|
||||
// 剩余主键(内存中已不存在,如被级联移除)→ 删除对应 KV 行
|
||||
for (const pk of affectedSet) {
|
||||
deletes.push(this.rowKey(tableName, pk));
|
||||
}
|
||||
// 级联影响表(SET NULL/CASCADE 外键)整表 diff
|
||||
for (const t of await this.affectedTables(tableName)) {
|
||||
if (t === tableName) continue;
|
||||
|
||||
Reference in New Issue
Block a user