subquery eager load from of_type() to plain mapper chains joins incorrectly
The SQL at the end gives us a "FROM <join of things>, <join of things>".
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import (relationship, sessionmaker, subqueryload,
with_polymorphic, joinedload)
from sqlalchemy import create_engine, Column, String, Integer
from sqlalchemy.schema import ForeignKey
Base = declarative_base()
class B(Base):
__tablename__ = 't_b'
id = Column(Integer, primary_key=True)
class C(Base):
__tablename__ = 't_c'
type = Column(String(2))
__mapper_args__ = {
'polymorphic_identity': 'c',
'polymorphic_on': type
}
id = Column(Integer, primary_key=True)
# Relationship to B
b_id = Column(Integer, ForeignKey('t_b.id'))
b = relationship('B', backref='cs')
class C2(C):
__tablename__ = 't_c2'
__mapper_args__ = {
'polymorphic_identity': 'c2',
}
id = Column(Integer, ForeignKey('t_c.id'), primary_key=True)
class D(Base):
__tablename__ = 't_d'
id = Column(Integer, primary_key=True)
# Relationship to B
c_id = Column(Integer, ForeignKey('t_c.id'))
c = relationship('C', backref='ds')
engine = create_engine('sqlite://', echo=True)
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
for i in xrange(2):
b = B()
session.add(b)
c = C2(b=b)
session.add(c)
d = D(c=c)
session.add(d)
session.commit()
c_c2 = with_polymorphic(C, [C2], flat=True)
r = session.query(
B
).options(
subqueryload(
B.cs.of_type(c_c2)
).subqueryload(
c_c2.ds# .of_type(D)
)
).all()
the patch is:
diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py
index 8260732..1d0058c 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -968,7 +968,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
if last and effective_entity is not self.mapper:
attr = attr.of_type(effective_entity)
else:
- if last and effective_entity is not self.mapper:
+ if last:
attr = getattr(parent_alias, key).\
of_type(effective_entity)
else:
the code with effective_entity was introduced in 3dd536ac06808adcf9c10707dbf2ebb6e3842be7, the test for it is test_of_type -> SubclassRelationshipTest.test_twolevel_subqueryload_wsubclass introduced in 4a4afca9595c9b4b10e6557c6ee819ae386c477a.
Comments (5)
-
reporter -
reporter - changed status to resolved
Ensure final link in subqueryload join is correct
Fixed bug in subquery eager loading where a subqueryload of an "of_type()" object linked to a second subqueryload of a plain mapped class would fail to link the joins correctly.
Change-Id: I4be89e6f5e492438464a2ded01eb9c84d7ff7d4e Fixes:
#3773→ <<cset b07eb3cb45d1>>
-
reporter Ensure final link in subqueryload join is correct
Fixed bug in subquery eager loading where a subqueryload of an "of_type()" object linked to a second subqueryload of a plain mapped class would fail to link the joins correctly.
Change-Id: I4be89e6f5e492438464a2ded01eb9c84d7ff7d4e Fixes:
#3773(cherry picked from commit b07eb3cb45d1a344759a2eee9d2166fbf3e44888)→ <<cset 1f956ed7b3b6>>
-
Hi Mike, I think there's still another bug lurking in subqueryload. Expanding the B/C/C2/D use case here to also include A and B2 generates another inappropriate cross join, this time between A-B and B-C-D. Here is the updated reproduction script:
https://gist.github.com/dpwrussell/508e8b33fe92434eafe001ee806f355a
-
reporter - Log in to comment
https://gerrit.sqlalchemy.org/#/q/I4be89e6f5e492438464a2ded01eb9c84d7ff7d4e