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

Add functionality for setting server options #52

Merged
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
40 changes: 40 additions & 0 deletions contrib/ruby/ext/trilogy-ruby/cext.c
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,40 @@ static VALUE rb_trilogy_change_db(VALUE self, VALUE database)
return Qtrue;
}

static VALUE rb_trilogy_set_server_option(VALUE self, VALUE option)
{
struct trilogy_ctx *ctx = get_open_ctx(self);

int rc = trilogy_set_option_send(&ctx->conn, NUM2INT(option));

if (rc == TRILOGY_AGAIN) {
rc = flush_writes(ctx);
}

if (rc != TRILOGY_OK) {
handle_trilogy_error(ctx, rc, "trilogy_set_option_send");
}

while (1) {
rc = trilogy_set_option_recv(&ctx->conn);

if (rc == TRILOGY_OK) {
break;
}

if (rc != TRILOGY_AGAIN) {
handle_trilogy_error(ctx, rc, "trilogy_set_option_recv");
}

if (trilogy_sock_wait_read(ctx->conn.socket) < 0) {
rb_raise(Trilogy_TimeoutError, "trilogy_set_option_recv");
}
}

return Qtrue;
}


static void load_query_options(unsigned int query_flags, struct rb_trilogy_cast_options *cast_options)
{
cast_options->cast = (query_flags & TRILOGY_FLAGS_CAST) != 0;
Expand Down Expand Up @@ -989,6 +1023,7 @@ void Init_cext()
rb_define_method(Trilogy, "server_version", rb_trilogy_server_version, 0);
rb_define_method(Trilogy, "more_results_exist?", rb_trilogy_more_results_exist, 0);
rb_define_method(Trilogy, "next_result", rb_trilogy_next_result, 0);
rb_define_method(Trilogy, "set_server_option", rb_trilogy_set_server_option, 1);
rb_define_const(Trilogy, "TLS_VERSION_10", INT2NUM(TRILOGY_TLS_VERSION_10));
rb_define_const(Trilogy, "TLS_VERSION_11", INT2NUM(TRILOGY_TLS_VERSION_11));
rb_define_const(Trilogy, "TLS_VERSION_12", INT2NUM(TRILOGY_TLS_VERSION_12));
Expand Down Expand Up @@ -1081,4 +1116,9 @@ void Init_cext()
#define XX(name, code) rb_const_set(Trilogy, rb_intern((char *)#name + strlen("TRILOGY_")), LONG2NUM(name));
TRILOGY_SERVER_STATUS(XX)
#undef XX

// set_server_option options
#define XX(name, code) rb_const_set(Trilogy, rb_intern((char *)#name + strlen("TRILOGY_")), LONG2NUM(name));
TRILOGY_SET_SERVER_OPTION(XX)
#undef XX
}
51 changes: 51 additions & 0 deletions contrib/ruby/test/client_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,57 @@ def test_trilogy_query_values
assert_equal [1, 4, 2, 3, 3, 1], result.rows
end

def test_trilogy_set_server_option
client = new_tcp_client
create_test_table(client)

client.set_server_option(Trilogy::SET_SERVER_MULTI_STATEMENTS_ON)
client.set_server_option(Trilogy::SET_SERVER_MULTI_STATEMENTS_OFF)
end

def test_trilogy_set_server_option_with_invalid_option
client = new_tcp_client
create_test_table(client)

e = assert_raises do
client.set_server_option(42)
end

assert_instance_of(Trilogy::ProtocolError, e)
assert_match(/1047: Unknown command/, e.message)
end

def test_trilogy_set_server_option_multi_statement
# Start with multi_statement disabled, enable it during connection
client = new_tcp_client
create_test_table(client)

e = assert_raises do
client.query("INSERT INTO trilogy_test (int_test) VALUES ('4'); INSERT INTO trilogy_test (int_test) VALUES ('1')")
end

assert_instance_of(Trilogy::QueryError, e)
assert_match(/1064: You have an error in your SQL syntax/, e.message)

client.set_server_option(Trilogy::SET_SERVER_MULTI_STATEMENTS_ON)
client.query("INSERT INTO trilogy_test (int_test) VALUES ('4'); INSERT INTO trilogy_test (int_test) VALUES ('1')")
client.next_result while client.more_results_exist?

# Start with multi_statement enabled, disable it during connection
client = new_tcp_client(multi_statement: true)
create_test_table(client)
client.query("INSERT INTO trilogy_test (int_test) VALUES ('4'); INSERT INTO trilogy_test (int_test) VALUES ('1')")
client.next_result while client.more_results_exist?
client.set_server_option(Trilogy::SET_SERVER_MULTI_STATEMENTS_OFF)

e = assert_raises do
client.query("INSERT INTO trilogy_test (int_test) VALUES ('4'); INSERT INTO trilogy_test (int_test) VALUES ('1')")
end

assert_instance_of(Trilogy::QueryError, e)
assert_match(/1064: You have an error in your SQL syntax/, e.message)
end

