Skip to content

refactor(driver-sql)!: analyzeQuery / findWithWindowFunctions 收窄 query 签名 (#6212 批 A+E) - #6355

Merged
os-zhuang merged 3 commits into
mainfrom
claude/issue-6212-batch-a-e-sql-query-signatures
Aug 7, 2026
Merged

refactor(driver-sql)!: analyzeQuery / findWithWindowFunctions 收窄 query 签名 (#6212 批 A+E)#6355
os-zhuang merged 3 commits into
mainfrom
claude/issue-6212-batch-a-e-sql-query-signatures

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Part of #6212 —— 只做 批 A + 批 E(外加实测判定为零冲突的 批 F)。⛔ 不碰 aggregate(批 B,等 #6203/PR #6312)、不碰 memory/mongodb(批 C)、不碰 distinct(已立单 #6320)、不碰 packages/spec

基线取合并后的最新 origin/main85ec26d#5234/PR #6296sql-driver.ts 改动已在其中,本分支在其之上;文本合并干净,合并后全套复验见下)。

前提复核(PM 测量棒的结论仍成立)

论断 复核结果
两处仍是 any sql-driver.tsfindWithWindowFunctions / analyzeQuery 收窄前均为 query: any
explain 本来就是 DriverQuery、一行转发 ✅ 转发前这一对自相矛盾:契约门声明 AST,其实现声明 any
analyzeQuery 只读 fields/where/orderBy/limit/offset ✅ 全在 DriverQuery
批 A 实测零报错、零 fixture 改动 ✅ driver-sql + driver-sqlite-wasm 两个包 typecheck 全绿,测试 fixture 一行未动
windowFunctionsretiredKey() 墓碑 ⇒ 不能标 DriverQuery ✅ 见下「为什么必须 Omit
两道门非测试生产者为零 ✅ 全仓 13 个 driver-sql 依赖包中,findWithWindowFunctions / analyzeQuery / buildWindowFunction 零命中

改了什么

批 A —— analyzeQuery(object, query: DriverQuery, options?) 纯注解。它是 explain() 的实现体,而 explain() 早已声明 DriverQuery 并转发过来。

批 E —— findWithWindowFunctions(object, query: SqlWindowFunctionQuery, options?),按 PM 裁决 B 在 driver-sql 本地声明扁平形,并从 @objectstack/driver-sql 导出(嵌入方要能叫出这个类型的名字——#4286 把这道门写成了已发布的迁移处方):

export interface SqlWindowFunctionSpec {
  function: string;                                          // 无参发射:`rank` -> `RANK()`
  alias: string;
  partitionBy?: string[];
  orderBy?: { field: string; order?: 'asc' | 'desc' }[];     // 门读 `s.order || 'asc'`,故 order 可省
}
export type SqlWindowFunctionQuery = Omit< DriverQuery, 'windowFunctions' > & {
  windowFunctions?: SqlWindowFunctionSpec[];
};

成员以实测方法体为准:buildWindowFunction 只读 function / partitionBy / orderBywf.alias 只在 as ?? 绑定里读。这与 spec 删除注记和 migrations/registry.ts 迁移处方里逐字写的 { function, alias, partitionBy?, orderBy? } 完全一致——三处必须始终说同一句话。buildWindowFunction(spec: any) 一并跟进为 SqlWindowFunctionSpec

为什么必须 Omit 而不是直接交集。 query.windowFunctionsretiredKey()z.never().optional()),QueryAST['windowFunctions'] 解析为 undefined;写成 DriverQuery & { windowFunctions?: SqlWindowFunctionSpec[] } 会把数组与 undefined 求交,属性变成不可写——这道门自己文档里的载荷会静默地编译不过,而报错点离类型声明十万八千里。这个坑本身被一条 pin 钉住了(pins WHY the type Omit s the tombstoned key):谁把 Omit 「简化」掉,红在测试里而不是红在文档里。

⛔ 不进 spec,⛔ 不退役。 类型放在能力真正所属的驱动层,是接着 #4286 的判断往下走(那次删 WindowFunctionNodeSchema 的理由正是它声明了 field/over/frame 这些门从不读的成员);退役该方法要撤回 6 处已发布文档的迁移处方,是维护者的取舍,PM 已记录该选项仍在桌上、但不在本单。请求面的墓碑没有被重新打开——analyzeQuery('o', { windowFunctions: [...] }) 依然是编译错误,有 pin 为证。

批 F(顺带,实测后判定并入)@objectstack/verifyBucketableDriver.aggregatequery: unknown 收到 DriverQuery,并删掉该文件两处 AST 字面量里多写一遍的 object 键。同时删掉一处 as never——它只是因为字面量推断把 'count' 放宽成了 string,注上类型就不需要了。这不预断批 B 对驱动自身 aggregate 参数类型的选择:方法参数按双变比较,驱动那边声明 anyQueryAST 还是收窄后的类型,都照样满足这个替身。(实测:改动 9 行、verify typecheck 全绿、删掉而非新增逃逸口,故判定「零冲突且改动微小」。)

反向验证(方向先判后跑)

两个方向都是 red,都先预测后执行:

  1. 给门传门不认的键 ⇒ 应红(在 driver-sqlite-wasm 上做,因为它才是门禁盲区所在)。临时把 spec 已删词汇塞进 wasm 的 window fixture、并给 analyzeQuery 传一个拼错的键:
src/sqlite-wasm-driver-advanced.test.ts(404,13): error TS2353: Object literal may only specify known
  properties, and 'field' does not exist in type 'SqlWindowFunctionSpec'.
src/sqlite-wasm-driver-advanced.test.ts(411,45): error TS2353: Object literal may only specify known
  properties, and 'sortBy' does not exist in type 'DriverQuery'.

同时证明了盲区 1 已覆盖:driver-sqlite-wasm 读的确实是 driver-sql 重建后的 dist/*.d.ts,不是假绿。已还原。

  1. 把两处签名改回 any ⇒ 新 pin 应红
src/sql-driver-query-signature.test.ts(110,23): error TS2322: Type 'string' is not assignable to type 'never'.
src/sql-driver-query-signature.test.ts(134,7): error TS2578: Unused '@ts-expect-error' directive.
src/sql-driver-query-signature.test.ts(144,9): error TS2578: Unused '@ts-expect-error' directive.
src/sql-driver-query-signature.test.ts(191,13): error TS2322: Type '"dropped"' is not assignable to type 'never'.

这里有一处值得写下来:DropsObject 这个既有工具顺带也抓 any——keyof anystring | number | symbol,所以 'object' extends keyof any 为真,DropsObject< any > 解析为 never。一条 pin 同时挡住「把 object 加回来」和「把参数放宽回 any 」两种回退,不需要再写第二条。

验收判定标准

「让门自己文档里的载荷编译得过」是判定标准而非可选项,所以它是一条 pin,载荷逐字抄自 content/docs/data-modeling/queries.mdx

const documented: SqlWindowFunctionQuery = {
  windowFunctions: [
    { function: 'rank', alias: 'salary_rank', partitionBy: ['department'],
      orderBy: [{ field: 'salary', order: 'desc' }] },
  ],
};

三个门禁盲区

  1. driver-sqlite-wasm 读 driver-sql 的 dist/*.d.ts ⇒ 已点名进验证面:先 build driver-sql 再 typecheck 它,并按上面的反向验证证明它确实读到了新 d.ts(不是假绿)。
  2. erasure 棘轮双向pnpm check:query-options-erasure 合并前后各跑一次,267 未动(本批既不新增也不删除 as any):
✓ query-options-erasure ratchet holds: 83 unswept non-test site(s) in 19 file(s), none new.
  test surface: 267 site(s) in 51 file(s) — at the ceiling
  baseline key set verified against 85ec26d: no files added.
  1. 全仓 pnpm check:type-check-debt re-measure34 ledger entr(ies) re-measured in 214.9s, 2024 raw tsc error(s) total, none above its recorded number. 本 PR 触及的三个包均不在台账内,只减不增成立。

另跑:check:nul-bytes OK(6000 个文件),check:driver-conformance OK,check:engine-double-contract OK,check:published-files OK,check:empty-changeset OK(1 declaring changeset added),改动文件 eslint 零输出,并按字节纪律自查了控制字符(零命中)。

测试

合并 origin/main(15 个提交,含对 sql-driver.ts 的改动)后重跑:

packages/drivers/driver-sql         Test Files 68 passed | 4 skipped   Tests 953 passed | 46 skipped
packages/drivers/driver-sqlite-wasm Test Files 18 passed               Tests 254 passed
packages/drivers/driver-turso       Test Files 25 passed               Tests 804 passed
packages/verify                     Test Files  4 passed               Tests  17 passed

@objectstack/dogfood 520 passed(含 date-bucket-parity-conformance,即批 F 改动后的 checkDateBucketParity 对真实 SqlDriver / SqliteWasmDriver 的行为证明)。typecheck:driver-sql / driver-sqlite-wasm / driver-turso / verify 四个包全绿。

零运行时改动:全部是类型注解,加上两处冗余 object 键的删除——全仓驱动实测无一读 query.object

消费半径

按规则枚举了 @objectstack/driver-sql 的 13 个依赖包(rest / runtime / service-datasource / service-automation / dogfood / cli / driver-sqlite-wasm / driver-turso / trigger-record-change / plugin-auth / plugin-approvals / app-showcase),grep 三个符号零命中;两道门只有各自驱动包的测试在用。changeset 标 major,依据与 #5181 / #6075 一致:源码级破坏性(调用点内联字面量与 BucketableDriver 的导出形状),运行时零变化,且 check:api-surface 不记录签名,所以 changeset 是该变更唯一的下游载体。

顺带发现(已另立单,不在本 PR 修)


🤖 Generated with Claude Code

https://claude.ai/code/session_01WyvqvKMG6asi9aXjKE6xtx


Generated by Claude Code

claude added 2 commits August 7, 2026 14:32
… a query type (#6212 batch A+E)

`IDataDriver`'s six query methods were narrowed to `DriverQuery` by #5181 and
followed through on five drivers by #6075. The SQL driver's OWN query doors —
not on the contract, therefore never in scope of either — kept `query: any`.

`any` there is not "the object name goes unchecked", it is every check off:
`where`'s filter dialect, `orderBy`'s sort-node shape, `limit`/`offset` being
numbers. Those are exactly the members both bodies read.

- `analyzeQuery` -> `DriverQuery`. It is `explain()`'s implementation and
  `explain()` already declared `DriverQuery` and forwarded here, so the pair was
  self-inconsistent. Pure annotation: zero errors and zero fixture changes in
  driver-sql and driver-sqlite-wasm.
- `findWithWindowFunctions` -> `SqlWindowFunctionQuery`, a driver-local flat
  type (exported, with `SqlWindowFunctionSpec`). It cannot take `DriverQuery`:
  `query.windowFunctions` is a `retiredKey()` tombstone since #4286, so
  `QueryAST['windowFunctions']` is `undefined` and this door's own published
  payload would stop compiling. The type is `Omit`-ed, not intersected, for
  exactly that reason, and a pin holds that trap still. It stays OUT of
  `packages/spec` deliberately: #4286 removed `WindowFunctionNodeSchema` because
  it declared `field`/`over`/`frame` members this door never reads, and the
  door's flat shape is quoted verbatim by the spec's removal note and the
  published migration prescription.
- `buildWindowFunction(spec: any)` follows to `SqlWindowFunctionSpec`.

Also (#6212 batch F): `@objectstack/verify`'s `BucketableDriver.aggregate`
declares `DriverQuery` instead of `unknown`. It is a PUBLISHED structural double
an out-of-tree driver implements, and `unknown` let the file's own two aggregate
literals each repeat the object name argument one carries. Drops one `as never`
that only existed because an inferred literal widened `'count'` to `string`. It
does not presume batch B's choice for the drivers' own `aggregate` parameter —
method parameters compare bivariantly either way.

Zero runtime change: type annotations plus two redundant keys removed (no driver
reads `query.object`).

Part of #6212

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WyvqvKMG6asi9aXjKE6xtx
@vercel

vercel Bot commented Aug 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 7, 2026 3:04pm

Request Review

@github-actions github-actions Bot added the size/m label Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/driver-sql, @objectstack/verify.

10 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/data-modeling/drivers.mdx (via @objectstack/driver-sql)
  • content/docs/getting-started/glossary.mdx (via @objectstack/driver-sql)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/driver-sql)
  • content/docs/plugins/anatomy.mdx (via @objectstack/driver-sql)
  • content/docs/plugins/packages.mdx (via @objectstack/driver-sql)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/driver-sql)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/driver-sql)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/driver-sql)
  • content/docs/releases/implementation-status.mdx (via @objectstack/driver-sql, @objectstack/verify)
  • content/docs/releases/v15.mdx (via @objectstack/verify)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants