From: Mike Bayer Date: Thu, 2 Jan 2014 23:26:32 +0000 (-0500) Subject: - Fixed regression where we apparently still create an implicit X-Git-Tag: rel_0_9_1~14 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ca8fca63916897f1bbc2fa4f1ee440c6b5d9a88a;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - 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. [ticket:2903] --- diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst index 65da0a39fd..069df37a4c 100644 --- a/doc/build/changelog/changelog_09.rst +++ b/doc/build/changelog/changelog_09.rst @@ -11,6 +11,21 @@ .. 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 diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 4f10a6ada2..3815f5a983 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -1842,14 +1842,30 @@ class Query(object): 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" % diff --git a/test/orm/inheritance/test_relationship.py b/test/orm/inheritance/test_relationship.py index a436ca5fc4..5e047cfdb1 100644 --- a/test/orm/inheritance/test_relationship.py +++ b/test/orm/inheritance/test_relationship.py @@ -154,6 +154,7 @@ class SelfReferentialJ2JTest(fixtures.MappedTest): managers.c.person_id == engineers.c.reports_to_id, backref='engineers')}) + def test_has(self): m1 = Manager(name='dogbert') e1 = Engineer(name='dilbert', primary_language='java', reports_to=m1) diff --git a/test/orm/test_joins.py b/test/orm/test_joins.py index 21b82f4087..5f48b39b14 100644 --- a/test/orm/test_joins.py +++ b/test/orm/test_joins.py @@ -333,7 +333,32 @@ class InheritedJoinTest(fixtures.MappedTest, AssertsCompiledSQL): , 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): @@ -1582,12 +1607,14 @@ class MultiplePathTest(fixtures.MappedTest, 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)