diff --git a/django/db/backends/postgresql/compiler.py b/django/db/backends/postgresql/compiler.py index 3b972b5ba5..c4080ac037 100644 --- a/django/db/backends/postgresql/compiler.py +++ b/django/db/backends/postgresql/compiler.py @@ -27,8 +27,8 @@ class InsertUnnest(list): class SQLInsertCompiler(BaseSQLInsertCompiler): def assemble_as_sql(self, fields, value_rows): - # Specialize bulk-insertion of literal non-array values through - # UNNEST to reduce the time spent planning the query. + # Specialize bulk-insertion of literal values through UNNEST to + # reduce the time spent planning the query. if ( # The optimization is not worth doing if there is a single # row as it will result in the same number of placeholders. @@ -36,15 +36,18 @@ class SQLInsertCompiler(BaseSQLInsertCompiler): # Lack of fields denote the usage of the DEFAULT keyword # for the insertion of empty rows. or any(field is None for field in fields) + # Fields that don't use standard internal types might not be + # unnest'able (e.g. array and geometry types are known to be + # problematic). + or any( + field.get_internal_type() not in self.connection.data_types + for field in fields + ) # Compilable cannot be combined in an array of literal values. or any(any(hasattr(value, "as_sql") for value in row) for row in value_rows) ): return super().assemble_as_sql(fields, value_rows) db_types = [field.db_type(self.connection) for field in fields] - # Abort if any of the fields are arrays as UNNEST indiscriminately - # flatten them instead of reducing their nesting by one. - if any(db_type.endswith("]") for db_type in db_types): - return super().assemble_as_sql(fields, value_rows) return InsertUnnest(["(%%s)::%s[]" % db_type for db_type in db_types]), [ list(map(list, zip(*value_rows))) ] diff --git a/docs/releases/5.2.1.txt b/docs/releases/5.2.1.txt index b17e63ebd1..0f95eda848 100644 --- a/docs/releases/5.2.1.txt +++ b/docs/releases/5.2.1.txt @@ -15,3 +15,7 @@ Bugfixes * Fixed a regression in Django 5.2 that caused unnecessary queries when prefetching nullable foreign key relationships (:ticket:`36290`). + +* Fixed a regression in Django 5.2 that caused a crash of + ``QuerySet.bulk_create()`` with nullable geometry fields on PostGIS + (:ticket:`36289`). diff --git a/tests/gis_tests/geo3d/models.py b/tests/gis_tests/geo3d/models.py index 456be077f0..a09c599b02 100644 --- a/tests/gis_tests/geo3d/models.py +++ b/tests/gis_tests/geo3d/models.py @@ -58,7 +58,7 @@ class SimpleModel(models.Model): class Point2D(SimpleModel): - point = models.PointField() + point = models.PointField(null=True) class Point3D(SimpleModel): diff --git a/tests/gis_tests/geo3d/tests.py b/tests/gis_tests/geo3d/tests.py index b37deabb46..6a9376f2f8 100644 --- a/tests/gis_tests/geo3d/tests.py +++ b/tests/gis_tests/geo3d/tests.py @@ -206,6 +206,10 @@ class Geo3DTest(Geo3DLoadingHelper, TestCase): lm.save() self.assertEqual(3, MultiPoint3D.objects.count()) + def test_bulk_create_point_field(self): + objs = Point2D.objects.bulk_create([Point2D(), Point2D()]) + self.assertEqual(len(objs), 2) + @skipUnlessDBFeature("supports_3d_functions") def test_union(self): """