]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- The MySQL dialect will now disable :meth:`.ConnectionEvents.handle_error`
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 25 Jul 2014 16:14:22 +0000 (12:14 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 25 Jul 2014 16:14:22 +0000 (12:14 -0400)
events from firing for those statements which it uses internally
to detect if a table exists or not.   This is achieved using an
execution option ``skip_user_error_events`` that disables the handle
error event for the scope of that execution.   In this way, user code
that rewrites exceptions doesn't need to worry about the MySQL
dialect or other dialects that occasionally need to catch
SQLAlchemy specific exceptions.

doc/build/changelog/changelog_10.rst
lib/sqlalchemy/dialects/mysql/base.py
lib/sqlalchemy/engine/base.py
lib/sqlalchemy/events.py
test/engine/test_execute.py

index 57ca3c863e77e0e757bc5b88cf0c60d69d17b249..e4671ed9bac89e48adf519050051bace9759bde8 100644 (file)
 .. changelog::
        :version: 1.0.0
 
+    .. change::
+        :tags: mysql, bug
+
+        The MySQL dialect will now disable :meth:`.ConnectionEvents.handle_error`
+        events from firing for those statements which it uses internally
+        to detect if a table exists or not.   This is achieved using an
+        execution option ``skip_user_error_events`` that disables the handle
+        error event for the scope of that execution.   In this way, user code
+        that rewrites exceptions doesn't need to worry about the MySQL
+        dialect or other dialects that occasionally need to catch
+        SQLAlchemy specific exceptions.
+
     .. change::
         :tags: mysql, bug
         :tickets: 2515
index 0c00cf53053b00f808fa6c14abbfbec9031ee1f0..06c3b0c501ac1fdb69c5247eb607b09edd637dd8 100644 (file)
@@ -2310,7 +2310,8 @@ class MySQLDialect(default.DefaultDialect):
         rs = None
         try:
             try:
-                rs = connection.execute(st)
+                rs = connection.execution_options(
+                    skip_user_error_events=True).execute(st)
                 have = rs.fetchone() is not None
                 rs.close()
                 return have
@@ -2616,7 +2617,8 @@ class MySQLDialect(default.DefaultDialect):
 
         rp = None
         try:
-            rp = connection.execute(st)
+            rp = connection.execution_options(
+                skip_user_error_events=True).execute(st)
         except exc.DBAPIError as e:
             if self._extract_error_code(e.orig) == 1146:
                 raise exc.NoSuchTableError(full_name)
@@ -2640,7 +2642,8 @@ class MySQLDialect(default.DefaultDialect):
         rp, rows = None, None
         try:
             try:
-                rp = connection.execute(st)
+                rp = connection.execution_options(
+                    skip_user_error_events=True).execute(st)
             except exc.DBAPIError as e:
                 if self._extract_error_code(e.orig) == 1146:
                     raise exc.NoSuchTableError(full_name)
index cf06896261edebcd9e8e5e099b7f33fad7a9a19d..f9dfea32b09183d16a83f9f6fe6dcfe9bbd7ff1d 100644 (file)
@@ -1112,7 +1112,9 @@ class Connection(Connectable):
 
             newraise = None
 
-            if self._has_events or self.engine._has_events:
+            if (self._has_events or self.engine._has_events) and \
+                not self._execution_options.get(
+                    'skip_user_error_events', False):
                 # legacy dbapi_error event
                 if should_wrap and context:
                     self.dispatch.dbapi_error(self,
index 42bbbfc0fb56ed622fa6fc4b84bede996ec09fc6..1ecec51b68e0ae02e1e61a5a9bb6f9b99b1742f8 100644 (file)
@@ -733,6 +733,16 @@ class ConnectionEvents(event.Events):
         .. versionadded:: 0.9.7 Added the
             :meth:`.ConnectionEvents.handle_error` hook.
 
+        .. versionchanged:: 1.0.0 The :meth:`.handle_error` event is
+           not fired off when a dialect makes use of the
+           ``skip_user_error_events`` execution option.   This is used
+           by dialects which intend to catch SQLAlchemy-specific exceptions
+           within specific operations, such as when the MySQL dialect detects
+           a table not present within the ``has_table()`` dialect method.
+           Prior to 1.0.0, code which implements :meth:`.handle_error` needs
+           to ensure that exceptions thrown in these scenarios are re-raised
+           without modification.
+
         """
 
     def engine_connect(self, conn, branch):
index 33e116cdfee88c4c166d9f77ac4b46f987a99a91..50cf41311d279a420ca2401ed9a60a0fb4c52789 100644 (file)
@@ -1659,6 +1659,34 @@ class HandleErrorTest(fixtures.TestBase):
         is_(ctx.is_disconnect, False)
         is_(ctx.original_exception, nope)
 
+    def test_exception_event_disable_handlers(self):
+        engine = engines.testing_engine()
+
+        class MyException1(Exception):
+            pass
+
+        @event.listens_for(engine, 'handle_error')
+        def err1(context):
+            stmt = context.statement
+
+            if "ERROR_ONE" in str(stmt):
+                raise MyException1("my exception short circuit")
+
+        with engine.connect() as conn:
+            assert_raises(
+                tsa.exc.DBAPIError,
+                conn.execution_options(
+                    skip_user_error_events=True
+                ).execute, "SELECT ERROR_ONE FROM I_DONT_EXIST"
+            )
+
+            assert_raises(
+                MyException1,
+                conn.execution_options(
+                    skip_user_error_events=False
+                ).execute, "SELECT ERROR_ONE FROM I_DONT_EXIST"
+            )
+
     def _test_alter_disconnect(self, orig_error, evt_value):
         engine = engines.testing_engine()