def test_trilogy_query_result_object
client = new_tcp_client

Expand Down
19 changes: 18 additions & 1 deletion inc/trilogy/blocking.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ int trilogy_connect_sock(trilogy_conn_t *conn, trilogy_sock_t *sock);
/* trilogy_change_db - Change the default database for a connection.
*
* conn - A connected trilogy_conn_t pointer. Using a disconnected
* trilogy_conn_t is undefined. name - Name of the database to set as default.
* trilogy_conn_t is undefined.
* name - Name of the database to set as default.
* name_len - Length of the database name string in bytes.
*
* Return values
Expand All @@ -63,6 +64,22 @@ int trilogy_connect_sock(trilogy_conn_t *conn, trilogy_sock_t *sock);
*/
int trilogy_change_db(trilogy_conn_t *conn, const char *name, size_t name_len);

/* trilogy_set_option - Set server options for the connection.
*
* conn - A connected trilogy_conn_t pointer. Using a disconnected
* trilogy_conn_t is undefined.
* option - The server option to set. See: TRILOGY_SET_SERVER_OPTION_TYPE_t;
*
* Return values
* TRILOGY_OK - The change db command completed successfully.
* TRILOGY_ERR - The server returned an error.
* TRILOGY_SYSERR - A system error occurred, check errno.
* TRILOGY_CLOSED_CONNECTION - The connection is closed.
* TRILOGY_PROTOCOL_VIOLATION - An error occurred while processing a network
* packet.
*/
int trilogy_set_option(trilogy_conn_t *conn, const uint16_t option);

/* trilogy_query - Send and execute a query.
*
* conn - A connected trilogy_conn_t pointer. Using a disconnected
Expand Down
44 changes: 44 additions & 0 deletions inc/trilogy/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,50 @@ int trilogy_change_db_send(trilogy_conn_t *conn, const char *name, size_t name_l
*/
int trilogy_change_db_recv(trilogy_conn_t *conn);

/* trilogy_set_option_send - Send a set option command to the server. This
* will change server capabilities based on the option selected.
*
* This should only be called while the connection is ready for commands.
*
* conn - A connected trilogy_conn_t pointer. Using a disconnected
* trilogy_conn_t is undefined.
* option - The server option to send.
*
* Return values:
* TRILOGY_OK - The change database command was successfully sent to the
* server.
* TRILOGY_AGAIN - The socket wasn't ready for writing. The caller should wait
* for writeability using `conn->sock`. Then call
* trilogy_flush_writes.
* TRILOGY_SYSERR - A system error occurred, check errno.
*/
int trilogy_set_option_send(trilogy_conn_t *conn, const uint16_t option);

/* trilogy_set_option_recv - Read the set option command response from the
* server.
*
* This should be called after all data written by trilogy_set_option_send is
* flushed to the network. Calling this at any other time during the connection
* lifecycle is undefined.
*
* conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
* undefined.
*
* Return values:
* TRILOGY_OK - The set option command was successfully
* sent to the server.
* TRILOGY_AGAIN - The socket wasn't ready for reading. The
* caller should wait for readability using
* `conn->sock`. Then call this function until
* it returns a different value.
* TRILOGY_UNEXPECTED_PACKET - The response packet wasn't what was expected.
* TRILOGY_PROTOCOL_VIOLATION - An error occurred while processing a network
* packet.
* TRILOGY_CLOSED_CONNECTION - The connection is closed.
* TRILOGY_SYSERR - A system error occurred, check errno.
*/
int trilogy_set_option_recv(trilogy_conn_t *conn);

/* trilogy_query_send - Send a query command to the server.
*
* This should only be called while the connection is ready for commands.
Expand Down
26 changes: 25 additions & 1 deletion inc/trilogy/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,16 @@ typedef enum {
#undef XX
} TRILOGY_SESSION_TRACK_TYPE_t;

#define TRILOGY_SET_SERVER_OPTION(XX) \
XX(TRILOGY_SET_SERVER_MULTI_STATEMENTS_ON, 0x00) \
XX(TRILOGY_SET_SERVER_MULTI_STATEMENTS_OFF, 0x01) \

typedef enum {
#define XX(name, code) name = code,
TRILOGY_SET_SERVER_OPTION(XX)
#undef XX
} TRILOGY_SET_SERVER_OPTION_TYPE_t;

/* trilogy_build_auth_packet - Build a handshake response (or authentication)
* packet.
*
Expand Down Expand Up @@ -446,7 +456,7 @@ int trilogy_build_auth_switch_response_packet(trilogy_builder_t *builder, const
* command will change the default database for the connection.
*
* builder - A pointer to a pre-initialized trilogy_builder_t.
* name - The name of the databaset to set as the default.
* name - The name of the database to set as the default.
* name_len - The length of name in bytes.
*
* Return values:
Expand All @@ -456,6 +466,20 @@ int trilogy_build_auth_switch_response_packet(trilogy_builder_t *builder, const
*/
int trilogy_build_change_db_packet(trilogy_builder_t *builder, const char *name, size_t name_len);

