summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2020-05-14 15:07:08 +0200
committerCarlton Gibson <carlton.gibson@noumenal.es>2020-05-14 15:11:18 +0200
commit92acf1022fb13a7a8b1ff7cdfe72c21050c1e4e7 (patch)
tree195de65659a75b42748fe3a580357b3be3a5fd7a
parent49bbf6570d9f0880b836f741d79e4cdb6e061ea2 (diff)
[3.0.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
-rw-r--r--django/db/backends/base/features.py3
-rw-r--r--django/db/models/expressions.py3
-rw-r--r--django/db/models/sql/compiler.py1
-rw-r--r--docs/releases/3.0.7.txt4
-rw-r--r--tests/annotations/tests.py3
5 files changed, 9 insertions, 5 deletions
diff --git a/django/db/backends/base/features.py b/django/db/backends/base/features.py
index 89b417c7de..20996f9cf5 100644
--- a/django/db/backends/base/features.py
+++ b/django/db/backends/base/features.py
@@ -289,7 +289,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
def __init__(self, connection):
diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py
index 2733fbada9..fee59551a8 100644
--- a/django/db/models/expressions.py
+++ b/django/db/models/expressions.py
@@ -1108,7 +1108,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 18365f1d75..ccec6fdc38 100644
--- a/django/db/models/sql/compiler.py
+++ b/django/db/models/sql/compiler.py
@@ -135,6 +135,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 0cdc9178b0..0064b14ed0 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'))),