#32650 closed Bug (fixed)
Cannot combine two queryset in a subquery
Reported by: | Raffaele Salmaso | Owned by: | Simon Charette |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 3.2 |
Severity: | Release blocker | Keywords: | |
Cc: | Triage Stage: | Ready for checkin | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
[Sample project https://github.com/rsalmaso/django32-subquery-test and run ./manage.py query
]
Django 3.2 fails this query (a combined queryset in a subquery):
import datetime as dt from decimal import Decimal from django.conf import settings from django.db import models from django.db.models import Case, OuterRef, Q, Subquery, Value, When from django.utils import timezone class UserQuerySet(models.QuerySet): def annotate_active_subscription_id(self): return self.annotate( active_subscription_id_db=Subquery( Subscription.objects.active() .annotate( plan_order=Case( When(plan__code="BASE", then=Value(1)), default=Value(0), output_field=models.PositiveSmallIntegerField(), ) ) .filter(user=OuterRef("id")) .order_by("plan_order", "-id") .values("id")[:1] ) ) class User(models.Model): objects = models.Manager.from_queryset(UserQuerySet)() class Plan(models.Model): code = models.CharField(verbose_name="Codice", max_length=255) class SubscriptionQuerySet(models.QuerySet): def will_be_renewed_today(self): today = dt.date.today() return self.filter(start_date__lte=today).exclude(user__subscriptions__start_date=today).distinct() def active(self): return self.filter(enabled=True).distinct() | self.will_be_renewed_today() class Subscription(models.Model): user = models.ForeignKey(User, verbose_name="Utente", on_delete=models.CASCADE, related_name="subscriptions") plan = models.ForeignKey(Plan, on_delete=models.CASCADE, verbose_name="Piano di abbonamento") start_date = models.DateField(verbose_name="Data di inizio", default=dt.date.today) enabled = models.BooleanField(verbose_name="Abilitato", default=True) objects = models.Manager.from_queryset(SubscriptionQuerySet)() print(User.objects.annotate_active_subscription_id().count())
with django 3.1.8
SELECT "subquery_user"."id", ( SELECT "subquery"."id" FROM ( SELECT DISTINCT U0."id", CASE WHEN (U2."code" = BASE) THEN 1 ELSE 0 END FROM "subquery_subscription" U0 INNER JOIN "subquery_plan" U2 ON (U0."plan_id" = U2."id") WHERE ( ( U0."enabled" OR ( U0."start_date" <= 2021 - 04 - 13 AND NOT ( U0."user_id" IN ( SELECT U2."user_id" FROM "subquery_subscription" U2 WHERE U2."start_date" = 2021 - 04 - 13 ) ) ) ) AND U0."user_id" = "subquery_user"."id" ) ORDER BY CASE WHEN (U2."code" = BASE) THEN 1 ELSE 0 END ASC, U0."id" DESC LIMIT 1 ) subquery ) AS "active_subscription_id_db" FROM "subquery_user"
with django 3.2 (
SELECT "subquery_user"."id", ( SELECT "subquery"."id" FROM ( SELECT DISTINCT U0."id", CASE WHEN (U2."code" = BASE) THEN 1 ELSE 0 END FROM "subquery_subscription" U0 INNER JOIN "subquery_plan" U2 ON (U0."plan_id" = U2."id") WHERE ( ( U0."enabled" OR ( U0."start_date" <= 2021 - 04 - 13 AND NOT ( EXISTS( SELECT (1) AS "a" FROM "subquery_subscription" V2 WHERE ( V2."start_date" = 2021 - 04 - 13 AND V2."user_id" = V0."user_id" ) LIMIT 1 ) ) ) ) AND U0."user_id" = "subquery_user"."id" ) ORDER BY CASE WHEN (U2."code" = BASE) THEN 1 ELSE 0 END ASC, U0."id" DESC LIMIT 1 ) subquery ) AS "active_subscription_id_db" FROM "subquery_user"
Traceback (most recent call last): File ".venvs/django32/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute return self.cursor.execute(sql, params) File ".venvs/django32/lib/python3.8/site-packages/django/db/backends/sqlite3/base.py", line 423, in execute return Database.Cursor.execute(self, query, params) sqlite3.OperationalError: no such column: V0.user_id The above exception was the direct cause of the following exception: Traceback (most recent call last): File "./manage.py", line 22, in <module> main() File "./manage.py", line 18, in main execute_from_command_line(sys.argv) File ".venvs/django32/lib/python3.8/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line utility.execute() File ".venvs/django32/lib/python3.8/site-packages/django/core/management/__init__.py", line 413, in execute self.fetch_command(subcommand).run_from_argv(self.argv) File ".venvs/django32/lib/python3.8/site-packages/django/core/management/base.py", line 354, in run_from_argv self.execute(*args, **cmd_options) File ".venvs/django32/lib/python3.8/site-packages/django/core/management/base.py", line 398, in execute output = self.handle(*args, **options) File "/home/raf/src/fiscozen/django-debug/subquery/management/commands/query.py", line 11, in handle print(qs.count()) File ".venvs/django32/lib/python3.8/site-packages/django/db/models/query.py", line 412, in count return self.query.get_count(using=self.db) File ".venvs/django32/lib/python3.8/site-packages/django/db/models/sql/query.py", line 526, in get_count number = obj.get_aggregation(using, ['__count'])['__count'] File ".venvs/django32/lib/python3.8/site-packages/django/db/models/sql/query.py", line 511, in get_aggregation result = compiler.execute_sql(SINGLE) File ".venvs/django32/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1175, in execute_sql cursor.execute(sql, params) File ".venvs/django32/lib/python3.8/site-packages/django/db/backends/utils.py", line 98, in execute return super().execute(sql, params) File ".venvs/django32/lib/python3.8/site-packages/django/db/backends/utils.py", line 66, in execute return self._execute_with_wrappers(sql, params, many=False, executor=self._execute) File ".venvs/django32/lib/python3.8/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers return executor(sql, params, many, context) File ".venvs/django32/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute return self.cursor.execute(sql, params) File ".venvs/django32/lib/python3.8/site-packages/django/db/utils.py", line 90, in __exit__ raise dj_exc_value.with_traceback(traceback) from exc_value File ".venvs/django32/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute return self.cursor.execute(sql, params) File ".venvs/django32/lib/python3.8/site-packages/django/db/backends/sqlite3/base.py", line 423, in execute return Database.Cursor.execute(self, query, params) django.db.utils.OperationalError: no such column: V0.user_id
Tested with 3.2, https://github.com/django/django/commit/d6314c4c2ef647efe0d12450214fc5b4a4055290 (next 3.2.1) and https://github.com/django/django/commit/59552bea5790c97be0da0a6f16ccd0189857c7a7 (main)
Change History (9)
comment:1 by , 4 years ago
Description: | modified (diff) |
---|
comment:2 by , 4 years ago
comment:3 by , 4 years ago
Owner: | changed from | to
---|---|
Severity: | Normal → Release blocker |
Status: | new → assigned |
Triage Stage: | Unreviewed → Accepted |
Looks like the code doesn't properly handle nested subquery exclusion, likely due to re-aliasing in Query.trim_start
.
comment:4 by , 4 years ago
After a bit of investigation it seems the issue might actually lies in sql.Query.combine
possibly with how it doesn't handle external_aliases
.
comment:5 by , 4 years ago
Has patch: | set |
---|---|
Patch needs improvement: | set |
It ended up being an issue in Query.combine
when dealing with subq_aliases
.
comment:6 by , 4 years ago
Patch needs improvement: | unset |
---|
comment:7 by , 4 years ago
Triage Stage: | Accepted → Ready for checkin |
---|
It seems to be related to https://code.djangoproject.com/ticket/32143 (https://github.com/django/django/commit/8593e162c9cb63a6c0b06daf045bc1c21eb4d7c1)