Skip to content

Commit 67bbb43

Browse files
Support comparing to null
1 parent f62bc77 commit 67bbb43

File tree

4 files changed

+49
-3
lines changed

4 files changed

+49
-3
lines changed

filter/converter.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,21 @@ func (c *Converter) convertFilter(filter map[string]any, paramIndex int) (string
190190
innerResult = "(" + innerResult + ")"
191191
}
192192
conditions = append(conditions, innerResult)
193+
case nil:
194+
// Comparing a column to NULL needs a different implementation depending on if the column is in JSONB or not.
195+
// JSONB columns are NULL even if they don't exist, so we need to check if the column exists first.
196+
isNestedColumn := c.nestedColumn != ""
197+
for _, exemption := range c.nestedExemptions {
198+
if exemption == key {
199+
isNestedColumn = false
200+
break
201+
}
202+
}
203+
if isNestedColumn {
204+
conditions = append(conditions, fmt.Sprintf("(jsonb_path_match(%s, 'exists($.%s)') AND %s IS NULL)", c.nestedColumn, key, c.columnName(key)))
205+
} else {
206+
conditions = append(conditions, fmt.Sprintf("(%s IS NULL)", c.columnName(key)))
207+
}
193208
default:
194209
if !isScalar(value) {
195210
return "", nil, fmt.Errorf("invalid comparison value (must be a primitive): %v", value)

filter/converter_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,30 @@ func TestConverter_Convert(t *testing.T) {
264264
nil,
265265
fmt.Errorf("invalid comparison value (must be a primitive): [200 300]"),
266266
},
267+
{
268+
"sql injection",
269+
nil,
270+
`{"\"bla = 1 --": 1}`,
271+
``,
272+
nil,
273+
fmt.Errorf("invalid column name: \"bla = 1 --"),
274+
},
275+
{
276+
"null nornal column",
277+
nil,
278+
`{"name": null}`,
279+
`("name" IS NULL)`,
280+
nil,
281+
nil,
282+
},
283+
{
284+
"null jsonb column",
285+
filter.WithNestedJSONB("meta"),
286+
`{"name": null}`,
287+
`(jsonb_path_match(meta, 'exists($.name)') AND "meta"->>'name' IS NULL)`,
288+
nil,
289+
nil,
290+
},
267291
}
268292

269293
for _, tt := range tests {

fuzz/fuzz_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func FuzzConverter(f *testing.F) {
3939
`{"\"bla = 1 --": 1}`,
4040
`{"$not": {"name": "John"}}`,
4141
`{"name": {"$not": {"$eq": "John"}}}`,
42+
`{"name": null}`,
4243
}
4344
for _, tc := range tcs {
4445
f.Add(tc, true)

integration/postgres_test.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -322,9 +322,15 @@ func TestIntegration_BasicOperators(t *testing.T) {
322322
nil,
323323
},
324324
{
325-
"$gt with jsonb column",
326-
`{"guild_id": { "$gt": 40 }}`,
327-
[]int{7, 8, 9, 10},
325+
`column equal to null`,
326+
`{"mount": null}`,
327+
[]int{3, 4},
328+
nil,
329+
},
330+
{
331+
`jsonb equal to null`,
332+
`{"pet": null}`,
333+
[]int{10},
328334
nil,
329335
},
330336
}

0 commit comments

Comments
 (0)