]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- [feature] Added new connection event
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 10 Apr 2012 23:38:22 +0000 (19:38 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 10 Apr 2012 23:38:22 +0000 (19:38 -0400)
dbapi_error(). Is called for all DBAPI-level
errors passing the original DBAPI exception
before SQLAlchemy modifies the state
of the cursor.

CHANGES
lib/sqlalchemy/engine/base.py
lib/sqlalchemy/events.py
test/engine/test_execute.py

diff --git a/CHANGES b/CHANGES
index a1c5dd4b9b9b0e666c3b5f6ca98a456e016844ea..f4d46f67329a0af20174215341ce468c865c42cb 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -35,6 +35,12 @@ CHANGES
     as an Index could be a placeholder for just an 
     index of a certain name.
 
+  - [feature] Added new connection event
+    dbapi_error(). Is called for all DBAPI-level
+    errors passing the original DBAPI exception
+    before SQLAlchemy modifies the state 
+    of the cursor.
+
 - mssql
   - [feature] Added interim create_engine flag
     supports_unicode_binds to PyODBC dialect,
index 1f28501f701017a09576a140d3418917db5dc3bf..110ac4e8a173503bb3c8acad027d9290526ba0b4 100644 (file)
@@ -1806,10 +1806,19 @@ class Connection(Connectable):
                 (statement is not None and context is None)
 
             if should_wrap and context:
+                if self._has_events:
+                    self.engine.dispatch.dbapi_error(self, 
+                                                    cursor, 
+                                                    statement, 
+                                                    parameters, 
+                                                    context, 
+                                                    e)
                 context.handle_dbapi_exception(e)
 
             is_disconnect = isinstance(e, self.dialect.dbapi.Error) and \
                                 self.dialect.is_disconnect(e, self.__connection, cursor)
+
+
             if is_disconnect:
                 self.invalidate(e)
                 self.engine.dispose()
index 504dfe150e3d36eac03d5f7e68856b8c17498d15..18888be0395a99ca3848c46b5c34d43a1b5cfc0a 100644 (file)
@@ -401,6 +401,36 @@ class ConnectionEvents(event.Events):
                         parameters, context, executemany):
         """Intercept low-level cursor execute() events."""
 
+    def dbapi_error(self, conn, cursor, statement, parameters, 
+                        context, exception):
+        """Intercept a raw DBAPI error.
+        
+        This event is called with the DBAPI exception instance 
+        received from the DBAPI itself, *before* SQLAlchemy wraps the 
+        exception with it's own exception wrappers, and before any
+        other operations are performed on the DBAPI cursor; the
+        existing transaction remains in effect as well as any state
+        on the cursor.
+        
+        The use case here is to inject low-level exception handling
+        into an :class:`.Engine`, typically for logging and
+        debugging purposes.   In general, user code should **not** modify
+        any state or throw any exceptions here as this will
+        interfere with SQLAlchemy's cleanup and error handling
+        routines.
+        
+        Subsequent to this hook, SQLAlchemy may attempt any
+        number of operations on the connection/cursor, including
+        closing the cursor, rolling back of the transaction in the 
+        case of connectionless execution, and disposing of the entire
+        connection pool if a "disconnect" was detected.   The
+        exception is then wrapped in a SQLAlchemy DBAPI exception
+        wrapper and re-thrown.
+        
+        New in 0.7.7.
+
+        """
+
     def begin(self, conn):
         """Intercept begin() events."""
 
index 146725f455f21e156b6d1b62573e8bb511f71c7e..6bdbf422724f99fc205e5858022f178858ad5681 100644 (file)
@@ -984,6 +984,23 @@ class EngineEventsTest(fixtures.TestBase):
         e1.execute(select([1]).compile(dialect=e1.dialect))
         e1._execute_compiled(select([1]).compile(dialect=e1.dialect), [], {})
 
+    def test_exception_event(self):
+        engine = engines.testing_engine()
+        canary = []
+
+        @event.listens_for(engine, 'dbapi_error')
+        def err(conn, cursor, stmt, parameters, context, exception):
+            canary.append((stmt, parameters, exception))
+
+        conn = engine.connect()
+        try:
+            conn.execute("SELECT FOO FROM I_DONT_EXIST")
+            assert False
+        except tsa.exc.DBAPIError, e:
+            assert canary[0][2] is e.orig
+            assert canary[0][0] == "SELECT FOO FROM I_DONT_EXIST"
+
+
     @testing.fails_on('firebird', 'Data type unknown')
     def test_execute_events(self):