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 binary protocol/prepared statement support #3

Merged
merged 26 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1d203a9
Add binary protocol/prepared statement support
brianmario Dec 17, 2021
a827d68
Tweak trilogy_build_stmt_bind_data_packet docs
brianmario Dec 29, 2021
99ae250
Add docs to prepared statement client C API
brianmario Dec 29, 2021
e08c384
Small tweaks to the docs for the prepared statement protocol C API
brianmario Dec 29, 2021
824a85b
Add docs to the prepared statement blocking C API
brianmario Dec 29, 2021
652d7e5
More tweaks to prepared statement client C API docs
brianmario Dec 29, 2021
ec3631c
Fix null bitmap building to allow for more than 8 columns
brianmario Dec 30, 2021
9eed8d2
Remove some free calls from old code iteration in test
brianmario Jan 4, 2022
292bfa8
Fix Ruby build
brianmario Jan 4, 2022
06ab59e
Make sure we can represent unsigned trilogy_binary_value_t's
brianmario Jan 4, 2022
4272c00
Small code cleanup
brianmario Jan 5, 2022
ba303f9
Add some prepared statement blocking API tests
brianmario Jan 7, 2022
3d0bdcd
Move time field up a level in trilogy_binary_value_t
brianmario Jan 26, 2022
a54c5a6
Move float/double serialization helpers inline
brianmario Jan 26, 2022
7ac41bd
Document trilogy_binary_value_t some.
brianmario Jan 26, 2022
c734cdc
Add some docs for trilogy_stmt_ok_packet_t as well.
brianmario Jan 26, 2022
23e07b8
We still need time fields in the date section of trilogy_binary_value_t
brianmario Jan 27, 2022
dfb8f52
Split out TRILOGY_TYPE_YEAR into it's own field in the union
brianmario Jan 27, 2022
9844b48
Add a few more binary protocol tests
brianmario Feb 1, 2022
42253e8
Remove dev references
brianmario Jun 2, 2022
4baf042
Update README
adrianna-chang-shopify Jun 6, 2023
5a89078
Test prepared statement support against all potential MySQL types
adrianna-chang-shopify Jun 7, 2023
80d608e
Bugfix: parsing TRILOGY_TYPE_TIME packet should not try to read micro…
adrianna-chang-shopify Jun 7, 2023
2bacaa2
Try year test with YEAR() instead of CAST()
adrianna-chang-shopify Jun 8, 2023
5ccb8ea
Document new builder methods
adrianna-chang-shopify Jun 8, 2023
7fe04a4
Handle prepared statements with floats
adrianna-chang-shopify Jun 8, 2023
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
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