1
0
mirror of https://github.com/django/django.git synced 2025-07-04 09:49:12 +00:00

[1.1.X] Fixed #12328 -- Corrected the handling of subqueries with ordering and 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).

Backport of r12912 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.1.X@12914 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2010-04-04 17:17:46 +00:00
parent 17636ef999
commit 0d6a776ccd
6 changed files with 59 additions and 13 deletions

View File

@ -102,6 +102,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):
"""

View File

@ -113,6 +113,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):
def date_extract_sql(self, lookup_type, field_name):

View File

@ -51,6 +51,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):

View File

@ -7,6 +7,8 @@ try:
except NameError:
from sets import Set as set # Python 2.3 fallback
from itertools import izip
from django.db import connection, transaction, IntegrityError
from django.db.models.aggregates import Aggregate
from django.db.models.fields import DateField
@ -387,11 +389,13 @@ class QuerySet(object):
# 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

View File

@ -456,13 +456,14 @@ class BaseQuery(object):
"""
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.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.as_sql()

View File

@ -1,6 +1,10 @@
import unittest
from models import Tag, Annotation
from django.db import DatabaseError, connection
from django.db.models import Count
from django.test import TestCase
from models import Tag, Annotation, DumbCategory
class QuerysetOrderedTests(unittest.TestCase):
"""
@ -25,3 +29,37 @@ class QuerysetOrderedTests(unittest.TestCase):
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(connection.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(connection.features.allow_sliced_subqueries)