summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-01-02 18:26:32 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2014-01-02 18:26:32 -0500
commitca8fca63916897f1bbc2fa4f1ee440c6b5d9a88a (patch)
treefaedf154f7574d302204e0c556a463e9d01bf451
parent504543090213db5b9c7cecb27a78b12fa12e9024 (diff)
downloadsqlalchemy-ca8fca63916897f1bbc2fa4f1ee440c6b5d9a88a.tar.gz
- 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]
-rw-r--r--doc/build/changelog/changelog_09.rst15
-rw-r--r--lib/sqlalchemy/orm/query.py24
-rw-r--r--test/orm/inheritance/test_relationship.py1
-rw-r--r--test/orm/test_joins.py35
4 files changed, 67 insertions, 8 deletions
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst
index 65da0a39f..069df37a4 100644
--- a/doc/build/changelog/changelog_09.rst
+++ b/doc/build/changelog/changelog_09.rst
@@ -12,6 +12,21 @@
: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 4f10a6ada..3815f5a98 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 a436ca5fc..5e047cfdb 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 21b82f408..5f48b39b1 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)