Fixed #14244: Allow lists of more than 1000 items to be used with the 'in' lookup in Oracle, by breaking them up into groups of 1000 items and ORing them together. Thanks to rlynch for the report and initial patch.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@13859 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
763bcf8472
commit
320c46999c
|
@ -233,6 +233,13 @@ class BaseDatabaseOperations(object):
|
||||||
"""
|
"""
|
||||||
return "%s"
|
return "%s"
|
||||||
|
|
||||||
|
def max_in_list_size(self):
|
||||||
|
"""
|
||||||
|
Returns the maximum number of items that can be passed in a single 'IN'
|
||||||
|
list condition, or None if the backend does not impose a limit.
|
||||||
|
"""
|
||||||
|
return None
|
||||||
|
|
||||||
def max_name_length(self):
|
def max_name_length(self):
|
||||||
"""
|
"""
|
||||||
Returns the maximum length of table and column names, or None if there
|
Returns the maximum length of table and column names, or None if there
|
||||||
|
|
|
@ -178,6 +178,9 @@ WHEN (new.%(col_name)s IS NULL)
|
||||||
return "UPPER(%s)"
|
return "UPPER(%s)"
|
||||||
return "%s"
|
return "%s"
|
||||||
|
|
||||||
|
def max_in_list_size(self):
|
||||||
|
return 1000
|
||||||
|
|
||||||
def max_name_length(self):
|
def max_name_length(self):
|
||||||
return 30
|
return 30
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
Code to manage the creation and SQL rendering of 'where' constraints.
|
Code to manage the creation and SQL rendering of 'where' constraints.
|
||||||
"""
|
"""
|
||||||
import datetime
|
import datetime
|
||||||
|
from itertools import repeat
|
||||||
|
|
||||||
from django.utils import tree
|
from django.utils import tree
|
||||||
from django.db.models.fields import Field
|
from django.db.models.fields import Field
|
||||||
|
@ -178,7 +179,23 @@ class WhereNode(tree.Node):
|
||||||
raise EmptyResultSet
|
raise EmptyResultSet
|
||||||
if extra:
|
if extra:
|
||||||
return ('%s IN %s' % (field_sql, extra), params)
|
return ('%s IN %s' % (field_sql, extra), params)
|
||||||
return ('%s IN (%s)' % (field_sql, ', '.join(['%s'] * len(params))),
|
max_in_list_size = connection.ops.max_in_list_size()
|
||||||
|
if max_in_list_size and len(params) > max_in_list_size:
|
||||||
|
# Break up the params list into an OR of manageable chunks.
|
||||||
|
in_clause_elements = ['(']
|
||||||
|
for offset in xrange(0, len(params), max_in_list_size):
|
||||||
|
if offset > 0:
|
||||||
|
in_clause_elements.append(' OR ')
|
||||||
|
in_clause_elements.append('%s IN (' % field_sql)
|
||||||
|
group_size = min(len(params) - offset, max_in_list_size)
|
||||||
|
param_group = ', '.join(repeat('%s', group_size))
|
||||||
|
in_clause_elements.append(param_group)
|
||||||
|
in_clause_elements.append(')')
|
||||||
|
in_clause_elements.append(')')
|
||||||
|
return ''.join(in_clause_elements), params
|
||||||
|
else:
|
||||||
|
return ('%s IN (%s)' % (field_sql,
|
||||||
|
', '.join(repeat('%s', len(params)))),
|
||||||
params)
|
params)
|
||||||
elif lookup_type in ('range', 'year'):
|
elif lookup_type in ('range', 'year'):
|
||||||
return ('%s BETWEEN %%s and %%s' % field_sql, params)
|
return ('%s BETWEEN %%s and %%s' % field_sql, params)
|
||||||
|
|
|
@ -1339,3 +1339,23 @@ Using an empty generator expression as the rvalue for an "__in" lookup is legal
|
||||||
[]
|
[]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Sqlite 3 does not support passing in more than 1000 parameters except by
|
||||||
|
# changing a parameter at compilation time.
|
||||||
|
if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != "django.db.backends.sqlite3":
|
||||||
|
__test__["API_TESTS"] += """
|
||||||
|
Bug #14244: Test that the "in" lookup works with lists of 1000 items or more.
|
||||||
|
>>> Number.objects.all().delete()
|
||||||
|
>>> numbers = range(2500)
|
||||||
|
>>> for num in numbers:
|
||||||
|
... _ = Number.objects.create(num=num)
|
||||||
|
>>> Number.objects.filter(num__in=numbers[:1000]).count()
|
||||||
|
1000
|
||||||
|
>>> Number.objects.filter(num__in=numbers[:1001]).count()
|
||||||
|
1001
|
||||||
|
>>> Number.objects.filter(num__in=numbers[:2000]).count()
|
||||||
|
2000
|
||||||
|
>>> Number.objects.filter(num__in=numbers).count()
|
||||||
|
2500
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
Loading…
Reference in New Issue