/* trilogy_build_set_option_packet - Build a set option command packet. This
* command will enable/disable server capabilities for the connection. Options
* must be one of `enum_mysql_set_option`.
*
* builder - A pointer to a pre-initialized trilogy_builder_t.
* option - An integer corresponding to the operation to perform.
*
* Return values:
* TRILOGY_OK - The packet was successfully built and written to the
* builder's internal buffer.
* TRILOGY_SYSERR - A system error occurred, check errno.
*/
int trilogy_build_set_option_packet(trilogy_builder_t *builder, const uint16_t option);

/* trilogy_build_ping_packet - Build a ping command packet.
*
* builder - A pointer to a pre-initialized trilogy_builder_t.
Expand Down
23 changes: 23 additions & 0 deletions src/blocking.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,29 @@ int trilogy_change_db(trilogy_conn_t *conn, const char *name, size_t name_len)
}
}

int trilogy_set_option(trilogy_conn_t *conn, const uint16_t option)
{
int rc = trilogy_set_option_send(conn, option);

if (rc == TRILOGY_AGAIN) {
rc = flush_full(conn);
}

if (rc < 0) {
return rc;
}

while (1) {
rc = trilogy_set_option_recv(conn);

if (rc != TRILOGY_AGAIN) {
return rc;
}

CHECKED(trilogy_sock_wait_read(conn->socket));
}
}

int trilogy_ping(trilogy_conn_t *conn)
{
int rc = trilogy_ping_send(conn);
Expand Down
38 changes: 38 additions & 0 deletions src/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,44 @@ int trilogy_change_db_send(trilogy_conn_t *conn, const char *name, size_t name_l

int trilogy_change_db_recv(trilogy_conn_t *conn) { return read_generic_response(conn); }

int trilogy_set_option_send(trilogy_conn_t *conn, const uint16_t option)
{
trilogy_builder_t builder;
int err = begin_command_phase(&builder, conn, 0);
if (err < 0) {
return err;
}

err = trilogy_build_set_option_packet(&builder, option);

if (err < 0) {
return err;
}

return begin_write(conn);
}

int trilogy_set_option_recv(trilogy_conn_t *conn) {
int rc = read_packet(conn);

if (rc < 0) {
return rc;
}

switch (current_packet_type(conn)) {
case TRILOGY_PACKET_OK:
case TRILOGY_PACKET_EOF: // COM_SET_OPTION returns an EOF packet, but it should be treated as an OK packet.
return read_ok_packet(conn);

case TRILOGY_PACKET_ERR:
return read_err_packet(conn);

default:
return TRILOGY_UNEXPECTED_PACKET;
}
}


int trilogy_ping_send(trilogy_conn_t *conn)
{
trilogy_builder_t builder;
Expand Down
17 changes: 17 additions & 0 deletions src/protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define TRILOGY_CMD_CHANGE_DB 0x02
#define TRILOGY_CMD_QUERY 0x03
#define TRILOGY_CMD_PING 0x0e
#define TRILOGY_CMD_SET_OPTION 0x1b

#define SCRAMBLE_LEN 20

Expand Down Expand Up @@ -646,6 +647,22 @@ int trilogy_build_quit_packet(trilogy_builder_t *builder)
return rc;
}

int trilogy_build_set_option_packet(trilogy_builder_t *builder, const uint16_t option)
{
int rc = TRILOGY_OK;

CHECKED(trilogy_builder_write_uint8(builder, TRILOGY_CMD_SET_OPTION));
CHECKED(trilogy_builder_write_uint16(builder, option));

trilogy_builder_finalize(builder);

return TRILOGY_OK;

fail:
return rc;
}


int trilogy_build_ssl_request_packet(trilogy_builder_t *builder, TRILOGY_CAPABILITIES_t flags)
{
static const char zeroes[23] = {0};
Expand Down
19 changes: 19 additions & 0 deletions test/blocking_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,24 @@ TEST test_blocking_change_db()
PASS();
}

TEST test_blocking_set_option()
{
trilogy_conn_t conn;

connect_conn(&conn);

const uint16_t option = 1;

int err = trilogy_set_option(&conn, option);
ASSERT_OK(err);

err = trilogy_close(&conn);
ASSERT_OK(err);

trilogy_free(&conn);
PASS();
}

TEST test_blocking_ping()
{
trilogy_conn_t conn;
Expand Down Expand Up @@ -151,6 +169,7 @@ int blocking_test()
{
RUN_TEST(test_blocking_connect);
RUN_TEST(test_blocking_change_db);
RUN_TEST(test_blocking_set_option);
RUN_TEST(test_blocking_ping);
RUN_TEST(test_blocking_query);
RUN_TEST(test_blocking_query_error);
Expand Down
Loading