Fixed #32645 -- Fixed QuerySet.update() crash when ordered by joined fields on MySQL/MariaDB.
Thanks Matt Westcott for the report.
Regression in 779e615e36
.
This commit is contained in:
parent
338eb65b6e
commit
ca98729055
|
@ -1,4 +1,5 @@
|
||||||
from django.core.exceptions import FieldError
|
from django.core.exceptions import FieldError
|
||||||
|
from django.db.models.expressions import Col
|
||||||
from django.db.models.sql import compiler
|
from django.db.models.sql import compiler
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,8 +46,16 @@ class SQLUpdateCompiler(compiler.SQLUpdateCompiler, SQLCompiler):
|
||||||
if self.query.order_by:
|
if self.query.order_by:
|
||||||
order_by_sql = []
|
order_by_sql = []
|
||||||
order_by_params = []
|
order_by_params = []
|
||||||
|
db_table = self.query.get_meta().db_table
|
||||||
try:
|
try:
|
||||||
for _, (sql, params, _) in self.get_order_by():
|
for resolved, (sql, params, _) in self.get_order_by():
|
||||||
|
if (
|
||||||
|
isinstance(resolved.expression, Col) and
|
||||||
|
resolved.expression.alias != db_table
|
||||||
|
):
|
||||||
|
# Ignore ordering if it contains joined fields, because
|
||||||
|
# they cannot be used in the ORDER BY clause.
|
||||||
|
raise FieldError
|
||||||
order_by_sql.append(sql)
|
order_by_sql.append(sql)
|
||||||
order_by_params.extend(params)
|
order_by_params.extend(params)
|
||||||
update_query += ' ORDER BY ' + ', '.join(order_by_sql)
|
update_query += ' ORDER BY ' + ', '.join(order_by_sql)
|
||||||
|
|
|
@ -2646,7 +2646,8 @@ unique field in the order that is specified without conflicts. For example::
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
If the ``order_by()`` clause contains annotations, it will be ignored.
|
``order_by()`` clause will be ignored if it contains annotations, inherited
|
||||||
|
fields, or lookups spanning relations.
|
||||||
|
|
||||||
``delete()``
|
``delete()``
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
|
@ -36,3 +36,7 @@ Bugfixes
|
||||||
|
|
||||||
* Fixed a regression in Django 3.2 that caused a crash when combining ``Q()``
|
* Fixed a regression in Django 3.2 that caused a crash when combining ``Q()``
|
||||||
objects which contains boolean expressions (:ticket:`32548`).
|
objects which contains boolean expressions (:ticket:`32548`).
|
||||||
|
|
||||||
|
* Fixed a regression in Django 3.2 that caused a crash of ``QuerySet.update()``
|
||||||
|
on a queryset ordered by inherited or joined fields on MySQL and MariaDB
|
||||||
|
(:ticket:`32645`).
|
||||||
|
|
|
@ -45,3 +45,7 @@ class Bar(models.Model):
|
||||||
|
|
||||||
class UniqueNumber(models.Model):
|
class UniqueNumber(models.Model):
|
||||||
number = models.IntegerField(unique=True)
|
number = models.IntegerField(unique=True)
|
||||||
|
|
||||||
|
|
||||||
|
class UniqueNumberChild(UniqueNumber):
|
||||||
|
pass
|
||||||
|
|
|
@ -7,7 +7,10 @@ from django.db.models.functions import Abs, Concat, Lower
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.utils import register_lookup
|
from django.test.utils import register_lookup
|
||||||
|
|
||||||
from .models import A, B, Bar, D, DataPoint, Foo, RelatedPoint, UniqueNumber
|
from .models import (
|
||||||
|
A, B, Bar, D, DataPoint, Foo, RelatedPoint, UniqueNumber,
|
||||||
|
UniqueNumberChild,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class SimpleTest(TestCase):
|
class SimpleTest(TestCase):
|
||||||
|
@ -249,3 +252,26 @@ class MySQLUpdateOrderByTest(TestCase):
|
||||||
).order_by('number_inverse').update(
|
).order_by('number_inverse').update(
|
||||||
number=F('number') + 1,
|
number=F('number') + 1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_order_by_update_on_parent_unique_constraint(self):
|
||||||
|
# Ordering by inherited fields is omitted because joined fields cannot
|
||||||
|
# be used in the ORDER BY clause.
|
||||||
|
UniqueNumberChild.objects.create(number=3)
|
||||||
|
UniqueNumberChild.objects.create(number=4)
|
||||||
|
with self.assertRaises(IntegrityError):
|
||||||
|
UniqueNumberChild.objects.order_by('number').update(
|
||||||
|
number=F('number') + 1,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_order_by_update_on_related_field(self):
|
||||||
|
# Ordering by related fields is omitted because joined fields cannot be
|
||||||
|
# used in the ORDER BY clause.
|
||||||
|
data = DataPoint.objects.create(name='d0', value='apple')
|
||||||
|
related = RelatedPoint.objects.create(name='r0', data=data)
|
||||||
|
with self.assertNumQueries(1) as ctx:
|
||||||
|
updated = RelatedPoint.objects.order_by('data__name').update(name='new')
|
||||||
|
sql = ctx.captured_queries[0]['sql']
|
||||||
|
self.assertNotIn('ORDER BY', sql)
|
||||||
|
self.assertEqual(updated, 1)
|
||||||
|
related.refresh_from_db()
|
||||||
|
self.assertEqual(related.name, 'new')
|
||||||
|
|
Loading…
Reference in New Issue