Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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
  • Loading branch information...
commit 320c46999c44efdc31be9cc80d3a2d1bf5186f18 1 parent 763bcf8
Ian Kelly ikelly authored
7 django/db/backends/__init__.py
View
@@ -233,6 +233,13 @@ def lookup_cast(self, lookup_type):
"""
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):
"""
Returns the maximum length of table and column names, or None if there
3  django/db/backends/oracle/base.py
View
@@ -178,6 +178,9 @@ def lookup_cast(self, lookup_type):
return "UPPER(%s)"
return "%s"
+ def max_in_list_size(self):
+ return 1000
+
def max_name_length(self):
return 30
21 django/db/models/sql/where.py
View
@@ -2,6 +2,7 @@
Code to manage the creation and SQL rendering of 'where' constraints.
"""
import datetime
+from itertools import repeat
from django.utils import tree
from django.db.models.fields import Field
@@ -178,8 +179,24 @@ def make_atom(self, child, qn, connection):
raise EmptyResultSet
if extra:
return ('%s IN %s' % (field_sql, extra), params)
- return ('%s IN (%s)' % (field_sql, ', '.join(['%s'] * len(params))),
- 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)
elif lookup_type in ('range', 'year'):
return ('%s BETWEEN %%s and %%s' % field_sql, params)
elif lookup_type in ('month', 'day', 'week_day'):
20 tests/regressiontests/queries/models.py
View
@@ -1339,3 +1339,23 @@ def __unicode__(self):
[]
"""
+
+# 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
+
+"""
Please sign in to comment.
Something went wrong with that request. Please try again.