diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-08-23 15:40:09 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-08-23 15:40:09 -0400 |
commit | 28bfc3d5dfdaae57a94f660959aaaeb83a5e2132 (patch) | |
tree | 43d42adce27ce2d606e8f1fae4849b722f0d8bd3 /lib/sqlalchemy/events.py | |
parent | 16d66d35644eaa6609fdc5c8f805314f7cf4d0fc (diff) | |
download | sqlalchemy-28bfc3d5dfdaae57a94f660959aaaeb83a5e2132.tar.gz |
- [feature] The before_cursor_execute event
fires off for so-called "_cursor_execute"
events, which are usually special-case
executions of primary-key bound sequences
and default-generation SQL
phrases that invoke separately when RETURNING
is not used with INSERT. [ticket:2459]
Diffstat (limited to 'lib/sqlalchemy/events.py')
-rw-r--r-- | lib/sqlalchemy/events.py | 229 |
1 files changed, 209 insertions, 20 deletions
diff --git a/lib/sqlalchemy/events.py b/lib/sqlalchemy/events.py index 10717ee50..61392ea62 100644 --- a/lib/sqlalchemy/events.py +++ b/lib/sqlalchemy/events.py @@ -341,18 +341,56 @@ class ConnectionEvents(event.Events): The methods here define the name of an event as well as the names of members that are passed to listener functions. - An event listener can be associated with any :class:`.Connectable`, - such as an :class:`.Engine`, e.g.:: + An event listener can be associated with any :class:`.Connectable` + class or instance, such as an :class:`.Engine`, e.g.:: from sqlalchemy import event, create_engine - def before_execute(conn, clauseelement, multiparams, params): - log.info("Received statement: %s" % clauseelement) + def before_cursor_execute(conn, cursor, statement, parameters, context, + executemany): + log.info("Received statement: %s" % statement) engine = create_engine('postgresql://scott:tiger@localhost/test') - event.listen(engine, "before_execute", before_execute) + event.listen(engine, "before_cursor_execute", before_cursor_execute) - Some events allow modifiers to the :func:`.event.listen` function. + or with a specific :class:`.Connection`:: + + with engine.begin() as conn: + @event.listens_for(conn, 'before_cursor_execute') + def before_cursor_execute(conn, cursor, statement, parameters, + context, executemany): + log.info("Received statement: %s" % statement) + + The :meth:`.before_execute` and :meth:`.before_cursor_execute` + events can also be established with the ``retval=True`` flag, which + allows modification of the statement and parameters to be sent + to the database. The :meth:`.before_cursor_execute` event is + particularly useful here to add ad-hoc string transformations, such + as comments, to all executions:: + + from sqlalchemy.engine import Engine + from sqlalchemy import event + + @event.listens_for(Engine, "before_cursor_execute", retval=True) + def comment_sql_calls(conn, cursor, statement, parameters, + context, executemany): + statement = statement + " -- some comment" + return statement, parameters + + .. note:: :class:`.ConnectionEvents` can be established on any + combination of :class:`.Engine`, :class:`.Connection`, as well + as instances of each of those classes. Events across all + four scopes will fire off for a given instance of + :class:`.Connection`. However, for performance reasons, the + :class:`.Connection` object determines at instantiation time + whether or not its parent :class:`.Engine` has event listeners + established. Event listeners added to the :class:`.Engine` + class or to an instance of :class:`.Engine` *after* the instantiation + of a dependent :class:`.Connection` instance will usually + *not* be available on that :class:`.Connection` instance. The newly + added listeners will instead take effect for :class:`.Connection` + instances created subsequent to those event listeners being + established on the parent :class:`.Engine` class or instance. :param retval=False: Applies to the :meth:`.before_execute` and :meth:`.before_cursor_execute` events only. When True, the @@ -400,18 +438,107 @@ class ConnectionEvents(event.Events): event.Events._listen(target, identifier, fn) def before_execute(self, conn, clauseelement, multiparams, params): - """Intercept high level execute() events.""" + """Intercept high level execute() events, receiving uncompiled + SQL constructs and other objects prior to rendering into SQL. + + This event is good for debugging SQL compilation issues as well + as early manipulation of the parameters being sent to the database, + as the parameter lists will be in a consistent format here. + + This event can be optionally established with the ``retval=True`` + flag. The ``clauseelement``, ``multiparams``, and ``params`` + arguments should be returned as a three-tuple in this case:: + + @event.listens_for(Engine, "before_execute", retval=True) + def before_execute(conn, conn, clauseelement, multiparams, params): + # do something with clauseelement, multiparams, params + return clauseelement, multiparams, params + + :param conn: :class:`.Connection` object + :param clauseelement: SQL expression construct, :class:`.Compiled` + instance, or string statement passed to :meth:`.Connection.execute`. + :param multiparams: Multiple parameter sets, a list of dictionaries. + :param params: Single parameter set, a single dictionary. + + See also: + + :meth:`.before_cursor_execute` + + """ def after_execute(self, conn, clauseelement, multiparams, params, result): - """Intercept high level execute() events.""" + """Intercept high level execute() events after execute. + + + :param conn: :class:`.Connection` object + :param clauseelement: SQL expression construct, :class:`.Compiled` + instance, or string statement passed to :meth:`.Connection.execute`. + :param multiparams: Multiple parameter sets, a list of dictionaries. + :param params: Single parameter set, a single dictionary. + :param result: :class:`.ResultProxy` generated by the execution. + + """ def before_cursor_execute(self, conn, cursor, statement, parameters, context, executemany): - """Intercept low-level cursor execute() events.""" + """Intercept low-level cursor execute() events before execution, + receiving the string + SQL statement and DBAPI-specific parameter list to be invoked + against a cursor. + + This event is a good choice for logging as well as late modifications + to the SQL string. It's less ideal for parameter modifications except + for those which are specific to a target backend. + + This event can be optionally established with the ``retval=True`` + flag. The ``statement`` and ``parameters`` arguments should be + returned as a two-tuple in this case:: + + @event.listens_for(Engine, "before_cursor_execute", retval=True) + def before_cursor_execute(conn, cursor, statement, + parameters, context, executemany): + # do something with statement, parameters + return statement, parameters + + See the example at :class:`.ConnectionEvents`. + + :param conn: :class:`.Connection` object + :param cursor: DBAPI cursor object + :param statement: string SQL statement + :param parameters: Dictionary, tuple, or list of parameters being + passed to the ``execute()`` or ``executemany()`` method of the + DBAPI ``cursor``. In some cases may be ``None``. + :param context: :class:`.ExecutionContext` object in use. May + be ``None``. + :param executemany: boolean, if ``True``, this is an ``executemany()`` + call, if ``False``, this is an ``execute()`` call. + + See also: + + :meth:`.before_execute` + + :meth:`.after_cursor_execute` + + """ def after_cursor_execute(self, conn, cursor, statement, parameters, context, executemany): - """Intercept low-level cursor execute() events.""" + """Intercept low-level cursor execute() events after execution. + + :param conn: :class:`.Connection` object + :param cursor: DBAPI cursor object. Will have results pending + if the statement was a SELECT, but these should not be consumed + as they will be needed by the :class:`.ResultProxy`. + :param statement: string SQL statement + :param parameters: Dictionary, tuple, or list of parameters being + passed to the ``execute()`` or ``executemany()`` method of the + DBAPI ``cursor``. In some cases may be ``None``. + :param context: :class:`.ExecutionContext` object in use. May + be ``None``. + :param executemany: boolean, if ``True``, this is an ``executemany()`` + call, if ``False``, this is an ``execute()`` call. + + """ def dbapi_error(self, conn, cursor, statement, parameters, context, exception): @@ -439,37 +566,99 @@ class ConnectionEvents(event.Events): exception is then wrapped in a SQLAlchemy DBAPI exception wrapper and re-thrown. + :param conn: :class:`.Connection` object + :param cursor: DBAPI cursor object + :param statement: string SQL statement + :param parameters: Dictionary, tuple, or list of parameters being + passed to the ``execute()`` or ``executemany()`` method of the + DBAPI ``cursor``. In some cases may be ``None``. + :param context: :class:`.ExecutionContext` object in use. May + be ``None``. + :param exception: The **unwrapped** exception emitted directly from the + DBAPI. The class here is specific to the DBAPI module in use. + .. versionadded:: 0.7.7 """ def begin(self, conn): - """Intercept begin() events.""" + """Intercept begin() events. + + :param conn: :class:`.Connection` object + + """ def rollback(self, conn): - """Intercept rollback() events.""" + """Intercept rollback() events. + + :param conn: :class:`.Connection` object + + """ def commit(self, conn): - """Intercept commit() events.""" + """Intercept commit() events. + + :param conn: :class:`.Connection` object + """ def savepoint(self, conn, name=None): - """Intercept savepoint() events.""" + """Intercept savepoint() events. + + :param conn: :class:`.Connection` object + :param name: specified name used for the savepoint. + + """ def rollback_savepoint(self, conn, name, context): - """Intercept rollback_savepoint() events.""" + """Intercept rollback_savepoint() events. + + :param conn: :class:`.Connection` object + :param name: specified name used for the savepoint. + :param context: :class:`.ExecutionContext` in use. May be ``None``. + + """ def release_savepoint(self, conn, name, context): - """Intercept release_savepoint() events.""" + """Intercept release_savepoint() events. + + :param conn: :class:`.Connection` object + :param name: specified name used for the savepoint. + :param context: :class:`.ExecutionContext` in use. May be ``None``. + + """ def begin_twophase(self, conn, xid): - """Intercept begin_twophase() events.""" + """Intercept begin_twophase() events. + + :param conn: :class:`.Connection` object + :param xid: two-phase XID identifier + + """ def prepare_twophase(self, conn, xid): - """Intercept prepare_twophase() events.""" + """Intercept prepare_twophase() events. + + :param conn: :class:`.Connection` object + :param xid: two-phase XID identifier + """ def rollback_twophase(self, conn, xid, is_prepared): - """Intercept rollback_twophase() events.""" + """Intercept rollback_twophase() events. + + :param conn: :class:`.Connection` object + :param xid: two-phase XID identifier + :param is_prepared: boolean, indicates if + :meth:`.TwoPhaseTransaction.prepare` was called. + + """ def commit_twophase(self, conn, xid, is_prepared): - """Intercept commit_twophase() events.""" + """Intercept commit_twophase() events. + + :param conn: :class:`.Connection` object + :param xid: two-phase XID identifier + :param is_prepared: boolean, indicates if + :meth:`.TwoPhaseTransaction.prepare` was called. + + """ |