]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Non-DBAPI errors which occur in the scope of an `execute()`
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 9 Feb 2011 23:11:40 +0000 (18:11 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 9 Feb 2011 23:11:40 +0000 (18:11 -0500)
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]

15 files changed:
CHANGES
lib/sqlalchemy/dialects/mysql/base.py
lib/sqlalchemy/engine/base.py
lib/sqlalchemy/engine/default.py
lib/sqlalchemy/engine/strategies.py
lib/sqlalchemy/exc.py
test/base/test_except.py
test/dialect/test_mysql.py
test/dialect/test_postgresql.py
test/dialect/test_sqlite.py
test/engine/test_execute.py
test/engine/test_reconnect.py
test/sql/test_constraints.py
test/sql/test_defaults.py
test/sql/test_query.py

diff --git a/CHANGES b/CHANGES
index ceba445c5410386f330c4492991c4e0bdb05b519..f8367673584a2a3ef98ed18355cb719255f10b9b 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -175,6 +175,13 @@ CHANGES
 
   - TypeDecorator is present in the "sqlalchemy" import space.
 
+  - 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]
+
 -sqlite
   - SQLite dialect now uses `NullPool` for file-based databases
     [ticket:1921]
index 882e13d2e0af8d0a310d0e5c0eedf9e1ffe95113..271218dba812e62196faa2ff4c02d88be02d524c 100644 (file)
@@ -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:
index f6c9741365f64e338e673c83d7d7e16d4e5dc5ae..cf6c6ad49f011fe33d0b7d0080a51ab41239ba9f 100644 (file)
@@ -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
index e669b305ea1250c1645fa7392b2ab1eb22e4b116..75a6864753c23ffa8a30b764df46b794b732ba01 100644 (file)
@@ -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:
index e49d0e99e63ec64556c11defccf6d833e63dc4a8..06bf1126fd600cd1d3ec392b1d988db60bdfdd2d 100644 (file)
@@ -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)
index b50e000a2e6e77b3fb63390c6be7e71df5c7c8f2..dd3f5a9f88efb591da7e4289725eb9f0fb9b92c4 100644 (file)
@@ -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."""
index f02ca988bce14bb3047034f9042b3b2d7707be35..044e7c2440022cb76fc04c842f212a3f89200c60 100644 (file)
@@ -38,14 +38,14 @@ class WrapTest(TestBase):
     def test_db_error_normal(self):
         try:
             raise sa_exceptions.DBAPIError.instance('', [],
-                    OperationalError())
+                    OperationalError(), DatabaseError)
         except sa_exceptions.DBAPIError:
             self.assert_(True)
 
     def test_tostring(self):
         try:
             raise sa_exceptions.DBAPIError.instance('this is a message'
-                    , None, OperationalError())
+                    , None, OperationalError(), DatabaseError)
         except sa_exceptions.DBAPIError, exc:
             assert str(exc) \
                 == "(OperationalError)  'this is a message' None"
@@ -56,7 +56,7 @@ class WrapTest(TestBase):
                     , 
                 {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7, 'h':
                 8, 'i': 9, 'j': 10, 'k': 11,
-                }, OperationalError())
+                }, OperationalError(), DatabaseError)
         except sa_exceptions.DBAPIError, exc:
             assert str(exc).startswith("(OperationalError)  'this is a "
                     "message' {")
@@ -64,7 +64,8 @@ class WrapTest(TestBase):
     def test_tostring_large_list(self):
         try:
             raise sa_exceptions.DBAPIError.instance('this is a message', 
-                [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,], OperationalError())
+                [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,], 
+                OperationalError(), DatabaseError)
         except sa_exceptions.DBAPIError, exc:
             assert str(exc).startswith("(OperationalError)  'this is a "
                     "message' [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]")
@@ -74,7 +75,7 @@ class WrapTest(TestBase):
             raise sa_exceptions.DBAPIError.instance('this is a message', 
                 [{1: 1}, {1: 1}, {1: 1}, {1: 1}, {1: 1}, {1: 1}, 
                 {1: 1}, {1:1}, {1: 1}, {1: 1},], 
-                OperationalError())
+                OperationalError(), DatabaseError)
         except sa_exceptions.DBAPIError, exc:
             assert str(exc) \
                 == "(OperationalError)  'this is a message' [{1: 1}, "\
@@ -84,7 +85,7 @@ class WrapTest(TestBase):
             raise sa_exceptions.DBAPIError.instance('this is a message', [
                 {1: 1}, {1: 1}, {1: 1}, {1: 1}, {1: 1}, {1: 1}, {1: 1}, 
                 {1:1}, {1: 1}, {1: 1}, {1: 1},
-                ], OperationalError())
+                ], OperationalError(), DatabaseError)
         except sa_exceptions.DBAPIError, exc:
             assert str(exc) \
                 == "(OperationalError)  'this is a message' [{1: 1}, "\
@@ -94,7 +95,7 @@ class WrapTest(TestBase):
                 [
                 (1, ), (1, ), (1, ), (1, ), (1, ), (1, ), (1, ), (1, ), (1, ),
                 (1, ),
-                ], OperationalError())
+                ], OperationalError(), DatabaseError)
         except sa_exceptions.DBAPIError, exc:
             assert str(exc) \
                 == "(OperationalError)  'this is a message' [(1,), "\
@@ -103,7 +104,7 @@ class WrapTest(TestBase):
             raise sa_exceptions.DBAPIError.instance('this is a message', [
                 (1, ), (1, ), (1, ), (1, ), (1, ), (1, ), (1, ), (1, ), (1, ),
                 (1, ), (1, ),
-                ], OperationalError())
+                ], OperationalError(), DatabaseError)
         except sa_exceptions.DBAPIError, exc:
             assert str(exc) \
                 == "(OperationalError)  'this is a message' [(1,), "\
@@ -112,25 +113,23 @@ class WrapTest(TestBase):
     def test_db_error_busted_dbapi(self):
         try:
             raise sa_exceptions.DBAPIError.instance('', [],
-                    ProgrammingError())
+                    ProgrammingError(), DatabaseError)
         except sa_exceptions.DBAPIError, e:
             self.assert_(True)
             self.assert_('Error in str() of DB-API' in e.args[0])
 
     def test_db_error_noncompliant_dbapi(self):
         try:
-            raise sa_exceptions.DBAPIError.instance('', [], OutOfSpec())
+            raise sa_exceptions.DBAPIError.instance('', [], OutOfSpec(), 
+                        DatabaseError)
         except sa_exceptions.DBAPIError, e:
             self.assert_(e.__class__ is sa_exceptions.DBAPIError)
         except OutOfSpec:
             self.assert_(False)
 
-        # Make sure the DatabaseError recognition logic is limited to
-        # subclasses of sqlalchemy.exceptions.DBAPIError
-
         try:
             raise sa_exceptions.DBAPIError.instance('', [],
-                    sa_exceptions.ArgumentError())
+                    sa_exceptions.ArgumentError(), DatabaseError)
         except sa_exceptions.DBAPIError, e:
             self.assert_(e.__class__ is sa_exceptions.DBAPIError)
         except sa_exceptions.ArgumentError:
@@ -139,7 +138,7 @@ class WrapTest(TestBase):
     def test_db_error_keyboard_interrupt(self):
         try:
             raise sa_exceptions.DBAPIError.instance('', [],
-                    KeyboardInterrupt())
+                    KeyboardInterrupt(), DatabaseError)
         except sa_exceptions.DBAPIError:
             self.assert_(False)
         except KeyboardInterrupt:
@@ -148,7 +147,7 @@ class WrapTest(TestBase):
     def test_db_error_system_exit(self):
         try:
             raise sa_exceptions.DBAPIError.instance('', [],
-                    SystemExit())
+                    SystemExit(), DatabaseError)
         except sa_exceptions.DBAPIError:
             self.assert_(False)
         except SystemExit:
index 183f227c381768b87890d4796cc0f8aa4385898b..fd7a15e5b1caa37035fb59db4bbd466670d9009e 100644 (file)
@@ -621,10 +621,10 @@ class TypesTest(TestBase, AssertsExecutionResults, AssertsCompiledSQL):
         enum_table.drop(checkfirst=True)
         enum_table.create()
 
-        assert_raises(exc.SQLError, enum_table.insert().execute, 
+        assert_raises(exc.DBAPIError, enum_table.insert().execute, 
                         e1=None, e2=None, e3=None, e4=None)
 
-        assert_raises(exc.InvalidRequestError, enum_table.insert().execute,
+        assert_raises(exc.StatementError, enum_table.insert().execute,
                                         e1='c', e2='c', e2generic='c', e3='c',
                                         e4='c', e5='c', e5generic='c', e6='c')
 
index 9aa281979d7543ce3d860548c08465aef14e0ce8..10067a6696c21657ca4ab947f15ac69a367861e2 100644 (file)
@@ -993,7 +993,7 @@ class DomainReflectionTest(TestBase, AssertsExecutionResults):
             :
             try:
                 con.execute(ddl)
-            except exc.SQLError, e:
+            except exc.DBAPIError, e:
                 if not 'already exists' in str(e):
                     raise e
         con.execute('CREATE TABLE testtable (question integer, answer '
index b99f58bd25bed25a3dc9609a73f2f7e8e3e392a9..efd616c3deaac3a9d651a903693ccc6c4d623b84 100644 (file)
@@ -43,7 +43,7 @@ class TestTypes(TestBase, AssertsExecutionResults):
             meta.drop_all()
 
     def test_string_dates_raise(self):
-        assert_raises(TypeError, testing.db.execute,
+        assert_raises(exc.StatementError, testing.db.execute,
                       select([1]).where(bindparam('date', type_=Date)),
                       date=str(datetime.date(2007, 10, 30)))
 
index bb4e7de42c89c129d3f614cc9d97e5cf4ff33f3d..01a0100abdeb37ebbed98b31f6e76b829f56e284 100644 (file)
@@ -1,8 +1,9 @@
-from test.lib.testing import eq_, assert_raises
+from test.lib.testing import eq_, assert_raises, assert_raises_message
 import re
 from sqlalchemy.interfaces import ConnectionProxy
 from sqlalchemy import MetaData, Integer, String, INT, VARCHAR, func, \
-    bindparam, select, event
+    bindparam, select, event, TypeDecorator
+from sqlalchemy.sql import column, literal
 from test.lib.schema import Table, Column
 import sqlalchemy as tsa
 from test.lib import TestBase, testing, engines
@@ -122,14 +123,30 @@ class ExecuteTest(TestBase):
                     'horse'), (4, 'sally')]
             conn.execute('delete from users')
 
-    def test_exception_wrapping(self):
+    def test_exception_wrapping_dbapi(self):
         for conn in testing.db, testing.db.connect():
-            try:
-                conn.execute('osdjafioajwoejoasfjdoifjowejfoawejqoijwef'
-                             )
-                assert False
-            except tsa.exc.DBAPIError:
-                assert True
+            assert_raises_message(
+                tsa.exc.DBAPIError,
+                r"not_a_valid_statement",
+                conn.execute, 'not_a_valid_statement'
+            )
+
+    def test_exception_wrapping_non_dbapi_statement(self):
+        class MyType(TypeDecorator):
+            impl = Integer
+            def process_bind_param(self, value, dialect):
+                raise Exception("nope")
+
+        for conn in testing.db, testing.db.connect():
+            assert_raises_message(
+                tsa.exc.StatementError,
+                "nope 'SELECT 1 ",
+                conn.execute,
+                    select([1]).\
+                        where(
+                            column('foo') == literal('bar', MyType())
+                        )
+            )
 
     def test_empty_insert(self):
         """test that execute() interprets [] as a list with no params"""
index 9b3a1b4db681237c26d202e5f6dd42f4c1dfa77d..74986c178402de9f5d24e46e8880da500d4e547e 100644 (file)
@@ -1,4 +1,4 @@
-from test.lib.testing import eq_, assert_raises
+from test.lib.testing import eq_, assert_raises, assert_raises_message
 import time
 import weakref
 from sqlalchemy import select, MetaData, Integer, String, pool
@@ -130,13 +130,11 @@ class MockReconnectTest(TestBase):
         assert not conn.closed
         assert conn.invalidated
         assert trans.is_active
-        try:
-            conn.execute(select([1]))
-            assert False
-        except tsa.exc.InvalidRequestError, e:
-            assert str(e) \
-                == "Can't reconnect until invalid transaction is "\
-                "rolled back"
+        assert_raises_message(
+            tsa.exc.StatementError,
+            "Can't reconnect until invalid transaction is rolled back",
+            conn.execute, select([1])
+        )
         assert trans.is_active
         try:
             trans.commit()
@@ -364,13 +362,12 @@ class RealReconnectTest(TestBase):
         assert not conn.closed
         assert conn.invalidated
         assert trans.is_active
-        try:
-            conn.execute(select([1]))
-            assert False
-        except tsa.exc.InvalidRequestError, e:
-            assert str(e) \
-                == "Can't reconnect until invalid transaction is "\
-                "rolled back"
+        assert_raises_message(
+            tsa.exc.StatementError,
+            "Can't reconnect until invalid transaction is "\
+                "rolled back",
+            conn.execute, select([1])
+        )
         assert trans.is_active
         try:
             trans.commit()
index 4bacbd27110ba8184773e70abbc242ba27f0a2cf..f4791c0bdeeb21fb9887a0764a9f1cbf50bb7282 100644 (file)
@@ -79,9 +79,9 @@ class ConstraintTest(TestBase, AssertsExecutionResults, AssertsCompiledSQL):
 
         metadata.create_all()
         foo.insert().execute(id=1,x=9,y=5)
-        assert_raises(exc.SQLError, foo.insert().execute, id=2,x=5,y=9)
+        assert_raises(exc.DBAPIError, foo.insert().execute, id=2,x=5,y=9)
         bar.insert().execute(id=1,x=10)
-        assert_raises(exc.SQLError, bar.insert().execute, id=2,x=5)
+        assert_raises(exc.DBAPIError, bar.insert().execute, id=2,x=5)
 
     def test_unique_constraint(self):
         foo = Table('foo', metadata,
@@ -98,8 +98,8 @@ class ConstraintTest(TestBase, AssertsExecutionResults, AssertsCompiledSQL):
         foo.insert().execute(id=2, value='value2')
         bar.insert().execute(id=1, value='a', value2='a')
         bar.insert().execute(id=2, value='a', value2='b')
-        assert_raises(exc.SQLError, foo.insert().execute, id=3, value='value1')
-        assert_raises(exc.SQLError, bar.insert().execute, id=3, value='a', value2='b')
+        assert_raises(exc.DBAPIError, foo.insert().execute, id=3, value='value1')
+        assert_raises(exc.DBAPIError, bar.insert().execute, id=3, value='a', value2='b')
 
     def test_index_create(self):
         employees = Table('employees', metadata,
index 1f2226842032a8406ba523c4f7b401fa46095f56..cbecdbe1813d057b6cfc6de03835208411f654b5 100644 (file)
@@ -304,7 +304,7 @@ class DefaultTest(testing.TestBase):
               12, today, 'py')])
 
     def test_missing_many_param(self):
-        assert_raises_message(exc.InvalidRequestError, 
+        assert_raises_message(exc.StatementError, 
             "A value is required for bind parameter 'col7', in parameter group 1",
             t.insert().execute,
             {'col4':7, 'col7':12, 'col8':19},
@@ -531,7 +531,7 @@ class AutoIncrementTest(_base.TablesTest):
             nonai.insert().execute(data='row 1')
             nonai.insert().execute(data='row 2')
             assert False
-        except sa.exc.SQLError, e:
+        except sa.exc.DBAPIError, e:
             assert True
 
         nonai.insert().execute(id=1, data='row 1')
index cbf6e6e582843d817bf2d0cc2453eaaad62c2e75..359084cd81adcba120b35eb0f422098188aa6824 100644 (file)
@@ -48,8 +48,8 @@ class QueryTest(TestBase):
     def test_insert_heterogeneous_params(self):
         """test that executemany parameters are asserted to match the parameter set of the first."""
 
-        assert_raises_message(exc.InvalidRequestError, 
-            "A value is required for bind parameter 'user_name', in parameter group 2",
+        assert_raises_message(exc.StatementError, 
+            "A value is required for bind parameter 'user_name', in parameter group 2 'INSERT INTO query_users",
             users.insert().execute,
             {'user_id':7, 'user_name':'jack'},
             {'user_id':8, 'user_name':'ed'},
@@ -852,8 +852,8 @@ class QueryTest(TestBase):
     def test_cant_execute_join(self):
         try:
             users.join(addresses).execute()
-        except exc.ArgumentError, e:
-            assert str(e).startswith('Not an executable clause: ')
+        except exc.StatementError, e:
+            assert str(e).startswith('Not an executable clause ')