diff --git a/CHANGELOG.md b/CHANGELOG.md index e81c9982..5ce0ddcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). ### Fixed - Fix msec values for time columns. #61 +### Changed + - All SystemCallErrors classified as `Trilogy::Error`. + ## 2.3.0 ### Added diff --git a/contrib/ruby/ext/trilogy-ruby/cext.c b/contrib/ruby/ext/trilogy-ruby/cext.c index 6ce4f2fe..6121b13a 100644 --- a/contrib/ruby/ext/trilogy-ruby/cext.c +++ b/contrib/ruby/ext/trilogy-ruby/cext.c @@ -17,14 +17,14 @@ VALUE Trilogy_CastError; static VALUE Trilogy_BaseConnectionError, Trilogy_ProtocolError, Trilogy_SSLError, Trilogy_QueryError, Trilogy_ConnectionClosedError, Trilogy_ConnectionRefusedError, Trilogy_ConnectionResetError, - Trilogy_TimeoutError, Trilogy_Result; + Trilogy_TimeoutError, Trilogy_SyscallError, Trilogy_Result; static ID id_socket, id_host, id_port, id_username, id_password, id_found_rows, id_connect_timeout, id_read_timeout, id_write_timeout, id_keepalive_enabled, id_keepalive_idle, id_keepalive_interval, id_keepalive_count, id_ivar_affected_rows, id_ivar_fields, id_ivar_last_insert_id, id_ivar_rows, id_ivar_query_time, id_password, id_database, id_ssl_ca, id_ssl_capath, id_ssl_cert, id_ssl_cipher, id_ssl_crl, id_ssl_crlpath, id_ssl_key, id_ssl_mode, id_tls_ciphersuites, id_tls_min_version, id_tls_max_version, id_multi_statement, id_multi_result, - id_from_code, id_connection_options; + id_from_code, id_from_errno, id_connection_options; struct trilogy_ctx { trilogy_conn_t conn; @@ -87,8 +87,8 @@ static void trilogy_syserr_fail_str(int e, VALUE msg) } else if (e == ECONNRESET) { rb_raise(Trilogy_ConnectionResetError, "%" PRIsVALUE, msg); } else { - // TODO: All syserr should be wrapped. - rb_syserr_fail_str(e, msg); + VALUE exc = rb_funcall(Trilogy_SyscallError, id_from_errno, 2, msg, INT2NUM(e)); + rb_exc_raise(exc); } } @@ -1065,6 +1065,9 @@ RUBY_FUNC_EXPORTED void Init_cext() Trilogy_Result = rb_const_get(Trilogy, rb_intern("Result")); rb_global_variable(&Trilogy_Result); + Trilogy_SyscallError = rb_const_get(Trilogy, rb_intern("SyscallError")); + rb_global_variable(&Trilogy_SyscallError); + Trilogy_CastError = rb_const_get(Trilogy, rb_intern("CastError")); rb_global_variable(&Trilogy_CastError); @@ -1096,6 +1099,7 @@ RUBY_FUNC_EXPORTED void Init_cext() id_multi_statement = rb_intern("multi_statement"); id_multi_result = rb_intern("multi_result"); id_from_code = rb_intern("from_code"); + id_from_errno = rb_intern("from_errno"); id_ivar_affected_rows = rb_intern("@affected_rows"); id_ivar_fields = rb_intern("@fields"); id_ivar_last_insert_id = rb_intern("@last_insert_id"); diff --git a/contrib/ruby/lib/trilogy.rb b/contrib/ruby/lib/trilogy.rb index b3dda50b..701b7fe4 100644 --- a/contrib/ruby/lib/trilogy.rb +++ b/contrib/ruby/lib/trilogy.rb @@ -13,6 +13,23 @@ module ConnectionError include Error end + # Trilogy may raise various syscall errors, which we treat as Trilogy::Errors. + class SyscallError + ERRORS = {} + + ObjectSpace.each_object(::Class).select { |klass| klass < SystemCallError }.each do |klass| + ERRORS[klass::Errno] = Class.new(klass) { include Trilogy::Error } + end + + ERRORS.freeze + + class << self + def from_errno(message, errno) + ERRORS[errno].new(message) + end + end + end + class BaseError < StandardError include Error @@ -41,29 +58,14 @@ class CastError < ClientError class TimeoutError < Errno::ETIMEDOUT include ConnectionError - - def initialize(error_message = nil, error_code = nil) - super - @error_code = error_code - end end class ConnectionRefusedError < Errno::ECONNREFUSED include ConnectionError - - def initialize(error_message = nil, error_code = nil) - super - @error_code = error_code - end end class ConnectionResetError < Errno::ECONNRESET include ConnectionError - - def initialize(error_message = nil, error_code = nil) - super - @error_code = error_code - end end # DatabaseError was replaced by ProtocolError, but we'll keep it around as an diff --git a/contrib/ruby/test/client_test.rb b/contrib/ruby/test/client_test.rb index 87ce137b..b339654f 100644 --- a/contrib/ruby/test/client_test.rb +++ b/contrib/ruby/test/client_test.rb @@ -607,6 +607,15 @@ def test_timeout_deadlines end end + def test_generic_syscall_error + err = assert_raises Trilogy::Error do + new_unix_client(File::NULL) + end + + assert_includes err.message, "Socket operation on non-socket" + assert err.class < Errno::ENOTSOCK + end + def test_timeout_error client_1 = new_tcp_client client_2 = new_tcp_client