the issue is quite simple as can be seen in this demonstration: from sqlalchemy import *
# two columns. Nothing up my sleeve ! c1 = Column('c1', Integer) c2 = Column('c2', Integer) # put one of them into a Select. # generate _from_objects collection of c1 too early s = select([c1]) t = Table('t', MetaData(), c1, c2) # c1 has the wrong _from_objects assert c1._from_objects == [] # c2 has the correct one assert c2._from_objects == [t] # see it here assert str(select([c1])) == "SELECT t.c1" assert str(select([c2])) == "SELECT t.c2 \nFROM t" what to do about it, unsure. Removing the caching from _from_objects is the immediate fix. However, the original select() is still wrong. _from_objects gave it the wrong data, period. How to guard against this issue, a potentially expensive reorg of Select internals. As well as future issues of this sort, using Column objects which generates cached state, then mutating the Column. On Aug 18, 2011, at 3:12 PM, brianhawthorne wrote: > Hello, > I have found a case where instantiating a select statement during the > declaration of the parent class in a one-to-many pair can cause a > downstream failure to generate well formed sql when performing a query > with a subqueryload option. I've boiled down a minimal example > (pasted below) which will produce the bug. > > Note that the select must be called *during* the declaration (eg when > defining a custom column property) of the parent class and must refer > to a column of that class. Removing the id reference or moving the > select anywhere outside the scope of the parent class declaration will > make the bug disappear. > > Cheers, > Brian Hawthorne > Amyris, Inc. > > > #---------------------------------------------------------------------------------- > from sqlalchemy import * > from sqlalchemy.orm import * > from sqlalchemy.ext.declarative import declarative_base > > Base = declarative_base() > > class Parent(Base): > __tablename__ = 'parent' > id = Column(Integer, primary_key=True) > select([id]) > > class Child(Base): > __tablename__ = 'child' > id = Column(Integer, primary_key=True) > parent_id = Column(Integer, ForeignKey('parent.id')) > parent = relationship(Parent, backref='children') > > engine = create_engine('sqlite://', echo=True) > Base.metadata.create_all(engine) > session = sessionmaker(engine)() > session.add(Parent()) > session.commit() > > # Malformed SQL! > session.query(Parent).options(subqueryload('children')).all() > #---------------------------------------------------------------------------------- > > Executing the above produces the following traceback: > > Traceback (most recent call last): > File "test.py", line 25, in <module> > session.query(Parent).options(subqueryload('children')).all() > File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.2-py2.7- > linux-i686.egg/sqlalchemy/orm/query.py", line 1729, in all > return list(self) > File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.2-py2.7- > linux-i686.egg/sqlalchemy/orm/query.py", line 1960, in instances > rows = [process[0](row, None) for row in fetch] > File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.2-py2.7- > linux-i686.egg/sqlalchemy/orm/mapper.py", line 2481, in _instance > eager_populators > File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.2-py2.7- > linux-i686.egg/sqlalchemy/orm/mapper.py", line 2664, in _populators > self, row, adapter)): > File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.2-py2.7- > linux-i686.egg/sqlalchemy/orm/interfaces.py", line 326, in > create_row_processor > reduced_path, mapper, row, adapter) > File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.2-py2.7- > linux-i686.egg/sqlalchemy/orm/strategies.py", line 890, in > create_row_processor > lambda x:x[1:] > File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.2-py2.7- > linux-i686.egg/sqlalchemy/orm/query.py", line 1839, in __iter__ > return self._execute_and_instances(context) > File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.2-py2.7- > linux-i686.egg/sqlalchemy/orm/query.py", line 1854, in > _execute_and_instances > result = conn.execute(querycontext.statement, self._params) > File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.2-py2.7- > linux-i686.egg/sqlalchemy/engine/base.py", line 1399, in execute > params) > File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.2-py2.7- > linux-i686.egg/sqlalchemy/engine/base.py", line 1532, in > _execute_clauseelement > compiled_sql, distilled_params > File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.2-py2.7- > linux-i686.egg/sqlalchemy/engine/base.py", line 1640, in > _execute_context > context) > File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.2-py2.7- > linux-i686.egg/sqlalchemy/engine/base.py", line 1633, in > _execute_context > context) > File "/usr/local/lib/python2.7/dist-packages/SQLAlchemy-0.7.2-py2.7- > linux-i686.egg/sqlalchemy/engine/default.py", line 325, in do_execute > cursor.execute(statement, parameters) > sqlalchemy.exc.OperationalError: (OperationalError) no such column: > parent.id u'SELECT child.id AS child_id, child.parent_id AS > child_parent_id, anon_1.parent_id AS anon_1_parent_id \nFROM (SELECT > parent.id AS parent_id) AS anon_1 JOIN child ON parent.id = > child.parent_id ORDER BY anon_1.parent_id' () > > -- > You received this message because you are subscribed to the Google Groups > "sqlalchemy" group. > To post to this group, send email to sqlalchemy@googlegroups.com. > To unsubscribe from this group, send email to > sqlalchemy+unsubscr...@googlegroups.com. > For more options, visit this group at > http://groups.google.com/group/sqlalchemy?hl=en. > -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To post to this group, send email to sqlalchemy@googlegroups.com. To unsubscribe from this group, send email to sqlalchemy+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/sqlalchemy?hl=en.