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) …,codestatus 都是 undefined —— 正是 #1116 / #4436 为这条通路装信封时清点过的形态,只是这一格漏在名单外。mapDataError 落默认分支,客户端拿到一个没有 code 的 500,而它其实是一条 caller 写坏的 filter。

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

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

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

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

  • operatorIsNullTotal('$ne', undefined)undefined === nullfalse(判它「不 total」,与发射器矛盾)
  • nullValueSatisfiesOperator('$ne', undefined)undefined !== nulltrue(判「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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions