Browse Source

Fixed #27193 -- Preserved ordering in select_for_update subqueries.

François Freitag 8 years ago
parent
commit
8ac115c730
2 changed files with 14 additions and 2 deletions
  1. 5 2
      django/db/models/sql/compiler.py
  2. 9 0
      tests/select_for_update/tests.py

+ 5 - 2
django/db/models/sql/compiler.py

@@ -476,8 +476,11 @@ class SQLCompiler(object):
         Used when nesting this query inside another.
         """
         obj = self.query.clone()
-        if obj.low_mark == 0 and obj.high_mark is None and not self.query.distinct_fields:
-            # If there is no slicing in use, then we can safely drop all ordering
+        # It's safe to drop ordering if the queryset isn't using slicing,
+        # distinct(*fields) or select_for_update().
+        if (obj.low_mark == 0 and obj.high_mark is None and
+                not self.query.distinct_fields and
+                not self.query.select_for_update):
             obj.clear_ordering(True)
         nested_sql = obj.get_compiler(connection=self.connection).as_sql(subquery=True)
         if nested_sql == ('', ()):

+ 9 - 0
tests/select_for_update/tests.py

@@ -300,3 +300,12 @@ class SelectForUpdateTests(TransactionTestCase):
     def test_nowait_and_skip_locked(self):
         with self.assertRaisesMessage(ValueError, 'The nowait option cannot be used with skip_locked.'):
             Person.objects.select_for_update(nowait=True, skip_locked=True)
+
+    def test_ordered_select_for_update(self):
+        """
+        Subqueries should respect ordering as an ORDER BY clause may be useful
+        to specify a row locking order to prevent deadlocks (#27193).
+        """
+        with transaction.atomic():
+            qs = Person.objects.filter(id__in=Person.objects.order_by('-id').select_for_update())
+            self.assertIn('ORDER BY', str(qs.query))