Skip to content

drivers: undefined 比较数被发射器读作 null、却被守卫/校验读作「值」—— turso local 抛裸 knex 错、remote 静默答 IS NULL(实测,origin/main) #6050

Description

@os-zhuang

出自 #5903 的实施(PR #6047)—— 给 remote 侧写 NULL-safe 极性表时,为把每一格对齐到「守卫必须匹配它自己的发射器」这条 #5298 不变量,逐格复核了 undefined 比较数,发现一族与 #5903 同类但成因不同的分叉。范围外,按 Prime Directive #10 记录,不指派。

事实(origin/main @ efedd289f,同一个 TursoDriver,共享 fixture:1-2 行 d 有值、3-4 行 d 为 NULL)

filter turso LOCAL(继承 SqlDriver) turso REMOTE(RemoteTransport)
{ d: undefined } 抛 Undefined binding(s) detected when compiling SELECT(knex 裸 Error) ['3','4']
{ d: { $eq: undefined } } 抛 同上 ['3','4']
{ $not: { d: undefined } } 抛 Undefined binding(s) … compiling WHERE ['1','2']
{ d: { $ne: undefined } } ['1','2'] ✅ ['1','2'] ✅
{ $not: { d: { $ne: undefined } } } [] ['3','4']

两个可分开的毛病:

A. 抛出的那三格没有 ADR-0112 信封

LOCAL 抛的是 knex 的 Undefined binding(s) …,code 与 status 都是 undefined —— 正是 #1116 / #4436 为这条通路装信封时清点过的形态,只是这一格漏在名单外。mapDataError 落默认分支,客户端拿到一个没有 code 的 500,而它其实是一条 caller 写坏的 filter。

B. $ne: undefined 一格,driver-sql 的极性表与它自己的发射器互相矛盾

sql-driver.ts 的发射器读 coerced == null(宽松相等,所以 undefined 走 whereNotNull,编译成 IS NOT NULL —— 一条 TOTAL 谓词):

case '$ne':
  if (coerced == null) (builder)[…'whereNotNull'](field);

但同文件的两张守卫表读的是严格相等:

  • operatorIsNullTotal('$ne', undefined) → undefined === null → false(判它「不 total」,与发射器矛盾)
  • nullValueSatisfiesOperator('$ne', undefined) → undefined !== null → true(判「NULL 行满足它」,与 IS NOT NULL 相反)

两处一起,nullGuardForFieldSpec 得出 allowNull,于是 $not 的操作数被改写成 d IS NULL OR d IS NOT NULL —— 恒真 —— 取反后恒假,答 []。remote 侧(PR #6047 的表,两格都按 == null 语义写,与它自己的发射器一致)答 ['3','4']。

这正是 #5298 立的那条不变量:「每张极性表钉的是它自己发射器的拼写,不变量是守卫与发射器一致,不是三份源码字面相同」。driver-sql 这一格恰好没有对上自己。⚠️ 因此 ['3','4'] 是按该驱动自身发射器推出的正确答案,[] 是错的 —— 但这句话是本单的分析,不是裁决。

为什么值得修(不是纯理论)

undefined 过不了 JSON 往返,所以只会从进程内代码来 —— 而那正是它最危险的地方:{ owner_id: ctx.user?.id } 里 id 缺失时,remote 会把整条谓词静默变成 owner_id IS NULL,匹配所有环境级(env-wide)行,而不是响亮地失败。同一条规则在 local 上抛错、在 remote 上放行,是 #5903 刚关掉的那个「按连接模式给两个答案」家族的又一格,只是这次触发键是比较数的形态而不是算子。

需要一次裁决(所以本单不自带修法)

undefined 比较数应当:

B 案与本仓 contract-first 的方向更一致,但会改变 remote 今天已经在答的行为,且要先量一遍仓内有没有依赖 A 案的调用方 —— 请裁决方拍。

落点

关联

#5903 / PR #6047(同族,不同成因:那单是裁决没落到独立发射器,本单是守卫没对上自己的发射器)、#5298(NULL/无值语义族的裁决入口与「守卫匹配发射器」不变量的出处)、#5146、#5347 / #5369(比较数按声明拒收的先例)、#1116 / #4436(ADR-0112 信封,毛病 A 的清点来源)。

会话:session_01WyvqvKMG6asi9aXjKE6xtx

Activity

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions