From 616f468760e4984915bb2ccca6b9eb3d80ddadb0 Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Sun, 26 Nov 2017 17:32:17 +0100 Subject: [PATCH] Fixed #28848 -- Fixed SQLite/MySQL crash when ordering by a filtered subquery that uses nulls_first/nulls_last. --- django/db/models/expressions.py | 1 + docs/releases/1.11.8.txt | 3 +++ tests/ordering/tests.py | 23 ++++++++++++++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index e03538932e..82db5c8bf8 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -1106,6 +1106,7 @@ class OrderBy(BaseExpression): } placeholders.update(extra_context) template = template or self.template + params *= template.count('%(expression)s') return (template % placeholders).rstrip(), params def as_sqlite(self, compiler, connection): diff --git a/docs/releases/1.11.8.txt b/docs/releases/1.11.8.txt index 89b6e5ed52..6f744a6886 100644 --- a/docs/releases/1.11.8.txt +++ b/docs/releases/1.11.8.txt @@ -21,3 +21,6 @@ Bugfixes * Made ``QuerySet.iterator()`` use server-side cursors on PostgreSQL after ``values()`` and ``values_list()`` (:ticket:`28817`). + +* Fixed crash on SQLite and MySQL when ordering by a filtered subquery that + uses ``nulls_first`` or ``nulls_last`` (:ticket:`28848`). diff --git a/tests/ordering/tests.py b/tests/ordering/tests.py index 07c319b4c3..8c07a27428 100644 --- a/tests/ordering/tests.py +++ b/tests/ordering/tests.py @@ -1,7 +1,7 @@ from datetime import datetime from operator import attrgetter -from django.db.models import F +from django.db.models import DateTimeField, F, Max, OuterRef, Subquery from django.db.models.functions import Upper from django.test import TestCase @@ -138,6 +138,27 @@ class OrderingTests(TestCase): [self.a1, self.a2, self.a4, self.a3], ) + def test_orders_nulls_first_on_filtered_subquery(self): + Article.objects.filter(headline='Article 1').update(author=self.author_1) + Article.objects.filter(headline='Article 2').update(author=self.author_1) + Article.objects.filter(headline='Article 4').update(author=self.author_2) + Author.objects.filter(name__isnull=True).delete() + author_3 = Author.objects.create(name='Name 3') + article_subquery = Article.objects.filter( + author=OuterRef('pk'), + headline__icontains='Article', + ).order_by().values('author').annotate( + last_date=Max('pub_date'), + ).values('last_date') + self.assertQuerysetEqualReversible( + Author.objects.annotate( + last_date=Subquery(article_subquery, output_field=DateTimeField()) + ).order_by( + F('last_date').asc(nulls_first=True) + ).distinct(), + [author_3, self.author_1, self.author_2], + ) + def test_stop_slicing(self): """ Use the 'stop' part of slicing notation to limit the results.