[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 inefa1908f66
. Backport of3a941230c8
from master
This commit is contained in:
parent
bebb7d4d7e
commit
b46b0f80e8
|
@ -297,7 +297,8 @@ class BaseDatabaseFeatures:
|
||||||
# field(s)?
|
# field(s)?
|
||||||
allows_multiple_constraints_on_same_fields = True
|
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
|
supports_boolean_expr_in_select_clause = True
|
||||||
|
|
||||||
# Does the backend support JSONField?
|
# Does the backend support JSONField?
|
||||||
|
|
|
@ -1089,7 +1089,8 @@ class Exists(Subquery):
|
||||||
|
|
||||||
def select_format(self, compiler, sql, params):
|
def select_format(self, compiler, sql, params):
|
||||||
# Wrap EXISTS() with a CASE WHEN expression if a database backend
|
# 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:
|
if not compiler.connection.features.supports_boolean_expr_in_select_clause:
|
||||||
sql = 'CASE WHEN {} THEN 1 ELSE 0 END'.format(sql)
|
sql = 'CASE WHEN {} THEN 1 ELSE 0 END'.format(sql)
|
||||||
return sql, params
|
return sql, params
|
||||||
|
|
|
@ -139,6 +139,7 @@ class SQLCompiler:
|
||||||
|
|
||||||
for expr in expressions:
|
for expr in expressions:
|
||||||
sql, params = self.compile(expr)
|
sql, params = self.compile(expr)
|
||||||
|
sql, params = expr.select_format(self, sql, params)
|
||||||
params_hash = make_hashable(params)
|
params_hash = make_hashable(params)
|
||||||
if (sql, params_hash) not in seen:
|
if (sql, params_hash) not in seen:
|
||||||
result.append((sql, params))
|
result.append((sql, params))
|
||||||
|
|
|
@ -18,3 +18,7 @@ Bugfixes
|
||||||
|
|
||||||
* Fixed a regression in Django 3.0 where aggregates used wrong annotations when
|
* Fixed a regression in Django 3.0 where aggregates used wrong annotations when
|
||||||
a queryset has multiple subqueries annotations (:ticket:`31568`).
|
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`).
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
import datetime
|
import datetime
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from unittest import skipIf
|
|
||||||
|
|
||||||
from django.core.exceptions import FieldDoesNotExist, FieldError
|
from django.core.exceptions import FieldDoesNotExist, FieldError
|
||||||
from django.db import connection
|
|
||||||
from django.db.models import (
|
from django.db.models import (
|
||||||
BooleanField, CharField, Count, DateTimeField, Exists, ExpressionWrapper,
|
BooleanField, CharField, Count, DateTimeField, Exists, ExpressionWrapper,
|
||||||
F, Func, IntegerField, Max, NullBooleanField, OuterRef, Q, Subquery, Sum,
|
F, Func, IntegerField, Max, NullBooleanField, OuterRef, Q, Subquery, Sum,
|
||||||
|
@ -623,7 +621,6 @@ class NonAggregateAnnotationTestCase(TestCase):
|
||||||
).values('name')
|
).values('name')
|
||||||
self.assertCountEqual(publisher_books_qs, [{'name': 'Sams'}, {'name': 'Morgan Kaufmann'}])
|
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):
|
def test_annotation_exists_aggregate_values_chaining(self):
|
||||||
qs = Book.objects.values('publisher').annotate(
|
qs = Book.objects.values('publisher').annotate(
|
||||||
has_authors=Exists(Book.authors.through.objects.filter(book=OuterRef('pk'))),
|
has_authors=Exists(Book.authors.through.objects.filter(book=OuterRef('pk'))),
|
||||||
|
|
Loading…
Reference in New Issue