Skip to content

Commit

Permalink
Fixed #36107 -- Adjusted UNNEST bulk_create strategy to opt-out sized…
Browse files Browse the repository at this point in the history
… arrays.

The array fields opt-out heuristic failed to account for sized arrays.

Note that we keep relying on db_type as opposed to performing an ArrayField
instance check against the column's field as there could be other
implementations of model fields that use Postgres arrays to store the
optimization must be disabled for all of them.

Refs #35936.

Thanks Claude Paroz for the report and test.
  • Loading branch information
charettes authored and sarahboyce committed Jan 20, 2025
1 parent f5772de commit 22fc151
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 1 deletion.
2 changes: 1 addition & 1 deletion django/db/backends/postgresql/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def assemble_as_sql(self, 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):
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)))
Expand Down
22 changes: 22 additions & 0 deletions tests/postgres_tests/migrations/0002_create_test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,28 @@ class Migration(migrations.Migration):
},
bases=(models.Model,),
),
migrations.CreateModel(
name="WithSizeArrayModel",
fields=[
(
"id",
models.AutoField(
verbose_name="ID",
serialize=False,
auto_created=True,
primary_key=True,
),
),
(
"field",
ArrayField(models.FloatField(), size=2, null=True, blank=True),
),
],
options={
"required_db_vendor": "postgresql",
},
bases=(models.Model,),
),
migrations.CreateModel(
name="NullableIntegerArrayModel",
fields=[
Expand Down
4 changes: 4 additions & 0 deletions tests/postgres_tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ class DateTimeArrayModel(PostgreSQLModel):
times = ArrayField(models.TimeField())


class WithSizeArrayModel(PostgreSQLModel):
field = ArrayField(models.FloatField(), size=3)


class NestedIntegerArrayModel(PostgreSQLModel):
field = ArrayField(ArrayField(models.IntegerField()))

Expand Down
11 changes: 11 additions & 0 deletions tests/postgres_tests/test_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
OtherTypesArrayModel,
PostgreSQLModel,
Tag,
WithSizeArrayModel,
)

try:
Expand Down Expand Up @@ -216,6 +217,16 @@ def setUpTestData(cls):
]
)

def test_bulk_create_with_sized_arrayfield(self):
objs = WithSizeArrayModel.objects.bulk_create(
[
WithSizeArrayModel(field=[1, 2]),
WithSizeArrayModel(field=[3, 4]),
]
)
self.assertEqual(objs[0].field, [1, 2])
self.assertEqual(objs[1].field, [3, 4])

def test_empty_list(self):
NullableIntegerArrayModel.objects.create(field=[])
obj = (
Expand Down

0 comments on commit 22fc151

Please sign in to comment.