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:
committed by
Tim Graham
parent
f1a22feaa8
commit
e11ff3975f
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user