diff --git a/contrib/ruby/ext/trilogy-ruby/cext.c b/contrib/ruby/ext/trilogy-ruby/cext.c index f86101a0..538740ba 100644 --- a/contrib/ruby/ext/trilogy-ruby/cext.c +++ b/contrib/ruby/ext/trilogy-ruby/cext.c @@ -27,7 +27,7 @@ static ID id_socket, id_host, id_port, id_username, id_password, id_found_rows, 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_from_errno, id_connection_options; + id_from_code, id_from_errno, id_connection_options, id_max_allowed_packet; struct trilogy_ctx { trilogy_conn_t conn; @@ -139,6 +139,10 @@ static void handle_trilogy_error(struct trilogy_ctx *ctx, int rc, const char *ms rb_raise(Trilogy_BaseConnectionError, "%" PRIsVALUE ": TRILOGY_DNS_ERROR", rbmsg); } + case TRILOGY_MAX_PACKET_EXCEEDED: { + rb_raise(Trilogy_QueryError, "%" PRIsVALUE ": TRILOGY_MAX_PACKET_EXCEEDED", rbmsg); + } + default: rb_raise(Trilogy_QueryError, "%" PRIsVALUE ": %s", rbmsg, trilogy_error(rc)); } @@ -415,6 +419,11 @@ static VALUE rb_trilogy_initialize(VALUE self, VALUE encoding, VALUE charset, VA connopt.keepalive_interval = NUM2USHORT(val); } + if ((val = rb_hash_lookup(opts, ID2SYM(id_max_allowed_packet))) != Qnil) { + Check_Type(val, T_FIXNUM); + connopt.max_allowed_packet = NUM2SIZET(val); + } + if ((val = rb_hash_lookup(opts, ID2SYM(id_host))) != Qnil) { Check_Type(val, T_STRING); @@ -1114,6 +1123,7 @@ RUBY_FUNC_EXPORTED void Init_cext() id_connect_timeout = rb_intern("connect_timeout"); id_read_timeout = rb_intern("read_timeout"); id_write_timeout = rb_intern("write_timeout"); + id_max_allowed_packet = rb_intern("max_allowed_packet"); id_keepalive_enabled = rb_intern("keepalive_enabled"); id_keepalive_idle = rb_intern("keepalive_idle"); id_keepalive_count = rb_intern("keepalive_count"); diff --git a/contrib/ruby/test/client_test.rb b/contrib/ruby/test/client_test.rb index 92eebafc..81c98fa0 100644 --- a/contrib/ruby/test/client_test.rb +++ b/contrib/ruby/test/client_test.rb @@ -745,6 +745,15 @@ def test_connect_by_multiple_names Trilogy.new(host: "localhost") end + PADDED_QUERY_TEMPLATE = "SELECT LENGTH('%s')" + PROTOCOL_OVERHEAD = 2 # One byte for the 0x03 (COM_QUERY); one because the packet is actually required to be shorter than the "max" + PADDED_QUERY_OVERHEAD = + PADDED_QUERY_TEMPLATE.size - "%s".size + PROTOCOL_OVERHEAD + + def query_for_target_packet_size(size) + PADDED_QUERY_TEMPLATE % ("x" * (size - PADDED_QUERY_OVERHEAD)) + end + def set_max_allowed_packet(size) client = new_tcp_client client.query "SET GLOBAL max_allowed_packet = #{size}" @@ -752,21 +761,100 @@ def set_max_allowed_packet(size) ensure_closed client end - def test_packet_size + def test_packet_size_lower_than_trilogy_max_packet_len + set_max_allowed_packet(4 * 1024 * 1024) # TRILOGY_MAX_PACKET_LEN is 16MB + + client = new_tcp_client(max_allowed_packet: 4 * 1024 * 1024) + + assert client.query query_for_target_packet_size(1 * 1024 * 1024) + + assert client.query query_for_target_packet_size(2 * 1024 * 1024) + + assert client.query query_for_target_packet_size(4 * 1024 * 1024) + + exception = assert_raises Trilogy::QueryError do + client.query query_for_target_packet_size(4 * 1024 * 1024 + 1) + end + + assert_equal exception.message, "trilogy_query_send: TRILOGY_MAX_PACKET_EXCEEDED" + ensure + ensure_closed client + end + + def test_packet_size_greater_than_trilogy_max_packet_len + set_max_allowed_packet(32 * 1024 * 1024) # TRILOGY_MAX_PACKET_LEN is 16MB + + client = new_tcp_client(max_allowed_packet: 32 * 1024 * 1024) + + assert client.query query_for_target_packet_size(16 * 1024 * 1024) + + assert client.query query_for_target_packet_size(32 * 1024 * 1024) + + exception = assert_raises Trilogy::QueryError do + client.query query_for_target_packet_size(32 * 1024 * 1024 + 1) + end + + assert_equal exception.message, "trilogy_query_send: TRILOGY_MAX_PACKET_EXCEEDED" + ensure + ensure_closed client + end + + def test_configured_max_packet_below_server set_max_allowed_packet(32 * 1024 * 1024) - client = new_tcp_client + client = new_tcp_client(max_allowed_packet: 24 * 1024 * 1024) - create_test_table(client) - client.query "TRUNCATE trilogy_test" + assert client.query query_for_target_packet_size(16 * 1024 * 1024) - result = client.query "INSERT INTO trilogy_test (blob_test) VALUES ('#{"x" * (15 * 1024 * 1024)}')" - assert result - assert_equal 1, client.last_insert_id + assert client.query query_for_target_packet_size(24 * 1024 * 1024) - result = client.query "INSERT INTO trilogy_test (blob_test) VALUES ('#{"x" * (31 * 1024 * 1024)}')" - assert result - assert_equal 2, client.last_insert_id + exception = assert_raises Trilogy::QueryError do + client.query query_for_target_packet_size(24 * 1024 * 1024 + 1) + end + + assert_equal exception.message, "trilogy_query_send: TRILOGY_MAX_PACKET_EXCEEDED" + ensure + ensure_closed client + end + + def test_configured_max_packet_above_server + set_max_allowed_packet(24 * 1024 * 1024) + + client = new_tcp_client(max_allowed_packet: 32 * 1024 * 1024) + + assert client.query query_for_target_packet_size(16 * 1024 * 1024) + + assert client.query query_for_target_packet_size(24 * 1024 * 1024) + + exception = assert_raises Trilogy::QueryError do + client.query query_for_target_packet_size(32 * 1024 * 1024 + 1) + end + + assert_equal exception.message, "trilogy_query_send: TRILOGY_MAX_PACKET_EXCEEDED" + + exception = assert_raises Trilogy::QueryError do + client.query query_for_target_packet_size(24 * 1024 * 1024 + 1) + end + + refute_match exception.message, /TRILOGY_MAX_PACKET_EXCEEDED/ + ensure + ensure_closed client + end + + def test_absolute_maximum_packet_size + skip unless ENV["CI"] + + set_max_allowed_packet(1024 * 1024 * 1024) # 1GB is the highest maximum allowed + + client = new_tcp_client(max_allowed_packet: 1024 * 1024 * 1024) + + assert client.query query_for_target_packet_size(1024 * 1024 * 1024) + + exception = assert_raises Trilogy::QueryError do + client.query query_for_target_packet_size(1024 * 1024 * 1024 + 1) + end + + assert_equal exception.message, "trilogy_query_send: TRILOGY_MAX_PACKET_EXCEEDED" ensure ensure_closed client end