fix(drivers): 聚合函数拒收带上 ADR-0112 信封,并把两类条件分开措辞 (#5907) - #6204
Merged
Conversation
`SqlDriver.mapAggregateFunc()` 与 `RemoteTransport.aggregate()` 对同一条件各抛 一个裸 `Error`(`code`/`status` 皆 `undefined`),`mapDataError` 因此落默认分支, 一条本该 4xx 的调用方错误以不透明 500 到达客户端。两处同改: - 协议未声明的函数名(`median`)→ `INVALID_QUERY` / 400,与协议门 (`metadata-protocol` 的 `invalidQueryError`,#4254)对同一条件已有的码一致。 - 协议已声明、本后端编不出(`count_distinct` / `array_agg` / `string_agg`)→ `NOT_IMPLEMENTED` / 501,标准目录的能力缺口码;措辞明确说查询拼写无误, 不把作者说成打错字(#5345 在 driver-memory 上画过的同一条线)。 两面首句逐字一致(#5240),由一条跨包 parity 用例比对两个**运行时**消息钉住。 编得出的五个函数生成的 SQL 逐字节不变;分类按调用方写的原始拼写判定,免得两面 归一化差异顺着信封扩散(#6203)。 Co-Authored-By: Claude Opus 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
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 12 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
`query-options-erasure` 棘轮(#4918)红:新增的两个测试文件里有 5 处 query options 调用点写成 `as any`,把测试面从 267 抬到 272。基线数字一个没动,5 处 逐一按「该给类型」还是「刻意越契约」重写: - 第 2 类(`count_distinct` / `array_agg` / `string_agg`)与全部控制组用例走 `declaredAst()`,`function` 形参类型是 `AggregationNode['function']` —— 于是 `tsc` 反过来证明这三个名字确实是已声明枚举的成员,正是第 2 类在运行时主张的 那件事;写错一个字母会在构建期红,而不是悄悄变成一条第 1 类输入却仍然让第 2 类断言「通过」。 - 第 1 类(`median` / 错误大小写)与「无 alias 默认列名」控制组按定义就构造不出 `QueryAST`,写 `as unknown as QueryAST`:命名了被绕过的契约、其余键仍受检、 可 grep 为有意为之,且不计入棘轮。 行为与断言一字未改。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WyvqvKMG6asi9aXjKE6xtx
os-zhuang
marked this pull request as ready for review
August 7, 2026 11:17
This was referenced Aug 7, 2026
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.
Fixes #5907
前提复核(先证后改)
在
origin/main@80f7dc6a3双面实测,两处裸Error都还在(行号已漂到sql-driver.ts:7071/remote-transport.ts:726,与派发一致),两类输入的现行为如下:前提成立,两类今天确实同一句话,
code/status皆undefined。改了什么
两面同改,同一信封体例、首句逐字一致(#5240),体例取 PR #5921(
invalidFilterError形态):median)INVALID_QUERY/ 400Aggregate function "median" is not a declared aggregate function.count_distinct/array_agg/string_agg)NOT_IMPLEMENTED/ 501Aggregate function "count_distinct" is declared but not implemented by this backend.packages/drivers/driver-sql/src/sql-driver.ts——mapAggregateFunc的switch换成一张降级表(SQL_AGGREGATE_FUNCTIONS),拒收消息里的「本面编得出什么」从这张表读,不再手抄一份(driver-memory 的 analytics 面静默丢弃大半个 filter:$or/$not整条丢,$between/$startsWith/$null/$regex因无 cube 映射而丢 —— 聚合结果被放大 #5345 的教训);packages/drivers/driver-turso/src/remote-transport.ts—— 同形的孪生实现。该文件刻意不依赖 knex /SqlDriver(见文件头),所以是孪生而非共享导入,与driver-sql↔driver-memory既有的unsupportedFilterError/filterArrayReachedDriverError孪生先例一致。「已声明」的集合从
@objectstack/spec的AggregationFunction读,不在驱动里复述。第 2 类为什么选
NOT_IMPLEMENTED/ 501(裁决要求说明理由)目录里四个候选,按既有语义与 status 映射实测比较:
NOT_IMPLEMENTEDStandardErrorCode)HttpStatusErrorCodeMap[501]就是它;仓内 20+ 处501+ 「not supported by this protocol/runtime」UNSUPPORTEDUNSUPPORTED_QUERY_PARAMisExpectedQueryRejection的「调用方点错参数名」清单上UNSUPPORTED_TRANSFORM@objectstack/rest)另两条支撑:账本自己的规矩是「条件通用就用标准目录,不要注册同义词」,而驱动包今天在账本里没有条目(注册反而要新开一行);标准目录 +
HttpStatusErrorCodeMap让 code 与 status 互为镜像,不是本函数自己挑的。第 1 类的
INVALID_QUERY同理不是新拼法:协议门对同一条件已经在给这个码 ——metadata-protocol的invalidQueryError对「aggregations 轴上一个枚举外的 function」refuse 的就是400 INVALID_QUERY(#4254)。于是进程内调用方与 REST 调用方读到同一个答案。一处被迫做的判断:按调用方写的原始拼写分类
两面的归一化本来就不一样(remote 先
.toLowerCase(),local 不动),实测:若各面按自己归一化之后的名字分类,
COUNT_DISTINCT会在 remote 拿 501、在 local 拿 400 —— 一条今天两面答案一致的输入,被本 PR 劈成新的叉,正是本单要关的东西。所以分类一律判调用方写的那串字节对AggregationFunction(大小写敏感的z.enum)的成员关系:任何规范拼写两面完全一致;任何错误大小写两面都落第 1 类。归一化差异本身(COUNT在 remote 编得出)未触碰,如实钉在用例里并另立 #6203。测试
新增两个文件,进两包既有拒收族(命名沿用各自族的惯例):
packages/drivers/driver-sql/src/sql-driver-out-of-contract-aggregate-function.test.ts—— 14 例packages/drivers/driver-turso/src/remote-transport-aggregate-function-refusal.test.ts—— 16 例,含 4 条跨包 parity 用例:比对SqlDriver与RemoteTransport两个运行时消息(不是两份字面量),这半边只改一个包是满足不了的。code与status,不只断言「它抛了」——未修驱动本来就抛(见上方实测),只断言抛出的用例会恒绿、对本缺陷完全失明(#6144)。反向验证(方向先判后跑),两个方向都测了
预判:未修驱动对这些输入本来就抛,所以还原裸
Error后,每条拒收用例应当挂在第一条断言(err.code→undefined),没有一条该走refusalOf的「它居然 resolved」分支;控制组保持绿。实测:
10 failed / 4 passed(共 14),driver-turso8 failed / 8 passed(共 16)。全部挂在expected undefined to be 'INVALID_QUERY' / 'NOT_IMPLEMENTED',无一条挂在「resolved」,与预判一致。但预判有一半错了,如实记下:4 条 parity 用例这时是绿的 —— 两面都匿名,就都在
undefined/undefined上「一致」。parity 测的是一致性,不是正确性。@objectstack/driver-sql的陈旧dist/意外测到的(本包源码已修、依赖包是旧的):4 条 parity 用例以expected 'NOT_IMPLEMENTED' to be undefined全红。(2) 才是这个文件存在的方向:本单防的不是「某一面沉默」,而是「两面对同一条查询给不同答案」,所以必须变红的是单面改动。两条测量都写进了测试文件的 docblock。
命令与结果
消费半径已扫:全仓没有任何测试同时构造 SQL 驱动与这三个函数;
Unsupported aggregate function的旧措辞除本 PR 新增文件外只出现在两处文档,已一并订正(不是content/docs/releases/)。边界
query.zod.ts一字未动;第 2 类的消息把这条路指过去。$-算子键 —— 不再编译成静默空集/全表写 (#5769) #5921 的条件层拒收闸、fix(driver-turso): remote 模式对齐 NULL-safe 语义 —— $not/$ne/$nin/$notContains 四算子 + $exists 拒收闸 (#5903) #6047/fix(drivers):undefined比较数一律拒收 —— 闸落在任何发射器/守卫之前,两个毛病同闸消灭 (#6050) #6142 的 NULL/undefined 闸。find/count/…仍声明query: QueryAST,而调用方已可省略object—— 双变让它编译,但声明开始说谎 #6075 零重叠(签名区 vs 聚合区)。uncompilableAggregateFunctionError的 docblock 里,免得被当成驱动 bug 重查:/data路由上mapDataError的状态直通只覆盖 4xx,所以这条声明的 501 到不了线上,会落到UNCLASSIFIED_FAULT的500 INTERNAL_ERROR(连code一起被覆写)。那是 REST 边界的缺口 mapDataError 的显式状态直通只覆盖 4xx,数据路由上一个声明了 502/503 的生产者拿不回自己的状态码(与 resolveErrorResponse 不对等) #5582 —— 本 PR 是它的第一个活体生产者,已在该单补上证据;驱动侧该做的(在抛出点如实声明,ADR-0112)已经做完。顺带记录的发现(未在本 PR 修)
mapDataError4xx-only 直通把本 PR 的 501 压成500 INTERNAL_ERROR,连语义码一起丢;该单原本自评为观察类「没测到活体生产者」,现在有了。COUNT在 remote 编得出、在 local 被拒(同一个 TursoDriver,取决于 url) #6203(新开,finding):聚合函数名大小写归一化两面不一致 ——COUNT在 remote 编得出、在 local 被拒。仓内无活体发送方、协议门也表达不出该输入,故记为观察类,严重度请分诊判定。🤖 Generated with Claude Code
https://claude.ai/code/session_01WyvqvKMG6asi9aXjKE6xtx
Generated by Claude Code