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

[1.5.x] Fixed #19672 -- Error in negated Q() filtering

There was a variable overwrite error in negated join filtering. This
happened when add_filter() was adding the IS NULL condition to the
WHERE clause.

This is not a backport from master as there have been some other
refactorings which made this patch irrelevant.

The patch is from Ian Kelly.
parent 8d4342f2
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -1193,15 +1193,15 @@ class Query(object):
            self.promote_joins(join_list)
            if lookup_type != 'isnull':
                if len(join_list) > 1:
                    for alias in join_list:
                        if self.alias_map[alias].join_type == self.LOUTER:
                            j_col = self.alias_map[alias].rhs_join_col
                    for j_alias in join_list:
                        if self.alias_map[j_alias].join_type == self.LOUTER:
                            j_col = self.alias_map[j_alias].rhs_join_col
                            # The join promotion logic should never produce
                            # a LOUTER join for the base join - assert that.
                            assert j_col is not None
                            entry = self.where_class()
                            entry.add(
                                (Constraint(alias, j_col, None), 'isnull', True),
                                (Constraint(j_alias, j_col, None), 'isnull', True),
                                AND
                            )
                            entry.negate()
+1 −0
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ class Annotation(models.Model):
class ExtraInfo(models.Model):
    info = models.CharField(max_length=100)
    note = models.ForeignKey(Note)
    value = models.IntegerField(null=True)

    class Meta:
        ordering = ['info']
+10 −2
Original line number Diff line number Diff line
@@ -51,8 +51,8 @@ class Queries1Tests(BaseQuerysetTest):

        # Create these out of order so that sorting by 'id' will be different to sorting
        # by 'info'. Helps detect some problems later.
        self.e2 = ExtraInfo.objects.create(info='e2', note=n2)
        e1 = ExtraInfo.objects.create(info='e1', note=self.n1)
        self.e2 = ExtraInfo.objects.create(info='e2', note=n2, value=41)
        e1 = ExtraInfo.objects.create(info='e1', note=self.n1, value=42)

        self.a1 = Author.objects.create(name='a1', num=1001, extra=e1)
        self.a2 = Author.objects.create(name='a2', num=2002, extra=e1)
@@ -880,6 +880,14 @@ class Queries1Tests(BaseQuerysetTest):
            Item.objects.filter(Q(tags__name__in=['t4', 't3'])),
            [repr(i) for i in Item.objects.filter(~~Q(tags__name__in=['t4', 't3']))])

    def test_ticket19672(self):
        self.assertQuerysetEqual(
            Report.objects.filter(Q(creator__isnull=False) &
                                  ~Q(creator__extra__value=41)),
            ['<Report: r1>']
        )


class Queries2Tests(TestCase):
    def setUp(self):
        Number.objects.create(num=4)