Skip to content

Commit

Permalink
Merge pull request #3 from brianmario/binary-protocol
Browse files Browse the repository at this point in the history
Add binary protocol/prepared statement support
  • Loading branch information
composerinteralia authored Jun 14, 2023
2 parents 5a7eb6a + 7fe04a4 commit c359ac1
Show file tree
Hide file tree
Showing 24 changed files with 3,438 additions and 3 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ It's currently in production use on github.com.
* Password authentication
* Query, ping, and quit commands

* Support prepared statements (binary protocol)

* Low-level protocol API completely decoupled from IO

* Non-blocking client API wrapping the protocol API
Expand All @@ -25,7 +27,7 @@ It's currently in production use on github.com.

## Limitations

* Only supports the parts of the text protocol that are in common use. There's no support for the binary protocol or prepared statements
* Only supports the parts of the text protocol that are in common use.

* No support for `LOAD DATA INFILE` on local files

Expand Down
118 changes: 118 additions & 0 deletions inc/trilogy/blocking.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,122 @@ int trilogy_ping(trilogy_conn_t *conn);
*/
int trilogy_close(trilogy_conn_t *conn);

/* trilogy_stmt_prepare - Send a prepared statement prepare command to the server.
*
* conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
* undefined.
* stmt - A pointer to the buffer containing the statement to prepare.
* stmt_len - The length of the data buffer.
* stmt_out - A pointer to a pre-allocated trilogy_stmt_t.
*
* Return values:
* TRILOGY_OK - The prepare command was successfully sent to the server.
* TRILOGY_UNEXPECTED_PACKET - The response packet wasn't what was expected.
* TRILOGY_PROTOCOL_VIOLATION - An error occurred while processing a network
* packet.
* TRILOGY_SYSERR - A system error occurred, check errno.
* TRILOGY_CLOSED_CONNECTION - The connection is closed.
*/
int trilogy_stmt_prepare(trilogy_conn_t *conn, const char *stmt, size_t stmt_len, trilogy_stmt_t *stmt_out);

/* trilogy_stmt_execute - Send a prepared statement execute command to the server.
*
* conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
* undefined.
* stmt - Pointer to a valid trilogy_stmt_t, representing the prepared statement you're
* requesting to execute.
* flags - The flags (TRILOGY_STMT_FLAGS_t) to be used with this execute command packet.
* binds - Pointer to an array of trilogy_binary_value_t's. The array size should
* match that of `trilogy_stmt_t.column_count`.
* column_count_out - Out parameter; A pointer to a pre-allocated uint64_t. Represents the
* number of columns in the response.
*
* Return values:
* TRILOGY_OK - The execute command was successfully sent to the server.
* TRILOGY_UNEXPECTED_PACKET - The response packet wasn't what was expected.
* TRILOGY_PROTOCOL_VIOLATION - An error occurred while processing a network
* packet.
* TRILOGY_SYSERR - A system error occurred, check errno.
* TRILOGY_CLOSED_CONNECTION - The connection is closed.
*/
int trilogy_stmt_execute(trilogy_conn_t *conn, trilogy_stmt_t *stmt, uint8_t flags, trilogy_binary_value_t *binds,
uint64_t *column_count_out);

/* trilogy_stmt_bind_data - Send a prepared statement bind long data command to the server.
*
* conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
* undefined.
* stmt - Pointer to a valid trilogy_stmt_t, representing the prepared statement for which
* to bind the supplied parameter data to.
* param_num - The parameter index for which the supplied data should be bound to.
* data - A pointer to the buffer containing the data to be bound.
* data_len - The length of the data buffer.
*
* Return values:
* TRILOGY_OK - The bind data command was successfully sent to the server.
* TRILOGY_SYSERR - A system error occurred, check errno.
*/
int trilogy_stmt_bind_data(trilogy_conn_t *conn, trilogy_stmt_t *stmt, uint16_t param_num, uint8_t *data,
size_t data_len);

