Repository navigation
spec: FilterConditionSchema (the save-time door) admits a non-boolean $null / $exists flag, so a stored dataset or widget filter carrying one saves clean and is refused 400 on every query face #20116
Description
Filing gate: ① a defect with a named landing site, measured. Finding class (c): two faces of one contract disagree.
- Filed by: the
domain:servicesexecution seat (session_01Evb5jFDZGKQE9KG4jbMfMF, seat post [PM seat] domain:services — ⏳ vacant #6021). - Surfaced by: the service-analytics: the caller-where lowering reads
$null/$existsflags by identity, so a non-boolean flag ({ $null: "x" }) is answered 200 as IS NOT NULL where every other face refuses it (#5347 / #5369) #20040 dev on PR fix(service-analytics)!: the analytics where door refuses a non-boolean $null / $exists flag, which it read as IS NOT NULL (#20040) #20115, as an out-of-scope finding. ⛔ Filed bare: routing and grading are triage's. ⛔ Not a claim. - Acting reader: the seat that owns
packages/speconce triage routes it. The site ispackages/spec/src/data/filter.zod.ts,FilterConditionSchema's comparand check (checkFilterConditionComparands), the same door finding(spec):ViewFilterRuleSchemaaccepts two shapes every consumer refuses, andObjectGridProps.defaultFiltersisz.unknown()so nothing gates it at all — the protocol half of objectui#9050's ruling C′ #19514 made refuse$icontains: ''.
The contract
FieldOperatorsSchema.$null/$existsare declaredz.boolean().- The
$null的比较值不是布尔时,driver-sql 与 driver-memory 给出**完全相反**的答案(一个 IS NULL,一个 IS NOT NULL)—— 实测 #5347 /$exists的比较值不是布尔时,三个后端面给出三个答案,driver-memory 自己的两个面在'yes'上就已分叉 —— 实测,$null(#5347)的同族另一轴 #5369 rulings: a non-boolean flag is refused in every position and on every backend, because the backends read one in opposite directions. driver-sqlrefuses it (nonBooleanNullComparandError). The read-scope compiler refuses it (read-scope-sql 的$null/$exists按真值性读比较数 ——{$null: "false"}编成IS NULL,#5347 / #5369 的先例没推到 RLS 编译器 #6387). As of PR fix(service-analytics)!: the analytics where door refuses a non-boolean $null / $exists flag, which it read as IS NOT NULL (#20040) #20115 (service-analytics: the caller-where lowering reads$null/$existsflags by identity, so a non-boolean flag ({ $null: "x" }) is answered 200 as IS NOT NULL where every other face refuses it (#5347 / #5369) #20040), the analyticswheredoor refuses it too.
The defect
Measured by the #20040 dev at 66266c2a3b:
FilterConditionSchema.safeParse({ stage: { $null: 'x' } })succeeds.DatasetSchema.safeParsewithfilter: { stage: { $null: 'x' } }succeeds, and so do$exists: 'false'and$null: null.- The same save door refuses
$icontains: ''(finding(spec):ViewFilterRuleSchemaaccepts two shapes every consumer refuses, andObjectGridProps.defaultFiltersisz.unknown()so nothing gates it at all — the protocol half of objectui#9050's ruling C′ #19514). So the door already judges comparands; it does not judge this one.
The field entry is typed z.unknown() at that position. A stored dataset or widget filter carrying a non-boolean flag therefore saves and publishes clean, then answers INVALID_FILTER / 400 on every query face. The author finds out at run time, not at save.
Producer: whoever authors a dataset filter, through defineStack metadata or the Studio. Stored deployments were NOT measured for the shape.
Seam: spec:FilterConditionSchema (checkFilterConditionComparands) → runtime:driver-sql reduceFilterKey | service-analytics normalizeWhereComparands
Filing-gate answers
- Class: ①, a defect with a named landing site, measured by
safeParse; finding class (c). - Acting reader: the
packages/specseat after triage. - Dedupe: MCP semantic issue search in this repo,
closedincluded. QueryFilterConditionSchema $null non-boolean admitted at save dataset filter $exists string checkFilterConditionComparands boolean flag→ 5 hits: A list inside a nested-relation condition in a dataset or measure filter ({ account: { region: ['a'] } }) passes the save-time schema door and is refused only when the chart runs #20081, A list inside a nested-relation condition in a dataset or measure filter ({ account: { region: ['a'] } }) passes the save-time schema door and is refused only when the chart runs #20080, [finding]FilterConditionSchemastill PARSES{ field: [...] }and{ field: { $eq: [...] } }, so a stored dataset or widget filter publishes clean and is refused at query time on every backend #19889, [spec]NormalizedFilterSchemaaccepts ANY field-condition shape — its union's second branch is a non-strict catch-all #7711, objectqlhaving-filter.ts是「无值字段」语义的第五个求值面,且带着 #5299 同款的早退守卫($nin/$notContains不在豁免名单) #5905.- [finding]
FilterConditionSchemastill PARSES{ field: [...] }and{ field: { $eq: [...] } }, so a stored dataset or widget filter publishes clean and is refused at query time on every backend #19889 (closed) is the same class for a list in the equality slot. - A list inside a nested-relation condition in a dataset or measure filter (
{ account: { region: ['a'] } }) passes the save-time schema door and is refused only when the chart runs #20080 / A list inside a nested-relation condition in a dataset or measure filter ({ account: { region: ['a'] } }) passes the save-time schema door and is refused only when the chart runs #20081 (open) are the same class for a list inside a nested-relation condition. - None covers the boolean flags.
- Control: the query reaches the save-door cards of the same class.
- [finding]
Dedupe words: FilterConditionSchema $null non-boolean admitted at save · dataset filter $exists string saves then 400 · checkFilterConditionComparands boolean flag
Generated by Claude Code
Activity