Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #12328 -- Corrected the handling of subqueries with ordering an…

…d slicing, especially when used in delete subqueries. Thanks to Walter Doekes for the report.

This fixes a feature that isn't available under MySQL and Oracle (Refs #10099).

git-svn-id: http://code.djangoproject.com/svn/django/trunk@12912 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 82efb4840311d549989ccf224fa78765226f6040 1 parent f92d73f
Russell Keith-Magee freakboy3742 authored
1  django/db/backends/__init__.py
View
@@ -95,6 +95,7 @@ class BaseDatabaseFeatures(object):
# If True, don't use integer foreign keys referring to, e.g., positive
# integer primary keys.
related_fields_match_type = False
+ allow_sliced_subqueries = True
class BaseDatabaseOperations(object):
"""
1  django/db/backends/mysql/base.py
View
@@ -123,6 +123,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
update_can_self_select = False
allows_group_by_pk = True
related_fields_match_type = True
+ allow_sliced_subqueries = False
class DatabaseOperations(BaseDatabaseOperations):
compiler_module = "django.db.backends.mysql.compiler"
1  django/db/backends/oracle/base.py
View
@@ -52,6 +52,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
interprets_empty_strings_as_nulls = True
uses_savepoints = True
can_return_id_from_insert = True
+ allow_sliced_subqueries = False
class DatabaseOperations(BaseDatabaseOperations):
9 django/db/models/query.py
View
@@ -3,6 +3,7 @@
"""
from copy import deepcopy
+from itertools import izip
from django.db import connections, router, transaction, IntegrityError
from django.db.models.aggregates import Aggregate
@@ -429,11 +430,13 @@ def delete(self):
# becoming too long.
seen_objs = None
while 1:
- # Collect all the objects to be deleted in this chunk, and all the
+ # Collect a chunk of objects to be deleted, and then all the
# objects that are related to the objects that are to be deleted.
+ # The chunking *isn't* done by slicing the del_query because we
+ # need to maintain the query cache on del_query (see #12328)
seen_objs = CollectedObjects(seen_objs)
- for object in del_query[:CHUNK_SIZE]:
- object._collect_sub_objects(seen_objs)
+ for i, obj in izip(xrange(CHUNK_SIZE), del_query):
+ obj._collect_sub_objects(seen_objs)
if not seen_objs:
break
8 django/db/models/sql/compiler.py
View
@@ -120,13 +120,15 @@ def as_nested_sql(self):
"""
Perform the same functionality as the as_sql() method, returning an
SQL string and parameters. However, the alias prefixes are bumped
- beforehand (in a copy -- the current query isn't changed) and any
- ordering is removed.
+ beforehand (in a copy -- the current query isn't changed), and any
+ ordering is removed if the query is unsliced.
Used when nesting this query inside another.
"""
obj = self.query.clone()
- obj.clear_ordering(True)
+ if obj.low_mark == 0 and obj.high_mark is None:
+ # If there is no slicing in use, then we can safely drop all ordering
+ obj.clear_ordering(True)
obj.bump_prefix()
return obj.get_compiler(connection=self.connection).as_sql()
50 tests/regressiontests/queries/tests.py
View
@@ -1,27 +1,65 @@
import unittest
-from models import Tag, Annotation
+
+from django.db import DatabaseError, connections, DEFAULT_DB_ALIAS
from django.db.models import Count
+from django.test import TestCase
+
+from models import Tag, Annotation, DumbCategory
class QuerysetOrderedTests(unittest.TestCase):
"""
Tests for the Queryset.ordered attribute.
"""
-
+
def test_no_default_or_explicit_ordering(self):
self.assertEqual(Annotation.objects.all().ordered, False)
def test_cleared_default_ordering(self):
self.assertEqual(Tag.objects.all().ordered, True)
self.assertEqual(Tag.objects.all().order_by().ordered, False)
-
+
def test_explicit_ordering(self):
self.assertEqual(Annotation.objects.all().order_by('id').ordered, True)
-
+
def test_order_by_extra(self):
self.assertEqual(Annotation.objects.all().extra(order_by=['id']).ordered, True)
-
+
def test_annotated_ordering(self):
qs = Annotation.objects.annotate(num_notes=Count('notes'))
self.assertEqual(qs.ordered, False)
self.assertEqual(qs.order_by('num_notes').ordered, True)
-
+
+
+class SubqueryTests(TestCase):
+ def setUp(self):
+ DumbCategory.objects.create(id=1)
+ DumbCategory.objects.create(id=2)
+ DumbCategory.objects.create(id=3)
+
+ def test_ordered_subselect(self):
+ "Subselects honor any manual ordering"
+ try:
+ query = DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[0:2])
+ self.assertEquals(set(query.values_list('id', flat=True)), set([2,3]))
+
+ query = DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[:2])
+ self.assertEquals(set(query.values_list('id', flat=True)), set([2,3]))
+
+ query = DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[2:])
+ self.assertEquals(set(query.values_list('id', flat=True)), set([1]))
+ except DatabaseError:
+ # Oracle and MySQL both have problems with sliced subselects.
+ # This prevents us from even evaluating this test case at all.
+ # Refs #10099
+ self.assertFalse(connections[DEFAULT_DB_ALIAS].features.allow_sliced_subqueries)
+
+ def test_sliced_delete(self):
+ "Delete queries can safely contain sliced subqueries"
+ try:
+ DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[0:1]).delete()
+ self.assertEquals(set(DumbCategory.objects.values_list('id', flat=True)), set([1,2]))
+ except DatabaseError:
+ # Oracle and MySQL both have problems with sliced subselects.
+ # This prevents us from even evaluating this test case at all.
+ # Refs #10099
+ self.assertFalse(connections[DEFAULT_DB_ALIAS].features.allow_sliced_subqueries)
Please sign in to comment.
Something went wrong with that request. Please try again.