SQLAlchemy: print the actual query SQLAlchemy: print the actual query python python

SQLAlchemy: print the actual query


In the vast majority of cases, the "stringification" of a SQLAlchemy statement or query is as simple as:

print(str(statement))

This applies both to an ORM Query as well as any select() or other statement.

Note: the following detailed answer is being maintained on the sqlalchemy documentation.

To get the statement as compiled to a specific dialect or engine, if the statement itself is not already bound to one you can pass this in to compile():

print(statement.compile(someengine))

or without an engine:

from sqlalchemy.dialects import postgresqlprint(statement.compile(dialect=postgresql.dialect()))

When given an ORM Query object, in order to get at the compile() method we only need access the .statement accessor first:

statement = query.statementprint(statement.compile(someengine))

with regards to the original stipulation that bound parameters are to be "inlined" into the final string, the challenge here is that SQLAlchemy normally is not tasked with this, as this is handled appropriately by the Python DBAPI, not to mention bypassing bound parameters is probably the most widely exploited security holes in modern web applications. SQLAlchemy has limited ability to do this stringification in certain circumstances such as that of emitting DDL. In order to access this functionality one can use the 'literal_binds' flag, passed to compile_kwargs:

from sqlalchemy.sql import table, column, selectt = table('t', column('x'))s = select([t]).where(t.c.x == 5)print(s.compile(compile_kwargs={"literal_binds": True}))

the above approach has the caveats that it is only supported for basictypes, such as ints and strings, and furthermore if a bindparamwithout a pre-set value is used directly, it won't be able tostringify that either.

To support inline literal rendering for types not supported, implementa TypeDecorator for the target type which includes aTypeDecorator.process_literal_param method:

from sqlalchemy import TypeDecorator, Integerclass MyFancyType(TypeDecorator):    impl = Integer    def process_literal_param(self, value, dialect):        return "my_fancy_formatting(%s)" % valuefrom sqlalchemy import Table, Column, MetaDatatab = Table('mytable', MetaData(), Column('x', MyFancyType()))print(    tab.select().where(tab.c.x > 5).compile(        compile_kwargs={"literal_binds": True}))

producing output like:

SELECT mytable.xFROM mytableWHERE mytable.x > my_fancy_formatting(5)


Given that what you want makes sense only when debugging, you could start SQLAlchemy with echo=True, to log all SQL queries. For example:

engine = create_engine(    "mysql://scott:tiger@hostname/dbname",    encoding="latin1",    echo=True,)

This can also be modified for just a single request:

echo=False – if True, the Engine will log all statements as well as a repr() of their parameter lists to the engines logger, which defaults to sys.stdout. The echo attribute of Engine can be modified at any time to turn logging on and off. If set to the string "debug", result rows will be printed to the standard output as well. This flag ultimately controls a Python logger; see Configuring Logging for information on how to configure logging directly.

Source: SQLAlchemy Engine Configuration

If used with Flask, you can simply set

app.config["SQLALCHEMY_ECHO"] = True

to get the same behaviour.


This works in python 2 and 3 and is a bit cleaner than before, but requires SA>=1.0.

from sqlalchemy.engine.default import DefaultDialectfrom sqlalchemy.sql.sqltypes import String, DateTime, NullType# python2/3 compatible.PY3 = str is not bytestext = str if PY3 else unicodeint_type = int if PY3 else (int, long)str_type = str if PY3 else (str, unicode)class StringLiteral(String):    """Teach SA how to literalize various things."""    def literal_processor(self, dialect):        super_processor = super(StringLiteral, self).literal_processor(dialect)        def process(value):            if isinstance(value, int_type):                return text(value)            if not isinstance(value, str_type):                value = text(value)            result = super_processor(value)            if isinstance(result, bytes):                result = result.decode(dialect.encoding)            return result        return processclass LiteralDialect(DefaultDialect):    colspecs = {        # prevent various encoding explosions        String: StringLiteral,        # teach SA about how to literalize a datetime        DateTime: StringLiteral,        # don't format py2 long integers to NULL        NullType: StringLiteral,    }def literalquery(statement):    """NOTE: This is entirely insecure. DO NOT execute the resulting strings."""    import sqlalchemy.orm    if isinstance(statement, sqlalchemy.orm.Query):        statement = statement.statement    return statement.compile(        dialect=LiteralDialect(),        compile_kwargs={'literal_binds': True},    ).string

Demo:

# coding: UTF-8from datetime import datetimefrom decimal import Decimalfrom literalquery import literalquerydef test():    from sqlalchemy.sql import table, column, select    mytable = table('mytable', column('mycol'))    values = (        5,        u'snowman: ☃',        b'UTF-8 snowman: \xe2\x98\x83',        datetime.now(),        Decimal('3.14159'),        10 ** 20,  # a long integer    )    statement = select([mytable]).where(mytable.c.mycol.in_(values)).limit(1)    print(literalquery(statement))if __name__ == '__main__':    test()

Gives this output: (tested in python 2.7 and 3.4)

SELECT mytable.mycolFROM mytableWHERE mytable.mycol IN (5, 'snowman: ☃', 'UTF-8 snowman: ☃',      '2015-06-24 18:09:29.042517', 3.14159, 100000000000000000000) LIMIT 1