From 345e3cf57f1e9bf870482cebf36b169d79ae63d2 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Tue, 26 Mar 2024 22:58:47 +0100 Subject: [PATCH] [5.0.x] Fixed #35329 -- Fixed migrations crash when adding partial unique constraints with nulls_distinct. Bug in 595a2abb58e04caa4d55fb2589bb80fb2a8fdfa1. Thanks Lucas Lemke Saunitti for the report. Backport of b98271a6e42107233311d17f5d7bc74fbb47f22c from main --- django/db/backends/base/schema.py | 2 +- docs/releases/5.0.4.txt | 4 ++++ tests/schema/tests.py | 32 +++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py index d2c8c65979..86bf8b821a 100644 --- a/django/db/backends/base/schema.py +++ b/django/db/backends/base/schema.py @@ -129,7 +129,7 @@ class BaseDatabaseSchemaEditor: ) sql_create_unique_index = ( "CREATE UNIQUE INDEX %(name)s ON %(table)s " - "(%(columns)s)%(include)s%(condition)s%(nulls_distinct)s" + "(%(columns)s)%(include)s%(nulls_distinct)s%(condition)s" ) sql_rename_index = "ALTER INDEX %(old_name)s RENAME TO %(new_name)s" sql_delete_index = "DROP INDEX %(name)s" diff --git a/docs/releases/5.0.4.txt b/docs/releases/5.0.4.txt index 9b2fd6b170..6503f1ada3 100644 --- a/docs/releases/5.0.4.txt +++ b/docs/releases/5.0.4.txt @@ -17,3 +17,7 @@ Bugfixes * Fixed a regression in Django 5.0 where the ``AdminFileWidget`` could be rendered with two ``id`` attributes on the "Clear" checkbox (:ticket:`35273`). + +* Fixed a bug in Django 5.0 that caused a migration crash on PostgreSQL 15+ + when adding a partial ``UniqueConstraint`` with ``nulls_distinct`` + (:ticket:`35329`). diff --git a/tests/schema/tests.py b/tests/schema/tests.py index 465245e3e7..fff4982315 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -3596,6 +3596,38 @@ class SchemaTests(TransactionTestCase): constraints = self.get_constraints(Author._meta.db_table) self.assertNotIn(constraint.name, constraints) + @skipUnlessDBFeature( + "supports_nulls_distinct_unique_constraints", + "supports_partial_indexes", + ) + def test_unique_constraint_nulls_distinct_condition(self): + with connection.schema_editor() as editor: + editor.create_model(Author) + constraint = UniqueConstraint( + fields=["height", "weight"], + name="un_height_weight_start_A", + condition=Q(name__startswith="A"), + nulls_distinct=False, + ) + with connection.schema_editor() as editor: + editor.add_constraint(Author, constraint) + Author.objects.create(name="Adam", height=None, weight=None) + Author.objects.create(name="Avocado", height=1, weight=None) + Author.objects.create(name="Adrian", height=None, weight=1) + with self.assertRaises(IntegrityError): + Author.objects.create(name="Alex", height=None, weight=None) + Author.objects.create(name="Bob", height=None, weight=None) + with self.assertRaises(IntegrityError): + Author.objects.create(name="Alex", height=1, weight=None) + Author.objects.create(name="Bill", height=None, weight=None) + with self.assertRaises(IntegrityError): + Author.objects.create(name="Alex", height=None, weight=1) + Author.objects.create(name="Celine", height=None, weight=1) + with connection.schema_editor() as editor: + editor.remove_constraint(Author, constraint) + constraints = self.get_constraints(Author._meta.db_table) + self.assertNotIn(constraint.name, constraints) + @skipIfDBFeature("supports_nulls_distinct_unique_constraints") def test_unique_constraint_nulls_distinct_unsupported(self): # UniqueConstraint is ignored on databases that don't support