Skip to content

Commit 5834643

Browse files
CharStringjacobtylerwalls
authored andcommitted
Fixed #36748 -- Filtered non-standard placeholders from UNNEST queries.
1 parent ff843bc commit 5834643

6 files changed

Lines changed: 49 additions & 0 deletions

File tree

django/db/backends/postgresql/compiler.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ def assemble_as_sql(self, fields, value_rows):
3636
# Lack of fields denote the usage of the DEFAULT keyword
3737
# for the insertion of empty rows.
3838
or any(field is None for field in fields)
39+
# Field.get_placeholder takes value as an argument, so the
40+
# resulting placeholder might be dependent on the value.
41+
# in UNNEST requires a single placeholder to "fit all values" in
42+
# the array.
43+
or any(hasattr(field, "get_placeholder") for field in fields)
3944
# Fields that don't use standard internal types might not be
4045
# unnest'able (e.g. array and geometry types are known to be
4146
# problematic).

docs/releases/5.2.9.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@ Bugfixes
1313
``django.utils.feedgenerator.Stylesheet.__str__()`` did not escape
1414
the ``url``, ``mimetype``, and ``media`` attributes, potentially leading
1515
to invalid XML markup (:ticket:`36733`).
16+
17+
* Fixed a bug in Django 5.2 on PostgreSQL where ``bulk_create()`` did not apply
18+
a field's custom query placeholders (:ticket:`36748`).

tests/postgres_tests/fields.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,8 @@ def deconstruct(self):
5757
class EnumField(models.CharField):
5858
def get_prep_value(self, value):
5959
return value.value if isinstance(value, enum.Enum) else value
60+
61+
62+
class OffByOneField(models.IntegerField):
63+
def get_placeholder(self, value, compiler, connection):
64+
return "(%s + 1)"

tests/postgres_tests/migrations/0002_create_test_models.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
EnumField,
1010
HStoreField,
1111
IntegerRangeField,
12+
OffByOneField,
1213
SearchVectorField,
1314
)
1415
from ..models import TagField
@@ -565,4 +566,26 @@ class Migration(migrations.Migration):
565566
"required_db_vendor": "postgresql",
566567
},
567568
),
569+
migrations.CreateModel(
570+
name="OffByOneModel",
571+
fields=[
572+
(
573+
"id",
574+
models.BigAutoField(
575+
verbose_name="ID",
576+
serialize=False,
577+
auto_created=True,
578+
primary_key=True,
579+
),
580+
),
581+
(
582+
"one_off",
583+
OffByOneField(),
584+
),
585+
],
586+
options={
587+
"required_db_vendor": "postgresql",
588+
},
589+
bases=(models.Model,),
590+
),
568591
]

tests/postgres_tests/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
EnumField,
1010
HStoreField,
1111
IntegerRangeField,
12+
OffByOneField,
1213
SearchVectorField,
1314
)
1415

@@ -207,3 +208,7 @@ class HotelReservation(PostgreSQLModel):
207208
end = models.DateTimeField()
208209
cancelled = models.BooleanField(default=False)
209210
requirements = models.JSONField(blank=True, null=True)
211+
212+
213+
class OffByOneModel(PostgreSQLModel):
214+
one_off = OffByOneField()

tests/postgres_tests/test_bulk_update.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
IntegerArrayModel,
77
NestedIntegerArrayModel,
88
NullableIntegerArrayModel,
9+
OffByOneModel,
910
OtherTypesArrayModel,
1011
RangesModel,
1112
)
@@ -44,3 +45,10 @@ def test_bulk_update(self):
4445
self.assertSequenceEqual(
4546
Model.objects.filter(**{field: new}), instances
4647
)
48+
49+
def test_bulk_create(self):
50+
OffByOneModel.objects.bulk_create(OffByOneModel(one_off=0) for _ in range(20))
51+
52+
self.assertSequenceEqual(
53+
[m.one_off for m in OffByOneModel.objects.all()], 20 * [1]
54+
)

0 commit comments

Comments
 (0)