Skip to content

Commit 01df1ee

Browse files
committed
fix(tables): refuse cell-value filters on json columns before the array branch
1 parent e079fa4 commit 01df1ee

2 files changed

Lines changed: 19 additions & 7 deletions

File tree

apps/sim/lib/table/query-builder/__tests__/cell-filter.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ describe('cellValueFilterConditions', () => {
9797
expect(cellValueFilterConditions(column({ type: 'json' }), [1, 2])).toEqual([])
9898
})
9999

100+
// A json cell holding a STRING array is shaped exactly like a multi-select
101+
// cell. The server accepts `contains` on json and compiles it to an ILIKE
102+
// substring match, so letting it through would quietly match unrelated rows.
103+
it('refuses a json cell holding a string array', () => {
104+
expect(cellValueFilterConditions(column({ type: 'json' }), ['a', 'b'])).toEqual([])
105+
})
106+
100107
// `json.coerce` accepts anything, so a json cell legitimately holds a scalar.
101108
// The server rejects eq/ne/in/nin on a json column, and the rejected filter
102109
// would stick in state and 400 every later refetch.

apps/sim/lib/table/query-builder/cell-filter.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ export function cellValueFilterConditions(
5050
return supports('isEmpty') ? [{ field, op: 'isEmpty' }] : []
5151
}
5252

53+
// A `json` column has no same-value filter to offer, whatever the cell holds.
54+
// It must be checked BEFORE the shape branches below: `json.coerce` accepts
55+
// anything, so a json cell holds arrays and scalars alike, and letting a
56+
// string array through the multi-select branch would emit `contains` — which
57+
// the server accepts on json and compiles to ILIKE substring matching, so
58+
// unrelated rows would match. `eq` there is refused outright by `validateLeaf`
59+
// in `query-builder/validate.ts`, and a refused predicate would stay in state
60+
// and 400 every later refetch. Only the emptiness check above survives.
61+
if (column.type === 'json') return []
62+
5363
// A multi-select cell holds several option ids, so "the same as this cell"
5464
// is one membership test per id — equality against the whole array can never
5565
// be true. Every id must be filterable, or the row the user clicked would
@@ -60,13 +70,8 @@ export function cellValueFilterConditions(
6070
return value.map((id) => ({ field, op: 'contains', value: id }) satisfies Predicate)
6171
}
6272

63-
// No equality to offer: `validateLeaf` in `query-builder/validate.ts` rejects
64-
// the containment operators on a `json` column outright, and a rejected
65-
// predicate would stay in state and 400 every later refetch. `json.coerce`
66-
// accepts anything, so its cells hold scalars too — the type, not the value
67-
// shape, is what decides. The `typeof` guard covers a structured value
68-
// reaching any other type.
69-
if (column.type === 'json' || typeof value === 'object') return []
73+
// A structured value on any other column type has no meaningful equality.
74+
if (typeof value === 'object') return []
7075
if (!supports('eq')) return []
7176
return [{ field, op: 'eq', value: value as JsonValue }]
7277
}

0 commit comments

Comments
 (0)