Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Removed Query.setup_joins() and join() argument outer_if_first.

Instead always create new joins as OUTER.
  • Loading branch information...
commit ba6c9fae452d3e4260ed0c1c74230da74f74f665 1 parent e7b61e5
@akaariai akaariai authored
Showing with 16 additions and 37 deletions.
  1. +6 −19 django/db/models/sql/compiler.py
  2. +10 −18 django/db/models/sql/query.py
View
25 django/db/models/sql/compiler.py
@@ -453,7 +453,7 @@ def find_ordering_name(self, name, opts, alias=None, default_order='ASC',
def _setup_joins(self, pieces, opts, alias):
"""
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
columns on same input, as the prefixes of get_ordering and get_distinct
@@ -463,20 +463,12 @@ def _setup_joins(self, pieces, opts, alias):
alias = self.query.get_initial_alias()
field, targets, opts, joins, path = self.query.setup_joins(
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]
if not field.rel:
# To avoid inadvertent trimming of a necessary alias, use the
# refcount to show that we are referencing a non-relation field on
# the model.
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
def get_from_clause(self):
@@ -589,7 +581,7 @@ def get_grouping(self, having_group_by, ordering_group_by):
return result, params
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
depth is measured as the number of connections away from the root model
@@ -623,9 +615,8 @@ def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
if not select_related_descend(f, restricted, requested,
only_load.get(field_model)):
continue
- promote = nullable or f.null
_, _, _, joins, _ = self.query.setup_joins(
- [f.name], opts, root_alias, outer_if_first=promote)
+ [f.name], opts, root_alias)
alias = joins[-1]
columns, aliases = self.get_default_columns(start_alias=alias,
opts=f.rel.to._meta, as_pairs=True)
@@ -635,9 +626,8 @@ def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
next = requested.get(f.name, {})
else:
next = False
- new_nullable = f.null or promote
self.fill_related_selections(f.rel.to._meta, alias, cur_depth + 1,
- next, restricted, new_nullable)
+ next, restricted)
if restricted:
related_fields = [
@@ -651,7 +641,7 @@ def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
continue
_, _, _, 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]
from_parent = (opts.model if issubclass(model, opts.model)
else None)
@@ -661,11 +651,8 @@ def fill_related_selections(self, opts=None, root_alias=None, cur_depth=1,
SelectInfo(col, field) for col, field
in zip(columns, model._meta.concrete_fields))
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,
- next, restricted, new_nullable)
+ next, restricted)
def deferred_to_columns(self):
"""
View
28 django/db/models/sql/query.py
@@ -491,8 +491,7 @@ def combine(self, rhs, connector):
lhs = change_map.get(lhs, lhs)
new_alias = self.join(
(lhs, table, join_cols), reuse=reuse,
- outer_if_first=True, nullable=nullable,
- join_field=join_field)
+ nullable=nullable, join_field=join_field)
if join_type == self.INNER:
rhs_votes.add(new_alias)
# We can't reuse the same join again in the query. If we have two
@@ -854,8 +853,7 @@ def count_active_tables(self):
"""
return len([1 for count in self.alias_refcount.values() if count])
- def join(self, connection, reuse=None, outer_if_first=False,
- nullable=False, join_field=None):
+ def join(self, connection, reuse=None, nullable=False, join_field=None):
"""
Returns an alias for the join in 'connection', either reusing an
existing alias for that join or creating a new one. 'connection' is a
@@ -870,11 +868,9 @@ def join(self, connection, reuse=None, outer_if_first=False,
(matching the connection) are reusable, or it can be a set containing
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
- 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
is a candidate for promotion (to "left outer") when combining querysets.
@@ -904,15 +900,13 @@ def join(self, connection, reuse=None, outer_if_first=False,
# Not all tables need to be joined to anything. No join type
# means the later columns are ignored.
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
else:
join_type = self.INNER
join = JoinInfo(table, alias, join_type, lhs, join_cols or ((None, None),), nullable,
join_field)
self.alias_map[alias] = join
- if outer_if_first:
- self.promote_joins([alias])
if connection in self.join_map:
self.join_map[connection] += (alias,)
else:
@@ -1010,7 +1004,7 @@ def add_aggregate(self, aggregate, model, alias, is_summary):
# Join promotion note - we must not remove any rows here, so use
# outer join if there isn't any existing join.
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
targets, _, join_list = self.trim_joins(sources, join_list, path)
@@ -1139,7 +1133,7 @@ def build_filter(self, filter_expr, branch_negated=False, current_negated=False,
try:
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:
return self.split_exclude(filter_expr, LOOKUP_SEP.join(parts[:e.level]),
can_reuse, e.names_with_path)
@@ -1343,8 +1337,7 @@ def names_to_path(self, names, opts, allow_many):
raise FieldError("Join on field %r not permitted." % name)
return path, final_field, targets
- def setup_joins(self, names, opts, alias, can_reuse=None, allow_many=True,
- outer_if_first=False):
+ def setup_joins(self, names, opts, alias, can_reuse=None, allow_many=True):
"""
Compute the necessary table joins for the passage through the fields
given in 'names'. 'opts' is the Options class for the current model
@@ -1385,8 +1378,7 @@ def setup_joins(self, names, opts, alias, can_reuse=None, allow_many=True,
connection = alias, opts.db_table, join.join_field.get_joining_columns()
reuse = can_reuse if join.m2m else None
alias = self.join(
- connection, reuse=reuse, nullable=nullable, join_field=join.join_field,
- outer_if_first=outer_if_first)
+ connection, reuse=reuse, nullable=nullable, join_field=join.join_field)
joins.append(alias)
if hasattr(final_field, 'field'):
final_field = final_field.field
@@ -1561,7 +1553,7 @@ def add_fields(self, field_names, allow_m2m=True):
# if there is no existing joins, use outer join.
field, targets, u2, joins, path = self.setup_joins(
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)
for target in targets:
self.select.append(SelectInfo((final_alias, target.column), target))
Please sign in to comment.
Something went wrong with that request. Please try again.