Skip to content

Commit f9ee855

Browse files
Trilogy::ProtocolError should use SQL codes to determine error class
Instead of raising all ProtocolErrors as connection errors, we use the SQL error code to determine the type of error to raise when handling the Trilogy error in the Ruby bindings. The mapping of error codes to error classes was taken from the mysql2 implementation: https://github.com/brianmario/mysql2/blob/master/lib/mysql2/error.rb#L13-L27 Note that we don't need to care about the 2xxx errors because those are raised by the libmysql client, which Trilogy doesn't deal with. Co-authored-by: Jean Boussier <[email protected]>
1 parent 9db5431 commit f9ee855

File tree

3 files changed

+53
-13
lines changed

3 files changed

+53
-13
lines changed

contrib/ruby/ext/trilogy-ruby/cext.c

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ static ID id_socket, id_host, id_port, id_username, id_password, id_found_rows,
2121
id_write_timeout, id_keepalive_enabled, id_keepalive_idle, id_keepalive_interval, id_keepalive_count,
2222
id_ivar_affected_rows, id_ivar_fields, id_ivar_last_insert_id, id_ivar_rows, id_ivar_query_time, id_password,
2323
id_database, id_ssl_ca, id_ssl_capath, id_ssl_cert, id_ssl_cipher, id_ssl_crl, id_ssl_crlpath, id_ssl_key,
24-
id_ssl_mode, id_tls_ciphersuites, id_tls_min_version, id_tls_max_version, id_multi_statement;
24+
id_ssl_mode, id_tls_ciphersuites, id_tls_min_version, id_tls_max_version, id_multi_statement, id_from_code;
2525

2626
struct trilogy_ctx {
2727
trilogy_conn_t conn;
@@ -91,12 +91,7 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms
9191

9292
case TRILOGY_ERR: {
9393
VALUE message = rb_str_new(ctx->conn.error_message, ctx->conn.error_message_len);
94-
VALUE exc = rb_exc_new3(Trilogy_ProtocolError,
95-
rb_sprintf("%" PRIsVALUE ": %d %" PRIsVALUE, rbmsg, ctx->conn.error_code, message));
96-
97-
rb_ivar_set(exc, rb_intern("@error_code"), INT2FIX(ctx->conn.error_code));
98-
rb_ivar_set(exc, rb_intern("@error_message"), message);
99-
94+
VALUE exc = rb_funcall(Trilogy_ProtocolError, id_from_code, 2, message, INT2NUM(ctx->conn.error_code));
10095
rb_exc_raise(exc);
10196
}
10297

@@ -1035,7 +1030,7 @@ void Init_cext()
10351030
id_tls_min_version = rb_intern("tls_min_version");
10361031
id_tls_max_version = rb_intern("tls_max_version");
10371032
id_multi_statement = rb_intern("multi_statement");
1038-
1033+
id_from_code = rb_intern("from_code");
10391034
id_ivar_affected_rows = rb_intern("@affected_rows");
10401035
id_ivar_fields = rb_intern("@fields");
10411036
id_ivar_last_insert_id = rb_intern("@last_insert_id");

contrib/ruby/lib/trilogy.rb

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,42 @@ class QueryError < ClientError
2828
class CastError < ClientError
2929
end
3030

31-
class ProtocolError < BaseError
31+
32+
class TimeoutError < Errno::ETIMEDOUT
3233
include ConnectionError
34+
end
35+
36+
class ProtocolError < BaseError
37+
ERROR_CODES = {
38+
1205 => TimeoutError, # ER_LOCK_WAIT_TIMEOUT
39+
1044 => ConnectionError, # ER_DBACCESS_DENIED_ERROR
40+
1045 => ConnectionError, # ER_ACCESS_DENIED_ERROR
41+
1152 => ConnectionError, # ER_ABORTING_CONNECTION
42+
1153 => ConnectionError, # ER_NET_PACKET_TOO_LARGE
43+
1154 => ConnectionError, # ER_NET_READ_ERROR_FROM_PIPE
44+
1155 => ConnectionError, # ER_NET_FCNTL_ERROR
45+
1156 => ConnectionError, # ER_NET_PACKETS_OUT_OF_ORDER
46+
1157 => ConnectionError, # ER_NET_UNCOMPRESS_ERROR
47+
1158 => ConnectionError, # ER_NET_READ_ERROR
48+
1159 => ConnectionError, # ER_NET_READ_INTERRUPTED
49+
1160 => ConnectionError, # ER_NET_ERROR_ON_WRITE
50+
1161 => ConnectionError, # ER_NET_WRITE_INTERRUPTED
51+
1927 => ConnectionError, # ER_CONNECTION_KILLED
52+
}
3353

3454
attr_reader :error_code, :error_message
55+
56+
class << self
57+
def from_code(message, code)
58+
ERROR_CODES.fetch(code, self).new(message, code)
59+
end
60+
end
61+
62+
def initialize(error_message, error_code)
63+
super("#{error_code}: #{error_message}")
64+
@error_code = error_code
65+
@error_message = error_message
66+
end
3567
end
3668

3769
class SSLError < BaseError
@@ -42,10 +74,6 @@ class ConnectionClosed < IOError
4274
include ConnectionError
4375
end
4476

45-
class TimeoutError < Errno::ETIMEDOUT
46-
include ConnectionError
47-
end
48-
4977
def in_transaction?
5078
(server_status & SERVER_STATUS_IN_TRANS) != 0
5179
end

contrib/ruby/test/client_test.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,23 @@ def test_timeout_deadlines
511511
end
512512
end
513513

514+
def test_timeout_error
515+
client_1 = new_tcp_client
516+
client_2 = new_tcp_client
517+
518+
create_test_table(client_1)
519+
client_2.change_db("test")
520+
521+
client_1.query("SET GLOBAL innodb_lock_wait_timeout = 2;")
522+
client_1.query("INSERT INTO trilogy_test (varchar_test) VALUES ('a')")
523+
client_1.query("BEGIN")
524+
client_1.query("SELECT * FROM trilogy_test FOR UPDATE")
525+
526+
assert_raises Trilogy::TimeoutError do
527+
client_2.query("SELECT * FROM trilogy_test FOR UPDATE")
528+
end
529+
end
530+
514531
def test_database_error
515532
client = new_tcp_client
516533

0 commit comments

Comments
 (0)