Permalink
Browse files

Recognize disconnections when issuing BEGIN/ROLLBACK/COMMIT statement…

…s (Fixes #368)

Add Database#disconnect_error? private method that raise_error calls
to determine whether to raise a DatabaseError or
DatabaseDisconnectError.  In some cases, the disconnect determination
depends on the exception, but in other cases it depends on the
connection, so pass the connection in a :conn option and
use that if available.

This also cleans up the handling of regular disconnects, since in
most cases you don't need to specify the :disconnect option manually
(though that still works).
  • Loading branch information...
1 parent 8e85783 commit 147847d8c66fa5ad91e5c46f2357e43e541d9045 @jeremyevans committed Jul 5, 2011
View
@@ -1,3 +1,7 @@
+=== HEAD
+
+* Recognize disconnections when issuing BEGIN/ROLLBACK/COMMIT statements (jeremyevans) (#368)
+
=== 3.25.0 (2011-07-01)
* Work with tiny_tds-0.4.5 in the tinytds adapter, older versions are no longer supported (jeremyevans)
@@ -285,6 +285,12 @@ def disconnect_connection(c)
c.close
end
+ # Raise a disconnect error if the SQL state of the cause of the exception indicates so.
+ def disconnect_error?(exception, opts)
+ cause = exception.respond_to?(:cause) ? exception.cause : exception
+ super || (cause.respond_to?(:getSQLState) && cause.getSQLState =~ /^08/)
+ end
+
# Execute the prepared statement. If the provided name is a
# dataset, use that as the prepared statement, otherwise use
# it as a key to look it up in the prepared_statements hash.
@@ -400,12 +406,6 @@ def metadata(*args, &block)
end
end
- # Treat SQLExceptions with a "Connection Error" SQLState as disconnects
- def raise_error(exception, opts={})
- cause = exception.respond_to?(:cause) ? exception.cause : exception
- super(exception, {:disconnect => cause.respond_to?(:getSQLState) && cause.getSQLState =~ /^08/}.merge(opts))
- end
-
# Java being java, you need to specify the type of each argument
# for the prepared statement, and bind it individually. This
# guesses which JDBC method to use, and hopefully JRuby will convert
@@ -213,14 +213,14 @@ def _execute(conn, sql, opts)
conn.next_result
r = conn.use_result
rescue Mysql::Error => e
- raise_error(e, :disconnect=>true) if MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message)
+ raise_error(e)
break
end
yield r if opts[:type] == :select
end
end
rescue Mysql::Error => e
- raise_error(e, :disconnect=>MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
+ raise_error(e)
ensure
r.free if r
# Use up all results to avoid a commands out of sync message.
@@ -230,7 +230,7 @@ def _execute(conn, sql, opts)
conn.next_result
r = conn.use_result
rescue Mysql::Error => e
- raise_error(e, :disconnect=>true) if MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message)
+ raise_error(e)
break
end
r.free if r
@@ -248,6 +248,12 @@ def connection_execute_method
def database_error_classes
[Mysql::Error]
end
+
+ # Raise a disconnect error if the exception message matches the list
+ # of recognized exceptions.
+ def disconnect_error?(e, opts)
+ super || (e.is_a?(::Mysql::Error) && MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
+ end
# The database name when using the native adapter is always stored in
# the :database option.
@@ -94,7 +94,7 @@ def _execute(conn, sql, opts)
yield conn
end
rescue ::Mysql2::Error => e
- raise_error(e, :disconnect=>MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
+ raise_error(e)
end
end
@@ -108,6 +108,10 @@ def database_error_classes
[::Mysql2::Error]
end
+ def disconnect_error?(e, opts)
+ super || (e.is_a?(::Mysql2::Error) && MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
+ end
+
# The database name when using the native adapter is always stored in
# the :database option.
def database_name
@@ -52,7 +52,7 @@ def execute(sql, opts={})
r = log_yield(sql){conn.run(sql)}
yield(r) if block_given?
rescue ::ODBC::Error, ArgumentError => e
- raise_error(e, :disconnect=>DISCONNECT_ERRORS.match(e.message))
+ raise_error(e)
ensure
r.drop if r
end
@@ -65,7 +65,7 @@ def execute_dui(sql, opts={})
begin
log_yield(sql){conn.do(sql)}
rescue ::ODBC::Error, ArgumentError => e
- raise_error(e, :disconnect=>DISCONNECT_ERRORS.match(e.message))
+ raise_error(e)
end
end
end
@@ -80,6 +80,10 @@ def connection_execute_method
def disconnect_connection(c)
c.disconnect
end
+
+ def disconnect_error?(e, opts)
+ super || (e.is_a?(::ODBC::Error) && DISCONNECT_ERRORS.match(e.message))
+ end
end
class Dataset < Sequel::Dataset
@@ -78,7 +78,7 @@ def execute(sql, opts={})
yield(r) if block_given?
r
rescue OCIException => e
- raise_error(e, :disconnect=>CONNECTION_ERROR_CODES.include?(e.code))
+ raise_error(e)
end
end
end
@@ -100,6 +100,10 @@ def disconnect_connection(c)
rescue OCIInvalidHandle
nil
end
+
+ def disconnect_error?(e, opts)
+ super || (e.is_a?(::OCIException) && CONNECTION_ERROR_CODES.include?(e.code))
+ end
def remove_transaction(conn)
conn.autocommit = true if conn
@@ -78,6 +78,10 @@ def column_list_sql(g)
def disconnect_connection(c)
c.close
end
+
+ def disconnect_error(e, opts)
+ super || (opts[:conn] && !opts[:conn].active?)
+ end
end
class Dataset < Sequel::Dataset
@@ -181,12 +181,17 @@ def blank_object?(obj)
def database_error_classes
[]
end
+
+ # Return true if exception represents a disconnect error, false otherwise.
+ def disconnect_error?(exception, opts)
+ opts[:disconnect]
+ end
# Convert the given exception to a DatabaseError, keeping message
# and traceback.
def raise_error(exception, opts={})
if !opts[:classes] || Array(opts[:classes]).any?{|c| exception.is_a?(c)}
- raise Sequel.convert_exception_class(exception, opts[:disconnect] ? DatabaseDisconnectError : DatabaseError)
+ raise Sequel.convert_exception_class(exception, disconnect_error?(exception, opts) ? DatabaseDisconnectError : DatabaseError)
else
raise exception
end
@@ -230,12 +230,12 @@ def _transaction(conn, opts={})
yield(conn)
rescue Exception => e
rollback_transaction(t, opts) if t
- transaction_error(e)
+ transaction_error(e, :conn=>conn)
ensure
begin
commit_or_rollback_transaction(e, t, opts)
rescue Exception => e
- raise_error(e, :classes=>database_error_classes)
+ raise_error(e, :classes=>database_error_classes, :conn=>conn)
ensure
remove_transaction(t)
end
@@ -490,8 +490,8 @@ def set_transaction_isolation_sql(level)
end
# Raise a database error unless the exception is an Rollback.
- def transaction_error(e)
- raise_error(e, :classes=>database_error_classes) unless e.is_a?(Rollback)
+ def transaction_error(e, opts={})
+ raise_error(e, opts.merge(:classes=>database_error_classes)) unless e.is_a?(Rollback)
end
end
end

0 comments on commit 147847d

Please sign in to comment.