Skip to content

driver-memory 对形状错误的 $between 静默不发谓词(匹配 0 行),driver-sql 同一过滤器抛错 —— 一个过滤器两个答案 #5328

Description

@os-zhuang

做 #5158 第 2 步(engine Door 2 下沉 + 四驱动数组方言删除)时,把 memory-filter-ast-vocabulary.test.ts 里原本经数组路径的用例迁到声明路径(parseFilterAST → 驱动)后,实测到这一处不属于 #5158 范围的缺陷。按 Prime Directive #10 单独记在这里,unassigned。

现象

同一个过滤器 { score: { $between: 5 } }($between 的 comparand 不是二元数组),两个后端两种答案:

后端 行为
driver-sql 抛 INVALID_FILTER / 400 —— Operator "between" on field "score" requires a [min, max] value array.
driver-memory(InMemoryDriver.find,即真实查询路径) 不抛,解析为「匹配 0 行」,find 返回 []

实测原文(worktree 基于 0f1711470,InMemoryDriver + 两行数据):

await find({ score: { $between: 5 } })   =>  resolves []      // 期望 rejects

机制

memory-driver.ts 的 normalizeFilterCondition 里,$between 那条 arm 是有条件的:

case '$between':
  if (Array.isArray(val) && val.length === 2) {
    result.$gte = store(val[0]);
    ...
  }
  break;          // ← 形状不对时什么都不写,整条约束消失

val 不是二元数组时 arm 整个跳过,该字段归一化成 {},mingo 把 { score: {} } 当结构相等比较 —— 于是「匹配 0 行」。没有任何一处报告这条谓词没被编译。

为什么是 bug

  1. 一个已声明算子,两个后端两个答案。 $between 是 spec 声明的算子;driver-sql 自 A filter with an operator outside VALID_AST_OPERATORS is silently dropped, not rejected — single-condition views return unfiltered results #3948 起把「无法编译的过滤器」定为响亮拒收,data: unsupported-filter-operator refusal ships without error.code and leaks the [sql-driver] prefix #4436 又给这类拒收统一了 ADR-0112 信封。driver-memory 在对象路径上从未有过这条 arm 的守卫 —— 它过去只在数组路径上有(convertConditionToMongo 的 "between" on field "…" needs a two-element array),而数组路径正是 [engine] driver-sql 编译 spec 未声明的「数组 where 方言」—— 与 Turso remote 的拒收分叉,需一次定调(接纳进 spec 或响亮弃用) #5158 删掉的那条。
  2. 静默方向是「匹配 0 行」,不是「匹配全表」 —— 比 A filter with an operator outside VALID_AST_OPERATORS is silently dropped, not rejected — single-condition views return unfiltered results #3948 的原始缺陷轻,但仍是静默的错误答案:调用方要的是一个区间,拿到的是空结果,而 if (!rows.length) 分辨不出「真的没有」和「过滤器根本没编译」。作为默认的开发/测试驱动,这会让一条写错的规则在本地看起来「没有匹配数据」。
  3. 它是 driver-memory 的实时查询路径根本不支持 $not —— mingo 抛无 code 的 MingoError,CEL !expr 降下来的 RLS scope 在该驱动上直接 500 #5324 的邻居而不是同一条:driver-memory 的实时查询路径根本不支持 $not —— mingo 抛无 code 的 MingoError,CEL !expr 降下来的 RLS scope 在该驱动上直接 500 #5324 是透传给 mingo 后抛出无信封的 MingoError(default: result[op] = val),这一条是 arm 被吞掉、连异常都没有。两者共用 normalizeFilterCondition 这个缝,修的时候值得一起看。

为什么至今没被测出来

memory-filter-ast-vocabulary.test.ts 里 “throws on a malformed between rather than emitting no predicate” 这条一直是绿的 —— 但它走的是数组路径(find([['score','between',5]])),命中的是 convertConditionToMongo 的守卫。对象路径同一输入从未被断言过。#5158 把该用例迁到声明路径后,这一条立刻暴露。当前该用例已按现状钉住(resolves.toEqual([]))并在注释里指向本 issue,好让分叉是可见的而不是口口相传 —— 修好后请一并把它翻回 rejects。

建议(不代裁决)

未验证的部分:我没有量化 $between 形状错误在真实元数据里出现的频率,也没有测 driver-mongodb / driver-sqlite-wasm 在同一输入下的行为(sqlite-wasm 继承 SqlDriver,预期与 SQL 一致,但没实测)。严重度请 PM 按 triage 定,不代表我判断它低。

关联:#5158(实测出这一条的那一单)、#5324(同一 normalizeFilterCondition 缝的透传型缺陷)、#3948(no-silent-drop 的原则)、#4436(driver-memory 的 refusal envelope)、#5240(一个条件一种措辞)、#5239(一致性表)。

Activity

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions