浏览代码

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.
Mariusz Felisiak 4 年之前
父节点
当前提交
3a941230c8

+ 2 - 1
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?

+ 2 - 1
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

+ 1 - 0
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))

+ 4 - 0
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`).

+ 0 - 3
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'))),