From b46b0f80e81bbf8fcf7e8c20c249c8041db5e227 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Thu, 14 May 2020 15:07:08 +0200 Subject: [PATCH] [3.1.x] Fixed #31584 -- Fixed crash when chaining values()/values_list() after Exists() annotation and aggregation on Oracle. Oracle requires the EXISTS expression to be wrapped in a CASE WHEN in the GROUP BY clause. Regression in efa1908f662c19038a944129c81462485c4a9fe8. Backport of 3a941230c85b2702a5e1cd97e17251ce21057efa from master --- django/db/backends/base/features.py | 3 ++- django/db/models/expressions.py | 3 ++- django/db/models/sql/compiler.py | 1 + docs/releases/3.0.7.txt | 4 ++++ tests/annotations/tests.py | 3 --- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/django/db/backends/base/features.py b/django/db/backends/base/features.py index 33eeff171d..67bfe77e7c 100644 --- a/django/db/backends/base/features.py +++ b/django/db/backends/base/features.py @@ -297,7 +297,8 @@ class BaseDatabaseFeatures: # field(s)? allows_multiple_constraints_on_same_fields = True - # Does the backend support boolean expressions in the SELECT clause? + # Does the backend support boolean expressions in SELECT and GROUP BY + # clauses? supports_boolean_expr_in_select_clause = True # Does the backend support JSONField? diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index 8d37c457ce..eb5fafc90b 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -1089,7 +1089,8 @@ class Exists(Subquery): def select_format(self, compiler, sql, params): # Wrap EXISTS() with a CASE WHEN expression if a database backend - # (e.g. Oracle) doesn't support boolean expression in the SELECT list. + # (e.g. Oracle) doesn't support boolean expression in SELECT or GROUP + # BY list. if not compiler.connection.features.supports_boolean_expr_in_select_clause: sql = 'CASE WHEN {} THEN 1 ELSE 0 END'.format(sql) return sql, params diff --git a/django/db/models/sql/compiler.py b/django/db/models/sql/compiler.py index 09a9d73077..c368a59226 100644 --- a/django/db/models/sql/compiler.py +++ b/django/db/models/sql/compiler.py @@ -139,6 +139,7 @@ class SQLCompiler: for expr in expressions: sql, params = self.compile(expr) + sql, params = expr.select_format(self, sql, params) params_hash = make_hashable(params) if (sql, params_hash) not in seen: result.append((sql, params)) diff --git a/docs/releases/3.0.7.txt b/docs/releases/3.0.7.txt index 0f1188724a..def27a49ec 100644 --- a/docs/releases/3.0.7.txt +++ b/docs/releases/3.0.7.txt @@ -18,3 +18,7 @@ Bugfixes * Fixed a regression in Django 3.0 where aggregates used wrong annotations when a queryset has multiple subqueries annotations (:ticket:`31568`). + +* Fixed a regression in Django 3.0 where ``QuerySet.values()`` and + ``values_list()`` crashed if a queryset contained an aggregation and an + ``Exists()`` annotation on Oracle (:ticket:`31584`). diff --git a/tests/annotations/tests.py b/tests/annotations/tests.py index dad34e03eb..142c23ead6 100644 --- a/tests/annotations/tests.py +++ b/tests/annotations/tests.py @@ -1,9 +1,7 @@ import datetime from decimal import Decimal -from unittest import skipIf from django.core.exceptions import FieldDoesNotExist, FieldError -from django.db import connection from django.db.models import ( BooleanField, CharField, Count, DateTimeField, Exists, ExpressionWrapper, F, Func, IntegerField, Max, NullBooleanField, OuterRef, Q, Subquery, Sum, @@ -623,7 +621,6 @@ class NonAggregateAnnotationTestCase(TestCase): ).values('name') self.assertCountEqual(publisher_books_qs, [{'name': 'Sams'}, {'name': 'Morgan Kaufmann'}]) - @skipIf(connection.vendor == 'oracle', 'See https://code.djangoproject.com/ticket/31584') def test_annotation_exists_aggregate_values_chaining(self): qs = Book.objects.values('publisher').annotate( has_authors=Exists(Book.authors.through.objects.filter(book=OuterRef('pk'))),