Skip to content

Commit

Permalink
Merge pull request #63 from github/ac-syscall-errs-inherit-from-trilo…
Browse files Browse the repository at this point in the history
…gy-error

Raise all SystemCallErrors as `Trilogy::Error`
  • Loading branch information
adrianna-chang-shopify authored Apr 14, 2023
2 parents 973f981 + c104e5c commit ca99504
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
Trilogy client can now accept an `:encoding` option, which will tell the connection to use the specified encoding,
and will ensure that outgoing query strings are transcoded appropriately. If no encoding is supplied,
utf8mb4 is used by default. #64
- All SystemCallErrors classified as `Trilogy::Error`.

## 2.3.0

Expand Down
12 changes: 8 additions & 4 deletions contrib/ruby/ext/trilogy-ruby/cext.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,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;
Expand Down Expand Up @@ -97,8 +97,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, INT2NUM(e), msg);
rb_exc_raise(exc);
}
}

Expand Down Expand Up @@ -1099,6 +1099,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);

Expand Down Expand Up @@ -1130,6 +1133,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");
Expand Down
36 changes: 21 additions & 15 deletions contrib/ruby/lib/trilogy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,27 @@ module ConnectionError
include Error
end

# Trilogy may raise various syscall errors, which we treat as Trilogy::Errors.
class SyscallError
ERRORS = {}

Errno.constants
.map { |c| Errno.const_get(c) }.uniq
.select { |c| c.is_a?(Class) && c < SystemCallError }
.each do |c|
errno_name = c.to_s.split('::').last
ERRORS[c::Errno] = const_set(errno_name, Class.new(c) { include Trilogy::Error })
end

ERRORS.freeze

class << self
def from_errno(errno, message)
ERRORS[errno].new(message)
end
end
end

class BaseError < StandardError
include Error

Expand Down Expand Up @@ -43,29 +64,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
Expand Down

0 comments on commit ca99504

Please sign in to comment.