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 [email protected].
> To unsubscribe from this group, send email to
> [email protected].
> 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 [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/sqlalchemy?hl=en.