/* trilogy_stmt_read_full_row - Read a row from the prepared statement execute response.
*
* This should only be called after a sucessful call to trilogy_stmt_execute.
* You should continue calling this until TRILOGY_EOF is returned. Denoting the end
* of the result set.
*
* conn - A pre-initialized trilogy_conn_t pointer. It can also be connected but
* a disconnected trilogy_conn_t will also return TRILOGY_OK.
* stmt - Pointer to a valid trilogy_stmt_t, representing the prepared statement you're
* requesting to execute.
* columns - The list of columns from the prepared statement.
* column_count - The number of columns in prepared statement.
* values_out - Out parameter; A pointer to a pre-allocated array of
* trilogy_binary_value_t's. There must be enough space to fit all of the
* values. This can be computed with:
* `(sizeof(trilogy_binary_value_t) * column_count)`.
*
* Return values:
* TRILOGY_OK - The prepare command response successfully read from
* the server.
* TRILOGY_EOF - There are no more rows to read from the result set.
* TRILOGY_UNEXPECTED_PACKET - The response packet wasn't what was expected.
* TRILOGY_PROTOCOL_VIOLATION - Invalid length parsed for a TIME/DATETIME/TIMESTAMP value.
* TRILOGY_UNKNOWN_TYPE - An unsupported or unknown MySQL type was seen.
* TRILOGY_SYSERR - A system error occurred, check errno.
* TRILOGY_CLOSED_CONNECTION - The connection is closed.
*/
int trilogy_stmt_read_full_row(trilogy_conn_t *conn, trilogy_stmt_t *stmt, trilogy_column_packet_t *columns,
trilogy_binary_value_t *values_out);

/* trilogy_stmt_reset - Send a prepared statement reset command to the server.
*
* conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
* undefined.
* stmt - Pointer to a valid trilogy_stmt_t, representing the prepared statement you're
* requesting to reset.
*
* Return values:
* TRILOGY_OK - The reset command was successfully sent to the server.
* TRILOGY_UNEXPECTED_PACKET - The response packet wasn't what was expected.
* TRILOGY_PROTOCOL_VIOLATION - An error occurred while processing a network
* packet.
* TRILOGY_SYSERR - A system error occurred, check errno.
* TRILOGY_CLOSED_CONNECTION - The connection is closed.
*/
int trilogy_stmt_reset(trilogy_conn_t *conn, trilogy_stmt_t *stmt);

/* trilogy_stmt_close_send - Send a prepared statement close command to the server.
*
* conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
* undefined.
* stmt - Pointer to a valid trilogy_stmt_t, representing the prepared statement you're
* requesting to close.
*
* Return values:
* TRILOGY_OK - The close command was successfully sent to the server.
* TRILOGY_SYSERR - A system error occurred, check errno.
*/
int trilogy_stmt_close(trilogy_conn_t *conn, trilogy_stmt_t *stmt);

#endif
26 changes: 26 additions & 0 deletions inc/trilogy/builder.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,32 @@ int trilogy_builder_write_uint32(trilogy_builder_t *builder, uint32_t val);
*/
int trilogy_builder_write_uint64(trilogy_builder_t *builder, uint64_t val);

/* trilogy_builder_write_float - Append a float to the packet buffer.
*
* builder - A pre-initialized trilogy_builder_t pointer
* val - The value to append to the buffer
*
* Return values:
* TRILOGY_OK - The value was appended to the packet buffer.
* TRILOGY_SYSERR - A system error occurred, check errno.
* TRILOGY_MAX_PACKET_EXCEEDED - Appending this value would exceed the maximum
* packet size.
*/
int trilogy_builder_write_float(trilogy_builder_t *builder, float val);

/* trilogy_builder_write_double - Append a double to the packet buffer.
*
* builder - A pre-initialized trilogy_builder_t pointer
* val - The value to append to the buffer
*
* Return values:
* TRILOGY_OK - The value was appended to the packet buffer.
* TRILOGY_SYSERR - A system error occurred, check errno.
* TRILOGY_MAX_PACKET_EXCEEDED - Appending this value would exceed the maximum
* packet size.
*/
int trilogy_builder_write_double(trilogy_builder_t *builder, double val);

