Skip to content

Commit 884c17f

Browse files
1 parent e530fd8 commit 884c17f

File tree

4 files changed

+55
-16
lines changed

4 files changed

+55
-16
lines changed

filter/converter.go

+18
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,24 @@ func (c *Converter) convertFilter(filter map[string]any, paramIndex int) (string
174174
v[operator] = c.arrayDriver(v[operator])
175175
}
176176
values = append(values, v[operator])
177+
case "$exists":
178+
// $exists only works on jsonb columns, so we need to check if the key is in the JSONB data first.
179+
isNestedColumn := c.nestedColumn != ""
180+
for _, exemption := range c.nestedExemptions {
181+
if exemption == key {
182+
isNestedColumn = false
183+
break
184+
}
185+
}
186+
if !isNestedColumn {
187+
// There is no way in Postgres to check if a column exists on a table.
188+
return "", nil, fmt.Errorf("$exists operator not supported on non-nested jsonb columns")
189+
}
190+
neg := ""
191+
if v[operator] == false {
192+
neg = "NOT "
193+
}
194+
inner = append(inner, fmt.Sprintf("(%sjsonb_path_match(%s, 'exists($.%s)'))", neg, c.nestedColumn, key))
177195
default:
178196
value := v[operator]
179197
op, ok := basicOperatorMap[operator]

filter/converter_test.go

+24-16
Original file line numberDiff line numberDiff line change
@@ -248,14 +248,6 @@ func TestConverter_Convert(t *testing.T) {
248248
nil,
249249
fmt.Errorf("invalid value for $not operator (must be object): John"),
250250
},
251-
{
252-
"sql injection",
253-
nil,
254-
`{"\"bla = 1 --": 1}`,
255-
``,
256-
nil,
257-
fmt.Errorf("invalid column name: \"bla = 1 --"),
258-
},
259251
{
260252
"compare with array",
261253
nil,
@@ -264,14 +256,6 @@ func TestConverter_Convert(t *testing.T) {
264256
nil,
265257
fmt.Errorf("invalid comparison value (must be a primitive): [200 300]"),
266258
},
267-
{
268-
"sql injection",
269-
nil,
270-
`{"\"bla = 1 --": 1}`,
271-
``,
272-
nil,
273-
fmt.Errorf("invalid column name: \"bla = 1 --"),
274-
},
275259
{
276260
"null nornal column",
277261
nil,
@@ -288,6 +272,30 @@ func TestConverter_Convert(t *testing.T) {
288272
nil,
289273
nil,
290274
},
275+
{
276+
"$exists on normal column",
277+
nil,
278+
`{"name": {"$exists": false}}`,
279+
``,
280+
nil,
281+
fmt.Errorf("$exists operator not supported on non-nested jsonb columns"),
282+
},
283+
{
284+
"not $exists jsonb column",
285+
filter.WithNestedJSONB("meta"),
286+
`{"name": {"$exists": false}}`,
287+
`(NOT jsonb_path_match(meta, 'exists($.name)'))`,
288+
nil,
289+
nil,
290+
},
291+
{
292+
"$exists jsonb column",
293+
filter.WithNestedJSONB("meta"),
294+
`{"name": {"$exists": true}}`,
295+
`(jsonb_path_match(meta, 'exists($.name)'))`,
296+
nil,
297+
nil,
298+
},
291299
}
292300

293301
for _, tt := range tests {

fuzz/fuzz_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func FuzzConverter(f *testing.F) {
4040
`{"$not": {"name": "John"}}`,
4141
`{"name": {"$not": {"$eq": "John"}}}`,
4242
`{"name": null}`,
43+
`{"name": {"$exists": false}}`,
4344
}
4445
for _, tc := range tcs {
4546
f.Add(tc, true)

integration/postgres_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,18 @@ func TestIntegration_BasicOperators(t *testing.T) {
333333
[]int{10},
334334
nil,
335335
},
336+
{
337+
`jsonb exists`,
338+
`{"pet": {"$exists": false}}`,
339+
[]int{9},
340+
nil,
341+
},
342+
{
343+
`jsonb exists`,
344+
`{"pet": {"$exists": true}}`,
345+
[]int{1, 2, 3, 4, 5, 6, 7, 8, 10},
346+
nil,
347+
},
336348
}
337349

338350
for _, tt := range tests {

0 commit comments

Comments
 (0)