summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorPiotr Pawlaczek <info@pawlaczek.pl>2014-11-15 14:45:00 +0100
committerTim Graham <timograham@gmail.com>2014-12-31 09:42:07 -0500
commite11ff3975f69b96521d94417bbe4125f4abb2774 (patch)
tree48ac9eadafd3dfe990b336177d958e82740687a2 /django
parentf1a22feaa877e4ff655db7014557112274419898 (diff)
[1.7.x] Fixed #23758 -- Allowed more than 5 levels of subqueries
Refactored bump_prefix() to avoid infinite loop and allow more than than 5 subquires by extending the alphabet to use multi-letters. Backport of 41fc1c0b5eac156e200a10233c7c9210a1c0fed8 from master
Diffstat (limited to 'django')
-rw-r--r--django/db/models/sql/query.py34
1 files changed, 30 insertions, 4 deletions
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index 4c60a3b09e..46ef56de91 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -6,6 +6,8 @@ themselves do not have to (and could be backed by things other than SQL
databases). The abstraction barrier only works one way: this module has to know
all about the internals of models in order to get the information it needs.
"""
+from string import ascii_uppercase
+from itertools import count, product
from collections import OrderedDict
import copy
@@ -837,13 +839,37 @@ class Query(object):
conflict. Even tables that previously had no alias will get an alias
after this call.
"""
+ def prefix_gen():
+ """
+ Generates a sequence of characters in alphabetical order:
+ -> 'A', 'B', 'C', ...
+
+ When the alphabet is finished, the sequence will continue with the
+ Cartesian product:
+ -> 'AA', 'AB', 'AC', ...
+ """
+ alphabet = ascii_uppercase
+ prefix = chr(ord(self.alias_prefix) + 1)
+ yield prefix
+ for n in count(1):
+ seq = alphabet[alphabet.index(prefix):] if prefix else alphabet
+ for s in product(seq, repeat=n):
+ yield ''.join(s)
+ prefix = None
+
if self.alias_prefix != outer_query.alias_prefix:
# No clashes between self and outer query should be possible.
return
- self.alias_prefix = chr(ord(self.alias_prefix) + 1)
- while self.alias_prefix in self.subq_aliases:
- self.alias_prefix = chr(ord(self.alias_prefix) + 1)
- assert self.alias_prefix < 'Z'
+
+ local_recursion_limit = 127 # explicitly avoid infinite loop
+ for pos, prefix in enumerate(prefix_gen()):
+ if prefix not in self.subq_aliases:
+ self.alias_prefix = prefix
+ break
+ if pos > local_recursion_limit:
+ raise RuntimeError(
+ 'Maximum recursion depth exceeded: too many subqueries.'
+ )
self.subq_aliases = self.subq_aliases.union([self.alias_prefix])
outer_query.subq_aliases = outer_query.subq_aliases.union(self.subq_aliases)
change_map = OrderedDict()