'Returns an alias for the join in \'connection\', either reusing an
existing alias for that join or creating a new one. \'connection\' is a
tuple (lhs, table, join_cols) where \'lhs\' is either an existing
table alias or a table name. \'join_cols\' is a tuple of tuples containing
columns to join on ((l_id1, r_id1), (l_id2, r_id2)). The join corresponds
to the SQL equivalent of::
lhs.l_id1 = table.r_id1 AND lhs.l_id2 = table.r_id2
The \'reuse\' parameter can be either None which means all joins
(matching the connection) are reusable, or it can be a set containing
the aliases that can be reused.
If \'outer_if_first\' is True and a new join is created, it will have the
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.
If \'nullable\' is True, the join can potentially involve NULL values and
is a candidate for promotion (to "left outer") when combining querysets.
The \'join_field\' is the field we are joining along (if any).'

def join(self, connection, reuse=None, outer_if_first=False, nullable=False, join_field=None):

    (lhs, table, join_cols) = connection
    assert ((lhs is None) or (join_field is not None))
    existing = self.join_map.get(connection, ())
    if (reuse is None):
        reuse = existing
    else:
        reuse = [a for a in existing if (a in reuse)]
    for alias in reuse:
        if (join_field and (self.alias_map[alias].join_field != join_field)):
            continue
        self.ref_alias(alias)
        return alias
    (alias, _) = self.table_alias(table, True)
    if (not lhs):
        join_type = None
    elif (outer_if_first or (self.alias_map[lhs].join_type == self.LOUTER)):
        join_type = self.LOUTER
    else:
        join_type = self.INNER
    join = JoinInfo(table, alias, join_type, lhs, (join_cols or ((None, None),)), nullable, join_field)
    self.alias_map[alias] = join
    if (connection in self.join_map):
        self.join_map[connection] += (alias,)
    else:
        self.join_map[connection] = (alias,)
    return alias
