mirror of
https://github.com/django/django.git
synced 2025-07-06 02:39:12 +00:00
queryset-refactor: Added a way to change the prefix of all aliases in a query.
This fixes the last few corner cases for nested queries that had overlapping aliases. Also tidies up a couple of internal data structures. git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7231 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
428450b7a9
commit
c8b33b824b
@ -44,7 +44,6 @@ class Query(object):
|
|||||||
self.connection = connection
|
self.connection = connection
|
||||||
self.alias_map = {} # Maps alias to table name
|
self.alias_map = {} # Maps alias to table name
|
||||||
self.table_map = {} # Maps table names to list of aliases.
|
self.table_map = {} # Maps table names to list of aliases.
|
||||||
self.join_map = {} # Maps join_tuple to list of aliases.
|
|
||||||
self.rev_join_map = {} # Reverse of join_map.
|
self.rev_join_map = {} # Reverse of join_map.
|
||||||
self.quote_cache = {}
|
self.quote_cache = {}
|
||||||
self.default_cols = True
|
self.default_cols = True
|
||||||
@ -128,8 +127,7 @@ class Query(object):
|
|||||||
obj.connection = self.connection
|
obj.connection = self.connection
|
||||||
obj.alias_map = copy.deepcopy(self.alias_map)
|
obj.alias_map = copy.deepcopy(self.alias_map)
|
||||||
obj.table_map = self.table_map.copy()
|
obj.table_map = self.table_map.copy()
|
||||||
obj.join_map = copy.deepcopy(self.join_map)
|
obj.rev_join_map = self.rev_join_map.copy()
|
||||||
obj.rev_join_map = copy.deepcopy(self.rev_join_map)
|
|
||||||
obj.quote_cache = {}
|
obj.quote_cache = {}
|
||||||
obj.default_cols = self.default_cols
|
obj.default_cols = self.default_cols
|
||||||
obj.default_ordering = self.default_ordering
|
obj.default_ordering = self.default_ordering
|
||||||
@ -584,42 +582,65 @@ class Query(object):
|
|||||||
if self.alias_map[alias][ALIAS_NULLABLE]:
|
if self.alias_map[alias][ALIAS_NULLABLE]:
|
||||||
self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER
|
self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER
|
||||||
|
|
||||||
def change_alias(self, old_alias, new_alias):
|
def change_aliases(self, change_map):
|
||||||
"""
|
"""
|
||||||
Changes old_alias to new_alias, relabelling any references to it in
|
Changes the aliases in change_map (which maps old-alias -> new-alias),
|
||||||
select columns and the where clause.
|
relabelling any references to them in select columns and the where
|
||||||
|
clause.
|
||||||
"""
|
"""
|
||||||
assert new_alias not in self.alias_map
|
assert set(change_map.keys()).intersection(set(change_map.values())) == set()
|
||||||
|
|
||||||
# 1. Update references in "select" and "where".
|
# 1. Update references in "select" and "where".
|
||||||
change_map = {old_alias: new_alias}
|
|
||||||
self.where.relabel_aliases(change_map)
|
self.where.relabel_aliases(change_map)
|
||||||
for pos, col in enumerate(self.select):
|
for pos, col in enumerate(self.select):
|
||||||
if isinstance(col, (list, tuple)):
|
if isinstance(col, (list, tuple)):
|
||||||
if col[0] == old_alias:
|
self.select[pos] = (change_map.get(old_alias, old_alias), col[1])
|
||||||
self.select[pos] = (new_alias, col[1])
|
|
||||||
else:
|
else:
|
||||||
col.relabel_aliases(change_map)
|
col.relabel_aliases(change_map)
|
||||||
|
|
||||||
# 2. Rename the alias in the internal table/alias datastructures.
|
# 2. Rename the alias in the internal table/alias datastructures.
|
||||||
alias_data = self.alias_map[old_alias]
|
for old_alias, new_alias in change_map.items():
|
||||||
alias_data[ALIAS_JOIN][RHS_ALIAS] = new_alias
|
alias_data = self.alias_map[old_alias]
|
||||||
table_aliases = self.table_map[alias_data[ALIAS_TABLE]]
|
alias_data[ALIAS_JOIN][RHS_ALIAS] = new_alias
|
||||||
for pos, alias in enumerate(table_aliases):
|
self.rev_join_map[new_alias] = self.rev_join_map[old_alias]
|
||||||
if alias == old_alias:
|
del self.rev_join_map[old_alias]
|
||||||
table_aliases[pos] = new_alias
|
table_aliases = self.table_map[alias_data[ALIAS_TABLE]]
|
||||||
break
|
for pos, alias in enumerate(table_aliases):
|
||||||
self.alias_map[new_alias] = alias_data
|
if alias == old_alias:
|
||||||
del self.alias_map[old_alias]
|
table_aliases[pos] = new_alias
|
||||||
for pos, alias in enumerate(self.tables):
|
break
|
||||||
if alias == old_alias:
|
self.alias_map[new_alias] = alias_data
|
||||||
self.tables[pos] = new_alias
|
del self.alias_map[old_alias]
|
||||||
break
|
for pos, alias in enumerate(self.tables):
|
||||||
|
if alias == old_alias:
|
||||||
|
self.tables[pos] = new_alias
|
||||||
|
break
|
||||||
|
|
||||||
# 3. Update any joins that refer to the old alias.
|
# 3. Update any joins that refer to the old alias.
|
||||||
for data in self.alias_map.values():
|
for data in self.alias_map.values():
|
||||||
if data[ALIAS_JOIN][LHS_ALIAS] == old_alias:
|
alias = data[ALIAS_JOIN][LHS_ALIAS]
|
||||||
data[ALIAS_JOIN][LHS_ALIAS] = new_alias
|
if alias in change_map:
|
||||||
|
data[ALIAS_JOIN][LHS_ALIAS] = change_map[alias]
|
||||||
|
|
||||||
|
def bump_prefix(self):
|
||||||
|
"""
|
||||||
|
Changes the alias prefix to the next letter in the alphabet and
|
||||||
|
relabels all the aliases. Even tables that previously had no alias will
|
||||||
|
get an alias after this call (it's mostly used for nested queries and
|
||||||
|
the outer query will already be using the non-aliased table name).
|
||||||
|
|
||||||
|
Subclasses who create their own prefix should override this method to
|
||||||
|
produce a similar result (a new prefix and relabelled aliases).
|
||||||
|
"""
|
||||||
|
assert ord(self.alias_prefix) < ord('Z')
|
||||||
|
self.alias_prefix = chr(ord(self.alias_prefix) + 1)
|
||||||
|
change_map = {}
|
||||||
|
prefix = self.alias_prefix
|
||||||
|
for pos, alias in enumerate(self.tables):
|
||||||
|
new_alias = '%s%d' % (prefix, pos)
|
||||||
|
change_map[alias] = new_alias
|
||||||
|
self.tables[pos] = new_alias
|
||||||
|
self.change_aliases(change_map)
|
||||||
|
|
||||||
def get_initial_alias(self):
|
def get_initial_alias(self):
|
||||||
"""
|
"""
|
||||||
@ -681,15 +702,12 @@ class Query(object):
|
|||||||
is_table = False
|
is_table = False
|
||||||
t_ident = (lhs_table, table, lhs_col, col)
|
t_ident = (lhs_table, table, lhs_col, col)
|
||||||
if not always_create:
|
if not always_create:
|
||||||
aliases = self.join_map.get(t_ident)
|
for alias, row in self.rev_join_map.items():
|
||||||
if aliases:
|
if t_ident == row and alias not in exclusions:
|
||||||
for alias in aliases:
|
self.ref_alias(alias)
|
||||||
if alias not in exclusions:
|
if promote and self.alias_map[alias][ALIAS_NULLABLE]:
|
||||||
self.ref_alias(alias)
|
self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER
|
||||||
if promote and self.alias_map[alias][ALIAS_NULLABLE]:
|
return alias
|
||||||
self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = \
|
|
||||||
self.LOUTER
|
|
||||||
return alias
|
|
||||||
# If we get to here (no non-excluded alias exists), we'll fall
|
# If we get to here (no non-excluded alias exists), we'll fall
|
||||||
# through to creating a new alias.
|
# through to creating a new alias.
|
||||||
|
|
||||||
@ -708,7 +726,6 @@ class Query(object):
|
|||||||
join[JOIN_TYPE] = None
|
join[JOIN_TYPE] = None
|
||||||
self.alias_map[alias][ALIAS_JOIN] = join
|
self.alias_map[alias][ALIAS_JOIN] = join
|
||||||
self.alias_map[alias][ALIAS_NULLABLE] = nullable
|
self.alias_map[alias][ALIAS_NULLABLE] = nullable
|
||||||
self.join_map.setdefault(t_ident, []).append(alias)
|
|
||||||
self.rev_join_map[alias] = t_ident
|
self.rev_join_map[alias] = t_ident
|
||||||
return alias
|
return alias
|
||||||
|
|
||||||
|
@ -158,8 +158,7 @@ class UpdateQuery(Query):
|
|||||||
# We need to use a sub-select in the where clause to filter on things
|
# We need to use a sub-select in the where clause to filter on things
|
||||||
# from other tables.
|
# from other tables.
|
||||||
query = self.clone(klass=Query)
|
query = self.clone(klass=Query)
|
||||||
alias = '%s0' % self.alias_prefix
|
query.bump_prefix()
|
||||||
query.change_alias(query.tables[0], alias)
|
|
||||||
self.add_fields([query.model._meta.pk.name])
|
self.add_fields([query.model._meta.pk.name])
|
||||||
|
|
||||||
# Now we adjust the current query: reset the where clause and get rid
|
# Now we adjust the current query: reset the where clause and get rid
|
||||||
|
Loading…
x
Reference in New Issue
Block a user