Summary:
The DBI::Handle layer should trap and translate DB layer errors, using a conversion function registered by the DBD layer
(by the dbd-driver). See below for possible API.
Background:
Currently (dbi-0.4.3), each DBD is responsible for transforming its underlying DB layer exceptions into the appropriate
DBI::DatabaseError derivative, as the DBI layer passes on any exceptions.
DBDs typically must everywhere rescue the underlying SqlDriverError to re-raise its DBI analog, which means that some
error trapping is invariably omitted. For example, no dbd-mysql-0.4.3 ``Mysql::Database#__native_func'' methods wrap
mysql-specific errors. Further, many DBDs do not take full advantage of the DBI::DatabaseError class, setting only
a message but not setting the @err nor @state (SQLSTATE) members.
Possible API:
# lib/dbd/mockesscueell/driver.rb
# -- trap and translate MockSQLError
DBI::ErrUtil.register_error_conv(driver_name, MockSQLError) do |err, handle, method|
# 'err' - the MockSQLError exception object
# 'handle' - DBI::Handle derivative (e.g., StatementHandle) rescue'ing
# 'method' - name of the method in which the DBI is rescue'ing err
klass = case err.sqlstate
when /^08/
DBI::ConnectionError
...
return klass.new(err.message, err, err.sqlstate)
end
# lib/dbi/handles/statement.rb
def finish
@handle.finish
rescue
# will simply return $! unmodified if there's no conversion
raise DBI::ErrUtil.convert_error(self.dbh.driver_name, $!)
end
Other approaches:
One might also wrap or delegate the underlying DB handle, trapping and re-raising all errors there. However, at that
low level there'd be many unneeded rescue's which could be aggregated, and the error handler wouldn't necessarily know
the high-level operation in whose service it was ultimately called. (And this is somewhat slow in my testing!) |