Skip to content

fix(driver-turso)!: remote 停止对聚合函数名做大小写归一化,两面只认协议声明的拼写 (#6203) - #6312

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-6203-aggregate-case-normalization
Aug 7, 2026
Merged

fix(driver-turso)!: remote 停止对聚合函数名做大小写归一化,两面只认协议声明的拼写 (#6203)#6312
os-zhuang merged 2 commits into
mainfrom
claude/issue-6203-aggregate-case-normalization

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #6203

缺陷

TursoDriver 按连接串 url 选面:本地/副本继承 SqlDriver,远程委派 RemoteTransport。两面对聚合函数名的归一化不一致 —— remote 先 .toLowerCase() 再查自己的编译表,local 拿到什么查什么。于是同一个驱动、同一条查询,答案取决于连接串

origin/main @ d367f03d6(已含 #6204 的信封与 #6210 的签名)实测:

COUNT   REMOTE -> RESOLVED "SELECT count(\"stage\") AS \"n\" FROM \"deal\""
        LOCAL  -> THREW    INVALID_QUERY / 400  "…\"COUNT\" is not a declared aggregate function"
Count   REMOTE -> RESOLVED (同上)              LOCAL -> THREW INVALID_QUERY / 400
COUNT_DISTINCT / Median  两面已一致(#6204 按调用方原始拼写分类的结果)

即 issue 的前提在最新 main 上依然成立,且剩下的分叉正好收敛到「与某个已编译名字只差大小写」这一族。

修法(裁决方向 1:收紧 remote)

删掉 RemoteTransport.aggregate() 里的 .toLowerCase(),查表键改为调用方写的原始拼写。

  • AggregationFunction大小写敏感z.enum —— AggregationFunction.parse('COUNT') 直接抛错,这条已由 packages/spec/src/data/query.test.ts 钉住。所以 COUNT 是协议从未声明的拼写,remote 此前多认的是一种私有方言,属 PD#12 明确反对的宽容消费端;
  • 方向 2(给 local 也加归一化)会把方言固化成第二套事实契约,与之相反。

不需要新增任何分类逻辑,已确认。 #5907(PR #6204)已经把两面的分类改成按调用方原始拼写判定,所以查表不再归一化之后,COUNT 自然落进第 1 类 INVALID_QUERY / 400,措辞与 local 逐字相同。信封、措辞、降级表、#6210 的签名、条件层与 NULL/undefined 闸一律未动;driver-memory / driver-mongodb 零改动。

默认 alias 的拼法对所有仍能编译的输入字节不变 —— 只有已在编译表里的名字才走到那一行,而表里每个键都是小写。

测试

反向验证(方向先判后跑,四条并不同向,如实记录)

.toLowerCase() 放回去,预判:

用例 预判 理由
COUNTCount ,且红在「but it compiled」而不是比较失败 与已编译名字只差大小写,正是 toLowerCase() 搬动的全部人口
COUNT_DISTINCTMedian 绿,不动 归一化后仍然查不到表,而 #5907 早已按调用方拼写分类,改动前两面就是同码同文

实测与预判完全一致:2 failed | 806 passed,两条失败均为 expected the transport to refuse "COUNT"/"Count", but it compiled to [...]

两条不敏感的用例保留:这一族断言的是「任何大小写变体都不会因连接串拿到不同答案」,而它们是 #5907 那一半的常驻守卫 —— 归一化若从分类侧(而非查表侧)回来,只有它们看得见。

影响面 / 消费半径

  • 全仓大写拼写零活体发送方(function: 'COUNT' 等零命中;service-analytics 的 measure 词汇本身就是小写 enum);
  • 经 REST/协议门进来的查询在 AggregationNodeSchema 就被拒,到不了驱动;
  • 受影响的只有绕过 spec 校验、直连远程驱动且依赖该归一化的进程内调用方 —— 那本来就不是协议表达得出的输入。已在 changeset 的升级说明里写明「作者侧修法是改用小写」。

验证

pnpm --filter @objectstack/driver-turso test        808 passed (25 files)
pnpm --filter @objectstack/driver-sql   test        924 passed | 46 skipped
pnpm --filter ... typecheck (turso + sql)           Done / Done
pnpm --filter '...@objectstack/driver-turso' test   cli / app-crm / app-showcase / app-todo / dogfood 全绿
pnpm check:query-options-erasure                    test surface 267 —— 仍在基线,未抬
pnpm check:type-check-debt                          34 条台账 re-measure,none above its recorded number
pnpm check:nul-bytes / check:empty-changeset / check:driver-conformance / check:error-code-casing   OK

消费半径按 --filter '...pkg'(前缀=下游)清扫,未用后缀(#6218)。

附:driver-sql 的注释订正(仅注释)

sql-driver.tsundeclaredAggregateFunctionError 的 docblock 把「remote transport lowercases it before its own lookup」写成了现状事实 —— 本 PR 之后不再成立。改为交代其历史并指出:大小写敏感的判定不是这条分叉的副产品,而是 enum 本身的语义。行为零改动,local 侧 mapAggregateFunc 的归一化行为未触碰。


Generated by Claude Code

…#6203)

`TursoDriver` picks its face from `url`: a local/replica URL inherits
`SqlDriver`, a remote one delegates to `RemoteTransport`. The two faces
normalised the aggregate function name differently — the remote transport
lowercased it before its lowering-table lookup, the local driver looked up
whatever it was handed — so one driver answered one query two ways, decided
by a connection string. Measured on origin/main @ d367f03:

  COUNT  REMOTE -> RESOLVED "SELECT count(\"stage\") AS \"n\" FROM \"deal\""
         LOCAL  -> THREW    INVALID_QUERY / 400
  Count  REMOTE -> RESOLVED (same)   LOCAL -> THREW INVALID_QUERY / 400

Delete the `.toLowerCase()`. `AggregationFunction` is a case-sensitive
`z.enum` (`AggregationFunction.parse('COUNT')` throws), so `COUNT` is a
spelling the Query Protocol never declared: what the remote transport
accepted was a private dialect, and PD#12 rejects widening a consumer to
keep one alive. Teaching the local driver the same dialect would have
fossilised it into a second de-facto contract instead.

No new classification logic was needed. #5907 (PR #6204) already classifies
on the CALLER's spelling, so with the lookup no longer normalising, `COUNT`
falls straight onto class 1 — INVALID_QUERY / 400, "not a declared aggregate
function" — the same envelope, in the same words, that the local driver has
been giving it. The default alias is unchanged for every input that still
compiles: only a name already in the lowering table reaches that line, and
every key there is lowercase.

Tests: the `[filed, not fixed]` control #5907 left here recording `COUNT`
compiling on this face is flipped into a four-case local/remote parity block
asserting code AND status AND first sentence (#6144), plus a control that the
declared lowercase spelling still compiles on both faces. The aliasless
fixture drops its miscased axis, which stopped being expressible.

Reverse verification, direction predicted before running and recorded in the
test docblock because the four cases do NOT move together: with the
`toLowerCase()` restored, `COUNT`/`Count` go red on "but it compiled" (2/4),
while `COUNT_DISTINCT`/`Median` stay green — lowercasing them still misses
the lowering table and #5907 already judged them on the caller's spelling.
Measured: exactly that, 2 failed / 806 passed. Both are kept: the
insensitive pair is the standing guard on #5907's half of the same fork.

driver-sql carries a comment-only correction — a docblock there described the
remote transport's lowercasing as current fact.

Fixes #6203

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 1:47pm

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels 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/driver-turso.

12 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, @objectstack/driver-turso)
  • content/docs/deployment/cli.mdx (via @objectstack/driver-turso)
  • content/docs/deployment/environment-variables.mdx (via @objectstack/driver-turso)
  • content/docs/deployment/self-hosting.mdx (via @objectstack/driver-turso)
  • content/docs/getting-started/glossary.mdx (via @objectstack/driver-sql, @objectstack/driver-turso)
  • 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, @objectstack/driver-turso)
  • 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)

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

2 participants