Commit b1b04df0 authored by Anssi Kääriäinen's avatar Anssi Kääriäinen
Browse files

Fixed #20600 -- ordered distinct(*fields) in subqueries

parent ccbba981
Loading
Loading
Loading
Loading
+16 −10
Original line number Diff line number Diff line
@@ -164,7 +164,7 @@ class SQLCompiler(object):
        Used when nesting this query inside another.
        """
        obj = self.query.clone()
        if obj.low_mark == 0 and obj.high_mark is None:
        if obj.low_mark == 0 and obj.high_mark is None and not self.query.distinct_fields:
            # If there is no slicing in use, then we can safely drop all ordering
            obj.clear_ordering(True)
        return obj.get_compiler(connection=self.connection).as_sql()
@@ -364,6 +364,10 @@ class SQLCompiler(object):

        params = []
        ordering_params = []
        # For plain DISTINCT queries any ORDER BY clause must appear
        # in SELECT clause.
        # http://www.postgresql.org/message-id/27009.1171559417@sss.pgh.pa.us
        must_append_to_select = distinct and not self.query.distinct_fields
        for pos, field in enumerate(ordering):
            if field == '?':
                result.append(self.connection.ops.random_function_sql())
@@ -388,7 +392,7 @@ class SQLCompiler(object):
                if (table, col) not in processed_pairs:
                    elt = '%s.%s' % (qn(table), col)
                    processed_pairs.add((table, col))
                    if not distinct or elt in select_aliases:
                    if not must_append_to_select or elt in select_aliases:
                        result.append('%s %s' % (elt, order))
                        group_by.append((elt, []))
            elif not self.query._extra or get_order_dir(field)[0] not in self.query._extra:
@@ -400,20 +404,22 @@ class SQLCompiler(object):
                        if (table, col) not in processed_pairs:
                            elt = '%s.%s' % (qn(table), qn2(col))
                            processed_pairs.add((table, col))
                            if distinct and elt not in select_aliases:
                            if must_append_to_select and elt not in select_aliases:
                                ordering_aliases.append(elt)
                            result.append('%s %s' % (elt, order))
                            group_by.append((elt, []))
            else:
                elt = qn2(col)
                if col not in self.query.extra_select:
                    if must_append_to_select:
                        sql = "(%s) AS %s" % (self.query.extra[col][0], elt)
                        ordering_aliases.append(sql)
                        ordering_params.extend(self.query.extra[col][1])
                        result.append('%s %s' % (elt, order))
                    else:
                        result.append("(%s) %s" % (self.query.extra[col][0], order))
                        params.extend(self.query.extra[col][1])
                else:
                    if distinct and col not in select_aliases:
                        ordering_aliases.append(elt)
                        ordering_params.extend(params)
                    result.append('%s %s' % (elt, order))
                group_by.append(self.query.extra[col])
        self.ordering_aliases = ordering_aliases
+21 −7
Original line number Diff line number Diff line
@@ -16,13 +16,13 @@ class DistinctOnTests(TestCase):
        Tag.objects.create(name='t4', parent=t3)
        Tag.objects.create(name='t5', parent=t3)

        p1_o1 = Staff.objects.create(id=1, name="p1", organisation="o1")
        p2_o1 = Staff.objects.create(id=2, name="p2", organisation="o1")
        p3_o1 = Staff.objects.create(id=3, name="p3", organisation="o1")
        Staff.objects.create(id=4, name="p1", organisation="o2")
        p1_o1.coworkers.add(p2_o1, p3_o1)
        StaffTag.objects.create(staff=p1_o1, tag=t1)
        StaffTag.objects.create(staff=p1_o1, tag=t1)
        self.p1_o1 = Staff.objects.create(id=1, name="p1", organisation="o1")
        self.p2_o1 = Staff.objects.create(id=2, name="p2", organisation="o1")
        self.p3_o1 = Staff.objects.create(id=3, name="p3", organisation="o1")
        self.p1_o2 = Staff.objects.create(id=4, name="p1", organisation="o2")
        self.p1_o1.coworkers.add(self.p2_o1, self.p3_o1)
        StaffTag.objects.create(staff=self.p1_o1, tag=t1)
        StaffTag.objects.create(staff=self.p1_o1, tag=t1)

        celeb1 = Celebrity.objects.create(name="c1")
        celeb2 = Celebrity.objects.create(name="c2")
@@ -114,3 +114,17 @@ class DistinctOnTests(TestCase):
        # distinct + aggregate not allowed
        with self.assertRaises(NotImplementedError):
            Celebrity.objects.distinct('id').aggregate(Max('id'))

    def test_distinct_on_in_ordered_subquery(self):
        qs = Staff.objects.distinct('name').order_by('name', 'id')
        qs = Staff.objects.filter(pk__in=qs).order_by('name')
        self.assertQuerysetEqual(
            qs, [self.p1_o1, self.p2_o1, self.p3_o1],
            lambda x: x
        )
        qs = Staff.objects.distinct('name').order_by('name', '-id')
        qs = Staff.objects.filter(pk__in=qs).order_by('name')
        self.assertQuerysetEqual(
            qs, [self.p1_o2, self.p2_o1, self.p3_o1],
            lambda x: x
        )
+16 −0
Original line number Diff line number Diff line
@@ -347,3 +347,19 @@ class ExtraRegressTests(TestCase):
            ['<TestObject: TestObject: a,a,a>', '<TestObject: TestObject: b,a,a>'],
            ordered=False
        )

    def test_extra_values_distinct_ordering(self):
        t1 = TestObject.objects.create(first='a', second='a', third='a')
        t2 = TestObject.objects.create(first='a', second='b', third='b')
        qs = TestObject.objects.extra(
            select={'second_extra': 'second'}
        ).values_list('id', flat=True).distinct()
        self.assertQuerysetEqual(
            qs.order_by('second_extra'), [t1.pk, t2.pk], lambda x: x)
        self.assertQuerysetEqual(
            qs.order_by('-second_extra'), [t2.pk, t1.pk], lambda x: x)
        # Note: the extra ordering must appear in select clause, so we get two
        # non-distinct results here (this is on purpose, see #7070).
        self.assertQuerysetEqual(
            qs.order_by('-second_extra').values_list('first', flat=True),
            ['a', 'a'], lambda x: x)