/* trilogy_builder_write_lenenc - Append a length-encoded integer to the packet
* buffer.
*
Expand Down
214 changes: 214 additions & 0 deletions inc/trilogy/client.h
Original file line number Diff line number Diff line change
Expand Up @@ -601,4 +601,218 @@ void trilogy_free(trilogy_conn_t *conn);
*/
int trilogy_discard(trilogy_conn_t *conn);

/* trilogy_stmt_prepare_send - Send a prepared statement prepare command to the server.
*
* conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
* undefined.
* stmt - A pointer to the buffer containing the statement to prepare.
* stmt_len - The length of the data buffer.
*
* Return values:
* TRILOGY_OK - The quit 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_stmt_prepare_send(trilogy_conn_t *conn, const char *stmt, size_t stmt_len);

/* trilogy_stmt_t - The trilogy client's prepared statement type.
*/
typedef trilogy_stmt_ok_packet_t trilogy_stmt_t;

/* trilogy_stmt_prepare_recv - Read the prepared statement prepare command response
* from the MySQL-compatible server.
*
* This should be called after all data written by trilogy_stmt_prepare_send is flushed
* to the network. Calling this at any other time during the connection
* lifecycle is undefined.
*
* Following a successful call to this function, the caller will also need to read off
* `trilogy_stmt_t.column_count` parameters as column packets, then
* `trilogy_stmt_t.column_count` columns as column packets. This must be done before
* the socket will be command-ready again.
*
* conn - A pre-initialized trilogy_conn_t pointer. It can also be connected but
* a disconnected trilogy_conn_t will also return TRILOGY_OK.
* stmt_out - A pointer to a pre-allocated trilogy_stmt_t.
*
* Return values:
* TRILOGY_OK - The prepare command response successfully read from
* 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_SYSERR - A system error occurred, check errno.
* TRILOGY_CLOSED_CONNECTION - The connection is closed.
*/
int trilogy_stmt_prepare_recv(trilogy_conn_t *conn, trilogy_stmt_t *stmt_out);

/* trilogy_stmt_bind_data_send - Send a prepared statement bind long data command to the server.
*
* There is no pairing `trilogy_stmt_bind_data_recv` fucntion to this one because the server
* doesn't send a response to this command.
*
* conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
* undefined.
* stmt - Pointer to a valid trilogy_stmt_t, representing the prepared statement for which
* to bind the supplied parameter data to.
* param_num - The parameter index for which the supplied data should be bound to.
* data - A pointer to the buffer containing the data to be bound.
* data_len - The length of the data buffer.
*
* Return values:
* TRILOGY_OK - The bind data 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_stmt_bind_data_send(trilogy_conn_t *conn, trilogy_stmt_t *stmt, uint16_t param_num, uint8_t *data,
size_t data_len);

/* trilogy_stmt_execute_send - Send a prepared statement execute command to the server.
*
* conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
* undefined.
* stmt - Pointer to a valid trilogy_stmt_t, representing the prepared statement you're
* requesting to execute.
* flags - The flags (TRILOGY_STMT_FLAGS_t) to be used with this execute command packet.
* binds - Pointer to an array of trilogy_binary_value_t's. The array size should match that
* of `trilogy_stmt_t.column_count`.
*
* Return values:
* TRILOGY_OK - The execute 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_stmt_execute_send(trilogy_conn_t *conn, trilogy_stmt_t *stmt, uint8_t flags, trilogy_binary_value_t *binds);

/* trilogy_stmt_execute_recv - Read the prepared statement execute command response
* from the MySQL-compatible server.
*
* This should be called after all data written by trilogy_stmt_execute_send is flushed
* to the network. Calling this at any other time during the connection
* lifecycle is undefined.
*
* conn - A pre-initialized trilogy_conn_t pointer. It can also be connected but
* a disconnected trilogy_conn_t will also return TRILOGY_OK.
* column_count_out - Out parameter; A pointer to a pre-allocated uint64_t. Represents the
* number of columns in the response.
*
* Return values:
* TRILOGY_OK - The prepare command response successfully read from
* 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_SYSERR - A system error occurred, check errno.
* TRILOGY_CLOSED_CONNECTION - The connection is closed.
*/
int trilogy_stmt_execute_recv(trilogy_conn_t *conn, uint64_t *column_count_out);

