summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2013-06-15 15:20:48 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2013-06-15 15:20:48 -0400
commitb92007da7d9cd7453a2da9f1767e8a988f05acb7 (patch)
tree8995ad4e01d445092982bd1881f408c5ba5c4fb5
parent9d3e2206363d819ad334d7893c700b861fdc5b57 (diff)
downloadsqlalchemy-b92007da7d9cd7453a2da9f1767e8a988f05acb7.tar.gz
Fixed bug in polymorphic SQL generation where multiple joined-inheritance
entities against the same base class joined to each other as well would not track columns on the base table independently of each other if the string of joins were more than two entities long. Also in 0.8.2. [ticket:2759]
-rw-r--r--doc/build/changelog/changelog_08.rst9
-rw-r--r--doc/build/changelog/changelog_09.rst9
-rw-r--r--lib/sqlalchemy/orm/query.py15
-rw-r--r--test/orm/inheritance/test_relationship.py92
4 files changed, 119 insertions, 6 deletions
diff --git a/doc/build/changelog/changelog_08.rst b/doc/build/changelog/changelog_08.rst
index 8e81ff1b9..ffb2a24d4 100644
--- a/doc/build/changelog/changelog_08.rst
+++ b/doc/build/changelog/changelog_08.rst
@@ -7,6 +7,15 @@
:version: 0.8.2
.. change::
+ :tags: bug, orm
+ :tickets: 2759
+
+ Fixed bug in polymorphic SQL generation where multiple joined-inheritance
+ entities against the same base class joined to each other as well
+ would not track columns on the base table independently of each other if
+ the string of joins were more than two entities long.
+
+ .. change::
:tags: bug, engine
:pullreq: 6
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst
index 1ec648474..288642817 100644
--- a/doc/build/changelog/changelog_09.rst
+++ b/doc/build/changelog/changelog_09.rst
@@ -7,6 +7,15 @@
:version: 0.9.0
.. change::
+ :tags: bug, orm
+ :tickets: 2759
+
+ Fixed bug in polymorphic SQL generation where multiple joined-inheritance
+ entities against the same base class joined to each other as well
+ would not track columns on the base table independently of each other if
+ the string of joins were more than two entities long. Also in 0.8.2.
+
+ .. change::
:tags: bug, engine
:pullreq: 6
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index 54f5d7393..bd3ee8f72 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -203,17 +203,22 @@ class Query(object):
self._polymorphic_adapters.pop(m.local_table, None)
def _adapt_polymorphic_element(self, element):
+ if "parententity" in element._annotations:
+ search = element._annotations['parententity']
+ alias = self._polymorphic_adapters.get(search, None)
+ if alias:
+ return alias.adapt_clause(element)
+
if isinstance(element, expression.FromClause):
search = element
elif hasattr(element, 'table'):
search = element.table
else:
- search = None
+ return None
- if search is not None:
- alias = self._polymorphic_adapters.get(search, None)
- if alias:
- return alias.adapt_clause(element)
+ alias = self._polymorphic_adapters.get(search, None)
+ if alias:
+ return alias.adapt_clause(element)
def _adapt_col_list(self, cols):
return [
diff --git a/test/orm/inheritance/test_relationship.py b/test/orm/inheritance/test_relationship.py
index 20c6de284..ecb4bf407 100644
--- a/test/orm/inheritance/test_relationship.py
+++ b/test/orm/inheritance/test_relationship.py
@@ -8,7 +8,7 @@ from sqlalchemy.engine import default
from sqlalchemy.testing import AssertsCompiledSQL, fixtures
from sqlalchemy import testing
from sqlalchemy.testing.schema import Table, Column
-from sqlalchemy.testing import assert_raises, eq_
+from sqlalchemy.testing import assert_raises, eq_, is_
class Company(fixtures.ComparableEntity):
pass
@@ -1354,3 +1354,93 @@ class SubClassToSubClassMultiTest(AssertsCompiledSQL, fixtures.MappedTest):
"JOIN ep2 ON anon_1.base2_id = ep2.base2_id"
)
+class MultipleAdaptUsesEntityOverTableTest(AssertsCompiledSQL, fixtures.MappedTest):
+ __dialect__ = 'default'
+ run_create_tables = None
+
+ @classmethod
+ def define_tables(cls, metadata):
+ Table('a', metadata,
+ Column('id', Integer, primary_key=True),
+ Column('name', String)
+ )
+ Table('b', metadata,
+ Column('id', Integer, ForeignKey('a.id'), primary_key=True)
+ )
+ Table('c', metadata,
+ Column('id', Integer, ForeignKey('a.id'), primary_key=True),
+ Column('bid', Integer, ForeignKey('b.id'))
+ )
+ Table('d', metadata,
+ Column('id', Integer, ForeignKey('a.id'), primary_key=True),
+ Column('cid', Integer, ForeignKey('c.id'))
+ )
+
+ @classmethod
+ def setup_classes(cls):
+ class A(cls.Comparable):
+ pass
+ class B(A):
+ pass
+ class C(A):
+ pass
+ class D(A):
+ pass
+
+ @classmethod
+ def setup_mappers(cls):
+ A, B, C, D = cls.classes.A, cls.classes.B, cls.classes.C, cls.classes.D
+ a, b, c, d = cls.tables.a, cls.tables.b, cls.tables.c, cls.tables.d
+ mapper(A, a)
+ mapper(B, b, inherits=A)
+ mapper(C, c, inherits=A)
+ mapper(D, d, inherits=A)
+
+ def _two_join_fixture(self):
+ A, B, C, D = self.classes.A, self.classes.B, self.classes.C, self.classes.D
+ s = Session()
+ return s.query(B.name, C.name, D.name).select_from(B).\
+ join(C, C.bid == B.id).\
+ join(D, D.cid == C.id)
+
+ def test_two_joins_adaption(self):
+ a, b, c, d = self.tables.a, self.tables.b, self.tables.c, self.tables.d
+ q = self._two_join_fixture()
+
+ btoc = q._from_obj[0].left
+
+ ac_adapted = btoc.right.element.left
+ c_adapted = btoc.right.element.right
+
+ is_(ac_adapted.element, a)
+ is_(c_adapted.element, c)
+
+ ctod = q._from_obj[0].right
+ ad_adapted = ctod.left
+ d_adapted = ctod.right
+ is_(ad_adapted.element, a)
+ is_(d_adapted.element, d)
+
+ bname, cname, dname = q._entities
+
+ b_name_adapted = bname._resolve_expr_against_query_aliases(
+ q, bname.column, None)
+ c_name_adapted = cname._resolve_expr_against_query_aliases(
+ q, cname.column, None)
+ d_name_adapted = dname._resolve_expr_against_query_aliases(
+ q, dname.column, None)
+
+ assert bool(b_name_adapted == a.c.name)
+ assert bool(c_name_adapted == ac_adapted.c.name)
+ assert bool(d_name_adapted == ad_adapted.c.name)
+
+ def test_two_joins_sql(self):
+ q = self._two_join_fixture()
+ self.assert_compile(q,
+ "SELECT a.name AS a_name, a_1.name AS a_1_name, "
+ "a_2.name AS a_2_name "
+ "FROM a JOIN b ON a.id = b.id JOIN "
+ "(a AS a_1 JOIN c AS c_1 ON a_1.id = c_1.id) ON c_1.bid = b.id "
+ "JOIN (a AS a_2 JOIN d AS d_1 ON a_2.id = d_1.id) "
+ "ON d_1.cid = c_1.id"
+ ) \ No newline at end of file