Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error class revamp (part 2) #143

Merged
merged 5 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 3 additions & 13 deletions contrib/ruby/ext/trilogy-ruby/cext.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

VALUE Trilogy_CastError;
static VALUE Trilogy_BaseConnectionError, Trilogy_ProtocolError, Trilogy_SSLError, Trilogy_QueryError,
Trilogy_ConnectionClosedError, Trilogy_ConnectionRefusedError, Trilogy_ConnectionResetError,
Trilogy_ConnectionClosedError,
Trilogy_TimeoutError, Trilogy_SyscallError, Trilogy_Result, Trilogy_EOFError;

static ID id_socket, id_host, id_port, id_username, id_password, id_found_rows, id_connect_timeout, id_read_timeout,
Expand Down Expand Up @@ -90,12 +90,8 @@ static struct trilogy_ctx *get_open_ctx(VALUE obj)
NORETURN(static void trilogy_syserr_fail_str(int, VALUE));
static void trilogy_syserr_fail_str(int e, VALUE msg)
{
if (e == ECONNREFUSED) {
rb_raise(Trilogy_ConnectionRefusedError, "%" PRIsVALUE, msg);
} else if (e == ECONNRESET) {
rb_raise(Trilogy_ConnectionResetError, "%" PRIsVALUE, msg);
} else if (e == EPIPE) {
// Backwards compatibility: This error class makes no sense, but matches legacy behavior
if (e == EPIPE) {
// Backwards compatibility: This error message is a bit odd, but includes "TRILOGY_CLOSED_CONNECTION" to match legacy string matching
rb_raise(Trilogy_EOFError, "%" PRIsVALUE ": TRILOGY_CLOSED_CONNECTION: EPIPE", msg);
} else {
VALUE exc = rb_funcall(Trilogy_SyscallError, id_from_errno, 2, INT2NUM(e), msg);
Expand Down Expand Up @@ -1158,12 +1154,6 @@ RUBY_FUNC_EXPORTED void Init_cext()
Trilogy_TimeoutError = rb_const_get(Trilogy, rb_intern("TimeoutError"));
rb_global_variable(&Trilogy_TimeoutError);

Trilogy_ConnectionRefusedError = rb_const_get(Trilogy, rb_intern("ConnectionRefusedError"));
rb_global_variable(&Trilogy_ConnectionRefusedError);

Trilogy_ConnectionResetError = rb_const_get(Trilogy, rb_intern("ConnectionResetError"));
rb_global_variable(&Trilogy_ConnectionResetError);

Trilogy_BaseConnectionError = rb_const_get(Trilogy, rb_intern("BaseConnectionError"));
rb_global_variable(&Trilogy_BaseConnectionError);

Expand Down
28 changes: 11 additions & 17 deletions contrib/ruby/lib/trilogy/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@ module ConnectionError
end

# Trilogy may raise various syscall errors, which we treat as Trilogy::Errors.
class SyscallError
module 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::ConnectionError })
ERRORS[c::Errno] = const_set(errno_name, Class.new(c) {
include Trilogy::ConnectionError
singleton_class.define_method(:===, Module.instance_method(:===))
})
end

ERRORS.freeze
Expand All @@ -32,6 +35,11 @@ def from_errno(errno, message)
end
end

ConnectionRefusedError = SyscallError::ECONNREFUSED
deprecate_constant :ConnectionRefusedError
ConnectionResetError = SyscallError::ECONNRESET
deprecate_constant :ConnectionResetError

class BaseError < StandardError
include Error

Expand All @@ -58,21 +66,7 @@ class QueryError < ClientError
class CastError < ClientError
end

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
end

class ConnectionResetError < Errno::ECONNRESET
include ConnectionError
class TimeoutError < BaseConnectionError
end

# DatabaseError was replaced by ProtocolError, but we'll keep it around as an
Expand Down
14 changes: 13 additions & 1 deletion contrib/ruby/test/client_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ def test_trilogy_connect_unix_socket

socket = new_tcp_client.query("SHOW VARIABLES LIKE 'socket'").to_a[0][1]

assert File.exist?(socket), "cound not find socket at #{socket}"
if !File.exist?(socket)
skip "cound not find socket at #{socket}"
end

client = new_unix_client(socket)
refute_nil client
Expand Down Expand Up @@ -1097,4 +1099,14 @@ def test_connection_options_casting

assert client.query("SELECT 1")
end

def test_error_classes_exclusively_match_subclasses
klass = Trilogy::SyscallError::ECONNRESET
assert_operator klass, :===, klass.new
refute_operator klass, :===, Errno::ECONNRESET.new

assert_operator Errno::ECONNRESET, :===, klass.new
assert_operator SystemCallError, :===, klass.new
assert_operator Trilogy::ConnectionError, :===, klass.new
end
end