mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Removed Query.setup_joins() and join() argument outer_if_first.
Instead always create new joins as OUTER.
This commit is contained in:
		| @@ -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)) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user