refactor(driver-sql)!: analyzeQuery / findWithWindowFunctions 收窄 query 签名 (#6212 批 A+E) - #6355
Merged
Merged
Conversation
… 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
…ch-a-e-sql-query-signatures
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 10 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
August 7, 2026 14:59
os-zhuang
enabled auto-merge
August 7, 2026 14:59
This was referenced Aug 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #6212 —— 只做 批 A + 批 E(外加实测判定为零冲突的 批 F)。⛔ 不碰
aggregate(批 B,等 #6203/PR #6312)、不碰 memory/mongodb(批 C)、不碰distinct(已立单 #6320)、不碰packages/spec。基线取合并后的最新
origin/main(85ec26d,#5234/PR #6296 的sql-driver.ts改动已在其中,本分支在其之上;文本合并干净,合并后全套复验见下)。前提复核(PM 测量棒的结论仍成立)
anysql-driver.ts的findWithWindowFunctions/analyzeQuery收窄前均为query: anyexplain本来就是DriverQuery、一行转发anyanalyzeQuery只读fields/where/orderBy/limit/offsetDriverQuery内windowFunctions是retiredKey()墓碑 ⇒ 不能标DriverQueryOmit」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 把这道门写成了已发布的迁移处方):成员以实测方法体为准:
buildWindowFunction只读function/partitionBy/orderBy,wf.alias只在as ??绑定里读。这与 spec 删除注记和migrations/registry.ts迁移处方里逐字写的{ function, alias, partitionBy?, orderBy? }完全一致——三处必须始终说同一句话。buildWindowFunction(spec: any)一并跟进为SqlWindowFunctionSpec。为什么必须
Omit而不是直接交集。query.windowFunctions是retiredKey()(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/verify的BucketableDriver.aggregate从query: unknown收到DriverQuery,并删掉该文件两处 AST 字面量里多写一遍的object键。同时删掉一处as never——它只是因为字面量推断把'count'放宽成了string,注上类型就不需要了。这不预断批 B 对驱动自身aggregate参数类型的选择:方法参数按双变比较,驱动那边声明any、QueryAST还是收窄后的类型,都照样满足这个替身。(实测:改动 9 行、verifytypecheck 全绿、删掉而非新增逃逸口,故判定「零冲突且改动微小」。)反向验证(方向先判后跑)
两个方向都是 red,都先预测后执行:
driver-sqlite-wasm上做,因为它才是门禁盲区所在)。临时把 spec 已删词汇塞进 wasm 的 window fixture、并给analyzeQuery传一个拼错的键:这同时证明了盲区 1 已覆盖:driver-sqlite-wasm 读的确实是 driver-sql 重建后的
dist/*.d.ts,不是假绿。已还原。any⇒ 新 pin 应红:这里有一处值得写下来:
DropsObject这个既有工具顺带也抓any——keyof any是string | number | symbol,所以'object' extends keyof any为真,DropsObject< any >解析为never。一条 pin 同时挡住「把object加回来」和「把参数放宽回any」两种回退,不需要再写第二条。验收判定标准
「让门自己文档里的载荷编译得过」是判定标准而非可选项,所以它是一条 pin,载荷逐字抄自
content/docs/data-modeling/queries.mdx:三个门禁盲区
dist/*.d.ts⇒ 已点名进验证面:先builddriver-sql 再 typecheck 它,并按上面的反向验证证明它确实读到了新 d.ts(不是假绿)。pnpm check:query-options-erasure合并前后各跑一次,267 未动(本批既不新增也不删除as any):pnpm check:type-check-debtre-measure:34 ledger entr(ies) re-measured in 214.9s, 2024 raw tsc error(s) total, none above its recorded number.本 PR 触及的三个包均不在台账内,只减不增成立。另跑:
check:nul-bytesOK(6000 个文件),check:driver-conformanceOK,check:engine-double-contractOK,check:published-filesOK,check:empty-changesetOK(1 declaring changeset added),改动文件eslint零输出,并按字节纪律自查了控制字符(零命中)。测试
合并
origin/main(15 个提交,含对sql-driver.ts的改动)后重跑:@objectstack/dogfood520 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 修)
checkDateBucketParity的每个调用点都写driver as never,把BucketableDriver替身的编译期一致性整个抹掉 —— 且实测该 cast 并不必要 #6354:checkDateBucketParity的每一个调用点都写driver as never(dogfood 与 driver-turso 共 3 处),把BucketableDriver这个结构替身存在的意义整个抹掉。实测该 cast 并不必要——去掉后@objectstack/dogfoodtypecheck 依旧全绿(在本 PR 收窄之后仍然如此)。观察类:运行期行为正确,只是编译期一致性被静默关掉。🤖 Generated with Claude Code
https://claude.ai/code/session_01WyvqvKMG6asi9aXjKE6xtx
Generated by Claude Code