- attached test14.py
subqueryload_all() generates invalid query: a column is referenced from a table that is not joined
Issue #2014
resolved
See the attached test case. Tested with SQLA 0.6.5 and the current rel_0_6 branch. Removing the subqueryload_all() makes things work.
Comments (6)
-
Account Deleted -
repo owner - changed milestone to 0.6.6
some notes on this.
simplest form of the issue:
#! /usr/bin/env python from sqlalchemy import (create_engine, Table, Column, Integer, ForeignKey) from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import (Session, relation, subqueryload_all) engine = create_engine('sqlite://', echo=True) session = Session(engine) Base = declarative_base() class C(Base): __tablename__ = "c" id = Column(Integer, primary_key=True) class BSuper(Base): __tablename__ = "bsuper" id = Column(Integer, primary_key=True) c_id = Column(Integer, ForeignKey("c.id"), nullable=False) c = relation(C) class BSub(BSuper): __tablename__ = "bsub" id = Column(Integer, ForeignKey('bsuper.id'), primary_key=True) a_id = Column(Integer, ForeignKey('a.id'), nullable=False) class A(Base): __tablename__ = "a" id = Column(Integer, primary_key=True) bs = relation(BSub) Base.metadata.create_all(engine) a = A(bs=[BSub(c=C())](BSub(c=C()))) session.add(a) session.commit() q = session.query(A).options(subqueryload_all("bs.c")).all()
then, the actual mis-representation of the join from within strategies.py is due to the failure of this statement:
a1 = aliased(A) b1 = aliased(BSuper) session.query(a1).join((b1, a1.bs)).join(b1.c).all()
which renders:
SELECT a_1.id AS a_1_id FROM a AS a_1 JOIN bsuper AS bsuper_1 ON a_1.id = bsub.a_id JOIN c ON c.id = bsuper_1.c_id
so this is a conflict between the philosophy of query.join() with aliased() constructs, which is to not mess with the target, and the way subqueryload is using query.join().
-
repo owner here's the patch if you're impatient:
diff -r e46301b5154000148155ef00d15b35857ebb31ad lib/sqlalchemy/orm/strategies.py --- a/lib/sqlalchemy/orm/strategies.py Tue Jan 04 13:48:46 2011 -0500 +++ b/lib/sqlalchemy/orm/strategies.py Thu Jan 06 12:03:50 2011 -0500 @@ -746,9 +746,23 @@ for i in xrange(0, len(subq_path), 2) ] + # determine the immediate parent class we are joining from, + # which needs to be aliased. + if len(to_join) < 2: + # in the case of a one level eager load, this is the + # leftmost "left_alias". parent_alias = left_alias + elif subq_path[-2](-2).isa(self.parent): + # In the case of multiple levels, retrieve + # it from subq_path[-2](-2). This is the same as self.parent + # in the vast majority of cases, and #2014 + # illustrates a case where sub_path[-2](-2) is a subclass + # of self.parent + parent_alias = mapperutil.AliasedClass(subq_path[-2](-2)) else: + # if of_type() were used leading to this relationship, + # self.parent is more specific than subq_path[-2](-2) parent_alias = mapperutil.AliasedClass(self.parent) local_cols, remote_cols = \
-
repo owner - changed status to resolved
-
Account Deleted The patch works, thanks for the quick response.
-
repo owner - removed milestone
Removing milestone: 0.6.6 (automated comment)
- Log in to comment
Test case