Skip to content

ADR-0053 D-A3:共享矩阵的 driver-sql 消费者被钉死在 SQLite —— server-timezone 轴与 D-A3 自己写的「Postgres at minimum」都从未执行过 #4245

Description

@os-zhuang

#4191 收尾时核实 server-timezone 轴现状发现。ADR 里那句话点了两条轴,storage-form 已由 #4205 / #4223 / cloud#938 / cloud#942 落地,server-timezone 这条的现状比「只是隐含在 CI 配置里」更差。

D-A3's conformance matrix should gain a storage-form axis (canonical, legacy-epoch, legacy-naive) and a server-timezone axis, since both are now known to have produced dialect-divergent row results.
—— docs/adr/0053-date-and-datetime-semantics.md,D-B3 的 Consequences

现状(已逐条核对,不是推测)

packages/plugins/driver-sql/src/sql-driver-temporal-conformance.test.ts 是共享矩阵的 driver-sql 消费者。它的三个 describe 全部硬编码 client: 'better-sqlite3':63:112:155),也从不读 OS_TEST_POSTGRES_URL / OS_TEST_MYSQL_URL。真正连 live server 的只有三个专用套件:

  • sql-driver-datetime-postgres-timezone.test.ts
  • sql-driver-time-live-dialects.test.ts
  • sql-driver-date-now-default-live.test.ts

它们断言的是各自的存储形态 / NOW() 默认值 / 往返精度不是 TEMPORAL_CASES / TEMPORAL_TIME_CASES 的行 id 集合。

于是逐轴现状是:

矩阵是否执行
进程时区偏斜(job 的 TZ=America/New_York ✅ 覆盖——整包在该 TZ 下跑,矩阵也在内
服务器时区(PG Asia/Shanghai、MySQL +08:00,job 已provision) ❌ 矩阵根本没连服务器
driver 轴(D-A3 原文 driver {SQLite, Postgres at minimum} ❌ 对矩阵不成立

附带:一处 declared ≠ enforced

该文件头注写着:

Runs against a real SQLite, and — through the Temporal Conformance (live PG + MySQL) CI job, which runs this package's whole suite under TZ=America/New_York against servers on Asia/Shanghaiagainst real Postgres and MySQL too, with no workflow change needed.

前半句成立(整包确实在该 job 下跑、进程时区确实偏斜),后半句对本文件不成立:矩阵里的 driver 永远是 SQLite。这与 #4191 记的 mongodb-temporal.ts canon 表同species——文档写对了、代码没实现,直到有人去执行它才可见。

为什么这格值得补,而不只是记账

服务器时区已知造成过方言分歧的行结果,这正是 ADR 点名它的原因:

A bare YYYY-MM-DD comparand (what a {30_days_ago} token expands to) meant midnight in the SERVER's timezone, so the identical query over the identical instant put a row on a different calendar day than it did on SQLite.
—— D-B2,对 PG 16 @ Asia/Shanghai 的实测

D-B2 的修复(comparand 规则方言无关、两侧都写明 Z)有回归锁,但那是它自己的夹具。矩阵的意义恰恰是「一个后端漂了就有一条具名用例红」——而 PG/MySQL 在矩阵里没有格子,所以这个信号对它们不存在。这与 #4081 follow-up 的教训逐字相同:NativeSQLStrategy 曾在矩阵的后端表里被列名却没有消费者。

建议形状

  1. 把矩阵消费者按 driver 参数化,而不是新写一份。三个 live 套件已有现成范式:OS_TEST_*_URL 存在则跑、缺失则 skip,并断言服务器非 UTC 以防空转。让 sql-driver-temporal-conformance.test.ts[sqlite, postgres?, mysql?] 各跑一遍同一批 TEMPORAL_CASES / TEMPORAL_TIME_CASES行 id 集合逐格相同
  2. server-timezone 成为显式轴:断言「服务器时区 ≠ 进程时区 ≠ UTC」三方错开确实生效(job 已经 provision 了 Asia/Shanghai / +08:00America/New_York,缺的是矩阵去消费它并在非空转守卫下断言)。
  3. 修正头注,让它描述代码实际做的事——或者在第 1 步之后它自然成立。
  4. 顺带值得考虑:driver-turso(cloud)local 模式同样只跑 SQLite,但它是 SQLite-only 驱动,不适用;driver-sqlite-wasm 同理。这一格只对 driver-sql 有意义。

验收

同一批用例在 SQLite / Postgres / MySQL 上返回完全相同的行 id 集合,且三者的服务器时区与进程时区互不相同、非空转守卫生效。任何一格不同即该方言的 comparand 或存储路径与共识有偏差。

关联

#4191(storage-form 轴,已落地)、#4081(D-A3 本体)、#4205#4223#4220、cloud#938、cloud#942;ADR-0053 D-A3 / D-A3.1 / D-B2 / D-B3 / D-B4 / D-C3。

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions