Opened 10 years ago

Closed 10 years ago

#26908 closed Bug (fixed)

jsonfield__key__isnull lookup crashes on PostgreSQL 9.4

Reported by: Luca Simone Sigfrido Percich Owned by: PREMANAND
Component: contrib.postgres Version: 1.9
Severity: Normal Keywords: jsonb, postgresql
Cc: Marc Tamlyn Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Given this model:

class MyModel(models.Model):
    jsonbfield = JSONField(null=True, blank=True, default=dict)

The following filter will always result in a "ProgrammingError: operator does not exist jsonb -> boolean":

MyModel.objects.filter(jsonbfield__key__isnull=True)

That's because this is beng translated in the following SQL expression:

... where myapp_mymodel.jsonbfield -> 'key' is null

which does not yield the desired result because in postgres the ->, ->> or #> operators have no precedence over is.

Fix: enclose mapping in parenthesis

(myapp_mymodel.jsonbfield -> 'key') is null

Relevant code:

File: django/contrib/postgres/fields/jsonb.py

row 76 and 83:

return "{} #> %s".format(lhs), [key_transforms] + params
...
return "%s -> %s" % (lhs, lookup), params

Should become:

return "({} #> %s)".format(lhs), [key_transforms] + params
...
return "(%s -> %s)" % (lhs, lookup), params

Change History (9)

comment:1 by Tim Graham, 10 years ago

Has patch: set
Summary: jsonb field: error with __isnull lookup → jsonfield__key__isnull lookup crashes
Triage Stage: Unreviewed → Accepted

I agree that Django shouldn't crash. Is this expected to give the same semantics as has_key? Is there any advantage of using one form over the other that might be worth documenting? ​PR

comment:2 by Luca Simone Sigfrido Percich, 10 years ago

You're right, I didn't notice:

(('{"key": null}' :: jsonb) -> 'key') = 'null'::jsonb

-- which is different from SQL null, thus:

(('{"key": null}' :: jsonb) -> 'key') is null = false

-- but:

(('{"key": null}' :: jsonb) ->> 'key') is null = true

so after the fix, I believe the isnull lookup would behave exactly as has_key.

I won't expect that. There would be no way to find instances having a {'key':null}.

On the other hand, on PG docs they say that jsonb 'null' and SQL NULL have different meaning. Should we introduce a new lookup is_null_json maybe?

comment:3 by Tim Graham, 10 years ago

Easy pickings: unset
Has patch: unset

I'm not certain, but I guess it might be better to override the isnull lookup to work as you expect. I'll withdraw my patch.

comment:4 by PREMANAND, 10 years ago

Owner: set to PREMANAND
Status: new → assigned

comment:5 by PREMANAND, 10 years ago

@sigfrid/@tim

Im unable to recreate this issue using the dev version of the Django code.

>>> MyModel.objects.filter(jsonbfield__key__isnull=True)
<QuerySet []>

and here is my model.

class MyModel(models.Model):
    jsonbfield = JSONField(null=True, blank=True, default=dict)


comment:6 by Tim Graham, 10 years ago

Summary: jsonfield__key__isnull lookup crashes → jsonfield__key__isnull lookup crashes on PostgreSQL 9.4

Looks like the crash happens on PostgreSQL 9.4 but not 9.5. The test on ​my PR passes on 9.5 which could make it more difficult to change the behavior since it would change the behavior of possibly working code.

in reply to:  6 comment:7 by PREMANAND, 10 years ago

ok,got it. I'm using 9.5 as expected. How do we proceed on these issues?
Replying to timgraham:

Looks like the crash happens on PostgreSQL 9.4 but not 9.5. The test on ​my PR passes on 9.5 which could make it more difficult to change the behavior since it would change the behavior of possibly working code.

comment:8 by Tim Graham, 10 years ago

Cc: Marc Tamlyn added
Has patch: set

I guess we might proceed with ​my PR unless anyone feels like we should change the behavior of the isnull lookup. We could then open a new ticket to add a way to query for keys that have a 'null' value, if one doesn't exist.

comment:9 by GitHub <noreply@…>, 10 years ago

Resolution: → fixed
Status: assigned → closed

In 2eb7d6e6:

Fixed #26908 -- Fixed crash with jsonfieldkeyisnull lookup.

Note: See TracTickets for help on using tickets.
Back to Top