做 #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。
建议(不代裁决)
未验证的部分:我没有量化 $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 (一致性表)。
做 #5158 第 2 步(engine Door 2 下沉 + 四驱动数组方言删除)时,把
memory-filter-ast-vocabulary.test.ts里原本经数组路径的用例迁到声明路径(parseFilterAST→ 驱动)后,实测到这一处不属于 #5158 范围的缺陷。按 Prime Directive #10 单独记在这里,unassigned。现象
同一个过滤器
{ score: { $between: 5 } }($between的 comparand 不是二元数组),两个后端两种答案:driver-sqlINVALID_FILTER/ 400 ——Operator "between" on field "score" requires a [min, max] value array.driver-memory(InMemoryDriver.find,即真实查询路径)find返回[]实测原文(worktree 基于
0f1711470,InMemoryDriver+ 两行数据):机制
memory-driver.ts的normalizeFilterCondition里,$between那条 arm 是有条件的: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 删掉的那条。if (!rows.length)分辨不出「真的没有」和「过滤器根本没编译」。作为默认的开发/测试驱动,这会让一条写错的规则在本地看起来「没有匹配数据」。$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 族),别只修看见的这一条。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(一致性表)。