thzxx
841db2e049
fix(A22/A23/A25/A26/A27/A29/A30/A36): 查询层 8 项缺陷根治 + 单一语义收敛
...
每项都先用可执行探针复现出**错误的实际输出**,再修根因、补永久回归套件
(tests/v080-query-layer.test.ts,8 项 × 4 引擎 + 跨引擎项,共 111 断言)。
A22 GROUP BY 引用 SELECT 别名
修复前:`SELECT g AS grp, COUNT(*) FROM t GROUP BY grp` 抛
`COLUMN_NOT_FOUND Unknown column "g" in SELECT list` —— 错误信息与真正原因
(GROUP BY 用了别名)无关,因为 grp 取到 undefined 使全表并成一组,
投影阶段又发现 g 不在输出行里。
修复:GROUP BY 项先解析回**基列**(别名 → 源表达式)再分组,输出键与
"按基列分组"完全一致。
A23 HAVING 引用未出现在 SELECT 里的聚合
修复前:`SELECT g FROM t GROUP BY g HAVING SUM(n) > 25` → `[]`
(SUM(n) 从未被求值 → HAVING 的键在分组行里不存在 → UNKNOWN)。
修复:需要计算的聚合 = SELECT 列 ∪ HAVING 中的聚合(并集),
并在 HAVING **之后**才把行收缩为 SELECT 输出键(否则又变回 [];
顺序错了会双向出错:先投影 → 空结果,不投影 → 泄漏内部聚合列)。
A25 带表前缀的聚合参数恒 0
修复前:`COUNT(t.n)` → 0、`SUM(t.n)` → null(行键是 n,直接取 row['t.n']
得 undefined 再被"过滤 NULL"剔除,**且不报错**)。
修复:新增唯一列引用解析 resolveColumnValue(前缀剥离 → 精确 → 唯一后缀),
聚合识别统一为 parseAggregateExpression —— 此前"是否聚合"与"如何求值"
用两条不同的正则。取不到列改为抛 COLUMN_NOT_FOUND,不再静默计 0。
A26 UNION 尾部 ORDER BY/LIMIT 归属错误
修复前:`A UNION B ORDER BY id DESC` 只排 B;`... LIMIT 3` 返回 4 行
(parser 把子句挂在右侧 SELECT 上,AST 没有复合查询级字段)。
修复:SelectUnionStatement 增加 orderBy/limit/offset,parser 把子句**上移**
(移动而非复制,否则 LIMIT 应用两次),executor 在合并+去重后统一排序/切片。
A27 DISTINCT 作用在投影前
修复前:`SELECT DISTINCT g AS d FROM t` 返回 4 行 a,a,b,b
(对 {id,g,n} 原始行去重),而 `SELECT DISTINCT g` 返回 2 行。
修复:DISTINCT 移到投影后(作用于输出列);ORDER BY 的应用时机随之拆成
"引用输出列 → 投影后" / "引用非输出列 → 投影前",两者互为因果必须一起改。
A29 maxRowsPerQuery 静默截断写入
修复前:maxRowsPerQuery=2 时 `INSERT INTO dst SELECT id FROM src`(4 行源)
只写 2 行并报成功 —— 不是"限制查询规模"而是**静默丢数据**。
修复:行源不截断(executeSelect 增加 purpose='source'),写路径显式报错。
A30 INSERT 值多于目标列静默丢弃
修复前:`INSERT INTO t (id,g) VALUES ('9','z','LOST')` 报成功、'LOST' 消失。
修复:显式列名时解析期拦截(PARSE_ERROR),未给列名时 executor 对照 schema
拦截(VALIDATION_ERROR)——两种情况都需要,因为前者无需 schema。
A36 派生表别名引用
修复前:`SELECT d.id FROM (SELECT id, g FROM t) AS d` 返回 `[]`,
而同义的 `SELECT id FROM (...) AS d` 正确。
修复:抽出 normalizeUnprefixedReferences(WHERE/ORDER BY/GROUP BY/SELECT
四类引用统一剥离别名前缀),非 JOIN 单表路径与派生表路径共用同一规则。
连带根治(修复过程中发现的两个更底层问题):
1. **同步抛错穿过 async 边界**:`executor.execute()` 里 `return this.executeXxx(stmt)`
的同步前导段若抛错(arity/校验),异常成为**同步抛出** ——
`await expect(db.query(...)).rejects...` 的断言不生效、`.catch()` 永不执行。
现统一包一层 try/catch,保证任何错误都是 rejected promise。
2. **缺列的行形状不一致**:validateRow 此前"值为 undefined 就不落键",
于是 `INSERT INTO t (id,g) VALUES ('9','z')` 的行里没有 n 键 →
`SELECT id,g,n FROM t` 抛 COLUMN_NOT_FOUND: n,而 `SELECT * FROM t` 正常。
现在缺列且无 default → 显式补 null(SQL 语义),行始终含全部 schema 列;
ALTER ADD 同步在已有行上物化 null,使"内存视图"与"重启后视图"一致。
验证:全量 83 套件 / 1589 测试通过(含 Aria 5 万行索引竞态、KVStore 持久化);
typecheck(src+tests) 与 lint 零错误。
2026-09-15 00:00:08 +08:00
thzxx
4ab04df882
fix(A15): 三值逻辑求值器统一 —— 消除 WHERE 的第二套语义(静默错值根治)
...
背景(PLAN-v0.7.5.md 根因 1/7、缺陷 A15):
项目里 `matchWhere`(布尔版,自带 matchField)与三值求值器并存。同一条 SQL 的
语义取决于走哪个函数,实测三类静默错值:
- `WHERE n = NULL` 命中 NULL 行、`WHERE n != NULL` 返回所有非 NULL 行;
- `WHERE s NOT LIKE 'x'` 会把 NULL 行判真(布尔取反);
- `WHERE n NOT BETWEEN 1 AND 2` 恒空集 —— parser 生成的字段级
`{ n: { $or: [ {$lt:1}, {$gt:2} ] } }` 递归进了 where 子句级求值器,
子项 `{ $lt: 1 }` 被当成"查询列 `$lt`" → 每行 UNKNOWN。
根治方式(不是打补丁,而是取消第二套实现):
1. where-matcher.ts 重写为**唯一一个递归求值器**,同时理解 where 子句级
(键是列名/逻辑连接词)与操作符级(键是 `$gt` …),位置由上下文承载而非
由另一个函数承载;`matchWhere` 退化为"三值结果是否恰为 TRUE"。
行上下文随求值上下文下传,`$col` 在任意嵌套深度都能解析。
2. parser:`IS NULL` / `IS NOT NULL` 生成 `$isNull` / `$isNotNull` **谓词**
(此前与 `= NULL` / `!= NULL` 共用 `$eq: null` / `$ne: null`,两者语义无法区分);
`BETWEEN` 生成真正的范围条件(此前把同一对象同时当操作符对象与操作数);
`NOT BETWEEN` 展开为 `$or: [{$lt}, {$gt}]`。
3. executor 新增 enginePreFilter:逐行求值谓词(`$col` / `$exists` / CASE 键)
必须整体移出引擎层 —— 引擎无外层行上下文,会把它们判 UNKNOWN 并把**所有行**
过滤掉,逐行求值再正确也无行可算。粒度按连接词决定:`$and` 成员可单独移除,
`$or`/`$not` 成员一移除就改变结果集(漏行/多行),故整条下推放弃。
契约变更(旧测试编码了错误语义,已按 SQL 标准改正并注明理由):
- a) `{ $eq: null }` 不再命中 NULL 行(`= NULL` 恒 UNKNOWN)→ 用 `$isNull`;
- b) `IN` 列表含 NULL:`x IN (NULL, 'a')` 只命中 'a'(`null = NULL` 为 UNKNOWN),
未命中的行仍因 UNKNOWN 不保留。
- c) 引擎层 `$in: [null, ...]` 与 `$eq: null` 的断言同步修正。
验证:
- 新增 tests/v080-sql-three-valued.test.ts:26 条 SQL 语义矩阵 × 4 引擎
(memory/disk/hybrid/aria)+ UPDATE/DELETE 写路径,共 104 断言;
- 全量 83 套件 / 1458 测试通过(含 Aria 生产负载 10 万行);
- typecheck(src+tests) 与 lint 零错误。
2026-09-14 23:22:26 +08:00
thzxx
752bdea97d
fix(A5/A6/A7/A16/A18/A24/A28): 查询层语义根治 —— LIMIT 双重应用、未知列静默、聚合崩溃与空集语义
...
A6 LIMIT/OFFSET 被应用两次(丢行)
compileSelect 无条件下推 limit/offset,引擎切一次,executeSelect 末尾又切一次。
实测 4 行表:LIMIT 2 OFFSET 1 只返回 1 行;LIMIT 10 OFFSET 3 返回空。
JOIN/派生表路径因不走 plan 反而正确,同一 executor 内自相矛盾。
现引入 analyzeSelect() 统一判定 limitPushdownSafe,两条路径互斥且只应用一次。
实测 10 种查询形态(含 DISTINCT/GROUP BY/别名/深 OFFSET/LIMIT 0)全部正确。
A7 queryStream 与 query 结果不一致(四类静默分歧)
core.ts 自己重写了一套能否走引擎快路径/如何投影的规则,与 executor 各写一份:
SELECT id AS x FROM t query [{x}],stream [{id,v}](全列+原列名)
SELECT t.id FROM t query [{id}],stream [{}](空对象)
LIMIT 2 OFFSET 1 query 1 行,stream 2 行
LIMIT 0 query 0 行,stream 1 行(Aria 又是 0 行)
且流式路径不触发 beforeQuery/afterQuery、不受 maxRowsPerQuery 约束。
现由 executor.analyzeSelect() 做单一事实来源;不可流式一律回退物化路径;
列引用剥离主表别名前缀;LIMIT 0 短路;回调返回 Promise 时显式报错
(此前用 constructor.name === 'AsyncFunction' 判定,普通函数返回 Promise 会静默丢弃)。
实测 16 种查询形态 × 4 引擎 = 64 组,query 与 queryStream 逐值相等。
A5 MIN/MAX 栈溢出(崩溃)
Math.min(...arr) 展开实参:20 万行同组直接 RangeError。改为单次遍历归约 reduceNumeric。
A24 空集聚合语义
SUM/AVG/MIN/MAX 对空集返回 0(使 和为 0 与 无数据 不可区分),改为 SQL 标准的 NULL;
COUNT 仍返回 0。
A18 未知列静默产出空对象行
SELECT bogus FROM t 返回 [{},{},...](行数对、内容空、无报错);SELECT NAME(列名 name)
同样静默空。新增 assertProjectionColumnsExist:投影前校验列存在性,
未知列抛 COLUMN_NOT_FOUND;同一后缀命中多个表别名时抛歧义错误。空结果集不误报。
A16 INSERT 列/值个数不校验(静默丢弃/写半行)
显式列名时在解析期校验每行值个数与列数一致:
INSERT INTO t (id,name) VALUES ('4','z',9) 此前静默丢弃 9,现报错。
A28 UPDATE SET __proto__ 静默吞掉
sets['__proto__'] 触发原型 setter,sets 变空对象,既不写入也不被未知列预检看到,
表现为返回成功但什么都没发生。parser 的列名映射统一改为 Object.create(null)。
附带修复(A18 验证时发现):
SELECT 1 AS one FROM t 返回 [{}] —— parseColumnRef 的数字分支提前 return 吞掉别名;
裸 SELECT 1 同样产出空对象 —— 投影未处理匿名常量列。现按 SQLite 语义以表达式原文为键。
2026-09-14 21:19:49 +08:00
thzxx
7526951804
fix(A19/A20/A21/A2): 词法器标准化 —— 双引号标识符、未闭合注释报错、去递归、移除方言转义
...
A19 双引号是分隔标识符(SQL 标准)
此前 '"' 与 "'" 一起交给 readString,于是 SELECT "name" FROM t 静默产出
一个名为 'name' 的**常量列**(行数正确、值全错、无报错),且被 lexer.test.ts
钉死为期望。新增 TokenType.QUOTED_IDENTIFIER;双引号内 "" 表示一个双引号;
可用于引用保留字列名(SELECT "order" FROM t)。parser 的 expectIdentifier
显式接受 QUOTED_IDENTIFIER。
A20 未闭合块注释必须报错
此前 skipBlockComment 循环到 EOF 就返回、不抛错,实测
db.query("DELETE FROM t WHERE id = '4' /*") **真的删掉了 1 行**;
SQLite 会报 unterminated comment。任何被截断/拼接的 SQL 都会静默改变语义。
A21 注释跳过改为循环(消除递归爆栈)
此前 default 分支用 return this.nextToken() 递归,栈深 = 连续注释数,
两万个连续块注释直接 RangeError(原生错误,调用方无法按 code 分类)。
A2 移除 MySQL 方言的反斜杠转义
此前只识别反斜杠+单引号:双反斜杠不解转义,以反斜杠结尾的 Windows 路径会
吞掉闭引号并报出与输入无关的解析错误。更严重的是参数绑定器只翻倍单引号、
不处理反斜杠 —— 两份词法规则不一致,"参数不改变 SQL 结构"在文本层不成立。
SQL 标准中反斜杠是普通字符,移除后词法器与绑定器的字符串边界判定完全一致。
更新 2 处把旧行为钉死的测试(lexer.test 双引号=字符串、v033 反斜杠转义)。
2026-09-14 21:08:20 +08:00
thzxx
83c5aa0b9d
fix(A11): AND 优先级高于 OR(SQL 标准)—— parser 条件表达式三层分层
...
此前 parseCondition 对 AND/OR 做纯左折叠:
a = 1 OR a = 2 AND b = 3 → (a = 1 OR a = 2) AND b = 3 (错,返回 1 行)
标准语义:a = 1 OR (a = 2 AND b = 3) (对,返回 3 行)
任何"权限条件 OR 业务条件 AND 软删标记"的写法都会静默返回错误行集。
改为标准文法分层:parseCondition → parseOrExpression → parseAndExpression
→ parseSimpleCondition(NOT 已在内层处理,结合性正确)。
仅在确有多个操作数时才包 $and/$or,避免产生 {$and:[x]} 冗余节点而破坏
既有 AST 契约与下游索引下推识别。
实测:WHERE a = 1 OR a = 2 AND b = 3 现返回 [1,2,4](此前 [2,4]),
与显式括号写法结果一致。parser/SQL 全部 111 用例通过。
2026-09-14 21:03:58 +08:00
thzxx
50468b9b0e
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
2026-08-14 22:53:46 +08:00
thzxx
cbe407eb49
fix: v0.7.1 API 修复与防御统一 — static create / 未 open 防护 / 原型污染 / lint 清零
...
CI / test (22.x) (push) Successful in 17m24s
CI / e2e (push) Successful in 10m39s
CI / test (18.x) (push) Successful in 19m5s
CI / test (20.x) (push) Successful in 18m4s
CI / test (24.x) (push) Successful in 23m19s
- MetonaSqlark.create 静态工厂(README 示例在 ESM/Node 下此前 TypeError),
独立 create 函数委托静态实现
- close 未初始化防御(engine undefined 不再崩溃)
- AriaEngine hasTable/getTableNames/getTableSchema 统一 ensureOpen
- 事务回滚失败不掩盖原始错误
- __proto__ 列名防护:Executor 列映射 Object.create(null) + schema 校验拒绝
- lint 清零(移除 5 处未使用导入)
测试 1147 → 1155(73 套件);行覆盖率 89.8%;版本 0.7.1
2026-08-13 11:14:42 +08:00
thzxx
334067d89e
release: v0.5.1 — 存储后端生产级硬化(CRC-32/全库加密/WAL分片/页面化存储/多标签页锁/e2e)+ 深度审查修复(假实现接线/死代码清理)
CI / test (18.x) (push) Successful in 10m10s
CI / test (20.x) (push) Successful in 10m10s
CI / test (22.x) (push) Successful in 10m6s
CI / e2e (push) Successful in 9m51s
CI / test (24.x) (push) Successful in 10m28s
2026-08-10 12:07:00 +08:00
thzxx
a1e4f5071c
release: v0.4.1 — Aria 级联/ALTER/clearAll + 流式查询/派生表 + 正确性加固
...
CI / test (20.x) (push) Successful in 10m4s
CI / test (22.x) (push) Successful in 10m8s
CI / test (24.x) (push) Successful in 9m55s
CI / test (18.x) (push) Successful in 10m9s
新增:
- AriaEngine 外键级联(CASCADE/SET NULL/RESTRICT)+ clearAll() 重置 API
- 引擎级 alterTable:Aria DROP COLUMN 重写存储行 + schema 持久化
- 流式查询 queryStream / findStream(LSM 惰性扫描不物化)
- FROM 派生表 / 多列 ON 哈希连接 / COUNT(DISTINCT) / NULLS FIRST/LAST
- 普通列别名 + ORDER BY 别名 + 无表查询 + 字符串常量列
- 演示页引擎切换器(Memory/Aria)+ 预设自动重置
修复:
- Aria WAL DROP_TABLE 崩溃恢复(删表复活)+ 恢复后 WAL 截断
- Memory update/delete 索引维护(unique 约束绕过)
- 关联 EXISTS 绑定失效 / HAVING 标量子查询 / INSERT SELECT 位置错位
- 裸布尔列条件(WHERE done / CASE WHEN done)
- Aria $in 重复行 / JOIN 主表 WHERE 下推 / DROP INDEX 报错
- ORDER BY/GROUP BY/SELECT 表前缀列 + SQL '' 标准转义
质量:894 测试 · 47 套件 · 81.5% 覆盖率
2026-08-08 13:36:34 +08:00
thzxx
d544501e1c
release: v0.3.2 — 质量加固 + SQL扩展 + 表达式 + 并发同步
...
CI / test (18.x) (push) Failing after 5m11s
CI / test (20.x) (push) Failing after 5m8s
CI / test (22.x) (push) Successful in 9m58s
CI / test (24.x) (push) Successful in 9m56s
v0.2.6 质量加固:
- 修复 AriaEngine 二级索引 SSTable 互相覆盖(命名空间隔离)
- 修复 LSM 多版本读取顺序错误 + MergeIterator 取最新来源
- 重写 LZ4 压缩器(往返一致性 + 缓冲区溢出)
- sstableCache LRU 上限 + 预加载兜底(BufferPool 配置生效)
- 修复 React/Vue 集成 import type 运行时 bug + exports 子路径
- 新增 38 个测试(LZ4往返/Crypto/集成), 删除伪测试
v0.3.0 SQL 功能扩展:
- 多语句 parseAll + 事务语句 BEGIN/COMMIT/ROLLBACK
- INSERT INTO ... SELECT + UNION/UNION ALL + EXISTS 关联子查询
- CREATE/DROP INDEX 五引擎实现 + 别名 WHERE 修复
- benchmark 页面 + 36 个新测试
v0.3.1 表达式与性能:
- CASE WHEN 表达式(SELECT 列/WHERE/聚合)
- JOIN + 关联子查询逐行绑定
- WAL 批量组提交(写放大 O(N)→O(1))
- 修复 pending frozen 可见性 + flush 缓存竞争
v0.3.2 并发:
- CASE WHEN 用于 WHERE/聚合 + JOIN 哈希连接
- 多标签页同步(multiTabSync + BroadcastChannel)
- IndexedDB schema 持久化(reopen 后表结构恢复)
- 修复 where-matcher 顶层 $not
- 修复 CJS 产物 .js 被 ESM 解析(exports 空) — .cjs 后缀 + exports 修正
- 836 测试 / 44 套件 / 81.0% 覆盖率
2026-08-08 10:41:30 +08:00
thzxx
eb79b2198e
release: v0.2.5 — 质量加固 + Bug修复 + 性能优化 + SQL扩展
CI / test (18.x) (push) Successful in 10m4s
CI / test (20.x) (push) Successful in 10m0s
CI / test (22.x) (push) Successful in 9m58s
CI / test (24.x) (push) Successful in 9m58s
2026-07-29 21:50:53 +08:00
thzxx
648526c188
feat: SQL 语法支持 CREATE TABLE IF NOT EXISTS
...
CI / test (20.x) (push) Canceled after 0s
CI / test (22.x) (push) Canceled after 0s
CI / test (24.x) (push) Canceled after 0s
CI / test (18.x) (push) Canceled after 10s
- Parser 支持 CREATE TABLE IF NOT EXISTS 语法
- AST CreateTableStatement 新增 ifNotExists 字段
- Executor 表已存在时静默跳过
- 修复 demo.html Aria 预设报错
2026-07-27 17:18:52 +08:00
thzxx
1f8e11bf72
feat: SQL 语法支持 BETWEEN AND、DROP TABLE IF EXISTS
CI / test (18.x) (push) Successful in 9m56s
CI / test (20.x) (push) Successful in 9m54s
CI / test (22.x) (push) Successful in 9m50s
CI / test (24.x) (push) Successful in 9m49s
2026-07-26 17:55:45 +08:00
thzxx
71e238a5cc
test: v0.1.13 新增42项测试覆盖(事务回滚/子查询/外键级联/连接池)
CI / test (18.x) (push) Successful in 9m52s
CI / test (20.x) (push) Successful in 9m50s
CI / test (22.x) (push) Successful in 9m51s
CI / test (24.x) (push) Successful in 9m49s
2026-07-26 16:40:11 +08:00
thzxx
0f128da34a
feat: v0.1.13 — 事务回滚 + 子查询 + 外键级联 + 连接池
CI / test (18.x) (push) Successful in 9m54s
CI / test (20.x) (push) Successful in 9m52s
CI / test (22.x) (push) Successful in 9m54s
CI / test (24.x) (push) Successful in 9m47s
2026-07-26 16:31:15 +08:00
thzxx
e2a590c5b1
feat: metona-sqlark v0.1.12 — 前端TypeScript关系型数据库
...
- 4种存储引擎:Memory / IndexedDB / OPFS / Hybrid
- 完整SQL支持:SELECT/INSERT/UPDATE/DELETE/JOIN/GROUP BY/HAVING/DISTINCT
- Query Builder链式API + TypeScript泛型支持
- 聚合函数:COUNT/SUM/AVG/MIN/MAX
- 事务、插件系统(14 hooks)、发布订阅、数据迁移、导入导出
- React/Vue框架集成
- 264个测试用例,93.46%覆盖率
- 零运行时依赖
2026-07-26 15:00:01 +08:00