summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2021-04-14 21:11:17 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2021-04-14 21:13:27 +0200
commit208e72276a3e12a4e7998b9a1219bc96a16cf7b8 (patch)
tree0b82534d3aef1eecaf3b83a224cc11a0fb1f0fe0
parentd0267690f8a8e83065459d13a5a6f29e75640f78 (diff)
[3.2.x] Fixed #32645 -- Fixed QuerySet.update() crash when ordered by joined fields on MySQL/MariaDB.
Thanks Matt Westcott for the report. Regression in 779e615e362108862f1681f965ee9e4f1d0ae6d2. Backport of ca9872905559026af82000e46cde6f7dedc897b6 from main
-rw-r--r--django/db/backends/mysql/compiler.py11
-rw-r--r--docs/ref/models/querysets.txt3
-rw-r--r--docs/releases/3.2.1.txt4
-rw-r--r--tests/update/models.py4
-rw-r--r--tests/update/tests.py28
5 files changed, 47 insertions, 3 deletions
diff --git a/django/db/backends/mysql/compiler.py b/django/db/backends/mysql/compiler.py
index da02c1fd73..49b47961a1 100644
--- a/django/db/backends/mysql/compiler.py
+++ b/django/db/backends/mysql/compiler.py
@@ -1,4 +1,5 @@
from django.core.exceptions import FieldError
+from django.db.models.expressions import Col
from django.db.models.sql import compiler
@@ -45,8 +46,16 @@ class SQLUpdateCompiler(compiler.SQLUpdateCompiler, SQLCompiler):
if self.query.order_by:
order_by_sql = []
order_by_params = []
+ db_table = self.query.get_meta().db_table
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_params.extend(params)
update_query += ' ORDER BY ' + ', '.join(order_by_sql)
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
index 7b6709ae8c..c840b6cd89 100644
--- a/docs/ref/models/querysets.txt
+++ b/docs/ref/models/querysets.txt
@@ -2645,7 +2645,8 @@ unique field in the order that is specified without conflicts. For example::
.. 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()``
~~~~~~~~~~~~
diff --git a/docs/releases/3.2.1.txt b/docs/releases/3.2.1.txt
index df8d192327..3de4b385c9 100644
--- a/docs/releases/3.2.1.txt
+++ b/docs/releases/3.2.1.txt
@@ -36,3 +36,7 @@ Bugfixes
* Fixed a regression in Django 3.2 that caused a crash when combining ``Q()``
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`).
diff --git a/tests/update/models.py b/tests/update/models.py
index 98f40a8603..131c5b327e 100644
--- a/tests/update/models.py
+++ b/tests/update/models.py
@@ -45,3 +45,7 @@ class Bar(models.Model):
class UniqueNumber(models.Model):
number = models.IntegerField(unique=True)
+
+
+class UniqueNumberChild(UniqueNumber):
+ pass
diff --git a/tests/update/tests.py b/tests/update/tests.py
index 588db40de6..ccaeb13c58 100644
--- a/tests/update/tests.py
+++ b/tests/update/tests.py
@@ -7,7 +7,10 @@ from django.db.models.functions import Abs, Concat, Lower
from django.test import TestCase
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):
@@ -249,3 +252,26 @@ class MySQLUpdateOrderByTest(TestCase):
).order_by('number_inverse').update(
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')