diff --git a/mysql.lua b/mysql.lua index 021c01c..951ec54 100644 --- a/mysql.lua +++ b/mysql.lua @@ -1,4 +1,10 @@ ---mysql ffi binding (Cosmin Apreutesei, public domain). supports mysql Connector/C 6.1. based on mySQL 5.7 manual. + +--mySQL client library ffi binding. +--Written by Cosmin Apreutesei. Public domain. + +--Supports mysql Connector/C 6.1. +--Based on mySQL 5.7 manual. + local ffi = require'ffi' local bit = require'bit' require'mysql_h' @@ -22,24 +28,25 @@ end --error reporting -local function myerror(mysql) +local function myerror(mysql, stacklevel) local err = cstring(C.mysql_error(mysql)) if not err then return end - error(string.format('mysql error: %s', err)) + error(string.format('mysql error: %s', err), stacklevel or 3) end local function checkz(mysql, ret) if ret == 0 then return end - myerror(mysql) + myerror(mysql, 4) end local function checkh(mysql, ret) if ret ~= NULL then return ret end - myerror(mysql) + myerror(mysql, 4) end local function enum(e, prefix) - return assert(type(e) == 'string' and (prefix and C[prefix..e] or C[e]) or e, 'invalid enum value') + local v = type(e) == 'string' and (prefix and C[prefix..e] or C[e]) or e + return assert(v, 'invalid enum value') end --client library info @@ -582,7 +589,7 @@ local function fetch_row(res, numeric, assoc, decode, field_count, fields, t) local values = C.mysql_fetch_row(res) if values == NULL then if res.conn ~= NULL then --buffered read: check for errors - myerror(res.conn) + myerror(res.conn, 4) end return nil end @@ -694,25 +701,25 @@ end --prepared statements -local function sterror(stmt) +local function sterror(stmt, stacklevel) local err = cstring(C.mysql_stmt_error(stmt)) if not err then return end - error(string.format('mysql error: %s', err)) + error(string.format('mysql error: %s', err), stacklevel or 3) end local function stcheckz(stmt, ret) if ret == 0 then return end - sterror(stmt) + sterror(stmt, 4) end local function stcheckbool(stmt, ret) if ret == 1 then return end - sterror(stmt) + sterror(stmt, 4) end local function stcheckh(stmt, ret) if ret ~= NULL then return ret end - sterror(stmt) + sterror(stmt, 4) end function conn.prepare(mysql, query) @@ -774,11 +781,12 @@ end function stmt.result_metadata(stmt) local res = stcheckh(stmt, C.mysql_stmt_result_metadata(stmt)) - return ffi.gc(res, C.mysql_free_result) + return res and ffi.gc(res, C.mysql_free_result) end function stmt.fields(stmt) local res = stmt:result_metadata() + if not res then return nil end local fields = res:fields() return function() local i, info = fields() @@ -1183,6 +1191,7 @@ function stmt.bind_result_types(stmt, maxsize) local types = {} local field_count = stmt:field_count() local res = stmt:result_metadata() + if not res then return nil end for i=1,field_count do local ftype, size, unsigned, decimals = res:field_type(i) if ftype == 'decimal' then @@ -1199,7 +1208,7 @@ function stmt.bind_result_types(stmt, maxsize) end function stmt.bind_params(stmt, ...) - local types = type(...) == 'string' and {...} or ... or {} + local types = type((...)) == 'string' and {...} or ... or {} assert(stmt:param_count() == #types, 'wrong number of param types') local bb = params_bind_buffer(types) stcheckz(stmt, C.mysql_stmt_bind_param(stmt, bb.buffer)) diff --git a/mysql.md b/mysql.md index f9c403e..6646298 100644 --- a/mysql.md +++ b/mysql.md @@ -51,6 +51,7 @@ A complete, lightweight ffi binding of the mysql client library. `conn:more_results() -> true | false` are there more result sets? **[Prepared statements]** `conn:prepare(query) -> stmt` prepare a query for multiple executions +`stmt:param_count() -> n` number of params `stmt:exec()` execute a prepared statement `stmt:store_result()` store all the resulted rows to the client `stmt:fetch() -> true | false | true, 'truncated'` fetch the next row @@ -322,6 +323,10 @@ The flow for prepared statements is like this: Prepare a query for multiple execution and return a statement object. +## `stmt:param_count() -> n` + +Number of parameters. + ## `stmt:exec()` Execute a prepared statement. diff --git a/mysql_h.lua b/mysql_h.lua index 10b8639..37fe2a0 100644 --- a/mysql_h.lua +++ b/mysql_h.lua @@ -1,5 +1,8 @@ ---result of `cpp mysql.h` plus lots of cleanup and defines from other headers; by Cosmin Apreutesei; MySQL Connector/C 6.1 ---NOTE that MySQL Connector/C is GPL software. Could this file be considered "derived work" then? + +--result of `cpp mysql.h` with lots of cleanup and defines from other headers. +--Written by Cosmin Apreutesei. MySQL Connector/C 6.1. + +--NOTE: MySQL Connector/C is GPL software. Is this "derived work" then? local ffi = require'ffi' @@ -17,7 +20,7 @@ enum { MYSQL_DATA_TRUNCATED = 101 }; -// -------------------------------------------------------------------------------- error constants +// ----------------------------------------------------------- error constants // NOTE: added MYSQL_ prefix to these. enum mysql_error_code { @@ -88,13 +91,13 @@ enum mysql_error_code { MYSQL_CR_AUTH_PLUGIN_ERR = 2061 }; -// -------------------------------------------------------------------------------- client library +// ------------------------------------------------------------ client library -unsigned int mysql_thread_safe(void); // is the client library thread safe? +unsigned int mysql_thread_safe(void); // is the client library thread safe? const char *mysql_get_client_info(void); unsigned long mysql_get_client_version(void); -// -------------------------------------------------------------------------------- connections +// --------------------------------------------------------------- connections typedef struct MYSQL_ MYSQL; @@ -176,7 +179,8 @@ int mysql_set_character_set(MYSQL *mysql, const char *csname); int mysql_select_db(MYSQL *mysql, const char *db); -my_bool mysql_change_user(MYSQL *mysql, const char *user, const char *passwd, const char *db); +my_bool mysql_change_user(MYSQL *mysql, const char *user, const char *passwd, + const char *db); my_bool mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert, const char *ca, @@ -189,7 +193,7 @@ enum enum_mysql_set_option }; int mysql_set_server_option(MYSQL *mysql, enum enum_mysql_set_option option); -// -------------------------------------------------------------------------------- connection info +// ----------------------------------------------------------- connection info const char * mysql_character_set_name(MYSQL *mysql); @@ -215,18 +219,19 @@ unsigned long mysql_get_server_version(MYSQL *mysql); unsigned int mysql_get_proto_info(MYSQL *mysql); const char * mysql_get_ssl_cipher(MYSQL *mysql); -// -------------------------------------------------------------------------------- transactions +// -------------------------------------------------------------- transactions my_bool mysql_commit(MYSQL * mysql); my_bool mysql_rollback(MYSQL * mysql); my_bool mysql_autocommit(MYSQL * mysql, my_bool auto_mode); -// -------------------------------------------------------------------------------- queries +// ------------------------------------------------------------------- queries -unsigned long mysql_real_escape_string(MYSQL *mysql, char *to, const char *from, unsigned long length); +unsigned long mysql_real_escape_string(MYSQL *mysql, char *to, + const char *from, unsigned long length); int mysql_real_query(MYSQL *mysql, const char *q, unsigned long length); -// -------------------------------------------------------------------------------- query info +// ---------------------------------------------------------------- query info unsigned int mysql_field_count(MYSQL *mysql); my_ulonglong mysql_affected_rows(MYSQL *mysql); @@ -237,7 +242,7 @@ const char * mysql_sqlstate(MYSQL *mysql); unsigned int mysql_warning_count(MYSQL *mysql); const char * mysql_info(MYSQL *mysql); -// -------------------------------------------------------------------------------- query results +// ------------------------------------------------------------- query results int mysql_next_result(MYSQL *mysql); my_bool mysql_more_results(MYSQL *mysql); @@ -272,7 +277,7 @@ typedef MYSQL_ROWS *MYSQL_ROW_OFFSET; MYSQL_ROW_OFFSET mysql_row_tell(MYSQL_RES *res); MYSQL_ROW_OFFSET mysql_row_seek(MYSQL_RES *result, MYSQL_ROW_OFFSET offset); -// -------------------------------------------------------------------------------- query field info +// ---------------------------------------------------------- query field info enum enum_field_types { MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY, @@ -352,13 +357,13 @@ typedef struct st_mysql_field { MYSQL_FIELD *mysql_fetch_field_direct(MYSQL_RES *res, unsigned int fieldnr); -// -------------------------------------------------------------------------------- reflection +// ---------------------------------------------------------------- reflection MYSQL_RES *mysql_list_dbs(MYSQL *mysql, const char *wild); MYSQL_RES *mysql_list_tables(MYSQL *mysql, const char *wild); MYSQL_RES *mysql_list_processes(MYSQL *mysql); -// -------------------------------------------------------------------------------- remote control +// ------------------------------------------------------------ remote control int mysql_kill(MYSQL *mysql, unsigned long pid); @@ -403,10 +408,10 @@ enum { MYSQL_REFRESH_USER_RESOURCES = 0x80000L, MYSQL_REFRESH_FOR_EXPORT = 0x100000L, /* FLUSH TABLES ... FOR EXPORT */ }; -int mysql_refresh(MYSQL *mysql, unsigned int refresh_options); // needs RELOAD priviledge -int mysql_dump_debug_info(MYSQL *mysql); // needs SUPER priviledge +int mysql_refresh(MYSQL *mysql, unsigned int refresh_options); // needs RELOAD priviledge +int mysql_dump_debug_info(MYSQL *mysql); // needs SUPER priviledge -// -------------------------------------------------------------------------------- prepared statements +// ------------------------------------------------------- prepared statements typedef struct MYSQL_STMT_ MYSQL_STMT; @@ -461,7 +466,7 @@ my_bool mysql_stmt_send_long_data(MYSQL_STMT *stmt, const char *data, unsigned long length); -// -------------------------------------------------------------------------------- prepared statements / bindings +// -------------------------------------------- prepared statements / bindings enum enum_mysql_timestamp_type { @@ -511,7 +516,7 @@ int mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind_arg, unsigned int column, unsigned long offset); -// -------------------------------------------------------------------------------- LOAD DATA LOCAL INFILE hooks +// ---------------------------------------------- LOAD DATA LOCAL INFILE hooks void mysql_set_local_infile_handler(MYSQL *mysql, int (*local_infile_init)(void **, const char *, void *), @@ -521,31 +526,33 @@ void mysql_set_local_infile_handler(MYSQL *mysql, void *); void mysql_set_local_infile_default(MYSQL *mysql); -// -------------------------------------------------------------------------------- mysql proxy scripting +// ----------------------------------------------------- mysql proxy scripting my_bool mysql_read_query_result(MYSQL *mysql); -// -------------------------------------------------------------------------------- debugging +// ----------------------------------------------------------------- debugging void mysql_debug(const char *debug); -// -------------------------------------------------------------------------------- present but not documented +// ------------------------------------------------ present but not documented -int mysql_server_init(int argc, char **argv, char **groups); -void mysql_server_end(void); +int mysql_server_init(int argc, char **argv, char **groups); +void mysql_server_end(void); char *get_tty_password(const char *opt_message); void myodbc_remove_escape(MYSQL *mysql, char *name); my_bool mysql_embedded(void); int mysql_send_query(MYSQL *mysql, const char *q, unsigned long length); -// -------------------------------------------------------------------------------- redundant functions +// ------------------------------------------------------- redundant functions my_bool mysql_thread_init(void); // called anyway -void mysql_thread_end(void); // called anyway +void mysql_thread_end(void); // called anyway const char *mysql_errno_to_sqlstate(unsigned int mysql_errno); // use mysql_sqlstate -unsigned long mysql_hex_string(char *to, const char *from, unsigned long from_length); // bad taste +unsigned long mysql_hex_string(char *to, const char *from, + unsigned long from_length); // bad taste -// redundant ways to get field info (we use use mysql_field_count and mysql_fetch_field_direct) +// redundant ways to get field info. +// we use use mysql_field_count and mysql_fetch_field_direct instead. MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result); MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *res); typedef unsigned int MYSQL_FIELD_OFFSET; @@ -553,10 +560,12 @@ MYSQL_FIELD_OFFSET mysql_field_tell(MYSQL_RES *res); MYSQL_FIELD_OFFSET mysql_field_seek(MYSQL_RES *result, MYSQL_FIELD_OFFSET offset); MYSQL_RES *mysql_stmt_param_metadata(MYSQL_STMT *stmt); -// -------------------------------------------------------------------------------- deprecated functions +// ------------------------------------------------------ deprecated functions -unsigned long mysql_escape_string(char *to, const char *from, unsigned long from_length); // use mysql_real_escape_string +unsigned long mysql_escape_string(char *to, const char *from, + unsigned long from_length); // use mysql_real_escape_string int mysql_query(MYSQL *mysql, const char *q); // use mysql_real_query -MYSQL_RES *mysql_list_fields(MYSQL *mysql, const char *table, const char *wild); // use "SHOW COLUMNS FROM table" +MYSQL_RES *mysql_list_fields(MYSQL *mysql, const char *table, + const char *wild); // use "SHOW COLUMNS FROM table" ]]