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

Fixed #21376 -- New implementation for query join promotion logic

This commit introduced a new class JoinPromoter that can be used to
abstract away join promotion problems for complex filter conditions.
Query._add_q() and Query.combine() now use the new class.

Also, added a lot of comments about why join promotion is done the way
it is.

Thanks to Tim Graham for original report and testing the changes, and
for Loic Bistuer for review.
parent ae029b44
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ class SQLEvaluator(object):
                field, sources, opts, join_list, path = query.setup_joins(
                    field_list, query.get_meta(),
                    query.get_initial_alias(), self.reuse)
                self._used_joins = join_list
                targets, _, join_list = query.trim_joins(sources, join_list, path)
                if self.reuse is not None:
                    self.reuse.update(join_list)
+145 −135

File changed.

Preview size limit exceeded, changes collapsed.

+2 −2
Original line number Diff line number Diff line
@@ -412,8 +412,8 @@ class ObjectB(models.Model):
@python_2_unicode_compatible
class ObjectC(models.Model):
    name = models.CharField(max_length=50)
    objecta = models.ForeignKey(ObjectA)
    objectb = models.ForeignKey(ObjectB)
    objecta = models.ForeignKey(ObjectA, null=True)
    objectb = models.ForeignKey(ObjectB, null=True)

    def __str__(self):
        return self.name
+13 −0
Original line number Diff line number Diff line
@@ -3189,3 +3189,16 @@ class ValuesJoinPromotionTests(TestCase):
    def test_non_nullable_fk_not_promoted(self):
        qs = ObjectB.objects.values('objecta__name')
        self.assertTrue(' INNER JOIN ' in str(qs.query))

    def test_ticket_21376(self):
        a = ObjectA.objects.create()
        ObjectC.objects.create(objecta=a)
        qs = ObjectC.objects.filter(
            Q(objecta=a) | Q(objectb__objecta=a),
        )
        qs = qs.filter(
            Q(objectb=1) | Q(objecta=a),
        )
        self.assertEqual(qs.count(), 1)
        tblname = connection.ops.quote_name(ObjectB._meta.db_table)
        self.assertTrue(' LEFT OUTER JOIN %s' % tblname in str(qs.query))