Opened 7 weeks ago

Closed 6 weeks ago

Last modified 5 weeks ago

#36419 closed Bug (fixed)

bulk_update() incorrectly updates JSONField None to JSON 'null' instead of SQL NULL

Reported by: Clifford Gama Owned by: Clifford Gama
Component: Database layer (models, ORM) Version: 5.2
Severity: Release blocker Keywords:
Cc: Simon Charette, Sage Abdullah, Adam Johnson 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

Failing test:

  • tests/queries/test_bulk_update.py

    diff --git a/tests/queries/test_bulk_update.py b/tests/queries/test_bulk_update.py
    index 765fa934ca..2d24a74a60 100644
    a b class BulkUpdateTests(TestCase):  
    300300            JSONFieldNullable.objects.filter(json_field__has_key="c"), objs
    301301        )
    302302
     303    @skipUnlessDBFeature("supports_json_field")
     304    def test_json_field_none_value(self):
     305        """Top level None value must be equivalent to SQL NULL in db."""
     306        obj = JSONFieldNullable.objects.create(json_field={"k": "v"})
     307        obj.json_field = None
     308        JSONFieldNullable.objects.bulk_update([obj], fields=["json_field"])
     309        obj.refresh_from_db()
     310        qs = JSONFieldNullable.objects.filter(json_field__isnull=True)
     311        self.assertSequenceEqual(qs, [obj])
     312
    303313    def test_nullable_fk_after_related_save(self):
    304314        parent = RelatedObject.objects.create()
    305315        child = SingleObject()

This is inconsistent with create(), update() and save() (see test and docs), and can cause unexpected behaviour when a JSONField has null=False, since JSON null does not violate null=False.

Change History (14)

comment:1 by Clifford Gama, 7 weeks ago

Has patch: set

comment:2 by Natalia Bidart, 7 weeks ago

Cc: Simon Charette Sage Abdullah added
Triage Stage: UnreviewedAccepted

Hello Clifford, thank you for your investigation on #36418 and for creating this ticket. If you ask me, I think the reporter in #36418 was reporting this exact same thing, but I also agree the report was a bit confusing (since it was not stated what was the expected outcome). I'll be accepting this ticket based on that, and also because for models with a null=False JSONField, this code does raise IntegrityError:

obj = JSONModel()                                                      
obj.value = None                                                       
obj.save()                                                             

but bulk_update does not.

comment:3 by Clifford Gama, 7 weeks ago

Summary: bulk_update() incorrectly updates top-level JSONField value to JSON 'null' instead of SQL NULLbulk_update() incorrectly updates JSONField None to JSON 'null' instead of SQL NULL

Thanks for the triage, Natalia.

Last edited 7 weeks ago by Clifford Gama (previous) (diff)

comment:4 by Jacob Walls, 6 weeks ago

Severity: NormalRelease blocker

#36440 was a dupe. Adam bisected it to 5.2 commit e306687a3a5507d59365ba9bf545010e5fd4b2a8, so elevating to blocker for now.

Last edited 6 weeks ago by Natalia Bidart (previous) (diff)

comment:5 by Adam Johnson, 6 weeks ago

Cc: Adam Johnson added

comment:6 by Sarah Boyce, 6 weeks ago

Version: dev5.2

comment:7 by Natalia Bidart, 6 weeks ago

Severity: Release blockerNormal

For what is worth, the test_json_field_json_null_value as presented in the proposed branch fails in 5.1.x, 4.2.x, 4.1.x, 4.0.x and 3.2.x, so unclear this is truly a release blocker for 5.2.x.

Adam, could you provide more details about your bisect outcome?

Last edited 6 weeks ago by Natalia Bidart (previous) (diff)

comment:8 by Clifford Gama, 6 weeks ago

Severity: NormalRelease blocker

Hello Natalia. I did a git bisect for test_json_field_sql_null_value and the issue is indeed a regression in 00c690efbc0b10f67924687f24a7b30397bf47d9. The test by test_json_field_json_null_value is something I had assumed to have always worked and isn't actually a regression test for the issue in this ticket.

It seems bulk_update() was creating SQL NULL even for Value(None, output_field=JSONField()) maybe since JSONField was introduced.

Last edited 6 weeks ago by Clifford Gama (previous) (diff)

comment:9 by Adam Johnson, 6 weeks ago

Adam, could you provide more details about your bisect outcome?

I bisected for my client project failure, so unfortunately, I can't really share that. I found that the client test failed for e306687a3a5507d59365ba9bf545010e5fd4b2a8.

I then minimized the issue into the test provided in https://github.com/django/django/pull/19527 , which reproduces the exception I encountered. I didn't bisect with that test before.

I just tried bisecting with it and found 1df0f998ae8d1626adaa9b807a43e4cbdfc590e2, the 5.2 backport of 00c690efbc0b10f67924687f24a7b30397bf47d9, is indeed the first commit that makes my test fail.

So it seems we're working on the same issue, but it may be that there may be some other failure lurking in the client project test. I'll try to check, but no promises!

comment:10 by Sarah Boyce, 6 weeks ago

Triage Stage: AcceptedReady for checkin

comment:11 by Sarah Boyce <42296566+sarahboyce@…>, 6 weeks ago

Resolution: fixed
Status: assignedclosed

In c1fa3fdd:

Fixed #36419 -- Ensured for_save was propagated when resolving expressions.

The for_save flag wasn't properly propagated when resolving expressions, which
prevented get_db_prep_save() from being called in some cases. This affected
fields like JSONField where None would be saved as JSON null instead of SQL NULL.

Regression in 00c690efbc0b10f67924687f24a7b30397bf47d9.

Thanks to David Sanders and Simon Charette for reviews.

Co-authored-by: Adam Johnson <me@…>

comment:12 by Sarah Boyce <42296566+sarahboyce@…>, 6 weeks ago

In 6fc620b4:

[5.2.x] Fixed #36419 -- Ensured for_save was propagated when resolving expressions.

The for_save flag wasn't properly propagated when resolving expressions, which
prevented get_db_prep_save() from being called in some cases. This affected
fields like JSONField where None would be saved as JSON null instead of SQL NULL.

Regression in 00c690efbc0b10f67924687f24a7b30397bf47d9.

Thanks to David Sanders and Simon Charette for reviews.

Co-authored-by: Adam Johnson <me@…>

Backport of c1fa3fdd040718356e5a3b9a0fe699d73f47a940 from main.

comment:13 by GitHub <noreply@…>, 5 weeks ago

In f5441e42:

Refs #36419 -- Fixed BulkUpdateTests.test_json_field_sql_null() crash on Oracle.

Follow up to c1fa3fdd040718356e5a3b9a0fe699d73f47a940.

comment:14 by Mariusz Felisiak <felisiak.mariusz@…>, 5 weeks ago

In aec11db:

[5.2.x] Refs #36419 -- Fixed BulkUpdateTests.test_json_field_sql_null() crash on Oracle.

Follow up to c1fa3fdd040718356e5a3b9a0fe699d73f47a940.

Backport of f5441e42da691ee2e7aeeb9be70f98e2bce6d17d from main.

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