mirror of https://github.com/django/django.git
Removed Query.setup_joins() and join() argument outer_if_first.
Instead always create new joins as OUTER.
This commit is contained in:
parent
e7b61e5717
commit
ba6c9fae45
|
@ -453,7 +453,7 @@ class SQLCompiler(object):
|
||||||
def _setup_joins(self, pieces, opts, alias):
|
def _setup_joins(self, pieces, opts, alias):
|
||||||
"""
|
"""
|
||||||
A helper method for get_ordering and get_distinct. This method will
|
A helper method for get_ordering and get_distinct. This method will
|
||||||
call query.setup_joins, handle refcounts and then promote the joins.
|
call query.setup_joins and handle refcounts.
|
||||||
|
|
||||||
Note that get_ordering and get_distinct must produce same target
|
Note that get_ordering and get_distinct must produce same target
|
||||||
columns on same input, as the prefixes of get_ordering and get_distinct
|
columns on same input, as the prefixes of get_ordering and get_distinct
|
||||||
|
@ -463,20 +463,12 @@ class SQLCompiler(object):
|
||||||
alias = self.query.get_initial_alias()
|
alias = self.query.get_initial_alias()
|
||||||
field, targets, opts, joins, path = self.query.setup_joins(
|
field, targets, opts, joins, path = self.query.setup_joins(
|
||||||
pieces, opts, alias)
|
pieces, opts, alias)
|
||||||
# We will later on need to promote those joins that were added to the
|
|
||||||
# query afresh above.
|
|
||||||
joins_to_promote = [j for j in joins if self.query.alias_refcount[j] < 2]
|
|
||||||
alias = joins[-1]
|
alias = joins[-1]
|
||||||
if not field.rel:
|
if not field.rel:
|
||||||
# To avoid inadvertent trimming of a necessary alias, use the
|
# To avoid inadvertent trimming of a necessary alias, use the
|
||||||
# refcount to show that we are referencing a non-relation field on
|
# refcount to show that we are referencing a non-relation field on
|
||||||
# the model.
|
# the model.
|
||||||
self.query.ref_alias(alias)
|
self.query.ref_alias(alias)
|
||||||
|
|
||||||
# Must use left outer joins for nullable fields and their relations.
|
|
||||||
# Ordering or distinct must not affect the returned set, and INNER
|
|
||||||
# JOINS for nullable fields could do this.
|
|
||||||
self.query.promote_joins(joins_to_promote)
|
|
||||||
return field, targets, alias, joins, path, opts
|
return field, targets, alias, joins, path, opts
|
||||||
|
|
||||||
def get_from_clause(self):
|
def get_from_clause(self):
|
||||||
|
@ -589,7 +581,7 @@ class SQLCompiler(object):
|
||||||
return result, params
|
return result, params
|
||||||
|
|
||||||
def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
|
def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
|
||||||
requested=None, restricted=None, nullable=None):
|
requested=None, restricted=None):
|
||||||
"""
|
"""
|
||||||
Fill in the information needed for a select_related query. The current
|
Fill in the information needed for a select_related query. The current
|
||||||
depth is measured as the number of connections away from the root model
|
depth is measured as the number of connections away from the root model
|
||||||
|
@ -623,9 +615,8 @@ class SQLCompiler(object):
|
||||||
if not select_related_descend(f, restricted, requested,
|
if not select_related_descend(f, restricted, requested,
|
||||||
only_load.get(field_model)):
|
only_load.get(field_model)):
|
||||||
continue
|
continue
|
||||||
promote = nullable or f.null
|
|
||||||
_, _, _, joins, _ = self.query.setup_joins(
|
_, _, _, joins, _ = self.query.setup_joins(
|
||||||
[f.name], opts, root_alias, outer_if_first=promote)
|
[f.name], opts, root_alias)
|
||||||
alias = joins[-1]
|
alias = joins[-1]
|
||||||
columns, aliases = self.get_default_columns(start_alias=alias,
|
columns, aliases = self.get_default_columns(start_alias=alias,
|
||||||
opts=f.rel.to._meta, as_pairs=True)
|
opts=f.rel.to._meta, as_pairs=True)
|
||||||
|
@ -635,9 +626,8 @@ class SQLCompiler(object):
|
||||||
next = requested.get(f.name, {})
|
next = requested.get(f.name, {})
|
||||||
else:
|
else:
|
||||||
next = False
|
next = False
|
||||||
new_nullable = f.null or promote
|
|
||||||
self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1,
|
self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1,
|
||||||
next, restricted, new_nullable)
|
next, restricted)
|
||||||
|
|
||||||
if restricted:
|
if restricted:
|
||||||
related_fields = [
|
related_fields = [
|
||||||
|
@ -651,7 +641,7 @@ class SQLCompiler(object):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
_, _, _, joins, _ = self.query.setup_joins(
|
_, _, _, joins, _ = self.query.setup_joins(
|
||||||
[f.related_query_name()], opts, root_alias, outer_if_first=True)
|
[f.related_query_name()], opts, root_alias)
|
||||||
alias = joins[-1]
|
alias = joins[-1]
|
||||||
from_parent = (opts.model if issubclass(model, opts.model)
|
from_parent = (opts.model if issubclass(model, opts.model)
|
||||||
else None)
|
else None)
|
||||||
|
@ -661,11 +651,8 @@ class SQLCompiler(object):
|
||||||
SelectInfo(col, field) for col, field
|
SelectInfo(col, field) for col, field
|
||||||
in zip(columns, model._meta.concrete_fields))
|
in zip(columns, model._meta.concrete_fields))
|
||||||
next = requested.get(f.related_query_name(), {})
|
next = requested.get(f.related_query_name(), {})
|
||||||
# Use True here because we are looking at the _reverse_ side of
|
|
||||||
# the relation, which is always nullable.
|
|
||||||
new_nullable = True
|
|
||||||
self.fill_related_selections(model._meta, alias, cur_depth + 1,
|
self.fill_related_selections(model._meta, alias, cur_depth + 1,
|
||||||
next, restricted, new_nullable)
|
next, restricted)
|
||||||
|
|
||||||
def deferred_to_columns(self):
|
def deferred_to_columns(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -491,8 +491,7 @@ class Query(object):
|
||||||
lhs = change_map.get(lhs, lhs)
|
lhs = change_map.get(lhs, lhs)
|
||||||
new_alias = self.join(
|
new_alias = self.join(
|
||||||
(lhs, table, join_cols), reuse=reuse,
|
(lhs, table, join_cols), reuse=reuse,
|
||||||
outer_if_first=True, nullable=nullable,
|
nullable=nullable, join_field=join_field)
|
||||||
join_field=join_field)
|
|
||||||
if join_type == self.INNER:
|
if join_type == self.INNER:
|
||||||
rhs_votes.add(new_alias)
|
rhs_votes.add(new_alias)
|
||||||
# We can't reuse the same join again in the query. If we have two
|
# We can't reuse the same join again in the query. If we have two
|
||||||
|
@ -854,8 +853,7 @@ class Query(object):
|
||||||
"""
|
"""
|
||||||
return len([1 for count in self.alias_refcount.values() if count])
|
return len([1 for count in self.alias_refcount.values() if count])
|
||||||
|
|
||||||
def join(self, connection, reuse=None, outer_if_first=False,
|
def join(self, connection, reuse=None, nullable=False, join_field=None):
|
||||||
nullable=False, join_field=None):
|
|
||||||
"""
|
"""
|
||||||
Returns an alias for the join in 'connection', either reusing an
|
Returns an alias for the join in 'connection', either reusing an
|
||||||
existing alias for that join or creating a new one. 'connection' is a
|
existing alias for that join or creating a new one. 'connection' is a
|
||||||
|
@ -870,11 +868,9 @@ class Query(object):
|
||||||
(matching the connection) are reusable, or it can be a set containing
|
(matching the connection) are reusable, or it can be a set containing
|
||||||
the aliases that can be reused.
|
the aliases that can be reused.
|
||||||
|
|
||||||
If 'outer_if_first' is True and a new join is created, it will have the
|
|
||||||
LOUTER join type.
|
|
||||||
|
|
||||||
A join is always created as LOUTER if the lhs alias is LOUTER to make
|
A join is always created as LOUTER if the lhs alias is LOUTER to make
|
||||||
sure we do not generate chains like t1 LOUTER t2 INNER t3.
|
sure we do not generate chains like t1 LOUTER t2 INNER t3. All new
|
||||||
|
joins are created as LOUTER if nullable is True.
|
||||||
|
|
||||||
If 'nullable' is True, the join can potentially involve NULL values and
|
If 'nullable' is True, the join can potentially involve NULL values and
|
||||||
is a candidate for promotion (to "left outer") when combining querysets.
|
is a candidate for promotion (to "left outer") when combining querysets.
|
||||||
|
@ -904,15 +900,13 @@ class Query(object):
|
||||||
# Not all tables need to be joined to anything. No join type
|
# Not all tables need to be joined to anything. No join type
|
||||||
# means the later columns are ignored.
|
# means the later columns are ignored.
|
||||||
join_type = None
|
join_type = None
|
||||||
elif self.alias_map[lhs].join_type == self.LOUTER:
|
elif self.alias_map[lhs].join_type == self.LOUTER or nullable:
|
||||||
join_type = self.LOUTER
|
join_type = self.LOUTER
|
||||||
else:
|
else:
|
||||||
join_type = self.INNER
|
join_type = self.INNER
|
||||||
join = JoinInfo(table, alias, join_type, lhs, join_cols or ((None, None),), nullable,
|
join = JoinInfo(table, alias, join_type, lhs, join_cols or ((None, None),), nullable,
|
||||||
join_field)
|
join_field)
|
||||||
self.alias_map[alias] = join
|
self.alias_map[alias] = join
|
||||||
if outer_if_first:
|
|
||||||
self.promote_joins([alias])
|
|
||||||
if connection in self.join_map:
|
if connection in self.join_map:
|
||||||
self.join_map[connection] += (alias,)
|
self.join_map[connection] += (alias,)
|
||||||
else:
|
else:
|
||||||
|
@ -1010,7 +1004,7 @@ class Query(object):
|
||||||
# Join promotion note - we must not remove any rows here, so use
|
# Join promotion note - we must not remove any rows here, so use
|
||||||
# outer join if there isn't any existing join.
|
# outer join if there isn't any existing join.
|
||||||
field, sources, opts, join_list, path = self.setup_joins(
|
field, sources, opts, join_list, path = self.setup_joins(
|
||||||
field_list, opts, self.get_initial_alias(), outer_if_first=True)
|
field_list, opts, self.get_initial_alias())
|
||||||
|
|
||||||
# Process the join chain to see if it can be trimmed
|
# Process the join chain to see if it can be trimmed
|
||||||
targets, _, join_list = self.trim_joins(sources, join_list, path)
|
targets, _, join_list = self.trim_joins(sources, join_list, path)
|
||||||
|
@ -1139,7 +1133,7 @@ class Query(object):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
field, sources, opts, join_list, path = self.setup_joins(
|
field, sources, opts, join_list, path = self.setup_joins(
|
||||||
parts, opts, alias, can_reuse, allow_many, outer_if_first=True)
|
parts, opts, alias, can_reuse, allow_many)
|
||||||
except MultiJoin as e:
|
except MultiJoin as e:
|
||||||
return self.split_exclude(filter_expr, LOOKUP_SEP.join(parts[:e.level]),
|
return self.split_exclude(filter_expr, LOOKUP_SEP.join(parts[:e.level]),
|
||||||
can_reuse, e.names_with_path)
|
can_reuse, e.names_with_path)
|
||||||
|
@ -1343,8 +1337,7 @@ class Query(object):
|
||||||
raise FieldError("Join on field %r not permitted." % name)
|
raise FieldError("Join on field %r not permitted." % name)
|
||||||
return path, final_field, targets
|
return path, final_field, targets
|
||||||
|
|
||||||
def setup_joins(self, names, opts, alias, can_reuse=None, allow_many=True,
|
def setup_joins(self, names, opts, alias, can_reuse=None, allow_many=True):
|
||||||
outer_if_first=False):
|
|
||||||
"""
|
"""
|
||||||
Compute the necessary table joins for the passage through the fields
|
Compute the necessary table joins for the passage through the fields
|
||||||
given in 'names'. 'opts' is the Options class for the current model
|
given in 'names'. 'opts' is the Options class for the current model
|
||||||
|
@ -1385,8 +1378,7 @@ class Query(object):
|
||||||
connection = alias, opts.db_table, join.join_field.get_joining_columns()
|
connection = alias, opts.db_table, join.join_field.get_joining_columns()
|
||||||
reuse = can_reuse if join.m2m else None
|
reuse = can_reuse if join.m2m else None
|
||||||
alias = self.join(
|
alias = self.join(
|
||||||
connection, reuse=reuse, nullable=nullable, join_field=join.join_field,
|
connection, reuse=reuse, nullable=nullable, join_field=join.join_field)
|
||||||
outer_if_first=outer_if_first)
|
|
||||||
joins.append(alias)
|
joins.append(alias)
|
||||||
if hasattr(final_field, 'field'):
|
if hasattr(final_field, 'field'):
|
||||||
final_field = final_field.field
|
final_field = final_field.field
|
||||||
|
@ -1561,7 +1553,7 @@ class Query(object):
|
||||||
# if there is no existing joins, use outer join.
|
# if there is no existing joins, use outer join.
|
||||||
field, targets, u2, joins, path = self.setup_joins(
|
field, targets, u2, joins, path = self.setup_joins(
|
||||||
name.split(LOOKUP_SEP), opts, alias, can_reuse=None,
|
name.split(LOOKUP_SEP), opts, alias, can_reuse=None,
|
||||||
allow_many=allow_m2m, outer_if_first=True)
|
allow_many=allow_m2m)
|
||||||
targets, final_alias, joins = self.trim_joins(targets, joins, path)
|
targets, final_alias, joins = self.trim_joins(targets, joins, path)
|
||||||
for target in targets:
|
for target in targets:
|
||||||
self.select.append(SelectInfo((final_alias, target.column), target))
|
self.select.append(SelectInfo((final_alias, target.column), target))
|
||||||
|
|
Loading…
Reference in New Issue