Skip to content

unable to use load_only together with contains_eager and aliased polymorphic entities #10125

Closed
@torsdag

Description

@torsdag

Describe the bug

I am having issues loading select columns when using relying on contains_eager / joinedload when using aliased Polymorphic classes .

In the example below I am attempting to only load the id column from the user relationship but it appears that it will always load the do_not_load column from the parent class Resource.

When not using aliases the query works as expected.

Optional link from https://docs.sqlalchemy.org which documents the behavior that is expected

No response

SQLAlchemy Version in Use

1.4.49, 2.0.19

DBAPI (i.e. the database driver)

pysqlite, mysqlclient

Database Vendor and Major Version

SQLite, MariaDB

Python Version

3.11

Operating system

Linux

To Reproduce

import warnings
import sqlalchemy
import sqlalchemy.orm

Base = sqlalchemy.orm.declarative_base()


class Resource(Base):
    __tablename__ = 'resource'

    id = sqlalchemy.Column(
        sqlalchemy.Integer, primary_key=True
    )
    _type = sqlalchemy.Column(
        sqlalchemy.String(50)
    )

    do_not_load = sqlalchemy.Column(
        sqlalchemy.Integer
    )
    __mapper_args__ = {
        'polymorphic_on': '_type',
        'polymorphic_identity': 'resource'
    }


class User(Resource):
    __tablename__ = 'user'

    id = sqlalchemy.Column(
        sqlalchemy.Integer, sqlalchemy.ForeignKey(Resource.id), primary_key=True
    )

    __mapper_args__ = {
        'polymorphic_identity': 'user'
    }


class Manager(Base):
    __tablename__ = 'manager'

    id = sqlalchemy.Column(
        sqlalchemy.Integer, primary_key=True
    )

    parent_id = sqlalchemy.Column(
        sqlalchemy.Integer, sqlalchemy.ForeignKey(User.id)
    )

    user = sqlalchemy.orm.relationship(
        User
    )


engine = sqlalchemy.create_engine(
    'sqlite:///:memory:', echo=True
)


Base.metadata.create_all(engine)

Session = sqlalchemy.orm.sessionmaker(
    bind=engine
)

session = Session()

user_alias = sqlalchemy.orm.aliased(User, flat=True)
manager_alias = sqlalchemy.orm.aliased(Manager, flat=True)


q1 = session.query(
    Manager
).join(
    User, Manager.user.of_type(user_alias)
).options(
    sqlalchemy.orm.contains_eager(
        Manager.user.of_type(User)
    ).load_only(
        User.id,
    )
)


q2 = session.query(
    manager_alias
).outerjoin(
    user_alias, manager_alias.user.of_type(user_alias)
).options(
    sqlalchemy.orm.contains_eager(
        manager_alias.user.of_type(user_alias)
    ).load_only(
        user_alias.id,
    )
)


q3 = session.query(
    manager_alias
).options(
    sqlalchemy.orm.joinedload(
        manager_alias.user.of_type(user_alias)
    ).load_only(
        user_alias.id,
    )
)

for label, query in (
    ('without_aliases', q1),
    ('with_aliases_contains_eager', q2),
    ('with_aliases_joinedload', q3)
):
    if str(query).count('do_not_load'):
        print('Warning: {0} contains do_not_load: {1}'.format(label, query))

Error

Warning: with_aliases_eager_load contains do_not_load: SELECT user_1.id AS user_1_id, resource_1.id AS resource_1_id, resource_1._type AS resource_1__type, resource_1.do_not_load AS resource_1_do_not_load, manager_1.id AS manager_1_id, manager_1.parent_id AS manager_1_parent_id 
FROM manager AS manager_1 LEFT OUTER JOIN (resource AS resource_1 JOIN user AS user_1 ON resource_1.id = user_1.id) ON user_1.id = manager_1.parent_id
Warning: with_aliases_joinedload contains do_not_load: SELECT manager_1.id AS manager_1_id, manager_1.parent_id AS manager_1_parent_id, user_1.id AS user_1_id, resource_1.id AS resource_1_id, resource_1._type AS resource_1__type, resource_1.do_not_load AS resource_1_do_not_load 
FROM manager AS manager_1 LEFT OUTER JOIN (resource AS resource_1 JOIN user AS user_1 ON resource_1.id = user_1.id) ON user_1.id = manager_1.parent_id

Additional context

No response

Activity

zzzeek

zzzeek commented on Jul 20, 2023

@zzzeek
Member

hi -

I see you are having multiple, extremely deep issues with this join. unfortunately I am going on vacation for a week starting tomorrow and your issue at #10124 is already kind of an "all day" issue unless I have a quick breakthrough. this looks like a different issue entirely but aliasing/contains_eager are again "all day" kinds of issues and I likely dont have the time to fix these today.

added
bugSomething isn't working
hard ormhard ORM bugs mike needs to look at
and removed
requires triageNew issue that requires categorization
on Jul 20, 2023
torsdag

torsdag commented on Jul 20, 2023

@torsdag
Author

Hey,

thank you for taking a look. I hope you will have a good vacation without any kind of polymorphism!

I think I can workaround this issue for now by using defer. But obviously it would be great to avoid that

ie

user_alias = sqlalchemy.orm.aliased(User, flat=True)
manager_alias = sqlalchemy.orm.aliased(Manager, flat=True)

q4 = session.query(
    manager_alias
).options(
    sqlalchemy.orm.joinedload(
        manager_alias.user.of_type(user_alias)
    ).load_only(
        user_alias.id,
    ).defer(
        user_alias.do_not_load
    )
)
added
inheritanceissues to do wtih ORM inheritance, a particularly tricky area
on Jul 20, 2023
added this to the 2.0.x milestone on Jul 20, 2023
zzzeek

zzzeek commented on Jul 20, 2023

@zzzeek
Member

if defer() works, that would be a very good sign that this bug might be not quite as hard to fix

added
near-term releaseaddition to the milestone which indicates this should be in a near-term release
on Jul 20, 2023
torsdag

torsdag commented on Jul 20, 2023

@torsdag
Author

will this make it into 1.4 as well?

zzzeek

zzzeek commented on Jul 20, 2023

@zzzeek
Member

1.4 backport would be unlikely here, if the fix is very trivial then it's a maybe, but then again the entire options logic was rewritten for 2.0. issue reproduces in both 1.4 and 2.0 ?

torsdag

torsdag commented on Jul 20, 2023

@torsdag
Author

yeah the issue is present in both 1.4 and 2.0. Would be amazing if it could be backported but understand it may not be possible ( #10124 is also present in both )

cheers

sqla-tester

sqla-tester commented on Jul 20, 2023

@sqla-tester
Collaborator

Mike Bayer has proposed a fix for this issue in the main branch:

WIP: have token load flatten aliases unconditionally https://gerrit.sqlalchemy.org/c/sqlalchemy/sqlalchemy/+/4776

zzzeek

zzzeek commented on Jul 20, 2023

@zzzeek
Member

this one is hard and the architecture involved is totally new in 2.0 so this one would not be a backport unfortunately

3 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghard ormhard ORM bugs mike needs to look atinheritanceissues to do wtih ORM inheritance, a particularly tricky areanear-term releaseaddition to the milestone which indicates this should be in a near-term release

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @zzzeek@torsdag@sqla-tester

        Issue actions

          unable to use load_only together with contains_eager and aliased polymorphic entities · Issue #10125 · sqlalchemy/sqlalchemy