Closed
Description
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
Metadata
Metadata
Assignees
Labels
Type
Projects
Milestone
Relationships
Development
No branches or pull requests
Activity
zzzeek commentedon Jul 20, 2023
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.
torsdag commentedon Jul 20, 2023
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
zzzeek commentedon Jul 20, 2023
if defer() works, that would be a very good sign that this bug might be not quite as hard to fix
torsdag commentedon Jul 20, 2023
will this make it into 1.4 as well?
zzzeek commentedon Jul 20, 2023
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 commentedon Jul 20, 2023
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 commentedon Jul 20, 2023
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 commentedon Jul 20, 2023
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