diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-03-24 10:22:39 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2014-03-24 10:22:39 -0400 |
commit | 87d7076b49ec52d6f890d1dc56f61ea2eb83f3a6 (patch) | |
tree | 8519617b40132ceb353098d419a045a56a855fcf /lib/sqlalchemy | |
parent | ebd24974f47f409fef785fd262087a48c02b4ff6 (diff) | |
download | sqlalchemy-87d7076b49ec52d6f890d1dc56f61ea2eb83f3a6.tar.gz |
- Added some new event mechanics for dialect-level events; the initial
implementation allows an event handler to redefine the specific mechanics
by which an arbitrary dialect invokes execute() or executemany() on a
DBAPI cursor. The new events, at this point semi-public and experimental,
are in support of some upcoming transaction-related extensions.
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r-- | lib/sqlalchemy/engine/base.py | 60 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/interfaces.py | 3 | ||||
-rw-r--r-- | lib/sqlalchemy/events.py | 84 |
3 files changed, 127 insertions, 20 deletions
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index 5b1e61a21..6e1564c34 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -905,22 +905,39 @@ class Connection(Connectable): sql_util._repr_params(parameters, batches=10)) try: if context.executemany: - self.dialect.do_executemany( - cursor, - statement, - parameters, - context) + for fn in () if not self.dialect._has_events \ + else self.dialect.dispatch.do_executemany: + if fn(cursor, statement, parameters, context): + break + else: + self.dialect.do_executemany( + cursor, + statement, + parameters, + context) + elif not parameters and context.no_parameters: - self.dialect.do_execute_no_params( - cursor, - statement, - context) + for fn in () if not self.dialect._has_events \ + else self.dialect.dispatch.do_execute_no_params: + if fn(cursor, statement, context): + break + else: + self.dialect.do_execute_no_params( + cursor, + statement, + context) + else: - self.dialect.do_execute( - cursor, - statement, - parameters, - context) + for fn in () if not self.dialect._has_events \ + else self.dialect.dispatch.do_execute: + if fn(cursor, statement, parameters, context): + break + else: + self.dialect.do_execute( + cursor, + statement, + parameters, + context) except Exception as e: self._handle_dbapi_exception( e, @@ -995,11 +1012,16 @@ class Connection(Connectable): self.engine.logger.info(statement) self.engine.logger.info("%r", parameters) try: - self.dialect.do_execute( - cursor, - statement, - parameters, - context) + for fn in () if not self.dialect._has_events \ + else self.dialect.dispatch.do_execute: + if fn(cursor, statement, parameters, context): + break + else: + self.dialect.do_execute( + cursor, + statement, + parameters, + context) except Exception as e: self._handle_dbapi_exception( e, diff --git a/lib/sqlalchemy/engine/interfaces.py b/lib/sqlalchemy/engine/interfaces.py index 5c44933e8..737225863 100644 --- a/lib/sqlalchemy/engine/interfaces.py +++ b/lib/sqlalchemy/engine/interfaces.py @@ -150,6 +150,9 @@ class Dialect(object): """ + _has_events = False + + def create_connect_args(self, url): """Build DB-API compatible connection arguments. diff --git a/lib/sqlalchemy/events.py b/lib/sqlalchemy/events.py index 9f05c8b5b..9ba6de68b 100644 --- a/lib/sqlalchemy/events.py +++ b/lib/sqlalchemy/events.py @@ -8,7 +8,7 @@ from . import event, exc from .pool import Pool -from .engine import Connectable, Engine +from .engine import Connectable, Engine, Dialect from .sql.base import SchemaEventTarget class DDLEvents(event.Events): @@ -840,3 +840,85 @@ class ConnectionEvents(event.Events): :meth:`.TwoPhaseTransaction.prepare` was called. """ + + +class DialectEvents(event.Events): + """event interface for execution-replacement functions. + + These events allow direct instrumentation and replacement + of key dialect functions which interact with the DBAPI. + + .. note:: + + :class:`.DialectEvents` hooks should be considered **semi-public** + and experimental. + These hooks are not for general use and are only for those situations where + intricate re-statement of DBAPI mechanics must be injected onto an existing + dialect. For general-use statement-interception events, please + use the :class:`.ConnectionEvents` interface. + + .. seealso:: + + :meth:`.ConnectionEvents.before_cursor_execute` + + :meth:`.ConnectionEvents.before_execute` + + :meth:`.ConnectionEvents.after_cursor_execute` + + :meth:`.ConnectionEvents.after_execute` + + + .. versionadded:: 0.9.4 + + """ + + _target_class_doc = "SomeEngine" + _dispatch_target = Dialect + + @classmethod + def _listen(cls, event_key, retval=False): + target, identifier, fn = \ + event_key.dispatch_target, event_key.identifier, event_key.fn + + target._has_events = True + event_key.base_listen() + + @classmethod + def _accept_with(cls, target): + if isinstance(target, type): + if issubclass(target, Engine): + return Dialect + elif issubclass(target, Dialect): + return target + elif isinstance(target, Engine): + return target.dialect + else: + return target + + def do_executemany(self, cursor, statement, parameters, context): + """Receive a cursor to have executemany() called. + + Return the value True to halt further events from invoking, + and to indicate that the cursor execution has already taken + place within the event handler. + + """ + + def do_execute_no_params(self, cursor, statement, context): + """Receive a cursor to have execute() with no parameters called. + + Return the value True to halt further events from invoking, + and to indicate that the cursor execution has already taken + place within the event handler. + + """ + + def do_execute(self, cursor, statement, parameters, context): + """Receive a cursor to have execute() called. + + Return the value True to halt further events from invoking, + and to indicate that the cursor execution has already taken + place within the event handler. + + """ + |