when you send this construct into the compiler, you’re wrapping around heavy 
constructs like an UPDATE which doesn’t expect to be nested like that.   So it 
sets up flags like .isupdate (which affect how the construct is treated during 
execution, and also within compilation ) that cause it to treat self.statement 
like an update().  So you’d want to proxy all of its state:

class Explain(HasPrefixes, Executable, ClauseElement):
    _prefixes = ()

    def __init__(self, stmt, prefixes=None):
        self.stmt = stmt
        if prefixes:
            self._setup_prefixes(prefixes)

    def __getattr__(self, key):
        return getattr(self.stmt, key)

@compiles(Explain)
def visit_explain(explain, compiler, **kw):
    text = "EXPLAIN "
    if explain._prefixes:
        text += compiler._generate_prefixes(explain, explain._prefixes, **kw)
    text += compiler.process(explain.stmt)
    return text


works on this end without changing the flags (but that could vary by dialect):

 EXPLAIN QUERY PLAN UPDATE t SET c=?
2014-02-26 19:34:49,700 INFO sqlalchemy.engine.base.Engine ('something',)
[{u'detail': u'SCAN TABLE t (~1000000 rows)', u'from': 0, u'order': 0, 
u'selectid': 0}]




On Feb 26, 2014, at 7:29 PM, polkapiquee <giles_br...@hotmail.com> wrote:

