1
0
mirror of https://github.com/django/django.git synced 2025-07-06 10:49:17 +00:00

queryset-refactor: Create a new join when merging two QuerySets that use a 1-m

field.


git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6492 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2007-10-14 02:14:53 +00:00
parent 8926b431ed
commit 142e400c5c
2 changed files with 21 additions and 12 deletions

View File

@ -48,6 +48,7 @@ RHS_JOIN_COL = 5
ALIAS_TABLE = 0 ALIAS_TABLE = 0
ALIAS_REFCOUNT = 1 ALIAS_REFCOUNT = 1
ALIAS_JOIN = 2 ALIAS_JOIN = 2
ALIAS_MERGE_SEP = 3
# How many results to expect from a cursor.execute call # How many results to expect from a cursor.execute call
MULTI = 'multi' MULTI = 'multi'
@ -252,8 +253,10 @@ class Query(object):
continue continue
promote = (rhs.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] == promote = (rhs.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] ==
self.LOUTER) self.LOUTER)
merge_separate = (connection == AND)
new_alias = self.join(rhs.rev_join_map[alias], exclusions=used, new_alias = self.join(rhs.rev_join_map[alias], exclusions=used,
promote=promote, outer_if_first=True) promote=promote, outer_if_first=True,
merge_separate=merge_separate)
if self.alias_map[alias][ALIAS_REFCOUNT] == 1: if self.alias_map[alias][ALIAS_REFCOUNT] == 1:
first_new_join = False first_new_join = False
used[new_alias] = None used[new_alias] = None
@ -417,7 +420,7 @@ class Query(object):
alias = table_name alias = table_name
else: else:
alias = '%s%d' % (self.alias_prefix, len(self.alias_map) + 1) alias = '%s%d' % (self.alias_prefix, len(self.alias_map) + 1)
self.alias_map[alias] = [table_name, 1, None] self.alias_map[alias] = [table_name, 1, None, False]
self.table_map.setdefault(table_name, []).append(alias) self.table_map.setdefault(table_name, []).append(alias)
self.tables.append(alias) self.tables.append(alias)
return alias, True return alias, True
@ -435,7 +438,8 @@ class Query(object):
self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = self.LOUTER
def join(self, (lhs, table, lhs_col, col), always_create=False, def join(self, (lhs, table, lhs_col, col), always_create=False,
exclusions=(), promote=False, outer_if_first=False): exclusions=(), promote=False, outer_if_first=False,
merge_separate=False):
""" """
Returns an alias for a join between 'table' and 'lhs' on the given Returns an alias for a join between 'table' and 'lhs' on the given
columns, either reusing an existing alias for that join or creating a columns, either reusing an existing alias for that join or creating a
@ -456,6 +460,10 @@ class Query(object):
If 'outer_if_first' is True and a new join is created, it will have the If 'outer_if_first' is True and a new join is created, it will have the
LOUTER join type. This is used when joining certain types of querysets LOUTER join type. This is used when joining certain types of querysets
and Q-objects together. and Q-objects together.
If the 'merge_separate' parameter is True, we create a new alias if we
would otherwise reuse an alias that also had 'merge_separate' set to
True when it was created.
""" """
if lhs not in self.alias_map: if lhs not in self.alias_map:
lhs_table = lhs lhs_table = lhs
@ -467,7 +475,9 @@ class Query(object):
aliases = self.join_map.get(t_ident) aliases = self.join_map.get(t_ident)
if aliases and not always_create: if aliases and not always_create:
for alias in aliases: for alias in aliases:
if alias not in exclusions: if (alias not in exclusions and
not (merge_separate and
self.alias_map[alias][ALIAS_MERGE_SEP])):
self.ref_alias(alias) self.ref_alias(alias)
if promote: if promote:
self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = \ self.alias_map[alias][ALIAS_JOIN][JOIN_TYPE] = \
@ -487,6 +497,7 @@ class Query(object):
# means the later columns are ignored. # means the later columns are ignored.
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_MERGE_SEP] = merge_separate
self.join_map.setdefault(t_ident, []).append(alias) 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
@ -647,7 +658,7 @@ class Query(object):
opts.pk.column, field.m2m_column_name()), dupe_multis) opts.pk.column, field.m2m_column_name()), dupe_multis)
far_alias = self.join((int_alias, remote_opts.db_table, far_alias = self.join((int_alias, remote_opts.db_table,
field.m2m_reverse_name(), remote_opts.pk.column), field.m2m_reverse_name(), remote_opts.pk.column),
dupe_multis) dupe_multis, merge_separate=True)
return ([int_alias, far_alias], remote_opts, field, remote_opts.pk, return ([int_alias, far_alias], remote_opts, field, remote_opts.pk,
None) None)
@ -661,7 +672,7 @@ class Query(object):
opts.pk.column, field.m2m_reverse_name()), dupe_multis) opts.pk.column, field.m2m_reverse_name()), dupe_multis)
far_alias = self.join((int_alias, remote_opts.db_table, far_alias = self.join((int_alias, remote_opts.db_table,
field.m2m_column_name(), remote_opts.pk.column), field.m2m_column_name(), remote_opts.pk.column),
dupe_multis) dupe_multis, merge_separate=True)
# XXX: Why is the final component able to be None here? # XXX: Why is the final component able to be None here?
return ([int_alias, far_alias], remote_opts, field, remote_opts.pk, return ([int_alias, far_alias], remote_opts, field, remote_opts.pk,
None) None)
@ -673,7 +684,8 @@ class Query(object):
field = field.field field = field.field
local_field = opts.get_field(field.rel.field_name) local_field = opts.get_field(field.rel.field_name)
alias = self.join((root_alias, remote_opts.db_table, alias = self.join((root_alias, remote_opts.db_table,
local_field.column, field.column), dupe_multis) local_field.column, field.column), dupe_multis,
merge_separate=True)
return ([alias], remote_opts, field, field, remote_opts.pk.column) return ([alias], remote_opts, field, field, remote_opts.pk.column)

View File

@ -81,11 +81,8 @@ Bug #1801
[<Author: a2>] [<Author: a2>]
>>> Author.objects.filter(item=i3) >>> Author.objects.filter(item=i3)
[<Author: a2>] [<Author: a2>]
>>> Author.objects.filter(item=i2) & Author.objects.filter(item=i3)
# FIXME: When we join these queries, we MUST NOT share the table joins. this is [<Author: a2>]
# the case for all m-to-m and 1-to-m joins (but m-to-1 is fine).
# >>> Author.objects.filter(item=i2) & Author.objects.filter(item=i3)
# [<Author: a2>]
Bug #2306 Bug #2306
Checking that no join types are "left outer" joins. Checking that no join types are "left outer" joins.