.. include:: changelog_07.rst
:start-line: 5
+.. changelog::
+ :version: 0.9.1
+
+ .. change::
+ :tags: bug, orm
+ :tickets: 2903
+
+ Fixed regression where we apparently still create an implicit
+ alias when saying query(B).join(B.cs), where "C" is a joined inh
+ class; however, this implicit alias was created only considering
+ the immediate left side, and not a longer chain of joins along different
+ joined-inh subclasses of the same base. As long as we're still
+ implicitly aliasing in this case, the behavior is dialed back a bit
+ so that it will alias the right side in a wider variety of cases.
+
.. changelog::
:version: 0.9.0
:released: December 30, 2013
raise sa_exc.InvalidRequestError(
"Can't construct a join from %s to %s, they "
"are the same entity" %
- (left, right))
+ (left, right))
l_info = inspect(left)
r_info = inspect(right)
- overlap = not create_aliases and \
- sql_util.selectables_overlap(l_info.selectable,
- r_info.selectable)
+
+ overlap = False
+ if not create_aliases:
+ right_mapper = getattr(r_info, "mapper", None)
+ # if the target is a joined inheritance mapping,
+ # be more liberal about auto-aliasing.
+ if right_mapper and (
+ right_mapper.with_polymorphic or
+ isinstance(right_mapper.mapped_table, expression.Join)
+ ):
+ for from_obj in self._from_obj or [l_info.selectable]:
+ if sql_util.selectables_overlap(l_info.selectable, from_obj) and \
+ sql_util.selectables_overlap(from_obj, r_info.selectable):
+ overlap = True
+ break
+ elif sql_util.selectables_overlap(l_info.selectable, r_info.selectable):
+ overlap = True
+
+
if overlap and l_info.selectable is r_info.selectable:
raise sa_exc.InvalidRequestError(
"Can't join table/selectable '%s' to itself" %
, use_default_dialect = True
)
+ def test_auto_aliasing_multi_link(self):
+ # test [ticket:2903]
+ sess = create_session()
+ Company, Engineer, Manager, Boss = self.classes.Company, \
+ self.classes.Engineer, \
+ self.classes.Manager, self.classes.Boss
+ q = sess.query(Company).\
+ join(Company.employees.of_type(Engineer)).\
+ join(Company.employees.of_type(Manager)).\
+ join(Company.employees.of_type(Boss))
+
+ self.assert_compile(q,
+ "SELECT companies.company_id AS companies_company_id, "
+ "companies.name AS companies_name FROM companies "
+ "JOIN (people JOIN engineers ON people.person_id = engineers.person_id) "
+ "ON companies.company_id = people.company_id "
+ "JOIN (people AS people_1 JOIN managers AS managers_1 "
+ "ON people_1.person_id = managers_1.person_id) "
+ "ON companies.company_id = people_1.company_id "
+ "JOIN (people AS people_2 JOIN managers AS managers_2 "
+ "ON people_2.person_id = managers_2.person_id JOIN boss AS boss_1 "
+ "ON managers_2.person_id = boss_1.boss_id) "
+ "ON companies.company_id = people_2.company_id",
+ use_default_dialect=True
+ )
class JoinTest(QueryTest, AssertsCompiledSQL):
self.tables.t1t2_2,
self.tables.t1)
- class T1(object):pass
- class T2(object):pass
+ class T1(object):
+ pass
+ class T2(object):
+ pass
mapper(T1, t1, properties={
- 't2s_1':relationship(T2, secondary=t1t2_1),
- 't2s_2':relationship(T2, secondary=t1t2_2),
+ 't2s_1': relationship(T2, secondary=t1t2_1),
+ 't2s_2': relationship(T2, secondary=t1t2_2),
})
mapper(T2, t2)