> I have a little code using sqlalchemy.ext.compiler'
> 
> from sqlalchemy import Table, Column
> from sqlalchemy.ext.compiler import compiles
> from sqlalchemy.sql.expression import HasPrefixes
> from sqlalchemy.sql.expression import Executable
> from sqlalchemy.sql.expression import ClauseElement
> 
> 
> #
> # EXPLAIN
> #
> 
> class Explain(HasPrefixes, Executable, ClauseElement):
>     _prefixes = ()
> 
>     def __init__(self, stmt, prefixes=None):
>         self.stmt = stmt
>         if prefixes:
>             self._setup_prefixes(prefixes)
> 
> 
> @compiles(Explain)
> def visit_explain(explain, compiler, **kw):
>     text = "EXPLAIN "
>     if explain._prefixes:
>         text += compiler._generate_prefixes(explain, explain._prefixes, **kw)
>     text += compiler.process(explain.stmt)
>     # Can't remember why I needed these >:O
>     compiler.isinsert = False
>     compiler.isupdate = False
>     compiler.isdelete = False
>     return text
> 
> 
> def explain(stmt, **kw):
>     return Explain(stmt, **kw)
> 
> 
> from sqlalchemy import MetaData, String, create_engine
> 
> metadata = MetaData()
> table = Table('t', metadata, Column('c', String))
> engine = create_engine('sqlite://')
> table.create(engine)
> update = table.update().values(c='something')
> print [dict(r) for r in engine.execute(explain(update, prefixes=['QUERY 
> PLAN']))]
> 
> This runs ok under 0.9.1, but if I upgrade to 0.9.3 I get a traceback 
> complaining about no '_extra_froms'.
> 
> (wexdev)gsbrown@gsbrowndesktop:~/Sandbox/sa_explain$ python explain.py 
> Traceback (most recent call last):
>   File "explain.py", line 44, in <module>
>     print [dict(r) for r in engine.execute(explain(update, prefixes=['QUERY 
> PLAN']))]
>   File 
> "/home/gsbrown/Envs/wexdev/local/lib/python2.7/site-packages/SQLAlchemy-0.9.3-py2.7-linux-x86_64.egg/sqlalchemy/engine/base.py",
>  line 1651, in execute
>     return connection.execute(statement, *multiparams, **params)
>   File 
> "/home/gsbrown/Envs/wexdev/local/lib/python2.7/site-packages/SQLAlchemy-0.9.3-py2.7-linux-x86_64.egg/sqlalchemy/engine/base.py",
>  line 717, in execute
>     return meth(self, multiparams, params)
>   File 
> "/home/gsbrown/Envs/wexdev/local/lib/python2.7/site-packages/SQLAlchemy-0.9.3-py2.7-linux-x86_64.egg/sqlalchemy/sql/elements.py",
>  line 317, in _execute_on_connection
>     return connection._execute_clauseelement(self, multiparams, params)
>   File 
> "/home/gsbrown/Envs/wexdev/local/lib/python2.7/site-packages/SQLAlchemy-0.9.3-py2.7-linux-x86_64.egg/sqlalchemy/engine/base.py",
>  line 807, in _execute_clauseelement
>     inline=len(distilled_params) > 1)
>   File "<string>", line 1, in <lambda>
>   File 
> "/home/gsbrown/Envs/wexdev/local/lib/python2.7/site-packages/SQLAlchemy-0.9.3-py2.7-linux-x86_64.egg/sqlalchemy/sql/elements.py",
>  line 468, in compile
>     return self._compiler(dialect, bind=bind, **kw)
>   File 
> "/home/gsbrown/Envs/wexdev/local/lib/python2.7/site-packages/SQLAlchemy-0.9.3-py2.7-linux-x86_64.egg/sqlalchemy/sql/elements.py",
>  line 474, in _compiler
>     return dialect.statement_compiler(dialect, self, **kw)
>   File 
> "/home/gsbrown/Envs/wexdev/local/lib/python2.7/site-packages/SQLAlchemy-0.9.3-py2.7-linux-x86_64.egg/sqlalchemy/sql/compiler.py",
>  line 391, in __init__
>     Compiled.__init__(self, dialect, statement, **kwargs)
>   File 
> "/home/gsbrown/Envs/wexdev/local/lib/python2.7/site-packages/SQLAlchemy-0.9.3-py2.7-linux-x86_64.egg/sqlalchemy/sql/compiler.py",
>  line 197, in __init__
>     self.string = self.process(self.statement, **compile_kwargs)
>   File 
> "/home/gsbrown/Envs/wexdev/local/lib/python2.7/site-packages/SQLAlchemy-0.9.3-py2.7-linux-x86_64.egg/sqlalchemy/sql/compiler.py",
>  line 220, in process
>     return obj._compiler_dispatch(self, **kwargs)
>   File 
> "/home/gsbrown/Envs/wexdev/local/lib/python2.7/site-packages/SQLAlchemy-0.9.3-py2.7-linux-x86_64.egg/sqlalchemy/ext/compiler.py",
>  line 410, in <lambda>
>     lambda *arg, **kw: existing(*arg, **kw))
>   File 
> "/home/gsbrown/Envs/wexdev/local/lib/python2.7/site-packages/SQLAlchemy-0.9.3-py2.7-linux-x86_64.egg/sqlalchemy/ext/compiler.py",
>  line 448, in __call__
>     return fn(element, compiler, **kw)
>   File "explain.py", line 26, in visit_explain
>     text += compiler.process(explain.stmt)
>   File 
> "/home/gsbrown/Envs/wexdev/local/lib/python2.7/site-packages/SQLAlchemy-0.9.3-py2.7-linux-x86_64.egg/sqlalchemy/sql/compiler.py",
>  line 220, in process
>     return obj._compiler_dispatch(self, **kwargs)
>   File 
> "/home/gsbrown/Envs/wexdev/local/lib/python2.7/site-packages/SQLAlchemy-0.9.3-py2.7-linux-x86_64.egg/sqlalchemy/sql/visitors.py",
>  line 79, in _compiler_dispatch
>     return meth(self, **kw)
>   File 
> "/home/gsbrown/Envs/wexdev/local/lib/python2.7/site-packages/SQLAlchemy-0.9.3-py2.7-linux-x86_64.egg/sqlalchemy/sql/compiler.py",
>  line 1778, in visit_update
>     colparams = self._get_colparams(update_stmt, **kw)
>   File 
> "/home/gsbrown/Envs/wexdev/local/lib/python2.7/site-packages/SQLAlchemy-0.9.3-py2.7-linux-x86_64.egg/sqlalchemy/sql/compiler.py",
>  line 1913, in _get_colparams
>     self._key_getters_for_crud_column
>   File 
> "/home/gsbrown/Envs/wexdev/local/lib/python2.7/site-packages/SQLAlchemy-0.9.3-py2.7-linux-x86_64.egg/sqlalchemy/util/langhelpers.py",
>  line 689, in __get__
>     obj.__dict__[self.__name__] = result = self.fget(obj)
>   File 
> "/home/gsbrown/Envs/wexdev/local/lib/python2.7/site-packages/SQLAlchemy-0.9.3-py2.7-linux-x86_64.egg/sqlalchemy/sql/compiler.py",
>  line 1849, in _key_getters_for_crud_column
>     if self.isupdate and self.statement._extra_froms:
> AttributeError: 'Explain' object has no attribute '_extra_froms'
> 
> 
> I can fix this by making the Explain statement have an '_extra_froms' 
> attribute, but I'm not sure if that should just be empty or borrowed from the 
> wrapped statement or what.  Any suggestions on how best to keep this clean?
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to sqlalchemy+unsubscr...@googlegroups.com.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> Visit this group at http://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/groups/opt_out.

Attachment: signature.asc
Description: Message signed with OpenPGP using GPGMail

Reply via email to