test(driver-sql): LIKE-escape 守卫接入 live PG + MySQL 方言矩阵,补字面反斜杠用例 (#5589) - #5655
Conversation
… matrix, plus a literal-backslash case (#5589) `SqlDriver.applyLike`'s own TSDoc grades an unescaped `%` a P0 filter bypass, but its regression guard hard-coded `client: 'better-sqlite3'` — so the P0 was verified on exactly one of the three shipped dialects, and on the end of the range with the least to say (SQLite honours no default escape character at all, which is why the explicit bound `ESCAPE` exists). The matrix and the CI job were already here: `live-dialect-matrix.testkit.ts` is the shared D-A3 cell list, and `Temporal Conformance (live PG + MySQL)` already runs this whole package against Postgres 16 and MySQL 8.0. The LIKE family was simply 0 of the 8 files reading `OS_TEST_POSTGRES_URL` / `OS_TEST_MYSQL_URL`. This wires it in — no new CI job. - the three existing cases (literal `%`, literal `_`, ordinary substring) now also run once per `DIALECT_CELLS` cell, over an issue-prefixed table; - a fourth case pins the escape character itself (literal `\`), which is where MySQL's "ESCAPE must evaluate as a constant at execution time" and its string-literal backslash doubling stop being inferences and get executed; - a fixture-integrity assertion first, so a backslash eaten on the way IN cannot be misread as an `applyLike` verdict; - the hard-coded SQLite guard is KEPT: it is the one that runs on every `pnpm test` with nothing to provision. The matrix extends it, not replaces it. An unprovisioned cell is a named skip and a red under `OS_EXPECT_LIVE_DIALECT_MATRIX=1`, via the testkit's `declareUnprovisionedCell`. Test-only; no runtime change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WyvqvKMG6asi9aXjKE6xtx
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckNo hand-written docs reference the 0 changed package(s). ✅ |
|
CI 红旗说明(无需修复动作,#5562 同款):失败的 Generated by Claude Code |
合并前提已满足:live PG 16 + MySQL 8.0 作业绿,且新 cell 确实跑到了
关键证据不是「作业绿」,是用例计数。作业日志里本文件那一行: 对账:
driver-sql 那一条腿的整体:
因此,issue 里两条「未验证」当中的第一条已被验证MySQL 侧那条文档约束 —— 第二条(knex + mysql2 是否可能走服务端预处理 日志阅读范围(如实)作业日志约 449k 字符、单行结构,我只做了定向 grep( Generated by Claude Code Generated by Claude Code |
Fixes #5589
Test-only。无任何运行时改动,
sql-driver.ts一个字节都没动。前提复核(issue 是线索不是规格)
issue 写于 #4645 Phase A 之前,引用的是旧路径。基于当前
origin/main(7bf3d1ce2)重核,结论:前提全部成立,只有路径与行号偏移。applyLike在packages/plugins/driver-sql/src/sql-driver.ts~:6276packages/drivers/driver-sql/src/sql-driver.ts:6292(Phase A 已 MERGED,06ba03627);注释里的 "a filter-bypass, P0" 与绑定的ESCAPE ?与描述一致sql-driver-like-escape.test.ts硬编码单一方言client: 'better-sqlite3',connection: { filename: ':memory:' }$contains/LIKE/startsWith/endsWith的:0 个Temporal Conformance (live PG + MySQL)已跑整个 driver-sql 套件并注入两个 URL +OS_EXPECT_LIVE_DIALECT_MATRIX=1.github/workflows/ci.yml:378起;测试步骤跑pnpm --filter @objectstack/driver-sql test)所以缺的确实不是基础设施,只是没接线。不新增 CI 作业。
改了什么
单文件:
packages/drivers/driver-sql/src/sql-driver-like-escape.test.ts。pnpm test、不需要 provision 任何东西」路径上唯一必跑的那份;矩阵是它的扩展不是替代。%/ 字面_/ 普通子串)按DIALECT_CELLS逐 cell 重跑,表名带 issue 前缀os5589_like_escape(live cell 与本包其它套件共库,裸表名会把撞名读成过滤器旁路回归)。\—— 转义符自身。这是 MySQL 那两条约束从「推断」变成「实测」的地方:手册要求ESCAPE实参 "must evaluate as a constant at execution time",以及 MySQL 在字符串字面量里应用 C 转义语法(那一个反斜杠要挺过 mysql2 的客户端转义 + 服务端 lexer 才轮得到 LIKE 看见)。C:\logs存进去是一个反斜杠。进库路上被吃掉的反斜杠会让\用例因为跟applyLike毫无关系的原因变红;更糟的是被加倍的那种,会让一个已经停止转义的构建因为错误的理由变绿。declareUnprovisionedCell—— 具名 skip,在OS_EXPECT_LIVE_DIALECT_MATRIX=1下转具名红。新用例自动落在这个语义里。D-B3 服务器时区轴不引入:这里没有任何时间量,要求非 UTC 服务器只会制造与 LIKE 无关的红。与
sql-driver-or-filter.test.ts/ 分页矩阵同一判断。反向验证:预测方向 = 实测方向,但 issue 的那句话被证伪
先定方向后跑:预测「删掉转义字符类里的
\\那一支 → 新的字面\用例在 SQLite cell 上变红,其余三个保持绿」。实测完全命中(
applyLike的/[\\%_]/g临时改成/[%_]/g):两处需要如实纠正,都已写进代码注释而不是留给下一个读者去踩:
applyLike绑的是显式ESCAPE,三方言被放进同一条规则,所以这一格的算术在进程内就可判定,SQLite 能前红后绿。live cell 真正独占的不是算术,是传输:绑定参数经 mysql2 插值 / PG 协议之后,那一个反斜杠和ESCAPE实参是否还成立。注释里把这个区分写死了,免得把 SQLite cell 读成装饰品。[],不是我原先注释里写的['pct']。 因为 pattern 退化成%\%时,contains形状结尾的那个通配符被反斜杠吃掉了,语义变成「任意内容 + 结尾一个字面%」,本 fixture 无行命中。注释已按实测改写。本地能证明什么、不能证明什么
Tests 8 passed | 2 skipped (10)—— sqlite cell 4 个用例 + fixture 断言全绿,两个 live cell 具名 skipOS_EXPECT_LIVE_DIALECT_MATRIX=1且缺 URLTests 2 failed | 8 passed—— 两个 live cell 转具名红,消息点名OS_TEST_POSTGRES_URL/OS_TEST_MYSQL_URL与 ADR-0053 D-A3Test Files 63 passed | 4 skipped (67)/Tests 860 passed | 46 skipped (906)pnpm --filter @objectstack/driver-sql typechecktsc --noEmit无输出)eslint --no-inline-config变更文件check:query-options-erasuretest surface: 267 site(s) ... at the ceiling,未涨check:nul-bytes(含--self-test)+ 变更文件控制字节自扫check:adr-anchors两个 live 方言的真实执行只会发生在 CI 的
Temporal Conformance (live PG + MySQL)作业里。该作业绿是本 PR 的合并前提。若该作业在这两个新 cell 上变红,那个失败输出就是本单最有价值的产出(很可能坐实 issue 第 2 点的 MySQL
ESCAPE约束问题)—— 届时按 Prime Directive #10 另立单,⛔ 不在本 PR 里改实现。未验证
execute()路径(issue 自己也把这条列为未验证)。本 PR 只让当前路径被执行,不对该路径的选择做任何断言。$notContains/$startsWith/$endsWith三个 shape 未接入矩阵:它们与$contains共用同一个applyLike转义表达式,shape 只决定%的位置。按 issue + 分诊的范围面(三个既有用例 + 一个\)收口,不扩。标签
test-only,请挂
skip-changeset(本 agent 无标签写权限,PM 代挂)。Generated by Claude Code