diff options
| author | Piotr Pawlaczek <info@pawlaczek.pl> | 2014-11-15 14:45:00 +0100 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2014-12-31 09:42:07 -0500 |
| commit | e11ff3975f69b96521d94417bbe4125f4abb2774 (patch) | |
| tree | 48ac9eadafd3dfe990b336177d958e82740687a2 /django | |
| parent | f1a22feaa877e4ff655db7014557112274419898 (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.py | 34 |
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() |
