Skip to content

Commit

Permalink
Support setting server options dynamically
Browse files Browse the repository at this point in the history
This will be particularly useful for enabling/disabling multi statements
on the fly, after a connection has already been initialized.
  • Loading branch information
paarthmadan authored and HParker committed Mar 16, 2023
1 parent e2b4c7d commit ff04c1c
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 0 deletions.
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
14 changes: 14 additions & 0 deletions inc/trilogy/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,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
19 changes: 19 additions & 0 deletions src/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,25 @@ 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) { return read_generic_response(conn); }

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 0x1a

#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
126 changes: 126 additions & 0 deletions test/client/set_option_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "../test.h"

#include "trilogy/blocking.h"
#include "trilogy/client.h"
#include "trilogy/error.h"

#define do_connect(CONN) \
do { \
int err = trilogy_init(CONN); \
ASSERT_OK(err); \
err = trilogy_connect(CONN, get_connopt()); \
ASSERT_OK(err); \
} while (0)

TEST test_set_option_send()
{
trilogy_conn_t conn;

do_connect(&conn);

const uint16_t option = 1;

int err = trilogy_set_option_send(&conn, option);
while (err == TRILOGY_AGAIN) {
err = wait_writable(&conn);
ASSERT_OK(err);

err = trilogy_flush_writes(&conn);
}
ASSERT_OK(err);

trilogy_free(&conn);
PASS();
}

TEST test_set_option_send_closed_socket()
{
trilogy_conn_t conn;

do_connect(&conn);

close_socket(&conn);

const uint16_t option = 1;

int err = trilogy_set_option_send(&conn, option);
ASSERT_ERR(TRILOGY_SYSERR, err);

trilogy_free(&conn);
PASS();
}

TEST test_set_option_recv()
{
trilogy_conn_t conn;

do_connect(&conn);

const uint16_t option = 0;

int err = trilogy_set_option_send(&conn, option);
while (err == TRILOGY_AGAIN) {
err = wait_writable(&conn);
ASSERT_OK(err);

err = trilogy_flush_writes(&conn);
}
ASSERT_OK(err);

err = trilogy_set_option_recv(&conn);
while (err == TRILOGY_AGAIN) {
err = wait_readable(&conn);
ASSERT_OK(err);

err = trilogy_set_option_recv(&conn);
}

printf("%d %s\n", conn.error_code, conn.error_message);

ASSERT_OK(err); // TODO: Figure out why this assertion fails...

trilogy_free(&conn);
PASS();
}

TEST test_set_option_recv_closed_socket()
{
trilogy_conn_t conn;

do_connect(&conn);

const uint16_t option = 1;

int err = trilogy_set_option_send(&conn, option);
while (err == TRILOGY_AGAIN) {
err = wait_writable(&conn);
ASSERT_OK(err);

err = trilogy_flush_writes(&conn);
}
ASSERT_OK(err);

close_socket(&conn);

err = trilogy_set_option_recv(&conn);
ASSERT_ERR(TRILOGY_SYSERR, err);

trilogy_free(&conn);
PASS();
}

int client_set_option_test()
{
RUN_TEST(test_set_option_send);
RUN_TEST(test_set_option_send_closed_socket);
RUN_TEST(test_set_option_recv);
RUN_TEST(test_set_option_recv_closed_socket);

return 0;
}
39 changes: 39 additions & 0 deletions test/protocol/building/set_option_packet_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "../../test.h"

#include "trilogy/error.h"
#include "trilogy/protocol.h"

TEST test_build_set_option_packet()
{
trilogy_builder_t builder;
trilogy_buffer_t buff;

int err = trilogy_buffer_init(&buff, 1);
ASSERT_OK(err);

err = trilogy_builder_init(&builder, &buff, 0);
ASSERT_OK(err);

const uint16_t option = 1;

err = trilogy_build_set_option_packet(&builder, option);
ASSERT_OK(err);

static const uint8_t expected[] = {0x03, 0x00, 0x00, 0x00, 0x1a, 0x01, 0x00};

ASSERT_MEM_EQ(buff.buff, expected, buff.len);

trilogy_buffer_free(&buff);
PASS();
}

int build_set_option_packet_test()
{
RUN_TEST(test_build_set_option_packet);

return 0;
}
2 changes: 2 additions & 0 deletions test/runner.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ const trilogy_sockopt_t *get_connopt(void) { return &connopt; }
SUITE(build_change_db_packet_test) \
SUITE(build_ping_packet_test) \
SUITE(build_quit_packet_test) \
SUITE(build_set_option_packet_test) \
SUITE(build_query_packet_test) \
SUITE(client_connect_test) \
SUITE(client_escape_test) \
SUITE(client_auth_test) \
SUITE(client_change_db_test) \
SUITE(client_set_option_test) \
SUITE(client_ping_test)

#define XX(name) extern int name();
Expand Down

0 comments on commit ff04c1c

Please sign in to comment.