-
Notifications
You must be signed in to change notification settings - Fork 83
Description
We shipped the C API for prepared statements in #3. The next step is to add support to the Ruby bindings.
API
I imagine we'll want something similar to Mysql2's implementation: a Trilogy#prepare method that returns a Trilogy::Statement object, and a Trilogy::Statement#execute method that can be used to execute a PS with given parameters, e.g.:
client = Trilogy.new(host: "127.0.0.1", port: 3306, username: "root", read_timeout: 2, database: "trilogy_test")
statement = client.prepare("SELECT * FROM users WHERE id = ?")
result1 = statement.execute(1)
result2 = statement.execute(2)
statement = client.prepare("SELECT * FROM users WHERE id >= ? AND name LIKE ?")
result = statement.execute(1, "Joe")Trilogy::Statement should, at a minimum, expose the underlying data values from the C API: the statement id, column count, parameter count, and warning count. Mysql2::Statement exposes things like affected_rows, fields, last_id, etc. that we may eventually want Trilogy::Statement to expose as well.
Blockers
@byroot and I began working on this and ran into some issues while fleshing out the Ruby class that will wrap the C prepared statement struct (trilogy_stmt_t). As Mysql2 does, we'll need to close the prepared statement when freeing the Ruby Trilogy::Statement object:
https://github.com/brianmario/mysql2/blob/6cf5e1d732b4a6ee5485def98637f100cbc86f1b/ext/mysql2/statement.c#L26-L29
https://github.com/brianmario/mysql2/blob/master/ext/mysql2/statement.c#L75
https://github.com/brianmario/mysql2/blob/master/ext/mysql2/statement.c#L65
trilogy_stmt_close needs the stmt and the connection:
Lines 366 to 368 in 61a8b40
| int trilogy_stmt_close(trilogy_conn_t *conn, trilogy_stmt_t *stmt) | |
| { | |
| int rc = trilogy_stmt_close_send(conn, stmt); |
We need the connection to send TRILOGY_CMD_STMT_CLOSE -- so we can create an intermediate struct (that will be wrapped into Trilogy::Statement) that encapsulates both trilogy_stmt_t and the connection. The problem arises if the connection ends up being closed / freed before we attempt to close the prepared statement / free its memory. In this case, attempting to access the connection on the statement wrapper will raise an EXC_BAD_ACCESS exception.
The solution is likely to do something similar to what the native SQL client library does -- it stores a linked list of all prepared statements on the client itself, and then when the client is closed, it "detaches" the statement list by clearing the connection pointer of every statement. That way, we can no-op in trilogy_stmt_close if the connection is gone, and avoid accessing memory for a conn that's already been freed.
If anyone else has opinions on this though, would love to hear!
I've started a branch here for the Ruby bindings, but it's still very much a WIP: https://github.com/trilogy-libraries/trilogy/compare/main...adrianna-chang-shopify:trilogy:ac-prepared-statements-ruby?expand=1