Fixed #29542 -- Fixed invalid SQL if a Subquery from the HAVING clause is used in the GROUP BY clause.
Thanks Tim Graham for the review.
This commit is contained in:
parent
312eb5cb11
commit
dd3b470719
|
@ -6,7 +6,7 @@ from itertools import chain
|
||||||
|
|
||||||
from django.core.exceptions import EmptyResultSet, FieldError
|
from django.core.exceptions import EmptyResultSet, FieldError
|
||||||
from django.db.models.constants import LOOKUP_SEP
|
from django.db.models.constants import LOOKUP_SEP
|
||||||
from django.db.models.expressions import OrderBy, Random, RawSQL, Ref
|
from django.db.models.expressions import OrderBy, Random, RawSQL, Ref, Subquery
|
||||||
from django.db.models.query_utils import QueryWrapper, select_related_descend
|
from django.db.models.query_utils import QueryWrapper, select_related_descend
|
||||||
from django.db.models.sql.constants import (
|
from django.db.models.sql.constants import (
|
||||||
CURSOR, GET_ITERATOR_CHUNK_SIZE, MULTI, NO_RESULTS, ORDER_DIR, SINGLE,
|
CURSOR, GET_ITERATOR_CHUNK_SIZE, MULTI, NO_RESULTS, ORDER_DIR, SINGLE,
|
||||||
|
@ -127,6 +127,11 @@ class SQLCompiler:
|
||||||
|
|
||||||
for expr in expressions:
|
for expr in expressions:
|
||||||
sql, params = self.compile(expr)
|
sql, params = self.compile(expr)
|
||||||
|
if isinstance(expr, Subquery) and not sql.startswith('('):
|
||||||
|
# Subquery expression from HAVING clause may not contain
|
||||||
|
# wrapping () because they could be removed when a subquery is
|
||||||
|
# the "rhs" in an expression (see Subquery._prepare()).
|
||||||
|
sql = '(%s)' % sql
|
||||||
if (sql, tuple(params)) not in seen:
|
if (sql, tuple(params)) not in seen:
|
||||||
result.append((sql, params))
|
result.append((sql, params))
|
||||||
seen.add((sql, tuple(params)))
|
seen.add((sql, tuple(params)))
|
||||||
|
|
|
@ -4,7 +4,7 @@ from decimal import Decimal
|
||||||
from django.core.exceptions import FieldDoesNotExist, FieldError
|
from django.core.exceptions import FieldDoesNotExist, FieldError
|
||||||
from django.db.models import (
|
from django.db.models import (
|
||||||
BooleanField, CharField, Count, DateTimeField, ExpressionWrapper, F, Func,
|
BooleanField, CharField, Count, DateTimeField, ExpressionWrapper, F, Func,
|
||||||
IntegerField, NullBooleanField, Q, Sum, Value,
|
IntegerField, NullBooleanField, OuterRef, Q, Subquery, Sum, Value,
|
||||||
)
|
)
|
||||||
from django.db.models.expressions import RawSQL
|
from django.db.models.expressions import RawSQL
|
||||||
from django.db.models.functions import Length, Lower
|
from django.db.models.functions import Length, Lower
|
||||||
|
@ -585,3 +585,16 @@ class NonAggregateAnnotationTestCase(TestCase):
|
||||||
qs,
|
qs,
|
||||||
[{'jacob_name': 'Jacob Kaplan-Moss', 'james_name': 'James Bennett'}],
|
[{'jacob_name': 'Jacob Kaplan-Moss', 'james_name': 'James Bennett'}],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@skipUnlessDBFeature('supports_subqueries_in_group_by')
|
||||||
|
def test_annotation_filter_with_subquery(self):
|
||||||
|
long_books_qs = Book.objects.filter(
|
||||||
|
publisher=OuterRef('pk'),
|
||||||
|
pages__gt=400,
|
||||||
|
).values('publisher').annotate(count=Count('pk')).values('count')
|
||||||
|
publisher_books_qs = Publisher.objects.annotate(
|
||||||
|
total_books=Count('book'),
|
||||||
|
).filter(
|
||||||
|
total_books=Subquery(long_books_qs, output_field=IntegerField()),
|
||||||
|
).values('name')
|
||||||
|
self.assertCountEqual(publisher_books_qs, [{'name': 'Sams'}, {'name': 'Morgan Kaufmann'}])
|
||||||
|
|
Loading…
Reference in New Issue