Repository navigation
driver-memory 对形状错误的 $between 静默不发谓词(匹配 0 行),driver-sql 同一过滤器抛错 —— 一个过滤器两个答案 #5328
Description
做 #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
- 一个已声明算子,两个后端两个答案。
$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 删掉的那条。 - 静默方向是「匹配 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)分辨不出「真的没有」和「过滤器根本没编译」。作为默认的开发/测试驱动,这会让一条写错的规则在本地看起来「没有匹配数据」。 - 它是 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。
建议(不代裁决)
- 在
normalizeFilterCondition的$betweenarm 上补 else 分支,抛unsupportedFilterError(filter-refusal.ts已有,自带INVALID_FILTER/ 400),措辞与 driver-sql 对齐 —— 「一个条件一种措辞」是{ field: {} }(零个操作符的字段约束)在同仓有三个答案:driver-sql 组合子内 TRUE、顶层抛 INVALID_FILTER、formula/driver-memory FALSE #5240 已经确立的规矩。 - 顺带查同一
switch里其他有条件写入的 arm 是否同款($null、regex 族),别只修看见的这一条。 - 一致性表([spec] FILTER_LOGIC_CASES 补空组合子的布尔单位元四条 —— 需与 driver-mongodb 的单位元归约同时落地 #5239)若能穿过
InMemoryDriver.find跑,这类缺口下次会自己冒出来 —— 与 driver-memory 的实时查询路径根本不支持$not—— mingo 抛无 code 的 MingoError,CEL!expr降下来的 RLS scope 在该驱动上直接 500 #5324 的同款建议。
未验证的部分:我没有量化 $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
os-zhuang commentedon Aug 4, 2026
认领 —— 并入 #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
fix(driver-memory): refuse the filters the live query path cannot eva…
fix(driver-mongodb): refuse malformed $between, undeclared node-level…