subquery eager load from of_type() to plain mapper chains joins incorrectly

Issue #3773 resolved
Mike Bayer repo owner created an issue

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)

  1. Mike Bayer 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

    → <<cset b07eb3cb45d1>>

  2. Mike Bayer 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>>

  3. Log in to comment