summaryrefslogtreecommitdiff
path: root/django/db/models/sql/query.py
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2019-12-17 01:21:13 -0500
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2019-12-19 10:04:56 +0100
commit5a4d7285bd10bd40d9f7e574a7c421eb21094858 (patch)
tree2ac7cade7304e783a4e0819a35427df30bd1c05c /django/db/models/sql/query.py
parenta0f34d8fef2942e17c40e7009b355b661d30e138 (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.py42
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),