Opened 3 months ago

Last modified 3 months ago

#36453 closed Bug

5.2.3 introduces a regression when using `Value(None, output_field=JSONField()` in a `When` clause. — at Version 1

Reported by: Thomas Owned by:
Component: Database layer (models, ORM) Version: 5.2
Severity: Release blocker Keywords:
Cc: Thomas Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Thomas)

We've tried to upgrade to 5.2.3 and several of our tests started failing. We've pinpointed the problem to one query which uses Value(None, output_field=JSONField() in a When clause. Here's a minimal reproducible example (with a WHERE clause too, to highlight the difference in behavior):

from django.db import connection
from django.db.models import (
    BigIntegerField,
    Case,
    F,
    JSONField,
    Model,
    Value,
    When,
)
from django.db.models.functions import Cast


class Foo(Model):
    bar = BigIntegerField(blank=True, null=True)
    json_field = JSONField(blank=True, null=True)


if __name__ == '__main__':
    Foo.objects.filter(
        json_field__key_1=Value(None, output_field=JSONField())
    ).update(
        bar=Case(
            When(
                json_field__key_2=Value(None, output_field=JSONField()),
                then=None
            ),
            default=Cast(F('json_field__key_2'), BigIntegerField()),
            output_field=BigIntegerField(),
        ),
    )

    print(connection.queries[-1]['sql'])

We ran this code with 5.2.2 (with this version our test suite passes) and 5.2.3 (with this version our test suite fails) and got this:

-- Generated with Django 5.2.2
UPDATE "polls_foo"
SET "bar" = CASE WHEN (
        ("polls_foo"."json_field" -> 'key_2') = 'null'::jsonb  -- JSON "null"
    )
    THEN NULL
    ELSE (("polls_foo"."json_field" -> 'key_2'))::bigint
    END
WHERE ("polls_foo"."json_field" -> 'key_1') = 'null'::jsonb;

-- Generated with Django 5.2.3
UPDATE "polls_foo"
SET "bar" = CASE WHEN (
        ("polls_foo"."json_field" -> 'key_2') = NULL  -- SQL NULL
    )
    THEN NULL
    ELSE (("polls_foo"."json_field" -> 'key_2'))::bigint
    END
WHERE ("polls_foo"."json_field" -> 'key_1') = 'null'::jsonb;

Notice how the WHEN clause differs, but the WHERE clause stays the same, despite both using the same Value(None, output_field=JSONField()) syntax.

Edit: If that makes any difference, we're using PostgreSQL as a backend.

Change History (1)

comment:1 by Thomas, 3 months ago

Description: modified (diff)
Note: See TracTickets for help on using tickets.
Back to Top