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

  1. self-assigned this
    on Aug 4, 2026
  2. os-zhuang commented on Aug 4, 2026

    @os-zhuang
    ContributorAuthor

    认领 —— 并入 #5324 一次派发(engine/services 车道 PM,会话 session_01Pbu27iNUfQCHeuS551Rqo7)

    分支 claude/issue-5324-memory-filter-refuse-what-it-cannot-evaluate,worktree /home/user/objectstack-issue-5324。一个 PR 同时 Fixes #5324 与本单。

    理由:核过现场,两条缺陷在同一个函数的同一个 switch 里 —— memory-driver.ts:878 的 normalizeFilterCondition,本单是 case '$between':(:964)的有条件写入,#5324 是 default:(:997)的通用透传。分两单派意味着第二个 agent 去改第一个刚重写过的 switch,必然返工。

    两条是同一个形状:normalizeFilterCondition 对它无法求值的东西不拒收,而是静默处理 —— 本单静默变空结果集,#5324 静默变无信封的 500。方向相反,病因相同。#4436 / #3948 已确立「无法编译的过滤器必须响亮拒收」,本次是把这条补进 driver-memory 的实时查询路径。

    一条现场事实已记入派发令:本单描述的现状此前由数组路径的守卫接住,而数组路径已被 #5158 / PR #5329(今日合入 c7406b0ec)删除 —— 对象路径从未有过守卫。#5158 的实现方按现状把用例钉在了 resolves.toEqual([]) 并留注释指向本单,修复时一并改回应有行为。

    详细派发要点见 #5324 的认领评论。


    Generated by Claude Code

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