1
0
mirror of https://github.com/django/django.git synced 2025-10-31 09:41:08 +00:00

[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 41fc1c0b5e from master
This commit is contained in:
Piotr Pawlaczek
2014-11-15 14:45:00 +01:00
committed by Tim Graham
parent f1a22feaa8
commit e11ff3975f
3 changed files with 53 additions and 4 deletions

View File

@@ -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()