Opened 2 years ago
Closed 23 months ago
#35042 closed Bug (fixed)
Queryset count does not work after union when annotations on aggregation are unused.
| Reported by: | Marcin | Owned by: | Simon Charette |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | 4.2 |
| Severity: | Normal | Keywords: | |
| Cc: | Simon Charette | 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
While upgrading to Django 4 I've found a bug with count() which can be replicated using the following code. Notice that evaluating the queryset works fine, the issue is that for some reason, when counting django is not including total_count column from the second queryset in the query and this is causing union to fail. In Django 3 the code works fine and produces a correct SQL. I've attached an example project which can be used to reproduce the bug, with command tox -e ALL.
models.py
class Foo(models.Model): name = models.TextField() class Bar(models.Model): foo = models.ForeignKey(Foo, on_delete=models.CASCADE) count = models.IntegerField()
tests.py
qs1 = Foo.objects.all().annotate( _count=models.Sum('bar__count') ).values('name').annotate( total_count=models.F('_count') ).values('name', 'total_count') qs2 = Bar.objects.all().annotate( name=models.F('foo__name'), total_count=models.Value(0, output_field=models.IntegerField()) ).values('name', 'total_count') self.assertEqual(list(qs1.union(qs2, all=True)), []) self.assertEqual(qs1.union(qs2, all=True).count(), 0)
Exception thrown in Django 4+
Traceback (most recent call last):
File "/home/user/foo/bar/tests.py", line 21, in test_recreate_bug
self.assertEqual(qs1.union(qs2, all=True).count(), 0)
File "/home/user/foo/.tox/django5/lib/python3.10/site-packages/django/db/models/query.py", line 618, in count
return self.query.get_count(using=self.db)
File "/home/user/foo/.tox/django5/lib/python3.10/site-packages/django/db/models/sql/query.py", line 616, in get_count
return obj.get_aggregation(using, {"__count": Count("*")})["__count"]
File "/home/user/foo/.tox/django5/lib/python3.10/site-packages/django/db/models/sql/query.py", line 602, in get_aggregation
result = compiler.execute_sql(SINGLE)
File "/home/user/foo/.tox/django5/lib/python3.10/site-packages/django/db/models/sql/compiler.py", line 1562, in execute_sql
cursor.execute(sql, params)
File "/home/user/foo/.tox/django5/lib/python3.10/site-packages/django/db/backends/utils.py", line 79, in execute
return self._execute_with_wrappers(
File "/home/user/foo/.tox/django5/lib/python3.10/site-packages/django/db/backends/utils.py", line 92, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/home/user/foo/.tox/django5/lib/python3.10/site-packages/django/db/backends/utils.py", line 100, in _execute
with self.db.wrap_database_errors:
File "/home/user/foo/.tox/django5/lib/python3.10/site-packages/django/db/utils.py", line 91, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/home/user/foo/.tox/django5/lib/python3.10/site-packages/django/db/backends/utils.py", line 105, in _execute
return self.cursor.execute(sql, params)
File "/home/user/foo/.tox/django5/lib/python3.10/site-packages/django/db/backends/sqlite3/base.py", line 328, in execute
return super().execute(query, params)
django.db.utils.OperationalError: SELECTs to the left and right of UNION ALL do not have the same number of result columns
Attachments (1)
Change History (6)
by , 2 years ago
| Attachment: | foo.tar.gz added |
|---|
comment:1 by , 2 years ago
| Cc: | added |
|---|---|
| Component: | Uncategorized → Database layer (models, ORM) |
| Summary: | Queryset count does not work after union → Queryset count does not work after union when annotations on aggregation are unused. |
| Triage Stage: | Unreviewed → Accepted |
| Type: | Uncategorized → Bug |
Thanks for the report.
Regression in 59bea9efd2768102fc9d3aedda469502c218e9b7.
comment:2 by , 2 years ago
| Owner: | changed from to |
|---|---|
| Status: | new → assigned |
comment:3 by , 2 years ago
| Has patch: | set |
|---|
comment:4 by , 23 months ago
| Triage Stage: | Accepted → Ready for checkin |
|---|
minimal example