Opened 15 hours ago
Last modified 81 minutes ago
#36181 assigned Bug
Composite primary key fields cannot use __in lookup with explicit Subquery
Reported by: | Jacob Walls | Owned by: | Simon Charette |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 5.2 |
Severity: | Release blocker | Keywords: | |
Cc: | Triage Stage: | Accepted | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
In a recent PR, we surfaced some inconsistencies with the resolution of explicit Subquery()
such that some lookups might fail:
-
tests/composite_pk/test_filter.py
diff --git a/tests/composite_pk/test_filter.py b/tests/composite_pk/test_filter.py index 937dd86652..60d43f4a52 100644
a b class CompositePKFilterTests(TestCase): 437 437 queryset = User.objects.filter(comments__in=subquery) 438 438 self.assertSequenceEqual(queryset, (self.user_2,)) 439 439 440 def test_explicit_subquery(self): 441 subquery = Subquery(User.objects.values("pk")) 442 self.assertEqual(User.objects.filter(pk__in=subquery).count(), 5) 443 self.assertEqual(Comment.objects.filter(user__in=subquery).count(), 5) 444 440 445 def test_cannot_cast_pk(self): 441 446 msg = "Cast expression does not support composite primary keys." 442 447 with self.assertRaisesMessage(ValueError, msg):
gives
====================================================================== ERROR: test_explicit_subquery (composite_pk.test_filter.CompositePKFilterTests.test_explicit_subquery) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/.../django/tests/composite_pk/test_filter.py", line 418, in test_explicit_subquery self.assertEqual(Comment.objects.filter(user__in=subquery).count(), 5) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/.../django/django/db/models/query.py", line 603, in count return self.query.get_count(using=self.db) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/.../django/django/db/models/sql/query.py", line 644, in get_count return obj.get_aggregation(using, {"__count": Count("*")})["__count"] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/.../django/django/db/models/sql/query.py", line 626, in get_aggregation result = compiler.execute_sql(SINGLE) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/.../django/django/db/models/sql/compiler.py", line 1610, in execute_sql sql, params = self.as_sql() ^^^^^^^^^^^^^ File "/Users/.../django/django/db/models/sql/compiler.py", line 794, in as_sql self.compile(self.where) if self.where is not None else ("", []) ^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/.../django/django/db/models/sql/compiler.py", line 577, in compile sql, params = node.as_sql(self, self.connection) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/.../django/django/db/models/sql/where.py", line 151, in as_sql sql, params = compiler.compile(child) ^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/.../django/django/db/models/sql/compiler.py", line 577, in compile sql, params = node.as_sql(self, self.connection) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/.../django/django/db/models/fields/related_lookups.py", line 86, in as_sql SubqueryConstraint( File "/Users/.../django/django/db/models/sql/where.py", line 358, in __init__ query_object.clear_ordering(clear_default=True) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ AttributeError: 'Subquery' object has no attribute 'clear_ordering'
Change History (2)
comment:1 by , 13 hours ago
Severity: | Normal → Release blocker |
---|---|
Triage Stage: | Unreviewed → Accepted |
comment:2 by , 81 minutes ago
Has patch: | set |
---|---|
Owner: | set to |
Status: | new → assigned |
Note:
See TracTickets
for help on using tickets.
Well good news, I think the work on #36149 managed to make
SubqueryConstraint
completely irrelevant while solving this issue.django/db/backends/mysql/compiler.py
class SQLCompiler(compiler.SQLCompiler):def as_subquery_condition(self, alias, columns, compiler):qn = compiler.quote_name_unless_aliasqn2 = self.connection.ops.quote_namesql, params = self.as_sql()return ("(%s) IN (%s)"% (", ".join("%s.%s" % (qn(alias), qn2(column)) for column in columns),sql,),params,)class SQLInsertCompiler(compiler.SQLInsertCompiler, SQLCompiler):passcompiler.SQLUpdateCompiler, SQLCompiler):class SQLAggregateCompiler(compiler.SQLAggregateCompiler, SQLCompiler):passdjango/db/models/fields/related_lookups.py
from django.db.models.sql.where import SubqueryConstraintreturn compiler.compile(lookup)django/db/models/fields/tuple_lookups.py
Query):django/db/models/sql/compiler.py
def as_subquery_condition(self, alias, columns, compiler):qn = compiler.quote_name_unless_aliasqn2 = self.connection.ops.quote_namequery = self.query.clone()for index, select_col in enumerate(query.select):lhs_sql, lhs_params = self.compile(select_col)rhs = "%s.%s" % (qn(alias), qn2(columns[index]))query.where.add(RawSQL("%s = %s" % (lhs_sql, rhs), lhs_params), AND)sql, params = query.as_sql(compiler, self.connection)return "EXISTS %s" % sql, paramsdjango/db/models/sql/where.py
class SubqueryConstraint:# Even if aggregates or windows would be used in a subquery,# the outer query isn't interested about those.contains_aggregate = Falsecontains_over_clause = Falsedef __init__(self, alias, columns, targets, query_object):self.alias = aliasself.columns = columnsself.targets = targetsquery_object.clear_ordering(clear_default=True)self.query_object = query_objectdef as_sql(self, compiler, connection):query = self.query_objectquery.set_values(self.targets)query_compiler = query.get_compiler(connection=connection)return query_compiler.as_subquery_condition(self.alias, self.columns, compiler)tests/composite_pk/test_filter.py
Getting rid of
SuqueryConstraint
andas_subquery_condition
was a goal of #373 so that's a great win!