Skip to content

driver-memory 的实时查询路径根本不支持 $not —— mingo 抛无 code 的 MingoError,CEL !expr 降下来的 RLS scope 在该驱动上直接 500 #5324

Description

@os-zhuang

#5240(四后端对 { field: {} } 拒收)时,为了确认 driver-memory 那一行的现场而实测了驱动的真实查询路径,发现这一处不属于 #5240 范围的缺陷。按 Prime Directive #10 单独记在这里,unassigned。

现象

InMemoryDriver.find 并不走 memory-matcher.tsmatch() —— 它只从那个文件 import 了 getValueByPath,过滤实际由 convertToMongoQuery()mingo 完成。而 mingo 是 MongoDB 语义的实现,MongoDB 没有文档级 $not,于是:

WHERE {"$not":{"stage":"won"}}              => THROW MingoError | code= undefined | status= undefined | unknown top level operator: $not
WHERE {"$or":[{"$not":{"stage":"won"}}]}    => THROW MingoError | code= undefined | status= undefined | unknown top level operator: $not
WHERE {"$and":[{"$not":{"stage":"won"}}]}   => THROW MingoError | code= undefined | status= undefined | unknown top level operator: $not

(实测原文,InMemoryDriver + syncSchema('deal') + 两行数据,worktree 基于 26e1029f5。)

normalizeFilterCondition$not 只是递归归一化后原样透传 (result[key] = ...),没有任何 De Morgan 改写,所以 $not任何位置都失败,不只是顶层。

为什么是 bug

  1. $not 是已声明的协议算子。 spec LOGICAL_OPERATORS 声明它;driver-sql 实现它([driver-sql] 未知查询 operator(is/is_null)静默透传返整表,应报错;缺 $null 处理 #2704 加的分支,$not 的语义在 driver-sql 与 driver-memory / formula 之间分叉:NULL 行的去留相反,$not: {} 一个是 TRUE 一个是 FALSE #5146 又把它改成 NULL-safe);driver-mongodb 专门有一层翻译(mongodb-filter-logic-translation.test.ts)正是为了绕开「MongoDB 没有文档级 $not」;memory-matcher.match 自己也实现了它。四个后端里只有 driver-memory 的实时路径没有。
  2. 它落在权限路径上。 cel-to-filter.ts 把 CEL !expr 编译成 { $not: {...} },这是 RLS read scope 的常规产物。driver-memory 是默认的开发/测试驱动 —— 一条带否定的 scope 在它上面不是「多返回/少返回几行」,而是整个查询抛异常。
  3. 异常逃出了 ADR-0112 信封。 MingoError 没有 code 也没有 status,于是 mapDataError 走 default 分支,客户端拿到的是 500 形状的裸 { error } —— data: unsupported-filter-operator refusal ships without error.code and leaks the [sql-driver] prefix #4436 为这个驱动建立的 INVALID_FILTER / 400 envelope(见 memory-filter-refusal-envelope.test.ts)对这一类完全没覆盖到。

为什么至今没被测出来

FILTER_LOGIC_CASES 对 driver-memory 只经由 memory-matcher(memory-matcher-or-semantics.test.ts)跑,那是参考实现,不是驱动的实时路径;而 driver-sql / driver-sqlite-wasm / driver-mongodb 三家的同名 conformance 都是穿过真驱动跑的。表里第 $not ANDs with its sibling keys inside a branch 条(filter-logic-conformance.ts:172)因此从未在 InMemoryDriver.find 上执行过一次。

即:一致性表覆盖了这个后端的一半,而没覆盖的那一半正好是用户真正用到的那一半。

建议(不代裁决)

两条路都要维护者/spec 车道拍板方向,故只列不选:

另外无论走哪条,建议补一条 FILTER_LOGIC_CASES 穿过 InMemoryDriver.find 的 conformance 套件 —— 覆盖缺口本身比这个 $not 更值钱(可与 #5239 同批考虑)。

未验证的部分:我没有测 driver-mongodb 的翻译层是否已经覆盖全部 $not 形状,也没有量化今天有多少 RLS 规则真的编译出 $not。严重度请 PM 按 triage 定,不代表我判断它低。

关联:#5240(实测出这一条的那一单)、#5146(driver-sql 的 NULL-safe $not)、#5297(service-analytics 的 $not 分叉)、#5239(一致性表)、#4436(driver-memory 的 refusal envelope)。

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions