summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-07-25 12:14:22 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2014-07-25 12:14:22 -0400
commit6b60d3a9e6ba93d177ac777bfaae8269c18ddee6 (patch)
treed663ca41f3bf05fcfbae2755426469cae5f855f1
parent8560522ff0244c93dab62276f9ba445df90f0d39 (diff)
downloadsqlalchemy-6b60d3a9e6ba93d177ac777bfaae8269c18ddee6.tar.gz
- 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.
-rw-r--r--doc/build/changelog/changelog_10.rst12
-rw-r--r--lib/sqlalchemy/dialects/mysql/base.py9
-rw-r--r--lib/sqlalchemy/engine/base.py4
-rw-r--r--lib/sqlalchemy/events.py10
-rw-r--r--test/engine/test_execute.py28
5 files changed, 59 insertions, 4 deletions
diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst
index 57ca3c863..e4671ed9b 100644
--- a/doc/build/changelog/changelog_10.rst
+++ b/doc/build/changelog/changelog_10.rst
@@ -18,6 +18,18 @@
.. 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
Changed the default value of "raise_on_warnings" to False for
diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py
index 0c00cf530..06c3b0c50 100644
--- a/lib/sqlalchemy/dialects/mysql/base.py
+++ b/lib/sqlalchemy/dialects/mysql/base.py
@@ -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)
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py
index cf0689626..f9dfea32b 100644
--- a/lib/sqlalchemy/engine/base.py
+++ b/lib/sqlalchemy/engine/base.py
@@ -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,
diff --git a/lib/sqlalchemy/events.py b/lib/sqlalchemy/events.py
index 42bbbfc0f..1ecec51b6 100644
--- a/lib/sqlalchemy/events.py
+++ b/lib/sqlalchemy/events.py
@@ -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):
diff --git a/test/engine/test_execute.py b/test/engine/test_execute.py
index 33e116cdf..50cf41311 100644
--- a/test/engine/test_execute.py
+++ b/test/engine/test_execute.py
@@ -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()