Commit 320c4699 authored by Ian Kelly's avatar Ian Kelly
Browse files

Fixed #14244: Allow lists of more than 1000 items to be used with the 'in'...

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
parent 763bcf84
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -233,6 +233,13 @@ class BaseDatabaseOperations(object):
        """
        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 −0
Original line number Diff line number Diff line
@@ -178,6 +178,9 @@ WHEN (new.%(col_name)s IS NULL)
            return "UPPER(%s)"
        return "%s"

    def max_in_list_size(self):
        return 1000

    def max_name_length(self):
        return 30

+19 −2
Original line number Diff line number Diff line
@@ -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,7 +179,23 @@ class WhereNode(tree.Node):
                raise EmptyResultSet
            if extra:
                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)
        elif lookup_type in ('range', 'year'):
            return ('%s BETWEEN %%s and %%s' % field_sql, params)
+20 −0
Original line number Diff line number Diff line
@@ -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

"""