diff options
| author | Simon Charette <charette.s@gmail.com> | 2019-12-17 01:21:13 -0500 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2019-12-19 10:04:56 +0100 |
| commit | 5a4d7285bd10bd40d9f7e574a7c421eb21094858 (patch) | |
| tree | 2ac7cade7304e783a4e0819a35427df30bd1c05c /django/db/models/sql/query.py | |
| parent | a0f34d8fef2942e17c40e7009b355b661d30e138 (diff) | |
Fixed #31094 -- Included columns referenced by subqueries in GROUP BY on aggregations.
Thanks Johannes Hoppe for the report.
Regression in fb3f034f1c63160c0ff13c609acd01c18be12f80.
Co-authored-by: Mariusz Felisiak <felisiak.mariusz@gmail.com>
Diffstat (limited to 'django/db/models/sql/query.py')
| -rw-r--r-- | django/db/models/sql/query.py | 42 |
1 files changed, 27 insertions, 15 deletions
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index d002698c63..ac4822e18a 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -157,7 +157,8 @@ class Query(BaseExpression): # Sometimes the query contains references to aliases in outer queries (as # a result of split_exclude). Correct alias quoting needs to know these # aliases too. - self.external_aliases = set() + # Map external tables to whether they are aliased. + self.external_aliases = {} self.table_map = {} # Maps table names to list of aliases. self.default_cols = True self.default_ordering = True @@ -855,8 +856,11 @@ class Query(BaseExpression): if alias == old_alias: table_aliases[pos] = new_alias break - self.external_aliases = {change_map.get(alias, alias) - for alias in self.external_aliases} + self.external_aliases = { + # Table is aliased or it's being changed and thus is aliased. + change_map.get(alias, alias): (aliased or alias in change_map) + for alias, aliased in self.external_aliases.items() + } def bump_prefix(self, outer_query): """ @@ -1030,19 +1034,23 @@ class Query(BaseExpression): for key, value in clone.annotations.items(): resolved = value.resolve_expression(query, *args, **kwargs) if hasattr(resolved, 'external_aliases'): - resolved.external_aliases.update(clone.alias_map) + resolved.external_aliases.update(clone.external_aliases) clone.annotations[key] = resolved # Outer query's aliases are considered external. - clone.external_aliases.update( - alias for alias, table in query.alias_map.items() - if ( - isinstance(table, Join) and table.join_field.related_model._meta.db_table != alias - ) or ( - isinstance(table, BaseTable) and table.table_name != table.table_alias + for alias, table in query.alias_map.items(): + clone.external_aliases[alias] = ( + (isinstance(table, Join) and table.join_field.related_model._meta.db_table != alias) or + (isinstance(table, BaseTable) and table.table_name != table.table_alias) ) - ) return clone + def get_external_cols(self): + exprs = chain(self.annotations.values(), self.where.children) + return [ + col for col in self._gen_cols(exprs) + if col.alias in self.external_aliases + ] + def as_sql(self, compiler, connection): sql, params = self.get_compiler(connection=connection).as_sql() if self.subquery: @@ -1635,12 +1643,16 @@ class Query(BaseExpression): return targets, joins[-1], joins @classmethod - def _gen_col_aliases(cls, exprs): + def _gen_cols(cls, exprs): for expr in exprs: if isinstance(expr, Col): - yield expr.alias + yield expr else: - yield from cls._gen_col_aliases(expr.get_source_expressions()) + yield from cls._gen_cols(expr.get_source_expressions()) + + @classmethod + def _gen_col_aliases(cls, exprs): + yield from (expr.alias for expr in cls._gen_cols(exprs)) def resolve_ref(self, name, allow_joins=True, reuse=None, summarize=False): if not allow_joins and LOOKUP_SEP in name: @@ -1733,7 +1745,7 @@ class Query(BaseExpression): lookup = lookup_class(pk.get_col(query.select[0].alias), pk.get_col(alias)) query.where.add(lookup, AND) - query.external_aliases.add(alias) + query.external_aliases[alias] = True condition, needed_inner = self.build_filter( ('%s__in' % trimmed_prefix, query), |
