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

Removed join() promote kwarg

The join promote=True was over-aggressive in select_related handling.
After that was removed, the only other user was query.combine(). That
use case is very easy to handle locally, so there is no more need for
the join(promote=True) flag.

Refs #19849.
parent 3fef304f
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -620,7 +620,7 @@ class SQLCompiler(object):

            alias = self.query.join((alias, table, f.column,
                    f.rel.get_related_field().column),
                    promote=promote, join_field=f)
                    outer_if_first=promote, join_field=f)
            columns, aliases = self.get_default_columns(start_alias=alias,
                    opts=f.rel.to._meta, as_pairs=True)
            self.query.related_select_cols.extend(
@@ -648,7 +648,7 @@ class SQLCompiler(object):
                table = model._meta.db_table
                alias = self.query.join(
                    (alias, table, f.rel.get_related_field().column, f.column),
                    promote=True, join_field=f
                    outer_if_first=True, join_field=f
                )
                from_parent = (opts.model if issubclass(model, opts.model)
                               else None)
+9 −16
Original line number Diff line number Diff line
@@ -507,9 +507,11 @@ class Query(object):
            # updated alias.
            lhs = change_map.get(lhs, lhs)
            new_alias = self.join(
                (lhs, table, lhs_col, col), reuse=reuse, promote=promote,
                (lhs, table, lhs_col, col), reuse=reuse,
                outer_if_first=not conjunction, nullable=nullable,
                join_field=join_field)
            if promote:
                self.promote_joins([new_alias])
            # We can't reuse the same join again in the query. If we have two
            # distinct joins for the same connection in rhs query, then the
            # combined query must have two joins, too.
@@ -914,8 +916,8 @@ class Query(object):
        """
        return len([1 for count in self.alias_refcount.values() if count])

    def join(self, connection, reuse=None, promote=False,
             outer_if_first=False, nullable=False, join_field=None):
    def join(self, connection, reuse=None, outer_if_first=False,
             nullable=False, join_field=None):
        """
        Returns an alias for the join in 'connection', either reusing an
        existing alias for that join or creating a new one. 'connection' is a
@@ -929,14 +931,8 @@ class Query(object):
        (matching the connection) are reusable, or it can be a set containing
        the aliases that can be reused.

        If 'promote' is True, the join type for the alias will be LOUTER (if
        the alias previously existed, the join type will be promoted from INNER
        to LOUTER, if necessary).

        If 'outer_if_first' is True and a new join is created, it will have the
        LOUTER join type. Used for example when adding ORed filters, where we
        want to use LOUTER joins except if some other join already restricts
        the join to INNER join.
        LOUTER join type.

        A join is always created as LOUTER if the lhs alias is LOUTER to make
        sure we do not generate chains like t1 LOUTER t2 INNER t3.
@@ -961,8 +957,6 @@ class Query(object):
                # join_field used for the under work join.
                continue
            self.ref_alias(alias)
            if promote or (lhs and self.alias_map[lhs].join_type == self.LOUTER):
                self.promote_joins([alias])
            return alias

        # No reuse is possible, so we need a new alias.
@@ -971,10 +965,9 @@ class Query(object):
            # Not all tables need to be joined to anything. No join type
            # means the later columns are ignored.
            join_type = None
        elif (promote or outer_if_first
              or self.alias_map[lhs].join_type == self.LOUTER):
            # We need to use LOUTER join if asked by promote or outer_if_first,
            # or if the LHS table is left-joined in the query.
        elif outer_if_first or self.alias_map[lhs].join_type == self.LOUTER:
            # We need to use LOUTER join if asked by outer_if_first or if the
            # LHS table is left-joined in the query.
            join_type = self.LOUTER
        else:
            join_type = self.INNER
+23 −0
Original line number Diff line number Diff line
@@ -139,3 +139,26 @@ class SelectRelatedRegressTests(TestCase):
        self.assertEqual(troy.name, 'Troy Buswell')
        self.assertEqual(troy.value, 42)
        self.assertEqual(troy.state.name, 'Western Australia')

    def test_null_join_promotion(self):
        australia = Country.objects.create(name='Australia')
        active = ClientStatus.objects.create(name='active')

        wa = State.objects.create(name="Western Australia", country=australia)
        bob = Client.objects.create(name='Bob', status=active)
        jack = Client.objects.create(name='Jack', status=active, state=wa)
        qs = Client.objects.filter(state=wa).select_related('state')
        with self.assertNumQueries(1):
            self.assertEqual(list(qs), [jack])
            self.assertEqual(qs[0].state, wa)
            # The select_related join wasn't promoted as there was already an
            # existing (even if trimmed) inner join to state.
            self.assertFalse('LEFT OUTER' in str(qs.query))
        qs = Client.objects.select_related('state').order_by('name')
        with self.assertNumQueries(1):
            self.assertEqual(list(qs), [bob, jack])
            self.assertIs(qs[0].state, None)
            self.assertEqual(qs[1].state, wa)
            # The select_related join was promoted as there is already an
            # existing join.
            self.assertTrue('LEFT OUTER' in str(qs.query))