/* trilogy_stmt_read_row - Read a row from the prepared statement execute response.
*
* This should only be called after a sucessful call to trilogy_stmt_execute_recv.
* You should continue calling this until TRILOGY_EOF is returned. Denoting the end
* of the result set.
*
* conn - A pre-initialized trilogy_conn_t pointer. It can also be connected but
* a disconnected trilogy_conn_t will also return TRILOGY_OK.
* stmt - Pointer to a valid trilogy_stmt_t, representing the prepared statement you're
* requesting to execute.
* columns - The list of columns from the prepared statement.
* column_count - The number of columns in prepared statement.
* values_out - Out parameter; A pointer to a pre-allocated array of
* trilogy_binary_value_t's. There must be enough space to fit all of the
* values. This can be computed with:
* `(sizeof(trilogy_binary_value_t) * column_count)`.
*
* Return values:
* TRILOGY_OK - The prepare command response successfully read from
* 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_EOF - There are no more rows to read from the result set.
* TRILOGY_UNEXPECTED_PACKET - The response packet wasn't what was expected.
* TRILOGY_PROTOCOL_VIOLATION - Invalid length parsed for a TIME/DATETIME/TIMESTAMP value.
* TRILOGY_UNKNOWN_TYPE - An unsupported or unknown MySQL type was seen.
* TRILOGY_SYSERR - A system error occurred, check errno.
* TRILOGY_CLOSED_CONNECTION - The connection is closed.
*/
int trilogy_stmt_read_row(trilogy_conn_t *conn, trilogy_stmt_t *stmt, trilogy_column_packet_t *columns,
trilogy_binary_value_t *values_out);

/* trilogy_stmt_reset_send - Send a prepared statement reset command to the server.
*
* conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
* undefined.
* stmt - Pointer to a valid trilogy_stmt_t, representing the prepared statement you're
* requesting to reset.
*
* Return values:
* TRILOGY_OK - The reset 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_stmt_reset_send(trilogy_conn_t *conn, trilogy_stmt_t *stmt);

/* trilogy_stmt_reset_recv - Read the prepared statement reset command response
* from the MySQL-compatible server.
*
* This should be called after all data written by trilogy_stmt_reset_send is flushed
* to the network. Calling this at any other time during the connection
* lifecycle is undefined.
*
* conn - A pre-initialized trilogy_conn_t pointer. It can also be connected but
* a disconnected trilogy_conn_t will also return TRILOGY_OK.
*
* Return values:
* TRILOGY_OK - The reset command response successfully read from
* 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_SYSERR - A system error occurred, check errno.
* TRILOGY_CLOSED_CONNECTION - The connection is closed.
*/
int trilogy_stmt_reset_recv(trilogy_conn_t *conn);

/* trilogy_stmt_close_send - Send a prepared statement close command to the server.
*
* There is no pairing `trilogy_stmt_close_recv` fucntion to this one because the server
* doesn't send a response to this command.
*
* conn - A connected trilogy_conn_t pointer. Using a disconnected trilogy_conn_t is
* undefined.
* stmt - Pointer to a valid trilogy_stmt_t, representing the prepared statement you're
* requesting to close.
*
* Return values:
* TRILOGY_OK - The close 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_stmt_close_send(trilogy_conn_t *conn, trilogy_stmt_t *stmt);

#endif
Loading

0 comments on commit c359ac1

Please sign in to comment.