Files
MetonaSqlark/tests
thzxx 2c945ee05a feat(B-5): 输出列序号 + 分隔标识符语义统一
两个相关缺陷:

① **输出列序号不被支持**(SQL 标准特性)
   `ORDER BY 1` / `GROUP BY 2` 此前直接 `PARSE_ERROR: Expected identifier, got "1"`
   (实测)。这在手写 SQL 与 UNION 里很常用 —— 各分支输出列名可能不同,只能按
   序号引用。序号含义依赖 SELECT 列表,故 parser 只保留数字文本,由 executor
   在拿到列表后解析。
   规则(与 SQLite/标准一致):裸数字在范围内 → 位置;越界 → QUERY_ERROR
   (不说"未知列",问题出在位置而非名字);`GROUP BY <序号>` 指向聚合表达式 →
   QUERY_ERROR(按聚合值分组语义上不成立);`ORDER BY 0` / `1.5` → PARSE_ERROR。

② **分隔标识符(`"1"`)在投影期被当成常量**
   `CREATE TABLE q ("1" STRING ...)` 后 `SELECT "1" FROM q` 返回 `{"1": 1}`
   (**字面量 1**),而 `SELECT *` 返回正确的 `{"1": "z"}` —— 同一列两种结论。
   根因:parser 把 `"1"` 的引号丢掉,投影期的"裸数字 = 常量列"子句就把它吃了。

修复的关键设计(一致性是这里唯一的难点,实测踩了四次漂移):
  **引号只在"解析 → 执行"的边界脱去,且必须在同一处、对同一批引用统一处理。**
  parser 必须保留引号才能区分"名为 1 的列"(`"1"`)与"常量 1"(`1`);
  而校验/取值/排序/分组必须用真实列名。此前"校验用裸名、投影用带引号名"两套规则
  导致 `1` 与 `"1"` 两个键并存。现在统一在 `normalizeUnprefixedReferences`
  与 `projectRow`/`projectColumns`/`resolveGroupKeyValue` 的对应分支脱引号,
  并新增可复用的 `unquoteIdentifier`。

同时修正 `projectColumns`(where-matcher,被四个引擎共用):此前"找不到列就不产出
键",现在支持分隔标识符与 `别名.列` 的唯一后缀匹配;"是否未知列"仍由 executor 的
`assertProjectionColumnsExist` 判定并报错,投影层不做静默兜底。

验证:新增 tests/v080-output-ordinals.test.ts(64 项:序号四引擎 40 + 分隔标识符
四引擎 24),覆盖 DESC/多键/LIMIT+OFFSET/UNION/别名共存/越界/非法序号/
WHERE 与 GROUP BY 引用分隔标识符/优先级。
全量 91 套件 / 1870 测试通过;typecheck、lint、build 零错误零告警;dist 已重建。
2026-09-15 07:39:03 +08:00
..