summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2011-02-09 18:11:40 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2011-02-09 18:11:40 -0500
commit7e8f35109725ed3fd3caf96acf8b94a13c53fdfe (patch)
tree6ee96b736322eff04bb1ec5ff2b6e9de84e5545c /lib/sqlalchemy
parente80eac22a8669ada5ffaabbcfa8a991eee140697 (diff)
downloadsqlalchemy-7e8f35109725ed3fd3caf96acf8b94a13c53fdfe.tar.gz
- Non-DBAPI errors which occur in the scope of an `execute()`
call are now wrapped in sqlalchemy.exc.StatementError, and the text of the SQL statement and repr() of params is included. This makes it easier to identify statement executions which fail before the DBAPI becomes involved. [ticket:2015]
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/dialects/mysql/base.py6
-rw-r--r--lib/sqlalchemy/engine/base.py33
-rw-r--r--lib/sqlalchemy/engine/default.py2
-rw-r--r--lib/sqlalchemy/engine/strategies.py7
-rw-r--r--lib/sqlalchemy/exc.py77
5 files changed, 85 insertions, 40 deletions
diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py
index 882e13d2e..271218dba 100644
--- a/lib/sqlalchemy/dialects/mysql/base.py
+++ b/lib/sqlalchemy/dialects/mysql/base.py
@@ -1767,7 +1767,7 @@ class MySQLDialect(default.DefaultDialect):
have = rs.rowcount > 0
rs.close()
return have
- except exc.SQLError, e:
+ except exc.DBAPIError, e:
if self._extract_error_code(e.orig) == 1146:
return False
raise
@@ -2055,7 +2055,7 @@ class MySQLDialect(default.DefaultDialect):
rp = None
try:
rp = connection.execute(st)
- except exc.SQLError, e:
+ except exc.DBAPIError, e:
if self._extract_error_code(e.orig) == 1146:
raise exc.NoSuchTableError(full_name)
else:
@@ -2079,7 +2079,7 @@ class MySQLDialect(default.DefaultDialect):
try:
try:
rp = connection.execute(st)
- except exc.SQLError, e:
+ except exc.DBAPIError, e:
if self._extract_error_code(e.orig) == 1146:
raise exc.NoSuchTableError(full_name)
else:
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py
index f6c974136..cf6c6ad49 100644
--- a/lib/sqlalchemy/engine/base.py
+++ b/lib/sqlalchemy/engine/base.py
@@ -1286,12 +1286,14 @@ class Connection(Connectable):
"""Execute a schema.DDL object."""
dialect = self.dialect
+
+ compiled = ddl.compile(dialect=dialect)
return self._execute_context(
dialect,
dialect.execution_ctx_cls._init_ddl,
- None,
+ compiled,
None,
- ddl.compile(dialect=dialect)
+ compiled
)
def _execute_clauseelement(self, elem, multiparams, params):
@@ -1322,7 +1324,7 @@ class Connection(Connectable):
return self._execute_context(
dialect,
dialect.execution_ctx_cls._init_compiled,
- None,
+ compiled_sql,
params,
compiled_sql, params
)
@@ -1335,7 +1337,7 @@ class Connection(Connectable):
return self._execute_context(
dialect,
dialect.execution_ctx_cls._init_compiled,
- None,
+ compiled,
parameters,
compiled, parameters
)
@@ -1357,7 +1359,8 @@ class Connection(Connectable):
_after_cursor_execute = None
def _execute_context(self, dialect, constructor,
- statement, parameters, *args):
+ statement, parameters,
+ *args):
"""Create an :class:`.ExecutionContext` and execute, returning
a :class:`.ResultProxy`."""
@@ -1370,7 +1373,7 @@ class Connection(Connectable):
context = constructor(dialect, self, conn, *args)
except Exception, e:
self._handle_dbapi_exception(e,
- statement, parameters,
+ str(statement), parameters,
None, None)
raise
@@ -1505,20 +1508,28 @@ class Connection(Connectable):
context):
if getattr(self, '_reentrant_error', False):
# Py3K
- #raise exc.DBAPIError.instance(statement, parameters, e) from e
+ #raise exc.DBAPIError.instance(statement, parameters, e,
+ # self.dialect.dbapi.Error) from e
# Py2K
- raise exc.DBAPIError.instance(statement, parameters, e), \
+ raise exc.DBAPIError.instance(statement,
+ parameters,
+ e,
+ self.dialect.dbapi.Error), \
None, sys.exc_info()[2]
# end Py2K
self._reentrant_error = True
try:
- if not isinstance(e, self.dialect.dbapi.Error):
+ # non-DBAPI error - if we already got a context,
+ # or theres no string statement, don't wrap it
+ if not isinstance(e, self.dialect.dbapi.Error) and \
+ (statement is None or context is not None):
return
if context:
context.handle_dbapi_exception(e)
- is_disconnect = self.dialect.is_disconnect(e, self.__connection, cursor)
+ 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()
@@ -1533,6 +1544,7 @@ class Connection(Connectable):
# statement,
# parameters,
# e,
+ # self.dialect.dbapi.Error,
# connection_invalidated=is_disconnect) \
# from e
# Py2K
@@ -1540,6 +1552,7 @@ class Connection(Connectable):
statement,
parameters,
e,
+ self.dialect.dbapi.Error,
connection_invalidated=is_disconnect), \
None, sys.exc_info()[2]
# end Py2K
diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py
index e669b305e..75a686475 100644
--- a/lib/sqlalchemy/engine/default.py
+++ b/lib/sqlalchemy/engine/default.py
@@ -392,7 +392,7 @@ class DefaultExecutionContext(base.ExecutionContext):
self.compiled = compiled
if not compiled.can_execute:
- raise exc.ArgumentError("Not an executable clause: %s" % compiled)
+ raise exc.ArgumentError("Not an executable clause")
self.execution_options = compiled.statement._execution_options
if connection._execution_options:
diff --git a/lib/sqlalchemy/engine/strategies.py b/lib/sqlalchemy/engine/strategies.py
index e49d0e99e..06bf1126f 100644
--- a/lib/sqlalchemy/engine/strategies.py
+++ b/lib/sqlalchemy/engine/strategies.py
@@ -80,10 +80,13 @@ class DefaultEngineStrategy(EngineStrategy):
return dialect.connect(*cargs, **cparams)
except Exception, e:
# Py3K
- #raise exc.DBAPIError.instance(None, None, e) from e
+ #raise exc.DBAPIError.instance(None, None,
+ # dialect.dbapi.Error, e) from e
# Py2K
import sys
- raise exc.DBAPIError.instance(None, None, e), None, sys.exc_info()[2]
+ raise exc.DBAPIError.instance(
+ None, None, e, dialect.dbapi.Error), \
+ None, sys.exc_info()[2]
# end Py2K
creator = kwargs.pop('creator', connect)
diff --git a/lib/sqlalchemy/exc.py b/lib/sqlalchemy/exc.py
index b50e000a2..dd3f5a9f8 100644
--- a/lib/sqlalchemy/exc.py
+++ b/lib/sqlalchemy/exc.py
@@ -92,7 +92,37 @@ class UnboundExecutionError(InvalidRequestError):
# Moved to orm.exc; compatability definition installed by orm import until 0.6
UnmappedColumnError = None
-class DBAPIError(SQLAlchemyError):
+class StatementError(SQLAlchemyError):
+ """An error occured during execution of a SQL statement.
+
+ :class:`.StatementError` wraps the exception raised
+ during execution, and features :attr:`.statement`
+ and :attr:`.params` attributes which supply context regarding
+ the specifics of the statement which had an issue.
+
+ The wrapped exception object is available in
+ the :attr:`.orig` attribute.
+
+ """
+
+ def __init__(self, message, statement, params, orig):
+ SQLAlchemyError.__init__(self, message)
+ self.statement = statement
+ self.params = params
+ self.orig = orig
+
+ def __str__(self):
+ if isinstance(self.params, (list, tuple)) and \
+ len(self.params) > 10 and \
+ isinstance(self.params[0], (list, dict, tuple)):
+ return ' '.join((SQLAlchemyError.__str__(self),
+ repr(self.statement),
+ repr(self.params[:2]),
+ '... and a total of %i bound parameter sets' % len(self.params)))
+ return ' '.join((SQLAlchemyError.__str__(self),
+ repr(self.statement), repr(self.params)))
+
+class DBAPIError(StatementError):
"""Raised when the execution of a database operation fails.
``DBAPIError`` wraps exceptions raised by the DB-API underlying the
@@ -103,23 +133,33 @@ class DBAPIError(SQLAlchemyError):
that there is no guarantee that different DB-API implementations will
raise the same exception type for any given error condition.
- If the error-raising operation occured in the execution of a SQL
- statement, that statement and its parameters will be available on
- the exception object in the ``statement`` and ``params`` attributes.
+ :class:`.DBAPIError` features :attr:`.statement`
+ and :attr:`.params` attributes which supply context regarding
+ the specifics of the statement which had an issue, for the
+ typical case when the error was raised within the context of
+ emitting a SQL statement.
- The wrapped exception object is available in the ``orig`` attribute.
+ The wrapped exception object is available in the :attr:`.orig` attribute.
Its type and properties are DB-API implementation specific.
"""
@classmethod
- def instance(cls, statement, params, orig, connection_invalidated=False):
+ def instance(cls, statement, params,
+ orig,
+ dbapi_base_err,
+ connection_invalidated=False):
# Don't ever wrap these, just return them directly as if
# DBAPIError didn't exist.
if isinstance(orig, (KeyboardInterrupt, SystemExit)):
return orig
if orig is not None:
+ # not a DBAPI error, statement is present.
+ # raise a StatementError
+ if not isinstance(orig, dbapi_base_err) and statement:
+ return StatementError(str(orig), statement, params, orig)
+
name, glob = orig.__class__.__name__, globals()
if name in glob and issubclass(glob[name], DBAPIError):
cls = glob[name]
@@ -133,26 +173,15 @@ class DBAPIError(SQLAlchemyError):
raise
except Exception, e:
text = 'Error in str() of DB-API-generated exception: ' + str(e)
- SQLAlchemyError.__init__(
- self, '(%s) %s' % (orig.__class__.__name__, text))
- self.statement = statement
- self.params = params
- self.orig = orig
+ StatementError.__init__(
+ self,
+ '(%s) %s' % (orig.__class__.__name__, text),
+ statement,
+ params,
+ orig
+ )
self.connection_invalidated = connection_invalidated
- def __str__(self):
- if isinstance(self.params, (list, tuple)) and len(self.params) > 10 and isinstance(self.params[0], (list, dict, tuple)):
- return ' '.join((SQLAlchemyError.__str__(self),
- repr(self.statement),
- repr(self.params[:2]),
- '... and a total of %i bound parameter sets' % len(self.params)))
- return ' '.join((SQLAlchemyError.__str__(self),
- repr(self.statement), repr(self.params)))
-
-
-# As of 0.4, SQLError is now DBAPIError.
-# SQLError alias will be removed in 0.6.
-SQLError = DBAPIError
class InterfaceError(DBAPIError):
"""Wraps a DB-API InterfaceError."""