From 83992a4bf07917be94d07d55c88d6fcc59f65ef4 Mon Sep 17 00:00:00 2001 From: blumf Date: Tue, 27 Jan 2015 13:26:36 +0000 Subject: [PATCH 01/64] Support for directly providing ODBC DNS A new way of using the env.connect method, providing a table of options. In this case, the old method can now be used as: env.connect{ user = "foo", password = "bar", source = "My DB DSN from odbcad32" } But also, a DSN can be provided: env.connect{ dsn = [[DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};PWD=bar;DBQ=C:\path\my_db.mdb]] } --- src/ls_odbc.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 1 deletion(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index 9fe1a84..52b57fa 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -582,6 +582,112 @@ static int create_connection (lua_State *L, int o, env_data *env, SQLHDBC hdbc) return 1; } +static int env_table_connect_DSN (lua_State *L) { + env_data *env = (env_data *) getenvironment (L); + SQLCHAR *sourcename = (SQLCHAR*)luaL_checkstring (L, -1); + SQLHDBC hdbc; + SQLCHAR sqlOutBuf[4097]; + SQLSMALLINT sqlOutLen; + SQLRETURN ret; + + lua_pop(L, 1); + + ret = SQLSetEnvAttr (env->henv, SQL_ATTR_ODBC_VERSION, + (void*)SQL_OV_ODBC3, 0); + if (error(ret)) + return luasql_faildirect (L, "error setting SQL version."); + /* tries to allocate connection handle */ + ret = SQLAllocHandle (hDBC, env->henv, &hdbc); + if (error(ret)) + return luasql_faildirect (L, "connection allocation error."); + /* tries to connect handle */ + ret = SQLDriverConnect (hdbc, NULL, + sourcename, SQL_NTS, + sqlOutBuf, 4096, &sqlOutLen, + SQL_DRIVER_NOPROMPT); + if (error(ret)) { + ret = fail(L, hDBC, hdbc); + SQLFreeHandle(hDBC, hdbc); + return ret; + } + /* success, return connection object */ + ret = create_connection (L, 1, env, hdbc); + if(ret == 1) { + /* Add the sqlOutBuf string to the results, for diagnostics */ + lua_pushlstring(L, (char*)sqlOutBuf, sqlOutLen); + return 2; + } + return ret; +} + +static int env_table_connect_standard (lua_State *L) { + env_data *env = (env_data *) getenvironment (L); + SQLCHAR *sourcename = (SQLCHAR*)luaL_checkstring (L, -1); + SQLCHAR *username = NULL; + SQLCHAR *password = NULL; + SQLHDBC hdbc; + SQLRETURN ret; + + lua_pop(L, 1); + + /* get user */ + lua_getfield(L, 2, "user"); + if(lua_isstring(L, -1)) + username = (SQLCHAR*)luaL_checkstring(L, -1); + lua_pop(L, 1); + + /* get password */ + lua_getfield(L, 2, "password"); + if(lua_isstring(L, -1)) + password = (SQLCHAR*)luaL_checkstring(L, -1); + lua_pop(L, 1); + + /* tries to allocate connection handle */ + ret = SQLAllocHandle (hDBC, env->henv, &hdbc); + if (error(ret)) + return luasql_faildirect (L, "connection allocation error."); + + /* tries to connect handle */ + ret = SQLConnect (hdbc, sourcename, SQL_NTS, + username, SQL_NTS, password, SQL_NTS); + if (error(ret)) { + ret = fail(L, hDBC, hdbc); + SQLFreeHandle(hDBC, hdbc); + return ret; + } + /* success, return connection object */ + return create_connection (L, 1, env, hdbc); +} + +/* +** Creates and returns a connection object +** Lua Input: tab +** tab: table of connection parameters +** "dsn" : A custom DSN +** or +** "source" : The ODBC source DSN +** "user" : The connecting user +** "password" : The connecting user's password +** general params +** "" : ? +** Lua Returns: +** connection object if successfull +** nil and error message otherwise. +*/ +static int env_table_connect (lua_State *L) { + lua_getfield(L, 2, "dsn"); + if(lua_isstring(L, -1)) + return env_table_connect_DSN(L); /* use the custom DSN string */ + lua_pop(L, 1); + + lua_getfield(L, 2, "source"); + if(lua_isstring(L, -1)) + return env_table_connect_standard(L); /* standard source [user, password] */ + lua_pop(L, 1); + + /* failed to get any meaningful connection info*/ + return luasql_faildirect (L, "no meaningful connection info given"); +} /* ** Creates and returns a connection object @@ -592,7 +698,7 @@ static int create_connection (lua_State *L, int o, env_data *env, SQLHDBC hdbc) ** connection object if successfull ** nil and error message otherwise. */ -static int env_connect (lua_State *L) { +static int env_std_connect (lua_State *L) { env_data *env = (env_data *) getenvironment (L); SQLCHAR *sourcename = (SQLCHAR*)luaL_checkstring (L, 2); SQLCHAR *username = (SQLCHAR*)luaL_optstring (L, 3, NULL); @@ -618,6 +724,22 @@ static int env_connect (lua_State *L) { return create_connection (L, 1, env, hdbc); } +/* +** Creates and returns a connection object +** Lua Input: see env_std_connect and env_table_connect +** Lua Returns: +** connection object if successfull +** nil and error message otherwise. +*/ +static int env_connect (lua_State *L) { + /* have we been given a connection paramater list */ + if(lua_istable(L, 2)) + return env_table_connect(L); + + /* just use the standard conection arguments */ + return env_std_connect(L); +} + /* ** Closes an environment object */ From 2ab1e681e22ab8e51e27bf6936e356ccee2c4c90 Mon Sep 17 00:00:00 2001 From: blumf Date: Thu, 9 Apr 2015 13:21:16 +0100 Subject: [PATCH 02/64] Update README Changed readme file to match project's intentions and use markdown --- README | 20 -------------------- README.md | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 20 deletions(-) delete mode 100644 README create mode 100644 README.md diff --git a/README b/README deleted file mode 100644 index f6c614f..0000000 --- a/README +++ /dev/null @@ -1,20 +0,0 @@ -LuaSQL 2.1 -http://www.keplerproject.org/luasql/ - -LuaSQL is a simple interface from Lua to a DBMS. It enables a Lua program to: - - * Connect to ODBC, ADO, Oracle, MySQL, SQLite and PostgreSQL databases; - * Execute arbitrary SQL statements; - * Retrieve results in a row-by-row cursor fashion. - -LuaSQL is free software and uses the same license as Lua 5.1. - - -Source code for LuaSQL can be downloaded from the LuaForge page. - -If you are using LuaBinaries a Windows binary version of LuaSQL can be found at the same LuaForge page. - -LuaSQL 2.1.1 [29/Oct/2007] -* Fixed a bug in the SQLite3 error handling (patch by David Burgess) -* Fixed bug [#1834] for SQLite 3 (found by Savin Zlobec, patch by Marc Nijdam) -* Fixed bug [#1770] for SQLite 3 (found by Enrico Tassi, patch by Marc Nijdam) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..9d1b725 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# LuaSQL 3.0 (proposed) +http://www.keplerproject.org/luasql/ +https://github.com/keplerproject/luasql/ + +LuaSQL is a simple interface from Lua to a DBMS. It enables a Lua program to: + + * Connect to ODBC, ADO, Oracle, MySQL, SQLite and PostgreSQL databases; + * Execute arbitrary SQL statements; + * Retrieve results in a row-by-row cursor fashion. + +LuaSQL is free software and uses the same license as Lua 5.1. + +Source code for LuaSQL can be downloaded from the Kepler Project Github page. + +- - - + +## LuaSQL 3.0.0 [09/Apr/2015] +This is a proposed branch to add much needed features + * Prepared statements with parameters + + Really a major requirement for any DB system that takes data from the web + * More flexible connection method + + Current method of just passing DB name/path with user name+password is not enough to support the complexities of connecting to a DB + * Explicit handling of BLOB fields (? maybe) + + It might be useful to handle true binary data from BLOB fields rather than blindly convert them to Lua strings + From 2ce67ee0a555db79dac02913cfb14031ba334d08 Mon Sep 17 00:00:00 2001 From: blumf Date: Thu, 9 Apr 2015 14:30:28 +0100 Subject: [PATCH 03/64] Bumping version and copyright notes --- src/luasql.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/luasql.c b/src/luasql.c index caf9721..8030e52 100644 --- a/src/luasql.c +++ b/src/luasql.c @@ -122,12 +122,12 @@ LUASQL_API void luasql_setmeta (lua_State *L, const char *name) { */ LUASQL_API void luasql_set_info (lua_State *L) { lua_pushliteral (L, "_COPYRIGHT"); - lua_pushliteral (L, "Copyright (C) 2003-2012 Kepler Project"); + lua_pushliteral (L, "Copyright (C) 2003-2015 Kepler Project"); lua_settable (L, -3); lua_pushliteral (L, "_DESCRIPTION"); lua_pushliteral (L, "LuaSQL is a simple interface from Lua to a DBMS"); lua_settable (L, -3); lua_pushliteral (L, "_VERSION"); - lua_pushliteral (L, "LuaSQL 2.3.0"); + lua_pushliteral (L, "LuaSQL 3.0.0"); lua_settable (L, -3); } From c300729194b15292fb4b269a7a1e624a5b8c19ad Mon Sep 17 00:00:00 2001 From: blumf Date: Thu, 9 Apr 2015 16:33:15 +0100 Subject: [PATCH 04/64] New table helper functions Just some stuff to save typing when pulling info from tables --- src/luasql.c | 26 ++++++++++++++++++++++++++ src/luasql.h | 3 +++ 2 files changed, 29 insertions(+) diff --git a/src/luasql.c b/src/luasql.c index 8030e52..e96780e 100644 --- a/src/luasql.c +++ b/src/luasql.c @@ -131,3 +131,29 @@ LUASQL_API void luasql_set_info (lua_State *L) { lua_pushliteral (L, "LuaSQL 3.0.0"); lua_settable (L, -3); } + + +LUASQL_API const char* luasql_table_optstring(lua_State *L, int idx, const char* name, const char* def) { + const char* res = NULL; + + lua_pushstring(L, name); + lua_gettable(L, idx); + + res = lua_tostring(L, -1); + lua_pop(L, 1); + + return (res != NULL) ? res : def; +} + + +LUASQL_API lua_Number luasql_table_optnumber(lua_State *L, int idx, const char* name, lua_Number def) { + lua_Number res = 0; + + lua_pushstring(L, name); + lua_gettable(L, idx); + + res = lua_tonumber(L, -1); + lua_pop(L, 1); + + return lua_isnumber(L, -1) ? res : def; +} diff --git a/src/luasql.h b/src/luasql.h index 345bf57..f0e5e08 100644 --- a/src/luasql.h +++ b/src/luasql.h @@ -30,6 +30,9 @@ LUASQL_API int luasql_createmeta (lua_State *L, const char *name, const luaL_Reg LUASQL_API void luasql_setmeta (lua_State *L, const char *name); LUASQL_API void luasql_set_info (lua_State *L); +LUASQL_API const char* luasql_table_optstring(lua_State *L, int idx, const char* name, const char* def); +LUASQL_API lua_Number luasql_table_optnumber(lua_State *L, int idx, const char* name, lua_Number def); + #if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501 void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup); #endif From 0c06ede3ab1688d4f632b8334c916964dbdede5a Mon Sep 17 00:00:00 2001 From: blumf Date: Thu, 9 Apr 2015 16:34:57 +0100 Subject: [PATCH 05/64] Avoided use of strcpy and sprintf Mainly just to shut up MSVC compiler --- src/luasql.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/luasql.c b/src/luasql.c index e96780e..cb28fcc 100644 --- a/src/luasql.c +++ b/src/luasql.c @@ -51,13 +51,14 @@ typedef struct { short closed; } pseudo_data; ** This function is used by `tostring'. */ static int luasql_tostring (lua_State *L) { - char buff[100]; pseudo_data *obj = (pseudo_data *)lua_touserdata (L, 1); - if (obj->closed) - strcpy (buff, "closed"); - else - sprintf (buff, "%p", (void *)obj); - lua_pushfstring (L, "%s (%s)", lua_tostring(L,lua_upvalueindex(1)), buff); + + if (obj->closed) { + lua_pushfstring (L, "%s (closed)", lua_tostring(L,lua_upvalueindex(1))); + } else { + lua_pushfstring (L, "%s (%p)", lua_tostring(L,lua_upvalueindex(1)), (void *)obj); + } + return 1; } From 2a65a4bbcbd2925625e8d3d6ad1e6aee6809f88f Mon Sep 17 00:00:00 2001 From: blumf Date: Thu, 9 Apr 2015 16:41:41 +0100 Subject: [PATCH 06/64] Convert Firebird driver to use table for connection Firebird driver now uses connection info table for opening up connections. Next step will involve adding support for optional parameters --- src/ls_firebird.c | 69 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 9c94cee..923b053 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -8,6 +8,7 @@ #include /* For managing time values */ #include #include +#include /* Lua API */ #include @@ -64,7 +65,11 @@ typedef struct { #define luasql_pushinteger lua_pushnumber #endif -LUASQL_API int luaopen_luasql_firebird (lua_State *L); +/* MSVC still doesn't support C99 properly until 2015 */ +#if defined(_MSC_VER) && _MSC_VER<1900 +#pragma warning(disable:4996) /* and complains if you try to work around it */ +#define snprintf _snprintf +#endif /* ** Returns a standard database error message @@ -414,11 +419,11 @@ static int conn_execute (lua_State *L) { /* what do we return? a cursor or a count */ if(cur.out_sqlda->sqld > 0) { /* a cursor */ - char cur_name[32]; + char cur_name[64]; cur_data* user_cur = (cur_data*)lua_newuserdata(L, sizeof(cur_data)); luasql_setmeta (L, LUASQL_CURSOR_FIREBIRD); - sprintf(cur_name, "dyn_cursor_%p", (void *)user_cur); + snprintf(cur_name, sizeof(cur_name), "dyn_cursor_%p", (void *)user_cur); /* open the cursor ready for fetch cycles */ isc_dsql_set_cursor_name(cur.env->status_vector, &cur.stmt, cur_name, 0); @@ -442,7 +447,7 @@ static int conn_execute (lua_State *L) { lua_pushnumber(L, count); - /* totaly finnished with the cursor */ + /* totaly finished with the cursor */ isc_dsql_free_statement(conn->env->status_vector, &cur.stmt, DSQL_drop); free(cur.out_sqlda); } @@ -901,9 +906,43 @@ static int create_environment (lua_State *L) { return 1; } +/* +** Reforms old connection style to new one +** Lua Input: source, [user, [pass]] +** source: data source +** user, pass: data source authentication information +** Lua Returns: +** new connection details table +*/ +static void env_connect_fix_old (lua_State *L) { + if(lua_isstring(L, 2)) { + /* convert to new table format */ + int n = lua_gettop(L); + + const char *sourcename = luaL_checkstring (L, 2); + const char *username = luaL_optstring (L, 3, ""); + const char *password = luaL_optstring (L, 4, ""); + + lua_newtable(L); + lua_pushstring(L, "source"); + lua_pushstring(L, sourcename); + lua_settable(L, -3); + lua_pushstring(L, "user"); + lua_pushstring(L, username); + lua_settable(L, -3); + lua_pushstring(L, "password"); + lua_pushstring(L, password); + lua_settable(L, -3); + + while(n > 1) { + lua_remove(L, n--); + } + } +} + /* ** Creates and returns a connection object -** Lua Input: source, user, pass +** Lua Input: { source, user, password, ... } ** source: data source ** user, pass: data source authentication information ** Lua Returns: @@ -917,11 +956,25 @@ static int env_connect (lua_State *L) { isc_tpb_write }; conn_data conn; conn_data* res_conn; + const char *sourcename, *username, *password; env_data *env = (env_data *) getenvironment (L, 1); - const char *sourcename = luaL_checkstring (L, 2); - const char *username = luaL_optstring (L, 3, ""); - const char *password = luaL_optstring (L, 4, ""); + + if(lua_gettop(L) < 2) { + return luasql_faildirect(L, "No connection details provided"); + } + + if(!lua_istable(L, 2)) { + env_connect_fix_old(L); + } + + sourcename = luasql_table_optstring(L, 2, "source", NULL); + username = luasql_table_optstring(L, 2, "user", ""); + password = luasql_table_optstring(L, 2, "password", ""); + + if(sourcename == NULL) { + return luasql_faildirect(L, "connection details table missing 'source'"); + } conn.env = env; conn.db = 0L; From 3e6a06ed5e08a5451fca35233dd7faf887db5854 Mon Sep 17 00:00:00 2001 From: blumf Date: Thu, 9 Apr 2015 17:30:32 +0100 Subject: [PATCH 07/64] All drivers use same table When requiring a driver, it will search for other LuaSQL drivers already loaded and use that table to place their environment function in. i.e. ``` luasql = require"luasql.odbc" luasql = require"luasql.mysql" ``` Results in both drivers being available via the luasql table --- src/ls_firebird.c | 2 +- src/ls_mysql.c | 2 +- src/ls_oci8.c | 2 +- src/ls_odbc.c | 2 +- src/ls_postgres.c | 2 +- src/ls_sqlite.c | 2 +- src/ls_sqlite3.c | 2 +- src/luasql.c | 28 ++++++++++++++++++++++++++++ src/luasql.h | 2 ++ 9 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 923b053..a571863 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -1114,7 +1114,7 @@ LUASQL_API int luaopen_luasql_firebird (lua_State *L) { {NULL, NULL}, }; create_metatables (L); - lua_newtable (L); + luasql_find_driver_table (L); luaL_setfuncs (L, driver, 0); luasql_set_info (L); return 1; diff --git a/src/ls_mysql.c b/src/ls_mysql.c index c4a3f8b..ac534f3 100644 --- a/src/ls_mysql.c +++ b/src/ls_mysql.c @@ -598,7 +598,7 @@ LUASQL_API int luaopen_luasql_mysql (lua_State *L) { {NULL, NULL}, }; create_metatables (L); - lua_newtable(L); + luasql_find_driver_table(L); luaL_setfuncs(L, driver, 0); luasql_set_info (L); lua_pushliteral (L, "_MYSQLVERSION"); diff --git a/src/ls_oci8.c b/src/ls_oci8.c index 12cbdd9..aeb1a6c 100644 --- a/src/ls_oci8.c +++ b/src/ls_oci8.c @@ -851,7 +851,7 @@ LUASQL_API int luaopen_luasql_oci8 (lua_State *L) { {NULL, NULL}, }; create_metatables (L); - lua_newtable (L); + luasql_find_driver_table (L); luaL_setfuncs (L, driver, 0); luasql_set_info (L); return 1; diff --git a/src/ls_odbc.c b/src/ls_odbc.c index d3a3f21..56b7849 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -855,7 +855,7 @@ LUASQL_API int luaopen_luasql_odbc (lua_State *L) { {NULL, NULL}, }; create_metatables (L); - lua_newtable (L); + luasql_find_driver_table (L); luaL_setfuncs (L, driver, 0); luasql_set_info (L); return 1; diff --git a/src/ls_postgres.c b/src/ls_postgres.c index 3633c8a..0d211e5 100644 --- a/src/ls_postgres.c +++ b/src/ls_postgres.c @@ -602,7 +602,7 @@ LUASQL_API int luaopen_luasql_postgres (lua_State *L) { {NULL, NULL}, }; create_metatables (L); - lua_newtable (L); + luasql_find_driver_table (L); luaL_setfuncs (L, driver, 0); luasql_set_info (L); return 1; diff --git a/src/ls_sqlite.c b/src/ls_sqlite.c index c7ef8a8..8359510 100644 --- a/src/ls_sqlite.c +++ b/src/ls_sqlite.c @@ -618,7 +618,7 @@ LUASQL_API int luaopen_luasql_sqlite(lua_State *L) {NULL, NULL}, }; create_metatables (L); - lua_newtable (L); + luasql_find_driver_table (L); luaL_setfuncs (L, driver, 0); luasql_set_info (L); return 1; diff --git a/src/ls_sqlite3.c b/src/ls_sqlite3.c index 150073c..8258593 100644 --- a/src/ls_sqlite3.c +++ b/src/ls_sqlite3.c @@ -670,7 +670,7 @@ LUASQL_API int luaopen_luasql_sqlite3(lua_State *L) {NULL, NULL}, }; create_metatables (L); - lua_newtable (L); + luasql_find_driver_table (L); luaL_setfuncs (L, driver, 0); luasql_set_info (L); return 1; diff --git a/src/luasql.c b/src/luasql.c index cb28fcc..d943f10 100644 --- a/src/luasql.c +++ b/src/luasql.c @@ -158,3 +158,31 @@ LUASQL_API lua_Number luasql_table_optnumber(lua_State *L, int idx, const char* return lua_isnumber(L, -1) ? res : def; } + +/* +** Finds a pre-existing LuaSQL table, or creates a new one. +*/ +LUASQL_API void luasql_find_driver_table (lua_State *L) { + lua_getglobal(L, "package"); + if(lua_istable(L, -1)) { + lua_getfield(L, -1, "loaded"); + lua_remove(L, -2); + + lua_pushnil(L); + while(lua_next(L, -2) != 0) { + const char *key = lua_tostring(L, -2); + if(strncmp(key, LUASQL_TABLENAME, strlen(LUASQL_TABLENAME)) == 0) { + lua_remove(L, -2); + lua_remove(L, -2); + return; + } + + lua_pop(L, 1); + } + lua_pop(L, 2); + } else { + lua_pop(L, 1); + } + + lua_newtable (L); +} diff --git a/src/luasql.h b/src/luasql.h index f0e5e08..69949a3 100644 --- a/src/luasql.h +++ b/src/luasql.h @@ -33,6 +33,8 @@ LUASQL_API void luasql_set_info (lua_State *L); LUASQL_API const char* luasql_table_optstring(lua_State *L, int idx, const char* name, const char* def); LUASQL_API lua_Number luasql_table_optnumber(lua_State *L, int idx, const char* name, lua_Number def); +LUASQL_API void luasql_find_driver_table (lua_State *L); + #if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501 void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup); #endif From 862be2aa83f6ac77da3c49fee2deddf1d1edb820 Mon Sep 17 00:00:00 2001 From: blumf Date: Fri, 10 Apr 2015 11:33:46 +0100 Subject: [PATCH 08/64] Connect now supports 'charset' Connection charset can now be selected for Firebird, defaults to 'UTF8' --- src/ls_firebird.c | 69 +++++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index a571863..5388b91 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -22,27 +22,27 @@ typedef struct { short closed; - ISC_STATUS status_vector[20]; /* for error results */ - int lock; /* lock count for open connections */ + ISC_STATUS status_vector[20]; /* for error results */ + int lock; /* lock count for open connections */ } env_data; typedef struct { short closed; - env_data* env; /* the DB enviroment this is in */ - isc_db_handle db; /* the database handle */ - char dpb_buffer[256];/* holds the database paramet buffer */ - short dpb_length; /* the used amount of the dpb */ - isc_tr_handle transaction; /* the transaction handle */ - int lock; /* lock count for open cursors */ - int autocommit; /* should each statement be commited */ + env_data* env; /* the DB enviroment this is in */ + isc_db_handle db; /* the database handle */ + char dpb_buffer[1024]; /* holds the database paramet buffer */ + short dpb_length; /* the used amount of the dpb */ + isc_tr_handle transaction; /* the transaction handle */ + int lock; /* lock count for open cursors */ + int autocommit; /* should each statement be commited */ } conn_data; typedef struct { short closed; - env_data* env; /* the DB enviroment this is in */ - conn_data* conn; /* the DB connection this cursor is from */ - isc_stmt_handle stmt; /* the statement handle */ - XSQLDA *out_sqlda; /* the cursor data array */ + env_data* env; /* the DB enviroment this is in */ + conn_data* conn; /* the DB connection this cursor is from */ + isc_stmt_handle stmt; /* the statement handle */ + XSQLDA *out_sqlda; /* the cursor data array */ } cur_data; /* How many fields to pre-alloc to the cursor */ @@ -940,6 +940,20 @@ static void env_connect_fix_old (lua_State *L) { } } +static char* add_dpb_string(char* dpb, char item, const char* str) +{ + size_t len = strlen(str); + size_t i; + + *dpb++ = item; + *dpb++ = (char)len; + for(i=0; istatus_vector) ) return return_db_error(L, conn.env->status_vector); From c4a5d2f22b602c2c5dddc38745d4bfec5221c761 Mon Sep 17 00:00:00 2001 From: blumf Date: Fri, 10 Apr 2015 11:40:06 +0100 Subject: [PATCH 09/64] Document change in comment --- src/ls_firebird.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 5388b91..2591c80 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -956,9 +956,12 @@ static char* add_dpb_string(char* dpb, char item, const char* str) /* ** Creates and returns a connection object -** Lua Input: { source, user, password, ... } -** source: data source -** user, pass: data source authentication information +** Lua Input: { +** source = , +** user = , +** password = , +** [charset = ,] +** } ** Lua Returns: ** connection object if successfull ** nil and error message otherwise. From 342e875c56ecf4b75536ed14e25f4fd88bbe5349 Mon Sep 17 00:00:00 2001 From: blumf Date: Fri, 10 Apr 2015 11:49:20 +0100 Subject: [PATCH 10/64] Renamed functions to avoid confusion These may end up in the wider LuaSQL API, plus their current name made them look like official Lua API --- src/ls_firebird.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 2591c80..6c59ba6 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -93,7 +93,7 @@ static int return_db_error(lua_State *L, const ISC_STATUS *pvector) /* ** Registers a given C object in the registry to avoid GC */ -static void lua_registerobj(lua_State *L, int index, void *obj) +static void luasql_registerobj(lua_State *L, int index, void *obj) { lua_pushvalue(L, index); lua_pushlightuserdata(L, obj); @@ -105,7 +105,7 @@ static void lua_registerobj(lua_State *L, int index, void *obj) /* ** Unregisters a given C object from the registry */ -static void lua_unregisterobj(lua_State *L, void *obj) +static void luasql_unregisterobj(lua_State *L, void *obj) { lua_pushlightuserdata(L, obj); lua_pushnil(L); @@ -437,7 +437,7 @@ static int conn_execute (lua_State *L) { memcpy((void*)user_cur, (void*)&cur, sizeof(cur_data)); /* add cursor to the lock count */ - lua_registerobj(L, 1, conn); + luasql_registerobj(L, 1, conn); ++conn->lock; } else { /* a count */ if( (count = count_rows_affected(&cur)) < 0 ) { @@ -540,7 +540,7 @@ static int conn_close (lua_State *L) { /* check environment can be GC'd */ if(conn->env->lock == 0) - lua_unregisterobj(L, conn->env); + luasql_unregisterobj(L, conn->env); lua_pushboolean(L, 1); return 1; @@ -565,7 +565,7 @@ static int conn_gc (lua_State *L) { /* check environment can be GC'd */ if(conn->env->lock == 0) - lua_unregisterobj(L, conn->env); + luasql_unregisterobj(L, conn->env); } return 0; @@ -852,7 +852,7 @@ static int cur_close (lua_State *L) { /* check if connection can be unregistered */ if(cur->conn->lock == 0) - lua_unregisterobj(L, cur->conn); + luasql_unregisterobj(L, cur->conn); /* return sucsess */ lua_pushboolean(L, 1); @@ -882,7 +882,7 @@ static int cur_gc (lua_State *L) { /* check if connection can be unregistered */ if(cur->conn->lock == 0) - lua_unregisterobj(L, cur->conn); + luasql_unregisterobj(L, cur->conn); } return 0; @@ -1040,7 +1040,7 @@ static int env_connect (lua_State *L) { res_conn->closed = 0; /* connect now officially open */ /* register the connection */ - lua_registerobj(L, 1, env); + luasql_registerobj(L, 1, env); ++env->lock; return 1; @@ -1067,7 +1067,7 @@ static int env_close (lua_State *L) { return luasql_faildirect(L, "there are still open connections"); /* unregister */ - lua_unregisterobj(L, env); + luasql_unregisterobj(L, env); /* mark as closed */ env->closed = 1; From e3d38982831f260766e1e8870bda87ebc4148aa2 Mon Sep 17 00:00:00 2001 From: blumf Date: Sat, 11 Apr 2015 22:22:50 +0100 Subject: [PATCH 11/64] Firebird driver supports basic prepared statements Mass of refactoring and other work, but the Firebird driver now has prepared statements. No ability to set parameters yet though. --- src/ls_firebird.c | 516 +++++++++++++++++++++++++++++++--------------- 1 file changed, 352 insertions(+), 164 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 6c59ba6..48c1036 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -18,6 +18,7 @@ #define LUASQL_ENVIRONMENT_FIREBIRD "Firebird environment" #define LUASQL_CONNECTION_FIREBIRD "Firebird connection" +#define LUASQL_STATEMENT_FIREBIRD "Firebird statement" #define LUASQL_CURSOR_FIREBIRD "Firebird cursor" typedef struct { @@ -41,7 +42,17 @@ typedef struct { short closed; env_data* env; /* the DB enviroment this is in */ conn_data* conn; /* the DB connection this cursor is from */ - isc_stmt_handle stmt; /* the statement handle */ + XSQLDA *in_sqlda; /* the parameter data array */ + isc_stmt_handle handle; /* the statement handle */ + int type; /* the statment's type (SELECT, UPDATE, etc...) */ + int lock; /* lock count for open statements */ +} stmt_data; + +typedef struct { + short closed; + env_data* env; /* the DB enviroment this is in */ +// conn_data* conn; /* the DB connection this cursor is from */ + stmt_data* stmt; /* the DB statment this cursor is from */ XSQLDA *out_sqlda; /* the cursor data array */ } cur_data; @@ -90,6 +101,76 @@ static int return_db_error(lua_State *L, const ISC_STATUS *pvector) return 2; } +/* +** Allocate memory for XSQLDA data +*/ +static void malloc_sqlda_vars(XSQLDA *sqlda) +{ + int i; + XSQLVAR *var; + + /* prep the result set ready to handle the data */ + for (i=0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++) { + switch(var->sqltype & ~1) { + case SQL_VARYING: + var->sqldata = (char *)malloc(sizeof(char)*var->sqllen + 2); + break; + case SQL_TEXT: + var->sqldata = (char *)malloc(sizeof(char)*var->sqllen); + break; + case SQL_SHORT: + var->sqldata = (char *)malloc(sizeof(short)); + break; + case SQL_LONG: + var->sqldata = (char *)malloc(sizeof(long)); + break; + case SQL_INT64: + var->sqldata = (char *)malloc(sizeof(ISC_INT64)); + break; + case SQL_FLOAT: + var->sqldata = (char *)malloc(sizeof(float)); + break; + case SQL_DOUBLE: + var->sqldata = (char *)malloc(sizeof(double)); + break; + case SQL_TYPE_TIME: + var->sqldata = (char *)malloc(sizeof(ISC_TIME)); + break; + case SQL_TYPE_DATE: + var->sqldata = (char *)malloc(sizeof(ISC_DATE)); + break; + case SQL_TIMESTAMP: + var->sqldata = (char *)malloc(sizeof(ISC_TIMESTAMP)); + break; + case SQL_BLOB: + var->sqldata = (char *)malloc(sizeof(ISC_QUAD)); + break; + /* TODO : add extra data type handles here */ + } + + if (var->sqltype & 1) { + /* allocate variable to hold NULL status */ + var->sqlind = (short *)malloc(sizeof(short)); + } else { + var->sqlind = NULL; + } + } +} + +/* +** Frees memory allocated to XSQLDA data +*/ +static void free_sqlda_vars(XSQLDA *sqlda) { + int i; + XSQLVAR *var; + + /* prep the result set ready to handle the data */ + for (i=0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++) { + free(var->sqldata); + free(var->sqlind); + } +} + /* ** Registers a given C object in the registry to avoid GC */ @@ -112,20 +193,25 @@ static void luasql_unregisterobj(lua_State *L, void *obj) lua_settable(L, LUA_REGISTRYINDEX); } +/* +** Free's up the memory alloc'd to the statement data +*/ +static void free_stmt(stmt_data* stmt) +{ + /* free the field memory blocks */ + free_sqlda_vars(stmt->in_sqlda); + + /* free the data array */ + free(stmt->in_sqlda); +} + /* ** Free's up the memory alloc'd to the cursor data */ static void free_cur(cur_data* cur) { - int i; - XSQLVAR *var; - /* free the field memory blocks */ - for (i=0, var = cur->out_sqlda->sqlvar; i < cur->out_sqlda->sqld; i++, var++) { - free(var->sqldata); - if(var->sqlind != NULL) - free(var->sqlind); - } + free_sqlda_vars(cur->out_sqlda); /* free the data array */ free(cur->out_sqlda); @@ -151,6 +237,16 @@ static conn_data *getconnection (lua_State *L, int i) { return conn; } +/* +** Check for valid statement. +*/ +static stmt_data *getstatement (lua_State *L, int i) { + stmt_data *stmt = (stmt_data *)luaL_checkudata (L, i, LUASQL_STATEMENT_FIREBIRD); + luaL_argcheck (L, stmt != NULL, i, "statement expected"); + luaL_argcheck (L, !stmt->closed, i, "statement is closed"); + return stmt; +} + /* ** Check for valid cursor. */ @@ -161,10 +257,51 @@ static cur_data *getcursor (lua_State *L, int i) { return cur; } +/* +** Dumps the list of item's types into a new table +*/ +static int dump_xsqlda_types(lua_State *L, XSQLDA* sqlda) { + int i; + XSQLVAR *var; + + lua_newtable(L); + + for (i=1, var = sqlda->sqlvar; i <= sqlda->sqld; i++, var++) { + lua_pushnumber(L, i); + switch(var->sqltype & ~1) { + case SQL_VARYING: + case SQL_TEXT: + case SQL_TYPE_TIME: + case SQL_TYPE_DATE: + case SQL_TIMESTAMP: + case SQL_BLOB: + lua_pushstring(L, "string"); + break; + case SQL_SHORT: + case SQL_LONG: + case SQL_INT64: +#if LUA_VERSION_NUM>=503 + lua_pushstring(L, "integer"); + break; +#endif + case SQL_FLOAT: + case SQL_DOUBLE: + lua_pushstring(L, "number"); + break; + default: + lua_pushstring(L, "unknown"); + break; + } + lua_settable(L, -3); + } + + return 1; +} + /* ** Returns the statement type */ -static int get_statement_type(cur_data* cur) +static int get_statement_type(stmt_data *stmt) { int length, type; char type_item[] = { isc_info_sql_stmt_type }; @@ -172,10 +309,10 @@ static int get_statement_type(cur_data* cur) pres = res_buffer; - isc_dsql_sql_info( cur->env->status_vector, &cur->stmt, + isc_dsql_sql_info( stmt->env->status_vector, &stmt->handle, sizeof(type_item), type_item, sizeof(res_buffer), res_buffer ); - if (cur->env->status_vector[0] == 1 && cur->env->status_vector[1] > 0) + if (stmt->env->status_vector[0] == 1 && stmt->env->status_vector[1] > 0) return -1; /* check the type of the statement */ @@ -195,7 +332,7 @@ static int get_statement_type(cur_data* cur) /* ** Return the number of rows affected by last operation */ -static int count_rows_affected(cur_data* cur) +static int count_rows_affected(env_data *env, cur_data *cur) { int length, type, res=0; int del_count = 0, ins_count = 0, upd_count = 0, sel_count = 0; @@ -204,7 +341,7 @@ static int count_rows_affected(cur_data* cur) pres = res_buffer; - isc_dsql_sql_info( cur->env->status_vector, &cur->stmt, + isc_dsql_sql_info( env->status_vector, cur->stmt->handle, sizeof(type_item), type_item, sizeof(res_buffer), res_buffer ); if (cur->env->status_vector[0] == 1 && cur->env->status_vector[1] > 0) @@ -286,136 +423,129 @@ static int count_rows_affected(cur_data* cur) } /* -** Executes a SQL statement. +** Prepares a SQL statement. +** Lua input: +** SQL statement[, SQL dialect] ** Returns -** cursor object: if there are results or -** row count: number of rows affected by statement if no results +** statement object ready for setting parameters +** nil and error message otherwise. */ -static int conn_execute (lua_State *L) { +static int conn_prepare (lua_State *L) { conn_data *conn = getconnection(L,1); const char *statement = luaL_checkstring(L, 2); int dialect = (int)luaL_optnumber(L, 3, 3); - XSQLVAR *var; - long dtype; - int i, n, count, stmt_type; + stmt_data* user_stmt; - cur_data cur; + stmt_data stmt; - cur.closed = 0; - cur.env = conn->env; - cur.conn = conn; - cur.stmt = 0; + memset(&stmt, 0, sizeof(stmt_data)); - cur.out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(CURSOR_PREALLOC)); - cur.out_sqlda->version = SQLDA_VERSION1; - cur.out_sqlda->sqln = CURSOR_PREALLOC; + stmt.closed = 0; + stmt.env = conn->env; + stmt.conn = conn; + + stmt.handle = NULL; /* create a statement to handle the query */ - isc_dsql_allocate_statement(conn->env->status_vector, &conn->db, &cur.stmt); + isc_dsql_allocate_statement(conn->env->status_vector, &conn->db, &stmt.handle); if ( CHECK_DB_ERROR(conn->env->status_vector) ) { - free(cur.out_sqlda); return return_db_error(L, conn->env->status_vector); } /* process the SQL ready to run the query */ - isc_dsql_prepare(conn->env->status_vector, &conn->transaction, &cur.stmt, 0, (char*)statement, dialect, cur.out_sqlda); + isc_dsql_prepare(conn->env->status_vector, &conn->transaction, &stmt.handle, 0, (char*)statement, dialect, NULL); if ( CHECK_DB_ERROR(conn->env->status_vector) ) { - free(cur.out_sqlda); + free_stmt(&stmt); return return_db_error(L, conn->env->status_vector); } /* what type of SQL statement is it? */ - stmt_type = get_statement_type(&cur); - if(stmt_type < 0) { - free(cur.out_sqlda); - return return_db_error(L, conn->env->status_vector); + stmt.type = get_statement_type(&stmt); + if(stmt.type < 0) { + free_stmt(&stmt); + return return_db_error(L, stmt.env->status_vector); } /* an unsupported SQL statement (something like COMMIT) */ - if(stmt_type > 5) { - free(cur.out_sqlda); + if(stmt.type > 5) { + free_stmt(&stmt); return luasql_faildirect(L, "unsupported SQL statement"); } - /* resize the result set if needed */ - if (cur.out_sqlda->sqld > cur.out_sqlda->sqln) + /* bind the input parameters */ + stmt.in_sqlda = (XSQLDA*)malloc(XSQLDA_LENGTH(1)); + stmt.in_sqlda->version = SQLDA_VERSION1; + stmt.in_sqlda->sqln = 1; + isc_dsql_describe_bind(conn->env->status_vector, &stmt.handle, 1, stmt.in_sqlda); + if ( CHECK_DB_ERROR(conn->env->status_vector) ) { + free_stmt(&stmt); + return return_db_error(L, conn->env->status_vector); + } + /* resize the parameter set if needed */ + if (stmt.in_sqlda->sqld > stmt.in_sqlda->sqln) { - n = cur.out_sqlda->sqld; - free(cur.out_sqlda); - cur.out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n)); - cur.out_sqlda->sqln = n; - cur.out_sqlda->version = SQLDA_VERSION1; - isc_dsql_describe(conn->env->status_vector, &cur.stmt, 1, cur.out_sqlda); + short n = stmt.in_sqlda->sqld; + free(stmt.in_sqlda); + stmt.in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n)); + stmt.in_sqlda->sqln = n; + stmt.in_sqlda->version = SQLDA_VERSION1; + isc_dsql_describe_bind(conn->env->status_vector, &stmt.handle, 1, stmt.in_sqlda); if ( CHECK_DB_ERROR(conn->env->status_vector) ) { - free(cur.out_sqlda); + free_stmt(&stmt); return return_db_error(L, conn->env->status_vector); } } + malloc_sqlda_vars(stmt.in_sqlda); - /* prep the result set ready to handle the data */ - for (i=0, var = cur.out_sqlda->sqlvar; i < cur.out_sqlda->sqld; i++, var++) { - dtype = (var->sqltype & ~1); /* drop flag bit for now */ - switch(dtype) { - case SQL_VARYING: - var->sqldata = (char *)malloc(sizeof(char)*var->sqllen + 2); - break; - case SQL_TEXT: - var->sqldata = (char *)malloc(sizeof(char)*var->sqllen); - break; - case SQL_SHORT: - var->sqldata = (char *)malloc(sizeof(short)); - break; - case SQL_LONG: - var->sqldata = (char *)malloc(sizeof(long)); - break; - case SQL_INT64: - var->sqldata = (char *)malloc(sizeof(ISC_INT64)); - break; - case SQL_FLOAT: - var->sqldata = (char *)malloc(sizeof(float)); - break; - case SQL_DOUBLE: - var->sqldata = (char *)malloc(sizeof(double)); - break; - case SQL_TYPE_TIME: - var->sqldata = (char *)malloc(sizeof(ISC_TIME)); - break; - case SQL_TYPE_DATE: - var->sqldata = (char *)malloc(sizeof(ISC_DATE)); - break; - case SQL_TIMESTAMP: - var->sqldata = (char *)malloc(sizeof(ISC_TIMESTAMP)); - break; - case SQL_BLOB: - var->sqldata = (char *)malloc(sizeof(ISC_QUAD)); - break; - /* TODO : add extra data type handles here */ - } + /* copy the statement into a new lua userdata object */ + user_stmt = (stmt_data*)lua_newuserdata(L, sizeof(stmt_data)); + luasql_setmeta (L, LUASQL_STATEMENT_FIREBIRD); + memcpy((void*)user_stmt, (void*)&stmt, sizeof(stmt_data)); - if (var->sqltype & 1) { - /* allocate variable to hold NULL status */ - var->sqlind = (short *)malloc(sizeof(short)); - } else { - var->sqlind = NULL; - } - } + /* add statement to the lock count */ + luasql_registerobj(L, 1, conn); + ++conn->lock; + + return 1; +} + +static int raw_execute (lua_State *L, stmt_data *stmt) +{ + int count; + cur_data cur; + + memset(&cur, 0, sizeof(cur_data)); + + cur.closed = 0; + cur.stmt = stmt; + cur.env = stmt->env; /* run the query */ - isc_dsql_execute(conn->env->status_vector, &conn->transaction, &cur.stmt, 1, NULL); - if ( CHECK_DB_ERROR(conn->env->status_vector) ) { + isc_dsql_execute(stmt->env->status_vector, &stmt->conn->transaction, &stmt->handle, 1, NULL); + if ( CHECK_DB_ERROR(stmt->env->status_vector) ) { free_cur(&cur); - return return_db_error(L, conn->env->status_vector); + return return_db_error(L, cur.env->status_vector); } - /* if autocommit is set and it's a non SELECT query, commit change */ - if(conn->autocommit != 0 && stmt_type > 1) { - isc_commit_retaining(conn->env->status_vector, &conn->transaction); - if ( CHECK_DB_ERROR(conn->env->status_vector) ) { + /* size the result, set if needed */ + cur.out_sqlda = (XSQLDA*)malloc(XSQLDA_LENGTH(1)); + cur.out_sqlda->version = SQLDA_VERSION1; + cur.out_sqlda->sqln = 1; + isc_dsql_describe(cur.env->status_vector, &cur.stmt->handle, 1, cur.out_sqlda); + if (cur.out_sqlda->sqld > cur.out_sqlda->sqln) { + short n = cur.out_sqlda->sqld; + free(cur.out_sqlda); + cur.out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n)); + cur.out_sqlda->sqln = n; + cur.out_sqlda->version = SQLDA_VERSION1; + isc_dsql_describe(cur.env->status_vector, &cur.stmt->handle, 1, cur.out_sqlda); + if ( CHECK_DB_ERROR(cur.env->status_vector) ) { free_cur(&cur); - return return_db_error(L, conn->env->status_vector); + return return_db_error(L, cur.env->status_vector); } } + malloc_sqlda_vars(cur.out_sqlda); /* what do we return? a cursor or a count */ if(cur.out_sqlda->sqld > 0) { /* a cursor */ @@ -426,30 +556,68 @@ static int conn_execute (lua_State *L) { snprintf(cur_name, sizeof(cur_name), "dyn_cursor_%p", (void *)user_cur); /* open the cursor ready for fetch cycles */ - isc_dsql_set_cursor_name(cur.env->status_vector, &cur.stmt, cur_name, 0); - if ( CHECK_DB_ERROR(conn->env->status_vector) ) { + isc_dsql_set_cursor_name(cur.env->status_vector, &cur.stmt->handle, cur_name, 0); + if ( CHECK_DB_ERROR(cur.env->status_vector) ) { lua_pop(L, 1); /* the userdata */ free_cur(&cur); - return return_db_error(L, conn->env->status_vector); + return return_db_error(L, cur.env->status_vector); } /* copy the cursor into a new lua userdata object */ memcpy((void*)user_cur, (void*)&cur, sizeof(cur_data)); - /* add cursor to the lock count */ - luasql_registerobj(L, 1, conn); - ++conn->lock; + /* add statement to the lock count */ + luasql_registerobj(L, -2, user_cur->stmt); + ++user_cur->stmt->lock; } else { /* a count */ - if( (count = count_rows_affected(&cur)) < 0 ) { - free(cur.out_sqlda); - return return_db_error(L, conn->env->status_vector); + /* if autocommit is set, commit change */ + if(cur.stmt->conn->autocommit) { + isc_commit_retaining(cur.env->status_vector, &cur.stmt->conn->transaction); + if ( CHECK_DB_ERROR(cur.env->status_vector) ) { + free_cur(&cur); + return return_db_error(L, cur.env->status_vector); + } + } + + if( (count = count_rows_affected(cur.env, &cur)) < 0 ) { + free_cur(&cur); + return return_db_error(L, cur.env->status_vector); } lua_pushnumber(L, count); /* totaly finished with the cursor */ - isc_dsql_free_statement(conn->env->status_vector, &cur.stmt, DSQL_drop); - free(cur.out_sqlda); + isc_dsql_free_statement(cur.env->status_vector, &cur.stmt->handle, DSQL_drop); + free_cur(&cur); + } + + return 1; +} + +/* +** Executes a SQL statement. +** Lua input: +** SQL statement[, SQL dialect] +** Returns +** cursor object: if there are results or +** row count: number of rows affected by statement if no results +** nil and error message otherwise. +*/ + +static int conn_execute (lua_State *L) { + int ret; + + /* prepare the statement */ + if( (ret = conn_prepare(L)) != 1) { + return ret; + } + + /* execute and check result */ + ret = raw_execute(L, getstatement(L,-1)); + lua_remove(L, -(ret+1)); /* for neatness, remove stmt from stack */ + + if(ret != 1) { + return ret; } return 1; @@ -660,7 +828,7 @@ static void push_column(lua_State *L, int i, cur_data *cur) { /* get the BLOB ID and open it */ memcpy(&blob_id, cur->out_sqlda->sqlvar[i].sqldata, sizeof(ISC_QUAD)); isc_open_blob2( cur->env->status_vector, - &cur->conn->db, &cur->conn->transaction, + &cur->stmt->conn->db, &cur->stmt->conn->transaction, &blob_handle, &blob_id, 0, NULL ); /* fetch the blob data */ luaL_buffinit(L, &b); @@ -690,6 +858,53 @@ static void push_column(lua_State *L, int i, cur_data *cur) { } } +/* +** Returns a map of parameter IDs to their types +*/ +static int stmt_get_params (lua_State *L) { + stmt_data *stmt = getstatement(L,1); + + return dump_xsqlda_types(L, stmt->in_sqlda); +} + +/* +** Executes the statement +** Lua input: +** [table of values] +** Returns +** cursor object: if there are results or +** row count: number of rows affected by statement if no results +** nil and error message otherwise. +*/ +static int stmt_execute (lua_State *L) { + return raw_execute(L, getstatement(L,1)); +} + +/* +** Frees up memory alloc'd to a statement +*/ +static int stmt_gc (lua_State *L) { + stmt_data *stmt = (stmt_data *)luaL_checkudata(L,1,LUASQL_STATEMENT_FIREBIRD); + luaL_argcheck (L, stmt != NULL, 1, "statement expected"); + + if(stmt->closed == 0) { + isc_dsql_free_statement(stmt->env->status_vector, &stmt->handle, DSQL_drop); + + /* free the cursor data */ + free_stmt(stmt); + + /* remove cursor from lock count */ + stmt->closed = 1; + --stmt->conn->lock; + + /* check if connection can be unregistered */ + if(stmt->conn->lock == 0) + luasql_unregisterobj(L, stmt->conn); + } + + return 0; +} + /* ** Returns a row of data from the query ** Lua Returns: @@ -704,7 +919,7 @@ static int cur_fetch (lua_State *L) { int num = strchr(opts, 'n') != NULL; int alpha = strchr(opts, 'a') != NULL; - if ((fetch_stat = isc_dsql_fetch(cur->env->status_vector, &cur->stmt, 1, cur->out_sqlda)) == 0) { + if ((fetch_stat = isc_dsql_fetch(cur->env->status_vector, &cur->stmt->handle, 1, cur->out_sqlda)) == 0) { if (lua_istable (L, 2)) { /* remove the option string */ lua_settop(L, 2); @@ -745,7 +960,7 @@ static int cur_fetch (lua_State *L) { return return_db_error(L, cur->env->status_vector); /* last row has been fetched, close cursor */ - isc_dsql_free_statement(cur->env->status_vector, &cur->stmt, DSQL_drop); + isc_dsql_free_statement(cur->env->status_vector, &cur->stmt->handle, DSQL_drop); if ( CHECK_DB_ERROR(cur->env->status_vector) ) return return_db_error(L, cur->env->status_vector); @@ -754,8 +969,9 @@ static int cur_fetch (lua_State *L) { cur->closed = 1; - /* remove cursor from lock count */ - --cur->conn->lock; + /* remove cursor from lock count and check if connection can be unregistered */ + if(--cur->stmt->lock == 0) + luasql_unregisterobj(L, cur->stmt); /* return sucsess */ return 0; @@ -790,42 +1006,9 @@ static int cur_colnames (lua_State *L) { ** nil and error message otherwise. */ static int cur_coltypes (lua_State *L) { - int i; - XSQLVAR *var; cur_data *cur = getcursor(L,1); - lua_newtable(L); - - for (i=1, var = cur->out_sqlda->sqlvar; i <= cur->out_sqlda->sqld; i++, var++) { - lua_pushnumber(L, i); - switch(var->sqltype & ~1) { - case SQL_VARYING: - case SQL_TEXT: - case SQL_TYPE_TIME: - case SQL_TYPE_DATE: - case SQL_TIMESTAMP: - case SQL_BLOB: - lua_pushstring(L, "string"); - break; - case SQL_SHORT: - case SQL_LONG: - case SQL_INT64: -#if LUA_VERSION_NUM>=503 - lua_pushstring(L, "integer"); - break; -#endif - case SQL_FLOAT: - case SQL_DOUBLE: - lua_pushstring(L, "number"); - break; - default: - lua_pushstring(L, "unknown"); - break; - } - lua_settable(L, -3); - } - - return 1; + return dump_xsqlda_types(L, cur->out_sqlda); } /* @@ -839,20 +1022,17 @@ static int cur_close (lua_State *L) { luaL_argcheck (L, cur != NULL, 1, "cursor expected"); if(cur->closed == 0) { - isc_dsql_free_statement(cur->env->status_vector, &cur->stmt, DSQL_drop); + isc_dsql_free_statement(cur->env->status_vector, &cur->stmt->handle, DSQL_drop); if ( CHECK_DB_ERROR(cur->env->status_vector) ) return return_db_error(L, cur->env->status_vector); /* free the cursor data */ free_cur(cur); - /* remove cursor from lock count */ + /* remove cursor from lock count and check if connection can be unregistered */ cur->closed = 1; - --cur->conn->lock; - - /* check if connection can be unregistered */ - if(cur->conn->lock == 0) - luasql_unregisterobj(L, cur->conn); + if(--cur->stmt->lock == 0) + luasql_unregisterobj(L, cur->stmt); /* return sucsess */ lua_pushboolean(L, 1); @@ -871,18 +1051,18 @@ static int cur_gc (lua_State *L) { luaL_argcheck (L, cur != NULL, 1, "cursor expected"); if(cur->closed == 0) { - isc_dsql_free_statement(cur->env->status_vector, &cur->stmt, DSQL_drop); + isc_dsql_free_statement(cur->env->status_vector, &cur->stmt->handle, DSQL_drop); /* free the cursor data */ free_cur(cur); /* remove cursor from lock count */ cur->closed = 1; - --cur->conn->lock; + --cur->stmt->lock; /* check if connection can be unregistered */ - if(cur->conn->lock == 0) - luasql_unregisterobj(L, cur->conn); + if(cur->stmt->lock == 0) + luasql_unregisterobj(L, cur->stmt); } return 0; @@ -1097,6 +1277,7 @@ static void create_metatables (lua_State *L) { struct luaL_Reg connection_methods[] = { {"__gc", conn_gc}, {"close", conn_close}, + {"prepare", conn_prepare}, {"execute", conn_execute}, {"commit", conn_commit}, {"rollback", conn_rollback}, @@ -1104,6 +1285,12 @@ static void create_metatables (lua_State *L) { {"escape", conn_escape}, {NULL, NULL}, }; + struct luaL_Reg statement_methods[] = { + {"__gc", stmt_gc}, + {"list_params", stmt_get_params}, + {"execute", stmt_execute}, + {NULL, NULL}, + }; struct luaL_Reg cursor_methods[] = { {"__gc", cur_gc}, {"close", cur_close}, @@ -1114,8 +1301,9 @@ static void create_metatables (lua_State *L) { }; luasql_createmeta (L, LUASQL_ENVIRONMENT_FIREBIRD, environment_methods); luasql_createmeta (L, LUASQL_CONNECTION_FIREBIRD, connection_methods); + luasql_createmeta (L, LUASQL_STATEMENT_FIREBIRD, statement_methods); luasql_createmeta (L, LUASQL_CURSOR_FIREBIRD, cursor_methods); - lua_pop (L, 3); + lua_pop (L, 4); } /* From 2be82a8bed87ce2d20038875237a7c48ab8d1473 Mon Sep 17 00:00:00 2001 From: blumf Date: Sun, 12 Apr 2015 00:26:06 +0100 Subject: [PATCH 12/64] Firebird SQL dialect config Now specify the SQL dialect via a connection parameter --- src/ls_firebird.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 48c1036..0c11ee7 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -28,14 +28,18 @@ typedef struct { } env_data; typedef struct { - short closed; + /* general */ env_data* env; /* the DB enviroment this is in */ + short closed; + int lock; /* lock count for open cursors */ + int autocommit; /* should each statement be commited */ + /* implimentation */ isc_db_handle db; /* the database handle */ char dpb_buffer[1024]; /* holds the database paramet buffer */ short dpb_length; /* the used amount of the dpb */ isc_tr_handle transaction; /* the transaction handle */ - int lock; /* lock count for open cursors */ - int autocommit; /* should each statement be commited */ + /* config */ + unsigned short dialect; /* dialect of SQL used */ } conn_data; typedef struct { @@ -425,7 +429,7 @@ static int count_rows_affected(env_data *env, cur_data *cur) /* ** Prepares a SQL statement. ** Lua input: -** SQL statement[, SQL dialect] +** SQL statement ** Returns ** statement object ready for setting parameters ** nil and error message otherwise. @@ -433,7 +437,6 @@ static int count_rows_affected(env_data *env, cur_data *cur) static int conn_prepare (lua_State *L) { conn_data *conn = getconnection(L,1); const char *statement = luaL_checkstring(L, 2); - int dialect = (int)luaL_optnumber(L, 3, 3); stmt_data* user_stmt; @@ -454,7 +457,7 @@ static int conn_prepare (lua_State *L) { } /* process the SQL ready to run the query */ - isc_dsql_prepare(conn->env->status_vector, &conn->transaction, &stmt.handle, 0, (char*)statement, dialect, NULL); + isc_dsql_prepare(conn->env->status_vector, &conn->transaction, &stmt.handle, 0, (char*)statement, conn->dialect, NULL); if ( CHECK_DB_ERROR(conn->env->status_vector) ) { free_stmt(&stmt); return return_db_error(L, conn->env->status_vector); @@ -597,7 +600,7 @@ static int raw_execute (lua_State *L, stmt_data *stmt) /* ** Executes a SQL statement. ** Lua input: -** SQL statement[, SQL dialect] +** SQL statement ** Returns ** cursor object: if there are results or ** row count: number of rows affected by statement if no results @@ -1141,6 +1144,7 @@ static char* add_dpb_string(char* dpb, char item, const char* str) ** user = , ** password = , ** [charset = ,] +** [dialect = ,] ** } ** Lua Returns: ** connection object if successfull @@ -1192,6 +1196,7 @@ static int env_connect (lua_State *L) { /* other database parameters */ dpb = add_dpb_string(dpb, isc_dpb_lc_ctype, luasql_table_optstring(L, 2, "charset", "UTF8")); + conn.dialect = (unsigned short)luasql_table_optnumber(L, 2, "dialect", 3); /* the length of the dpb */ conn.dpb_length = (short)(dpb - conn.dpb_buffer); From 7aa596ea6f62a16600988f59107d81789677197e Mon Sep 17 00:00:00 2001 From: blumf Date: Wed, 15 Apr 2015 15:58:51 +0100 Subject: [PATCH 13/64] Protecting against buffer overrun Avoid potential buffer overrun from connection parameters --- src/ls_firebird.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 0c11ee7..bbce9f3 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -1123,15 +1123,17 @@ static void env_connect_fix_old (lua_State *L) { } } -static char* add_dpb_string(char* dpb, char item, const char* str) +static char* add_dpb_string(char* dpb, char* dpb_end, char item, const char* str) { size_t len = strlen(str); size_t i; - *dpb++ = item; - *dpb++ = (char)len; - for(i=0; i Date: Thu, 16 Apr 2015 14:19:07 +0100 Subject: [PATCH 14/64] Execute now accepts parameters Both connection and statement objects now accept parameters being passed to their execute method --- src/ls_firebird.c | 101 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 94 insertions(+), 7 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index bbce9f3..9cd92e3 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -55,7 +55,6 @@ typedef struct { typedef struct { short closed; env_data* env; /* the DB enviroment this is in */ -// conn_data* conn; /* the DB connection this cursor is from */ stmt_data* stmt; /* the DB statment this cursor is from */ XSQLDA *out_sqlda; /* the cursor data array */ } cur_data; @@ -168,10 +167,11 @@ static void free_sqlda_vars(XSQLDA *sqlda) { int i; XSQLVAR *var; - /* prep the result set ready to handle the data */ - for (i=0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++) { - free(var->sqldata); - free(var->sqlind); + if(sqlda != NULL) { + for (i=0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++) { + free(var->sqldata); + free(var->sqlind); + } } } @@ -426,10 +426,83 @@ static int count_rows_affected(env_data *env, cur_data *cur) return res; } +static void parse_params(lua_State *L, stmt_data* stmt, int params) +{ + int i; + for(i=0; iin_sqlda->sqln; i++) { + XSQLVAR *var; + const char* str; + + lua_pushnumber(L, i+1); + lua_gettable(L, params); + + var = &stmt->in_sqlda->sqlvar[i]; + if(var->sqlind == NULL) { + var->sqlind = (ISC_SHORT *)malloc(sizeof(ISC_SHORT)); + } + + if(lua_isnil(L, -1)) { + // nil -> NULL + *var->sqlind = -1; + } else { + switch(var->sqltype & ~1) { + case SQL_VARYING: + case SQL_VARYING+1: + case SQL_BLOB: + case SQL_TEXT: + case SQL_TEXT+1: + case SQL_TIMESTAMP: + case SQL_TYPE_TIME: + case SQL_TYPE_DATE: + if(var->sqldata != NULL) { + free(var->sqldata); + } + var->sqltype = SQL_TEXT+1; + *var->sqlind = 0; + + str = lua_tostring(L, -1); + var->sqllen = strlen(str); + var->sqldata = (ISC_SCHAR *)malloc(var->sqllen+1); + strncpy(var->sqldata, str, var->sqllen); + break; + + case SQL_INT64: + case SQL_LONG: + case SQL_SHORT: + var->sqltype = SQL_INT64; + *var->sqlind = 0; + if(var->sqldata != NULL) { + free(var->sqldata); + } + var->sqldata = (ISC_SCHAR *)malloc(sizeof(ISC_INT64)); + *(ISC_INT64 *)var->sqldata = (ISC_INT64)lua_tonumber(L, -1); + var->sqllen = sizeof(ISC_INT64); + break; + + case SQL_DOUBLE: + case SQL_D_FLOAT: + case SQL_FLOAT: + var->sqltype = SQL_DOUBLE; + *var->sqlind = 0; + if(var->sqldata != NULL) { + free(var->sqldata); + } + var->sqldata = (ISC_SCHAR *)malloc(sizeof(double)); + *(double *)var->sqldata = lua_tonumber(L, -1); + var->sqllen = sizeof(double); + break; + } + } + + lua_pop(L,1); /* param value */ + } +} + /* ** Prepares a SQL statement. ** Lua input: ** SQL statement +** [parmeter table] ** Returns ** statement object ready for setting parameters ** nil and error message otherwise. @@ -491,6 +564,7 @@ static int conn_prepare (lua_State *L) { short n = stmt.in_sqlda->sqld; free(stmt.in_sqlda); stmt.in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n)); + memset(stmt.in_sqlda, 0, XSQLDA_LENGTH(n)); stmt.in_sqlda->sqln = n; stmt.in_sqlda->version = SQLDA_VERSION1; isc_dsql_describe_bind(conn->env->status_vector, &stmt.handle, 1, stmt.in_sqlda); @@ -501,6 +575,11 @@ static int conn_prepare (lua_State *L) { } malloc_sqlda_vars(stmt.in_sqlda); + /* is there a parameter table to use */ + if(lua_istable(L, 3)) { + parse_params(L, &stmt, 3); + } + /* copy the statement into a new lua userdata object */ user_stmt = (stmt_data*)lua_newuserdata(L, sizeof(stmt_data)); luasql_setmeta (L, LUASQL_STATEMENT_FIREBIRD); @@ -525,7 +604,7 @@ static int raw_execute (lua_State *L, stmt_data *stmt) cur.env = stmt->env; /* run the query */ - isc_dsql_execute(stmt->env->status_vector, &stmt->conn->transaction, &stmt->handle, 1, NULL); + isc_dsql_execute(stmt->env->status_vector, &stmt->conn->transaction, &stmt->handle, 1, stmt->in_sqlda); if ( CHECK_DB_ERROR(stmt->env->status_vector) ) { free_cur(&cur); return return_db_error(L, cur.env->status_vector); @@ -601,6 +680,7 @@ static int raw_execute (lua_State *L, stmt_data *stmt) ** Executes a SQL statement. ** Lua input: ** SQL statement +** [parameter table] ** Returns ** cursor object: if there are results or ** row count: number of rows affected by statement if no results @@ -880,7 +960,14 @@ static int stmt_get_params (lua_State *L) { ** nil and error message otherwise. */ static int stmt_execute (lua_State *L) { - return raw_execute(L, getstatement(L,1)); + stmt_data *stmt = getstatement(L,1); + + /* is there a parameter table to use */ + if(lua_istable(L, 2)) { + parse_params(L, stmt, 2); + } + + return raw_execute(L, stmt); } /* From 9327b235ef0e033264ebabc449d3cf179a4dd69a Mon Sep 17 00:00:00 2001 From: blumf Date: Thu, 16 Apr 2015 15:51:29 +0100 Subject: [PATCH 15/64] Proper cursor handling Handling of cursor state and it's relation to statements is laid out properly --- src/ls_firebird.c | 116 ++++++++++++++++++++++++---------------------- 1 file changed, 61 insertions(+), 55 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 9cd92e3..84ab143 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -221,6 +221,27 @@ static void free_cur(cur_data* cur) free(cur->out_sqlda); } +/* +** Shuts down a cursor +*/ +static int cur_shut(lua_State *L, cur_data *cur) +{ + isc_dsql_free_statement(cur->env->status_vector, &cur->stmt->handle, DSQL_close); + if ( CHECK_DB_ERROR(cur->env->status_vector) ) { + return return_db_error(L, cur->env->status_vector); + } + + /* free the cursor data */ + free_cur(cur); + + /* remove cursor from lock count and check if connection can be unregistered */ + cur->closed = 1; + if(--cur->stmt->lock == 0) + luasql_unregisterobj(L, cur->stmt); + + return 0; +} + /* ** Check for valid environment. */ @@ -592,17 +613,43 @@ static int conn_prepare (lua_State *L) { return 1; } -static int raw_execute (lua_State *L, stmt_data *stmt) +static int raw_execute (lua_State *L, int stmt_indx) { int count; cur_data cur; + stmt_data *stmt; - memset(&cur, 0, sizeof(cur_data)); + if(stmt_indx < 0) { + stmt_indx = lua_gettop(L) + stmt_indx + 1; + } + + stmt = getstatement(L,stmt_indx); + /* is there already a cursor open */ + if(stmt->lock > 0) { + return luasql_faildirect(L, "statement already has an open cursor"); + } + + memset(&cur, 0, sizeof(cur_data)); cur.closed = 0; cur.stmt = stmt; cur.env = stmt->env; + /* if it's a SELECT statment, allocate a cursor */ + if(stmt->type == 1) { + char cur_name[64]; + + snprintf(cur_name, sizeof(cur_name), "dyn_cursor_%p", (void *)stmt); + + /* open the cursor ready for fetch cycles */ + isc_dsql_set_cursor_name(cur.env->status_vector, &cur.stmt->handle, cur_name, 0); + if ( CHECK_DB_ERROR(cur.env->status_vector) ) { + lua_pop(L, 1); /* the userdata */ + free_cur(&cur); + return return_db_error(L, cur.env->status_vector); + } + } + /* run the query */ isc_dsql_execute(stmt->env->status_vector, &stmt->conn->transaction, &stmt->handle, 1, stmt->in_sqlda); if ( CHECK_DB_ERROR(stmt->env->status_vector) ) { @@ -631,25 +678,14 @@ static int raw_execute (lua_State *L, stmt_data *stmt) /* what do we return? a cursor or a count */ if(cur.out_sqlda->sqld > 0) { /* a cursor */ - char cur_name[64]; cur_data* user_cur = (cur_data*)lua_newuserdata(L, sizeof(cur_data)); luasql_setmeta (L, LUASQL_CURSOR_FIREBIRD); - snprintf(cur_name, sizeof(cur_name), "dyn_cursor_%p", (void *)user_cur); - - /* open the cursor ready for fetch cycles */ - isc_dsql_set_cursor_name(cur.env->status_vector, &cur.stmt->handle, cur_name, 0); - if ( CHECK_DB_ERROR(cur.env->status_vector) ) { - lua_pop(L, 1); /* the userdata */ - free_cur(&cur); - return return_db_error(L, cur.env->status_vector); - } - /* copy the cursor into a new lua userdata object */ memcpy((void*)user_cur, (void*)&cur, sizeof(cur_data)); /* add statement to the lock count */ - luasql_registerobj(L, -2, user_cur->stmt); + luasql_registerobj(L, stmt_indx, user_cur->stmt); ++user_cur->stmt->lock; } else { /* a count */ /* if autocommit is set, commit change */ @@ -696,7 +732,7 @@ static int conn_execute (lua_State *L) { } /* execute and check result */ - ret = raw_execute(L, getstatement(L,-1)); + ret = raw_execute(L, -1); lua_remove(L, -(ret+1)); /* for neatness, remove stmt from stack */ if(ret != 1) { @@ -967,7 +1003,7 @@ static int stmt_execute (lua_State *L) { parse_params(L, stmt, 2); } - return raw_execute(L, stmt); + return raw_execute(L, 1); } /* @@ -1049,22 +1085,8 @@ static int cur_fetch (lua_State *L) { if (fetch_stat != 100L) return return_db_error(L, cur->env->status_vector); - /* last row has been fetched, close cursor */ - isc_dsql_free_statement(cur->env->status_vector, &cur->stmt->handle, DSQL_drop); - if ( CHECK_DB_ERROR(cur->env->status_vector) ) - return return_db_error(L, cur->env->status_vector); - - /* free the cursor data */ - free_cur(cur); - - cur->closed = 1; - - /* remove cursor from lock count and check if connection can be unregistered */ - if(--cur->stmt->lock == 0) - luasql_unregisterobj(L, cur->stmt); - - /* return sucsess */ - return 0; + /* shut cursor */ + return cur_shut(L, cur); } /* @@ -1112,17 +1134,10 @@ static int cur_close (lua_State *L) { luaL_argcheck (L, cur != NULL, 1, "cursor expected"); if(cur->closed == 0) { - isc_dsql_free_statement(cur->env->status_vector, &cur->stmt->handle, DSQL_drop); - if ( CHECK_DB_ERROR(cur->env->status_vector) ) - return return_db_error(L, cur->env->status_vector); - - /* free the cursor data */ - free_cur(cur); - - /* remove cursor from lock count and check if connection can be unregistered */ - cur->closed = 1; - if(--cur->stmt->lock == 0) - luasql_unregisterobj(L, cur->stmt); + int res = cur_shut(L, cur); + if(res != 0) { + return res; + } /* return sucsess */ lua_pushboolean(L, 1); @@ -1141,18 +1156,9 @@ static int cur_gc (lua_State *L) { luaL_argcheck (L, cur != NULL, 1, "cursor expected"); if(cur->closed == 0) { - isc_dsql_free_statement(cur->env->status_vector, &cur->stmt->handle, DSQL_drop); - - /* free the cursor data */ - free_cur(cur); - - /* remove cursor from lock count */ - cur->closed = 1; - --cur->stmt->lock; - - /* check if connection can be unregistered */ - if(cur->stmt->lock == 0) - luasql_unregisterobj(L, cur->stmt); + if(cur_shut(L, cur) != 0) { + return 1; + } } return 0; From b4fd2b4ebc6129a4e2b0c196de42d301cef94ab2 Mon Sep 17 00:00:00 2001 From: blumf Date: Thu, 16 Apr 2015 16:07:45 +0100 Subject: [PATCH 16/64] Proper statement handling Statements now check if any open cursors are depending on it and properly releases itself from the connection --- src/ls_firebird.c | 70 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 84ab143..aa50904 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -209,6 +209,23 @@ static void free_stmt(stmt_data* stmt) free(stmt->in_sqlda); } +static int stmt_shut(lua_State *L, stmt_data* stmt) +{ + isc_dsql_free_statement(stmt->env->status_vector, &stmt->handle, DSQL_drop); + if ( CHECK_DB_ERROR(stmt->env->status_vector) ) { + return return_db_error(L, stmt->env->status_vector); + } + + free_stmt(stmt); + + /* remove statement from lock count and check if connection can be unregistered */ + stmt->closed = 1; + if(--stmt->conn->lock == 0) + luasql_unregisterobj(L, stmt->conn); + + return 0; +} + /* ** Free's up the memory alloc'd to the cursor data */ @@ -234,7 +251,7 @@ static int cur_shut(lua_State *L, cur_data *cur) /* free the cursor data */ free_cur(cur); - /* remove cursor from lock count and check if connection can be unregistered */ + /* remove cursor from lock count and check if statment can be unregistered */ cur->closed = 1; if(--cur->stmt->lock == 0) luasql_unregisterobj(L, cur->stmt); @@ -807,9 +824,9 @@ static int conn_close (lua_State *L) { return 1; } - /* are all related cursors closed? */ + /* are all related statements closed? */ if(conn->lock > 0) - return luasql_faildirect(L, "there are still open cursors"); + return luasql_faildirect(L, "there are still open statements/cursors"); if(conn->autocommit != 0) isc_commit_transaction(conn->env->status_vector, &conn->transaction); @@ -1007,25 +1024,45 @@ static int stmt_execute (lua_State *L) { } /* -** Frees up memory alloc'd to a statement +** Closes a statement object +** Lua Returns: +** true if close was sucsessful, false if already closed +** nil and error message otherwise. */ -static int stmt_gc (lua_State *L) { +static int stmt_close (lua_State *L) { stmt_data *stmt = (stmt_data *)luaL_checkudata(L,1,LUASQL_STATEMENT_FIREBIRD); luaL_argcheck (L, stmt != NULL, 1, "statement expected"); + if(stmt->lock > 0) { + return luasql_faildirect(L, "there are still open cursors"); + } + if(stmt->closed == 0) { - isc_dsql_free_statement(stmt->env->status_vector, &stmt->handle, DSQL_drop); + int res = stmt_shut(L, stmt); + if(res != 0) { + return res; + } - /* free the cursor data */ - free_stmt(stmt); + /* return sucsess */ + lua_pushboolean(L, 1); + return 1; + } - /* remove cursor from lock count */ - stmt->closed = 1; - --stmt->conn->lock; + lua_pushboolean(L, 0); + return 1; +} - /* check if connection can be unregistered */ - if(stmt->conn->lock == 0) - luasql_unregisterobj(L, stmt->conn); +/* +** Frees up memory alloc'd to a statement +*/ +static int stmt_gc (lua_State *L) { + stmt_data *stmt = (stmt_data *)luaL_checkudata(L,1,LUASQL_STATEMENT_FIREBIRD); + luaL_argcheck (L, stmt != NULL, 1, "statement expected"); + + if(stmt->closed == 0) { + if(stmt_shut(L, stmt) != 0) { + return 1; + } } return 0; @@ -1126,7 +1163,7 @@ static int cur_coltypes (lua_State *L) { /* ** Closes a cursor object ** Lua Returns: -** 1 if close was sucsessful, 0 if already closed +** true if close was sucsessful, false if already closed ** nil and error message otherwise. */ static int cur_close (lua_State *L) { @@ -1388,7 +1425,8 @@ static void create_metatables (lua_State *L) { }; struct luaL_Reg statement_methods[] = { {"__gc", stmt_gc}, - {"list_params", stmt_get_params}, + {"close", stmt_close}, + {"getparamtypes", stmt_get_params}, {"execute", stmt_execute}, {NULL, NULL}, }; From 507fe0c3936b51bedef8acad0a7d4bde9270a8e3 Mon Sep 17 00:00:00 2001 From: blumf Date: Thu, 16 Apr 2015 17:41:28 +0100 Subject: [PATCH 17/64] Close none cursor statements Close statements on conn:execute when they don't return cursors Also some minor fixes/tidy --- src/ls_firebird.c | 28 ++++++++++++++++++---------- tests/test.lua | 3 +++ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index aa50904..2171880 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -383,7 +383,7 @@ static int count_rows_affected(env_data *env, cur_data *cur) pres = res_buffer; - isc_dsql_sql_info( env->status_vector, cur->stmt->handle, + isc_dsql_sql_info( env->status_vector, &cur->stmt->handle, sizeof(type_item), type_item, sizeof(res_buffer), res_buffer ); if (cur->env->status_vector[0] == 1 && cur->env->status_vector[1] > 0) @@ -653,9 +653,8 @@ static int raw_execute (lua_State *L, int stmt_indx) cur.env = stmt->env; /* if it's a SELECT statment, allocate a cursor */ - if(stmt->type == 1) { + if(stmt->type == isc_info_sql_stmt_select) { char cur_name[64]; - snprintf(cur_name, sizeof(cur_name), "dyn_cursor_%p", (void *)stmt); /* open the cursor ready for fetch cycles */ @@ -701,7 +700,7 @@ static int raw_execute (lua_State *L, int stmt_indx) /* copy the cursor into a new lua userdata object */ memcpy((void*)user_cur, (void*)&cur, sizeof(cur_data)); - /* add statement to the lock count */ + /* add cursor to the lock count */ luasql_registerobj(L, stmt_indx, user_cur->stmt); ++user_cur->stmt->lock; } else { /* a count */ @@ -719,10 +718,10 @@ static int raw_execute (lua_State *L, int stmt_indx) return return_db_error(L, cur.env->status_vector); } - lua_pushnumber(L, count); + luasql_pushinteger(L, count); /* totaly finished with the cursor */ - isc_dsql_free_statement(cur.env->status_vector, &cur.stmt->handle, DSQL_drop); + isc_dsql_free_statement(cur.env->status_vector, &cur.stmt->handle, DSQL_close); free_cur(&cur); } @@ -742,6 +741,7 @@ static int raw_execute (lua_State *L, int stmt_indx) static int conn_execute (lua_State *L) { int ret; + stmt_data *stmt; /* prepare the statement */ if( (ret = conn_prepare(L)) != 1) { @@ -749,13 +749,21 @@ static int conn_execute (lua_State *L) { } /* execute and check result */ - ret = raw_execute(L, -1); - lua_remove(L, -(ret+1)); /* for neatness, remove stmt from stack */ - - if(ret != 1) { + if((ret = raw_execute(L, -1)) != 1) { return ret; } + /* for neatness, remove stmt from stack */ + stmt = getstatement(L, -(ret+1)); + lua_remove(L, -(ret+1)); + + /* if statement doesn't return a cursor, close it */ + if(stmt->type != isc_info_sql_stmt_select) { + if((ret = stmt_shut(L, stmt)) != 0) { + return ret; + } + } + return 1; } diff --git a/tests/test.lua b/tests/test.lua index 0531cc4..70387d2 100644 --- a/tests/test.lua +++ b/tests/test.lua @@ -174,6 +174,9 @@ function create_table () CONN = CONN_OK (ENV:connect (datasource, username, password)) -- Create t. local cmd = define_table(TOTAL_FIELDS) + print() + print(cmd) + print() assert2 (CREATE_TABLE_RETURN_VALUE, CONN:execute (cmd)) end From 36b6a08fa912e5d7b249dddbd99403ac5c3c4488 Mon Sep 17 00:00:00 2001 From: blumf Date: Fri, 17 Apr 2015 14:43:33 +0100 Subject: [PATCH 18/64] Using unambiguous date format Convert date stamps to unambiguous YYYY-MM-DD format also supported by Firebird (e.g. value can be passed as a param) --- src/ls_firebird.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 2171880..42375a0 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -960,12 +960,12 @@ static void push_column(lua_State *L, int i, cur_data *cur) { break; case SQL_TYPE_DATE: isc_decode_sql_date((ISC_DATE*)(cur->out_sqlda->sqlvar[i].sqldata), &timevar); - strftime(timestr, 255, "%x", &timevar); + strftime(timestr, 255, "%Y-%m-%d", &timevar); lua_pushstring(L, timestr); break; case SQL_TIMESTAMP: isc_decode_timestamp((ISC_TIMESTAMP*)(cur->out_sqlda->sqlvar[i].sqldata), &timevar); - strftime(timestr, 255, "%x %X", &timevar); + strftime(timestr, 255, "%Y-%m-%d %X", &timevar); lua_pushstring(L, timestr); break; case SQL_BLOB: From 5b79d90b5199b5f1eb3fc21337e6f4a42a0db221 Mon Sep 17 00:00:00 2001 From: blumf Date: Fri, 17 Apr 2015 16:01:01 +0100 Subject: [PATCH 19/64] Parameters now take os.time values As well as timestamp strings, parameters can handle values returned from os.time() --- src/ls_firebird.c | 56 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 42375a0..3eca40d 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -75,8 +75,10 @@ typedef struct { #if LUA_VERSION_NUM>=503 #define luasql_pushinteger lua_pushinteger +#define luasql_isinteger lua_isinteger #else #define luasql_pushinteger lua_pushnumber +#define luasql_isinteger lua_isnumber #endif /* MSVC still doesn't support C99 properly until 2015 */ @@ -485,13 +487,8 @@ static void parse_params(lua_State *L, stmt_data* stmt, int params) } else { switch(var->sqltype & ~1) { case SQL_VARYING: - case SQL_VARYING+1: case SQL_BLOB: case SQL_TEXT: - case SQL_TEXT+1: - case SQL_TIMESTAMP: - case SQL_TYPE_TIME: - case SQL_TYPE_DATE: if(var->sqldata != NULL) { free(var->sqldata); } @@ -529,6 +526,55 @@ static void parse_params(lua_State *L, stmt_data* stmt, int params) *(double *)var->sqldata = lua_tonumber(L, -1); var->sqllen = sizeof(double); break; + + case SQL_TIMESTAMP: + case SQL_TYPE_TIME: + case SQL_TYPE_DATE: + switch(lua_type(L, -1)) { + case LUA_TNUMBER: { + /* os.time type value passed */ + time_t t_time = (time_t)lua_tointeger(L,-1); + struct tm *tm_time = localtime(&t_time); + + if(var->sqldata != NULL) { + free(var->sqldata); + } + var->sqltype = SQL_TIMESTAMP+1; + *var->sqlind = 0; + + var->sqldata = (ISC_SCHAR *)malloc(sizeof(ISC_TIMESTAMP)); + isc_encode_timestamp(tm_time, (ISC_TIMESTAMP *)var->sqldata); + } break; + + case LUA_TSTRING: { + /* date/time string passed */ + if(var->sqldata != NULL) { + free(var->sqldata); + } + var->sqltype = SQL_TEXT+1; + *var->sqlind = 0; + + str = lua_tostring(L, -1); + + var->sqllen = strlen(str); + var->sqldata = (ISC_SCHAR *)malloc(var->sqllen+1); + strncpy(var->sqldata, str, var->sqllen); + } break; + } + + default: { + /* unknown pass empty string, which should error out */ + if(var->sqldata != NULL) { + free(var->sqldata); + } + var->sqltype = SQL_TEXT+1; + *var->sqlind = 0; + + var->sqllen = 0; + var->sqldata = (ISC_SCHAR *)malloc(1); + *var->sqldata = '\0'; + } + break; } } From 65bfb3d89832452fe1c6054de0deac599829acde Mon Sep 17 00:00:00 2001 From: blumf Date: Fri, 17 Apr 2015 16:27:06 +0100 Subject: [PATCH 20/64] Neaten up parameter fill code Too much repetition of code, slimmed it down --- src/ls_firebird.c | 84 +++++++++++++++++------------------------------ 1 file changed, 31 insertions(+), 53 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 3eca40d..2cdc22f 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -466,12 +466,31 @@ static int count_rows_affected(env_data *env, cur_data *cur) return res; } +static void fill_param(XSQLVAR *var, ISC_SHORT type, ISC_SCHAR *data, ISC_SHORT len) +{ + var->sqltype = type; + *var->sqlind = 0; + var->sqllen = len; + + if((type & ~1) == SQL_TEXT) { + --var->sqllen; + } + + if(var->sqldata != NULL) { + free(var->sqldata); + } + var->sqldata = (ISC_SCHAR *)malloc(len); + memcpy(var->sqldata, data, len); +} + static void parse_params(lua_State *L, stmt_data* stmt, int params) { int i; for(i=0; iin_sqlda->sqln; i++) { XSQLVAR *var; const char* str; + ISC_INT64 inum; + double fnum; lua_pushnumber(L, i+1); lua_gettable(L, params); @@ -489,42 +508,22 @@ static void parse_params(lua_State *L, stmt_data* stmt, int params) case SQL_VARYING: case SQL_BLOB: case SQL_TEXT: - if(var->sqldata != NULL) { - free(var->sqldata); - } - var->sqltype = SQL_TEXT+1; - *var->sqlind = 0; - str = lua_tostring(L, -1); - var->sqllen = strlen(str); - var->sqldata = (ISC_SCHAR *)malloc(var->sqllen+1); - strncpy(var->sqldata, str, var->sqllen); + fill_param(var, SQL_TEXT+1, (ISC_SCHAR *)str, strlen(str)+1); break; case SQL_INT64: case SQL_LONG: case SQL_SHORT: - var->sqltype = SQL_INT64; - *var->sqlind = 0; - if(var->sqldata != NULL) { - free(var->sqldata); - } - var->sqldata = (ISC_SCHAR *)malloc(sizeof(ISC_INT64)); - *(ISC_INT64 *)var->sqldata = (ISC_INT64)lua_tonumber(L, -1); - var->sqllen = sizeof(ISC_INT64); + inum = (ISC_INT64)lua_tonumber(L, -1); + fill_param(var, SQL_INT64+1, (ISC_SCHAR *)&inum, sizeof(ISC_INT64)); break; case SQL_DOUBLE: case SQL_D_FLOAT: case SQL_FLOAT: - var->sqltype = SQL_DOUBLE; - *var->sqlind = 0; - if(var->sqldata != NULL) { - free(var->sqldata); - } - var->sqldata = (ISC_SCHAR *)malloc(sizeof(double)); - *(double *)var->sqldata = lua_tonumber(L, -1); - var->sqllen = sizeof(double); + fnum = (double)lua_tonumber(L, -1); + fill_param(var, SQL_DOUBLE+1, (ISC_SCHAR *)&fnum, sizeof(double)); break; case SQL_TIMESTAMP: @@ -535,44 +534,23 @@ static void parse_params(lua_State *L, stmt_data* stmt, int params) /* os.time type value passed */ time_t t_time = (time_t)lua_tointeger(L,-1); struct tm *tm_time = localtime(&t_time); + ISC_TIMESTAMP isc_ts; + isc_encode_timestamp(tm_time, &isc_ts); - if(var->sqldata != NULL) { - free(var->sqldata); - } - var->sqltype = SQL_TIMESTAMP+1; - *var->sqlind = 0; - - var->sqldata = (ISC_SCHAR *)malloc(sizeof(ISC_TIMESTAMP)); - isc_encode_timestamp(tm_time, (ISC_TIMESTAMP *)var->sqldata); + fill_param(var, SQL_TIMESTAMP+1, (ISC_SCHAR *)&isc_ts, sizeof(ISC_TIMESTAMP)); } break; case LUA_TSTRING: { /* date/time string passed */ - if(var->sqldata != NULL) { - free(var->sqldata); - } - var->sqltype = SQL_TEXT+1; - *var->sqlind = 0; - str = lua_tostring(L, -1); - - var->sqllen = strlen(str); - var->sqldata = (ISC_SCHAR *)malloc(var->sqllen+1); - strncpy(var->sqldata, str, var->sqllen); + fill_param(var, SQL_TEXT+1, (ISC_SCHAR *)str, strlen(str)+1); } break; - } default: { /* unknown pass empty string, which should error out */ - if(var->sqldata != NULL) { - free(var->sqldata); - } - var->sqltype = SQL_TEXT+1; - *var->sqlind = 0; - - var->sqllen = 0; - var->sqldata = (ISC_SCHAR *)malloc(1); - *var->sqldata = '\0'; + str = lua_tostring(L, -1); + fill_param(var, SQL_TEXT+1, (ISC_SCHAR *)"", 1); + } break; } break; } From f405cb8a68d51a48bbcb61778e877940007dbdc3 Mon Sep 17 00:00:00 2001 From: blumf Date: Fri, 17 Apr 2015 17:14:27 +0100 Subject: [PATCH 21/64] Force close of statements used internally When a cursor created from a direct con:execute call closes, the associated statement wasn't closed until garbage collection. Now flagging those internal statements so they can be forced closed with the cursor --- src/ls_firebird.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 2cdc22f..8121252 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -50,6 +50,7 @@ typedef struct { isc_stmt_handle handle; /* the statement handle */ int type; /* the statment's type (SELECT, UPDATE, etc...) */ int lock; /* lock count for open statements */ + unsigned char hidden; /* statement was used interally i.e. from a direct con:execute */ } stmt_data; typedef struct { @@ -222,8 +223,9 @@ static int stmt_shut(lua_State *L, stmt_data* stmt) /* remove statement from lock count and check if connection can be unregistered */ stmt->closed = 1; - if(--stmt->conn->lock == 0) + if(--stmt->conn->lock == 0) { luasql_unregisterobj(L, stmt->conn); + } return 0; } @@ -255,9 +257,15 @@ static int cur_shut(lua_State *L, cur_data *cur) /* remove cursor from lock count and check if statment can be unregistered */ cur->closed = 1; - if(--cur->stmt->lock == 0) + if(--cur->stmt->lock == 0) { luasql_unregisterobj(L, cur->stmt); + /* hidden statement, needs closing now */ + if(cur->stmt->hidden) { + return stmt_shut(L, cur->stmt); + } + } + return 0; } @@ -781,6 +789,9 @@ static int conn_execute (lua_State *L) { stmt = getstatement(L, -(ret+1)); lua_remove(L, -(ret+1)); + /* this will be an internal, hidden statment */ + stmt->hidden = 1; + /* if statement doesn't return a cursor, close it */ if(stmt->type != isc_info_sql_stmt_select) { if((ret = stmt_shut(L, stmt)) != 0) { From 7a709dc1e785e67d3ed52d7e83bfaeb473888939 Mon Sep 17 00:00:00 2001 From: blumf Date: Fri, 17 Apr 2015 17:21:41 +0100 Subject: [PATCH 22/64] Using uchar for holding bool value Didn't make much sense to use a short just to hold a bool value (and can't rely on existing) --- src/ls_firebird.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 8121252..8e11205 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -22,15 +22,15 @@ #define LUASQL_CURSOR_FIREBIRD "Firebird cursor" typedef struct { - short closed; - ISC_STATUS status_vector[20]; /* for error results */ - int lock; /* lock count for open connections */ + unsigned char closed; + ISC_STATUS status_vector[20];/* for error results */ + int lock; /* lock count for open connections */ } env_data; typedef struct { /* general */ + unsigned char closed; env_data* env; /* the DB enviroment this is in */ - short closed; int lock; /* lock count for open cursors */ int autocommit; /* should each statement be commited */ /* implimentation */ @@ -43,21 +43,21 @@ typedef struct { } conn_data; typedef struct { - short closed; - env_data* env; /* the DB enviroment this is in */ - conn_data* conn; /* the DB connection this cursor is from */ - XSQLDA *in_sqlda; /* the parameter data array */ + unsigned char closed; + env_data* env; /* the DB enviroment this is in */ + conn_data* conn; /* the DB connection this cursor is from */ + XSQLDA *in_sqlda; /* the parameter data array */ isc_stmt_handle handle; /* the statement handle */ int type; /* the statment's type (SELECT, UPDATE, etc...) */ - int lock; /* lock count for open statements */ + int lock; /* lock count for open statements */ unsigned char hidden; /* statement was used interally i.e. from a direct con:execute */ } stmt_data; typedef struct { - short closed; - env_data* env; /* the DB enviroment this is in */ - stmt_data* stmt; /* the DB statment this cursor is from */ - XSQLDA *out_sqlda; /* the cursor data array */ + unsigned char closed; + env_data* env; /* the DB enviroment this is in */ + stmt_data* stmt; /* the DB statment this cursor is from */ + XSQLDA *out_sqlda; /* the cursor data array */ } cur_data; /* How many fields to pre-alloc to the cursor */ From 2e200629f299786ce536313b0a26488b82b32ccc Mon Sep 17 00:00:00 2001 From: blumf Date: Fri, 17 Apr 2015 17:25:39 +0100 Subject: [PATCH 23/64] Scratch prev commit Forgot luasql_tostring relies on the layout of the structs starting with short --- src/ls_firebird.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 8e11205..19147d6 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -22,14 +22,14 @@ #define LUASQL_CURSOR_FIREBIRD "Firebird cursor" typedef struct { - unsigned char closed; + short closed; ISC_STATUS status_vector[20];/* for error results */ int lock; /* lock count for open connections */ } env_data; typedef struct { /* general */ - unsigned char closed; + short closed; env_data* env; /* the DB enviroment this is in */ int lock; /* lock count for open cursors */ int autocommit; /* should each statement be commited */ @@ -43,7 +43,7 @@ typedef struct { } conn_data; typedef struct { - unsigned char closed; + short closed; env_data* env; /* the DB enviroment this is in */ conn_data* conn; /* the DB connection this cursor is from */ XSQLDA *in_sqlda; /* the parameter data array */ @@ -54,7 +54,7 @@ typedef struct { } stmt_data; typedef struct { - unsigned char closed; + short closed; env_data* env; /* the DB enviroment this is in */ stmt_data* stmt; /* the DB statment this cursor is from */ XSQLDA *out_sqlda; /* the cursor data array */ From 2a4a8161d5e18f49c1b7e13bd20ce2c461f52508 Mon Sep 17 00:00:00 2001 From: blumf Date: Fri, 17 Apr 2015 17:46:20 +0100 Subject: [PATCH 24/64] Unified a bunch of memory code Reducing the line count --- src/ls_firebird.c | 62 ++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 27 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 19147d6..1a18c5e 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -107,6 +107,20 @@ static int return_db_error(lua_State *L, const ISC_STATUS *pvector) return 2; } +/* +** Allocates and initialises an XSQLDA struct +*/ +static XSQLDA* malloc_xsqlda(ISC_SHORT len) +{ + XSQLDA *res = (XSQLDA*)malloc(XSQLDA_LENGTH(len)); + + memset(res, 0, XSQLDA_LENGTH(len)); + res->version = SQLDA_VERSION1; + res->sqln = len; + + return res; +} + /* ** Allocate memory for XSQLDA data */ @@ -178,6 +192,15 @@ static void free_sqlda_vars(XSQLDA *sqlda) { } } +/* +** Frees all XSQLDA data +*/ +static void free_xsqlda(XSQLDA *sqlda) +{ + free_sqlda_vars(sqlda); + free(sqlda); +} + /* ** Registers a given C object in the registry to avoid GC */ @@ -205,11 +228,8 @@ static void luasql_unregisterobj(lua_State *L, void *obj) */ static void free_stmt(stmt_data* stmt) { - /* free the field memory blocks */ - free_sqlda_vars(stmt->in_sqlda); - - /* free the data array */ - free(stmt->in_sqlda); + /* free the input DA */ + free_xsqlda(stmt->in_sqlda); } static int stmt_shut(lua_State *L, stmt_data* stmt) @@ -235,11 +255,8 @@ static int stmt_shut(lua_State *L, stmt_data* stmt) */ static void free_cur(cur_data* cur) { - /* free the field memory blocks */ - free_sqlda_vars(cur->out_sqlda); - - /* free the data array */ - free(cur->out_sqlda); + /* free the output DA */ + free_xsqlda(cur->out_sqlda); } /* @@ -620,9 +637,7 @@ static int conn_prepare (lua_State *L) { } /* bind the input parameters */ - stmt.in_sqlda = (XSQLDA*)malloc(XSQLDA_LENGTH(1)); - stmt.in_sqlda->version = SQLDA_VERSION1; - stmt.in_sqlda->sqln = 1; + stmt.in_sqlda = malloc_xsqlda(1); isc_dsql_describe_bind(conn->env->status_vector, &stmt.handle, 1, stmt.in_sqlda); if ( CHECK_DB_ERROR(conn->env->status_vector) ) { free_stmt(&stmt); @@ -631,12 +646,9 @@ static int conn_prepare (lua_State *L) { /* resize the parameter set if needed */ if (stmt.in_sqlda->sqld > stmt.in_sqlda->sqln) { - short n = stmt.in_sqlda->sqld; - free(stmt.in_sqlda); - stmt.in_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n)); - memset(stmt.in_sqlda, 0, XSQLDA_LENGTH(n)); - stmt.in_sqlda->sqln = n; - stmt.in_sqlda->version = SQLDA_VERSION1; + ISC_SHORT n = stmt.in_sqlda->sqld; + free_xsqlda(stmt.in_sqlda); + stmt.in_sqlda = malloc_xsqlda(n); isc_dsql_describe_bind(conn->env->status_vector, &stmt.handle, 1, stmt.in_sqlda); if ( CHECK_DB_ERROR(conn->env->status_vector) ) { free_stmt(&stmt); @@ -706,16 +718,12 @@ static int raw_execute (lua_State *L, int stmt_indx) } /* size the result, set if needed */ - cur.out_sqlda = (XSQLDA*)malloc(XSQLDA_LENGTH(1)); - cur.out_sqlda->version = SQLDA_VERSION1; - cur.out_sqlda->sqln = 1; + cur.out_sqlda = malloc_xsqlda(1); isc_dsql_describe(cur.env->status_vector, &cur.stmt->handle, 1, cur.out_sqlda); if (cur.out_sqlda->sqld > cur.out_sqlda->sqln) { - short n = cur.out_sqlda->sqld; - free(cur.out_sqlda); - cur.out_sqlda = (XSQLDA *)malloc(XSQLDA_LENGTH(n)); - cur.out_sqlda->sqln = n; - cur.out_sqlda->version = SQLDA_VERSION1; + ISC_SHORT n = cur.out_sqlda->sqld; + free_xsqlda(cur.out_sqlda); + cur.out_sqlda = malloc_xsqlda(n); isc_dsql_describe(cur.env->status_vector, &cur.stmt->handle, 1, cur.out_sqlda); if ( CHECK_DB_ERROR(cur.env->status_vector) ) { free_cur(&cur); From 1bb7db1daaf14a446d1ab4433494e407011e4ed5 Mon Sep 17 00:00:00 2001 From: blumf Date: Mon, 20 Apr 2015 14:15:05 +0100 Subject: [PATCH 25/64] Tidied up ODBC's env:connect functions Neater layout to the ODBC driver's connect function --- src/ls_odbc.c | 190 +++++++++++++++++++++++--------------------------- 1 file changed, 87 insertions(+), 103 deletions(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index 56b7849..36f9a71 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -601,34 +601,42 @@ static int create_connection (lua_State *L, int o, env_data *env, SQLHDBC hdbc) return 1; } +/* +** Uses a DSN string to connect to a ODBC source dynamically +** Lua Input: { +** dsn = , +** } +** Lua Returns: +** connection object if successfull +** nil and error message otherwise. +*/ static int env_table_connect_DSN (lua_State *L) { env_data *env = (env_data *) getenvironment (L); - SQLCHAR *sourcename = (SQLCHAR*)luaL_checkstring (L, -1); + SQLCHAR *sourcename = (SQLCHAR*)luasql_table_optstring(L, 2, "dsn", NULL); SQLHDBC hdbc; SQLCHAR sqlOutBuf[4097]; SQLSMALLINT sqlOutLen; SQLRETURN ret; - lua_pop(L, 1); - - ret = SQLSetEnvAttr (env->henv, SQL_ATTR_ODBC_VERSION, - (void*)SQL_OV_ODBC3, 0); - if (error(ret)) + ret = SQLSetEnvAttr (env->henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); + if (error(ret)) { return luasql_faildirect (L, "error setting SQL version."); + } + /* tries to allocate connection handle */ ret = SQLAllocHandle (hDBC, env->henv, &hdbc); - if (error(ret)) + if (error(ret)) { return luasql_faildirect (L, "connection allocation error."); + } + /* tries to connect handle */ - ret = SQLDriverConnect (hdbc, NULL, - sourcename, SQL_NTS, - sqlOutBuf, 4096, &sqlOutLen, - SQL_DRIVER_NOPROMPT); + ret = SQLDriverConnect (hdbc, NULL, sourcename, SQL_NTS, sqlOutBuf, 4096, &sqlOutLen, SQL_DRIVER_NOPROMPT); if (error(ret)) { ret = fail(L, hDBC, hdbc); SQLFreeHandle(hDBC, hdbc); return ret; } + /* success, return connection object */ ret = create_connection (L, 1, env, hdbc); if(ret == 1) { @@ -636,103 +644,95 @@ static int env_table_connect_DSN (lua_State *L) { lua_pushlstring(L, (char*)sqlOutBuf, sqlOutLen); return 2; } - return ret; -} -static int env_table_connect_standard (lua_State *L) { - env_data *env = (env_data *) getenvironment (L); - SQLCHAR *sourcename = (SQLCHAR*)luaL_checkstring (L, -1); - SQLCHAR *username = NULL; - SQLCHAR *password = NULL; - SQLHDBC hdbc; - SQLRETURN ret; - - lua_pop(L, 1); - - /* get user */ - lua_getfield(L, 2, "user"); - if(lua_isstring(L, -1)) - username = (SQLCHAR*)luaL_checkstring(L, -1); - lua_pop(L, 1); - - /* get password */ - lua_getfield(L, 2, "password"); - if(lua_isstring(L, -1)) - password = (SQLCHAR*)luaL_checkstring(L, -1); - lua_pop(L, 1); - - /* tries to allocate connection handle */ - ret = SQLAllocHandle (hDBC, env->henv, &hdbc); - if (error(ret)) - return luasql_faildirect (L, "connection allocation error."); - - /* tries to connect handle */ - ret = SQLConnect (hdbc, sourcename, SQL_NTS, - username, SQL_NTS, password, SQL_NTS); - if (error(ret)) { - ret = fail(L, hDBC, hdbc); - SQLFreeHandle(hDBC, hdbc); - return ret; - } - /* success, return connection object */ - return create_connection (L, 1, env, hdbc); + return ret; } /* -** Creates and returns a connection object -** Lua Input: tab -** tab: table of connection parameters -** "dsn" : A custom DSN -** or -** "source" : The ODBC source DSN -** "user" : The connecting user -** "password" : The connecting user's password -** general params -** "" : ? +** Reforms old connection style to new one +** Lua Input: source, [user, [pass]] +** source: data source +** user, pass: data source authentication information ** Lua Returns: -** connection object if successfull -** nil and error message otherwise. +** new connection details table */ -static int env_table_connect (lua_State *L) { - lua_getfield(L, 2, "dsn"); - if(lua_isstring(L, -1)) - return env_table_connect_DSN(L); /* use the custom DSN string */ - lua_pop(L, 1); - - lua_getfield(L, 2, "source"); - if(lua_isstring(L, -1)) - return env_table_connect_standard(L); /* standard source [user, password] */ - lua_pop(L, 1); - - /* failed to get any meaningful connection info*/ - return luasql_faildirect (L, "no meaningful connection info given"); +static void env_connect_fix_old (lua_State *L) { + if(lua_isstring(L, 2)) { + /* convert to new table format */ + int n = lua_gettop(L); + + const char *sourcename = luaL_checkstring (L, 2); + const char *username = luaL_optstring (L, 3, NULL); + const char *password = luaL_optstring (L, 4, NULL); + + lua_newtable(L); + lua_pushstring(L, "source"); + lua_pushstring(L, sourcename); + lua_settable(L, -3); + if(username != NULL) { + lua_pushstring(L, "user"); + lua_pushstring(L, username); + lua_settable(L, -3); + } + if(password != NULL) { + lua_pushstring(L, "password"); + lua_pushstring(L, password); + lua_settable(L, -3); + } + + while(n > 1) { + lua_remove(L, n--); + } + } } /* ** Creates and returns a connection object -** Lua Input: source [, user [, pass]] -** source: data source -** user, pass: data source authentication information +** Lua Input: { +** source = , +** user = , +** password = , +** or +** dsn = , +** } ** Lua Returns: ** connection object if successfull ** nil and error message otherwise. */ -static int env_std_connect (lua_State *L) { +static int env_connect (lua_State *L) { env_data *env = (env_data *) getenvironment (L); - SQLCHAR *sourcename = (SQLCHAR*)luaL_checkstring (L, 2); - SQLCHAR *username = (SQLCHAR*)luaL_optstring (L, 3, NULL); - SQLCHAR *password = (SQLCHAR*)luaL_optstring (L, 4, NULL); - SQLHDBC hdbc; - SQLRETURN ret; + SQLCHAR *sourcename = NULL; + SQLCHAR *username = NULL; + SQLCHAR *password = NULL; + SQLHDBC hdbc = NULL; + SQLRETURN ret = 0; + + if(lua_gettop(L) < 2) { + return luasql_faildirect(L, "No connection details provided"); + } + + if(!lua_istable(L, 2)) { + env_connect_fix_old(L); + } + + /* check for the custom DSN connection string */ + if(luasql_table_optstring(L, 2, "dsn", NULL) != NULL) { + return env_table_connect_DSN(L); + } + + /* get the standard connection details */ + sourcename = (SQLCHAR*)luasql_table_optstring(L, 2, "source", NULL); + username = (SQLCHAR*)luasql_table_optstring(L, 2, "user", NULL); + password = (SQLCHAR*)luasql_table_optstring(L, 2, "password", NULL); /* tries to allocate connection handle */ ret = SQLAllocHandle (hDBC, env->henv, &hdbc); - if (error(ret)) + if (error(ret)) { return luasql_faildirect (L, "connection allocation error."); + } /* tries to connect handle */ - ret = SQLConnect (hdbc, sourcename, SQL_NTS, - username, SQL_NTS, password, SQL_NTS); + ret = SQLConnect (hdbc, sourcename, SQL_NTS, username, SQL_NTS, password, SQL_NTS); if (error(ret)) { ret = fail(L, hDBC, hdbc); SQLFreeHandle(hDBC, hdbc); @@ -743,22 +743,6 @@ static int env_std_connect (lua_State *L) { return create_connection (L, 1, env, hdbc); } -/* -** Creates and returns a connection object -** Lua Input: see env_std_connect and env_table_connect -** Lua Returns: -** connection object if successfull -** nil and error message otherwise. -*/ -static int env_connect (lua_State *L) { - /* have we been given a connection paramater list */ - if(lua_istable(L, 2)) - return env_table_connect(L); - - /* just use the standard conection arguments */ - return env_std_connect(L); -} - /* ** Closes an environment object */ From fc1ac8b46e01e3d0c40a7f623200f81fb957a5c9 Mon Sep 17 00:00:00 2001 From: blumf Date: Mon, 20 Apr 2015 14:49:02 +0100 Subject: [PATCH 26/64] Create concept of statements Opening up the struct and metatable for statements in ODBC --- src/ls_odbc.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index 36f9a71..bdf8f56 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -30,6 +30,7 @@ #define LUASQL_ENVIRONMENT_ODBC "ODBC environment" #define LUASQL_CONNECTION_ODBC "ODBC connection" +#define LUASQL_STATEMENT_ODBC "ODBC statement" #define LUASQL_CURSOR_ODBC "ODBC cursor" @@ -47,6 +48,9 @@ typedef struct { SQLHDBC hdbc; /* database connection handle */ } conn_data; +typedef struct { + short closed; +} stmt_data; typedef struct { short closed; @@ -786,6 +790,9 @@ static void create_metatables (lua_State *L) { {"setautocommit", conn_setautocommit}, {NULL, NULL}, }; + struct luaL_Reg statement_methods[] = { + {NULL, NULL}, + }; struct luaL_Reg cursor_methods[] = { {"__gc", cur_close}, /* Should this method be changed? */ {"close", cur_close}, @@ -796,8 +803,9 @@ static void create_metatables (lua_State *L) { }; luasql_createmeta (L, LUASQL_ENVIRONMENT_ODBC, environment_methods); luasql_createmeta (L, LUASQL_CONNECTION_ODBC, connection_methods); + luasql_createmeta (L, LUASQL_STATEMENT_ODBC, statement_methods); luasql_createmeta (L, LUASQL_CURSOR_ODBC, cursor_methods); - lua_pop (L, 3); + lua_pop (L, 4); } From 8af1e32e6f22df511ff4c8ef0eb3d7c59abf3d7c Mon Sep 17 00:00:00 2001 From: blumf Date: Mon, 20 Apr 2015 15:01:32 +0100 Subject: [PATCH 27/64] Format tidy up Mainly always use braces --- src/ls_odbc.c | 280 ++++++++++++++++++++++++++++---------------------- 1 file changed, 155 insertions(+), 125 deletions(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index bdf8f56..1a182ec 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -107,8 +107,8 @@ static cur_data *getcursor (lua_State *L) { ** Pushes true and returns 1 */ static int pass(lua_State *L) { - lua_pushboolean (L, 1); - return 1; + lua_pushboolean (L, 1); + return 1; } @@ -119,55 +119,56 @@ static int pass(lua_State *L) { ** handle: handle used in operation */ static int fail(lua_State *L, const SQLSMALLINT type, const SQLHANDLE handle) { - SQLCHAR State[6]; - SQLINTEGER NativeError; - SQLSMALLINT MsgSize, i; - SQLRETURN ret; - SQLCHAR Msg[SQL_MAX_MESSAGE_LENGTH]; - luaL_Buffer b; - lua_pushnil(L); - - luaL_buffinit(L, &b); - i = 1; - while (1) { - ret = SQLGetDiagRec(type, handle, i, State, &NativeError, Msg, - sizeof(Msg), &MsgSize); - if (ret == SQL_NO_DATA) break; - luaL_addlstring(&b, (char*)Msg, MsgSize); - luaL_addchar(&b, '\n'); - i++; - } - luaL_pushresult(&b); - return 2; + SQLCHAR State[6]; + SQLINTEGER NativeError; + SQLSMALLINT MsgSize, i; + SQLRETURN ret; + SQLCHAR Msg[SQL_MAX_MESSAGE_LENGTH]; + luaL_Buffer b; + lua_pushnil(L); + + luaL_buffinit(L, &b); + i = 1; + while (1) { + ret = SQLGetDiagRec(type, handle, i, State, &NativeError, Msg, sizeof(Msg), &MsgSize); + if (ret == SQL_NO_DATA) { + break; + } + luaL_addlstring(&b, (char*)Msg, MsgSize); + luaL_addchar(&b, '\n'); + i++; + } + luaL_pushresult(&b); + return 2; } /* ** Returns the name of an equivalent lua type for a SQL type. */ static const char *sqltypetolua (const SQLSMALLINT type) { - switch (type) { - case SQL_UNKNOWN_TYPE: case SQL_CHAR: case SQL_VARCHAR: - case SQL_TYPE_DATE: case SQL_TYPE_TIME: case SQL_TYPE_TIMESTAMP: - case SQL_DATE: case SQL_INTERVAL: case SQL_TIMESTAMP: - case SQL_LONGVARCHAR: - case SQL_WCHAR: case SQL_WVARCHAR: case SQL_WLONGVARCHAR: - return "string"; - case SQL_BIGINT: case SQL_TINYINT: - case SQL_INTEGER: case SQL_SMALLINT: + switch (type) { + case SQL_UNKNOWN_TYPE: case SQL_CHAR: case SQL_VARCHAR: + case SQL_TYPE_DATE: case SQL_TYPE_TIME: case SQL_TYPE_TIMESTAMP: + case SQL_DATE: case SQL_INTERVAL: case SQL_TIMESTAMP: + case SQL_LONGVARCHAR: + case SQL_WCHAR: case SQL_WVARCHAR: case SQL_WLONGVARCHAR: + return "string"; + case SQL_BIGINT: case SQL_TINYINT: + case SQL_INTEGER: case SQL_SMALLINT: #if LUA_VERSION_NUM>=503 - return "integer"; + return "integer"; #endif - case SQL_NUMERIC: case SQL_DECIMAL: - case SQL_FLOAT: case SQL_REAL: case SQL_DOUBLE: - return "number"; - case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: - return "binary"; /* !!!!!! nao seria string? */ - case SQL_BIT: - return "boolean"; - default: - assert(0); - return NULL; - } + case SQL_NUMERIC: case SQL_DECIMAL: + case SQL_FLOAT: case SQL_REAL: case SQL_DOUBLE: + return "number"; + case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: + return "binary"; /* !!!!!! nao seria string? */ + case SQL_BIT: + return "boolean"; + default: + assert(0); + return NULL; + } } @@ -181,65 +182,72 @@ static const char *sqltypetolua (const SQLSMALLINT type) { ** 0 if successfull, non-zero otherwise; */ static int push_column(lua_State *L, int coltypes, const SQLHSTMT hstmt, - SQLUSMALLINT i) { - const char *tname; - char type; - /* get column type from types table */ - lua_rawgeti (L, LUA_REGISTRYINDEX, coltypes); - lua_rawgeti (L, -1, i); /* typename of the column */ - tname = lua_tostring(L, -1); - if (!tname) - return luasql_faildirect(L, "invalid type in table."); - type = tname[1]; - lua_pop(L, 2); /* pops type name and coltypes table */ - - /* deal with data according to type */ - switch (type) { - /* nUmber */ - case 'u': { + SQLUSMALLINT i) { + const char *tname; + char type; + /* get column type from types table */ + lua_rawgeti (L, LUA_REGISTRYINDEX, coltypes); + lua_rawgeti (L, -1, i); /* typename of the column */ + tname = lua_tostring(L, -1); + if (!tname) { + return luasql_faildirect(L, "invalid type in table."); + } + type = tname[1]; + lua_pop(L, 2); /* pops type name and coltypes table */ + + /* deal with data according to type */ + switch (type) { + /* nUmber */ + case 'u': { double num; SQLLEN got; SQLRETURN rc = SQLGetData(hstmt, i, SQL_C_DOUBLE, &num, 0, &got); - if (error(rc)) + if (error(rc)) { return fail(L, hSTMT, hstmt); - if (got == SQL_NULL_DATA) + } + if (got == SQL_NULL_DATA) { lua_pushnil(L); - else + } else { lua_pushnumber(L, num); + } return 0; - } + } #if LUA_VERSION_NUM>=503 - /* iNteger */ + /* iNteger */ case 'n': { long int num; SQLLEN got; SQLRETURN rc = SQLGetData(hstmt, i, SQL_C_SLONG, &num, 0, &got); - if (error(rc)) + if (error(rc)) { return fail(L, hSTMT, hstmt); - if (got == SQL_NULL_DATA) + } + if (got == SQL_NULL_DATA) { lua_pushnil(L); - else + } else { lua_pushinteger(L, num); + } return 0; - } + } #endif - /* bOol */ - case 'o': { + /* bOol */ + case 'o': { char b; SQLLEN got; SQLRETURN rc = SQLGetData(hstmt, i, SQL_C_BIT, &b, 0, &got); - if (error(rc)) + if (error(rc)) { return fail(L, hSTMT, hstmt); - if (got == SQL_NULL_DATA) + } + if (got == SQL_NULL_DATA) { lua_pushnil(L); - else + } else { lua_pushboolean(L, b); + } return 0; - } - /* sTring */ - case 't': - /* bInary */ - case 'i': { + } + /* sTring */ + case 't': + /* bInary */ + case 'i': { SQLSMALLINT stype = (type == 't') ? SQL_C_CHAR : SQL_C_BINARY; SQLLEN got; char *buffer; @@ -257,43 +265,52 @@ static int push_column(lua_State *L, int coltypes, const SQLHSTMT hstmt, if (got >= LUAL_BUFFERSIZE || got == SQL_NO_TOTAL) { got = LUAL_BUFFERSIZE; /* get rid of null termination in string block */ - if (stype == SQL_C_CHAR) got--; + if (stype == SQL_C_CHAR) { + got--; + } } luaL_addsize(&b, got); buffer = luaL_prepbuffer(&b); - rc = SQLGetData(hstmt, i, stype, buffer, - LUAL_BUFFERSIZE, &got); + rc = SQLGetData(hstmt, i, stype, buffer, LUAL_BUFFERSIZE, &got); } /* concat last chunk */ if (rc == SQL_SUCCESS) { if (got >= LUAL_BUFFERSIZE || got == SQL_NO_TOTAL) { got = LUAL_BUFFERSIZE; /* get rid of null termination in string block */ - if (stype == SQL_C_CHAR) got--; + if (stype == SQL_C_CHAR) { + got--; + } } luaL_addsize(&b, got); } - if (rc == SQL_ERROR) return fail(L, hSTMT, hstmt); + if (rc == SQL_ERROR) { + return fail(L, hSTMT, hstmt); + } /* return everything we got */ luaL_pushresult(&b); return 0; + } } - } - return 0; + return 0; } /* ** Get another row of the given cursor. */ static int cur_fetch (lua_State *L) { - cur_data *cur = (cur_data *) getcursor (L); - SQLHSTMT hstmt = cur->hstmt; - int ret; - SQLRETURN rc = SQLFetch(cur->hstmt); - if (rc == SQL_NO_DATA) { - lua_pushnil(L); - return 1; - } else if (error(rc)) return fail(L, hSTMT, hstmt); + cur_data *cur = (cur_data *) getcursor (L); + SQLHSTMT hstmt = cur->hstmt; + int ret; + SQLRETURN rc = SQLFetch(cur->hstmt); + if (rc == SQL_NO_DATA) { + lua_pushnil(L); + return 1; + } else { + if (error(rc)) { + return fail(L, hSTMT, hstmt); + } + } if (lua_istable (L, 2)) { SQLUSMALLINT i; @@ -302,8 +319,9 @@ static int cur_fetch (lua_State *L) { int alpha = strchr (opts, 'a') != NULL; for (i = 1; i <= cur->numcols; i++) { ret = push_column (L, cur->coltypes, hstmt, i); - if (ret) + if (ret) { return ret; + } if (alpha) { lua_rawgeti (L, LUA_REGISTRYINDEX, cur->colnames); lua_rawgeti (L, -1, i); /* gets column name */ @@ -311,21 +329,22 @@ static int cur_fetch (lua_State *L) { lua_rawset (L, 2); /* table[name] = value */ lua_pop (L, 1); /* pops colnames table */ } - if (num) + if (num) { lua_rawseti (L, 2, i); - else + } else { lua_pop (L, 1); /* pops value */ + } } lua_pushvalue (L, 2); return 1; /* return table */ - } - else { + } else { SQLUSMALLINT i; luaL_checkstack (L, cur->numcols, LUASQL_PREFIX"too many columns"); for (i = 1; i <= cur->numcols; i++) { ret = push_column (L, cur->coltypes, hstmt, i); - if (ret) + if (ret) { return ret; + } } return cur->numcols; } @@ -347,11 +366,13 @@ static int cur_close (lua_State *L) { /* Nullify structure fields. */ cur->closed = 1; ret = SQLCloseCursor(cur->hstmt); - if (error(ret)) + if (error(ret)) { return fail(L, hSTMT, cur->hstmt); + } ret = SQLFreeHandle(hSTMT, cur->hstmt); - if (error(ret)) + if (error(ret)) { return fail(L, hSTMT, cur->hstmt); + } /* Decrement cursor counter on connection object */ lua_rawgeti (L, LUA_REGISTRYINDEX, cur->conn); conn = lua_touserdata (L, -1); @@ -359,7 +380,7 @@ static int cur_close (lua_State *L) { luaL_unref (L, LUA_REGISTRYINDEX, cur->conn); luaL_unref (L, LUA_REGISTRYINDEX, cur->colnames); luaL_unref (L, LUA_REGISTRYINDEX, cur->coltypes); - return pass(L); + return pass(L); } @@ -453,29 +474,34 @@ static int create_cursor (lua_State *L, int o, conn_data *conn, static int conn_close (lua_State *L) { SQLRETURN ret; env_data *env; - conn_data *conn = (conn_data *)luaL_checkudata(L,1,LUASQL_CONNECTION_ODBC); + conn_data *conn = (conn_data *)luaL_checkudata(L,1,LUASQL_CONNECTION_ODBC); luaL_argcheck (L, conn != NULL, 1, LUASQL_PREFIX"connection expected"); if (conn->closed) { lua_pushboolean (L, 0); return 1; } - if (conn->cur_counter > 0) + if (conn->cur_counter > 0) { return luaL_error (L, LUASQL_PREFIX"there are open cursors"); + } /* Decrement connection counter on environment object */ lua_rawgeti (L, LUA_REGISTRYINDEX, conn->env); - env = lua_touserdata (L, -1); + env = (env_data *)lua_touserdata (L, -1); env->conn_counter--; /* Nullify structure fields. */ conn->closed = 1; luaL_unref (L, LUA_REGISTRYINDEX, conn->env); ret = SQLDisconnect(conn->hdbc); - if (error(ret)) + if (error(ret)) { return fail(L, hDBC, conn->hdbc); + } + ret = SQLFreeHandle(hDBC, conn->hdbc); - if (error(ret)) + if (error(ret)) { return fail(L, hDBC, conn->hdbc); - return pass(L); + } + + return pass(L); } @@ -493,8 +519,9 @@ static int conn_execute (lua_State *L) { SQLSMALLINT numcols; SQLRETURN ret; ret = SQLAllocHandle(hSTMT, hdbc, &hstmt); - if (error(ret)) + if (error(ret)) { return fail(L, hDBC, hdbc); + } ret = SQLPrepare(hstmt, statement, SQL_NTS); if (error(ret)) { @@ -519,10 +546,10 @@ static int conn_execute (lua_State *L) { return ret; } - if (numcols > 0) - /* if there is a results table (e.g., SELECT) */ + if (numcols > 0) { + /* if there is a results table (e.g., SELECT) */ return create_cursor (L, 1, conn, hstmt, numcols); - else { + } else { /* if action has no results (e.g., UPDATE) */ SQLLEN numrows; ret = SQLRowCount(hstmt, &numrows); @@ -543,10 +570,11 @@ static int conn_execute (lua_State *L) { static int conn_commit (lua_State *L) { conn_data *conn = (conn_data *) getconnection (L); SQLRETURN ret = SQLEndTran(hDBC, conn->hdbc, SQL_COMMIT); - if (error(ret)) + if (error(ret)) { return fail(L, hSTMT, conn->hdbc); - else + } else { return pass(L); + } } /* @@ -555,10 +583,11 @@ static int conn_commit (lua_State *L) { static int conn_rollback (lua_State *L) { conn_data *conn = (conn_data *) getconnection (L); SQLRETURN ret = SQLEndTran(hDBC, conn->hdbc, SQL_ROLLBACK); - if (error(ret)) + if (error(ret)) { return fail(L, hSTMT, conn->hdbc); - else + } else { return pass(L); + } } /* @@ -574,10 +603,11 @@ static int conn_setautocommit (lua_State *L) { ret = SQLSetConnectAttr(conn->hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_OFF, 0); } - if (error(ret)) + if (error(ret)) { return fail(L, hSTMT, conn->hdbc); - else + } else { return pass(L); + } } @@ -587,10 +617,10 @@ static int conn_setautocommit (lua_State *L) { static int create_connection (lua_State *L, int o, env_data *env, SQLHDBC hdbc) { conn_data *conn = (conn_data *) lua_newuserdata(L, sizeof(conn_data)); /* set auto commit mode */ - SQLRETURN ret = SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, - (SQLPOINTER) SQL_AUTOCOMMIT_ON, 0); - if (error(ret)) + SQLRETURN ret = SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, 0); + if (error(ret)) { return fail(L, hDBC, hdbc); + } luasql_setmeta (L, LUASQL_CONNECTION_ODBC); @@ -816,16 +846,16 @@ static int create_environment (lua_State *L) { env_data *env; SQLHENV henv; SQLRETURN ret = SQLAllocHandle(hENV, SQL_NULL_HANDLE, &henv); - if (error(ret)) + if (error(ret)) { return luasql_faildirect(L, "error creating environment."); + } - ret = SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, - (void*)SQL_OV_ODBC3, 0); + ret = SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); if (error(ret)) { ret = luasql_faildirect (L, "error setting SQL version."); SQLFreeHandle (hENV, henv); return ret; - } + } env = (env_data *)lua_newuserdata (L, sizeof (env_data)); luasql_setmeta (L, LUASQL_ENVIRONMENT_ODBC); From f7f91694e35156eaabe8128baf0dc429d48f098f Mon Sep 17 00:00:00 2001 From: blumf Date: Mon, 20 Apr 2015 15:24:28 +0100 Subject: [PATCH 28/64] Move (un)registerobj functions into wider library Was used just in Firebird driver, now available for the whole package --- src/ls_firebird.c | 22 ---------------------- src/luasql.c | 22 ++++++++++++++++++++++ src/luasql.h | 3 +++ 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 1a18c5e..d228696 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -201,28 +201,6 @@ static void free_xsqlda(XSQLDA *sqlda) free(sqlda); } -/* -** Registers a given C object in the registry to avoid GC -*/ -static void luasql_registerobj(lua_State *L, int index, void *obj) -{ - lua_pushvalue(L, index); - lua_pushlightuserdata(L, obj); - lua_pushvalue(L, -2); - lua_settable(L, LUA_REGISTRYINDEX); - lua_pop(L, 1); -} - -/* -** Unregisters a given C object from the registry -*/ -static void luasql_unregisterobj(lua_State *L, void *obj) -{ - lua_pushlightuserdata(L, obj); - lua_pushnil(L); - lua_settable(L, LUA_REGISTRYINDEX); -} - /* ** Free's up the memory alloc'd to the statement data */ diff --git a/src/luasql.c b/src/luasql.c index d943f10..531fcb9 100644 --- a/src/luasql.c +++ b/src/luasql.c @@ -186,3 +186,25 @@ LUASQL_API void luasql_find_driver_table (lua_State *L) { lua_newtable (L); } + +/* +** Registers a given C object in the registry to avoid GC +*/ +void luasql_registerobj(lua_State *L, int index, void *obj) +{ + lua_pushvalue(L, index); + lua_pushlightuserdata(L, obj); + lua_pushvalue(L, -2); + lua_settable(L, LUA_REGISTRYINDEX); + lua_pop(L, 1); +} + +/* +** Unregisters a given C object from the registry +*/ +void luasql_unregisterobj(lua_State *L, void *obj) +{ + lua_pushlightuserdata(L, obj); + lua_pushnil(L); + lua_settable(L, LUA_REGISTRYINDEX); +} diff --git a/src/luasql.h b/src/luasql.h index 69949a3..dfa8595 100644 --- a/src/luasql.h +++ b/src/luasql.h @@ -35,6 +35,9 @@ LUASQL_API lua_Number luasql_table_optnumber(lua_State *L, int idx, const char* LUASQL_API void luasql_find_driver_table (lua_State *L); +void luasql_registerobj(lua_State *L, int index, void *obj); +void luasql_unregisterobj(lua_State *L, void *obj); + #if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501 void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup); #endif From cb62a5eefd507848b8dc44a9f0376cbafa100c6e Mon Sep 17 00:00:00 2001 From: blumf Date: Mon, 20 Apr 2015 16:00:25 +0100 Subject: [PATCH 29/64] Using more direct locking Lock via pointer so we don't need to also keep ref numbers --- src/ls_odbc.c | 93 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 30 deletions(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index 1a182ec..8c6435f 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -34,27 +34,34 @@ #define LUASQL_CURSOR_ODBC "ODBC cursor" +typedef struct { + short closed; + int lock; +} obj_data; + typedef struct { short closed; - int conn_counter; + int lock; /* lock count for open connections */ SQLHENV henv; /* environment handle */ } env_data; - typedef struct { short closed; - int cur_counter; - int env; /* reference to environment */ + int lock; /* lock count for open statements */ + env_data *env; /* the connection's environment */ SQLHDBC hdbc; /* database connection handle */ } conn_data; typedef struct { short closed; + int lock; /* lock count for open cursors */ + conn_data *conn; /* the statement's connection */ } stmt_data; typedef struct { short closed; - int conn; /* reference to connection */ + stmt_data *stmt; /* the cursor's statement */ + conn_data *conn; /* the connection (to be replaced with the statement soon) */ int numcols; /* number of columns */ int coltypes, colnames; /* reference to column information tables */ SQLHSTMT hstmt; /* statement handle */ @@ -70,6 +77,25 @@ typedef struct { LUASQL_API int luaopen_luasql_odbc (lua_State *L); +static int lock_obj(lua_State *L, int indx, void *ptr) +{ + obj_data *obj = (obj_data *)ptr; + + luasql_registerobj(L, indx, obj); + return ++obj->lock; +} + +static int unlock_obj(lua_State *L, void *ptr) +{ + obj_data *obj = (obj_data *)ptr; + + if(--obj->lock == 0) { + luasql_unregisterobj(L, obj); + } + + return obj->lock; +} + /* ** Check for valid environment. */ @@ -80,7 +106,6 @@ static env_data *getenvironment (lua_State *L) { return env; } - /* ** Check for valid connection. */ @@ -91,6 +116,15 @@ static conn_data *getconnection (lua_State *L) { return conn; } +/* +** Check for valid connection. +*/ +static stmt_data *getstatement (lua_State *L) { + stmt_data *stmt = (stmt_data *)luaL_checkudata (L, 1, LUASQL_STATEMENT_ODBC); + luaL_argcheck (L, stmt != NULL, 1, LUASQL_PREFIX"statement expected"); + luaL_argcheck (L, !stmt->closed, 1, LUASQL_PREFIX"statement is closed"); + return stmt; +} /* ** Check for valid cursor. @@ -354,7 +388,6 @@ static int cur_fetch (lua_State *L) { ** Closes a cursor. */ static int cur_close (lua_State *L) { - conn_data *conn; cur_data *cur = (cur_data *) luaL_checkudata (L, 1, LUASQL_CURSOR_ODBC); SQLRETURN ret; luaL_argcheck (L, cur != NULL, 1, LUASQL_PREFIX"cursor expected"); @@ -373,11 +406,9 @@ static int cur_close (lua_State *L) { if (error(ret)) { return fail(L, hSTMT, cur->hstmt); } + /* Decrement cursor counter on connection object */ - lua_rawgeti (L, LUA_REGISTRYINDEX, cur->conn); - conn = lua_touserdata (L, -1); - conn->cur_counter--; - luaL_unref (L, LUA_REGISTRYINDEX, cur->conn); + unlock_obj(L, cur->conn); luaL_unref (L, LUA_REGISTRYINDEX, cur->colnames); luaL_unref (L, LUA_REGISTRYINDEX, cur->coltypes); return pass(L); @@ -446,17 +477,17 @@ static int create_cursor (lua_State *L, int o, conn_data *conn, cur_data *cur = (cur_data *) lua_newuserdata(L, sizeof(cur_data)); luasql_setmeta (L, LUASQL_CURSOR_ODBC); - conn->cur_counter++; + conn->lock++; /* fill in structure */ cur->closed = 0; - cur->conn = LUA_NOREF; + cur->conn = conn; cur->numcols = numcols; cur->colnames = LUA_NOREF; cur->coltypes = LUA_NOREF; cur->hstmt = hstmt; - lua_pushvalue (L, o); - cur->conn = luaL_ref (L, LUA_REGISTRYINDEX); + + lock_obj(L, o, conn); /* make and store column information table */ if(create_colinfo (L, cur) < 0) { @@ -473,24 +504,21 @@ static int create_cursor (lua_State *L, int o, conn_data *conn, */ static int conn_close (lua_State *L) { SQLRETURN ret; - env_data *env; conn_data *conn = (conn_data *)luaL_checkudata(L,1,LUASQL_CONNECTION_ODBC); luaL_argcheck (L, conn != NULL, 1, LUASQL_PREFIX"connection expected"); if (conn->closed) { lua_pushboolean (L, 0); return 1; } - if (conn->cur_counter > 0) { + if (conn->lock > 0) { return luaL_error (L, LUASQL_PREFIX"there are open cursors"); } /* Decrement connection counter on environment object */ - lua_rawgeti (L, LUA_REGISTRYINDEX, conn->env); - env = (env_data *)lua_touserdata (L, -1); - env->conn_counter--; + unlock_obj(L, conn->env); + /* Nullify structure fields. */ conn->closed = 1; - luaL_unref (L, LUA_REGISTRYINDEX, conn->env); ret = SQLDisconnect(conn->hdbc); if (error(ret)) { return fail(L, hDBC, conn->hdbc); @@ -614,8 +642,10 @@ static int conn_setautocommit (lua_State *L) { /* ** Create a new Connection object and push it on top of the stack. */ -static int create_connection (lua_State *L, int o, env_data *env, SQLHDBC hdbc) { +static int create_connection (lua_State *L, int o, SQLHDBC hdbc) { + env_data *env = (env_data *) getenvironment (L); conn_data *conn = (conn_data *) lua_newuserdata(L, sizeof(conn_data)); + /* set auto commit mode */ SQLRETURN ret = SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, 0); if (error(ret)) { @@ -626,12 +656,13 @@ static int create_connection (lua_State *L, int o, env_data *env, SQLHDBC hdbc) /* fill in structure */ conn->closed = 0; - conn->cur_counter = 0; - conn->env = LUA_NOREF; + conn->lock = 0; + conn->env = env; conn->hdbc = hdbc; lua_pushvalue (L, o); - conn->env = luaL_ref (L, LUA_REGISTRYINDEX); - env->conn_counter++; + + lock_obj(L, 1, env); + return 1; } @@ -647,6 +678,7 @@ static int create_connection (lua_State *L, int o, env_data *env, SQLHDBC hdbc) static int env_table_connect_DSN (lua_State *L) { env_data *env = (env_data *) getenvironment (L); SQLCHAR *sourcename = (SQLCHAR*)luasql_table_optstring(L, 2, "dsn", NULL); + SQLHDBC hdbc; SQLCHAR sqlOutBuf[4097]; SQLSMALLINT sqlOutLen; @@ -672,7 +704,7 @@ static int env_table_connect_DSN (lua_State *L) { } /* success, return connection object */ - ret = create_connection (L, 1, env, hdbc); + ret = create_connection (L, 1, hdbc); if(ret == 1) { /* Add the sqlOutBuf string to the results, for diagnostics */ lua_pushlstring(L, (char*)sqlOutBuf, sqlOutLen); @@ -774,7 +806,7 @@ static int env_connect (lua_State *L) { } /* success, return connection object */ - return create_connection (L, 1, env, hdbc); + return create_connection (L, 1, hdbc); } /* @@ -788,8 +820,9 @@ static int env_close (lua_State *L) { lua_pushboolean (L, 0); return 1; } - if (env->conn_counter > 0) + if (env->lock > 0) { return luaL_error (L, LUASQL_PREFIX"there are open connections"); + } env->closed = 1; ret = SQLFreeHandle (hENV, env->henv); @@ -861,7 +894,7 @@ static int create_environment (lua_State *L) { luasql_setmeta (L, LUASQL_ENVIRONMENT_ODBC); /* fill in structure */ env->closed = 0; - env->conn_counter = 0; + env->lock = 0; env->henv = henv; return 1; } From 67ba7267268e2f8bc02d46930f48a44349f31b34 Mon Sep 17 00:00:00 2001 From: blumf Date: Mon, 20 Apr 2015 16:20:59 +0100 Subject: [PATCH 30/64] Added con:prepare function Creates a statement object --- src/ls_odbc.c | 107 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 87 insertions(+), 20 deletions(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index 8c6435f..9e4d003 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -40,31 +40,33 @@ typedef struct { } obj_data; typedef struct { - short closed; - int lock; /* lock count for open connections */ - SQLHENV henv; /* environment handle */ + short closed; + int lock; /* lock count for open connections */ + SQLHENV henv; /* environment handle */ } env_data; typedef struct { - short closed; - int lock; /* lock count for open statements */ - env_data *env; /* the connection's environment */ - SQLHDBC hdbc; /* database connection handle */ + short closed; + int lock; /* lock count for open statements */ + env_data *env; /* the connection's environment */ + SQLHDBC hdbc; /* database connection handle */ } conn_data; typedef struct { - short closed; - int lock; /* lock count for open cursors */ - conn_data *conn; /* the statement's connection */ + short closed; + int lock; /* lock count for open cursors */ + unsigned char hidden; /* these statement was created indirectly */ + conn_data *conn; /* the statement's connection */ + SQLHSTMT hstmt; /* statement handle */ } stmt_data; typedef struct { - short closed; - stmt_data *stmt; /* the cursor's statement */ - conn_data *conn; /* the connection (to be replaced with the statement soon) */ - int numcols; /* number of columns */ - int coltypes, colnames; /* reference to column information tables */ - SQLHSTMT hstmt; /* statement handle */ + short closed; + stmt_data *stmt; /* the cursor's statement */ + conn_data *conn; /* the connection (to be replaced with the statement soon) */ + int numcols; /* number of columns */ + int coltypes, colnames; /* reference to column information tables */ + SQLHSTMT hstmt; /* statement handle */ } cur_data; @@ -477,8 +479,6 @@ static int create_cursor (lua_State *L, int o, conn_data *conn, cur_data *cur = (cur_data *) lua_newuserdata(L, sizeof(cur_data)); luasql_setmeta (L, LUASQL_CURSOR_ODBC); - conn->lock++; - /* fill in structure */ cur->closed = 0; cur->conn = conn; @@ -498,11 +498,39 @@ static int create_cursor (lua_State *L, int o, conn_data *conn, return 1; } +static int stmt_close(lua_State *L) +{ + SQLRETURN ret; + + stmt_data *stmt = getstatement(L); + luaL_argcheck (L, stmt != NULL, 1, LUASQL_PREFIX"statement expected"); + luaL_argcheck (L, stmt->lock > 0, 1, LUASQL_PREFIX"there are still open cursors"); + + if (stmt->closed) { + lua_pushboolean (L, 0); + return 1; + } + + unlock_obj(L, stmt->conn); + + ret = SQLCloseCursor(stmt->hstmt); + if (error(ret)) { + return fail(L, hSTMT, stmt->hstmt); + } + ret = SQLFreeHandle(hSTMT, stmt->hstmt); + if (error(ret)) { + return fail(L, hSTMT, stmt->hstmt); + } + + lua_pushboolean(L, 1); + return 1; +} /* ** Closes a connection. */ -static int conn_close (lua_State *L) { +static int conn_close (lua_State *L) +{ SQLRETURN ret; conn_data *conn = (conn_data *)luaL_checkudata(L,1,LUASQL_CONNECTION_ODBC); luaL_argcheck (L, conn != NULL, 1, LUASQL_PREFIX"connection expected"); @@ -532,6 +560,41 @@ static int conn_close (lua_State *L) { return pass(L); } +static int conn_prepare(lua_State *L) +{ + conn_data *conn = (conn_data *) getconnection (L); + SQLCHAR *statement = (SQLCHAR*)luaL_checkstring(L, 2); + SQLHDBC hdbc = conn->hdbc; + SQLHSTMT hstmt; + SQLRETURN ret; + + stmt_data *stmt; + + ret = SQLAllocHandle(hSTMT, hdbc, &hstmt); + if (error(ret)) { + return fail(L, hDBC, hdbc); + } + + ret = SQLPrepare(hstmt, statement, SQL_NTS); + if (error(ret)) { + ret = fail(L, hSTMT, hstmt); + SQLFreeHandle(hSTMT, hstmt); + return ret; + } + + stmt = (stmt_data *) lua_newuserdata(L, sizeof(stmt_data)); + luasql_setmeta (L, LUASQL_STATEMENT_ODBC); + + stmt->closed = 0; + stmt->hidden = 0; + stmt->conn = conn; + stmt->lock = 0; + stmt->hstmt = hstmt; + + lock_obj(L, 1, conn); + + return 1; +} /* ** Executes a SQL statement. @@ -539,7 +602,8 @@ static int conn_close (lua_State *L) { ** cursor object: if there are results or ** row count: number of rows affected by statement if no results */ -static int conn_execute (lua_State *L) { +static int conn_execute (lua_State *L) +{ conn_data *conn = (conn_data *) getconnection (L); SQLCHAR *statement = (SQLCHAR*)luaL_checkstring(L, 2); SQLHDBC hdbc = conn->hdbc; @@ -847,6 +911,7 @@ static void create_metatables (lua_State *L) { struct luaL_Reg connection_methods[] = { {"__gc", conn_close}, /* Should this method be changed? */ {"close", conn_close}, + {"prepare", conn_prepare}, {"execute", conn_execute}, {"commit", conn_commit}, {"rollback", conn_rollback}, @@ -854,6 +919,8 @@ static void create_metatables (lua_State *L) { {NULL, NULL}, }; struct luaL_Reg statement_methods[] = { + {"__gc", stmt_close}, /* Should this method be changed? */ + {"close", stmt_close}, {NULL, NULL}, }; struct luaL_Reg cursor_methods[] = { From 8b82654a3a8090f7ba6a1056beeda1b8d9eb3421 Mon Sep 17 00:00:00 2001 From: blumf Date: Mon, 20 Apr 2015 16:36:20 +0100 Subject: [PATCH 31/64] Fixed stmt:close Also bug in env:connect wasn't returning proper item --- src/ls_odbc.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index 9e4d003..25e99b5 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -502,9 +502,9 @@ static int stmt_close(lua_State *L) { SQLRETURN ret; - stmt_data *stmt = getstatement(L); + stmt_data *stmt = (stmt_data *) luaL_checkudata (L, 1, LUASQL_STATEMENT_ODBC); luaL_argcheck (L, stmt != NULL, 1, LUASQL_PREFIX"statement expected"); - luaL_argcheck (L, stmt->lock > 0, 1, LUASQL_PREFIX"there are still open cursors"); + luaL_argcheck (L, stmt->lock == 0, 1, LUASQL_PREFIX"there are still open cursors"); if (stmt->closed) { lua_pushboolean (L, 0); @@ -512,11 +512,8 @@ static int stmt_close(lua_State *L) } unlock_obj(L, stmt->conn); + stmt->closed = 1; - ret = SQLCloseCursor(stmt->hstmt); - if (error(ret)) { - return fail(L, hSTMT, stmt->hstmt); - } ret = SQLFreeHandle(hSTMT, stmt->hstmt); if (error(ret)) { return fail(L, hSTMT, stmt->hstmt); @@ -586,9 +583,9 @@ static int conn_prepare(lua_State *L) luasql_setmeta (L, LUASQL_STATEMENT_ODBC); stmt->closed = 0; + stmt->lock = 0; stmt->hidden = 0; stmt->conn = conn; - stmt->lock = 0; stmt->hstmt = hstmt; lock_obj(L, 1, conn); @@ -723,7 +720,6 @@ static int create_connection (lua_State *L, int o, SQLHDBC hdbc) { conn->lock = 0; conn->env = env; conn->hdbc = hdbc; - lua_pushvalue (L, o); lock_obj(L, 1, env); From 1a434d742f240f6cb11f12ccf7f7f6fbae8659a1 Mon Sep 17 00:00:00 2001 From: blumf Date: Tue, 21 Apr 2015 15:13:21 +0100 Subject: [PATCH 32/64] Split up ODBC execution steps Ready to be used by both conn:execute and stmt:execute --- src/ls_odbc.c | 221 ++++++++++++++++++++++++++++---------------------- 1 file changed, 125 insertions(+), 96 deletions(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index 25e99b5..e4cd439 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -63,10 +63,8 @@ typedef struct { typedef struct { short closed; stmt_data *stmt; /* the cursor's statement */ - conn_data *conn; /* the connection (to be replaced with the statement soon) */ int numcols; /* number of columns */ int coltypes, colnames; /* reference to column information tables */ - SQLHSTMT hstmt; /* statement handle */ } cur_data; @@ -101,40 +99,40 @@ static int unlock_obj(lua_State *L, void *ptr) /* ** Check for valid environment. */ -static env_data *getenvironment (lua_State *L) { - env_data *env = (env_data *)luaL_checkudata (L, 1, LUASQL_ENVIRONMENT_ODBC); - luaL_argcheck (L, env != NULL, 1, LUASQL_PREFIX"environment expected"); - luaL_argcheck (L, !env->closed, 1, LUASQL_PREFIX"environment is closed"); +static env_data *getenvironment (lua_State *L, int i) { + env_data *env = (env_data *)luaL_checkudata (L, i, LUASQL_ENVIRONMENT_ODBC); + luaL_argcheck (L, env != NULL, i, LUASQL_PREFIX"environment expected"); + luaL_argcheck (L, !env->closed, i, LUASQL_PREFIX"environment is closed"); return env; } /* ** Check for valid connection. */ -static conn_data *getconnection (lua_State *L) { - conn_data *conn = (conn_data *)luaL_checkudata (L, 1, LUASQL_CONNECTION_ODBC); - luaL_argcheck (L, conn != NULL, 1, LUASQL_PREFIX"connection expected"); - luaL_argcheck (L, !conn->closed, 1, LUASQL_PREFIX"connection is closed"); +static conn_data *getconnection (lua_State *L, int i) { + conn_data *conn = (conn_data *)luaL_checkudata (L, i, LUASQL_CONNECTION_ODBC); + luaL_argcheck (L, conn != NULL, i, LUASQL_PREFIX"connection expected"); + luaL_argcheck (L, !conn->closed, i, LUASQL_PREFIX"connection is closed"); return conn; } /* ** Check for valid connection. */ -static stmt_data *getstatement (lua_State *L) { - stmt_data *stmt = (stmt_data *)luaL_checkudata (L, 1, LUASQL_STATEMENT_ODBC); - luaL_argcheck (L, stmt != NULL, 1, LUASQL_PREFIX"statement expected"); - luaL_argcheck (L, !stmt->closed, 1, LUASQL_PREFIX"statement is closed"); +static stmt_data *getstatement (lua_State *L, int i) { + stmt_data *stmt = (stmt_data *)luaL_checkudata (L, i, LUASQL_STATEMENT_ODBC); + luaL_argcheck (L, stmt != NULL, i, LUASQL_PREFIX"statement expected"); + luaL_argcheck (L, !stmt->closed, i, LUASQL_PREFIX"statement is closed"); return stmt; } /* ** Check for valid cursor. */ -static cur_data *getcursor (lua_State *L) { - cur_data *cursor = (cur_data *)luaL_checkudata (L, 1, LUASQL_CURSOR_ODBC); - luaL_argcheck (L, cursor != NULL, 1, LUASQL_PREFIX"cursor expected"); - luaL_argcheck (L, !cursor->closed, 1, LUASQL_PREFIX"cursor is closed"); +static cur_data *getcursor (lua_State *L, int i) { + cur_data *cursor = (cur_data *)luaL_checkudata (L, i, LUASQL_CURSOR_ODBC); + luaL_argcheck (L, cursor != NULL, i, LUASQL_PREFIX"cursor expected"); + luaL_argcheck (L, !cursor->closed, i, LUASQL_PREFIX"cursor is closed"); return cursor; } @@ -147,7 +145,6 @@ static int pass(lua_State *L) { return 1; } - /* ** Fails with error message from ODBC ** Inputs: @@ -178,6 +175,25 @@ static int fail(lua_State *L, const SQLSMALLINT type, const SQLHANDLE handle) { return 2; } +/* +** Shuts a statement +** Returns non-zero on error +*/ +static int stmt_shut(lua_State *L, stmt_data *stmt) +{ + SQLRETURN ret; + + unlock_obj(L, stmt->conn); + stmt->closed = 1; + + ret = SQLFreeHandle(hSTMT, stmt->hstmt); + if (error(ret)) { + return 1; + } + + return 0; +} + /* ** Returns the name of an equivalent lua type for a SQL type. */ @@ -335,10 +351,10 @@ static int push_column(lua_State *L, int coltypes, const SQLHSTMT hstmt, ** Get another row of the given cursor. */ static int cur_fetch (lua_State *L) { - cur_data *cur = (cur_data *) getcursor (L); - SQLHSTMT hstmt = cur->hstmt; + cur_data *cur = getcursor (L, 1); + SQLHSTMT hstmt = cur->stmt->hstmt; int ret; - SQLRETURN rc = SQLFetch(cur->hstmt); + SQLRETURN rc = SQLFetch(hstmt); if (rc == SQL_NO_DATA) { lua_pushnil(L); return 1; @@ -400,19 +416,22 @@ static int cur_close (lua_State *L) { /* Nullify structure fields. */ cur->closed = 1; - ret = SQLCloseCursor(cur->hstmt); + ret = SQLCloseCursor(cur->stmt->hstmt); if (error(ret)) { - return fail(L, hSTMT, cur->hstmt); - } - ret = SQLFreeHandle(hSTMT, cur->hstmt); - if (error(ret)) { - return fail(L, hSTMT, cur->hstmt); + return fail(L, hSTMT, cur->stmt->hstmt); } - /* Decrement cursor counter on connection object */ - unlock_obj(L, cur->conn); + /* release col tables */ luaL_unref (L, LUA_REGISTRYINDEX, cur->colnames); luaL_unref (L, LUA_REGISTRYINDEX, cur->coltypes); + + /* release statement and, if hidden, shut it */ + if(unlock_obj(L, cur->stmt) == 0) { + if(cur->stmt->hidden) { + stmt_shut(L, cur->stmt); + } + } + return pass(L); } @@ -421,7 +440,7 @@ static int cur_close (lua_State *L) { ** Returns the table with column names. */ static int cur_colnames (lua_State *L) { - cur_data *cur = (cur_data *) getcursor (L); + cur_data *cur = getcursor (L, 1); lua_rawgeti (L, LUA_REGISTRYINDEX, cur->colnames); return 1; } @@ -431,7 +450,7 @@ static int cur_colnames (lua_State *L) { ** Returns the table with column types. */ static int cur_coltypes (lua_State *L) { - cur_data *cur = (cur_data *) getcursor (L); + cur_data *cur = getcursor (L, 1); lua_rawgeti (L, LUA_REGISTRYINDEX, cur->coltypes); return 1; } @@ -451,7 +470,7 @@ static int create_colinfo (lua_State *L, cur_data *cur) { lua_newtable(L); names = lua_gettop (L); for (i = 1; i <= cur->numcols; i++) { - ret = SQLDescribeCol(cur->hstmt, i, buffer, sizeof(buffer), + ret = SQLDescribeCol(cur->stmt->hstmt, i, buffer, sizeof(buffer), &namelen, &datatype, NULL, NULL, NULL); if (ret == SQL_ERROR) { lua_pop(L, 2); @@ -473,26 +492,26 @@ static int create_colinfo (lua_State *L, cur_data *cur) { /* ** Creates a cursor table and leave it on the top of the stack. */ -static int create_cursor (lua_State *L, int o, conn_data *conn, - const SQLHSTMT hstmt, const SQLSMALLINT numcols) +static int create_cursor (lua_State *L, int stmt_i, stmt_data *stmt, const SQLSMALLINT numcols) { - cur_data *cur = (cur_data *) lua_newuserdata(L, sizeof(cur_data)); + cur_data *cur; + + lock_obj(L, stmt_i, stmt); + + cur = (cur_data *) lua_newuserdata(L, sizeof(cur_data)); luasql_setmeta (L, LUASQL_CURSOR_ODBC); /* fill in structure */ cur->closed = 0; - cur->conn = conn; + cur->stmt = stmt; cur->numcols = numcols; cur->colnames = LUA_NOREF; cur->coltypes = LUA_NOREF; - cur->hstmt = hstmt; - - lock_obj(L, o, conn); /* make and store column information table */ if(create_colinfo (L, cur) < 0) { lua_pop(L, 1); - return fail(L, hSTMT, cur->hstmt); + return fail(L, hSTMT, cur->stmt->hstmt); } return 1; @@ -500,8 +519,6 @@ static int create_cursor (lua_State *L, int o, conn_data *conn, static int stmt_close(lua_State *L) { - SQLRETURN ret; - stmt_data *stmt = (stmt_data *) luaL_checkudata (L, 1, LUASQL_STATEMENT_ODBC); luaL_argcheck (L, stmt != NULL, 1, LUASQL_PREFIX"statement expected"); luaL_argcheck (L, stmt->lock == 0, 1, LUASQL_PREFIX"there are still open cursors"); @@ -511,11 +528,7 @@ static int stmt_close(lua_State *L) return 1; } - unlock_obj(L, stmt->conn); - stmt->closed = 1; - - ret = SQLFreeHandle(hSTMT, stmt->hstmt); - if (error(ret)) { + if(stmt_shut(L, stmt)) { return fail(L, hSTMT, stmt->hstmt); } @@ -536,7 +549,7 @@ static int conn_close (lua_State *L) return 1; } if (conn->lock > 0) { - return luaL_error (L, LUASQL_PREFIX"there are open cursors"); + return luaL_error (L, LUASQL_PREFIX"there are open statements/cursors"); } /* Decrement connection counter on environment object */ @@ -557,9 +570,16 @@ static int conn_close (lua_State *L) return pass(L); } +static int stmt_execute(lua_State *L) +{ + stmt_data *stmt = getstatement(L, 1); + + return 0; +} + static int conn_prepare(lua_State *L) { - conn_data *conn = (conn_data *) getconnection (L); + conn_data *conn = getconnection(L, 1); SQLCHAR *statement = (SQLCHAR*)luaL_checkstring(L, 2); SQLHDBC hdbc = conn->hdbc; SQLHSTMT hstmt; @@ -593,71 +613,79 @@ static int conn_prepare(lua_State *L) return 1; } -/* -** Executes a SQL statement. -** Returns -** cursor object: if there are results or -** row count: number of rows affected by statement if no results -*/ -static int conn_execute (lua_State *L) +static int raw_execute(lua_State *L, int istmt) { - conn_data *conn = (conn_data *) getconnection (L); - SQLCHAR *statement = (SQLCHAR*)luaL_checkstring(L, 2); - SQLHDBC hdbc = conn->hdbc; - SQLHSTMT hstmt; SQLSMALLINT numcols; - SQLRETURN ret; - ret = SQLAllocHandle(hSTMT, hdbc, &hstmt); - if (error(ret)) { - return fail(L, hDBC, hdbc); - } - ret = SQLPrepare(hstmt, statement, SQL_NTS); - if (error(ret)) { - ret = fail(L, hSTMT, hstmt); - SQLFreeHandle(hSTMT, hstmt); - return ret; - } + stmt_data *stmt = getstatement(L, istmt); /* execute the statement */ - ret = SQLExecute (hstmt); - if (error(ret)) { - ret = fail(L, hSTMT, hstmt); - SQLFreeHandle(hSTMT, hstmt); - return ret; + if (error(SQLExecute(stmt->hstmt))) { + return fail(L, hSTMT, stmt->hstmt); } /* determine the number of results */ - ret = SQLNumResultCols (hstmt, &numcols); - if (error(ret)) { - ret = fail(L, hSTMT, hstmt); - SQLFreeHandle(hSTMT, hstmt); - return ret; + if (error(SQLNumResultCols(stmt->hstmt, &numcols))) { + return fail(L, hSTMT, stmt->hstmt); } if (numcols > 0) { /* if there is a results table (e.g., SELECT) */ - return create_cursor (L, 1, conn, hstmt, numcols); + int res = create_cursor(L, -1, stmt, numcols); + return res; } else { /* if action has no results (e.g., UPDATE) */ SQLLEN numrows; - ret = SQLRowCount(hstmt, &numrows); - if (error(ret)) { - ret = fail(L, hSTMT, hstmt); - SQLFreeHandle(hSTMT, hstmt); - return ret; + if(error(SQLRowCount(stmt->hstmt, &numrows))) { + return fail(L, hSTMT, stmt->hstmt); } + lua_pushnumber(L, numrows); - SQLFreeHandle(hSTMT, hstmt); return 1; } } +/* +** Executes a SQL statement. +** Returns +** cursor object: if there are results or +** row count: number of rows affected by statement if no results +*/ +static int conn_execute (lua_State *L) +{ + stmt_data *stmt; + SQLRETURN ret; + int res, istmt; + + /* prepare statement */ + if ((ret = conn_prepare(L)) != 1) { + return ret; + } + istmt = lua_gettop(L); + stmt = getstatement(L, istmt); + + /* because this is a direct execute, statement is hidden from user */ + stmt->hidden = 1; + + /* do it */ + res = raw_execute(L, istmt); + + /* anything but cursor, close the statement directly */ + if(!lua_isuserdata(L, -res)) { + stmt_shut(L, getstatement(L, istmt)); + } + + /* tidy up */ + lua_remove(L, istmt); + + return res; +} + /* ** Rolls back a transaction. */ static int conn_commit (lua_State *L) { - conn_data *conn = (conn_data *) getconnection (L); + conn_data *conn = getconnection (L, 1); SQLRETURN ret = SQLEndTran(hDBC, conn->hdbc, SQL_COMMIT); if (error(ret)) { return fail(L, hSTMT, conn->hdbc); @@ -670,7 +698,7 @@ static int conn_commit (lua_State *L) { ** Rollback the current transaction. */ static int conn_rollback (lua_State *L) { - conn_data *conn = (conn_data *) getconnection (L); + conn_data *conn = getconnection (L, 1); SQLRETURN ret = SQLEndTran(hDBC, conn->hdbc, SQL_ROLLBACK); if (error(ret)) { return fail(L, hSTMT, conn->hdbc); @@ -683,7 +711,7 @@ static int conn_rollback (lua_State *L) { ** Sets the auto commit mode */ static int conn_setautocommit (lua_State *L) { - conn_data *conn = (conn_data *) getconnection (L); + conn_data *conn = getconnection (L, 1); SQLRETURN ret; if (lua_toboolean (L, 2)) { ret = SQLSetConnectAttr(conn->hdbc, SQL_ATTR_AUTOCOMMIT, @@ -704,8 +732,8 @@ static int conn_setautocommit (lua_State *L) { ** Create a new Connection object and push it on top of the stack. */ static int create_connection (lua_State *L, int o, SQLHDBC hdbc) { - env_data *env = (env_data *) getenvironment (L); - conn_data *conn = (conn_data *) lua_newuserdata(L, sizeof(conn_data)); + env_data *env = getenvironment(L, 1); + conn_data *conn = (conn_data *)lua_newuserdata(L, sizeof(conn_data)); /* set auto commit mode */ SQLRETURN ret = SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, 0); @@ -736,7 +764,7 @@ static int create_connection (lua_State *L, int o, SQLHDBC hdbc) { ** nil and error message otherwise. */ static int env_table_connect_DSN (lua_State *L) { - env_data *env = (env_data *) getenvironment (L); + env_data *env = getenvironment (L, 1); SQLCHAR *sourcename = (SQLCHAR*)luasql_table_optstring(L, 2, "dsn", NULL); SQLHDBC hdbc; @@ -826,7 +854,7 @@ static void env_connect_fix_old (lua_State *L) { ** nil and error message otherwise. */ static int env_connect (lua_State *L) { - env_data *env = (env_data *) getenvironment (L); + env_data *env = getenvironment (L, 1); SQLCHAR *sourcename = NULL; SQLCHAR *username = NULL; SQLCHAR *password = NULL; @@ -917,6 +945,7 @@ static void create_metatables (lua_State *L) { struct luaL_Reg statement_methods[] = { {"__gc", stmt_close}, /* Should this method be changed? */ {"close", stmt_close}, + {"execute", stmt_execute}, {NULL, NULL}, }; struct luaL_Reg cursor_methods[] = { From fbb6245550498464b5279551e67b2397480c1fee Mon Sep 17 00:00:00 2001 From: blumf Date: Tue, 21 Apr 2015 15:18:06 +0100 Subject: [PATCH 33/64] ODBC stmt:execute now runs Next step, params --- src/ls_odbc.c | 73 +++++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index e4cd439..386d5c2 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -570,11 +570,46 @@ static int conn_close (lua_State *L) return pass(L); } -static int stmt_execute(lua_State *L) +/* +** Executes the given statement +** Takes: +** * istmt : location of the statement object on the stack +*/ +static int raw_execute(lua_State *L, int istmt) { - stmt_data *stmt = getstatement(L, 1); + SQLSMALLINT numcols; - return 0; + stmt_data *stmt = getstatement(L, istmt); + + /* execute the statement */ + if (error(SQLExecute(stmt->hstmt))) { + return fail(L, hSTMT, stmt->hstmt); + } + + /* determine the number of results */ + if (error(SQLNumResultCols(stmt->hstmt, &numcols))) { + return fail(L, hSTMT, stmt->hstmt); + } + + if (numcols > 0) { + /* if there is a results table (e.g., SELECT) */ + int res = create_cursor(L, -1, stmt, numcols); + return res; + } else { + /* if action has no results (e.g., UPDATE) */ + SQLLEN numrows; + if(error(SQLRowCount(stmt->hstmt, &numrows))) { + return fail(L, hSTMT, stmt->hstmt); + } + + lua_pushnumber(L, numrows); + return 1; + } +} + +static int stmt_execute(lua_State *L) +{ + return raw_execute(L, 1); } static int conn_prepare(lua_State *L) @@ -613,38 +648,6 @@ static int conn_prepare(lua_State *L) return 1; } -static int raw_execute(lua_State *L, int istmt) -{ - SQLSMALLINT numcols; - - stmt_data *stmt = getstatement(L, istmt); - - /* execute the statement */ - if (error(SQLExecute(stmt->hstmt))) { - return fail(L, hSTMT, stmt->hstmt); - } - - /* determine the number of results */ - if (error(SQLNumResultCols(stmt->hstmt, &numcols))) { - return fail(L, hSTMT, stmt->hstmt); - } - - if (numcols > 0) { - /* if there is a results table (e.g., SELECT) */ - int res = create_cursor(L, -1, stmt, numcols); - return res; - } else { - /* if action has no results (e.g., UPDATE) */ - SQLLEN numrows; - if(error(SQLRowCount(stmt->hstmt, &numrows))) { - return fail(L, hSTMT, stmt->hstmt); - } - - lua_pushnumber(L, numrows); - return 1; - } -} - /* ** Executes a SQL statement. ** Returns From fc220a5716d00045e561a5eb16323c488cba6a1d Mon Sep 17 00:00:00 2001 From: blumf Date: Tue, 21 Apr 2015 16:53:11 +0100 Subject: [PATCH 34/64] ODBC stmt:getparamtypes function support But, most ODBC drivers do not support SQLDescribeParam, so this won't work in most cases. --- src/ls_odbc.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index 386d5c2..da91b9b 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -58,6 +58,8 @@ typedef struct { unsigned char hidden; /* these statement was created indirectly */ conn_data *conn; /* the statement's connection */ SQLHSTMT hstmt; /* statement handle */ + SQLSMALLINT numparams; /* number of input parameters */ + int paramtypes; /* reference to param type table */ } stmt_data; typedef struct { @@ -72,8 +74,11 @@ typedef struct { #define hENV SQL_HANDLE_ENV #define hSTMT SQL_HANDLE_STMT #define hDBC SQL_HANDLE_DBC -#define error(a) ((a) != SQL_SUCCESS && (a) != SQL_SUCCESS_WITH_INFO) +static int error(SQLRETURN a) +{ + return (a != SQL_SUCCESS) && (a != SQL_SUCCESS_WITH_INFO); +} LUASQL_API int luaopen_luasql_odbc (lua_State *L); @@ -186,6 +191,9 @@ static int stmt_shut(lua_State *L, stmt_data *stmt) unlock_obj(L, stmt->conn); stmt->closed = 1; + luaL_unref (L, LUA_REGISTRYINDEX, stmt->paramtypes); + stmt->paramtypes = LUA_NOREF; + ret = SQLFreeHandle(hSTMT, stmt->hstmt); if (error(ret)) { return 1; @@ -517,6 +525,15 @@ static int create_cursor (lua_State *L, int stmt_i, stmt_data *stmt, const SQLSM return 1; } +/* +** Returns the table with statement params. +*/ +static int stmt_paramtypes (lua_State *L) { + stmt_data *stmt = getstatement(L, 1); + lua_rawgeti (L, LUA_REGISTRYINDEX, stmt->paramtypes); + return 1; +} + static int stmt_close(lua_State *L) { stmt_data *stmt = (stmt_data *) luaL_checkudata (L, 1, LUASQL_STATEMENT_ODBC); @@ -607,11 +624,40 @@ static int raw_execute(lua_State *L, int istmt) } } +/* +** executes the prepared statement +*/ static int stmt_execute(lua_State *L) { return raw_execute(L, 1); } +/* +** creates a table of parameter types (maybe) +** Returns: the reference key of the table +*/ +static int desc_params(lua_State *L, stmt_data *stmt) +{ + SQLSMALLINT i; + + lua_newtable(L); + for(i=1; i <= stmt->numparams; ++i) { + SQLSMALLINT type, digits, nullable; + SQLULEN len; + + /* fun fact: most ODBC drivers don't support this function (MS Access for example), so we can't get a param type table */ + if(error(SQLDescribeParam(stmt->hstmt, i, &type, &len, &digits, &nullable))) { + lua_pop(L,1); + return LUA_NOREF; + } + + lua_pushstring(L, sqltypetolua(type)); + lua_rawseti(L, -2, i); + } + + return luaL_ref(L, LUA_REGISTRYINDEX); +} + static int conn_prepare(lua_State *L) { conn_data *conn = getconnection(L, 1); @@ -642,6 +688,14 @@ static int conn_prepare(lua_State *L) stmt->hidden = 0; stmt->conn = conn; stmt->hstmt = hstmt; + if(error(SQLNumParams(hstmt, &stmt->numparams))) { + int res; + lua_pop(L, 1); + res = fail(L, hSTMT, stmt->hstmt); + SQLFreeHandle(hSTMT, hstmt); + return res; + } + stmt->paramtypes = desc_params(L, stmt); lock_obj(L, 1, conn); @@ -949,6 +1003,7 @@ static void create_metatables (lua_State *L) { {"__gc", stmt_close}, /* Should this method be changed? */ {"close", stmt_close}, {"execute", stmt_execute}, + {"getparamtypes", stmt_paramtypes}, {NULL, NULL}, }; struct luaL_Reg cursor_methods[] = { From 3dd893e800f1cf32e335f16575e061e13cc7fb5a Mon Sep 17 00:00:00 2001 From: blumf Date: Wed, 22 Apr 2015 15:20:11 +0100 Subject: [PATCH 35/64] Tidy up recent code changes --- src/ls_odbc.c | 49 +++++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index da91b9b..79aa384 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -603,18 +603,17 @@ static int raw_execute(lua_State *L, int istmt) return fail(L, hSTMT, stmt->hstmt); } - /* determine the number of results */ + /* determine the number of result columns */ if (error(SQLNumResultCols(stmt->hstmt, &numcols))) { return fail(L, hSTMT, stmt->hstmt); } if (numcols > 0) { /* if there is a results table (e.g., SELECT) */ - int res = create_cursor(L, -1, stmt, numcols); - return res; + return create_cursor(L, -1, stmt, numcols); } else { /* if action has no results (e.g., UPDATE) */ - SQLLEN numrows; + SQLINTEGER numrows; if(error(SQLRowCount(stmt->hstmt, &numrows))) { return fail(L, hSTMT, stmt->hstmt); } @@ -625,7 +624,12 @@ static int raw_execute(lua_State *L, int istmt) } /* -** executes the prepared statement +** Executes the prepared statement +** Lua Input: [params] +** params: A table of parameters to use in the statement +** Lua Returns +** cursor object: if there are results or +** row count: number of rows affected by statement if no results */ static int stmt_execute(lua_State *L) { @@ -634,7 +638,7 @@ static int stmt_execute(lua_State *L) /* ** creates a table of parameter types (maybe) -** Returns: the reference key of the table +** Returns: the reference key of the table (noref if unable to build the table) */ static int desc_params(lua_State *L, stmt_data *stmt) { @@ -658,6 +662,13 @@ static int desc_params(lua_State *L, stmt_data *stmt) return luaL_ref(L, LUA_REGISTRYINDEX); } +/* +** Prepares a statement +** Lua Input: sql +** sql: The SQL statement to prepare +** Lua Returns: +** Statement object +*/ static int conn_prepare(lua_State *L) { conn_data *conn = getconnection(L, 1); @@ -680,8 +691,7 @@ static int conn_prepare(lua_State *L) return ret; } - stmt = (stmt_data *) lua_newuserdata(L, sizeof(stmt_data)); - luasql_setmeta (L, LUASQL_STATEMENT_ODBC); + stmt = (stmt_data *)lua_newuserdata(L, sizeof(stmt_data)); stmt->closed = 0; stmt->lock = 0; @@ -691,32 +701,36 @@ static int conn_prepare(lua_State *L) if(error(SQLNumParams(hstmt, &stmt->numparams))) { int res; lua_pop(L, 1); - res = fail(L, hSTMT, stmt->hstmt); + res = fail(L, hSTMT, hstmt); SQLFreeHandle(hSTMT, hstmt); return res; } stmt->paramtypes = desc_params(L, stmt); + /* activate statement object */ + luasql_setmeta(L, LUASQL_STATEMENT_ODBC); lock_obj(L, 1, conn); return 1; } /* -** Executes a SQL statement. -** Returns +** Executes a SQL statement directly +** Lua Input: sql, [params] +** sql: The SQL statement to execute +** params: A table of parameters to use in the SQL +** Lua Returns ** cursor object: if there are results or ** row count: number of rows affected by statement if no results */ static int conn_execute (lua_State *L) { stmt_data *stmt; - SQLRETURN ret; int res, istmt; /* prepare statement */ - if ((ret = conn_prepare(L)) != 1) { - return ret; + if((res = conn_prepare(L)) != 1) { + return res; } istmt = lua_gettop(L); stmt = getstatement(L, istmt); @@ -727,9 +741,9 @@ static int conn_execute (lua_State *L) /* do it */ res = raw_execute(L, istmt); - /* anything but cursor, close the statement directly */ + /* anything but a cursor, close the statement directly */ if(!lua_isuserdata(L, -res)) { - stmt_shut(L, getstatement(L, istmt)); + stmt_shut(L, stmt); } /* tidy up */ @@ -739,7 +753,7 @@ static int conn_execute (lua_State *L) } /* -** Rolls back a transaction. +** Commits a transaction. */ static int conn_commit (lua_State *L) { conn_data *conn = getconnection (L, 1); @@ -784,7 +798,6 @@ static int conn_setautocommit (lua_State *L) { } } - /* ** Create a new Connection object and push it on top of the stack. */ From f6ee06dc25b15004cd8a3c2b1d2f512c98e1e918 Mon Sep 17 00:00:00 2001 From: blumf Date: Wed, 22 Apr 2015 17:51:53 +0100 Subject: [PATCH 36/64] First stab at ODBC parameter binding It's trickier than it should be because we can't rely on SQLDescribeParam to be working so it's just forcing the types to whatever the Lua type is --- src/ls_odbc.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index 79aa384..cce233e 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -33,10 +33,17 @@ #define LUASQL_STATEMENT_ODBC "ODBC statement" #define LUASQL_CURSOR_ODBC "ODBC cursor" +/* holds data for paramter binding */ +typedef struct { + SQLPOINTER buf; + SQLINTEGER len; + SQLINTEGER type; +} param_data; +/* general form of the driver objects */ typedef struct { short closed; - int lock; + int lock; } obj_data; typedef struct { @@ -60,6 +67,7 @@ typedef struct { SQLHSTMT hstmt; /* statement handle */ SQLSMALLINT numparams; /* number of input parameters */ int paramtypes; /* reference to param type table */ + param_data *params; /* array of parater data */ } stmt_data; typedef struct { @@ -180,6 +188,29 @@ static int fail(lua_State *L, const SQLSMALLINT type, const SQLHANDLE handle) { return 2; } +static param_data *malloc_stmt_params(SQLSMALLINT c) +{ + param_data *p = (param_data *)malloc(sizeof(param_data)*c); + memset(p, 0, sizeof(param_data)*c); + + return p; +} + +static param_data *free_stmt_params(param_data *data, SQLSMALLINT c) +{ + + if(data != NULL) { + param_data *p = data; + + for(; c>0; ++p, --c) { + free(p->buf); + } + free(data); + } + + return NULL; +} + /* ** Shuts a statement ** Returns non-zero on error @@ -193,6 +224,7 @@ static int stmt_shut(lua_State *L, stmt_data *stmt) luaL_unref (L, LUA_REGISTRYINDEX, stmt->paramtypes); stmt->paramtypes = LUA_NOREF; + stmt->params = free_stmt_params(stmt->params, stmt->numparams); ret = SQLFreeHandle(hSTMT, stmt->hstmt); if (error(ret)) { @@ -623,6 +655,71 @@ static int raw_execute(lua_State *L, int istmt) } } +/* +** Reads a param table into a statement +*/ +static int raw_readparams(lua_State *L, stmt_data *stmt, int iparams) +{ + static SQLINTEGER cbNull = SQL_NULL_DATA; + SQLSMALLINT i; + param_data *data; + + free_stmt_params(stmt->params, stmt->numparams); + stmt->params = malloc_stmt_params(stmt->numparams); + data = stmt->params; + + for(i=1; i<=stmt->numparams; ++i, ++data) { + lua_pushnumber(L, i); /* not using lua_geti for backwards compat with Lua 5.1/LuaJIT */ + lua_gettable(L, iparams); + + switch(lua_type(L, -1)) { + case LUA_TNIL: { + lua_pop(L, 1); + + if(error(SQLBindParameter(stmt->hstmt, i, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_UNKNOWN_TYPE, 0, 0, NULL, 0, &cbNull))) { + return fail(L, hSTMT, stmt->hstmt); + } + } break; + + case LUA_TNUMBER: { + data->buf = malloc(sizeof(double)); + *(double *)data->buf = (double)lua_tonumber(L, -1); + data->len = sizeof(double); + data->type = 0; + + lua_pop(L, 1); + + if(error(SQLBindParameter(stmt->hstmt, i, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, 0, 0, data->buf, data->len, &data->type))) { + return fail(L, hSTMT, stmt->hstmt); + } + } break; + + case LUA_TSTRING: { + const char* str = lua_tostring(L, -1); + size_t len = strlen(str); + + data->buf = malloc(len+1); + memcpy((char *)data->buf, str, len+1); + data->len = len; + data->type = SQL_NTS; + + lua_pop(L, 1); + + if(error(SQLBindParameter(stmt->hstmt, i, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, len, 0, data->buf, data->len, &data->type))) { + return fail(L, hSTMT, stmt->hstmt); + } + } break; + + default: + lua_pop(L, 1); + return luasql_faildirect(L, "unsupported parameter type"); + break; + } + } + + return 0; +} + /* ** Executes the prepared statement ** Lua Input: [params] @@ -633,6 +730,15 @@ static int raw_execute(lua_State *L, int istmt) */ static int stmt_execute(lua_State *L) { + /* any parameters to use */ + if(lua_gettop(L) > 1) { + stmt_data *stmt = getstatement(L, 1); + int res = raw_readparams(L, stmt, 2); + if(res != 0) { + return res; + } + } + return raw_execute(L, 1); } @@ -692,6 +798,7 @@ static int conn_prepare(lua_State *L) } stmt = (stmt_data *)lua_newuserdata(L, sizeof(stmt_data)); + memset(stmt, 0, sizeof(stmt_data)); stmt->closed = 0; stmt->lock = 0; @@ -706,6 +813,7 @@ static int conn_prepare(lua_State *L) return res; } stmt->paramtypes = desc_params(L, stmt); + stmt->params = NULL; /* activate statement object */ luasql_setmeta(L, LUASQL_STATEMENT_ODBC); From bc252b26cad7e2bddbf0cd75cb059ad73b41d4c3 Mon Sep 17 00:00:00 2001 From: blumf Date: Thu, 23 Apr 2015 13:24:25 +0100 Subject: [PATCH 37/64] ODBC con:execute now accepts parameters Nice and easy --- src/ls_odbc.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index cce233e..95579d4 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -303,7 +303,7 @@ static int push_column(lua_State *L, int coltypes, const SQLHSTMT hstmt, lua_pushnumber(L, num); } return 0; - } + } #if LUA_VERSION_NUM>=503 /* iNteger */ case 'n': { @@ -319,7 +319,7 @@ static int push_column(lua_State *L, int coltypes, const SQLHSTMT hstmt, lua_pushinteger(L, num); } return 0; - } + } #endif /* bOol */ case 'o': { @@ -335,7 +335,7 @@ static int push_column(lua_State *L, int coltypes, const SQLHSTMT hstmt, lua_pushboolean(L, b); } return 0; - } + } /* sTring */ case 't': /* bInary */ @@ -382,7 +382,7 @@ static int push_column(lua_State *L, int coltypes, const SQLHSTMT hstmt, /* return everything we got */ luaL_pushresult(&b); return 0; - } + } } return 0; } @@ -835,6 +835,7 @@ static int conn_execute (lua_State *L) { stmt_data *stmt; int res, istmt; + int ltop = lua_gettop(L); /* prepare statement */ if((res = conn_prepare(L)) != 1) { @@ -846,6 +847,13 @@ static int conn_execute (lua_State *L) /* because this is a direct execute, statement is hidden from user */ stmt->hidden = 1; + /* do we have any parameters */ + if(ltop > 2) { + if((res = raw_readparams(L, stmt, 3)) != 0) { + return res; + } + } + /* do it */ res = raw_execute(L, istmt); From 4d904170eeef10e8efb1b9d91870abf2860bf09b Mon Sep 17 00:00:00 2001 From: blumf Date: Thu, 23 Apr 2015 13:54:19 +0100 Subject: [PATCH 38/64] ODBC supports NULL & bool params NULL wasn't working, bool is new --- src/ls_odbc.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index 95579d4..dd1357a 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -676,7 +676,7 @@ static int raw_readparams(lua_State *L, stmt_data *stmt, int iparams) case LUA_TNIL: { lua_pop(L, 1); - if(error(SQLBindParameter(stmt->hstmt, i, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_UNKNOWN_TYPE, 0, 0, NULL, 0, &cbNull))) { + if(error(SQLBindParameter(stmt->hstmt, i, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_DOUBLE, 0, 0, NULL, 0, &cbNull))) { return fail(L, hSTMT, stmt->hstmt); } } break; @@ -710,6 +710,21 @@ static int raw_readparams(lua_State *L, stmt_data *stmt, int iparams) } } break; + case LUA_TBOOLEAN: { + SQLCHAR b = (SQLCHAR)lua_toboolean(L, -1); + + data->buf = malloc(sizeof(SQLCHAR)); + *(SQLCHAR *)data->buf = (SQLCHAR)lua_toboolean(L, -1); + data->len = 0; + data->type = 0; + + lua_pop(L, 1); + + if(error(SQLBindParameter(stmt->hstmt, i, SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT, 0, 0, data->buf, data->len, &data->type))) { + return fail(L, hSTMT, stmt->hstmt); + } + } break; + default: lua_pop(L, 1); return luasql_faildirect(L, "unsupported parameter type"); From 6f41ec61680979c260c4e046aeb37580b22c19be Mon Sep 17 00:00:00 2001 From: blumf Date: Thu, 23 Apr 2015 13:59:42 +0100 Subject: [PATCH 39/64] Moved luasql_pushinteger into wider project Using the luasql_ prefix to make clear it's a backward compat patch --- src/ls_firebird.c | 8 -------- src/ls_sqlite3.c | 2 +- src/luasql.h | 9 +++++++-- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index d228696..91c42b9 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -74,14 +74,6 @@ typedef struct { #define FB_INTERPRET(BUF, LEN, VECTOR) isc_interpret(BUF, VECTOR) #endif -#if LUA_VERSION_NUM>=503 -#define luasql_pushinteger lua_pushinteger -#define luasql_isinteger lua_isinteger -#else -#define luasql_pushinteger lua_pushnumber -#define luasql_isinteger lua_isnumber -#endif - /* MSVC still doesn't support C99 properly until 2015 */ #if defined(_MSC_VER) && _MSC_VER<1900 #pragma warning(disable:4996) /* and complains if you try to work around it */ diff --git a/src/ls_sqlite3.c b/src/ls_sqlite3.c index 8258593..e7b57d5 100644 --- a/src/ls_sqlite3.c +++ b/src/ls_sqlite3.c @@ -126,7 +126,7 @@ static int finalize(lua_State *L, cur_data *cur) { static void push_column(lua_State *L, sqlite3_stmt *vm, int column) { switch (sqlite3_column_type(vm, column)) { case SQLITE_INTEGER: - lua_pushinteger(L, sqlite3_column_int64(vm, column)); + luasql_pushinteger(L, sqlite3_column_int64(vm, column)); break; case SQLITE_FLOAT: lua_pushnumber(L, sqlite3_column_double(vm, column)); diff --git a/src/luasql.h b/src/luasql.h index dfa8595..7cbe90a 100644 --- a/src/luasql.h +++ b/src/luasql.h @@ -13,9 +13,14 @@ #if !defined LUA_VERSION_NUM /* Lua 5.0 */ #define luaL_Reg luaL_reg +#endif -#define lua_pushinteger(L, n) \ - lua_pushnumber(L, (lua_Number)n) +#if LUA_VERSION_NUM>=503 +#define luasql_pushinteger lua_pushinteger +#define luasql_isinteger lua_isinteger +#else +#define luasql_pushinteger lua_pushnumber +#define luasql_isinteger lua_isnumber #endif #define LUASQL_PREFIX "LuaSQL: " From 0e770cabae9609098ea765bd2c8c38efeaad4c5c Mon Sep 17 00:00:00 2001 From: blumf Date: Thu, 23 Apr 2015 14:11:53 +0100 Subject: [PATCH 40/64] Return whole number for SQL update statements --- src/ls_odbc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index dd1357a..434a84a 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -650,7 +650,7 @@ static int raw_execute(lua_State *L, int istmt) return fail(L, hSTMT, stmt->hstmt); } - lua_pushnumber(L, numrows); + luasql_pushinteger(L, numrows); return 1; } } From 22cebe4ece43bd2fc38574be66e43309773b2503 Mon Sep 17 00:00:00 2001 From: blumf Date: Thu, 23 Apr 2015 14:54:00 +0100 Subject: [PATCH 41/64] ODBC Cursor now closes itself when out of results This behaviour matches the documentation --- src/ls_odbc.c | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index 434a84a..0353b80 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -396,6 +396,11 @@ static int cur_fetch (lua_State *L) { int ret; SQLRETURN rc = SQLFetch(hstmt); if (rc == SQL_NO_DATA) { + /* automatically close cursor when end of resultset is reached */ + if((ret = cur_shut(L, cur)) != 0) { + return ret; + } + lua_pushnil(L); return 1; } else { @@ -443,21 +448,14 @@ static int cur_fetch (lua_State *L) { } /* -** Closes a cursor. +** Closes a cursor directly +** Returns non-zero on error */ -static int cur_close (lua_State *L) { - cur_data *cur = (cur_data *) luaL_checkudata (L, 1, LUASQL_CURSOR_ODBC); - SQLRETURN ret; - luaL_argcheck (L, cur != NULL, 1, LUASQL_PREFIX"cursor expected"); - if (cur->closed) { - lua_pushboolean (L, 0); - return 1; - } - +static int cur_shut(lua_State *L, cur_data *cur) +{ /* Nullify structure fields. */ cur->closed = 1; - ret = SQLCloseCursor(cur->stmt->hstmt); - if (error(ret)) { + if (error(SQLCloseCursor(cur->stmt->hstmt))) { return fail(L, hSTMT, cur->stmt->hstmt); } @@ -468,13 +466,32 @@ static int cur_close (lua_State *L) { /* release statement and, if hidden, shut it */ if(unlock_obj(L, cur->stmt) == 0) { if(cur->stmt->hidden) { - stmt_shut(L, cur->stmt); + return stmt_shut(L, cur->stmt); } } - return pass(L); + return 0; } +/* +** Closes a cursor. +*/ +static int cur_close (lua_State *L) { + int res; + cur_data *cur = (cur_data *) luaL_checkudata (L, 1, LUASQL_CURSOR_ODBC); + luaL_argcheck (L, cur != NULL, 1, LUASQL_PREFIX"cursor expected"); + + if (cur->closed) { + lua_pushboolean (L, 0); + return 1; + } + + if((res = cur_shut(L, cur)) != 0) { + return res; + } + + return pass(L); +} /* ** Returns the table with column names. From 210e8008aab8783bcbfd6a327e893e378a443e45 Mon Sep 17 00:00:00 2001 From: blumf Date: Thu, 23 Apr 2015 18:11:54 +0100 Subject: [PATCH 42/64] Better method to fix old connection calls Bit more flexible and clear, should be a quick copy/paste for other drivers --- src/ls_firebird.c | 35 +++++++++++++++-------------------- src/ls_odbc.c | 41 ++++++++++++++++------------------------- 2 files changed, 31 insertions(+), 45 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 91c42b9..0521cc1 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -1249,29 +1249,24 @@ static int create_environment (lua_State *L) { ** new connection details table */ static void env_connect_fix_old (lua_State *L) { - if(lua_isstring(L, 2)) { - /* convert to new table format */ - int n = lua_gettop(L); - - const char *sourcename = luaL_checkstring (L, 2); - const char *username = luaL_optstring (L, 3, ""); - const char *password = luaL_optstring (L, 4, ""); + static const char* const opt_names[] = { + "source", + "user", + "password", + NULL + }; + int i, t = lua_gettop(L)-1; - lua_newtable(L); - lua_pushstring(L, "source"); - lua_pushstring(L, sourcename); - lua_settable(L, -3); - lua_pushstring(L, "user"); - lua_pushstring(L, username); - lua_settable(L, -3); - lua_pushstring(L, "password"); - lua_pushstring(L, password); - lua_settable(L, -3); + lua_newtable(L); + lua_insert(L, 2); - while(n > 1) { - lua_remove(L, n--); - } + for(i=0; opt_names[i] != NULL && i 1) { - lua_remove(L, n--); - } + lua_newtable(L); + lua_insert(L, 2); + + for(i=0; opt_names[i] != NULL && i Date: Thu, 23 Apr 2015 18:13:05 +0100 Subject: [PATCH 43/64] Function in wrong place Was written after it's calling function --- src/ls_odbc.c | 52 +++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index 2d9ac9a..bcc16da 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -234,6 +234,32 @@ static int stmt_shut(lua_State *L, stmt_data *stmt) return 0; } +/* +** Closes a cursor directly +** Returns non-zero on error +*/ +static int cur_shut(lua_State *L, cur_data *cur) +{ + /* Nullify structure fields. */ + cur->closed = 1; + if (error(SQLCloseCursor(cur->stmt->hstmt))) { + return fail(L, hSTMT, cur->stmt->hstmt); + } + + /* release col tables */ + luaL_unref (L, LUA_REGISTRYINDEX, cur->colnames); + luaL_unref (L, LUA_REGISTRYINDEX, cur->coltypes); + + /* release statement and, if hidden, shut it */ + if(unlock_obj(L, cur->stmt) == 0) { + if(cur->stmt->hidden) { + return stmt_shut(L, cur->stmt); + } + } + + return 0; +} + /* ** Returns the name of an equivalent lua type for a SQL type. */ @@ -447,32 +473,6 @@ static int cur_fetch (lua_State *L) { } } -/* -** Closes a cursor directly -** Returns non-zero on error -*/ -static int cur_shut(lua_State *L, cur_data *cur) -{ - /* Nullify structure fields. */ - cur->closed = 1; - if (error(SQLCloseCursor(cur->stmt->hstmt))) { - return fail(L, hSTMT, cur->stmt->hstmt); - } - - /* release col tables */ - luaL_unref (L, LUA_REGISTRYINDEX, cur->colnames); - luaL_unref (L, LUA_REGISTRYINDEX, cur->coltypes); - - /* release statement and, if hidden, shut it */ - if(unlock_obj(L, cur->stmt) == 0) { - if(cur->stmt->hidden) { - return stmt_shut(L, cur->stmt); - } - } - - return 0; -} - /* ** Closes a cursor. */ From 92410074ddce629444689e04b630e76a24d930f2 Mon Sep 17 00:00:00 2001 From: blumf Date: Thu, 23 Apr 2015 18:22:29 +0100 Subject: [PATCH 44/64] MySQL driver supports new env:connect style Connection details table support in --- src/ls_mysql.c | 60 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/src/ls_mysql.c b/src/ls_mysql.c index ac534f3..7465eac 100644 --- a/src/ls_mysql.c +++ b/src/ls_mysql.c @@ -480,6 +480,36 @@ static int create_connection (lua_State *L, int env, MYSQL *const my_conn) { return 1; } +/* +** Reforms old connection style to new one +** Lua Input: source, [user], [pass], [host], [port] +** source: data source +** user, pass: data source authentication information +** Lua Returns: +** new connection details table +*/ +static void env_connect_fix_old (lua_State *L) { + static const char* const opt_names[] = { + "source", + "user", + "password", + "host", + "port", + NULL + }; + int i, t = lua_gettop(L)-1; + + lua_newtable(L); + lua_insert(L, 2); + + for(i=0; opt_names[i] != NULL && i Date: Thu, 23 Apr 2015 18:31:15 +0100 Subject: [PATCH 45/64] Tidy up some compiler warnings --- src/ls_mysql.c | 58 +++++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/src/ls_mysql.c b/src/ls_mysql.c index 7465eac..afcb38d 100644 --- a/src/ls_mysql.c +++ b/src/ls_mysql.c @@ -23,6 +23,12 @@ #include "luasql.h" +/* MSVC still doesn't support C99 properly until 2015 */ +#if defined(_MSC_VER) && _MSC_VER<1900 +#pragma warning(disable:4996) /* and complains if you try to work around it */ +#define snprintf _snprintf +#endif + #define LUASQL_ENVIRONMENT_MYSQL "MySQL environment" #define LUASQL_CONNECTION_MYSQL "MySQL connection" #define LUASQL_CURSOR_MYSQL "MySQL cursor" @@ -160,23 +166,25 @@ static char *getcolumntype (enum enum_field_types type) { /* ** Creates the lists of fields names and fields types. */ -static void create_colinfo (lua_State *L, cur_data *cur) { - MYSQL_FIELD *fields; - char typename[50]; +static void create_colinfo(lua_State *L, cur_data *cur) { + char name[50]; int i; - fields = mysql_fetch_fields(cur->my_res); - lua_newtable (L); /* names */ - lua_newtable (L); /* types */ - for (i = 1; i <= cur->numcols; i++) { - lua_pushstring (L, fields[i-1].name); - lua_rawseti (L, -3, i); - sprintf (typename, "%.20s(%ld)", getcolumntype (fields[i-1].type), fields[i-1].length); - lua_pushstring(L, typename); - lua_rawseti (L, -2, i); + MYSQL_FIELD *fields = mysql_fetch_fields(cur->my_res); + + lua_newtable(L); /* names */ + lua_newtable(L); /* types */ + + for(i = 1; i <= cur->numcols; i++) { + lua_pushstring(L, fields[i-1].name); + lua_rawseti(L, -3, i); + snprintf(name, sizeof(name), "%.20s(%ld)", getcolumntype (fields[i-1].type), fields[i-1].length); + lua_pushstring(L, name); + lua_rawseti(L, -2, i); } + /* Stores the references in the cursor structure */ - cur->coltypes = luaL_ref (L, LUA_REGISTRYINDEX); - cur->colnames = luaL_ref (L, LUA_REGISTRYINDEX); + cur->coltypes = luaL_ref(L, LUA_REGISTRYINDEX); + cur->colnames = luaL_ref(L, LUA_REGISTRYINDEX); } @@ -190,6 +198,7 @@ static int cur_nullify (lua_State *L, cur_data *cur) { luaL_unref (L, LUA_REGISTRYINDEX, cur->conn); luaL_unref (L, LUA_REGISTRYINDEX, cur->colnames); luaL_unref (L, LUA_REGISTRYINDEX, cur->coltypes); + return 0; } @@ -395,25 +404,26 @@ static int conn_execute (lua_State *L) { conn_data *conn = getconnection (L); size_t st_len; const char *statement = luaL_checklstring (L, 2, &st_len); - if (mysql_real_query(conn->my_conn, statement, st_len)) + if (mysql_real_query(conn->my_conn, statement, st_len)) { /* error executing query */ return luasql_failmsg(L, "error executing query. MySQL: ", mysql_error(conn->my_conn)); - else - { + } else { MYSQL_RES *res = mysql_store_result(conn->my_conn); unsigned int num_cols = mysql_field_count(conn->my_conn); - if (res) { /* tuples returned */ + if (res) { + /* tuples returned */ return create_cursor (L, 1, res, num_cols); - } - else { /* mysql_use_result() returned nothing; should it have? */ + } else { + /* mysql_use_result() returned nothing; should it have? */ if(num_cols == 0) { /* no tuples returned */ /* query does not return data (it was not a SELECT) */ - lua_pushnumber(L, mysql_affected_rows(conn->my_conn)); + luasql_pushinteger(L, mysql_affected_rows(conn->my_conn)); return 1; - } - else /* mysql_use_result() should have returned data */ + } else { + /* mysql_use_result() should have returned data */ return luasql_failmsg(L, "error retrieving result. MySQL: ", mysql_error(conn->my_conn)); + } } } } @@ -460,7 +470,7 @@ static int conn_setautocommit (lua_State *L) { */ static int conn_getlastautoid (lua_State *L) { conn_data *conn = getconnection(L); - lua_pushnumber(L, mysql_insert_id(conn->my_conn)); + luasql_pushinteger(L, mysql_insert_id(conn->my_conn)); return 1; } From d8702e4f2db0bbd134571b0f59ccc85e0828f384 Mon Sep 17 00:00:00 2001 From: Scott Morgan Date: Sat, 25 Apr 2015 01:56:52 +0100 Subject: [PATCH 46/64] Ran file through beautifier Using the following command: astyle --style=1tbs --indent=tab=5 --align-pointer=name --align-reference=name --max-code-length=80 --lineend=linux ls_firebird.c Then tidied up a bit --- src/ls_firebird.c | 3028 +++++++++++++++++++++++---------------------- 1 file changed, 1550 insertions(+), 1478 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 0521cc1..d268ed7 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -1,1478 +1,1550 @@ -/* -** LuaSQL, Firebird driver -** Authors: Scott Morgan -** ls_firebird.c -*/ - -#include /* The Firebird API*/ -#include /* For managing time values */ -#include -#include -#include - -/* Lua API */ -#include -#include - -#include "luasql.h" - -#define LUASQL_ENVIRONMENT_FIREBIRD "Firebird environment" -#define LUASQL_CONNECTION_FIREBIRD "Firebird connection" -#define LUASQL_STATEMENT_FIREBIRD "Firebird statement" -#define LUASQL_CURSOR_FIREBIRD "Firebird cursor" - -typedef struct { - short closed; - ISC_STATUS status_vector[20];/* for error results */ - int lock; /* lock count for open connections */ -} env_data; - -typedef struct { - /* general */ - short closed; - env_data* env; /* the DB enviroment this is in */ - int lock; /* lock count for open cursors */ - int autocommit; /* should each statement be commited */ - /* implimentation */ - isc_db_handle db; /* the database handle */ - char dpb_buffer[1024]; /* holds the database paramet buffer */ - short dpb_length; /* the used amount of the dpb */ - isc_tr_handle transaction; /* the transaction handle */ - /* config */ - unsigned short dialect; /* dialect of SQL used */ -} conn_data; - -typedef struct { - short closed; - env_data* env; /* the DB enviroment this is in */ - conn_data* conn; /* the DB connection this cursor is from */ - XSQLDA *in_sqlda; /* the parameter data array */ - isc_stmt_handle handle; /* the statement handle */ - int type; /* the statment's type (SELECT, UPDATE, etc...) */ - int lock; /* lock count for open statements */ - unsigned char hidden; /* statement was used interally i.e. from a direct con:execute */ -} stmt_data; - -typedef struct { - short closed; - env_data* env; /* the DB enviroment this is in */ - stmt_data* stmt; /* the DB statment this cursor is from */ - XSQLDA *out_sqlda; /* the cursor data array */ -} cur_data; - -/* How many fields to pre-alloc to the cursor */ -#define CURSOR_PREALLOC 10 - -/* Macro to ease code reading */ -#define CHECK_DB_ERROR( X ) ( (X)[0] == 1 && (X)[1] ) - -/* Use the new interpret API if available */ -#undef FB_INTERPRET -#if FB_API_VER >= 20 - #define FB_INTERPRET(BUF, LEN, VECTOR) fb_interpret(BUF, LEN, VECTOR) -#else - #define FB_INTERPRET(BUF, LEN, VECTOR) isc_interpret(BUF, VECTOR) -#endif - -/* MSVC still doesn't support C99 properly until 2015 */ -#if defined(_MSC_VER) && _MSC_VER<1900 -#pragma warning(disable:4996) /* and complains if you try to work around it */ -#define snprintf _snprintf -#endif - -/* -** Returns a standard database error message -*/ -static int return_db_error(lua_State *L, const ISC_STATUS *pvector) -{ - char errmsg[512]; - - lua_pushnil(L); - FB_INTERPRET(errmsg, 512, &pvector); - lua_pushstring(L, errmsg); - while(FB_INTERPRET(errmsg, 512, &pvector)) { - lua_pushstring(L, "\n * "); - lua_pushstring(L, errmsg); - lua_concat(L, 3); - } - - return 2; -} - -/* -** Allocates and initialises an XSQLDA struct -*/ -static XSQLDA* malloc_xsqlda(ISC_SHORT len) -{ - XSQLDA *res = (XSQLDA*)malloc(XSQLDA_LENGTH(len)); - - memset(res, 0, XSQLDA_LENGTH(len)); - res->version = SQLDA_VERSION1; - res->sqln = len; - - return res; -} - -/* -** Allocate memory for XSQLDA data -*/ -static void malloc_sqlda_vars(XSQLDA *sqlda) -{ - int i; - XSQLVAR *var; - - /* prep the result set ready to handle the data */ - for (i=0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++) { - switch(var->sqltype & ~1) { - case SQL_VARYING: - var->sqldata = (char *)malloc(sizeof(char)*var->sqllen + 2); - break; - case SQL_TEXT: - var->sqldata = (char *)malloc(sizeof(char)*var->sqllen); - break; - case SQL_SHORT: - var->sqldata = (char *)malloc(sizeof(short)); - break; - case SQL_LONG: - var->sqldata = (char *)malloc(sizeof(long)); - break; - case SQL_INT64: - var->sqldata = (char *)malloc(sizeof(ISC_INT64)); - break; - case SQL_FLOAT: - var->sqldata = (char *)malloc(sizeof(float)); - break; - case SQL_DOUBLE: - var->sqldata = (char *)malloc(sizeof(double)); - break; - case SQL_TYPE_TIME: - var->sqldata = (char *)malloc(sizeof(ISC_TIME)); - break; - case SQL_TYPE_DATE: - var->sqldata = (char *)malloc(sizeof(ISC_DATE)); - break; - case SQL_TIMESTAMP: - var->sqldata = (char *)malloc(sizeof(ISC_TIMESTAMP)); - break; - case SQL_BLOB: - var->sqldata = (char *)malloc(sizeof(ISC_QUAD)); - break; - /* TODO : add extra data type handles here */ - } - - if (var->sqltype & 1) { - /* allocate variable to hold NULL status */ - var->sqlind = (short *)malloc(sizeof(short)); - } else { - var->sqlind = NULL; - } - } -} - -/* -** Frees memory allocated to XSQLDA data -*/ -static void free_sqlda_vars(XSQLDA *sqlda) { - int i; - XSQLVAR *var; - - if(sqlda != NULL) { - for (i=0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++) { - free(var->sqldata); - free(var->sqlind); - } - } -} - -/* -** Frees all XSQLDA data -*/ -static void free_xsqlda(XSQLDA *sqlda) -{ - free_sqlda_vars(sqlda); - free(sqlda); -} - -/* -** Free's up the memory alloc'd to the statement data -*/ -static void free_stmt(stmt_data* stmt) -{ - /* free the input DA */ - free_xsqlda(stmt->in_sqlda); -} - -static int stmt_shut(lua_State *L, stmt_data* stmt) -{ - isc_dsql_free_statement(stmt->env->status_vector, &stmt->handle, DSQL_drop); - if ( CHECK_DB_ERROR(stmt->env->status_vector) ) { - return return_db_error(L, stmt->env->status_vector); - } - - free_stmt(stmt); - - /* remove statement from lock count and check if connection can be unregistered */ - stmt->closed = 1; - if(--stmt->conn->lock == 0) { - luasql_unregisterobj(L, stmt->conn); - } - - return 0; -} - -/* -** Free's up the memory alloc'd to the cursor data -*/ -static void free_cur(cur_data* cur) -{ - /* free the output DA */ - free_xsqlda(cur->out_sqlda); -} - -/* -** Shuts down a cursor -*/ -static int cur_shut(lua_State *L, cur_data *cur) -{ - isc_dsql_free_statement(cur->env->status_vector, &cur->stmt->handle, DSQL_close); - if ( CHECK_DB_ERROR(cur->env->status_vector) ) { - return return_db_error(L, cur->env->status_vector); - } - - /* free the cursor data */ - free_cur(cur); - - /* remove cursor from lock count and check if statment can be unregistered */ - cur->closed = 1; - if(--cur->stmt->lock == 0) { - luasql_unregisterobj(L, cur->stmt); - - /* hidden statement, needs closing now */ - if(cur->stmt->hidden) { - return stmt_shut(L, cur->stmt); - } - } - - return 0; -} - -/* -** Check for valid environment. -*/ -static env_data *getenvironment (lua_State *L, int i) { - env_data *env = (env_data *)luaL_checkudata (L, i, LUASQL_ENVIRONMENT_FIREBIRD); - luaL_argcheck (L, env != NULL, i, "environment expected"); - luaL_argcheck (L, !env->closed, i, "environment is closed"); - return env; -} - -/* -** Check for valid connection. -*/ -static conn_data *getconnection (lua_State *L, int i) { - conn_data *conn = (conn_data *)luaL_checkudata (L, i, LUASQL_CONNECTION_FIREBIRD); - luaL_argcheck (L, conn != NULL, i, "connection expected"); - luaL_argcheck (L, !conn->closed, i, "connection is closed"); - return conn; -} - -/* -** Check for valid statement. -*/ -static stmt_data *getstatement (lua_State *L, int i) { - stmt_data *stmt = (stmt_data *)luaL_checkudata (L, i, LUASQL_STATEMENT_FIREBIRD); - luaL_argcheck (L, stmt != NULL, i, "statement expected"); - luaL_argcheck (L, !stmt->closed, i, "statement is closed"); - return stmt; -} - -/* -** Check for valid cursor. -*/ -static cur_data *getcursor (lua_State *L, int i) { - cur_data *cur = (cur_data *)luaL_checkudata (L, i, LUASQL_CURSOR_FIREBIRD); - luaL_argcheck (L, cur != NULL, i, "cursor expected"); - luaL_argcheck (L, !cur->closed, i, "cursor is closed"); - return cur; -} - -/* -** Dumps the list of item's types into a new table -*/ -static int dump_xsqlda_types(lua_State *L, XSQLDA* sqlda) { - int i; - XSQLVAR *var; - - lua_newtable(L); - - for (i=1, var = sqlda->sqlvar; i <= sqlda->sqld; i++, var++) { - lua_pushnumber(L, i); - switch(var->sqltype & ~1) { - case SQL_VARYING: - case SQL_TEXT: - case SQL_TYPE_TIME: - case SQL_TYPE_DATE: - case SQL_TIMESTAMP: - case SQL_BLOB: - lua_pushstring(L, "string"); - break; - case SQL_SHORT: - case SQL_LONG: - case SQL_INT64: -#if LUA_VERSION_NUM>=503 - lua_pushstring(L, "integer"); - break; -#endif - case SQL_FLOAT: - case SQL_DOUBLE: - lua_pushstring(L, "number"); - break; - default: - lua_pushstring(L, "unknown"); - break; - } - lua_settable(L, -3); - } - - return 1; -} - -/* -** Returns the statement type -*/ -static int get_statement_type(stmt_data *stmt) -{ - int length, type; - char type_item[] = { isc_info_sql_stmt_type }; - char res_buffer[88], *pres; - - pres = res_buffer; - - isc_dsql_sql_info( stmt->env->status_vector, &stmt->handle, - sizeof(type_item), type_item, - sizeof(res_buffer), res_buffer ); - if (stmt->env->status_vector[0] == 1 && stmt->env->status_vector[1] > 0) - return -1; - - /* check the type of the statement */ - if (*pres == isc_info_sql_stmt_type) - { - pres++; - length = isc_vax_integer(pres, 2); - pres += 2; - type = isc_vax_integer(pres, length); - pres += length; - } else - return -2; /* should have had the isc_info_sql_stmt_type info */ - - return type; -} - -/* -** Return the number of rows affected by last operation -*/ -static int count_rows_affected(env_data *env, cur_data *cur) -{ - int length, type, res=0; - int del_count = 0, ins_count = 0, upd_count = 0, sel_count = 0; - char type_item[] = { isc_info_sql_stmt_type, isc_info_sql_records }; - char res_buffer[88], *pres; - - pres = res_buffer; - - isc_dsql_sql_info( env->status_vector, &cur->stmt->handle, - sizeof(type_item), type_item, - sizeof(res_buffer), res_buffer ); - if (cur->env->status_vector[0] == 1 && cur->env->status_vector[1] > 0) - return -1; - - /* check the type of the statement */ - if (*pres == isc_info_sql_stmt_type) - { - pres++; - length = isc_vax_integer(pres, 2); - pres += 2; - type = isc_vax_integer(pres, length); - pres += length; - } else - return -2; /* should have had the isc_info_sql_stmt_type info */ - - if(type > 4) - return 0; /* not a SELECT, INSERT, UPDATE or DELETE SQL statement */ - - if (*pres == isc_info_sql_records) - { - pres++; - length = isc_vax_integer(pres, 2); /* normally 29 bytes */ - pres += 2; - - while(*pres != 1) { - switch(*pres) { - case isc_info_req_select_count: - pres++; - length = isc_vax_integer(pres, 2); - pres += 2; - sel_count = isc_vax_integer(pres, length); - pres += length; - break; - case isc_info_req_insert_count: - pres++; - length = isc_vax_integer(pres, 2); - pres += 2; - ins_count = isc_vax_integer(pres, length); - pres += length; - break; - case isc_info_req_update_count: - pres++; - length = isc_vax_integer(pres, 2); - pres += 2; - upd_count = isc_vax_integer(pres, length); - pres += length; - break; - case isc_info_req_delete_count: - pres++; - length = isc_vax_integer(pres, 2); - pres += 2; - del_count = isc_vax_integer(pres, length); - pres += length; - break; - default: - pres++; - break; - } - } - } else - return -3; - - switch(type) { - case isc_info_sql_stmt_select: - res = sel_count; - break; - case isc_info_sql_stmt_delete: - res = del_count; - break; - case isc_info_sql_stmt_update: - res = upd_count; - break; - case isc_info_sql_stmt_insert: - res = ins_count; - break; - } - return res; -} - -static void fill_param(XSQLVAR *var, ISC_SHORT type, ISC_SCHAR *data, ISC_SHORT len) -{ - var->sqltype = type; - *var->sqlind = 0; - var->sqllen = len; - - if((type & ~1) == SQL_TEXT) { - --var->sqllen; - } - - if(var->sqldata != NULL) { - free(var->sqldata); - } - var->sqldata = (ISC_SCHAR *)malloc(len); - memcpy(var->sqldata, data, len); -} - -static void parse_params(lua_State *L, stmt_data* stmt, int params) -{ - int i; - for(i=0; iin_sqlda->sqln; i++) { - XSQLVAR *var; - const char* str; - ISC_INT64 inum; - double fnum; - - lua_pushnumber(L, i+1); - lua_gettable(L, params); - - var = &stmt->in_sqlda->sqlvar[i]; - if(var->sqlind == NULL) { - var->sqlind = (ISC_SHORT *)malloc(sizeof(ISC_SHORT)); - } - - if(lua_isnil(L, -1)) { - // nil -> NULL - *var->sqlind = -1; - } else { - switch(var->sqltype & ~1) { - case SQL_VARYING: - case SQL_BLOB: - case SQL_TEXT: - str = lua_tostring(L, -1); - fill_param(var, SQL_TEXT+1, (ISC_SCHAR *)str, strlen(str)+1); - break; - - case SQL_INT64: - case SQL_LONG: - case SQL_SHORT: - inum = (ISC_INT64)lua_tonumber(L, -1); - fill_param(var, SQL_INT64+1, (ISC_SCHAR *)&inum, sizeof(ISC_INT64)); - break; - - case SQL_DOUBLE: - case SQL_D_FLOAT: - case SQL_FLOAT: - fnum = (double)lua_tonumber(L, -1); - fill_param(var, SQL_DOUBLE+1, (ISC_SCHAR *)&fnum, sizeof(double)); - break; - - case SQL_TIMESTAMP: - case SQL_TYPE_TIME: - case SQL_TYPE_DATE: - switch(lua_type(L, -1)) { - case LUA_TNUMBER: { - /* os.time type value passed */ - time_t t_time = (time_t)lua_tointeger(L,-1); - struct tm *tm_time = localtime(&t_time); - ISC_TIMESTAMP isc_ts; - isc_encode_timestamp(tm_time, &isc_ts); - - fill_param(var, SQL_TIMESTAMP+1, (ISC_SCHAR *)&isc_ts, sizeof(ISC_TIMESTAMP)); - } break; - - case LUA_TSTRING: { - /* date/time string passed */ - str = lua_tostring(L, -1); - fill_param(var, SQL_TEXT+1, (ISC_SCHAR *)str, strlen(str)+1); - } break; - - default: { - /* unknown pass empty string, which should error out */ - str = lua_tostring(L, -1); - fill_param(var, SQL_TEXT+1, (ISC_SCHAR *)"", 1); - } break; - } - break; - } - } - - lua_pop(L,1); /* param value */ - } -} - -/* -** Prepares a SQL statement. -** Lua input: -** SQL statement -** [parmeter table] -** Returns -** statement object ready for setting parameters -** nil and error message otherwise. -*/ -static int conn_prepare (lua_State *L) { - conn_data *conn = getconnection(L,1); - const char *statement = luaL_checkstring(L, 2); - - stmt_data* user_stmt; - - stmt_data stmt; - - memset(&stmt, 0, sizeof(stmt_data)); - - stmt.closed = 0; - stmt.env = conn->env; - stmt.conn = conn; - - stmt.handle = NULL; - - /* create a statement to handle the query */ - isc_dsql_allocate_statement(conn->env->status_vector, &conn->db, &stmt.handle); - if ( CHECK_DB_ERROR(conn->env->status_vector) ) { - return return_db_error(L, conn->env->status_vector); - } - - /* process the SQL ready to run the query */ - isc_dsql_prepare(conn->env->status_vector, &conn->transaction, &stmt.handle, 0, (char*)statement, conn->dialect, NULL); - if ( CHECK_DB_ERROR(conn->env->status_vector) ) { - free_stmt(&stmt); - return return_db_error(L, conn->env->status_vector); - } - - /* what type of SQL statement is it? */ - stmt.type = get_statement_type(&stmt); - if(stmt.type < 0) { - free_stmt(&stmt); - return return_db_error(L, stmt.env->status_vector); - } - - /* an unsupported SQL statement (something like COMMIT) */ - if(stmt.type > 5) { - free_stmt(&stmt); - return luasql_faildirect(L, "unsupported SQL statement"); - } - - /* bind the input parameters */ - stmt.in_sqlda = malloc_xsqlda(1); - isc_dsql_describe_bind(conn->env->status_vector, &stmt.handle, 1, stmt.in_sqlda); - if ( CHECK_DB_ERROR(conn->env->status_vector) ) { - free_stmt(&stmt); - return return_db_error(L, conn->env->status_vector); - } - /* resize the parameter set if needed */ - if (stmt.in_sqlda->sqld > stmt.in_sqlda->sqln) - { - ISC_SHORT n = stmt.in_sqlda->sqld; - free_xsqlda(stmt.in_sqlda); - stmt.in_sqlda = malloc_xsqlda(n); - isc_dsql_describe_bind(conn->env->status_vector, &stmt.handle, 1, stmt.in_sqlda); - if ( CHECK_DB_ERROR(conn->env->status_vector) ) { - free_stmt(&stmt); - return return_db_error(L, conn->env->status_vector); - } - } - malloc_sqlda_vars(stmt.in_sqlda); - - /* is there a parameter table to use */ - if(lua_istable(L, 3)) { - parse_params(L, &stmt, 3); - } - - /* copy the statement into a new lua userdata object */ - user_stmt = (stmt_data*)lua_newuserdata(L, sizeof(stmt_data)); - luasql_setmeta (L, LUASQL_STATEMENT_FIREBIRD); - memcpy((void*)user_stmt, (void*)&stmt, sizeof(stmt_data)); - - /* add statement to the lock count */ - luasql_registerobj(L, 1, conn); - ++conn->lock; - - return 1; -} - -static int raw_execute (lua_State *L, int stmt_indx) -{ - int count; - cur_data cur; - stmt_data *stmt; - - if(stmt_indx < 0) { - stmt_indx = lua_gettop(L) + stmt_indx + 1; - } - - stmt = getstatement(L,stmt_indx); - - /* is there already a cursor open */ - if(stmt->lock > 0) { - return luasql_faildirect(L, "statement already has an open cursor"); - } - - memset(&cur, 0, sizeof(cur_data)); - cur.closed = 0; - cur.stmt = stmt; - cur.env = stmt->env; - - /* if it's a SELECT statment, allocate a cursor */ - if(stmt->type == isc_info_sql_stmt_select) { - char cur_name[64]; - snprintf(cur_name, sizeof(cur_name), "dyn_cursor_%p", (void *)stmt); - - /* open the cursor ready for fetch cycles */ - isc_dsql_set_cursor_name(cur.env->status_vector, &cur.stmt->handle, cur_name, 0); - if ( CHECK_DB_ERROR(cur.env->status_vector) ) { - lua_pop(L, 1); /* the userdata */ - free_cur(&cur); - return return_db_error(L, cur.env->status_vector); - } - } - - /* run the query */ - isc_dsql_execute(stmt->env->status_vector, &stmt->conn->transaction, &stmt->handle, 1, stmt->in_sqlda); - if ( CHECK_DB_ERROR(stmt->env->status_vector) ) { - free_cur(&cur); - return return_db_error(L, cur.env->status_vector); - } - - /* size the result, set if needed */ - cur.out_sqlda = malloc_xsqlda(1); - isc_dsql_describe(cur.env->status_vector, &cur.stmt->handle, 1, cur.out_sqlda); - if (cur.out_sqlda->sqld > cur.out_sqlda->sqln) { - ISC_SHORT n = cur.out_sqlda->sqld; - free_xsqlda(cur.out_sqlda); - cur.out_sqlda = malloc_xsqlda(n); - isc_dsql_describe(cur.env->status_vector, &cur.stmt->handle, 1, cur.out_sqlda); - if ( CHECK_DB_ERROR(cur.env->status_vector) ) { - free_cur(&cur); - return return_db_error(L, cur.env->status_vector); - } - } - malloc_sqlda_vars(cur.out_sqlda); - - /* what do we return? a cursor or a count */ - if(cur.out_sqlda->sqld > 0) { /* a cursor */ - cur_data* user_cur = (cur_data*)lua_newuserdata(L, sizeof(cur_data)); - luasql_setmeta (L, LUASQL_CURSOR_FIREBIRD); - - /* copy the cursor into a new lua userdata object */ - memcpy((void*)user_cur, (void*)&cur, sizeof(cur_data)); - - /* add cursor to the lock count */ - luasql_registerobj(L, stmt_indx, user_cur->stmt); - ++user_cur->stmt->lock; - } else { /* a count */ - /* if autocommit is set, commit change */ - if(cur.stmt->conn->autocommit) { - isc_commit_retaining(cur.env->status_vector, &cur.stmt->conn->transaction); - if ( CHECK_DB_ERROR(cur.env->status_vector) ) { - free_cur(&cur); - return return_db_error(L, cur.env->status_vector); - } - } - - if( (count = count_rows_affected(cur.env, &cur)) < 0 ) { - free_cur(&cur); - return return_db_error(L, cur.env->status_vector); - } - - luasql_pushinteger(L, count); - - /* totaly finished with the cursor */ - isc_dsql_free_statement(cur.env->status_vector, &cur.stmt->handle, DSQL_close); - free_cur(&cur); - } - - return 1; -} - -/* -** Executes a SQL statement. -** Lua input: -** SQL statement -** [parameter table] -** Returns -** cursor object: if there are results or -** row count: number of rows affected by statement if no results -** nil and error message otherwise. -*/ - -static int conn_execute (lua_State *L) { - int ret; - stmt_data *stmt; - - /* prepare the statement */ - if( (ret = conn_prepare(L)) != 1) { - return ret; - } - - /* execute and check result */ - if((ret = raw_execute(L, -1)) != 1) { - return ret; - } - - /* for neatness, remove stmt from stack */ - stmt = getstatement(L, -(ret+1)); - lua_remove(L, -(ret+1)); - - /* this will be an internal, hidden statment */ - stmt->hidden = 1; - - /* if statement doesn't return a cursor, close it */ - if(stmt->type != isc_info_sql_stmt_select) { - if((ret = stmt_shut(L, stmt)) != 0) { - return ret; - } - } - - return 1; -} - -/* -** Commits the current transaction -*/ -static int conn_commit(lua_State *L) { - conn_data *conn = getconnection(L,1); - - isc_commit_retaining(conn->env->status_vector, &conn->transaction); - if ( CHECK_DB_ERROR(conn->env->status_vector) ) - return return_db_error(L, conn->env->status_vector); - - lua_pushboolean(L, 1); - return 1; -} - -/* -** Rolls back the current transaction -** Lua Returns: -** 1 if rollback is sucsessful -** nil and error message otherwise. -*/ -static int conn_rollback(lua_State *L) { - conn_data *conn = getconnection(L,1); - - isc_rollback_retaining(conn->env->status_vector, &conn->transaction); - if ( CHECK_DB_ERROR(conn->env->status_vector) ) - return return_db_error(L, conn->env->status_vector); - - lua_pushboolean(L, 1); - return 1; -} - -/* -** Sets the autocommit state of the connection -** Lua Returns: -** autocommit state (0:off, 1:on) -** nil and error message on error. -*/ -static int conn_setautocommit(lua_State *L) { - conn_data *conn = getconnection(L,1); - - if(lua_toboolean(L, 2)) - conn->autocommit = 1; - else - conn->autocommit = 0; - - lua_pushboolean(L, 1); - return 1; -} - -/* -** Closes a connection. -** Lua Returns: -** 1 if close was sucsessful, 0 if already closed -** nil and error message otherwise. -*/ -static int conn_close (lua_State *L) { - conn_data *conn = (conn_data *)luaL_checkudata(L,1,LUASQL_CONNECTION_FIREBIRD); - luaL_argcheck (L, conn != NULL, 1, "connection expected"); - - /* already closed */ - if(conn->closed != 0) { - lua_pushboolean(L, 0); - return 1; - } - - /* are all related statements closed? */ - if(conn->lock > 0) - return luasql_faildirect(L, "there are still open statements/cursors"); - - if(conn->autocommit != 0) - isc_commit_transaction(conn->env->status_vector, &conn->transaction); - else - isc_rollback_transaction(conn->env->status_vector, &conn->transaction); - if ( CHECK_DB_ERROR(conn->env->status_vector) ) - return return_db_error(L, conn->env->status_vector); - - isc_detach_database(conn->env->status_vector, &conn->db); - if ( CHECK_DB_ERROR(conn->env->status_vector) ) - return return_db_error(L, conn->env->status_vector); - - conn->closed = 1; - --conn->env->lock; - - /* check environment can be GC'd */ - if(conn->env->lock == 0) - luasql_unregisterobj(L, conn->env); - - lua_pushboolean(L, 1); - return 1; -} - -/* -** GCs an connection object -*/ -static int conn_gc (lua_State *L) { - conn_data *conn = (conn_data *)luaL_checkudata(L,1,LUASQL_CONNECTION_FIREBIRD); - - if(conn->closed == 0) { - if(conn->autocommit != 0) - isc_commit_transaction(conn->env->status_vector, &conn->transaction); - else - isc_rollback_transaction(conn->env->status_vector, &conn->transaction); - - isc_detach_database(conn->env->status_vector, &conn->db); - - conn->closed = 1; - --conn->env->lock; - - /* check environment can be GC'd */ - if(conn->env->lock == 0) - luasql_unregisterobj(L, conn->env); - } - - return 0; -} - -/* -** Escapes a given string so that it can't break out of it's delimiting quotes -*/ -static int conn_escape(lua_State *L) { - size_t len; - const char *from = luaL_checklstring (L, 2, &len); - char *res = malloc(len*sizeof(char)*2+1); - char *to = res; - - if(res) { - while(*from != '\0') { - *(to++) = *from; - if(*from == '\'') - *(to++) = *from; - - from++; - } - *to = '\0'; - - lua_pushstring(L, res); - free(res); - return 1; - } - - luaL_error(L, "could not allocate escaped string"); - return 0; -} - -/* -** Pushes the indexed value onto the lua stack -*/ -static void push_column(lua_State *L, int i, cur_data *cur) { - int varcharlen; - struct tm timevar; - char timestr[256]; - ISC_STATUS blob_stat; - isc_blob_handle blob_handle = 0; - ISC_QUAD blob_id; - luaL_Buffer b; - char *buffer; - unsigned short actual_seg_len; - - if( (cur->out_sqlda->sqlvar[i].sqlind != NULL) && - (*(cur->out_sqlda->sqlvar[i].sqlind) != 0) ) { - /* a null field? */ - lua_pushnil(L); - } else { - switch(cur->out_sqlda->sqlvar[i].sqltype & ~1) { - case SQL_VARYING: - varcharlen = (int)isc_vax_integer(cur->out_sqlda->sqlvar[i].sqldata, 2); - lua_pushlstring(L, cur->out_sqlda->sqlvar[i].sqldata+2, varcharlen); - break; - case SQL_TEXT: - lua_pushlstring(L, cur->out_sqlda->sqlvar[i].sqldata, cur->out_sqlda->sqlvar[i].sqllen); - break; - case SQL_SHORT: - luasql_pushinteger(L, *(short*)(cur->out_sqlda->sqlvar[i].sqldata)); - break; - case SQL_LONG: - luasql_pushinteger(L, *(long*)(cur->out_sqlda->sqlvar[i].sqldata)); - break; - case SQL_INT64: - luasql_pushinteger(L, *(ISC_INT64*)(cur->out_sqlda->sqlvar[i].sqldata)); - break; - case SQL_FLOAT: - lua_pushnumber(L, *(float*)(cur->out_sqlda->sqlvar[i].sqldata)); - break; - case SQL_DOUBLE: - lua_pushnumber(L, *(double*)(cur->out_sqlda->sqlvar[i].sqldata)); - break; - case SQL_TYPE_TIME: - isc_decode_sql_time((ISC_TIME*)(cur->out_sqlda->sqlvar[i].sqldata), &timevar); - strftime(timestr, 255, "%X", &timevar); - lua_pushstring(L, timestr); - break; - case SQL_TYPE_DATE: - isc_decode_sql_date((ISC_DATE*)(cur->out_sqlda->sqlvar[i].sqldata), &timevar); - strftime(timestr, 255, "%Y-%m-%d", &timevar); - lua_pushstring(L, timestr); - break; - case SQL_TIMESTAMP: - isc_decode_timestamp((ISC_TIMESTAMP*)(cur->out_sqlda->sqlvar[i].sqldata), &timevar); - strftime(timestr, 255, "%Y-%m-%d %X", &timevar); - lua_pushstring(L, timestr); - break; - case SQL_BLOB: - /* get the BLOB ID and open it */ - memcpy(&blob_id, cur->out_sqlda->sqlvar[i].sqldata, sizeof(ISC_QUAD)); - isc_open_blob2( cur->env->status_vector, - &cur->stmt->conn->db, &cur->stmt->conn->transaction, - &blob_handle, &blob_id, 0, NULL ); - /* fetch the blob data */ - luaL_buffinit(L, &b); - buffer = luaL_prepbuffer(&b); - - blob_stat = isc_get_segment( cur->env->status_vector, - &blob_handle, &actual_seg_len, - LUAL_BUFFERSIZE, buffer ); - while(blob_stat == 0 || cur->env->status_vector[1] == isc_segment) { - luaL_addsize(&b, actual_seg_len); - buffer = luaL_prepbuffer(&b); - blob_stat = isc_get_segment( cur->env->status_vector, - &blob_handle, &actual_seg_len, - LUAL_BUFFERSIZE, buffer ); - } - - /* finnished, close the BLOB */ - isc_close_blob(cur->env->status_vector, &blob_handle); - blob_handle = 0; - - luaL_pushresult(&b); - break; - default: - lua_pushstring(L, ""); - break; - } - } -} - -/* -** Returns a map of parameter IDs to their types -*/ -static int stmt_get_params (lua_State *L) { - stmt_data *stmt = getstatement(L,1); - - return dump_xsqlda_types(L, stmt->in_sqlda); -} - -/* -** Executes the statement -** Lua input: -** [table of values] -** Returns -** cursor object: if there are results or -** row count: number of rows affected by statement if no results -** nil and error message otherwise. -*/ -static int stmt_execute (lua_State *L) { - stmt_data *stmt = getstatement(L,1); - - /* is there a parameter table to use */ - if(lua_istable(L, 2)) { - parse_params(L, stmt, 2); - } - - return raw_execute(L, 1); -} - -/* -** Closes a statement object -** Lua Returns: -** true if close was sucsessful, false if already closed -** nil and error message otherwise. -*/ -static int stmt_close (lua_State *L) { - stmt_data *stmt = (stmt_data *)luaL_checkudata(L,1,LUASQL_STATEMENT_FIREBIRD); - luaL_argcheck (L, stmt != NULL, 1, "statement expected"); - - if(stmt->lock > 0) { - return luasql_faildirect(L, "there are still open cursors"); - } - - if(stmt->closed == 0) { - int res = stmt_shut(L, stmt); - if(res != 0) { - return res; - } - - /* return sucsess */ - lua_pushboolean(L, 1); - return 1; - } - - lua_pushboolean(L, 0); - return 1; -} - -/* -** Frees up memory alloc'd to a statement -*/ -static int stmt_gc (lua_State *L) { - stmt_data *stmt = (stmt_data *)luaL_checkudata(L,1,LUASQL_STATEMENT_FIREBIRD); - luaL_argcheck (L, stmt != NULL, 1, "statement expected"); - - if(stmt->closed == 0) { - if(stmt_shut(L, stmt) != 0) { - return 1; - } - } - - return 0; -} - -/* -** Returns a row of data from the query -** Lua Returns: -** list of results or table of results depending on call -** nil and error message otherwise. -*/ -static int cur_fetch (lua_State *L) { - ISC_STATUS fetch_stat; - int i; - cur_data *cur = getcursor(L,1); - const char *opts = luaL_optstring (L, 3, "n"); - int num = strchr(opts, 'n') != NULL; - int alpha = strchr(opts, 'a') != NULL; - - if ((fetch_stat = isc_dsql_fetch(cur->env->status_vector, &cur->stmt->handle, 1, cur->out_sqlda)) == 0) { - if (lua_istable (L, 2)) { - /* remove the option string */ - lua_settop(L, 2); - - /* loop through the columns */ - for (i = 0; i < cur->out_sqlda->sqld; i++) { - push_column(L, i, cur); - - if( num ) { - lua_pushnumber(L, i+1); - lua_pushvalue(L, -2); - lua_settable(L, 2); - } - - if( alpha ) { - lua_pushlstring(L, cur->out_sqlda->sqlvar[i].aliasname, cur->out_sqlda->sqlvar[i].aliasname_length); - lua_pushvalue(L, -2); - lua_settable(L, 2); - } - - lua_pop(L, 1); - } - - /* returning given table */ - return 1; - } else { - for (i = 0; i < cur->out_sqlda->sqld; i++) - push_column(L, i, cur); - - /* returning a list of values */ - return cur->out_sqlda->sqld; - } - } - - /* isc_dsql_fetch returns 100 if no more rows remain to be retrieved - so this can be ignored */ - if (fetch_stat != 100L) - return return_db_error(L, cur->env->status_vector); - - /* shut cursor */ - return cur_shut(L, cur); -} - -/* -** Returns a table of column names from the query -** Lua Returns: -** a table of column names -** nil and error message otherwise. -*/ -static int cur_colnames (lua_State *L) { - int i; - XSQLVAR *var; - cur_data *cur = getcursor(L,1); - - lua_newtable(L); - - for (i=1, var = cur->out_sqlda->sqlvar; i <= cur->out_sqlda->sqld; i++, var++) { - lua_pushnumber(L, i); - lua_pushlstring(L, var->aliasname, var->aliasname_length); - lua_settable(L, -3); - } - - return 1; -} - -/* -** Returns a table of column types from the query -** Lua Returns: -** a table of column types -** nil and error message otherwise. -*/ -static int cur_coltypes (lua_State *L) { - cur_data *cur = getcursor(L,1); - - return dump_xsqlda_types(L, cur->out_sqlda); -} - -/* -** Closes a cursor object -** Lua Returns: -** true if close was sucsessful, false if already closed -** nil and error message otherwise. -*/ -static int cur_close (lua_State *L) { - cur_data *cur = (cur_data *)luaL_checkudata(L,1,LUASQL_CURSOR_FIREBIRD); - luaL_argcheck (L, cur != NULL, 1, "cursor expected"); - - if(cur->closed == 0) { - int res = cur_shut(L, cur); - if(res != 0) { - return res; - } - - /* return sucsess */ - lua_pushboolean(L, 1); - return 1; - } - - lua_pushboolean(L, 0); - return 1; -} - -/* -** GCs a cursor object -*/ -static int cur_gc (lua_State *L) { - cur_data *cur = (cur_data *)luaL_checkudata(L,1,LUASQL_CURSOR_FIREBIRD); - luaL_argcheck (L, cur != NULL, 1, "cursor expected"); - - if(cur->closed == 0) { - if(cur_shut(L, cur) != 0) { - return 1; - } - } - - return 0; -} - -/* -** Creates an Environment and returns it. -*/ -static int create_environment (lua_State *L) { - int i; - env_data *env; - - env = (env_data *)lua_newuserdata (L, sizeof (env_data)); - luasql_setmeta (L, LUASQL_ENVIRONMENT_FIREBIRD); - /* fill in structure */ - for(i=0; i<20; i++) - env->status_vector[i] = 0; - env->closed = 0; - env->lock = 0; - - return 1; -} - -/* -** Reforms old connection style to new one -** Lua Input: source, [user, [pass]] -** source: data source -** user, pass: data source authentication information -** Lua Returns: -** new connection details table -*/ -static void env_connect_fix_old (lua_State *L) { - static const char* const opt_names[] = { - "source", - "user", - "password", - NULL - }; - int i, t = lua_gettop(L)-1; - - lua_newtable(L); - lua_insert(L, 2); - - for(i=0; opt_names[i] != NULL && i, -** user = , -** password = , -** [charset = ,] -** [dialect = ,] -** } -** Lua Returns: -** connection object if successfull -** nil and error message otherwise. -*/ -static int env_connect (lua_State *L) { - char *dpb, *dpb_end; - static char isc_tpb[] = { - isc_tpb_version3, - isc_tpb_write - }; - - conn_data conn; - conn_data* res_conn; - const char *sourcename; - - env_data *env = (env_data *) getenvironment (L, 1); - - if(lua_gettop(L) < 2) { - return luasql_faildirect(L, "No connection details provided"); - } - - if(!lua_istable(L, 2)) { - env_connect_fix_old(L); - } - - sourcename = luasql_table_optstring(L, 2, "source", NULL); - - if(sourcename == NULL) { - return luasql_faildirect(L, "connection details table missing 'source'"); - } - - conn.env = env; - conn.db = 0L; - conn.transaction = 0L; - conn.lock = 0; - conn.autocommit = 0; - - /* construct a database parameter buffer. */ - dpb = conn.dpb_buffer; - dpb_end = conn.dpb_buffer + sizeof(conn.dpb_buffer); - *dpb++ = isc_dpb_version1; - *dpb++ = isc_dpb_num_buffers; - *dpb++ = 1; - *dpb++ = 90; - - /* add the user name and password */ - dpb = add_dpb_string(dpb, dpb_end, isc_dpb_user_name, luasql_table_optstring(L, 2, "user", "")); - dpb = add_dpb_string(dpb, dpb_end, isc_dpb_password, luasql_table_optstring(L, 2, "password", "")); - - /* other database parameters */ - dpb = add_dpb_string(dpb, dpb_end, isc_dpb_lc_ctype, luasql_table_optstring(L, 2, "charset", "UTF8")); - conn.dialect = (unsigned short)luasql_table_optnumber(L, 2, "dialect", 3); - - /* the length of the dpb */ - conn.dpb_length = (short)(dpb - conn.dpb_buffer); - - /* do the job */ - isc_attach_database(env->status_vector, (short)strlen(sourcename), (char*)sourcename, &conn.db, - conn.dpb_length, conn.dpb_buffer); - - /* an error? */ - if ( CHECK_DB_ERROR(conn.env->status_vector) ) - return return_db_error(L, conn.env->status_vector); - - /* open up the transaction handle */ - isc_start_transaction( env->status_vector, &conn.transaction, 1, - &conn.db, (unsigned short)sizeof(isc_tpb), - isc_tpb ); - - /* an error? */ - if ( CHECK_DB_ERROR(conn.env->status_vector) ) - return return_db_error(L, conn.env->status_vector); - - /* create the lua object and add the connection to the lock */ - res_conn = (conn_data*)lua_newuserdata(L, sizeof(conn_data)); - luasql_setmeta (L, LUASQL_CONNECTION_FIREBIRD); - memcpy(res_conn, &conn, sizeof(conn_data)); - res_conn->closed = 0; /* connect now officially open */ - - /* register the connection */ - luasql_registerobj(L, 1, env); - ++env->lock; - - return 1; -} - -/* -** Closes an environment object -** Lua Returns: -** 1 if close was sucsessful, 0 if already closed -** nil and error message otherwise. -*/ -static int env_close (lua_State *L) { - env_data *env = (env_data *)luaL_checkudata (L, 1, LUASQL_ENVIRONMENT_FIREBIRD); - luaL_argcheck (L, env != NULL, 1, "environment expected"); - - /* already closed? */ - if(env->closed == 1) { - lua_pushboolean(L, 0); - return 1; - } - - /* check the lock */ - if(env->lock > 0) - return luasql_faildirect(L, "there are still open connections"); - - /* unregister */ - luasql_unregisterobj(L, env); - - /* mark as closed */ - env->closed = 1; - - lua_pushboolean(L, 1); - return 1; -} - -/* -** GCs an environment object -*/ -static int env_gc (lua_State *L) { - /* nothing to be done for the FB envronment */ - return 0; -} - -/* -** Create metatables for each class of object. -*/ -static void create_metatables (lua_State *L) { - struct luaL_Reg environment_methods[] = { - {"__gc", env_gc}, - {"close", env_close}, - {"connect", env_connect}, - {NULL, NULL}, - }; - struct luaL_Reg connection_methods[] = { - {"__gc", conn_gc}, - {"close", conn_close}, - {"prepare", conn_prepare}, - {"execute", conn_execute}, - {"commit", conn_commit}, - {"rollback", conn_rollback}, - {"setautocommit", conn_setautocommit}, - {"escape", conn_escape}, - {NULL, NULL}, - }; - struct luaL_Reg statement_methods[] = { - {"__gc", stmt_gc}, - {"close", stmt_close}, - {"getparamtypes", stmt_get_params}, - {"execute", stmt_execute}, - {NULL, NULL}, - }; - struct luaL_Reg cursor_methods[] = { - {"__gc", cur_gc}, - {"close", cur_close}, - {"fetch", cur_fetch}, - {"getcoltypes", cur_coltypes}, - {"getcolnames", cur_colnames}, - {NULL, NULL}, - }; - luasql_createmeta (L, LUASQL_ENVIRONMENT_FIREBIRD, environment_methods); - luasql_createmeta (L, LUASQL_CONNECTION_FIREBIRD, connection_methods); - luasql_createmeta (L, LUASQL_STATEMENT_FIREBIRD, statement_methods); - luasql_createmeta (L, LUASQL_CURSOR_FIREBIRD, cursor_methods); - lua_pop (L, 4); -} - -/* -** Creates the metatables for the objects and registers the -** driver open method. -*/ -LUASQL_API int luaopen_luasql_firebird (lua_State *L) { - struct luaL_Reg driver[] = { - {"firebird", create_environment}, - {NULL, NULL}, - }; - create_metatables (L); - luasql_find_driver_table (L); - luaL_setfuncs (L, driver, 0); - luasql_set_info (L); - return 1; -} +/* +** LuaSQL, Firebird driver +** Authors: Scott Morgan +** ls_firebird.c +*/ + +#include /* The Firebird API*/ +#include /* For managing time values */ +#include +#include +#include + +/* Lua API */ +#include +#include + +#include "luasql.h" + +#define LUASQL_ENVIRONMENT_FIREBIRD "Firebird environment" +#define LUASQL_CONNECTION_FIREBIRD "Firebird connection" +#define LUASQL_STATEMENT_FIREBIRD "Firebird statement" +#define LUASQL_CURSOR_FIREBIRD "Firebird cursor" + +typedef struct { + short closed; + ISC_STATUS status_vector[20];/* for error results */ + int lock; /* lock count for open connections */ +} env_data; + +typedef struct { + /* general */ + short closed; + env_data *env; /* the DB enviroment this is in */ + int lock; /* lock count for open cursors */ + int autocommit; /* should each statement be commited */ + /* implimentation */ + isc_db_handle db; /* the database handle */ + char dpb_buffer[1024]; /* holds the database paramet buffer */ + short dpb_length; /* the used amount of the dpb */ + isc_tr_handle transaction; /* the transaction handle */ + /* config */ + unsigned short dialect; /* dialect of SQL used */ +} conn_data; + +typedef struct { + short closed; + env_data *env; /* the DB enviroment this is in */ + conn_data *conn; /* the DB connection this cursor is from */ + XSQLDA *in_sqlda; /* the parameter data array */ + isc_stmt_handle handle; /* the statement handle */ + int + type; /* the statment's type (SELECT, UPDATE, etc...) */ + int lock; /* lock count for open statements */ + unsigned char + hidden; /* statement was used interally i.e. from a + direct con:execute */ +} stmt_data; + +typedef struct { + short closed; + env_data *env; /* the DB enviroment this is in */ + stmt_data *stmt; /* the DB statment this cursor is from */ + XSQLDA *out_sqlda; /* the cursor data array */ +} cur_data; + +/* How many fields to pre-alloc to the cursor */ +#define CURSOR_PREALLOC 10 + +/* Macro to ease code reading */ +#define CHECK_DB_ERROR( X ) ( (X)[0] == 1 && (X)[1] ) + +/* Use the new interpret API if available */ +#undef FB_INTERPRET +#if FB_API_VER >= 20 +#define FB_INTERPRET(BUF, LEN, VECTOR) fb_interpret(BUF, LEN, VECTOR) +#else +#define FB_INTERPRET(BUF, LEN, VECTOR) isc_interpret(BUF, VECTOR) +#endif + +/* MSVC still doesn't support C99 properly until 2015 */ +#if defined(_MSC_VER) && _MSC_VER<1900 +#pragma warning(disable:4996) /* and complains if you try to work around it */ +#define snprintf _snprintf +#endif + +/* +** Returns a standard database error message +*/ +static int return_db_error(lua_State *L, const ISC_STATUS *pvector) +{ + char errmsg[512]; + + lua_pushnil(L); + FB_INTERPRET(errmsg, 512, &pvector); + lua_pushstring(L, errmsg); + while(FB_INTERPRET(errmsg, 512, &pvector)) { + lua_pushstring(L, "\n * "); + lua_pushstring(L, errmsg); + lua_concat(L, 3); + } + + return 2; +} + +/* +** Allocates and initialises an XSQLDA struct +*/ +static XSQLDA *malloc_xsqlda(ISC_SHORT len) +{ + XSQLDA *res = (XSQLDA *)malloc(XSQLDA_LENGTH(len)); + + memset(res, 0, XSQLDA_LENGTH(len)); + res->version = SQLDA_VERSION1; + res->sqln = len; + + return res; +} + +/* +** Allocate memory for XSQLDA data +*/ +static void malloc_sqlda_vars(XSQLDA *sqlda) +{ + int i; + XSQLVAR *var; + + /* prep the result set ready to handle the data */ + for (i=0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++) { + switch(var->sqltype & ~1) { + case SQL_VARYING: + var->sqldata = (char *)malloc(sizeof(char)*var->sqllen + 2); + break; + case SQL_TEXT: + var->sqldata = (char *)malloc(sizeof(char)*var->sqllen); + break; + case SQL_SHORT: + var->sqldata = (char *)malloc(sizeof(short)); + break; + case SQL_LONG: + var->sqldata = (char *)malloc(sizeof(long)); + break; + case SQL_INT64: + var->sqldata = (char *)malloc(sizeof(ISC_INT64)); + break; + case SQL_FLOAT: + var->sqldata = (char *)malloc(sizeof(float)); + break; + case SQL_DOUBLE: + var->sqldata = (char *)malloc(sizeof(double)); + break; + case SQL_TYPE_TIME: + var->sqldata = (char *)malloc(sizeof(ISC_TIME)); + break; + case SQL_TYPE_DATE: + var->sqldata = (char *)malloc(sizeof(ISC_DATE)); + break; + case SQL_TIMESTAMP: + var->sqldata = (char *)malloc(sizeof(ISC_TIMESTAMP)); + break; + case SQL_BLOB: + var->sqldata = (char *)malloc(sizeof(ISC_QUAD)); + break; + /* TODO : add extra data type handles here */ + } + + if (var->sqltype & 1) { + /* allocate variable to hold NULL status */ + var->sqlind = (short *)malloc(sizeof(short)); + } else { + var->sqlind = NULL; + } + } +} + +/* +** Frees memory allocated to XSQLDA data +*/ +static void free_sqlda_vars(XSQLDA *sqlda) +{ + int i; + XSQLVAR *var; + + if(sqlda != NULL) { + for (i=0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++) { + free(var->sqldata); + free(var->sqlind); + } + } +} + +/* +** Frees all XSQLDA data +*/ +static void free_xsqlda(XSQLDA *sqlda) +{ + free_sqlda_vars(sqlda); + free(sqlda); +} + +/* +** Free's up the memory alloc'd to the statement data +*/ +static void free_stmt(stmt_data *stmt) +{ + /* free the input DA */ + free_xsqlda(stmt->in_sqlda); +} + +static int stmt_shut(lua_State *L, stmt_data *stmt) +{ + isc_dsql_free_statement(stmt->env->status_vector, &stmt->handle, DSQL_drop); + if ( CHECK_DB_ERROR(stmt->env->status_vector) ) { + return return_db_error(L, stmt->env->status_vector); + } + + free_stmt(stmt); + + /* remove statement from lock count and check if connection can be unregistered */ + stmt->closed = 1; + if(--stmt->conn->lock == 0) { + luasql_unregisterobj(L, stmt->conn); + } + + return 0; +} + +/* +** Free's up the memory alloc'd to the cursor data +*/ +static void free_cur(cur_data *cur) +{ + /* free the output DA */ + free_xsqlda(cur->out_sqlda); +} + +/* +** Shuts down a cursor +*/ +static int cur_shut(lua_State *L, cur_data *cur) +{ + isc_dsql_free_statement(cur->env->status_vector, &cur->stmt->handle, + DSQL_close); + if ( CHECK_DB_ERROR(cur->env->status_vector) ) { + return return_db_error(L, cur->env->status_vector); + } + + /* free the cursor data */ + free_cur(cur); + + /* remove cursor from lock count and check if statment can be unregistered */ + cur->closed = 1; + if(--cur->stmt->lock == 0) { + luasql_unregisterobj(L, cur->stmt); + + /* hidden statement, needs closing now */ + if(cur->stmt->hidden) { + return stmt_shut(L, cur->stmt); + } + } + + return 0; +} + +/* +** Check for valid environment. +*/ +static env_data *getenvironment (lua_State *L, int i) +{ + env_data *env = (env_data *)luaL_checkudata (L, i, LUASQL_ENVIRONMENT_FIREBIRD); + luaL_argcheck (L, env != NULL, i, "environment expected"); + luaL_argcheck (L, !env->closed, i, "environment is closed"); + return env; +} + +/* +** Check for valid connection. +*/ +static conn_data *getconnection (lua_State *L, int i) +{ + conn_data *conn = (conn_data *)luaL_checkudata (L, i, + LUASQL_CONNECTION_FIREBIRD); + luaL_argcheck (L, conn != NULL, i, "connection expected"); + luaL_argcheck (L, !conn->closed, i, "connection is closed"); + return conn; +} + +/* +** Check for valid statement. +*/ +static stmt_data *getstatement (lua_State *L, int i) +{ + stmt_data *stmt = (stmt_data *)luaL_checkudata (L, i, + LUASQL_STATEMENT_FIREBIRD); + luaL_argcheck (L, stmt != NULL, i, "statement expected"); + luaL_argcheck (L, !stmt->closed, i, "statement is closed"); + return stmt; +} + +/* +** Check for valid cursor. +*/ +static cur_data *getcursor (lua_State *L, int i) +{ + cur_data *cur = (cur_data *)luaL_checkudata (L, i, LUASQL_CURSOR_FIREBIRD); + luaL_argcheck (L, cur != NULL, i, "cursor expected"); + luaL_argcheck (L, !cur->closed, i, "cursor is closed"); + return cur; +} + +/* +** Dumps the list of item's types into a new table +*/ +static int dump_xsqlda_types(lua_State *L, XSQLDA *sqlda) +{ + int i; + XSQLVAR *var; + + lua_newtable(L); + + for (i=1, var = sqlda->sqlvar; i <= sqlda->sqld; i++, var++) { + lua_pushnumber(L, i); + switch(var->sqltype & ~1) { + case SQL_VARYING: + case SQL_TEXT: + case SQL_TYPE_TIME: + case SQL_TYPE_DATE: + case SQL_TIMESTAMP: + case SQL_BLOB: + lua_pushstring(L, "string"); + break; + case SQL_SHORT: + case SQL_LONG: + case SQL_INT64: +#if LUA_VERSION_NUM>=503 + lua_pushstring(L, "integer"); + break; +#endif + case SQL_FLOAT: + case SQL_DOUBLE: + lua_pushstring(L, "number"); + break; + default: + lua_pushstring(L, "unknown"); + break; + } + lua_settable(L, -3); + } + + return 1; +} + +/* +** Returns the statement type +*/ +static int get_statement_type(stmt_data *stmt) +{ + int length, type; + char type_item[] = { isc_info_sql_stmt_type }; + char res_buffer[88], *pres; + + pres = res_buffer; + + isc_dsql_sql_info( stmt->env->status_vector, &stmt->handle, + sizeof(type_item), type_item, + sizeof(res_buffer), res_buffer ); + if (stmt->env->status_vector[0] == 1 && stmt->env->status_vector[1] > 0) { + return -1; + } + + /* check the type of the statement */ + if (*pres == isc_info_sql_stmt_type) { + pres++; + length = isc_vax_integer(pres, 2); + pres += 2; + type = isc_vax_integer(pres, length); + pres += length; + } else { + return -2; /* should have had the isc_info_sql_stmt_type info */ + } + + return type; +} + +/* +** Return the number of rows affected by last operation +*/ +static int count_rows_affected(env_data *env, cur_data *cur) +{ + int length, type, res=0; + int del_count = 0, ins_count = 0, upd_count = 0, sel_count = 0; + char type_item[] = { isc_info_sql_stmt_type, isc_info_sql_records }; + char res_buffer[88], *pres; + + pres = res_buffer; + + isc_dsql_sql_info( env->status_vector, &cur->stmt->handle, + sizeof(type_item), type_item, + sizeof(res_buffer), res_buffer ); + if (cur->env->status_vector[0] == 1 && cur->env->status_vector[1] > 0) { + return -1; + } + + /* check the type of the statement */ + if (*pres == isc_info_sql_stmt_type) { + pres++; + length = isc_vax_integer(pres, 2); + pres += 2; + type = isc_vax_integer(pres, length); + pres += length; + } else { + return -2; /* should have had the isc_info_sql_stmt_type info */ + } + + if(type > 4) { + return 0; /* not a SELECT, INSERT, UPDATE or DELETE SQL statement */ + } + + if (*pres == isc_info_sql_records) { + pres++; + length = isc_vax_integer(pres, 2); /* normally 29 bytes */ + pres += 2; + + while(*pres != 1) { + switch(*pres) { + case isc_info_req_select_count: + pres++; + length = isc_vax_integer(pres, 2); + pres += 2; + sel_count = isc_vax_integer(pres, length); + pres += length; + break; + case isc_info_req_insert_count: + pres++; + length = isc_vax_integer(pres, 2); + pres += 2; + ins_count = isc_vax_integer(pres, length); + pres += length; + break; + case isc_info_req_update_count: + pres++; + length = isc_vax_integer(pres, 2); + pres += 2; + upd_count = isc_vax_integer(pres, length); + pres += length; + break; + case isc_info_req_delete_count: + pres++; + length = isc_vax_integer(pres, 2); + pres += 2; + del_count = isc_vax_integer(pres, length); + pres += length; + break; + default: + pres++; + break; + } + } + } else { + return -3; + } + + switch(type) { + case isc_info_sql_stmt_select: + res = sel_count; + break; + case isc_info_sql_stmt_delete: + res = del_count; + break; + case isc_info_sql_stmt_update: + res = upd_count; + break; + case isc_info_sql_stmt_insert: + res = ins_count; + break; + } + return res; +} + +static void fill_param(XSQLVAR *var, ISC_SHORT type, ISC_SCHAR *data, + ISC_SHORT len) +{ + var->sqltype = type; + *var->sqlind = 0; + var->sqllen = len; + + if((type & ~1) == SQL_TEXT) { + --var->sqllen; + } + + if(var->sqldata != NULL) { + free(var->sqldata); + } + var->sqldata = (ISC_SCHAR *)malloc(len); + memcpy(var->sqldata, data, len); +} + +static void parse_params(lua_State *L, stmt_data *stmt, int params) +{ + int i; + for(i=0; iin_sqlda->sqln; i++) { + XSQLVAR *var; + const char *str; + ISC_INT64 inum; + double fnum; + + lua_pushnumber(L, i+1); + lua_gettable(L, params); + + var = &stmt->in_sqlda->sqlvar[i]; + if(var->sqlind == NULL) { + var->sqlind = (ISC_SHORT *)malloc(sizeof(ISC_SHORT)); + } + + if(lua_isnil(L, -1)) { + // nil -> NULL + *var->sqlind = -1; + } else { + switch(var->sqltype & ~1) { + case SQL_VARYING: + case SQL_BLOB: + case SQL_TEXT: + str = lua_tostring(L, -1); + fill_param(var, SQL_TEXT+1, (ISC_SCHAR *)str, strlen(str)+1); + break; + + case SQL_INT64: + case SQL_LONG: + case SQL_SHORT: + inum = (ISC_INT64)lua_tonumber(L, -1); + fill_param(var, SQL_INT64+1, (ISC_SCHAR *)&inum, sizeof(ISC_INT64)); + break; + + case SQL_DOUBLE: + case SQL_D_FLOAT: + case SQL_FLOAT: + fnum = (double)lua_tonumber(L, -1); + fill_param(var, SQL_DOUBLE+1, (ISC_SCHAR *)&fnum, sizeof(double)); + break; + + case SQL_TIMESTAMP: + case SQL_TYPE_TIME: + case SQL_TYPE_DATE: + switch(lua_type(L, -1)) { + case LUA_TNUMBER: { + /* os.time type value passed */ + time_t t_time = (time_t)lua_tointeger(L,-1); + struct tm *tm_time = localtime(&t_time); + ISC_TIMESTAMP isc_ts; + isc_encode_timestamp(tm_time, &isc_ts); + + fill_param(var, SQL_TIMESTAMP+1, (ISC_SCHAR *)&isc_ts, sizeof(ISC_TIMESTAMP)); + } break; + + case LUA_TSTRING: { + /* date/time string passed */ + str = lua_tostring(L, -1); + fill_param(var, SQL_TEXT+1, (ISC_SCHAR *)str, strlen(str)+1); + } break; + + default: { + /* unknown pass empty string, which should error out */ + str = lua_tostring(L, -1); + fill_param(var, SQL_TEXT+1, (ISC_SCHAR *)"", 1); + } break; + } + break; + } + } + + lua_pop(L,1); /* param value */ + } +} + +/* +** Prepares a SQL statement. +** Lua input: +** SQL statement +** [parmeter table] +** Returns +** statement object ready for setting parameters +** nil and error message otherwise. +*/ +static int conn_prepare (lua_State *L) +{ + conn_data *conn = getconnection(L,1); + const char *statement = luaL_checkstring(L, 2); + + stmt_data *user_stmt; + + stmt_data stmt; + + memset(&stmt, 0, sizeof(stmt_data)); + + stmt.closed = 0; + stmt.env = conn->env; + stmt.conn = conn; + + stmt.handle = NULL; + + /* create a statement to handle the query */ + isc_dsql_allocate_statement(conn->env->status_vector, &conn->db, &stmt.handle); + if ( CHECK_DB_ERROR(conn->env->status_vector) ) { + return return_db_error(L, conn->env->status_vector); + } + + /* process the SQL ready to run the query */ + isc_dsql_prepare(conn->env->status_vector, &conn->transaction, &stmt.handle, 0, + (char *)statement, conn->dialect, NULL); + if ( CHECK_DB_ERROR(conn->env->status_vector) ) { + free_stmt(&stmt); + return return_db_error(L, conn->env->status_vector); + } + + /* what type of SQL statement is it? */ + stmt.type = get_statement_type(&stmt); + if(stmt.type < 0) { + free_stmt(&stmt); + return return_db_error(L, stmt.env->status_vector); + } + + /* an unsupported SQL statement (something like COMMIT) */ + if(stmt.type > 5) { + free_stmt(&stmt); + return luasql_faildirect(L, "unsupported SQL statement"); + } + + /* bind the input parameters */ + stmt.in_sqlda = malloc_xsqlda(1); + isc_dsql_describe_bind(conn->env->status_vector, &stmt.handle, 1, + stmt.in_sqlda); + if ( CHECK_DB_ERROR(conn->env->status_vector) ) { + free_stmt(&stmt); + return return_db_error(L, conn->env->status_vector); + } + /* resize the parameter set if needed */ + if (stmt.in_sqlda->sqld > stmt.in_sqlda->sqln) { + ISC_SHORT n = stmt.in_sqlda->sqld; + free_xsqlda(stmt.in_sqlda); + stmt.in_sqlda = malloc_xsqlda(n); + isc_dsql_describe_bind(conn->env->status_vector, &stmt.handle, 1, + stmt.in_sqlda); + if ( CHECK_DB_ERROR(conn->env->status_vector) ) { + free_stmt(&stmt); + return return_db_error(L, conn->env->status_vector); + } + } + malloc_sqlda_vars(stmt.in_sqlda); + + /* is there a parameter table to use */ + if(lua_istable(L, 3)) { + parse_params(L, &stmt, 3); + } + + /* copy the statement into a new lua userdata object */ + user_stmt = (stmt_data *)lua_newuserdata(L, sizeof(stmt_data)); + luasql_setmeta (L, LUASQL_STATEMENT_FIREBIRD); + memcpy((void *)user_stmt, (void *)&stmt, sizeof(stmt_data)); + + /* add statement to the lock count */ + luasql_registerobj(L, 1, conn); + ++conn->lock; + + return 1; +} + +static int raw_execute (lua_State *L, int stmt_indx) +{ + int count; + cur_data cur; + stmt_data *stmt; + + if(stmt_indx < 0) { + stmt_indx = lua_gettop(L) + stmt_indx + 1; + } + + stmt = getstatement(L,stmt_indx); + + /* is there already a cursor open */ + if(stmt->lock > 0) { + return luasql_faildirect(L, "statement already has an open cursor"); + } + + memset(&cur, 0, sizeof(cur_data)); + cur.closed = 0; + cur.stmt = stmt; + cur.env = stmt->env; + + /* if it's a SELECT statment, allocate a cursor */ + if(stmt->type == isc_info_sql_stmt_select) { + char cur_name[64]; + snprintf(cur_name, sizeof(cur_name), "dyn_cursor_%p", (void *)stmt); + + /* open the cursor ready for fetch cycles */ + isc_dsql_set_cursor_name(cur.env->status_vector, &cur.stmt->handle, cur_name, + 0); + if ( CHECK_DB_ERROR(cur.env->status_vector) ) { + lua_pop(L, 1); /* the userdata */ + free_cur(&cur); + return return_db_error(L, cur.env->status_vector); + } + } + + /* run the query */ + isc_dsql_execute(stmt->env->status_vector, &stmt->conn->transaction, + &stmt->handle, 1, stmt->in_sqlda); + if ( CHECK_DB_ERROR(stmt->env->status_vector) ) { + free_cur(&cur); + return return_db_error(L, cur.env->status_vector); + } + + /* size the result, set if needed */ + cur.out_sqlda = malloc_xsqlda(1); + isc_dsql_describe(cur.env->status_vector, &cur.stmt->handle, 1, cur.out_sqlda); + if (cur.out_sqlda->sqld > cur.out_sqlda->sqln) { + ISC_SHORT n = cur.out_sqlda->sqld; + free_xsqlda(cur.out_sqlda); + cur.out_sqlda = malloc_xsqlda(n); + isc_dsql_describe(cur.env->status_vector, &cur.stmt->handle, 1, cur.out_sqlda); + if ( CHECK_DB_ERROR(cur.env->status_vector) ) { + free_cur(&cur); + return return_db_error(L, cur.env->status_vector); + } + } + malloc_sqlda_vars(cur.out_sqlda); + + /* what do we return? a cursor or a count */ + if(cur.out_sqlda->sqld > 0) { /* a cursor */ + cur_data *user_cur = (cur_data *)lua_newuserdata(L, sizeof(cur_data)); + luasql_setmeta (L, LUASQL_CURSOR_FIREBIRD); + + /* copy the cursor into a new lua userdata object */ + memcpy((void *)user_cur, (void *)&cur, sizeof(cur_data)); + + /* add cursor to the lock count */ + luasql_registerobj(L, stmt_indx, user_cur->stmt); + ++user_cur->stmt->lock; + } else { /* a count */ + /* if autocommit is set, commit change */ + if(cur.stmt->conn->autocommit) { + isc_commit_retaining(cur.env->status_vector, &cur.stmt->conn->transaction); + if ( CHECK_DB_ERROR(cur.env->status_vector) ) { + free_cur(&cur); + return return_db_error(L, cur.env->status_vector); + } + } + + if( (count = count_rows_affected(cur.env, &cur)) < 0 ) { + free_cur(&cur); + return return_db_error(L, cur.env->status_vector); + } + + luasql_pushinteger(L, count); + + /* totaly finished with the cursor */ + isc_dsql_free_statement(cur.env->status_vector, &cur.stmt->handle, DSQL_close); + free_cur(&cur); + } + + return 1; +} + +/* +** Executes a SQL statement. +** Lua input: +** SQL statement +** [parameter table] +** Returns +** cursor object: if there are results or +** row count: number of rows affected by statement if no results +** nil and error message otherwise. +*/ +static int conn_execute (lua_State *L) +{ + int ret; + stmt_data *stmt; + + /* prepare the statement */ + if( (ret = conn_prepare(L)) != 1) { + return ret; + } + + /* execute and check result */ + if((ret = raw_execute(L, -1)) != 1) { + return ret; + } + + /* for neatness, remove stmt from stack */ + stmt = getstatement(L, -(ret+1)); + lua_remove(L, -(ret+1)); + + /* this will be an internal, hidden statment */ + stmt->hidden = 1; + + /* if statement doesn't return a cursor, close it */ + if(stmt->type != isc_info_sql_stmt_select) { + if((ret = stmt_shut(L, stmt)) != 0) { + return ret; + } + } + + return 1; +} + +/* +** Commits the current transaction +*/ +static int conn_commit(lua_State *L) +{ + conn_data *conn = getconnection(L,1); + + isc_commit_retaining(conn->env->status_vector, &conn->transaction); + if ( CHECK_DB_ERROR(conn->env->status_vector) ) { + return return_db_error(L, conn->env->status_vector); + } + + lua_pushboolean(L, 1); + return 1; +} + +/* +** Rolls back the current transaction +** Lua Returns: +** 1 if rollback is sucsessful +** nil and error message otherwise. +*/ +static int conn_rollback(lua_State *L) +{ + conn_data *conn = getconnection(L,1); + + isc_rollback_retaining(conn->env->status_vector, &conn->transaction); + if ( CHECK_DB_ERROR(conn->env->status_vector) ) { + return return_db_error(L, conn->env->status_vector); + } + + lua_pushboolean(L, 1); + return 1; +} + +/* +** Sets the autocommit state of the connection +** Lua Returns: +** autocommit state (0:off, 1:on) +** nil and error message on error. +*/ +static int conn_setautocommit(lua_State *L) +{ + conn_data *conn = getconnection(L,1); + + if(lua_toboolean(L, 2)) { + conn->autocommit = 1; + } else { + conn->autocommit = 0; + } + + lua_pushboolean(L, 1); + return 1; +} + +/* +** Closes a connection. +** Lua Returns: +** 1 if close was sucsessful, 0 if already closed +** nil and error message otherwise. +*/ +static int conn_close (lua_State *L) +{ + conn_data *conn = (conn_data *)luaL_checkudata(L,1,LUASQL_CONNECTION_FIREBIRD); + luaL_argcheck (L, conn != NULL, 1, "connection expected"); + + /* already closed */ + if(conn->closed != 0) { + lua_pushboolean(L, 0); + return 1; + } + + /* are all related statements closed? */ + if(conn->lock > 0) { + return luasql_faildirect(L, "there are still open statements/cursors"); + } + + if(conn->autocommit != 0) { + isc_commit_transaction(conn->env->status_vector, &conn->transaction); + } else { + isc_rollback_transaction(conn->env->status_vector, &conn->transaction); + } + if ( CHECK_DB_ERROR(conn->env->status_vector) ) { + return return_db_error(L, conn->env->status_vector); + } + + isc_detach_database(conn->env->status_vector, &conn->db); + if ( CHECK_DB_ERROR(conn->env->status_vector) ) { + return return_db_error(L, conn->env->status_vector); + } + + conn->closed = 1; + --conn->env->lock; + + /* check environment can be GC'd */ + if(conn->env->lock == 0) { + luasql_unregisterobj(L, conn->env); + } + + lua_pushboolean(L, 1); + return 1; +} + +/* +** GCs an connection object +*/ +static int conn_gc (lua_State *L) +{ + conn_data *conn = (conn_data *)luaL_checkudata(L,1,LUASQL_CONNECTION_FIREBIRD); + + if(conn->closed == 0) { + if(conn->autocommit != 0) { + isc_commit_transaction(conn->env->status_vector, &conn->transaction); + } else { + isc_rollback_transaction(conn->env->status_vector, &conn->transaction); + } + + isc_detach_database(conn->env->status_vector, &conn->db); + + conn->closed = 1; + --conn->env->lock; + + /* check environment can be GC'd */ + if(conn->env->lock == 0) { + luasql_unregisterobj(L, conn->env); + } + } + + return 0; +} + +/* +** Escapes a given string so that it can't break out of it's delimiting quotes +*/ +static int conn_escape(lua_State *L) +{ + size_t len; + const char *from = luaL_checklstring (L, 2, &len); + char *res = malloc(len*sizeof(char)*2+1); + char *to = res; + + if(res) { + while(*from != '\0') { + *(to++) = *from; + if(*from == '\'') { + *(to++) = *from; + } + + from++; + } + *to = '\0'; + + lua_pushstring(L, res); + free(res); + return 1; + } + + luaL_error(L, "could not allocate escaped string"); + return 0; +} + +/* +** Pushes the indexed value onto the lua stack +*/ +static void push_column(lua_State *L, int i, cur_data *cur) +{ + int varcharlen; + struct tm timevar; + char timestr[256]; + ISC_STATUS blob_stat; + isc_blob_handle blob_handle = 0; + ISC_QUAD blob_id; + luaL_Buffer b; + char *buffer; + unsigned short actual_seg_len; + + if( (cur->out_sqlda->sqlvar[i].sqlind != NULL) && + (*(cur->out_sqlda->sqlvar[i].sqlind) != 0) ) { + /* a null field? */ + lua_pushnil(L); + } else { + switch(cur->out_sqlda->sqlvar[i].sqltype & ~1) { + case SQL_VARYING: + varcharlen = (int)isc_vax_integer(cur->out_sqlda->sqlvar[i].sqldata, 2); + lua_pushlstring(L, cur->out_sqlda->sqlvar[i].sqldata+2, varcharlen); + break; + case SQL_TEXT: + lua_pushlstring(L, cur->out_sqlda->sqlvar[i].sqldata, + cur->out_sqlda->sqlvar[i].sqllen); + break; + case SQL_SHORT: + luasql_pushinteger(L, *(short *)(cur->out_sqlda->sqlvar[i].sqldata)); + break; + case SQL_LONG: + luasql_pushinteger(L, *(long *)(cur->out_sqlda->sqlvar[i].sqldata)); + break; + case SQL_INT64: + luasql_pushinteger(L, *(ISC_INT64 *)(cur->out_sqlda->sqlvar[i].sqldata)); + break; + case SQL_FLOAT: + lua_pushnumber(L, *(float *)(cur->out_sqlda->sqlvar[i].sqldata)); + break; + case SQL_DOUBLE: + lua_pushnumber(L, *(double *)(cur->out_sqlda->sqlvar[i].sqldata)); + break; + case SQL_TYPE_TIME: + isc_decode_sql_time((ISC_TIME *)(cur->out_sqlda->sqlvar[i].sqldata), + &timevar); + strftime(timestr, 255, "%X", &timevar); + lua_pushstring(L, timestr); + break; + case SQL_TYPE_DATE: + isc_decode_sql_date((ISC_DATE *)(cur->out_sqlda->sqlvar[i].sqldata), + &timevar); + strftime(timestr, 255, "%Y-%m-%d", &timevar); + lua_pushstring(L, timestr); + break; + case SQL_TIMESTAMP: + isc_decode_timestamp((ISC_TIMESTAMP *)(cur->out_sqlda->sqlvar[i].sqldata), + &timevar); + strftime(timestr, 255, "%Y-%m-%d %X", &timevar); + lua_pushstring(L, timestr); + break; + case SQL_BLOB: + /* get the BLOB ID and open it */ + memcpy(&blob_id, cur->out_sqlda->sqlvar[i].sqldata, sizeof(ISC_QUAD)); + isc_open_blob2(cur->env->status_vector, &cur->stmt->conn->db, + &cur->stmt->conn->transaction, + &blob_handle, &blob_id, 0, NULL ); + /* fetch the blob data */ + luaL_buffinit(L, &b); + buffer = luaL_prepbuffer(&b); + + blob_stat = isc_get_segment(cur->env->status_vector, + &blob_handle, &actual_seg_len, + LUAL_BUFFERSIZE, buffer ); + while(blob_stat == 0 || cur->env->status_vector[1] == isc_segment) { + luaL_addsize(&b, actual_seg_len); + buffer = luaL_prepbuffer(&b); + blob_stat = isc_get_segment(cur->env->status_vector, + &blob_handle, &actual_seg_len, + LUAL_BUFFERSIZE, buffer ); + } + + /* finnished, close the BLOB */ + isc_close_blob(cur->env->status_vector, &blob_handle); + blob_handle = 0; + + luaL_pushresult(&b); + break; + default: + lua_pushstring(L, ""); + break; + } + } +} + +/* +** Returns a map of parameter IDs to their types +*/ +static int stmt_get_params (lua_State *L) +{ + stmt_data *stmt = getstatement(L,1); + + return dump_xsqlda_types(L, stmt->in_sqlda); +} + +/* +** Executes the statement +** Lua input: +** [table of values] +** Returns +** cursor object: if there are results or +** row count: number of rows affected by statement if no results +** nil and error message otherwise. +*/ +static int stmt_execute (lua_State *L) +{ + stmt_data *stmt = getstatement(L,1); + + /* is there a parameter table to use */ + if(lua_istable(L, 2)) { + parse_params(L, stmt, 2); + } + + return raw_execute(L, 1); +} + +/* +** Closes a statement object +** Lua Returns: +** true if close was sucsessful, false if already closed +** nil and error message otherwise. +*/ +static int stmt_close (lua_State *L) +{ + stmt_data *stmt = (stmt_data *)luaL_checkudata(L,1,LUASQL_STATEMENT_FIREBIRD); + luaL_argcheck (L, stmt != NULL, 1, "statement expected"); + + if(stmt->lock > 0) { + return luasql_faildirect(L, "there are still open cursors"); + } + + if(stmt->closed == 0) { + int res = stmt_shut(L, stmt); + if(res != 0) { + return res; + } + + /* return sucsess */ + lua_pushboolean(L, 1); + return 1; + } + + lua_pushboolean(L, 0); + return 1; +} + +/* +** Frees up memory alloc'd to a statement +*/ +static int stmt_gc (lua_State *L) +{ + stmt_data *stmt = (stmt_data *)luaL_checkudata(L,1,LUASQL_STATEMENT_FIREBIRD); + luaL_argcheck (L, stmt != NULL, 1, "statement expected"); + + if(stmt->closed == 0) { + if(stmt_shut(L, stmt) != 0) { + return 1; + } + } + + return 0; +} + +/* +** Returns a row of data from the query +** Lua Returns: +** list of results or table of results depending on call +** nil and error message otherwise. +*/ +static int cur_fetch (lua_State *L) +{ + ISC_STATUS fetch_stat; + int i; + cur_data *cur = getcursor(L,1); + const char *opts = luaL_optstring (L, 3, "n"); + int num = strchr(opts, 'n') != NULL; + int alpha = strchr(opts, 'a') != NULL; + + if ((fetch_stat = isc_dsql_fetch(cur->env->status_vector, &cur->stmt->handle, 1, + cur->out_sqlda)) == 0) { + if (lua_istable (L, 2)) { + /* remove the option string */ + lua_settop(L, 2); + + /* loop through the columns */ + for (i = 0; i < cur->out_sqlda->sqld; i++) { + push_column(L, i, cur); + + if( num ) { + lua_pushnumber(L, i+1); + lua_pushvalue(L, -2); + lua_settable(L, 2); + } + + if( alpha ) { + lua_pushlstring(L, cur->out_sqlda->sqlvar[i].aliasname, + cur->out_sqlda->sqlvar[i].aliasname_length); + lua_pushvalue(L, -2); + lua_settable(L, 2); + } + + lua_pop(L, 1); + } + + /* returning given table */ + return 1; + } else { + for (i = 0; i < cur->out_sqlda->sqld; i++) { + push_column(L, i, cur); + } + + /* returning a list of values */ + return cur->out_sqlda->sqld; + } + } + + /* isc_dsql_fetch returns 100 if no more rows remain to be retrieved + so this can be ignored */ + if (fetch_stat != 100L) { + return return_db_error(L, cur->env->status_vector); + } + + /* shut cursor */ + return cur_shut(L, cur); +} + +/* +** Returns a table of column names from the query +** Lua Returns: +** a table of column names +** nil and error message otherwise. +*/ +static int cur_colnames (lua_State *L) +{ + int i; + XSQLVAR *var; + cur_data *cur = getcursor(L,1); + + lua_newtable(L); + + for (i=1, var = cur->out_sqlda->sqlvar; i <= cur->out_sqlda->sqld; i++, var++) { + lua_pushnumber(L, i); + lua_pushlstring(L, var->aliasname, var->aliasname_length); + lua_settable(L, -3); + } + + return 1; +} + +/* +** Returns a table of column types from the query +** Lua Returns: +** a table of column types +** nil and error message otherwise. +*/ +static int cur_coltypes (lua_State *L) +{ + cur_data *cur = getcursor(L,1); + + return dump_xsqlda_types(L, cur->out_sqlda); +} + +/* +** Closes a cursor object +** Lua Returns: +** true if close was sucsessful, false if already closed +** nil and error message otherwise. +*/ +static int cur_close (lua_State *L) +{ + cur_data *cur = (cur_data *)luaL_checkudata(L,1,LUASQL_CURSOR_FIREBIRD); + luaL_argcheck (L, cur != NULL, 1, "cursor expected"); + + if(cur->closed == 0) { + int res = cur_shut(L, cur); + if(res != 0) { + return res; + } + + /* return sucsess */ + lua_pushboolean(L, 1); + return 1; + } + + lua_pushboolean(L, 0); + return 1; +} + +/* +** GCs a cursor object +*/ +static int cur_gc (lua_State *L) +{ + cur_data *cur = (cur_data *)luaL_checkudata(L,1,LUASQL_CURSOR_FIREBIRD); + luaL_argcheck (L, cur != NULL, 1, "cursor expected"); + + if(cur->closed == 0) { + if(cur_shut(L, cur) != 0) { + return 1; + } + } + + return 0; +} + +/* +** Creates an Environment and returns it. +*/ +static int create_environment (lua_State *L) +{ + int i; + env_data *env; + + env = (env_data *)lua_newuserdata (L, sizeof (env_data)); + luasql_setmeta (L, LUASQL_ENVIRONMENT_FIREBIRD); + /* fill in structure */ + for(i=0; i<20; i++) { + env->status_vector[i] = 0; + } + env->closed = 0; + env->lock = 0; + + return 1; +} + +/* +** Reforms old connection style to new one +** Lua Input: source, [user, [pass]] +** source: data source +** user, pass: data source authentication information +** Lua Returns: +** new connection details table +*/ +static void env_connect_fix_old (lua_State *L) +{ + static const char *const opt_names[] = { + "source", + "user", + "password", + NULL + }; + int i, t = lua_gettop(L)-1; + + lua_newtable(L); + lua_insert(L, 2); + + for(i=0; opt_names[i] != NULL && i, +** user = , +** password = , +** [charset = ,] +** [dialect = ,] +** } +** Lua Returns: +** connection object if successfull +** nil and error message otherwise. +*/ +static int env_connect (lua_State *L) +{ + char *dpb, *dpb_end; + static char isc_tpb[] = { + isc_tpb_version3, + isc_tpb_write + }; + + conn_data conn; + conn_data *res_conn; + const char *sourcename; + + env_data *env = (env_data *) getenvironment (L, 1); + + if(lua_gettop(L) < 2) { + return luasql_faildirect(L, "No connection details provided"); + } + + if(!lua_istable(L, 2)) { + env_connect_fix_old(L); + } + + sourcename = luasql_table_optstring(L, 2, "source", NULL); + + if(sourcename == NULL) { + return luasql_faildirect(L, "connection details table missing 'source'"); + } + + conn.env = env; + conn.db = 0L; + conn.transaction = 0L; + conn.lock = 0; + conn.autocommit = 0; + + /* construct a database parameter buffer. */ + dpb = conn.dpb_buffer; + dpb_end = conn.dpb_buffer + sizeof(conn.dpb_buffer); + *dpb++ = isc_dpb_version1; + *dpb++ = isc_dpb_num_buffers; + *dpb++ = 1; + *dpb++ = 90; + + /* add the user name and password */ + dpb = add_dpb_string(dpb, dpb_end, isc_dpb_user_name, + luasql_table_optstring(L, 2, "user", "")); + dpb = add_dpb_string(dpb, dpb_end, isc_dpb_password, + luasql_table_optstring(L, 2, "password", "")); + + /* other database parameters */ + dpb = add_dpb_string(dpb, dpb_end, isc_dpb_lc_ctype, + luasql_table_optstring(L, 2, "charset", "UTF8")); + conn.dialect = (unsigned short)luasql_table_optnumber(L, 2, "dialect", 3); + + /* the length of the dpb */ + conn.dpb_length = (short)(dpb - conn.dpb_buffer); + + /* do the job */ + isc_attach_database(env->status_vector, (short)strlen(sourcename), + (char *)sourcename, &conn.db, + conn.dpb_length, conn.dpb_buffer); + + /* an error? */ + if ( CHECK_DB_ERROR(conn.env->status_vector) ) { + return return_db_error(L, conn.env->status_vector); + } + + /* open up the transaction handle */ + isc_start_transaction(env->status_vector, &conn.transaction, 1, + &conn.db, (unsigned short)sizeof(isc_tpb), + isc_tpb ); + + /* an error? */ + if ( CHECK_DB_ERROR(conn.env->status_vector) ) { + return return_db_error(L, conn.env->status_vector); + } + + /* create the lua object and add the connection to the lock */ + res_conn = (conn_data *)lua_newuserdata(L, sizeof(conn_data)); + luasql_setmeta (L, LUASQL_CONNECTION_FIREBIRD); + memcpy(res_conn, &conn, sizeof(conn_data)); + res_conn->closed = 0; /* connect now officially open */ + + /* register the connection */ + luasql_registerobj(L, 1, env); + ++env->lock; + + return 1; +} + +/* +** Closes an environment object +** Lua Returns: +** 1 if close was sucsessful, 0 if already closed +** nil and error message otherwise. +*/ +static int env_close (lua_State *L) +{ + env_data *env = (env_data *)luaL_checkudata (L, 1, LUASQL_ENVIRONMENT_FIREBIRD); + luaL_argcheck (L, env != NULL, 1, "environment expected"); + + /* already closed? */ + if(env->closed == 1) { + lua_pushboolean(L, 0); + return 1; + } + + /* check the lock */ + if(env->lock > 0) { + return luasql_faildirect(L, "there are still open connections"); + } + + /* unregister */ + luasql_unregisterobj(L, env); + + /* mark as closed */ + env->closed = 1; + + lua_pushboolean(L, 1); + return 1; +} + +/* +** GCs an environment object +*/ +static int env_gc (lua_State *L) +{ + /* nothing to be done for the FB envronment */ + return 0; +} + +/* +** Create metatables for each class of object. +*/ +static void create_metatables (lua_State *L) +{ + struct luaL_Reg environment_methods[] = { + {"__gc", env_gc}, + {"close", env_close}, + {"connect", env_connect}, + {NULL, NULL}, + }; + struct luaL_Reg connection_methods[] = { + {"__gc", conn_gc}, + {"close", conn_close}, + {"prepare", conn_prepare}, + {"execute", conn_execute}, + {"commit", conn_commit}, + {"rollback", conn_rollback}, + {"setautocommit", conn_setautocommit}, + {"escape", conn_escape}, + {NULL, NULL}, + }; + struct luaL_Reg statement_methods[] = { + {"__gc", stmt_gc}, + {"close", stmt_close}, + {"getparamtypes", stmt_get_params}, + {"execute", stmt_execute}, + {NULL, NULL}, + }; + struct luaL_Reg cursor_methods[] = { + {"__gc", cur_gc}, + {"close", cur_close}, + {"fetch", cur_fetch}, + {"getcoltypes", cur_coltypes}, + {"getcolnames", cur_colnames}, + {NULL, NULL}, + }; + luasql_createmeta (L, LUASQL_ENVIRONMENT_FIREBIRD, environment_methods); + luasql_createmeta (L, LUASQL_CONNECTION_FIREBIRD, connection_methods); + luasql_createmeta (L, LUASQL_STATEMENT_FIREBIRD, statement_methods); + luasql_createmeta (L, LUASQL_CURSOR_FIREBIRD, cursor_methods); + lua_pop (L, 4); +} + +/* +** Creates the metatables for the objects and registers the +** driver open method. +*/ +LUASQL_API int luaopen_luasql_firebird (lua_State *L) +{ + struct luaL_Reg driver[] = { + {"firebird", create_environment}, + {NULL, NULL}, + }; + create_metatables (L); + luasql_find_driver_table (L); + luaL_setfuncs (L, driver, 0); + luasql_set_info (L); + return 1; +} From be804c69fbeb1a20e01b15d0c6693d4e2419b447 Mon Sep 17 00:00:00 2001 From: Scott Morgan Date: Sat, 25 Apr 2015 02:11:52 +0100 Subject: [PATCH 47/64] Ran file through beautifier Using the following command: astyle --style=1tbs --indent=tab=5 --align-pointer=name --align-reference=name --max-code-length=80 --lineend=linux ls_firebird.c Then tidied up a bit --- src/ls_odbc.c | 403 +++++++++++++++++++++++++++++--------------------- 1 file changed, 231 insertions(+), 172 deletions(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index bcc16da..55a151e 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -112,7 +112,8 @@ static int unlock_obj(lua_State *L, void *ptr) /* ** Check for valid environment. */ -static env_data *getenvironment (lua_State *L, int i) { +static env_data *getenvironment (lua_State *L, int i) +{ env_data *env = (env_data *)luaL_checkudata (L, i, LUASQL_ENVIRONMENT_ODBC); luaL_argcheck (L, env != NULL, i, LUASQL_PREFIX"environment expected"); luaL_argcheck (L, !env->closed, i, LUASQL_PREFIX"environment is closed"); @@ -122,7 +123,8 @@ static env_data *getenvironment (lua_State *L, int i) { /* ** Check for valid connection. */ -static conn_data *getconnection (lua_State *L, int i) { +static conn_data *getconnection (lua_State *L, int i) +{ conn_data *conn = (conn_data *)luaL_checkudata (L, i, LUASQL_CONNECTION_ODBC); luaL_argcheck (L, conn != NULL, i, LUASQL_PREFIX"connection expected"); luaL_argcheck (L, !conn->closed, i, LUASQL_PREFIX"connection is closed"); @@ -132,7 +134,8 @@ static conn_data *getconnection (lua_State *L, int i) { /* ** Check for valid connection. */ -static stmt_data *getstatement (lua_State *L, int i) { +static stmt_data *getstatement (lua_State *L, int i) +{ stmt_data *stmt = (stmt_data *)luaL_checkudata (L, i, LUASQL_STATEMENT_ODBC); luaL_argcheck (L, stmt != NULL, i, LUASQL_PREFIX"statement expected"); luaL_argcheck (L, !stmt->closed, i, LUASQL_PREFIX"statement is closed"); @@ -142,7 +145,8 @@ static stmt_data *getstatement (lua_State *L, int i) { /* ** Check for valid cursor. */ -static cur_data *getcursor (lua_State *L, int i) { +static cur_data *getcursor (lua_State *L, int i) +{ cur_data *cursor = (cur_data *)luaL_checkudata (L, i, LUASQL_CURSOR_ODBC); luaL_argcheck (L, cursor != NULL, i, LUASQL_PREFIX"cursor expected"); luaL_argcheck (L, !cursor->closed, i, LUASQL_PREFIX"cursor is closed"); @@ -153,18 +157,20 @@ static cur_data *getcursor (lua_State *L, int i) { /* ** Pushes true and returns 1 */ -static int pass(lua_State *L) { +static int pass(lua_State *L) +{ lua_pushboolean (L, 1); return 1; } /* ** Fails with error message from ODBC -** Inputs: +** Inputs: ** type: type of handle used in operation ** handle: handle used in operation */ -static int fail(lua_State *L, const SQLSMALLINT type, const SQLHANDLE handle) { +static int fail(lua_State *L, const SQLSMALLINT type, const SQLHANDLE handle) +{ SQLCHAR State[6]; SQLINTEGER NativeError; SQLSMALLINT MsgSize, i; @@ -176,14 +182,15 @@ static int fail(lua_State *L, const SQLSMALLINT type, const SQLHANDLE handle) { luaL_buffinit(L, &b); i = 1; while (1) { - ret = SQLGetDiagRec(type, handle, i, State, &NativeError, Msg, sizeof(Msg), &MsgSize); + ret = SQLGetDiagRec(type, handle, i, State, &NativeError, Msg, sizeof(Msg), + &MsgSize); if (ret == SQL_NO_DATA) { break; } - luaL_addlstring(&b, (char*)Msg, MsgSize); + luaL_addlstring(&b, (char *)Msg, MsgSize); luaL_addchar(&b, '\n'); i++; - } + } luaL_pushresult(&b); return 2; } @@ -263,23 +270,39 @@ static int cur_shut(lua_State *L, cur_data *cur) /* ** Returns the name of an equivalent lua type for a SQL type. */ -static const char *sqltypetolua (const SQLSMALLINT type) { +static const char *sqltypetolua (const SQLSMALLINT type) +{ switch (type) { - case SQL_UNKNOWN_TYPE: case SQL_CHAR: case SQL_VARCHAR: - case SQL_TYPE_DATE: case SQL_TYPE_TIME: case SQL_TYPE_TIMESTAMP: - case SQL_DATE: case SQL_INTERVAL: case SQL_TIMESTAMP: + case SQL_UNKNOWN_TYPE: + case SQL_CHAR: + case SQL_VARCHAR: + case SQL_TYPE_DATE: + case SQL_TYPE_TIME: + case SQL_TYPE_TIMESTAMP: + case SQL_DATE: + case SQL_INTERVAL: + case SQL_TIMESTAMP: case SQL_LONGVARCHAR: - case SQL_WCHAR: case SQL_WVARCHAR: case SQL_WLONGVARCHAR: + case SQL_WCHAR: + case SQL_WVARCHAR: + case SQL_WLONGVARCHAR: return "string"; - case SQL_BIGINT: case SQL_TINYINT: - case SQL_INTEGER: case SQL_SMALLINT: + case SQL_BIGINT: + case SQL_TINYINT: + case SQL_INTEGER: + case SQL_SMALLINT: #if LUA_VERSION_NUM>=503 return "integer"; #endif - case SQL_NUMERIC: case SQL_DECIMAL: - case SQL_FLOAT: case SQL_REAL: case SQL_DOUBLE: + case SQL_NUMERIC: + case SQL_DECIMAL: + case SQL_FLOAT: + case SQL_REAL: + case SQL_DOUBLE: return "number"; - case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: + case SQL_BINARY: + case SQL_VARBINARY: + case SQL_LONGVARBINARY: return "binary"; /* !!!!!! nao seria string? */ case SQL_BIT: return "boolean"; @@ -299,128 +322,130 @@ static const char *sqltypetolua (const SQLSMALLINT type) { ** Returns: ** 0 if successfull, non-zero otherwise; */ -static int push_column(lua_State *L, int coltypes, const SQLHSTMT hstmt, - SQLUSMALLINT i) { - const char *tname; - char type; - /* get column type from types table */ - lua_rawgeti (L, LUA_REGISTRYINDEX, coltypes); - lua_rawgeti (L, -1, i); /* typename of the column */ - tname = lua_tostring(L, -1); - if (!tname) { - return luasql_faildirect(L, "invalid type in table."); +static int push_column(lua_State *L, int coltypes, const SQLHSTMT hstmt, + SQLUSMALLINT i) +{ + const char *tname; + char type; + /* get column type from types table */ + lua_rawgeti (L, LUA_REGISTRYINDEX, coltypes); + lua_rawgeti (L, -1, i); /* typename of the column */ + tname = lua_tostring(L, -1); + if (!tname) { + return luasql_faildirect(L, "invalid type in table."); + } + type = tname[1]; + lua_pop(L, 2); /* pops type name and coltypes table */ + + /* deal with data according to type */ + switch (type) { + /* nUmber */ + case 'u': { + double num; + SQLLEN got; + SQLRETURN rc = SQLGetData(hstmt, i, SQL_C_DOUBLE, &num, 0, &got); + if (error(rc)) { + return fail(L, hSTMT, hstmt); } - type = tname[1]; - lua_pop(L, 2); /* pops type name and coltypes table */ - - /* deal with data according to type */ - switch (type) { - /* nUmber */ - case 'u': { - double num; - SQLLEN got; - SQLRETURN rc = SQLGetData(hstmt, i, SQL_C_DOUBLE, &num, 0, &got); - if (error(rc)) { - return fail(L, hSTMT, hstmt); - } - if (got == SQL_NULL_DATA) { - lua_pushnil(L); - } else { - lua_pushnumber(L, num); - } - return 0; + if (got == SQL_NULL_DATA) { + lua_pushnil(L); + } else { + lua_pushnumber(L, num); } + return 0; + } #if LUA_VERSION_NUM>=503 - /* iNteger */ - case 'n': { - long int num; - SQLLEN got; - SQLRETURN rc = SQLGetData(hstmt, i, SQL_C_SLONG, &num, 0, &got); - if (error(rc)) { - return fail(L, hSTMT, hstmt); - } - if (got == SQL_NULL_DATA) { - lua_pushnil(L); - } else { - lua_pushinteger(L, num); - } - return 0; + /* iNteger */ + case 'n': { + long int num; + SQLLEN got; + SQLRETURN rc = SQLGetData(hstmt, i, SQL_C_SLONG, &num, 0, &got); + if (error(rc)) { + return fail(L, hSTMT, hstmt); } + if (got == SQL_NULL_DATA) { + lua_pushnil(L); + } else { + lua_pushinteger(L, num); + } + return 0; + } #endif - /* bOol */ - case 'o': { - char b; - SQLLEN got; - SQLRETURN rc = SQLGetData(hstmt, i, SQL_C_BIT, &b, 0, &got); - if (error(rc)) { - return fail(L, hSTMT, hstmt); - } - if (got == SQL_NULL_DATA) { - lua_pushnil(L); - } else { - lua_pushboolean(L, b); - } + /* bOol */ + case 'o': { + char b; + SQLLEN got; + SQLRETURN rc = SQLGetData(hstmt, i, SQL_C_BIT, &b, 0, &got); + if (error(rc)) { + return fail(L, hSTMT, hstmt); + } + if (got == SQL_NULL_DATA) { + lua_pushnil(L); + } else { + lua_pushboolean(L, b); + } + return 0; + } + /* sTring */ + case 't': + /* bInary */ + case 'i': { + SQLSMALLINT stype = (type == 't') ? SQL_C_CHAR : SQL_C_BINARY; + SQLLEN got; + char *buffer; + luaL_Buffer b; + SQLRETURN rc; + luaL_buffinit(L, &b); + buffer = luaL_prepbuffer(&b); + rc = SQLGetData(hstmt, i, stype, buffer, LUAL_BUFFERSIZE, &got); + if (got == SQL_NULL_DATA) { + lua_pushnil(L); return 0; } - /* sTring */ - case 't': - /* bInary */ - case 'i': { - SQLSMALLINT stype = (type == 't') ? SQL_C_CHAR : SQL_C_BINARY; - SQLLEN got; - char *buffer; - luaL_Buffer b; - SQLRETURN rc; - luaL_buffinit(L, &b); - buffer = luaL_prepbuffer(&b); - rc = SQLGetData(hstmt, i, stype, buffer, LUAL_BUFFERSIZE, &got); - if (got == SQL_NULL_DATA) { - lua_pushnil(L); - return 0; - } - /* concat intermediary chunks */ - while (rc == SQL_SUCCESS_WITH_INFO) { - if (got >= LUAL_BUFFERSIZE || got == SQL_NO_TOTAL) { - got = LUAL_BUFFERSIZE; - /* get rid of null termination in string block */ - if (stype == SQL_C_CHAR) { - got--; - } + /* concat intermediary chunks */ + while (rc == SQL_SUCCESS_WITH_INFO) { + if (got >= LUAL_BUFFERSIZE || got == SQL_NO_TOTAL) { + got = LUAL_BUFFERSIZE; + /* get rid of null termination in string block */ + if (stype == SQL_C_CHAR) { + got--; } - luaL_addsize(&b, got); - buffer = luaL_prepbuffer(&b); - rc = SQLGetData(hstmt, i, stype, buffer, LUAL_BUFFERSIZE, &got); } - /* concat last chunk */ - if (rc == SQL_SUCCESS) { - if (got >= LUAL_BUFFERSIZE || got == SQL_NO_TOTAL) { - got = LUAL_BUFFERSIZE; - /* get rid of null termination in string block */ - if (stype == SQL_C_CHAR) { - got--; - } + luaL_addsize(&b, got); + buffer = luaL_prepbuffer(&b); + rc = SQLGetData(hstmt, i, stype, buffer, LUAL_BUFFERSIZE, &got); + } + /* concat last chunk */ + if (rc == SQL_SUCCESS) { + if (got >= LUAL_BUFFERSIZE || got == SQL_NO_TOTAL) { + got = LUAL_BUFFERSIZE; + /* get rid of null termination in string block */ + if (stype == SQL_C_CHAR) { + got--; } - luaL_addsize(&b, got); - } - if (rc == SQL_ERROR) { - return fail(L, hSTMT, hstmt); } - /* return everything we got */ - luaL_pushresult(&b); - return 0; + luaL_addsize(&b, got); } + if (rc == SQL_ERROR) { + return fail(L, hSTMT, hstmt); } + /* return everything we got */ + luaL_pushresult(&b); return 0; + } + } + return 0; } /* ** Get another row of the given cursor. */ -static int cur_fetch (lua_State *L) { +static int cur_fetch (lua_State *L) +{ cur_data *cur = getcursor (L, 1); SQLHSTMT hstmt = cur->stmt->hstmt; - int ret; - SQLRETURN rc = SQLFetch(hstmt); + int ret; + SQLRETURN rc = SQLFetch(hstmt); if (rc == SQL_NO_DATA) { /* automatically close cursor when end of resultset is reached */ if((ret = cur_shut(L, cur)) != 0) { @@ -476,7 +501,8 @@ static int cur_fetch (lua_State *L) { /* ** Closes a cursor. */ -static int cur_close (lua_State *L) { +static int cur_close (lua_State *L) +{ int res; cur_data *cur = (cur_data *) luaL_checkudata (L, 1, LUASQL_CURSOR_ODBC); luaL_argcheck (L, cur != NULL, 1, LUASQL_PREFIX"cursor expected"); @@ -496,7 +522,8 @@ static int cur_close (lua_State *L) { /* ** Returns the table with column names. */ -static int cur_colnames (lua_State *L) { +static int cur_colnames (lua_State *L) +{ cur_data *cur = getcursor (L, 1); lua_rawgeti (L, LUA_REGISTRYINDEX, cur->colnames); return 1; @@ -506,7 +533,8 @@ static int cur_colnames (lua_State *L) { /* ** Returns the table with column types. */ -static int cur_coltypes (lua_State *L) { +static int cur_coltypes (lua_State *L) +{ cur_data *cur = getcursor (L, 1); lua_rawgeti (L, LUA_REGISTRYINDEX, cur->coltypes); return 1; @@ -516,7 +544,8 @@ static int cur_coltypes (lua_State *L) { /* ** Creates two tables with the names and the types of the columns. */ -static int create_colinfo (lua_State *L, cur_data *cur) { +static int create_colinfo (lua_State *L, cur_data *cur) +{ SQLCHAR buffer[256]; SQLSMALLINT namelen, datatype, i; SQLRETURN ret; @@ -527,14 +556,14 @@ static int create_colinfo (lua_State *L, cur_data *cur) { lua_newtable(L); names = lua_gettop (L); for (i = 1; i <= cur->numcols; i++) { - ret = SQLDescribeCol(cur->stmt->hstmt, i, buffer, sizeof(buffer), - &namelen, &datatype, NULL, NULL, NULL); + ret = SQLDescribeCol(cur->stmt->hstmt, i, buffer, sizeof(buffer), + &namelen, &datatype, NULL, NULL, NULL); if (ret == SQL_ERROR) { lua_pop(L, 2); return -1; } - lua_pushstring (L, (char*)buffer); + lua_pushstring (L, (char *)buffer); lua_rawseti (L, names, i); lua_pushstring(L, sqltypetolua(datatype)); lua_rawseti (L, types, i); @@ -549,7 +578,8 @@ static int create_colinfo (lua_State *L, cur_data *cur) { /* ** Creates a cursor table and leave it on the top of the stack. */ -static int create_cursor (lua_State *L, int stmt_i, stmt_data *stmt, const SQLSMALLINT numcols) +static int create_cursor (lua_State *L, int stmt_i, stmt_data *stmt, + const SQLSMALLINT numcols) { cur_data *cur; @@ -577,7 +607,8 @@ static int create_cursor (lua_State *L, int stmt_i, stmt_data *stmt, const SQLSM /* ** Returns the table with statement params. */ -static int stmt_paramtypes (lua_State *L) { +static int stmt_paramtypes (lua_State *L) +{ stmt_data *stmt = getstatement(L, 1); lua_rawgeti (L, LUA_REGISTRYINDEX, stmt->paramtypes); return 1; @@ -587,7 +618,8 @@ static int stmt_close(lua_State *L) { stmt_data *stmt = (stmt_data *) luaL_checkudata (L, 1, LUASQL_STATEMENT_ODBC); luaL_argcheck (L, stmt != NULL, 1, LUASQL_PREFIX"statement expected"); - luaL_argcheck (L, stmt->lock == 0, 1, LUASQL_PREFIX"there are still open cursors"); + luaL_argcheck (L, stmt->lock == 0, 1, + LUASQL_PREFIX"there are still open cursors"); if (stmt->closed) { lua_pushboolean (L, 0); @@ -686,17 +718,20 @@ static int raw_readparams(lua_State *L, stmt_data *stmt, int iparams) data = stmt->params; for(i=1; i<=stmt->numparams; ++i, ++data) { - lua_pushnumber(L, i); /* not using lua_geti for backwards compat with Lua 5.1/LuaJIT */ + /* not using lua_geti for backwards compat with Lua 5.1/LuaJIT */ + lua_pushnumber(L, i); lua_gettable(L, iparams); switch(lua_type(L, -1)) { case LUA_TNIL: { lua_pop(L, 1); - if(error(SQLBindParameter(stmt->hstmt, i, SQL_PARAM_INPUT, SQL_C_DEFAULT, SQL_DOUBLE, 0, 0, NULL, 0, &cbNull))) { + if(error(SQLBindParameter(stmt->hstmt, i, SQL_PARAM_INPUT, SQL_C_DEFAULT, + SQL_DOUBLE, 0, 0, NULL, 0, &cbNull))) { return fail(L, hSTMT, stmt->hstmt); } - } break; + } + break; case LUA_TNUMBER: { data->buf = malloc(sizeof(double)); @@ -706,13 +741,16 @@ static int raw_readparams(lua_State *L, stmt_data *stmt, int iparams) lua_pop(L, 1); - if(error(SQLBindParameter(stmt->hstmt, i, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DOUBLE, 0, 0, data->buf, data->len, &data->type))) { + if(error(SQLBindParameter(stmt->hstmt, i, SQL_PARAM_INPUT, SQL_C_DOUBLE, + SQL_DOUBLE, 0, 0, data->buf, data->len, + &data->type))) { return fail(L, hSTMT, stmt->hstmt); } - } break; + } + break; case LUA_TSTRING: { - const char* str = lua_tostring(L, -1); + const char *str = lua_tostring(L, -1); size_t len = strlen(str); data->buf = malloc(len+1); @@ -722,10 +760,13 @@ static int raw_readparams(lua_State *L, stmt_data *stmt, int iparams) lua_pop(L, 1); - if(error(SQLBindParameter(stmt->hstmt, i, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, len, 0, data->buf, data->len, &data->type))) { + if(error(SQLBindParameter(stmt->hstmt, i, SQL_PARAM_INPUT, SQL_C_CHAR, + SQL_CHAR, len, 0, data->buf, data->len, + &data->type))) { return fail(L, hSTMT, stmt->hstmt); } - } break; + } + break; case LUA_TBOOLEAN: { SQLCHAR b = (SQLCHAR)lua_toboolean(L, -1); @@ -737,10 +778,13 @@ static int raw_readparams(lua_State *L, stmt_data *stmt, int iparams) lua_pop(L, 1); - if(error(SQLBindParameter(stmt->hstmt, i, SQL_PARAM_INPUT, SQL_C_BIT, SQL_BIT, 0, 0, data->buf, data->len, &data->type))) { + if(error(SQLBindParameter(stmt->hstmt, i, SQL_PARAM_INPUT, SQL_C_BIT, + SQL_BIT, 0, 0, data->buf, data->len, + &data->type))) { return fail(L, hSTMT, stmt->hstmt); } - } break; + } + break; default: lua_pop(L, 1); @@ -787,7 +831,8 @@ static int desc_params(lua_State *L, stmt_data *stmt) SQLSMALLINT type, digits, nullable; SQLULEN len; - /* fun fact: most ODBC drivers don't support this function (MS Access for example), so we can't get a param type table */ + /* fun fact: most ODBC drivers don't support this function (MS Access for + example), so we can't get a param type table */ if(error(SQLDescribeParam(stmt->hstmt, i, &type, &len, &digits, &nullable))) { lua_pop(L,1); return LUA_NOREF; @@ -810,7 +855,7 @@ static int desc_params(lua_State *L, stmt_data *stmt) static int conn_prepare(lua_State *L) { conn_data *conn = getconnection(L, 1); - SQLCHAR *statement = (SQLCHAR*)luaL_checkstring(L, 2); + SQLCHAR *statement = (SQLCHAR *)luaL_checkstring(L, 2); SQLHDBC hdbc = conn->hdbc; SQLHSTMT hstmt; SQLRETURN ret; @@ -831,7 +876,7 @@ static int conn_prepare(lua_State *L) stmt = (stmt_data *)lua_newuserdata(L, sizeof(stmt_data)); memset(stmt, 0, sizeof(stmt_data)); - + stmt->closed = 0; stmt->lock = 0; stmt->hidden = 0; @@ -901,9 +946,10 @@ static int conn_execute (lua_State *L) } /* -** Commits a transaction. +** Commits a transaction. */ -static int conn_commit (lua_State *L) { +static int conn_commit (lua_State *L) +{ conn_data *conn = getconnection (L, 1); SQLRETURN ret = SQLEndTran(hDBC, conn->hdbc, SQL_COMMIT); if (error(ret)) { @@ -914,9 +960,10 @@ static int conn_commit (lua_State *L) { } /* -** Rollback the current transaction. +** Rollback the current transaction. */ -static int conn_rollback (lua_State *L) { +static int conn_rollback (lua_State *L) +{ conn_data *conn = getconnection (L, 1); SQLRETURN ret = SQLEndTran(hDBC, conn->hdbc, SQL_ROLLBACK); if (error(ret)) { @@ -929,15 +976,16 @@ static int conn_rollback (lua_State *L) { /* ** Sets the auto commit mode */ -static int conn_setautocommit (lua_State *L) { +static int conn_setautocommit (lua_State *L) +{ conn_data *conn = getconnection (L, 1); SQLRETURN ret; if (lua_toboolean (L, 2)) { ret = SQLSetConnectAttr(conn->hdbc, SQL_ATTR_AUTOCOMMIT, - (SQLPOINTER) SQL_AUTOCOMMIT_ON, 0); + (SQLPOINTER) SQL_AUTOCOMMIT_ON, 0); } else { ret = SQLSetConnectAttr(conn->hdbc, SQL_ATTR_AUTOCOMMIT, - (SQLPOINTER) SQL_AUTOCOMMIT_OFF, 0); + (SQLPOINTER) SQL_AUTOCOMMIT_OFF, 0); } if (error(ret)) { return fail(L, hSTMT, conn->hdbc); @@ -949,12 +997,14 @@ static int conn_setautocommit (lua_State *L) { /* ** Create a new Connection object and push it on top of the stack. */ -static int create_connection (lua_State *L, int o, SQLHDBC hdbc) { +static int create_connection (lua_State *L, int o, SQLHDBC hdbc) +{ env_data *env = getenvironment(L, 1); conn_data *conn = (conn_data *)lua_newuserdata(L, sizeof(conn_data)); /* set auto commit mode */ - SQLRETURN ret = SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER) SQL_AUTOCOMMIT_ON, 0); + SQLRETURN ret = SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT, + (SQLPOINTER) SQL_AUTOCOMMIT_ON, 0); if (error(ret)) { return fail(L, hDBC, hdbc); } @@ -966,7 +1016,7 @@ static int create_connection (lua_State *L, int o, SQLHDBC hdbc) { conn->lock = 0; conn->env = env; conn->hdbc = hdbc; - + lock_obj(L, 1, env); return 1; @@ -981,16 +1031,17 @@ static int create_connection (lua_State *L, int o, SQLHDBC hdbc) { ** connection object if successfull ** nil and error message otherwise. */ -static int env_table_connect_DSN (lua_State *L) { +static int env_table_connect_DSN (lua_State *L) +{ env_data *env = getenvironment (L, 1); - SQLCHAR *sourcename = (SQLCHAR*)luasql_table_optstring(L, 2, "dsn", NULL); + SQLCHAR *sourcename = (SQLCHAR *)luasql_table_optstring(L, 2, "dsn", NULL); SQLHDBC hdbc; SQLCHAR sqlOutBuf[4097]; SQLSMALLINT sqlOutLen; SQLRETURN ret; - ret = SQLSetEnvAttr (env->henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); + ret = SQLSetEnvAttr (env->henv, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0); if (error(ret)) { return luasql_faildirect (L, "error setting SQL version."); } @@ -1002,7 +1053,8 @@ static int env_table_connect_DSN (lua_State *L) { } /* tries to connect handle */ - ret = SQLDriverConnect (hdbc, NULL, sourcename, SQL_NTS, sqlOutBuf, 4096, &sqlOutLen, SQL_DRIVER_NOPROMPT); + ret = SQLDriverConnect (hdbc, NULL, sourcename, SQL_NTS, sqlOutBuf, 4096, + &sqlOutLen, SQL_DRIVER_NOPROMPT); if (error(ret)) { ret = fail(L, hDBC, hdbc); SQLFreeHandle(hDBC, hdbc); @@ -1013,7 +1065,7 @@ static int env_table_connect_DSN (lua_State *L) { ret = create_connection (L, 1, hdbc); if(ret == 1) { /* Add the sqlOutBuf string to the results, for diagnostics */ - lua_pushlstring(L, (char*)sqlOutBuf, sqlOutLen); + lua_pushlstring(L, (char *)sqlOutBuf, sqlOutLen); return 2; } @@ -1028,8 +1080,9 @@ static int env_table_connect_DSN (lua_State *L) { ** Lua Returns: ** new connection details table */ -static void env_connect_fix_old (lua_State *L) { - static const char* const opt_names[] = { +static void env_connect_fix_old (lua_State *L) +{ + static const char *const opt_names[] = { "source", "user", "password", @@ -1062,7 +1115,8 @@ static void env_connect_fix_old (lua_State *L) { ** connection object if successfull ** nil and error message otherwise. */ -static int env_connect (lua_State *L) { +static int env_connect (lua_State *L) +{ env_data *env = getenvironment (L, 1); SQLCHAR *sourcename = NULL; SQLCHAR *username = NULL; @@ -1084,9 +1138,9 @@ static int env_connect (lua_State *L) { } /* get the standard connection details */ - sourcename = (SQLCHAR*)luasql_table_optstring(L, 2, "source", NULL); - username = (SQLCHAR*)luasql_table_optstring(L, 2, "user", NULL); - password = (SQLCHAR*)luasql_table_optstring(L, 2, "password", NULL); + sourcename = (SQLCHAR *)luasql_table_optstring(L, 2, "source", NULL); + username = (SQLCHAR *)luasql_table_optstring(L, 2, "user", NULL); + password = (SQLCHAR *)luasql_table_optstring(L, 2, "password", NULL); /* tries to allocate connection handle */ ret = SQLAllocHandle (hDBC, env->henv, &hdbc); @@ -1095,7 +1149,8 @@ static int env_connect (lua_State *L) { } /* tries to connect handle */ - ret = SQLConnect (hdbc, sourcename, SQL_NTS, username, SQL_NTS, password, SQL_NTS); + ret = SQLConnect (hdbc, sourcename, SQL_NTS, username, SQL_NTS, password, + SQL_NTS); if (error(ret)) { ret = fail(L, hDBC, hdbc); SQLFreeHandle(hDBC, hdbc); @@ -1109,7 +1164,8 @@ static int env_connect (lua_State *L) { /* ** Closes an environment object */ -static int env_close (lua_State *L) { +static int env_close (lua_State *L) +{ SQLRETURN ret; env_data *env = (env_data *)luaL_checkudata(L, 1, LUASQL_ENVIRONMENT_ODBC); luaL_argcheck (L, env != NULL, 1, LUASQL_PREFIX"environment expected"); @@ -1134,7 +1190,8 @@ static int env_close (lua_State *L) { /* ** Create metatables for each class of object. */ -static void create_metatables (lua_State *L) { +static void create_metatables (lua_State *L) +{ struct luaL_Reg environment_methods[] = { {"__gc", env_close}, /* Should this method be changed? */ {"close", env_close}, @@ -1177,7 +1234,8 @@ static void create_metatables (lua_State *L) { /* ** Creates an Environment and returns it. */ -static int create_environment (lua_State *L) { +static int create_environment (lua_State *L) +{ env_data *env; SQLHENV henv; SQLRETURN ret = SQLAllocHandle(hENV, SQL_NULL_HANDLE, &henv); @@ -1185,7 +1243,7 @@ static int create_environment (lua_State *L) { return luasql_faildirect(L, "error creating environment."); } - ret = SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); + ret = SQLSetEnvAttr (henv, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0); if (error(ret)) { ret = luasql_faildirect (L, "error setting SQL version."); SQLFreeHandle (hENV, henv); @@ -1206,7 +1264,8 @@ static int create_environment (lua_State *L) { ** Creates the metatables for the objects and registers the ** driver open method. */ -LUASQL_API int luaopen_luasql_odbc (lua_State *L) { +LUASQL_API int luaopen_luasql_odbc (lua_State *L) +{ struct luaL_Reg driver[] = { {"odbc", create_environment}, {NULL, NULL}, @@ -1216,4 +1275,4 @@ LUASQL_API int luaopen_luasql_odbc (lua_State *L) { luaL_setfuncs (L, driver, 0); luasql_set_info (L); return 1; -} +} From 87e04751a509df4edaf7f515fdffab02a1297802 Mon Sep 17 00:00:00 2001 From: Scott Morgan Date: Sat, 25 Apr 2015 19:35:47 +0100 Subject: [PATCH 48/64] Ran file through beautifier Using the following command: astyle --style=1tbs --indent=tab=5 --align-pointer=name --align-reference=name --max-code-length=80 --lineend=linux ls_mysql.c Then tidied up a bit --- src/ls_mysql.c | 355 ++++++++++++++++++++++++++++--------------------- 1 file changed, 200 insertions(+), 155 deletions(-) diff --git a/src/ls_mysql.c b/src/ls_mysql.c index afcb38d..fb7adb8 100644 --- a/src/ls_mysql.c +++ b/src/ls_mysql.c @@ -23,7 +23,7 @@ #include "luasql.h" -/* MSVC still doesn't support C99 properly until 2015 */ +/* MSVC still doesn't support C99 properly until 2015 */ #if defined(_MSC_VER) && _MSC_VER<1900 #pragma warning(disable:4996) /* and complains if you try to work around it */ #define snprintf _snprintf @@ -34,28 +34,28 @@ #define LUASQL_CURSOR_MYSQL "MySQL cursor" /* For compat with old version 4.0 */ -#if (MYSQL_VERSION_ID < 40100) -#define MYSQL_TYPE_VAR_STRING FIELD_TYPE_VAR_STRING -#define MYSQL_TYPE_STRING FIELD_TYPE_STRING -#define MYSQL_TYPE_DECIMAL FIELD_TYPE_DECIMAL -#define MYSQL_TYPE_SHORT FIELD_TYPE_SHORT -#define MYSQL_TYPE_LONG FIELD_TYPE_LONG -#define MYSQL_TYPE_FLOAT FIELD_TYPE_FLOAT -#define MYSQL_TYPE_DOUBLE FIELD_TYPE_DOUBLE -#define MYSQL_TYPE_LONGLONG FIELD_TYPE_LONGLONG -#define MYSQL_TYPE_INT24 FIELD_TYPE_INT24 -#define MYSQL_TYPE_YEAR FIELD_TYPE_YEAR -#define MYSQL_TYPE_TINY FIELD_TYPE_TINY -#define MYSQL_TYPE_TINY_BLOB FIELD_TYPE_TINY_BLOB -#define MYSQL_TYPE_MEDIUM_BLOB FIELD_TYPE_MEDIUM_BLOB -#define MYSQL_TYPE_LONG_BLOB FIELD_TYPE_LONG_BLOB -#define MYSQL_TYPE_BLOB FIELD_TYPE_BLOB -#define MYSQL_TYPE_DATE FIELD_TYPE_DATE -#define MYSQL_TYPE_NEWDATE FIELD_TYPE_NEWDATE -#define MYSQL_TYPE_DATETIME FIELD_TYPE_DATETIME -#define MYSQL_TYPE_TIME FIELD_TYPE_TIME -#define MYSQL_TYPE_TIMESTAMP FIELD_TYPE_TIMESTAMP -#define MYSQL_TYPE_ENUM FIELD_TYPE_ENUM +#if (MYSQL_VERSION_ID < 40100) +#define MYSQL_TYPE_VAR_STRING FIELD_TYPE_VAR_STRING +#define MYSQL_TYPE_STRING FIELD_TYPE_STRING +#define MYSQL_TYPE_DECIMAL FIELD_TYPE_DECIMAL +#define MYSQL_TYPE_SHORT FIELD_TYPE_SHORT +#define MYSQL_TYPE_LONG FIELD_TYPE_LONG +#define MYSQL_TYPE_FLOAT FIELD_TYPE_FLOAT +#define MYSQL_TYPE_DOUBLE FIELD_TYPE_DOUBLE +#define MYSQL_TYPE_LONGLONG FIELD_TYPE_LONGLONG +#define MYSQL_TYPE_INT24 FIELD_TYPE_INT24 +#define MYSQL_TYPE_YEAR FIELD_TYPE_YEAR +#define MYSQL_TYPE_TINY FIELD_TYPE_TINY +#define MYSQL_TYPE_TINY_BLOB FIELD_TYPE_TINY_BLOB +#define MYSQL_TYPE_MEDIUM_BLOB FIELD_TYPE_MEDIUM_BLOB +#define MYSQL_TYPE_LONG_BLOB FIELD_TYPE_LONG_BLOB +#define MYSQL_TYPE_BLOB FIELD_TYPE_BLOB +#define MYSQL_TYPE_DATE FIELD_TYPE_DATE +#define MYSQL_TYPE_NEWDATE FIELD_TYPE_NEWDATE +#define MYSQL_TYPE_DATETIME FIELD_TYPE_DATETIME +#define MYSQL_TYPE_TIME FIELD_TYPE_TIME +#define MYSQL_TYPE_TIMESTAMP FIELD_TYPE_TIMESTAMP +#define MYSQL_TYPE_ENUM FIELD_TYPE_ENUM #define MYSQL_TYPE_SET FIELD_TYPE_SET #define MYSQL_TYPE_NULL FIELD_TYPE_NULL @@ -89,7 +89,8 @@ LUASQL_API int luaopen_luasql_mysql (lua_State *L); /* ** Check for valid environment. */ -static env_data *getenvironment (lua_State *L) { +static env_data *getenvironment (lua_State *L) +{ env_data *env = (env_data *)luaL_checkudata (L, 1, LUASQL_ENVIRONMENT_MYSQL); luaL_argcheck (L, env != NULL, 1, "environment expected"); luaL_argcheck (L, !env->closed, 1, "environment is closed"); @@ -100,7 +101,8 @@ static env_data *getenvironment (lua_State *L) { /* ** Check for valid connection. */ -static conn_data *getconnection (lua_State *L) { +static conn_data *getconnection (lua_State *L) +{ conn_data *conn = (conn_data *)luaL_checkudata (L, 1, LUASQL_CONNECTION_MYSQL); luaL_argcheck (L, conn != NULL, 1, "connection expected"); luaL_argcheck (L, !conn->closed, 1, "connection is closed"); @@ -111,7 +113,8 @@ static conn_data *getconnection (lua_State *L) { /* ** Check for valid cursor. */ -static cur_data *getcursor (lua_State *L) { +static cur_data *getcursor (lua_State *L) +{ cur_data *cur = (cur_data *)luaL_checkudata (L, 1, LUASQL_CURSOR_MYSQL); luaL_argcheck (L, cur != NULL, 1, "cursor expected"); luaL_argcheck (L, !cur->closed, 1, "cursor is closed"); @@ -122,43 +125,57 @@ static cur_data *getcursor (lua_State *L) { /* ** Push the value of #i field of #tuple row. */ -static void pushvalue (lua_State *L, void *row, long int len) { - if (row == NULL) +static void pushvalue (lua_State *L, void *row, long int len) +{ + if (row == NULL) { lua_pushnil (L); - else + } else { lua_pushlstring (L, row, len); + } } /* ** Get the internal database type of the given column. */ -static char *getcolumntype (enum enum_field_types type) { +static char *getcolumntype (enum enum_field_types type) +{ switch (type) { - case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: - return "string"; - case MYSQL_TYPE_DECIMAL: case MYSQL_TYPE_SHORT: case MYSQL_TYPE_LONG: - case MYSQL_TYPE_FLOAT: case MYSQL_TYPE_DOUBLE: case MYSQL_TYPE_LONGLONG: - case MYSQL_TYPE_INT24: case MYSQL_TYPE_YEAR: case MYSQL_TYPE_TINY: - return "number"; - case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: - return "binary"; - case MYSQL_TYPE_DATE: case MYSQL_TYPE_NEWDATE: - return "date"; - case MYSQL_TYPE_DATETIME: - return "datetime"; - case MYSQL_TYPE_TIME: - return "time"; - case MYSQL_TYPE_TIMESTAMP: - return "timestamp"; - case MYSQL_TYPE_ENUM: case MYSQL_TYPE_SET: - return "set"; - case MYSQL_TYPE_NULL: - return "null"; - default: - return "undefined"; + case MYSQL_TYPE_VAR_STRING: + case MYSQL_TYPE_STRING: + return "string"; + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_SHORT: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_FLOAT: + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_TINY: + return "number"; + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + return "binary"; + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_NEWDATE: + return "date"; + case MYSQL_TYPE_DATETIME: + return "datetime"; + case MYSQL_TYPE_TIME: + return "time"; + case MYSQL_TYPE_TIMESTAMP: + return "timestamp"; + case MYSQL_TYPE_ENUM: + case MYSQL_TYPE_SET: + return "set"; + case MYSQL_TYPE_NULL: + return "null"; + default: + return "undefined"; } } @@ -166,7 +183,8 @@ static char *getcolumntype (enum enum_field_types type) { /* ** Creates the lists of fields names and fields types. */ -static void create_colinfo(lua_State *L, cur_data *cur) { +static void create_colinfo(lua_State *L, cur_data *cur) +{ char name[50]; int i; MYSQL_FIELD *fields = mysql_fetch_fields(cur->my_res); @@ -177,7 +195,8 @@ static void create_colinfo(lua_State *L, cur_data *cur) { for(i = 1; i <= cur->numcols; i++) { lua_pushstring(L, fields[i-1].name); lua_rawseti(L, -3, i); - snprintf(name, sizeof(name), "%.20s(%ld)", getcolumntype (fields[i-1].type), fields[i-1].length); + snprintf(name, sizeof(name), "%.20s(%ld)", getcolumntype (fields[i-1].type), + fields[i-1].length); lua_pushstring(L, name); lua_rawseti(L, -2, i); } @@ -191,7 +210,8 @@ static void create_colinfo(lua_State *L, cur_data *cur) { /* ** Closes the cursos and nullify all structure fields. */ -static int cur_nullify (lua_State *L, cur_data *cur) { +static int cur_nullify (lua_State *L, cur_data *cur) +{ /* Nullify structure fields. */ cur->closed = 1; mysql_free_result(cur->my_res); @@ -201,11 +221,12 @@ static int cur_nullify (lua_State *L, cur_data *cur) { return 0; } - + /* ** Get another row of the given cursor. */ -static int cur_fetch (lua_State *L) { +static int cur_fetch (lua_State *L) +{ cur_data *cur = getcursor (L); MYSQL_RES *res = cur->my_res; unsigned long *lengths; @@ -230,10 +251,12 @@ static int cur_fetch (lua_State *L) { if (strchr (opts, 'a') != NULL) { int i; /* Check if colnames exists */ - if (cur->colnames == LUA_NOREF) - create_colinfo(L, cur); - lua_rawgeti (L, LUA_REGISTRYINDEX, cur->colnames);/* Push colnames*/ - + if (cur->colnames == LUA_NOREF) { + create_colinfo(L, cur); + } + /* Push colnames */ + lua_rawgeti (L, LUA_REGISTRYINDEX, cur->colnames); + /* Copy values to alphanumerical indices */ for (i = 0; i < cur->numcols; i++) { lua_rawgeti(L, -1, i+1); /* push the field name */ @@ -246,12 +269,12 @@ static int cur_fetch (lua_State *L) { } lua_pushvalue(L, 2); return 1; /* return table */ - } - else { + } else { int i; luaL_checkstack (L, cur->numcols, LUASQL_PREFIX"too many columns"); - for (i = 0; i < cur->numcols; i++) + for (i = 0; i < cur->numcols; i++) { pushvalue (L, row[i], lengths[i]); + } return cur->numcols; /* return #numcols values */ } } @@ -260,10 +283,12 @@ static int cur_fetch (lua_State *L) { /* ** Cursor object collector function */ -static int cur_gc (lua_State *L) { +static int cur_gc (lua_State *L) +{ cur_data *cur = (cur_data *)luaL_checkudata (L, 1, LUASQL_CURSOR_MYSQL); - if (cur != NULL && !(cur->closed)) + if (cur != NULL && !(cur->closed)) { cur_nullify (L, cur); + } return 0; } @@ -272,7 +297,8 @@ static int cur_gc (lua_State *L) { ** Close the cursor on top of the stack. ** Return 1 */ -static int cur_close (lua_State *L) { +static int cur_close (lua_State *L) +{ cur_data *cur = (cur_data *)luaL_checkudata (L, 1, LUASQL_CURSOR_MYSQL); luaL_argcheck (L, cur != NULL, 1, LUASQL_PREFIX"cursor expected"); if (cur->closed) { @@ -290,13 +316,15 @@ static int cur_close (lua_State *L) { ** If the table isn't built yet, call the creator function and stores ** a reference to it on the cursor structure. */ -static void _pushtable (lua_State *L, cur_data *cur, size_t off) { +static void _pushtable (lua_State *L, cur_data *cur, size_t off) +{ int *ref = (int *)((char *)cur + off); /* If colnames or coltypes do not exist, create both. */ - if (*ref == LUA_NOREF) + if (*ref == LUA_NOREF) { create_colinfo(L, cur); - + } + /* Pushes the right table (colnames or coltypes) */ lua_rawgeti (L, LUA_REGISTRYINDEX, *ref); } @@ -306,7 +334,8 @@ static void _pushtable (lua_State *L, cur_data *cur, size_t off) { /* ** Return the list of field names. */ -static int cur_getcolnames (lua_State *L) { +static int cur_getcolnames (lua_State *L) +{ pushtable (L, getcursor(L), colnames); return 1; } @@ -315,7 +344,8 @@ static int cur_getcolnames (lua_State *L) { /* ** Return the list of field types. */ -static int cur_getcoltypes (lua_State *L) { +static int cur_getcoltypes (lua_State *L) +{ pushtable (L, getcursor(L), coltypes); return 1; } @@ -324,7 +354,8 @@ static int cur_getcoltypes (lua_State *L) { /* ** Push the number of rows. */ -static int cur_numrows (lua_State *L) { +static int cur_numrows (lua_State *L) +{ lua_pushnumber (L, (lua_Number)mysql_num_rows (getcursor(L)->my_res)); return 1; } @@ -333,7 +364,8 @@ static int cur_numrows (lua_State *L) { /* ** Create a new Cursor object and push it on top of the stack. */ -static int create_cursor (lua_State *L, int conn, MYSQL_RES *result, int cols) { +static int create_cursor (lua_State *L, int conn, MYSQL_RES *result, int cols) +{ cur_data *cur = (cur_data *)lua_newuserdata(L, sizeof(cur_data)); luasql_setmeta (L, LUASQL_CURSOR_MYSQL); @@ -351,7 +383,8 @@ static int create_cursor (lua_State *L, int conn, MYSQL_RES *result, int cols) { } -static int conn_gc (lua_State *L) { +static int conn_gc (lua_State *L) +{ conn_data *conn=(conn_data *)luaL_checkudata(L, 1, LUASQL_CONNECTION_MYSQL); if (conn != NULL && !(conn->closed)) { /* Nullify structure fields. */ @@ -366,7 +399,8 @@ static int conn_gc (lua_State *L) { /* ** Close a Connection object. */ -static int conn_close (lua_State *L) { +static int conn_close (lua_State *L) +{ conn_data *conn=(conn_data *)luaL_checkudata(L, 1, LUASQL_CONNECTION_MYSQL); luaL_argcheck (L, conn != NULL, 1, LUASQL_PREFIX"connection expected"); if (conn->closed) { @@ -379,20 +413,21 @@ static int conn_close (lua_State *L) { } -static int escape_string (lua_State *L) { - size_t size, new_size; - conn_data *conn = getconnection (L); - const char *from = luaL_checklstring(L, 2, &size); - char *to; - to = (char*)malloc(sizeof(char) * (2 * size + 1)); - if(to) { - new_size = mysql_real_escape_string(conn->my_conn, to, from, size); - lua_pushlstring(L, to, new_size); - free(to); - return 1; - } - luaL_error(L, "could not allocate escaped string"); - return 0; +static int escape_string (lua_State *L) +{ + size_t size, new_size; + conn_data *conn = getconnection (L); + const char *from = luaL_checklstring(L, 2, &size); + char *to; + to = (char *)malloc(sizeof(char) * (2 * size + 1)); + if(to) { + new_size = mysql_real_escape_string(conn->my_conn, to, from, size); + lua_pushlstring(L, to, new_size); + free(to); + return 1; + } + luaL_error(L, "could not allocate escaped string"); + return 0; } /* @@ -400,13 +435,15 @@ static int escape_string (lua_State *L) { ** Return a Cursor object if the statement is a query, otherwise ** return the number of tuples affected by the statement. */ -static int conn_execute (lua_State *L) { +static int conn_execute (lua_State *L) +{ conn_data *conn = getconnection (L); size_t st_len; const char *statement = luaL_checklstring (L, 2, &st_len); if (mysql_real_query(conn->my_conn, statement, st_len)) { /* error executing query */ - return luasql_failmsg(L, "error executing query. MySQL: ", mysql_error(conn->my_conn)); + return luasql_failmsg(L, "error executing query. MySQL: ", + mysql_error(conn->my_conn)); } else { MYSQL_RES *res = mysql_store_result(conn->my_conn); unsigned int num_cols = mysql_field_count(conn->my_conn); @@ -417,12 +454,13 @@ static int conn_execute (lua_State *L) { } else { /* mysql_use_result() returned nothing; should it have? */ if(num_cols == 0) { /* no tuples returned */ - /* query does not return data (it was not a SELECT) */ + /* query does not return data (it was not a SELECT) */ luasql_pushinteger(L, mysql_affected_rows(conn->my_conn)); return 1; - } else { + } else { /* mysql_use_result() should have returned data */ - return luasql_failmsg(L, "error retrieving result. MySQL: ", mysql_error(conn->my_conn)); + return luasql_failmsg(L, "error retrieving result. MySQL: ", + mysql_error(conn->my_conn)); } } } @@ -432,7 +470,8 @@ static int conn_execute (lua_State *L) { /* ** Commit the current transaction. */ -static int conn_commit (lua_State *L) { +static int conn_commit (lua_State *L) +{ conn_data *conn = getconnection (L); lua_pushboolean(L, !mysql_commit(conn->my_conn)); return 1; @@ -442,7 +481,8 @@ static int conn_commit (lua_State *L) { /* ** Rollback the current transaction. */ -static int conn_rollback (lua_State *L) { +static int conn_rollback (lua_State *L) +{ conn_data *conn = getconnection (L); lua_pushboolean(L, !mysql_rollback(conn->my_conn)); return 1; @@ -452,12 +492,12 @@ static int conn_rollback (lua_State *L) { /* ** Set "auto commit" property of the connection. Modes ON/OFF */ -static int conn_setautocommit (lua_State *L) { +static int conn_setautocommit (lua_State *L) +{ conn_data *conn = getconnection (L); if (lua_toboolean (L, 2)) { mysql_autocommit(conn->my_conn, 1); /* Set it ON */ - } - else { + } else { mysql_autocommit(conn->my_conn, 0); } lua_pushboolean(L, 1); @@ -468,16 +508,18 @@ static int conn_setautocommit (lua_State *L) { /* ** Get Last auto-increment id generated */ -static int conn_getlastautoid (lua_State *L) { - conn_data *conn = getconnection(L); - luasql_pushinteger(L, mysql_insert_id(conn->my_conn)); - return 1; +static int conn_getlastautoid (lua_State *L) +{ + conn_data *conn = getconnection(L); + luasql_pushinteger(L, mysql_insert_id(conn->my_conn)); + return 1; } /* ** Create a new Connection object and push it on top of the stack. */ -static int create_connection (lua_State *L, int env, MYSQL *const my_conn) { +static int create_connection (lua_State *L, int env, MYSQL *const my_conn) +{ conn_data *conn = (conn_data *)lua_newuserdata(L, sizeof(conn_data)); luasql_setmeta (L, LUASQL_CONNECTION_MYSQL); @@ -498,8 +540,9 @@ static int create_connection (lua_State *L, int env, MYSQL *const my_conn) { ** Lua Returns: ** new connection details table */ -static void env_connect_fix_old (lua_State *L) { - static const char* const opt_names[] = { +static void env_connect_fix_old (lua_State *L) +{ + static const char *const opt_names[] = { "source", "user", "password", @@ -526,18 +569,13 @@ static void env_connect_fix_old (lua_State *L) { ** param: one string for each connection parameter, said ** datasource, username, password, host and port. */ -static int env_connect (lua_State *L) { - env_data *env = getenvironment(L); /* validade environment */ - -/* const char *sourcename = luaL_checkstring(L, 2); - const char *username = luaL_optstring(L, 3, NULL); - const char *password = luaL_optstring(L, 4, NULL); - const char *host = luaL_optstring(L, 5, NULL); - const int port = luaL_optinteger(L, 6, 0); -*/ +static int env_connect (lua_State *L) +{ const char *sourcename; MYSQL *conn; + getenvironment(L); /* validade environment */ + if(lua_gettop(L) < 2) { return luasql_faildirect(L, "No connection details provided"); } @@ -552,17 +590,17 @@ static int env_connect (lua_State *L) { /* Try to init the connection object. */ conn = mysql_init(NULL); - if (conn == NULL) + if (conn == NULL) { return luasql_faildirect(L, "error connecting: Out of memory."); + } if (!mysql_real_connect(conn, - luasql_table_optstring(L, 2, "host", NULL), - luasql_table_optstring(L, 2, "user", NULL), - luasql_table_optstring(L, 2, "password", NULL), - sourcename, - (int)luasql_table_optnumber(L, 2, "port", 0), - NULL, 0)) - { + luasql_table_optstring(L, 2, "host", NULL), + luasql_table_optstring(L, 2, "user", NULL), + luasql_table_optstring(L, 2, "password", NULL), + sourcename, + (int)luasql_table_optnumber(L, 2, "port", 0), + NULL, 0)) { char error_msg[100]; strncpy (error_msg, mysql_error(conn), 99); mysql_close (conn); /* Close conn if connect failed */ @@ -575,9 +613,12 @@ static int env_connect (lua_State *L) { /* ** */ -static int env_gc (lua_State *L) { - env_data *env= (env_data *)luaL_checkudata (L, 1, LUASQL_ENVIRONMENT_MYSQL); if (env != NULL && !(env->closed)) +static int env_gc (lua_State *L) +{ + env_data *env= (env_data *)luaL_checkudata (L, 1, LUASQL_ENVIRONMENT_MYSQL); + if (env != NULL && !(env->closed)) { env->closed = 1; + } return 0; } @@ -585,7 +626,8 @@ static int env_gc (lua_State *L) { /* ** Close environment object. */ -static int env_close (lua_State *L) { +static int env_close (lua_State *L) +{ env_data *env= (env_data *)luaL_checkudata (L, 1, LUASQL_ENVIRONMENT_MYSQL); luaL_argcheck (L, env != NULL, 1, LUASQL_PREFIX"environment expected"); if (env->closed) { @@ -601,33 +643,34 @@ static int env_close (lua_State *L) { /* ** Create metatables for each class of object. */ -static void create_metatables (lua_State *L) { - struct luaL_Reg environment_methods[] = { - {"__gc", env_gc}, - {"close", env_close}, - {"connect", env_connect}, +static void create_metatables (lua_State *L) +{ + struct luaL_Reg environment_methods[] = { + {"__gc", env_gc}, + {"close", env_close}, + {"connect", env_connect}, {NULL, NULL}, }; - struct luaL_Reg connection_methods[] = { - {"__gc", conn_gc}, - {"close", conn_close}, - {"escape", escape_string}, - {"execute", conn_execute}, - {"commit", conn_commit}, - {"rollback", conn_rollback}, - {"setautocommit", conn_setautocommit}, + struct luaL_Reg connection_methods[] = { + {"__gc", conn_gc}, + {"close", conn_close}, + {"escape", escape_string}, + {"execute", conn_execute}, + {"commit", conn_commit}, + {"rollback", conn_rollback}, + {"setautocommit", conn_setautocommit}, {"getlastautoid", conn_getlastautoid}, {NULL, NULL}, - }; - struct luaL_Reg cursor_methods[] = { - {"__gc", cur_gc}, - {"close", cur_close}, - {"getcolnames", cur_getcolnames}, - {"getcoltypes", cur_getcoltypes}, - {"fetch", cur_fetch}, - {"numrows", cur_numrows}, + }; + struct luaL_Reg cursor_methods[] = { + {"__gc", cur_gc}, + {"close", cur_close}, + {"getcolnames", cur_getcolnames}, + {"getcoltypes", cur_getcoltypes}, + {"fetch", cur_fetch}, + {"numrows", cur_numrows}, {NULL, NULL}, - }; + }; luasql_createmeta (L, LUASQL_ENVIRONMENT_MYSQL, environment_methods); luasql_createmeta (L, LUASQL_CONNECTION_MYSQL, connection_methods); luasql_createmeta (L, LUASQL_CURSOR_MYSQL, cursor_methods); @@ -638,7 +681,8 @@ static void create_metatables (lua_State *L) { /* ** Creates an Environment and returns it. */ -static int create_environment (lua_State *L) { +static int create_environment (lua_State *L) +{ env_data *env = (env_data *)lua_newuserdata(L, sizeof(env_data)); luasql_setmeta (L, LUASQL_ENVIRONMENT_MYSQL); @@ -652,7 +696,8 @@ static int create_environment (lua_State *L) { ** Creates the metatables for the objects and registers the ** driver open method. */ -LUASQL_API int luaopen_luasql_mysql (lua_State *L) { +LUASQL_API int luaopen_luasql_mysql (lua_State *L) +{ struct luaL_Reg driver[] = { {"mysql", create_environment}, {NULL, NULL}, @@ -661,8 +706,8 @@ LUASQL_API int luaopen_luasql_mysql (lua_State *L) { luasql_find_driver_table(L); luaL_setfuncs(L, driver, 0); luasql_set_info (L); - lua_pushliteral (L, "_MYSQLVERSION"); - lua_pushliteral (L, MYSQL_SERVER_VERSION); - lua_settable (L, -3); + lua_pushliteral (L, "_MYSQLVERSION"); + lua_pushliteral (L, MYSQL_SERVER_VERSION); + lua_settable (L, -3); return 1; } From 4edf87b1756bba4586740288a87d5f6f2a14c4ef Mon Sep 17 00:00:00 2001 From: Scott Morgan Date: Sat, 25 Apr 2015 19:47:10 +0100 Subject: [PATCH 49/64] Using C99 for now Not 100% on this, ideally we'd want the lowest common denominator, but for now it avoids some warnings (e.g. no 'long long' and 'snprintf' support in plain ANSI) Also bumped the version in the makefile to 3.0.0 --- Makefile | 2 +- config | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index b7b9135..79d53a3 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -V= 2.3.0 +V= 3.0.0 CONFIG= ./config include $(CONFIG) diff --git a/config b/config index 7dd267b..7798d7f 100644 --- a/config +++ b/config @@ -12,19 +12,19 @@ T= mysql # Installation directories # Default prefix -PREFIX = /usr/local +PREFIX = /usr # System's libraries directory (where binary libraries are installed) -LUA_LIBDIR= $(PREFIX)/lib/lua/5.1 +LUA_LIBDIR= $(PREFIX)/lib/lua/5.2 # System's lua directory (where Lua libraries are installed) -LUA_DIR= $(PREFIX)/share/lua/5.1 +LUA_DIR= $(PREFIX)/share/lua/5.2 # Lua includes directory -LUA_INC= $(PREFIX)/include +LUA_INC= $(PREFIX)/include/lua5.2 # Lua version number (first and second digits of target version) -LUA_VERSION_NUM= 501 +LUA_VERSION_NUM= 502 # OS dependent LIB_OPTION= -shared #for Linux @@ -60,9 +60,9 @@ DRIVER_INCS= -I/usr/include/mysql #DRIVER_LIBS= -L/usr/local/firebird -lfbclient #DRIVER_INCS= -WARN= -Wall -Wmissing-prototypes -Wmissing-declarations -ansi -pedantic +WARN= -Wall -Wmissing-prototypes -Wmissing-declarations -pedantic INCS= -I$(LUA_INC) -CFLAGS= -O2 $(WARN) -fPIC $(DRIVER_INCS) $(INCS) -DLUASQL_VERSION_NUMBER='"$V"' $(DEFS) +CFLAGS= -O2 -std=gnu99 $(WARN) -fPIC $(DRIVER_INCS) $(INCS) -DLUASQL_VERSION_NUMBER='"$V"' $(DEFS) CC= gcc # $Id: config,v 1.10 2008/05/30 17:21:18 tomas Exp $ From 721822fdf4ba0ed5d9b19c05797ae1bc81b654fe Mon Sep 17 00:00:00 2001 From: Scott Morgan Date: Sat, 25 Apr 2015 19:57:45 +0100 Subject: [PATCH 50/64] Ran file through beautifier astyle --style=1tbs --indent=tab=5 --align-pointer=name --align-reference=name --max-code-length=80 --lineend=linux ls_sqlite.c ls_sqlite3.c --- src/ls_sqlite.c | 471 +++++++++++++------------- src/ls_sqlite3.c | 834 +++++++++++++++++++++++------------------------ 2 files changed, 640 insertions(+), 665 deletions(-) diff --git a/src/ls_sqlite.c b/src/ls_sqlite.c index 8359510..36e1824 100644 --- a/src/ls_sqlite.c +++ b/src/ls_sqlite.c @@ -21,24 +21,21 @@ #define LUASQL_CONNECTION_SQLITE "SQLite connection" #define LUASQL_CURSOR_SQLITE "SQLite cursor" -typedef struct -{ - short closed; +typedef struct { + short closed; } env_data; -typedef struct -{ +typedef struct { short closed; int env; /* reference to environment */ short auto_commit; /* 0 for manual commit */ - unsigned int cur_counter; + unsigned int cur_counter; sqlite *sql_conn; } conn_data; -typedef struct -{ +typedef struct { short closed; int conn; /* reference to connection */ int numcols; /* number of columns */ @@ -52,7 +49,8 @@ LUASQL_API int luaopen_luasql_sqlite(lua_State *L); /* ** Check for valid environment. */ -static env_data *getenvironment(lua_State *L) { +static env_data *getenvironment(lua_State *L) +{ env_data *env = (env_data *)luaL_checkudata(L, 1, LUASQL_ENVIRONMENT_SQLITE); luaL_argcheck(L, env != NULL, 1, LUASQL_PREFIX"environment expected"); luaL_argcheck(L, !env->closed, 1, LUASQL_PREFIX"environment is closed"); @@ -63,7 +61,8 @@ static env_data *getenvironment(lua_State *L) { /* ** Check for valid connection. */ -static conn_data *getconnection(lua_State *L) { +static conn_data *getconnection(lua_State *L) +{ conn_data *conn = (conn_data *)luaL_checkudata (L, 1, LUASQL_CONNECTION_SQLITE); luaL_argcheck(L, conn != NULL, 1, LUASQL_PREFIX"connection expected"); luaL_argcheck(L, !conn->closed, 1, LUASQL_PREFIX"connection is closed"); @@ -74,7 +73,8 @@ static conn_data *getconnection(lua_State *L) { /* ** Check for valid cursor. */ -static cur_data *getcursor(lua_State *L) { +static cur_data *getcursor(lua_State *L) +{ cur_data *cur = (cur_data *)luaL_checkudata (L, 1, LUASQL_CURSOR_SQLITE); luaL_argcheck(L, cur != NULL, 1, LUASQL_PREFIX"cursor expected"); luaL_argcheck(L, !cur->closed, 1, LUASQL_PREFIX"cursor is closed"); @@ -87,19 +87,19 @@ static cur_data *getcursor(lua_State *L) { */ static void cur_nullify(lua_State *L, cur_data *cur) { - conn_data *conn; - - /* Nullify structure fields. */ - cur->closed = 1; - cur->sql_vm = NULL; - /* Decrement cursor counter on connection object */ - lua_rawgeti (L, LUA_REGISTRYINDEX, cur->conn); - conn = lua_touserdata (L, -1); - conn->cur_counter--; - - luaL_unref(L, LUA_REGISTRYINDEX, cur->conn); - luaL_unref(L, LUA_REGISTRYINDEX, cur->colnames); - luaL_unref(L, LUA_REGISTRYINDEX, cur->coltypes); + conn_data *conn; + + /* Nullify structure fields. */ + cur->closed = 1; + cur->sql_vm = NULL; + /* Decrement cursor counter on connection object */ + lua_rawgeti (L, LUA_REGISTRYINDEX, cur->conn); + conn = lua_touserdata (L, -1); + conn->cur_counter--; + + luaL_unref(L, LUA_REGISTRYINDEX, cur->conn); + luaL_unref(L, LUA_REGISTRYINDEX, cur->colnames); + luaL_unref(L, LUA_REGISTRYINDEX, cur->coltypes); } @@ -107,80 +107,78 @@ static void cur_nullify(lua_State *L, cur_data *cur) ** Finalizes the vm ** Return nil + errmsg or nil in case of sucess */ -static int finalize(lua_State *L, cur_data *cur) { - char *errmsg; - if (sqlite_finalize(cur->sql_vm, &errmsg) != SQLITE_OK) - { +static int finalize(lua_State *L, cur_data *cur) +{ + char *errmsg; + if (sqlite_finalize(cur->sql_vm, &errmsg) != SQLITE_OK) { cur_nullify(L, cur); - lua_pushnil(L); - lua_pushliteral(L, LUASQL_PREFIX); - lua_pushstring(L, errmsg); - sqlite_freemem(errmsg); - lua_concat(L, 2); - return 2; - } + lua_pushnil(L); + lua_pushliteral(L, LUASQL_PREFIX); + lua_pushstring(L, errmsg); + sqlite_freemem(errmsg); + lua_concat(L, 2); + return 2; + } cur_nullify(L, cur); lua_pushnil(L); - return 1; + return 1; } /* ** Get another row of the given cursor. */ -static int cur_fetch (lua_State *L) { +static int cur_fetch (lua_State *L) +{ cur_data *cur = getcursor(L); - sqlite_vm *vm = cur->sql_vm; - const char **row = NULL; - int res; + sqlite_vm *vm = cur->sql_vm; + const char **row = NULL; + int res; - if (vm == NULL) - return 0; + if (vm == NULL) { + return 0; + } - res = sqlite_step(vm, NULL, &row, NULL); + res = sqlite_step(vm, NULL, &row, NULL); - /* no more results? */ - if (res == SQLITE_DONE) - return finalize(L, cur); + /* no more results? */ + if (res == SQLITE_DONE) { + return finalize(L, cur); + } - if (res != SQLITE_ROW) - return finalize(L, cur); + if (res != SQLITE_ROW) { + return finalize(L, cur); + } - if (lua_istable (L, 2)) - { + if (lua_istable (L, 2)) { int i; const char *opts = luaL_optstring(L, 3, "n"); - if (strchr(opts, 'n') != NULL) - { + if (strchr(opts, 'n') != NULL) { /* Copy values to numerical indices */ - for (i = 0; i < cur->numcols;) - { - lua_pushstring(L, row[i]); + for (i = 0; i < cur->numcols;) { + lua_pushstring(L, row[i]); lua_rawseti(L, 2, ++i); } - } - if (strchr(opts, 'a') != NULL) - { + } + if (strchr(opts, 'a') != NULL) { /* Copy values to alphanumerical indices */ - lua_rawgeti(L, LUA_REGISTRYINDEX, cur->colnames); + lua_rawgeti(L, LUA_REGISTRYINDEX, cur->colnames); - for (i = 0; i < cur->numcols; i++) - { + for (i = 0; i < cur->numcols; i++) { lua_rawgeti(L, -1, i+1); - lua_pushstring(L, row[i]); + lua_pushstring(L, row[i]); lua_rawset (L, 2); } - } + } lua_pushvalue(L, 2); return 1; /* return table */ - } - else - { + } else { int i; luaL_checkstack (L, cur->numcols, LUASQL_PREFIX"too many columns"); - for (i = 0; i < cur->numcols; ++i) + for (i = 0; i < cur->numcols; ++i) { lua_pushstring(L, row[i]); + } return cur->numcols; /* return #numcols values */ } } @@ -191,13 +189,12 @@ static int cur_fetch (lua_State *L) { */ static int cur_gc(lua_State *L) { - cur_data *cur = (cur_data *)luaL_checkudata(L, 1, LUASQL_CURSOR_SQLITE); - if (cur != NULL && !(cur->closed)) - { - sqlite_finalize(cur->sql_vm, NULL); - cur_nullify(L, cur); - } - return 0; + cur_data *cur = (cur_data *)luaL_checkudata(L, 1, LUASQL_CURSOR_SQLITE); + if (cur != NULL && !(cur->closed)) { + sqlite_finalize(cur->sql_vm, NULL); + cur_nullify(L, cur); + } + return 0; } @@ -225,8 +222,8 @@ static int cur_close(lua_State *L) */ static int cur_getcolnames(lua_State *L) { - cur_data *cur = getcursor(L); - lua_rawgeti(L, LUA_REGISTRYINDEX, cur->colnames); + cur_data *cur = getcursor(L); + lua_rawgeti(L, LUA_REGISTRYINDEX, cur->colnames); return 1; } @@ -236,8 +233,8 @@ static int cur_getcolnames(lua_State *L) */ static int cur_getcoltypes(lua_State *L) { - cur_data *cur = getcursor(L); - lua_rawgeti(L, LUA_REGISTRYINDEX, cur->coltypes); + cur_data *cur = getcursor(L); + lua_rawgeti(L, LUA_REGISTRYINDEX, cur->coltypes); return 1; } @@ -247,11 +244,11 @@ static int cur_getcoltypes(lua_State *L) */ /* static int create_cursor(lua_State *L, int conn, sqlite_vm *sql_vm, int numcols, const char **row, const char **col_info)*/ -static int create_cursor(lua_State *L, int o, conn_data *conn, - sqlite_vm *sql_vm, int numcols, const char **col_info) +static int create_cursor(lua_State *L, int o, conn_data *conn, + sqlite_vm *sql_vm, int numcols, const char **col_info) { - int i; - cur_data *cur = (cur_data*)lua_newuserdata(L, sizeof(cur_data)); + int i; + cur_data *cur = (cur_data *)lua_newuserdata(L, sizeof(cur_data)); luasql_setmeta (L, LUASQL_CURSOR_SQLITE); /* increment cursor count for the connection creating this cursor */ @@ -265,26 +262,24 @@ static int create_cursor(lua_State *L, int o, conn_data *conn, cur->coltypes = LUA_NOREF; cur->sql_vm = sql_vm; - lua_pushvalue(L, o); + lua_pushvalue(L, o); cur->conn = luaL_ref(L, LUA_REGISTRYINDEX); - /* create table with column names */ - lua_newtable(L); - for (i = 0; i < numcols;) - { - lua_pushstring(L, col_info[i]); - lua_rawseti(L, -2, ++i); - } - cur->colnames = luaL_ref(L, LUA_REGISTRYINDEX); - - /* create table with column types */ - lua_newtable(L); - for (i = 0; i < numcols;) - { - lua_pushstring(L, col_info[numcols+i]); - lua_rawseti(L, -2, ++i); - } - cur->coltypes = luaL_ref(L, LUA_REGISTRYINDEX); + /* create table with column names */ + lua_newtable(L); + for (i = 0; i < numcols;) { + lua_pushstring(L, col_info[i]); + lua_rawseti(L, -2, ++i); + } + cur->colnames = luaL_ref(L, LUA_REGISTRYINDEX); + + /* create table with column types */ + lua_newtable(L); + for (i = 0; i < numcols;) { + lua_pushstring(L, col_info[numcols+i]); + lua_rawseti(L, -2, ++i); + } + cur->coltypes = luaL_ref(L, LUA_REGISTRYINDEX); return 1; } @@ -295,18 +290,18 @@ static int create_cursor(lua_State *L, int o, conn_data *conn, */ static int conn_gc(lua_State *L) { - conn_data *conn = (conn_data *)luaL_checkudata(L, 1, LUASQL_CONNECTION_SQLITE); - if (conn != NULL && !(conn->closed)) - { - if (conn->cur_counter > 0) - return luaL_error (L, LUASQL_PREFIX"there are open cursors"); - - /* Nullify structure fields. */ - conn->closed = 1; - luaL_unref(L, LUA_REGISTRYINDEX, conn->env); - sqlite_close(conn->sql_conn); - } - return 0; + conn_data *conn = (conn_data *)luaL_checkudata(L, 1, LUASQL_CONNECTION_SQLITE); + if (conn != NULL && !(conn->closed)) { + if (conn->cur_counter > 0) { + return luaL_error (L, LUASQL_PREFIX"there are open cursors"); + } + + /* Nullify structure fields. */ + conn->closed = 1; + luaL_unref(L, LUA_REGISTRYINDEX, conn->env); + sqlite_close(conn->sql_conn); + } + return 0; } @@ -336,49 +331,46 @@ static int conn_execute(lua_State *L) { conn_data *conn = getconnection(L); const char *statement = luaL_checkstring(L, 2); - int res; - sqlite_vm *vm; - char *errmsg; - int numcols; - const char **col_info; - - res = sqlite_compile(conn->sql_conn, statement, NULL, &vm, &errmsg); - if (res != SQLITE_OK) - { - lua_pushnil(L); - lua_pushliteral(L, LUASQL_PREFIX); - lua_pushstring(L, errmsg); - sqlite_freemem(errmsg); - lua_concat(L, 2); - return 2; - } - - /* process first result to retrive query information and type */ - res = sqlite_step(vm, &numcols, NULL, &col_info); - - /* real query? if empty, must have numcols!=0 */ - if ((res == SQLITE_ROW) || ((res == SQLITE_DONE) && numcols)) - { + int res; + sqlite_vm *vm; + char *errmsg; + int numcols; + const char **col_info; + + res = sqlite_compile(conn->sql_conn, statement, NULL, &vm, &errmsg); + if (res != SQLITE_OK) { + lua_pushnil(L); + lua_pushliteral(L, LUASQL_PREFIX); + lua_pushstring(L, errmsg); + sqlite_freemem(errmsg); + lua_concat(L, 2); + return 2; + } + + /* process first result to retrive query information and type */ + res = sqlite_step(vm, &numcols, NULL, &col_info); + + /* real query? if empty, must have numcols!=0 */ + if ((res == SQLITE_ROW) || ((res == SQLITE_DONE) && numcols)) { sqlite_reset(vm, NULL); return create_cursor(L, 1, conn, vm, numcols, col_info); } - if (res == SQLITE_DONE) /* and numcols==0, INSERT,UPDATE,DELETE statement */ - { - sqlite_finalize(vm, NULL); - /* return number of columns changed */ - lua_pushnumber(L, sqlite_changes(conn->sql_conn)); - return 1; - } - - /* error */ - sqlite_finalize(vm, &errmsg); - lua_pushnil(L); - lua_pushliteral(L, LUASQL_PREFIX); - lua_pushstring(L, errmsg); - sqlite_freemem(errmsg); - lua_concat(L, 2); - return 2; + if (res == SQLITE_DONE) { /* and numcols==0, INSERT,UPDATE,DELETE statement */ + sqlite_finalize(vm, NULL); + /* return number of columns changed */ + lua_pushnumber(L, sqlite_changes(conn->sql_conn)); + return 1; + } + + /* error */ + sqlite_finalize(vm, &errmsg); + lua_pushnil(L); + lua_pushliteral(L, LUASQL_PREFIX); + lua_pushstring(L, errmsg); + sqlite_freemem(errmsg); + lua_concat(L, 2); + return 2; } @@ -387,24 +379,25 @@ static int conn_execute(lua_State *L) */ static int conn_commit(lua_State *L) { - char *errmsg; + char *errmsg; conn_data *conn = getconnection(L); int res; - const char *sql = "COMMIT"; + const char *sql = "COMMIT"; - if (conn->auto_commit == 0) sql = "COMMIT;BEGIN"; + if (conn->auto_commit == 0) { + sql = "COMMIT;BEGIN"; + } - res = sqlite_exec(conn->sql_conn, sql, NULL, NULL, &errmsg); - if (res != SQLITE_OK) - { + res = sqlite_exec(conn->sql_conn, sql, NULL, NULL, &errmsg); + if (res != SQLITE_OK) { lua_pushnil(L); - lua_pushliteral(L, LUASQL_PREFIX); - lua_pushstring(L, errmsg); - sqlite_freemem(errmsg); - lua_concat(L, 2); - return 2; - } - lua_pushboolean(L, 1); + lua_pushliteral(L, LUASQL_PREFIX); + lua_pushstring(L, errmsg); + sqlite_freemem(errmsg); + lua_concat(L, 2); + return 2; + } + lua_pushboolean(L, 1); return 1; } @@ -414,24 +407,25 @@ static int conn_commit(lua_State *L) */ static int conn_rollback(lua_State *L) { - char *errmsg; + char *errmsg; conn_data *conn = getconnection(L); int res; - const char *sql = "ROLLBACK"; + const char *sql = "ROLLBACK"; - if (conn->auto_commit == 0) sql = "ROLLBACK;BEGIN"; + if (conn->auto_commit == 0) { + sql = "ROLLBACK;BEGIN"; + } - res = sqlite_exec(conn->sql_conn, sql, NULL, NULL, &errmsg); - if (res != SQLITE_OK) - { + res = sqlite_exec(conn->sql_conn, sql, NULL, NULL, &errmsg); + if (res != SQLITE_OK) { lua_pushnil(L); - lua_pushliteral(L, LUASQL_PREFIX); - lua_pushstring(L, errmsg); - sqlite_freemem(errmsg); - lua_concat(L, 2); - return 2; - } - lua_pushboolean(L, 1); + lua_pushliteral(L, LUASQL_PREFIX); + lua_pushstring(L, errmsg); + sqlite_freemem(errmsg); + lua_concat(L, 2); + return 2; + } + lua_pushboolean(L, 1); return 1; } @@ -444,26 +438,22 @@ static int conn_rollback(lua_State *L) static int conn_setautocommit(lua_State *L) { conn_data *conn = getconnection(L); - if (lua_toboolean(L, 2)) - { + if (lua_toboolean(L, 2)) { conn->auto_commit = 1; - /* undo active transaction - ignore errors */ - sqlite_exec(conn->sql_conn, "ROLLBACK", NULL, NULL, NULL); - } - else - { - char *errmsg; - int res; + /* undo active transaction - ignore errors */ + sqlite_exec(conn->sql_conn, "ROLLBACK", NULL, NULL, NULL); + } else { + char *errmsg; + int res; conn->auto_commit = 0; - res = sqlite_exec(conn->sql_conn, "BEGIN", NULL, NULL, &errmsg); - if (res != SQLITE_OK) - { - lua_pushliteral(L, LUASQL_PREFIX); - lua_pushstring(L, errmsg); - sqlite_freemem(errmsg); - lua_concat(L, 2); - lua_error(L); - } + res = sqlite_exec(conn->sql_conn, "BEGIN", NULL, NULL, &errmsg); + if (res != SQLITE_OK) { + lua_pushliteral(L, LUASQL_PREFIX); + lua_pushstring(L, errmsg); + sqlite_freemem(errmsg); + lua_concat(L, 2); + lua_error(L); + } } lua_pushboolean(L, 1); return 1; @@ -475,7 +465,7 @@ static int conn_setautocommit(lua_State *L) */ static int create_connection(lua_State *L, int env, sqlite *sql_conn) { - conn_data *conn = (conn_data*)lua_newuserdata(L, sizeof(conn_data)); + conn_data *conn = (conn_data *)lua_newuserdata(L, sizeof(conn_data)); luasql_setmeta(L, LUASQL_CONNECTION_SQLITE); /* fill in structure */ @@ -495,22 +485,21 @@ static int create_connection(lua_State *L, int env, sqlite *sql_conn) */ static int env_connect(lua_State *L) { - const char *sourcename; + const char *sourcename; sqlite *conn; - char *errmsg; + char *errmsg; getenvironment(L); /* validate environment */ - sourcename = luaL_checkstring(L, 2); - conn = sqlite_open(sourcename, 0, &errmsg); - if (conn == NULL) - { - lua_pushnil(L); - lua_pushliteral(L, LUASQL_PREFIX); - lua_pushstring(L, errmsg); - sqlite_freemem(errmsg); - lua_concat(L, 2); - return 2; - } - return create_connection(L, 1, conn); + sourcename = luaL_checkstring(L, 2); + conn = sqlite_open(sourcename, 0, &errmsg); + if (conn == NULL) { + lua_pushnil(L); + lua_pushliteral(L, LUASQL_PREFIX); + lua_pushstring(L, errmsg); + sqlite_freemem(errmsg); + lua_concat(L, 2); + return 2; + } + return create_connection(L, 1, conn); } @@ -520,8 +509,9 @@ static int env_connect(lua_State *L) static int env_gc (lua_State *L) { env_data *env = (env_data *)luaL_checkudata(L, 1, LUASQL_ENVIRONMENT_SQLITE); - if (env != NULL && !(env->closed)) + if (env != NULL && !(env->closed)) { env->closed = 1; + } return 0; } @@ -544,18 +534,15 @@ static int env_close (lua_State *L) static int conn_escape(lua_State *L) { - const char *from = luaL_checklstring (L, 2, 0); - char *escaped = sqlite_mprintf("%q", from); - if (escaped == NULL) - { - lua_pushnil(L); - } - else - { - lua_pushstring(L, escaped); - sqlite_freemem(escaped); - } - return 1; + const char *from = luaL_checklstring (L, 2, 0); + char *escaped = sqlite_mprintf("%q", from); + if (escaped == NULL) { + lua_pushnil(L); + } else { + lua_pushstring(L, escaped); + sqlite_freemem(escaped); + } + return 1; } /* @@ -563,30 +550,30 @@ static int conn_escape(lua_State *L) */ static void create_metatables (lua_State *L) { - struct luaL_Reg environment_methods[] = { - {"__gc", env_gc}, - {"close", env_close}, - {"connect", env_connect}, + struct luaL_Reg environment_methods[] = { + {"__gc", env_gc}, + {"close", env_close}, + {"connect", env_connect}, {NULL, NULL}, }; - struct luaL_Reg connection_methods[] = { - {"__gc", conn_gc}, - {"close", conn_close}, + struct luaL_Reg connection_methods[] = { + {"__gc", conn_gc}, + {"close", conn_close}, {"escape", conn_escape}, - {"execute", conn_execute}, - {"commit", conn_commit}, - {"rollback", conn_rollback}, - {"setautocommit", conn_setautocommit}, + {"execute", conn_execute}, + {"commit", conn_commit}, + {"rollback", conn_rollback}, + {"setautocommit", conn_setautocommit}, {NULL, NULL}, - }; - struct luaL_Reg cursor_methods[] = { - {"__gc", cur_gc}, - {"close", cur_close}, - {"getcolnames", cur_getcolnames}, - {"getcoltypes", cur_getcoltypes}, - {"fetch", cur_fetch}, + }; + struct luaL_Reg cursor_methods[] = { + {"__gc", cur_gc}, + {"close", cur_close}, + {"getcolnames", cur_getcolnames}, + {"getcoltypes", cur_getcoltypes}, + {"fetch", cur_fetch}, {NULL, NULL}, - }; + }; luasql_createmeta(L, LUASQL_ENVIRONMENT_SQLITE, environment_methods); luasql_createmeta(L, LUASQL_CONNECTION_SQLITE, connection_methods); luasql_createmeta(L, LUASQL_CURSOR_SQLITE, cursor_methods); diff --git a/src/ls_sqlite3.c b/src/ls_sqlite3.c index e7b57d5..e0902a6 100644 --- a/src/ls_sqlite3.c +++ b/src/ls_sqlite3.c @@ -23,30 +23,27 @@ #define LUASQL_CONNECTION_SQLITE "SQLite3 connection" #define LUASQL_CURSOR_SQLITE "SQLite3 cursor" -typedef struct -{ - short closed; +typedef struct { + short closed; } env_data; -typedef struct -{ - short closed; - int env; /* reference to environment */ - short auto_commit; /* 0 for manual commit */ - unsigned int cur_counter; - sqlite3 *sql_conn; +typedef struct { + short closed; + int env; /* reference to environment */ + short auto_commit; /* 0 for manual commit */ + unsigned int cur_counter; + sqlite3 *sql_conn; } conn_data; -typedef struct -{ - short closed; - int conn; /* reference to connection */ - int numcols; /* number of columns */ - int colnames, coltypes; /* reference to column information tables */ - conn_data *conn_data; /* reference to connection for cursor */ - sqlite3_stmt *sql_vm; +typedef struct { + short closed; + int conn; /* reference to connection */ + int numcols; /* number of columns */ + int colnames, coltypes; /* reference to column information tables */ + conn_data *conn_data; /* reference to connection for cursor */ + sqlite3_stmt *sql_vm; } cur_data; LUASQL_API int luaopen_luasql_sqlite3(lua_State *L); @@ -55,33 +52,36 @@ LUASQL_API int luaopen_luasql_sqlite3(lua_State *L); /* ** Check for valid environment. */ -static env_data *getenvironment(lua_State *L) { - env_data *env = (env_data *)luaL_checkudata(L, 1, LUASQL_ENVIRONMENT_SQLITE); - luaL_argcheck(L, env != NULL, 1, LUASQL_PREFIX"environment expected"); - luaL_argcheck(L, !env->closed, 1, LUASQL_PREFIX"environment is closed"); - return env; +static env_data *getenvironment(lua_State *L) +{ + env_data *env = (env_data *)luaL_checkudata(L, 1, LUASQL_ENVIRONMENT_SQLITE); + luaL_argcheck(L, env != NULL, 1, LUASQL_PREFIX"environment expected"); + luaL_argcheck(L, !env->closed, 1, LUASQL_PREFIX"environment is closed"); + return env; } /* ** Check for valid connection. */ -static conn_data *getconnection(lua_State *L) { - conn_data *conn = (conn_data *)luaL_checkudata (L, 1, LUASQL_CONNECTION_SQLITE); - luaL_argcheck(L, conn != NULL, 1, LUASQL_PREFIX"connection expected"); - luaL_argcheck(L, !conn->closed, 1, LUASQL_PREFIX"connection is closed"); - return conn; +static conn_data *getconnection(lua_State *L) +{ + conn_data *conn = (conn_data *)luaL_checkudata (L, 1, LUASQL_CONNECTION_SQLITE); + luaL_argcheck(L, conn != NULL, 1, LUASQL_PREFIX"connection expected"); + luaL_argcheck(L, !conn->closed, 1, LUASQL_PREFIX"connection is closed"); + return conn; } /* ** Check for valid cursor. */ -static cur_data *getcursor(lua_State *L) { - cur_data *cur = (cur_data *)luaL_checkudata (L, 1, LUASQL_CURSOR_SQLITE); - luaL_argcheck(L, cur != NULL, 1, LUASQL_PREFIX"cursor expected"); - luaL_argcheck(L, !cur->closed, 1, LUASQL_PREFIX"cursor is closed"); - return cur; +static cur_data *getcursor(lua_State *L) +{ + cur_data *cur = (cur_data *)luaL_checkudata (L, 1, LUASQL_CURSOR_SQLITE); + luaL_argcheck(L, cur != NULL, 1, LUASQL_PREFIX"cursor expected"); + luaL_argcheck(L, !cur->closed, 1, LUASQL_PREFIX"cursor is closed"); + return cur; } /* @@ -89,19 +89,19 @@ static cur_data *getcursor(lua_State *L) { */ static void cur_nullify(lua_State *L, cur_data *cur) { - conn_data *conn; - - /* Nullify structure fields. */ - cur->closed = 1; - cur->sql_vm = NULL; - /* Decrement cursor counter on connection object */ - lua_rawgeti (L, LUA_REGISTRYINDEX, cur->conn); - conn = lua_touserdata (L, -1); - conn->cur_counter--; - - luaL_unref(L, LUA_REGISTRYINDEX, cur->conn); - luaL_unref(L, LUA_REGISTRYINDEX, cur->colnames); - luaL_unref(L, LUA_REGISTRYINDEX, cur->coltypes); + conn_data *conn; + + /* Nullify structure fields. */ + cur->closed = 1; + cur->sql_vm = NULL; + /* Decrement cursor counter on connection object */ + lua_rawgeti (L, LUA_REGISTRYINDEX, cur->conn); + conn = lua_touserdata (L, -1); + conn->cur_counter--; + + luaL_unref(L, LUA_REGISTRYINDEX, cur->conn); + luaL_unref(L, LUA_REGISTRYINDEX, cur->colnames); + luaL_unref(L, LUA_REGISTRYINDEX, cur->coltypes); } @@ -109,103 +109,102 @@ static void cur_nullify(lua_State *L, cur_data *cur) ** Finalizes the vm ** Return nil + errmsg or nil in case of sucess */ -static int finalize(lua_State *L, cur_data *cur) { - const char *errmsg; - if (sqlite3_finalize(cur->sql_vm) != SQLITE_OK) - { - errmsg = sqlite3_errmsg(cur->conn_data->sql_conn); - cur_nullify(L, cur); - return luasql_faildirect(L, errmsg); - } - cur_nullify(L, cur); - lua_pushnil(L); - return 1; +static int finalize(lua_State *L, cur_data *cur) +{ + const char *errmsg; + if (sqlite3_finalize(cur->sql_vm) != SQLITE_OK) { + errmsg = sqlite3_errmsg(cur->conn_data->sql_conn); + cur_nullify(L, cur); + return luasql_faildirect(L, errmsg); + } + cur_nullify(L, cur); + lua_pushnil(L); + return 1; } -static void push_column(lua_State *L, sqlite3_stmt *vm, int column) { - switch (sqlite3_column_type(vm, column)) { - case SQLITE_INTEGER: - luasql_pushinteger(L, sqlite3_column_int64(vm, column)); - break; - case SQLITE_FLOAT: - lua_pushnumber(L, sqlite3_column_double(vm, column)); - break; - case SQLITE_TEXT: - lua_pushlstring(L, (const char *)sqlite3_column_text(vm, column), - (size_t)sqlite3_column_bytes(vm, column)); - break; - case SQLITE_BLOB: - lua_pushlstring(L, sqlite3_column_blob(vm, column), - (size_t)sqlite3_column_bytes(vm, column)); - break; - case SQLITE_NULL: - lua_pushnil(L); - break; - default: - luaL_error(L, LUASQL_PREFIX"Unrecognized column type"); - break; - } +static void push_column(lua_State *L, sqlite3_stmt *vm, int column) +{ + switch (sqlite3_column_type(vm, column)) { + case SQLITE_INTEGER: + luasql_pushinteger(L, sqlite3_column_int64(vm, column)); + break; + case SQLITE_FLOAT: + lua_pushnumber(L, sqlite3_column_double(vm, column)); + break; + case SQLITE_TEXT: + lua_pushlstring(L, (const char *)sqlite3_column_text(vm, column), + (size_t)sqlite3_column_bytes(vm, column)); + break; + case SQLITE_BLOB: + lua_pushlstring(L, sqlite3_column_blob(vm, column), + (size_t)sqlite3_column_bytes(vm, column)); + break; + case SQLITE_NULL: + lua_pushnil(L); + break; + default: + luaL_error(L, LUASQL_PREFIX"Unrecognized column type"); + break; + } } /* ** Get another row of the given cursor. */ -static int cur_fetch (lua_State *L) { - cur_data *cur = getcursor(L); - sqlite3_stmt *vm = cur->sql_vm; - int res; - - if (vm == NULL) - return 0; - - res = sqlite3_step(vm); - - /* no more results? */ - if (res == SQLITE_DONE) - return finalize(L, cur); - - if (res != SQLITE_ROW) - return finalize(L, cur); - - if (lua_istable (L, 2)) - { - int i; - const char *opts = luaL_optstring(L, 3, "n"); - - if (strchr(opts, 'n') != NULL) - { - /* Copy values to numerical indices */ - for (i = 0; i < cur->numcols;) - { - push_column(L, vm, i); - lua_rawseti(L, 2, ++i); - } - } - if (strchr(opts, 'a') != NULL) - { - /* Copy values to alphanumerical indices */ - lua_rawgeti(L, LUA_REGISTRYINDEX, cur->colnames); - - for (i = 0; i < cur->numcols; i++) - { - lua_rawgeti(L, -1, i+1); - push_column(L, vm, i); - lua_rawset (L, 2); - } - } - lua_pushvalue(L, 2); - return 1; /* return table */ - } - else - { - int i; - luaL_checkstack (L, cur->numcols, LUASQL_PREFIX"too many columns"); - for (i = 0; i < cur->numcols; ++i) - push_column(L, vm, i); - return cur->numcols; /* return #numcols values */ - } +static int cur_fetch (lua_State *L) +{ + cur_data *cur = getcursor(L); + sqlite3_stmt *vm = cur->sql_vm; + int res; + + if (vm == NULL) { + return 0; + } + + res = sqlite3_step(vm); + + /* no more results? */ + if (res == SQLITE_DONE) { + return finalize(L, cur); + } + + if (res != SQLITE_ROW) { + return finalize(L, cur); + } + + if (lua_istable (L, 2)) { + int i; + const char *opts = luaL_optstring(L, 3, "n"); + + if (strchr(opts, 'n') != NULL) { + /* Copy values to numerical indices */ + for (i = 0; i < cur->numcols;) { + push_column(L, vm, i); + lua_rawseti(L, 2, ++i); + } + } + if (strchr(opts, 'a') != NULL) { + /* Copy values to alphanumerical indices */ + lua_rawgeti(L, LUA_REGISTRYINDEX, cur->colnames); + + for (i = 0; i < cur->numcols; i++) { + lua_rawgeti(L, -1, i+1); + push_column(L, vm, i); + lua_rawset (L, 2); + } + } + lua_pushvalue(L, 2); + return 1; /* return table */ + } else { + int i; + luaL_checkstack (L, cur->numcols, LUASQL_PREFIX"too many columns"); + for (i = 0; i < cur->numcols; ++i) { + push_column(L, vm, i); + } + return cur->numcols; /* return #numcols values */ + } } @@ -214,13 +213,12 @@ static int cur_fetch (lua_State *L) { */ static int cur_gc(lua_State *L) { - cur_data *cur = (cur_data *)luaL_checkudata(L, 1, LUASQL_CURSOR_SQLITE); - if (cur != NULL && !(cur->closed)) - { - sqlite3_finalize(cur->sql_vm); - cur_nullify(L, cur); - } - return 0; + cur_data *cur = (cur_data *)luaL_checkudata(L, 1, LUASQL_CURSOR_SQLITE); + if (cur != NULL && !(cur->closed)) { + sqlite3_finalize(cur->sql_vm); + cur_nullify(L, cur); + } + return 0; } @@ -230,16 +228,16 @@ static int cur_gc(lua_State *L) */ static int cur_close(lua_State *L) { - cur_data *cur = (cur_data *)luaL_checkudata(L, 1, LUASQL_CURSOR_SQLITE); - luaL_argcheck(L, cur != NULL, 1, LUASQL_PREFIX"cursor expected"); - if (cur->closed) { - lua_pushboolean(L, 0); - return 1; - } - sqlite3_finalize(cur->sql_vm); - cur_nullify(L, cur); - lua_pushboolean(L, 1); - return 1; + cur_data *cur = (cur_data *)luaL_checkudata(L, 1, LUASQL_CURSOR_SQLITE); + luaL_argcheck(L, cur != NULL, 1, LUASQL_PREFIX"cursor expected"); + if (cur->closed) { + lua_pushboolean(L, 0); + return 1; + } + sqlite3_finalize(cur->sql_vm); + cur_nullify(L, cur); + lua_pushboolean(L, 1); + return 1; } @@ -248,9 +246,9 @@ static int cur_close(lua_State *L) */ static int cur_getcolnames(lua_State *L) { - cur_data *cur = getcursor(L); - lua_rawgeti(L, LUA_REGISTRYINDEX, cur->colnames); - return 1; + cur_data *cur = getcursor(L); + lua_rawgeti(L, LUA_REGISTRYINDEX, cur->colnames); + return 1; } @@ -259,9 +257,9 @@ static int cur_getcolnames(lua_State *L) */ static int cur_getcoltypes(lua_State *L) { - cur_data *cur = getcursor(L); - lua_rawgeti(L, LUA_REGISTRYINDEX, cur->coltypes); - return 1; + cur_data *cur = getcursor(L); + lua_rawgeti(L, LUA_REGISTRYINDEX, cur->coltypes); + return 1; } @@ -271,46 +269,44 @@ static int cur_getcoltypes(lua_State *L) /* static int create_cursor(lua_State *L, int conn, sqlite3_stmt *sql_vm, int numcols, const char **row, const char **col_info)*/ static int create_cursor(lua_State *L, int o, conn_data *conn, - sqlite3_stmt *sql_vm, int numcols) + sqlite3_stmt *sql_vm, int numcols) { - int i; - cur_data *cur = (cur_data*)lua_newuserdata(L, sizeof(cur_data)); - luasql_setmeta (L, LUASQL_CURSOR_SQLITE); - - /* increment cursor count for the connection creating this cursor */ - conn->cur_counter++; - - /* fill in structure */ - cur->closed = 0; - cur->conn = LUA_NOREF; - cur->numcols = numcols; - cur->colnames = LUA_NOREF; - cur->coltypes = LUA_NOREF; - cur->sql_vm = sql_vm; - cur->conn_data = conn; - - lua_pushvalue(L, o); - cur->conn = luaL_ref(L, LUA_REGISTRYINDEX); - - /* create table with column names */ - lua_newtable(L); - for (i = 0; i < numcols;) - { - lua_pushstring(L, sqlite3_column_name(sql_vm, i)); - lua_rawseti(L, -2, ++i); - } - cur->colnames = luaL_ref(L, LUA_REGISTRYINDEX); - - /* create table with column types */ - lua_newtable(L); - for (i = 0; i < numcols;) - { - lua_pushstring(L, sqlite3_column_decltype(sql_vm, i)); - lua_rawseti(L, -2, ++i); - } - cur->coltypes = luaL_ref(L, LUA_REGISTRYINDEX); - - return 1; + int i; + cur_data *cur = (cur_data *)lua_newuserdata(L, sizeof(cur_data)); + luasql_setmeta (L, LUASQL_CURSOR_SQLITE); + + /* increment cursor count for the connection creating this cursor */ + conn->cur_counter++; + + /* fill in structure */ + cur->closed = 0; + cur->conn = LUA_NOREF; + cur->numcols = numcols; + cur->colnames = LUA_NOREF; + cur->coltypes = LUA_NOREF; + cur->sql_vm = sql_vm; + cur->conn_data = conn; + + lua_pushvalue(L, o); + cur->conn = luaL_ref(L, LUA_REGISTRYINDEX); + + /* create table with column names */ + lua_newtable(L); + for (i = 0; i < numcols;) { + lua_pushstring(L, sqlite3_column_name(sql_vm, i)); + lua_rawseti(L, -2, ++i); + } + cur->colnames = luaL_ref(L, LUA_REGISTRYINDEX); + + /* create table with column types */ + lua_newtable(L); + for (i = 0; i < numcols;) { + lua_pushstring(L, sqlite3_column_decltype(sql_vm, i)); + lua_rawseti(L, -2, ++i); + } + cur->coltypes = luaL_ref(L, LUA_REGISTRYINDEX); + + return 1; } @@ -319,18 +315,18 @@ static int create_cursor(lua_State *L, int o, conn_data *conn, */ static int conn_gc(lua_State *L) { - conn_data *conn = (conn_data *)luaL_checkudata(L, 1, LUASQL_CONNECTION_SQLITE); - if (conn != NULL && !(conn->closed)) - { - if (conn->cur_counter > 0) - return luaL_error (L, LUASQL_PREFIX"there are open cursors"); - - /* Nullify structure fields. */ - conn->closed = 1; - luaL_unref(L, LUA_REGISTRYINDEX, conn->env); - sqlite3_close(conn->sql_conn); - } - return 0; + conn_data *conn = (conn_data *)luaL_checkudata(L, 1, LUASQL_CONNECTION_SQLITE); + if (conn != NULL && !(conn->closed)) { + if (conn->cur_counter > 0) { + return luaL_error (L, LUASQL_PREFIX"there are open cursors"); + } + + /* Nullify structure fields. */ + conn->closed = 1; + luaL_unref(L, LUA_REGISTRYINDEX, conn->env); + sqlite3_close(conn->sql_conn); + } + return 0; } @@ -339,32 +335,28 @@ static int conn_gc(lua_State *L) */ static int conn_close(lua_State *L) { - conn_data *conn = (conn_data *)luaL_checkudata(L, 1, LUASQL_CONNECTION_SQLITE); - luaL_argcheck (L, conn != NULL, 1, LUASQL_PREFIX"connection expected"); - if (conn->closed) - { - lua_pushboolean(L, 0); - return 1; - } - conn_gc(L); - lua_pushboolean(L, 1); - return 1; + conn_data *conn = (conn_data *)luaL_checkudata(L, 1, LUASQL_CONNECTION_SQLITE); + luaL_argcheck (L, conn != NULL, 1, LUASQL_PREFIX"connection expected"); + if (conn->closed) { + lua_pushboolean(L, 0); + return 1; + } + conn_gc(L); + lua_pushboolean(L, 1); + return 1; } static int conn_escape(lua_State *L) { - const char *from = luaL_checklstring (L, 2, 0); - char *escaped = sqlite3_mprintf("%q", from); - if (escaped == NULL) - { - lua_pushnil(L); - } - else - { - lua_pushstring(L, escaped); - sqlite3_free(escaped); - } - return 1; + const char *from = luaL_checklstring (L, 2, 0); + char *escaped = sqlite3_mprintf("%q", from); + if (escaped == NULL) { + lua_pushnil(L); + } else { + lua_pushstring(L, escaped); + sqlite3_free(escaped); + } + return 1; } /* @@ -374,48 +366,45 @@ static int conn_escape(lua_State *L) */ static int conn_execute(lua_State *L) { - conn_data *conn = getconnection(L); - const char *statement = luaL_checkstring(L, 2); - int res; - sqlite3_stmt *vm; - const char *errmsg; - int numcols; - const char *tail; + conn_data *conn = getconnection(L); + const char *statement = luaL_checkstring(L, 2); + int res; + sqlite3_stmt *vm; + const char *errmsg; + int numcols; + const char *tail; #if SQLITE_VERSION_NUMBER > 3006013 - res = sqlite3_prepare_v2(conn->sql_conn, statement, -1, &vm, &tail); + res = sqlite3_prepare_v2(conn->sql_conn, statement, -1, &vm, &tail); #else - res = sqlite3_prepare(conn->sql_conn, statement, -1, &vm, &tail); + res = sqlite3_prepare(conn->sql_conn, statement, -1, &vm, &tail); #endif - if (res != SQLITE_OK) - { - errmsg = sqlite3_errmsg(conn->sql_conn); - return luasql_faildirect(L, errmsg); - } - - /* process first result to retrive query information and type */ - res = sqlite3_step(vm); - numcols = sqlite3_column_count(vm); - - /* real query? if empty, must have numcols!=0 */ - if ((res == SQLITE_ROW) || ((res == SQLITE_DONE) && numcols)) - { - sqlite3_reset(vm); - return create_cursor(L, 1, conn, vm, numcols); - } - - if (res == SQLITE_DONE) /* and numcols==0, INSERT,UPDATE,DELETE statement */ - { - sqlite3_finalize(vm); - /* return number of columns changed */ - lua_pushnumber(L, sqlite3_changes(conn->sql_conn)); - return 1; - } - - /* error */ - errmsg = sqlite3_errmsg(conn->sql_conn); - sqlite3_finalize(vm); - return luasql_faildirect(L, errmsg); + if (res != SQLITE_OK) { + errmsg = sqlite3_errmsg(conn->sql_conn); + return luasql_faildirect(L, errmsg); + } + + /* process first result to retrive query information and type */ + res = sqlite3_step(vm); + numcols = sqlite3_column_count(vm); + + /* real query? if empty, must have numcols!=0 */ + if ((res == SQLITE_ROW) || ((res == SQLITE_DONE) && numcols)) { + sqlite3_reset(vm); + return create_cursor(L, 1, conn, vm, numcols); + } + + if (res == SQLITE_DONE) { /* and numcols==0, INSERT,UPDATE,DELETE statement */ + sqlite3_finalize(vm); + /* return number of columns changed */ + lua_pushnumber(L, sqlite3_changes(conn->sql_conn)); + return 1; + } + + /* error */ + errmsg = sqlite3_errmsg(conn->sql_conn); + sqlite3_finalize(vm); + return luasql_faildirect(L, errmsg); } @@ -424,26 +413,27 @@ static int conn_execute(lua_State *L) */ static int conn_commit(lua_State *L) { - char *errmsg; - conn_data *conn = getconnection(L); - int res; - const char *sql = "COMMIT"; - - if (conn->auto_commit == 0) sql = "COMMIT;BEGIN"; - - res = sqlite3_exec(conn->sql_conn, sql, NULL, NULL, &errmsg); - - if (res != SQLITE_OK) - { - lua_pushnil(L); - lua_pushliteral(L, LUASQL_PREFIX); - lua_pushstring(L, errmsg); - sqlite3_free(errmsg); - lua_concat(L, 2); - return 2; - } - lua_pushboolean(L, 1); - return 1; + char *errmsg; + conn_data *conn = getconnection(L); + int res; + const char *sql = "COMMIT"; + + if (conn->auto_commit == 0) { + sql = "COMMIT;BEGIN"; + } + + res = sqlite3_exec(conn->sql_conn, sql, NULL, NULL, &errmsg); + + if (res != SQLITE_OK) { + lua_pushnil(L); + lua_pushliteral(L, LUASQL_PREFIX); + lua_pushstring(L, errmsg); + sqlite3_free(errmsg); + lua_concat(L, 2); + return 2; + } + lua_pushboolean(L, 1); + return 1; } @@ -452,32 +442,33 @@ static int conn_commit(lua_State *L) */ static int conn_rollback(lua_State *L) { - char *errmsg; - conn_data *conn = getconnection(L); - int res; - const char *sql = "ROLLBACK"; - - if (conn->auto_commit == 0) sql = "ROLLBACK;BEGIN"; - - res = sqlite3_exec(conn->sql_conn, sql, NULL, NULL, &errmsg); - if (res != SQLITE_OK) - { - lua_pushnil(L); - lua_pushliteral(L, LUASQL_PREFIX); - lua_pushstring(L, errmsg); - sqlite3_free(errmsg); - lua_concat(L, 2); - return 2; - } - lua_pushboolean(L, 1); - return 1; + char *errmsg; + conn_data *conn = getconnection(L); + int res; + const char *sql = "ROLLBACK"; + + if (conn->auto_commit == 0) { + sql = "ROLLBACK;BEGIN"; + } + + res = sqlite3_exec(conn->sql_conn, sql, NULL, NULL, &errmsg); + if (res != SQLITE_OK) { + lua_pushnil(L); + lua_pushliteral(L, LUASQL_PREFIX); + lua_pushstring(L, errmsg); + sqlite3_free(errmsg); + lua_concat(L, 2); + return 2; + } + lua_pushboolean(L, 1); + return 1; } static int conn_getlastautoid(lua_State *L) { - conn_data *conn = getconnection(L); - lua_pushnumber(L, sqlite3_last_insert_rowid(conn->sql_conn)); - return 1; + conn_data *conn = getconnection(L); + lua_pushnumber(L, sqlite3_last_insert_rowid(conn->sql_conn)); + return 1; } @@ -488,30 +479,26 @@ static int conn_getlastautoid(lua_State *L) */ static int conn_setautocommit(lua_State *L) { - conn_data *conn = getconnection(L); - if (lua_toboolean(L, 2)) - { - conn->auto_commit = 1; - /* undo active transaction - ignore errors */ - sqlite3_exec(conn->sql_conn, "ROLLBACK", NULL, NULL, NULL); - } - else - { - char *errmsg; - int res; - conn->auto_commit = 0; - res = sqlite3_exec(conn->sql_conn, "BEGIN", NULL, NULL, &errmsg); - if (res != SQLITE_OK) - { - lua_pushliteral(L, LUASQL_PREFIX); - lua_pushstring(L, errmsg); - sqlite3_free(errmsg); - lua_concat(L, 2); - lua_error(L); - } - } - lua_pushboolean(L, 1); - return 1; + conn_data *conn = getconnection(L); + if (lua_toboolean(L, 2)) { + conn->auto_commit = 1; + /* undo active transaction - ignore errors */ + sqlite3_exec(conn->sql_conn, "ROLLBACK", NULL, NULL, NULL); + } else { + char *errmsg; + int res; + conn->auto_commit = 0; + res = sqlite3_exec(conn->sql_conn, "BEGIN", NULL, NULL, &errmsg); + if (res != SQLITE_OK) { + lua_pushliteral(L, LUASQL_PREFIX); + lua_pushstring(L, errmsg); + sqlite3_free(errmsg); + lua_concat(L, 2); + lua_error(L); + } + } + lua_pushboolean(L, 1); + return 1; } @@ -520,18 +507,18 @@ static int conn_setautocommit(lua_State *L) */ static int create_connection(lua_State *L, int env, sqlite3 *sql_conn) { - conn_data *conn = (conn_data*)lua_newuserdata(L, sizeof(conn_data)); - luasql_setmeta(L, LUASQL_CONNECTION_SQLITE); - - /* fill in structure */ - conn->closed = 0; - conn->env = LUA_NOREF; - conn->auto_commit = 1; - conn->sql_conn = sql_conn; - conn->cur_counter = 0; - lua_pushvalue (L, env); - conn->env = luaL_ref (L, LUA_REGISTRYINDEX); - return 1; + conn_data *conn = (conn_data *)lua_newuserdata(L, sizeof(conn_data)); + luasql_setmeta(L, LUASQL_CONNECTION_SQLITE); + + /* fill in structure */ + conn->closed = 0; + conn->env = LUA_NOREF; + conn->auto_commit = 1; + conn->sql_conn = sql_conn; + conn->cur_counter = 0; + lua_pushvalue (L, env); + conn->env = luaL_ref (L, LUA_REGISTRYINDEX); + return 1; } @@ -540,32 +527,32 @@ static int create_connection(lua_State *L, int env, sqlite3 *sql_conn) */ static int env_connect(lua_State *L) { - const char *sourcename; - sqlite3 *conn; - const char *errmsg; - int res; - getenvironment(L); /* validate environment */ + const char *sourcename; + sqlite3 *conn; + const char *errmsg; + int res; + getenvironment(L); /* validate environment */ - sourcename = luaL_checkstring(L, 2); + sourcename = luaL_checkstring(L, 2); #if SQLITE_VERSION_NUMBER > 3006013 - res = sqlite3_open_v2(sourcename, &conn, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); + res = sqlite3_open_v2(sourcename, &conn, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); #else - res = sqlite3_open(sourcename, &conn); + res = sqlite3_open(sourcename, &conn); #endif - if (res != SQLITE_OK) - { - errmsg = sqlite3_errmsg(conn); - luasql_faildirect(L, errmsg); - sqlite3_close(conn); - return 2; - } - - if (lua_isnumber(L, 3)) { - sqlite3_busy_timeout(conn, lua_tonumber(L,3)); /* TODO: remove this */ - } - - return create_connection(L, 1, conn); + if (res != SQLITE_OK) { + errmsg = sqlite3_errmsg(conn); + luasql_faildirect(L, errmsg); + sqlite3_close(conn); + return 2; + } + + if (lua_isnumber(L, 3)) { + sqlite3_busy_timeout(conn, lua_tonumber(L,3)); /* TODO: remove this */ + } + + return create_connection(L, 1, conn); } @@ -574,10 +561,11 @@ static int env_connect(lua_State *L) */ static int env_gc (lua_State *L) { - env_data *env = (env_data *)luaL_checkudata(L, 1, LUASQL_ENVIRONMENT_SQLITE); - if (env != NULL && !(env->closed)) - env->closed = 1; - return 0; + env_data *env = (env_data *)luaL_checkudata(L, 1, LUASQL_ENVIRONMENT_SQLITE); + if (env != NULL && !(env->closed)) { + env->closed = 1; + } + return 0; } @@ -586,15 +574,15 @@ static int env_gc (lua_State *L) */ static int env_close (lua_State *L) { - env_data *env = (env_data *)luaL_checkudata(L, 1, LUASQL_ENVIRONMENT_SQLITE); - luaL_argcheck(L, env != NULL, 1, LUASQL_PREFIX"environment expected"); - if (env->closed) { - lua_pushboolean(L, 0); - return 1; - } - env_gc(L); - lua_pushboolean(L, 1); - return 1; + env_data *env = (env_data *)luaL_checkudata(L, 1, LUASQL_ENVIRONMENT_SQLITE); + luaL_argcheck(L, env != NULL, 1, LUASQL_PREFIX"environment expected"); + if (env->closed) { + lua_pushboolean(L, 0); + return 1; + } + env_gc(L); + lua_pushboolean(L, 1); + return 1; } @@ -614,35 +602,35 @@ static int opts_settimeout (lua_State *L) */ static void create_metatables (lua_State *L) { - struct luaL_Reg environment_methods[] = { - {"__gc", env_gc}, - {"close", env_close}, - {"connect", env_connect}, - {NULL, NULL}, - }; - struct luaL_Reg connection_methods[] = { - {"__gc", conn_gc}, - {"close", conn_close}, - {"escape", conn_escape}, - {"execute", conn_execute}, - {"commit", conn_commit}, - {"rollback", conn_rollback}, - {"setautocommit", conn_setautocommit}, - {"getlastautoid", conn_getlastautoid}, - {NULL, NULL}, - }; - struct luaL_Reg cursor_methods[] = { - {"__gc", cur_gc}, - {"close", cur_close}, - {"getcolnames", cur_getcolnames}, - {"getcoltypes", cur_getcoltypes}, - {"fetch", cur_fetch}, - {NULL, NULL}, - }; - luasql_createmeta(L, LUASQL_ENVIRONMENT_SQLITE, environment_methods); - luasql_createmeta(L, LUASQL_CONNECTION_SQLITE, connection_methods); - luasql_createmeta(L, LUASQL_CURSOR_SQLITE, cursor_methods); - lua_pop (L, 3); + struct luaL_Reg environment_methods[] = { + {"__gc", env_gc}, + {"close", env_close}, + {"connect", env_connect}, + {NULL, NULL}, + }; + struct luaL_Reg connection_methods[] = { + {"__gc", conn_gc}, + {"close", conn_close}, + {"escape", conn_escape}, + {"execute", conn_execute}, + {"commit", conn_commit}, + {"rollback", conn_rollback}, + {"setautocommit", conn_setautocommit}, + {"getlastautoid", conn_getlastautoid}, + {NULL, NULL}, + }; + struct luaL_Reg cursor_methods[] = { + {"__gc", cur_gc}, + {"close", cur_close}, + {"getcolnames", cur_getcolnames}, + {"getcoltypes", cur_getcoltypes}, + {"fetch", cur_fetch}, + {NULL, NULL}, + }; + luasql_createmeta(L, LUASQL_ENVIRONMENT_SQLITE, environment_methods); + luasql_createmeta(L, LUASQL_CONNECTION_SQLITE, connection_methods); + luasql_createmeta(L, LUASQL_CURSOR_SQLITE, cursor_methods); + lua_pop (L, 3); } /* @@ -650,12 +638,12 @@ static void create_metatables (lua_State *L) */ static int create_environment (lua_State *L) { - env_data *env = (env_data *)lua_newuserdata(L, sizeof(env_data)); - luasql_setmeta(L, LUASQL_ENVIRONMENT_SQLITE); + env_data *env = (env_data *)lua_newuserdata(L, sizeof(env_data)); + luasql_setmeta(L, LUASQL_ENVIRONMENT_SQLITE); - /* fill in structure */ - env->closed = 0; - return 1; + /* fill in structure */ + env->closed = 0; + return 1; } @@ -665,13 +653,13 @@ static int create_environment (lua_State *L) */ LUASQL_API int luaopen_luasql_sqlite3(lua_State *L) { - struct luaL_Reg driver[] = { - {"sqlite3", create_environment}, - {NULL, NULL}, - }; - create_metatables (L); - luasql_find_driver_table (L); - luaL_setfuncs (L, driver, 0); - luasql_set_info (L); - return 1; + struct luaL_Reg driver[] = { + {"sqlite3", create_environment}, + {NULL, NULL}, + }; + create_metatables (L); + luasql_find_driver_table (L); + luaL_setfuncs (L, driver, 0); + luasql_set_info (L); + return 1; } From 4f39b913edda324ec9b7565433447cbe9ef6987d Mon Sep 17 00:00:00 2001 From: Scott Morgan Date: Sun, 26 Apr 2015 00:03:51 +0100 Subject: [PATCH 51/64] More tidying Plus, making sure the Firebird drive compiles under Linux --- src/ls_firebird.c | 56 +++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index d268ed7..6f74f86 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -23,15 +23,15 @@ typedef struct { short closed; - ISC_STATUS status_vector[20];/* for error results */ int lock; /* lock count for open connections */ + ISC_STATUS status_vector[20];/* for error results */ } env_data; typedef struct { /* general */ short closed; - env_data *env; /* the DB enviroment this is in */ int lock; /* lock count for open cursors */ + env_data *env; /* the DB enviroment this is in */ int autocommit; /* should each statement be commited */ /* implimentation */ isc_db_handle db; /* the database handle */ @@ -44,15 +44,15 @@ typedef struct { typedef struct { short closed; + int lock; /* lock count for open statements */ env_data *env; /* the DB enviroment this is in */ conn_data *conn; /* the DB connection this cursor is from */ + /* implimentation */ XSQLDA *in_sqlda; /* the parameter data array */ isc_stmt_handle handle; /* the statement handle */ - int - type; /* the statment's type (SELECT, UPDATE, etc...) */ - int lock; /* lock count for open statements */ - unsigned char - hidden; /* statement was used interally i.e. from a + int type; /* the statment's type (SELECT, UPDATE, + etc...) */ + unsigned char hidden; /* statement was used interally i.e. from a direct con:execute */ } stmt_data; @@ -527,14 +527,16 @@ static void parse_params(lua_State *L, stmt_data *stmt, int params) case SQL_LONG: case SQL_SHORT: inum = (ISC_INT64)lua_tonumber(L, -1); - fill_param(var, SQL_INT64+1, (ISC_SCHAR *)&inum, sizeof(ISC_INT64)); + fill_param(var, SQL_INT64+1, (ISC_SCHAR *)&inum, + sizeof(ISC_INT64)); break; case SQL_DOUBLE: case SQL_D_FLOAT: case SQL_FLOAT: fnum = (double)lua_tonumber(L, -1); - fill_param(var, SQL_DOUBLE+1, (ISC_SCHAR *)&fnum, sizeof(double)); + fill_param(var, SQL_DOUBLE+1, (ISC_SCHAR *)&fnum, + sizeof(double)); break; case SQL_TIMESTAMP: @@ -548,7 +550,8 @@ static void parse_params(lua_State *L, stmt_data *stmt, int params) ISC_TIMESTAMP isc_ts; isc_encode_timestamp(tm_time, &isc_ts); - fill_param(var, SQL_TIMESTAMP+1, (ISC_SCHAR *)&isc_ts, sizeof(ISC_TIMESTAMP)); + fill_param(var, SQL_TIMESTAMP+1, (ISC_SCHAR *)&isc_ts, + sizeof(ISC_TIMESTAMP)); } break; case LUA_TSTRING: { @@ -595,8 +598,6 @@ static int conn_prepare (lua_State *L) stmt.env = conn->env; stmt.conn = conn; - stmt.handle = NULL; - /* create a statement to handle the query */ isc_dsql_allocate_statement(conn->env->status_vector, &conn->db, &stmt.handle); if ( CHECK_DB_ERROR(conn->env->status_vector) ) { @@ -691,8 +692,8 @@ static int raw_execute (lua_State *L, int stmt_indx) snprintf(cur_name, sizeof(cur_name), "dyn_cursor_%p", (void *)stmt); /* open the cursor ready for fetch cycles */ - isc_dsql_set_cursor_name(cur.env->status_vector, &cur.stmt->handle, cur_name, - 0); + isc_dsql_set_cursor_name(cur.env->status_vector, &cur.stmt->handle, + cur_name, 0); if ( CHECK_DB_ERROR(cur.env->status_vector) ) { lua_pop(L, 1); /* the userdata */ free_cur(&cur); @@ -715,7 +716,8 @@ static int raw_execute (lua_State *L, int stmt_indx) ISC_SHORT n = cur.out_sqlda->sqld; free_xsqlda(cur.out_sqlda); cur.out_sqlda = malloc_xsqlda(n); - isc_dsql_describe(cur.env->status_vector, &cur.stmt->handle, 1, cur.out_sqlda); + isc_dsql_describe(cur.env->status_vector, &cur.stmt->handle, 1, + cur.out_sqlda); if ( CHECK_DB_ERROR(cur.env->status_vector) ) { free_cur(&cur); return return_db_error(L, cur.env->status_vector); @@ -737,7 +739,8 @@ static int raw_execute (lua_State *L, int stmt_indx) } else { /* a count */ /* if autocommit is set, commit change */ if(cur.stmt->conn->autocommit) { - isc_commit_retaining(cur.env->status_vector, &cur.stmt->conn->transaction); + isc_commit_retaining(cur.env->status_vector, + &cur.stmt->conn->transaction); if ( CHECK_DB_ERROR(cur.env->status_vector) ) { free_cur(&cur); return return_db_error(L, cur.env->status_vector); @@ -752,7 +755,8 @@ static int raw_execute (lua_State *L, int stmt_indx) luasql_pushinteger(L, count); /* totaly finished with the cursor */ - isc_dsql_free_statement(cur.env->status_vector, &cur.stmt->handle, DSQL_close); + isc_dsql_free_statement(cur.env->status_vector, &cur.stmt->handle, + DSQL_close); free_cur(&cur); } @@ -913,9 +917,11 @@ static int conn_gc (lua_State *L) if(conn->closed == 0) { if(conn->autocommit != 0) { - isc_commit_transaction(conn->env->status_vector, &conn->transaction); + isc_commit_transaction(conn->env->status_vector, + &conn->transaction); } else { - isc_rollback_transaction(conn->env->status_vector, &conn->transaction); + isc_rollback_transaction(conn->env->status_vector, + &conn->transaction); } isc_detach_database(conn->env->status_vector, &conn->db); @@ -1151,8 +1157,8 @@ static int cur_fetch (lua_State *L) int num = strchr(opts, 'n') != NULL; int alpha = strchr(opts, 'a') != NULL; - if ((fetch_stat = isc_dsql_fetch(cur->env->status_vector, &cur->stmt->handle, 1, - cur->out_sqlda)) == 0) { + if ((fetch_stat = isc_dsql_fetch(cur->env->status_vector, &cur->stmt->handle, + 1, cur->out_sqlda)) == 0) { if (lua_istable (L, 2)) { /* remove the option string */ lua_settop(L, 2); @@ -1283,17 +1289,11 @@ static int cur_gc (lua_State *L) */ static int create_environment (lua_State *L) { - int i; env_data *env; env = (env_data *)lua_newuserdata (L, sizeof (env_data)); luasql_setmeta (L, LUASQL_ENVIRONMENT_FIREBIRD); - /* fill in structure */ - for(i=0; i<20; i++) { - env->status_vector[i] = 0; - } - env->closed = 0; - env->lock = 0; + memset (env, 0, sizeof (env_data)); return 1; } From 2c99c565532c8a55dacba0b5657b2048f812cd41 Mon Sep 17 00:00:00 2001 From: Scott Morgan Date: Sun, 26 Apr 2015 00:26:57 +0100 Subject: [PATCH 52/64] Created statement object for MySQL driver Basic struct and metadata entries --- src/ls_mysql.c | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/src/ls_mysql.c b/src/ls_mysql.c index fb7adb8..cc3016e 100644 --- a/src/ls_mysql.c +++ b/src/ls_mysql.c @@ -31,6 +31,7 @@ #define LUASQL_ENVIRONMENT_MYSQL "MySQL environment" #define LUASQL_CONNECTION_MYSQL "MySQL connection" +#define LUASQL_STATEMENT_MYSQL "MySQL statement" #define LUASQL_CURSOR_MYSQL "MySQL cursor" /* For compat with old version 4.0 */ @@ -66,20 +67,29 @@ #endif typedef struct { - short closed; + short closed; + int lock; /* lock count for open connections */ } env_data; typedef struct { - short closed; - int env; /* reference to environment */ - MYSQL *my_conn; + short closed; + int lock; /* lock count for open cursors */ + int env; /* reference to environment */ + MYSQL *my_conn; } conn_data; typedef struct { - short closed; - int conn; /* reference to connection */ - int numcols; /* number of columns */ - int colnames, coltypes; /* reference to column information tables */ + short closed; + int lock; /* lock count for open statements */ + unsigned char hidden; /* statement was used interally i.e. from a + direct con:execute */ +} stmt_data; + +typedef struct { + short closed; + int conn; /* reference to connection */ + int numcols; /* number of columns */ + int colnames, coltypes; /* reference to column information tables */ MYSQL_RES *my_res; } cur_data; @@ -110,6 +120,17 @@ static conn_data *getconnection (lua_State *L) } +/* +** Check for valid statement. +*/ +static stmt_data *getstatement (lua_State *L) +{ + stmt_data *stmt = (stmt_data *)luaL_checkudata (L, 1, LUASQL_STATEMENT_MYSQL); + luaL_argcheck (L, stmt != NULL, 1, "statement expected"); + luaL_argcheck (L, !stmt->closed, 1, "statement is closed"); + return stmt; +} + /* ** Check for valid cursor. */ @@ -662,6 +683,9 @@ static void create_metatables (lua_State *L) {"getlastautoid", conn_getlastautoid}, {NULL, NULL}, }; + struct luaL_Reg statement_methods[] = { + {NULL, NULL}, + }; struct luaL_Reg cursor_methods[] = { {"__gc", cur_gc}, {"close", cur_close}, @@ -673,8 +697,9 @@ static void create_metatables (lua_State *L) }; luasql_createmeta (L, LUASQL_ENVIRONMENT_MYSQL, environment_methods); luasql_createmeta (L, LUASQL_CONNECTION_MYSQL, connection_methods); + luasql_createmeta (L, LUASQL_STATEMENT_MYSQL, statement_methods); luasql_createmeta (L, LUASQL_CURSOR_MYSQL, cursor_methods); - lua_pop (L, 3); + lua_pop (L, 4); } From 1a6e57a80d50d01949a79b41b71d145efef98124 Mon Sep 17 00:00:00 2001 From: blumf Date: Mon, 27 Apr 2015 13:57:21 +0100 Subject: [PATCH 53/64] Sorted out differences between driver reg on 5.1/5.3 There are different ways to register libs on Lua between 5.1 and 5.3. Lua 5.1 support is handy because LuaJIT stuck with the 5.1 API --- src/ls_firebird.c | 6 ++--- src/ls_mysql.c | 6 ++--- src/ls_odbc.c | 6 ++--- src/luasql.c | 67 +++++++++++++++++++++++++++++------------------ src/luasql.h | 3 ++- 5 files changed, 52 insertions(+), 36 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 6f74f86..35eefd5 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -1542,9 +1542,9 @@ LUASQL_API int luaopen_luasql_firebird (lua_State *L) {"firebird", create_environment}, {NULL, NULL}, }; + create_metatables (L); - luasql_find_driver_table (L); - luaL_setfuncs (L, driver, 0); - luasql_set_info (L); + luasql_reg_driver(L, driver); + return 1; } diff --git a/src/ls_mysql.c b/src/ls_mysql.c index cc3016e..03f56b4 100644 --- a/src/ls_mysql.c +++ b/src/ls_mysql.c @@ -728,11 +728,11 @@ LUASQL_API int luaopen_luasql_mysql (lua_State *L) {NULL, NULL}, }; create_metatables (L); - luasql_find_driver_table(L); - luaL_setfuncs(L, driver, 0); - luasql_set_info (L); + luasql_reg_driver(L, driver); + lua_pushliteral (L, "_MYSQLVERSION"); lua_pushliteral (L, MYSQL_SERVER_VERSION); lua_settable (L, -3); + return 1; } diff --git a/src/ls_odbc.c b/src/ls_odbc.c index 55a151e..28a1071 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -1270,9 +1270,9 @@ LUASQL_API int luaopen_luasql_odbc (lua_State *L) {"odbc", create_environment}, {NULL, NULL}, }; + create_metatables (L); - luasql_find_driver_table (L); - luaL_setfuncs (L, driver, 0); - luasql_set_info (L); + luasql_reg_driver(L, driver); + return 1; } diff --git a/src/luasql.c b/src/luasql.c index 531fcb9..7a0d926 100644 --- a/src/luasql.c +++ b/src/luasql.c @@ -133,32 +133,6 @@ LUASQL_API void luasql_set_info (lua_State *L) { lua_settable (L, -3); } - -LUASQL_API const char* luasql_table_optstring(lua_State *L, int idx, const char* name, const char* def) { - const char* res = NULL; - - lua_pushstring(L, name); - lua_gettable(L, idx); - - res = lua_tostring(L, -1); - lua_pop(L, 1); - - return (res != NULL) ? res : def; -} - - -LUASQL_API lua_Number luasql_table_optnumber(lua_State *L, int idx, const char* name, lua_Number def) { - lua_Number res = 0; - - lua_pushstring(L, name); - lua_gettable(L, idx); - - res = lua_tonumber(L, -1); - lua_pop(L, 1); - - return lua_isnumber(L, -1) ? res : def; -} - /* ** Finds a pre-existing LuaSQL table, or creates a new one. */ @@ -187,6 +161,47 @@ LUASQL_API void luasql_find_driver_table (lua_State *L) { lua_newtable (L); } +/* +** registers a driver, taking account of the Lua version differences +** Lua Returns: +** The new/existing 'luasql' driver table on the top of the stack +*/ +LUASQL_API void luasql_reg_driver (lua_State *L, const luaL_Reg *driver) +{ +#if LUA_VERSION_NUM<=501 + luaL_register (L, LUASQL_TABLENAME, driver); +#else + luasql_find_driver_table (L); + luaL_setfuncs (L, driver, 0); +#endif + luasql_set_info (L); +} + +LUASQL_API const char* luasql_table_optstring(lua_State *L, int idx, const char* name, const char* def) { + const char* res = NULL; + + lua_pushstring(L, name); + lua_gettable(L, idx); + + res = lua_tostring(L, -1); + lua_pop(L, 1); + + return (res != NULL) ? res : def; +} + + +LUASQL_API lua_Number luasql_table_optnumber(lua_State *L, int idx, const char* name, lua_Number def) { + lua_Number res = 0; + + lua_pushstring(L, name); + lua_gettable(L, idx); + + res = lua_tonumber(L, -1); + lua_pop(L, 1); + + return lua_isnumber(L, -1) ? res : def; +} + /* ** Registers a given C object in the registry to avoid GC */ diff --git a/src/luasql.h b/src/luasql.h index 7cbe90a..4421e09 100644 --- a/src/luasql.h +++ b/src/luasql.h @@ -34,11 +34,12 @@ LUASQL_API int luasql_failmsg (lua_State *L, const char *err, const char *m); LUASQL_API int luasql_createmeta (lua_State *L, const char *name, const luaL_Reg *methods); LUASQL_API void luasql_setmeta (lua_State *L, const char *name); LUASQL_API void luasql_set_info (lua_State *L); +LUASQL_API void luasql_find_driver_table (lua_State *L); +LUASQL_API void luasql_reg_driver (lua_State *L, const luaL_Reg *driver); LUASQL_API const char* luasql_table_optstring(lua_State *L, int idx, const char* name, const char* def); LUASQL_API lua_Number luasql_table_optnumber(lua_State *L, int idx, const char* name, lua_Number def); -LUASQL_API void luasql_find_driver_table (lua_State *L); void luasql_registerobj(lua_State *L, int index, void *obj); void luasql_unregisterobj(lua_State *L, void *obj); From 66d924daab7a59aed2ed5796fb1013cf0a754fa2 Mon Sep 17 00:00:00 2001 From: blumf Date: Mon, 27 Apr 2015 17:07:37 +0100 Subject: [PATCH 54/64] Using wrong count in XSQLDA field --- src/ls_firebird.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 35eefd5..acb15c3 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -181,7 +181,7 @@ static void free_sqlda_vars(XSQLDA *sqlda) XSQLVAR *var; if(sqlda != NULL) { - for (i=0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++) { + for (i=0, var = sqlda->sqlvar; i < sqlda->sqln; i++, var++) { free(var->sqldata); free(var->sqlind); } From 81eb6c1679cab085f960aa7ebeb2385ac446338f Mon Sep 17 00:00:00 2001 From: blumf Date: Thu, 30 Apr 2015 15:25:50 +0100 Subject: [PATCH 55/64] ODBC function to list available drivers Handy to be able to find out what's available to use --- src/ls_odbc.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index 28a1071..cbed050 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -85,7 +85,7 @@ typedef struct { static int error(SQLRETURN a) { - return (a != SQL_SUCCESS) && (a != SQL_SUCCESS_WITH_INFO); + return (a != SQL_SUCCESS) && (a != SQL_SUCCESS_WITH_INFO) && (a != SQL_NO_DATA); } LUASQL_API int luaopen_luasql_odbc (lua_State *L); @@ -1161,6 +1161,94 @@ static int env_connect (lua_State *L) return create_connection (L, 1, hdbc); } +static int split_strnull_attr_list(lua_State *L, const char* str) +{ + const char *end = str; + const char *equ = str; + + lua_newtable(L); + + while(*str != '\0') { + while(*end != '\0') { + if(*end == '=') { + equ = end; + } + ++end; + } + + lua_pushlstring(L, str, equ-str); + lua_pushstring(L, equ + 1); + lua_settable(L, -3); + + str = ++end; + } + + return 1; +} + +/* +** Lists avilable ODBC drivers +*/ +static int env_drivers (lua_State *L) +{ + env_data *env = getenvironment(L, 1); + SQLCHAR *driver = NULL; + SQLCHAR *attr = NULL; + SQLSMALLINT attr_max = 5, driver_max = 5; + SQLSMALLINT attr_req, driver_req; + SQLRETURN ret; + int i = 1; + + /* pre-flight to get max sizes */ + if(error(ret = SQLDrivers(env->henv, SQL_FETCH_FIRST, + NULL, driver_max, &driver_req, + NULL, attr_max, &attr_req) ) ) + { + return fail(L, hENV, env->henv); + } + + do { + if(driver_req > driver_max) { + driver_max = driver_req; + } + if(attr_req > attr_max) { + attr_max = attr_req; + } + } while(SQL_SUCCEEDED(ret = SQLDrivers(env->henv, SQL_FETCH_NEXT, + NULL, driver_max, &driver_req, + NULL, attr_max, &attr_req) )); + if(error(ret)) { + return fail(L, hENV, env->henv); + } + + /* alloc the buffers and pull in the data */ + driver = (SQLCHAR *)malloc(++driver_max); + attr = (SQLCHAR *)malloc(++attr_max); + + lua_newtable(L); + while(SQL_SUCCEEDED(ret = SQLDrivers(env->henv, SQL_FETCH_NEXT, + driver, driver_max, &driver_req, + attr, attr_max, &attr_req) ) ) + { + lua_newtable(L); + lua_pushstring(L, (const char *)driver); + lua_setfield(L, -2, "driver"); + split_strnull_attr_list(L, (const char *)attr); + lua_setfield(L, -2, "attr"); + + lua_rawseti(L, -2, i++); + } + + free(driver); + free(attr); + + if(error(ret)) { + return fail(L, hENV, env->henv); + } + + return 1; +} + /* ** Closes an environment object */ @@ -1196,6 +1284,7 @@ static void create_metatables (lua_State *L) {"__gc", env_close}, /* Should this method be changed? */ {"close", env_close}, {"connect", env_connect}, + {"drivers", env_drivers}, {NULL, NULL}, }; struct luaL_Reg connection_methods[] = { From 817e8ad1dcc154c1a8c183d7ecb1a965c5d72291 Mon Sep 17 00:00:00 2001 From: Scott Morgan Date: Sat, 2 May 2015 23:48:40 +0100 Subject: [PATCH 56/64] More tidying --- src/ls_mysql.c | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/ls_mysql.c b/src/ls_mysql.c index 03f56b4..9c695af 100644 --- a/src/ls_mysql.c +++ b/src/ls_mysql.c @@ -16,7 +16,7 @@ #define NO_CLIENT_LONG_LONG #endif -#include "mysql.h" +#include #include "lua.h" #include "lauxlib.h" @@ -161,7 +161,6 @@ static void pushvalue (lua_State *L, void *row, long int len) */ static char *getcolumntype (enum enum_field_types type) { - switch (type) { case MYSQL_TYPE_VAR_STRING: case MYSQL_TYPE_STRING: @@ -252,11 +251,13 @@ static int cur_fetch (lua_State *L) MYSQL_RES *res = cur->my_res; unsigned long *lengths; MYSQL_ROW row = mysql_fetch_row(res); + if (row == NULL) { cur_nullify (L, cur); lua_pushnil(L); /* no more results */ return 1; } + lengths = mysql_fetch_lengths(res); if (lua_istable (L, 2)) { @@ -377,7 +378,7 @@ static int cur_getcoltypes (lua_State *L) */ static int cur_numrows (lua_State *L) { - lua_pushnumber (L, (lua_Number)mysql_num_rows (getcursor(L)->my_res)); + luasql_pushinteger (L, mysql_num_rows (getcursor(L)->my_res)); return 1; } @@ -406,7 +407,7 @@ static int create_cursor (lua_State *L, int conn, MYSQL_RES *result, int cols) static int conn_gc (lua_State *L) { - conn_data *conn=(conn_data *)luaL_checkudata(L, 1, LUASQL_CONNECTION_MYSQL); + conn_data *conn = (conn_data *)luaL_checkudata (L, 1, LUASQL_CONNECTION_MYSQL); if (conn != NULL && !(conn->closed)) { /* Nullify structure fields. */ conn->closed = 1; @@ -494,6 +495,7 @@ static int conn_execute (lua_State *L) static int conn_commit (lua_State *L) { conn_data *conn = getconnection (L); + lua_pushboolean(L, !mysql_commit(conn->my_conn)); return 1; } @@ -505,6 +507,7 @@ static int conn_commit (lua_State *L) static int conn_rollback (lua_State *L) { conn_data *conn = getconnection (L); + lua_pushboolean(L, !mysql_rollback(conn->my_conn)); return 1; } @@ -516,11 +519,9 @@ static int conn_rollback (lua_State *L) static int conn_setautocommit (lua_State *L) { conn_data *conn = getconnection (L); - if (lua_toboolean (L, 2)) { - mysql_autocommit(conn->my_conn, 1); /* Set it ON */ - } else { - mysql_autocommit(conn->my_conn, 0); - } + + mysql_autocommit(conn->my_conn, lua_toboolean (L, 2) ? 1 : 0); + lua_pushboolean(L, 1); return 1; } @@ -531,25 +532,26 @@ static int conn_setautocommit (lua_State *L) */ static int conn_getlastautoid (lua_State *L) { - conn_data *conn = getconnection(L); - luasql_pushinteger(L, mysql_insert_id(conn->my_conn)); + conn_data *conn = getconnection (L); + luasql_pushinteger (L, mysql_insert_id(conn->my_conn)); return 1; } /* ** Create a new Connection object and push it on top of the stack. */ -static int create_connection (lua_State *L, int env, MYSQL *const my_conn) +static int create_connection (lua_State *L, int env, MYSQL *my_conn) { - conn_data *conn = (conn_data *)lua_newuserdata(L, sizeof(conn_data)); - luasql_setmeta (L, LUASQL_CONNECTION_MYSQL); + conn_data *conn = getconnection (L); /* fill in structure */ conn->closed = 0; conn->env = LUA_NOREF; conn->my_conn = my_conn; + lua_pushvalue (L, env); conn->env = luaL_ref (L, LUA_REGISTRYINDEX); + return 1; } @@ -597,15 +599,15 @@ static int env_connect (lua_State *L) getenvironment(L); /* validade environment */ - if(lua_gettop(L) < 2) { + if (lua_gettop(L) < 2) { return luasql_faildirect(L, "No connection details provided"); } - if(!lua_istable(L, 2)) { + if (!lua_istable(L, 2)) { env_connect_fix_old(L); } - if((sourcename = luasql_table_optstring(L, 2, "source", NULL)) == NULL) { + if ((sourcename = luasql_table_optstring(L, 2, "source", NULL)) == NULL) { return luasql_faildirect(L, "connection details table missing 'source'"); } @@ -636,10 +638,12 @@ static int env_connect (lua_State *L) */ static int env_gc (lua_State *L) { - env_data *env= (env_data *)luaL_checkudata (L, 1, LUASQL_ENVIRONMENT_MYSQL); + env_data *env = getenvironment(L); + if (env != NULL && !(env->closed)) { env->closed = 1; } + return 0; } @@ -649,13 +653,15 @@ static int env_gc (lua_State *L) */ static int env_close (lua_State *L) { - env_data *env= (env_data *)luaL_checkudata (L, 1, LUASQL_ENVIRONMENT_MYSQL); - luaL_argcheck (L, env != NULL, 1, LUASQL_PREFIX"environment expected"); + env_data *env = getenvironment(L); + if (env->closed) { lua_pushboolean (L, 0); return 1; } + env->closed = 1; + lua_pushboolean (L, 1); return 1; } From f077127b530e321280052773852d6218fd57f13b Mon Sep 17 00:00:00 2001 From: blumf Date: Sun, 3 May 2015 01:48:57 +0100 Subject: [PATCH 57/64] Progress on the MySQL driver statement support --- src/ls_mysql.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/src/ls_mysql.c b/src/ls_mysql.c index 03f56b4..b7311be 100644 --- a/src/ls_mysql.c +++ b/src/ls_mysql.c @@ -83,6 +83,7 @@ typedef struct { int lock; /* lock count for open statements */ unsigned char hidden; /* statement was used interally i.e. from a direct con:execute */ + MYSQL_STMT *my_stmt; /* statement handle */ } stmt_data; typedef struct { @@ -90,7 +91,7 @@ typedef struct { int conn; /* reference to connection */ int numcols; /* number of columns */ int colnames, coltypes; /* reference to column information tables */ - MYSQL_RES *my_res; + MYSQL_RES *my_res; } cur_data; LUASQL_API int luaopen_luasql_mysql (lua_State *L); @@ -142,6 +143,18 @@ static cur_data *getcursor (lua_State *L) return cur; } +/* +** Shut an active statement object +*/ +static int stmt_shut(lua_State *L, stmt_data *stmt) +{ + if(!stmt->closed) { + mysql_stmt_close(stmt->my_stmt); + stmt->closed = 1; + } + + return 0; +} /* ** Push the value of #i field of #tuple row. @@ -403,6 +416,26 @@ static int create_cursor (lua_State *L, int conn, MYSQL_RES *result, int cols) return 1; } +static int stmt_gc (lua_State *L) +{ + stmt_shut(L, getstatement(L)); + + return 0; +} + +static int stmt_close(lua_State *L) +{ + stmt_data *stmt = getstatement(L); + if (stmt->closed) { + lua_pushboolean (L, 0); + return 1; + } + + stmt_shut(L, stmt); + + lua_pushboolean (L, 1); + return 1; +} static int conn_gc (lua_State *L) { @@ -451,6 +484,55 @@ static int escape_string (lua_State *L) return 0; } +/* +** Prepares an SQL statement +** Returns a Statement object +*/ +static int conn_prepare (lua_State *L) +{ + conn_data *conn = getconnection (L); + size_t st_len; + const char *statement = luaL_checklstring (L, 2, &st_len); + stmt_data *stmt = (stmt_data *)lua_newuserdata(L, sizeof(stmt_data)); + + memset(stmt, 0, sizeof(stmt_data)); + luasql_setmeta (L, LUASQL_STATEMENT_MYSQL); + + stmt->my_stmt = mysql_stmt_init(conn->my_conn); + + if(stmt->my_stmt == NULL) { + return luasql_failmsg(L, "error preparing query. MySQL: ", + "out of memory"); + } + + if(mysql_stmt_prepare(stmt->my_stmt, statement, st_len) < 0) { + return luasql_failmsg(L, "error preparing query. MySQL: ", + mysql_stmt_error(stmt->my_stmt)); + } + + return 1; +} + +static int stmt_execute(lua_State *L) +{ + stmt_data *stmt = getstatement(L); + + if(mysql_stmt_execute(stmt->my_stmt) < 0) { + return luasql_failmsg(L, "error executing statement. MySQL: ", + mysql_stmt_error(stmt->my_stmt)); + } + + if(mysql_stmt_field_count(stmt->my_stmt) != 0) { + /* SELECT */ + /* TODO: create cursor here */ + return 0; + } else { + /* INSERT, DELETE, UPDATE, etc */ + luasql_pushinteger(L, mysql_stmt_affected_rows(stmt->my_stmt)); + return 1; + } +} + /* ** Execute an SQL statement. ** Return a Cursor object if the statement is a query, otherwise @@ -676,6 +758,7 @@ static void create_metatables (lua_State *L) {"__gc", conn_gc}, {"close", conn_close}, {"escape", escape_string}, + {"prepare", conn_prepare}, {"execute", conn_execute}, {"commit", conn_commit}, {"rollback", conn_rollback}, @@ -684,6 +767,8 @@ static void create_metatables (lua_State *L) {NULL, NULL}, }; struct luaL_Reg statement_methods[] = { + {"__gc", stmt_gc}, + {"close", stmt_close}, {NULL, NULL}, }; struct luaL_Reg cursor_methods[] = { From c5ad4a39dd38ff10981f4f8bd0365578535c2156 Mon Sep 17 00:00:00 2001 From: Scott Morgan Date: Mon, 4 May 2015 23:43:02 +0100 Subject: [PATCH 58/64] Added includes to luasql.h Technically these should been here for the function decs --- src/luasql.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/luasql.h b/src/luasql.h index 4421e09..7f8631b 100644 --- a/src/luasql.h +++ b/src/luasql.h @@ -3,6 +3,9 @@ ** See Copyright Notice in license.html */ +#include +#include + #ifndef _LUASQL_ #define _LUASQL_ From 0a2c974b40441a93180a39b52d9f9b07bc53cddd Mon Sep 17 00:00:00 2001 From: Scott Morgan Date: Mon, 4 May 2015 23:46:33 +0100 Subject: [PATCH 59/64] Improved *nix makefile The old version was a bit clunky; required editing the config file to compile different drivers, and didn't support passing alt parameters (include/lib dirs) from the command line. --- Makefile | 36 ++++++++++--------- config | 108 ++++++++++++++++++++++++------------------------------- 2 files changed, 66 insertions(+), 78 deletions(-) diff --git a/Makefile b/Makefile index 79d53a3..a5b148b 100644 --- a/Makefile +++ b/Makefile @@ -3,35 +3,37 @@ CONFIG= ./config include $(CONFIG) -OBJS= src/luasql.o src/ls_$T.o +OBJS= src/luasql.o +SRCS= src/luasql.h src/luasql.c +# list of all driver names +DRIVER_LIST= $(subst src/ls_,,$(basename $(wildcard src/ls_*.c))) -SRCS= src/luasql.h src/luasql.c \ - src/ls_firebird.c \ - src/ls_postgres.c \ - src/ls_odbc.c \ - src/ls_oci8.c \ - src/ls_mysql.c \ - src/ls_sqlite.c \ - src/ls_sqlite3.c +# used for help formatting +EMPTY= +SPACE= $(EMPTY) $(EMPTY) -AR= ar rcu -RANLIB= ranlib +all : + @echo "usage: make { $(subst $(SPACE),$(SPACE)|$(SPACE),$(DRIVER_LIST)) }" +# explicity matches against the list of avilable driver names +$(DRIVER_LIST) : % : src/%.so -lib: src/$(LIBNAME) +# builds the specified driver +src/%.so : src/ls_%.c $(OBJS) + $(CC) $(CFLAGS) src/ls_$*.c -o $@ $(LIB_OPTION) $(OBJS) $(DRIVER_INCS_$*) $(DRIVER_LIBS_$*) -src/$(LIBNAME): $(OBJS) - export MACOSX_DEPLOYMENT_TARGET="10.3"; $(CC) $(CFLAGS) -o $@ $(LIB_OPTION) $(OBJS) $(DRIVER_LIBS) +# builds the general LuaSQL functions +$(OBJS) : $(SRCS) + $(CC) $(CFLAGS) -c src/luasql.c -o src/luasql.o install: mkdir -p $(LUA_LIBDIR)/luasql - cp src/$(LIBNAME) $(LUA_LIBDIR)/luasql + cp src/*.so $(LUA_LIBDIR)/luasql jdbc_driver: cd src/jdbc; make $@ clean: - rm -f src/$(LIBNAME) src/*.o + rm -f src/*.so src/*.o -# $Id: Makefile,v 1.56 2008/05/30 17:21:18 tomas Exp $ diff --git a/config b/config index 7798d7f..1bbf9df 100644 --- a/config +++ b/config @@ -1,68 +1,54 @@ -# $Id: config,v 1.10 2008/05/30 17:21:18 tomas Exp $ - -# Driver (leave uncommented ONLY the line with the name of the driver) -T= mysql -#T= oci8 -#T= odbc -#T= postgres -#T= sqlite -#T=sqlite3 -#T=firebird - # Installation directories # Default prefix -PREFIX = /usr - -# System's libraries directory (where binary libraries are installed) -LUA_LIBDIR= $(PREFIX)/lib/lua/5.2 - -# System's lua directory (where Lua libraries are installed) -LUA_DIR= $(PREFIX)/share/lua/5.2 - -# Lua includes directory -LUA_INC= $(PREFIX)/include/lua5.2 +PREFIX ?= /usr -# Lua version number (first and second digits of target version) -LUA_VERSION_NUM= 502 +# Lua version and dirs +LUA_SYS_VER ?= 5.2 +LUA_LIBDIR ?= $(PREFIX)/lib/lua/$(LUA_SYS_VER) +LUA_DIR ?= $(PREFIX)/share/lua/$(LUA_SYS_VER) +LUA_INC ?= $(PREFIX)/include/lua$(LUA_SYS_VER) # OS dependent -LIB_OPTION= -shared #for Linux -#LIB_OPTION= -bundle -undefined dynamic_lookup #for MacOS X - -LIBNAME= $T.so - -# Compilation parameters -# Driver specific -######## MySQL -#DRIVER_LIBS= -L/usr/local/mysql/lib -lmysqlclient -lz -#DRIVER_INCS= -I/usr/local/mysql/include -DRIVER_LIBS= -L/usr/lib -lmysqlclient -lz -DRIVER_INCS= -I/usr/include/mysql -######## Oracle OCI8 -#DRIVER_LIBS= -L/home/oracle/OraHome1/lib -lz -lclntsh -#DRIVER_INCS= -I/home/oracle/OraHome1/rdbms/demo -I/home/oracle/OraHome1/rdbms/public -######## PostgreSQL -#DRIVER_LIBS= -L/usr/local/pgsql/lib -lpq -#DRIVER_INCS= -I/usr/local/pgsql/include/ -#DRIVER_LIBS= -L/usr/lib -lpq -#DRIVER_INCS= -I/usr/include/postgresql -######## SQLite -#DRIVER_LIBS= -lsqlite -#DRIVER_INCS= -######## SQLite3 -#DRIVER_LIBS= -L/opt/local/lib -lsqlite3 -#DRIVER_INCS= -I/opt/local/include -######## ODBC -#DRIVER_LIBS= -L/usr/local/lib -lodbc -#DRIVER_INCS= -DUNIXODBC -I/usr/local/include -######## Firebird -#DRIVER_LIBS= -L/usr/local/firebird -lfbclient -#DRIVER_INCS= - -WARN= -Wall -Wmissing-prototypes -Wmissing-declarations -pedantic -INCS= -I$(LUA_INC) -CFLAGS= -O2 -std=gnu99 $(WARN) -fPIC $(DRIVER_INCS) $(INCS) -DLUASQL_VERSION_NUMBER='"$V"' $(DEFS) +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Darwin) # MacOS + LIB_OPTION ?= -bundle -undefined dynamic_lookup -mmacosx-version-min=10.3 +else # Linux/BSD + LIB_OPTION ?= -shared +endif + +# driver specific params +# - MySQL +#DRIVER_LIBS_mysql ?= -L/usr/local/mysql/lib -lmysqlclient -lz +#DRIVER_INCS_mysql ?= -I/usr/local/mysql/include +DRIVER_LIBS_mysql ?= -L/usr/lib -lmysqlclient -lz +DRIVER_INCS_mysql ?= -I/usr/include/mysql +# - Oracle OCI8 +DRIVER_LIBS_oci8 ?= -L/home/oracle/OraHome1/lib -lz -lclntsh +DRIVER_INCS_oci8 ?= -I/home/oracle/OraHome1/rdbms/demo \ + -I/home/oracle/OraHome1/rdbms/public +# - PostgreSQL +#DRIVER_LIBS_postgres ?= -L/usr/local/pgsql/lib -lpq +#DRIVER_INCS_postgres ?= -I/usr/local/pgsql/include/ +DRIVER_LIBS_postgres ?= -L/usr/lib -lpq +DRIVER_INCS_postgres ?= -I/usr/include/postgresql +# - SQLite +DRIVER_LIBS_sqlite ?= -lsqlite +DRIVER_INCS_sqlite ?= +# - SQLite3 +DRIVER_LIBS_sqlite3 ?= -L/opt/local/lib -lsqlite3 +DRIVER_INCS_sqlite3 ?= -I/opt/local/include +# - ODBC +DRIVER_LIBS_odbc ?= -L/usr/local/lib -lodbc +DRIVER_INCS_odbc ?= -DUNIXODBC -I/usr/local/include +# - Firebird +DRIVER_LIBS_firebird ?= -L/usr/local/firebird -lfbclient +DRIVER_INCS_firebird ?= + +# general compilation parameters +WARN = -Wall -Wmissing-prototypes -Wmissing-declarations -pedantic +INCS = -I$(LUA_INC) +DEFS = +CFLAGS = -O2 -std=gnu99 $(WARN) -fPIC $(DRIVER_INCS) $(INCS) \ + -DLUASQL_VERSION_NUMBER='"$V"' $(DEFS) CC= gcc - -# $Id: config,v 1.10 2008/05/30 17:21:18 tomas Exp $ From 28f3b3ec751881c28de0860c79148df9d82c7886 Mon Sep 17 00:00:00 2001 From: Scott Morgan Date: Wed, 6 May 2015 00:34:50 +0100 Subject: [PATCH 60/64] Pre-definition to avoid compiler warning --- src/ls_firebird.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index acb15c3..28ef7a7 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -21,6 +21,9 @@ #define LUASQL_STATEMENT_FIREBIRD "Firebird statement" #define LUASQL_CURSOR_FIREBIRD "Firebird cursor" +/* pre-def to keep the compiler happy */ +LUASQL_API int luaopen_luasql_firebird (lua_State *L); + typedef struct { short closed; int lock; /* lock count for open connections */ From e484b48a5c44dca22d4ddfe03c43d26f92e5603f Mon Sep 17 00:00:00 2001 From: Scott Morgan Date: Thu, 7 May 2015 20:18:13 +0100 Subject: [PATCH 61/64] MySQL now uses statements directly Compiles but untested --- src/ls_mysql.c | 61 +++++++++++++++++++++----------------------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/src/ls_mysql.c b/src/ls_mysql.c index 7c497d1..9f96c0e 100644 --- a/src/ls_mysql.c +++ b/src/ls_mysql.c @@ -88,7 +88,7 @@ typedef struct { typedef struct { short closed; - int conn; /* reference to connection */ + int stmt; /* reference to statement */ int numcols; /* number of columns */ int colnames, coltypes; /* reference to column information tables */ MYSQL_RES *my_res; @@ -248,7 +248,7 @@ static int cur_nullify (lua_State *L, cur_data *cur) /* Nullify structure fields. */ cur->closed = 1; mysql_free_result(cur->my_res); - luaL_unref (L, LUA_REGISTRYINDEX, cur->conn); + luaL_unref (L, LUA_REGISTRYINDEX, cur->stmt); luaL_unref (L, LUA_REGISTRYINDEX, cur->colnames); luaL_unref (L, LUA_REGISTRYINDEX, cur->coltypes); return 0; @@ -399,20 +399,20 @@ static int cur_numrows (lua_State *L) /* ** Create a new Cursor object and push it on top of the stack. */ -static int create_cursor (lua_State *L, int conn, MYSQL_RES *result, int cols) +static int create_cursor (lua_State *L, int stmt, MYSQL_RES *result, int cols) { cur_data *cur = (cur_data *)lua_newuserdata(L, sizeof(cur_data)); luasql_setmeta (L, LUASQL_CURSOR_MYSQL); /* fill in structure */ cur->closed = 0; - cur->conn = LUA_NOREF; + cur->stmt = LUA_NOREF; cur->numcols = cols; cur->colnames = LUA_NOREF; cur->coltypes = LUA_NOREF; cur->my_res = result; - lua_pushvalue (L, conn); - cur->conn = luaL_ref (L, LUA_REGISTRYINDEX); + lua_pushvalue (L, stmt); + cur->stmt = luaL_ref (L, LUA_REGISTRYINDEX); return 1; } @@ -517,16 +517,23 @@ static int conn_prepare (lua_State *L) static int stmt_execute(lua_State *L) { stmt_data *stmt = getstatement(L); + int numcols; if(mysql_stmt_execute(stmt->my_stmt) < 0) { return luasql_failmsg(L, "error executing statement. MySQL: ", mysql_stmt_error(stmt->my_stmt)); } - if(mysql_stmt_field_count(stmt->my_stmt) != 0) { + if((numcols = mysql_stmt_field_count(stmt->my_stmt)) != 0) { /* SELECT */ - /* TODO: create cursor here */ - return 0; + MYSQL_RES *res = mysql_stmt_result_metadata(stmt->my_stmt); + + if(res != NULL) { + return create_cursor(L, 1, res, numcols); + } + + return luasql_failmsg(L, "error creating cursor. MySQL: ", + mysql_stmt_error(stmt->my_stmt)); } else { /* INSERT, DELETE, UPDATE, etc */ luasql_pushinteger(L, mysql_stmt_affected_rows(stmt->my_stmt)); @@ -541,33 +548,16 @@ static int stmt_execute(lua_State *L) */ static int conn_execute (lua_State *L) { - conn_data *conn = getconnection (L); - size_t st_len; - const char *statement = luaL_checklstring (L, 2, &st_len); - if (mysql_real_query(conn->my_conn, statement, st_len)) { - /* error executing query */ - return luasql_failmsg(L, "error executing query. MySQL: ", - mysql_error(conn->my_conn)); - } else { - MYSQL_RES *res = mysql_store_result(conn->my_conn); - unsigned int num_cols = mysql_field_count(conn->my_conn); - - if (res) { - /* tuples returned */ - return create_cursor (L, 1, res, num_cols); - } else { - /* mysql_use_result() returned nothing; should it have? */ - if(num_cols == 0) { /* no tuples returned */ - /* query does not return data (it was not a SELECT) */ - luasql_pushinteger(L, mysql_affected_rows(conn->my_conn)); - return 1; - } else { - /* mysql_use_result() should have returned data */ - return luasql_failmsg(L, "error retrieving result. MySQL: ", - mysql_error(conn->my_conn)); - } - } + int res; + + /* prepare statement */ + if((res = conn_prepare(L)) != 1) { + return res; } + + /* execute statement */ + lua_insert(L, 1); + return stmt_execute(L); } @@ -775,6 +765,7 @@ static void create_metatables (lua_State *L) struct luaL_Reg statement_methods[] = { {"__gc", stmt_gc}, {"close", stmt_close}, + {"execute", stmt_execute}, {NULL, NULL}, }; struct luaL_Reg cursor_methods[] = { From 60d2635d8c4391add86b1864fc11c9aa8f9038c0 Mon Sep 17 00:00:00 2001 From: blumf Date: Fri, 8 May 2015 17:01:32 +0100 Subject: [PATCH 62/64] Wasn't creating a new connection object Mistake from prev commit --- src/ls_mysql.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ls_mysql.c b/src/ls_mysql.c index 9f96c0e..be01960 100644 --- a/src/ls_mysql.c +++ b/src/ls_mysql.c @@ -614,7 +614,8 @@ static int conn_getlastautoid (lua_State *L) */ static int create_connection (lua_State *L, int env, MYSQL *my_conn) { - conn_data *conn = getconnection (L); + conn_data *conn = (conn_data *)lua_newuserdata(L, sizeof(conn_data)); + luasql_setmeta (L, LUASQL_CONNECTION_MYSQL); /* fill in structure */ conn->closed = 0; From 66bbedec860fdd9c0cb05e875eeca4e31770f4f8 Mon Sep 17 00:00:00 2001 From: Scott Morgan Date: Fri, 8 May 2015 23:24:51 +0100 Subject: [PATCH 63/64] Removed unused var --- src/ls_odbc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ls_odbc.c b/src/ls_odbc.c index cbed050..9a5b4d6 100644 --- a/src/ls_odbc.c +++ b/src/ls_odbc.c @@ -769,8 +769,6 @@ static int raw_readparams(lua_State *L, stmt_data *stmt, int iparams) break; case LUA_TBOOLEAN: { - SQLCHAR b = (SQLCHAR)lua_toboolean(L, -1); - data->buf = malloc(sizeof(SQLCHAR)); *(SQLCHAR *)data->buf = (SQLCHAR)lua_toboolean(L, -1); data->len = 0; From 2900df61d83dd52aa3c744ef0946ef4ff9614dd6 Mon Sep 17 00:00:00 2001 From: blumf Date: Fri, 31 Jul 2015 15:53:45 +0100 Subject: [PATCH 64/64] Platform agnostic column buffers Issues with integer sizes differing between 32 & 64-bit archs (same as committed to Kepler root, but the code is far to different to merge now) --- src/ls_firebird.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/src/ls_firebird.c b/src/ls_firebird.c index 28ef7a7..f654463 100644 --- a/src/ls_firebird.c +++ b/src/ls_firebird.c @@ -119,6 +119,13 @@ static XSQLDA *malloc_xsqlda(ISC_SHORT len) return res; } +static void *malloc_zero(size_t len) +{ + void *res = malloc(len); + memset(res, 0, len); + return res; +} + /* ** Allocate memory for XSQLDA data */ @@ -131,37 +138,37 @@ static void malloc_sqlda_vars(XSQLDA *sqlda) for (i=0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++) { switch(var->sqltype & ~1) { case SQL_VARYING: - var->sqldata = (char *)malloc(sizeof(char)*var->sqllen + 2); + var->sqldata = (char *)malloc_zero(sizeof(char)*var->sqllen + 2); break; case SQL_TEXT: - var->sqldata = (char *)malloc(sizeof(char)*var->sqllen); + var->sqldata = (char *)malloc_zero(sizeof(char)*var->sqllen); break; case SQL_SHORT: - var->sqldata = (char *)malloc(sizeof(short)); + var->sqldata = (char *)malloc_zero(sizeof(ISC_SHORT)); break; case SQL_LONG: - var->sqldata = (char *)malloc(sizeof(long)); + var->sqldata = (char *)malloc_zero(sizeof(ISC_LONG)); break; case SQL_INT64: - var->sqldata = (char *)malloc(sizeof(ISC_INT64)); + var->sqldata = (char *)malloc_zero(sizeof(ISC_INT64)); break; case SQL_FLOAT: - var->sqldata = (char *)malloc(sizeof(float)); + var->sqldata = (char *)malloc_zero(sizeof(float)); break; case SQL_DOUBLE: - var->sqldata = (char *)malloc(sizeof(double)); + var->sqldata = (char *)malloc_zero(sizeof(double)); break; case SQL_TYPE_TIME: - var->sqldata = (char *)malloc(sizeof(ISC_TIME)); + var->sqldata = (char *)malloc_zero(sizeof(ISC_TIME)); break; case SQL_TYPE_DATE: - var->sqldata = (char *)malloc(sizeof(ISC_DATE)); + var->sqldata = (char *)malloc_zero(sizeof(ISC_DATE)); break; case SQL_TIMESTAMP: - var->sqldata = (char *)malloc(sizeof(ISC_TIMESTAMP)); + var->sqldata = (char *)malloc_zero(sizeof(ISC_TIMESTAMP)); break; case SQL_BLOB: - var->sqldata = (char *)malloc(sizeof(ISC_QUAD)); + var->sqldata = (char *)malloc_zero(sizeof(ISC_QUAD)); break; /* TODO : add extra data type handles here */ } @@ -1001,10 +1008,10 @@ static void push_column(lua_State *L, int i, cur_data *cur) cur->out_sqlda->sqlvar[i].sqllen); break; case SQL_SHORT: - luasql_pushinteger(L, *(short *)(cur->out_sqlda->sqlvar[i].sqldata)); + luasql_pushinteger(L, *(ISC_SHORT *)(cur->out_sqlda->sqlvar[i].sqldata)); break; case SQL_LONG: - luasql_pushinteger(L, *(long *)(cur->out_sqlda->sqlvar[i].sqldata)); + luasql_pushinteger(L, *(ISC_LONG *)(cur->out_sqlda->sqlvar[i].sqldata)); break; case SQL_INT64: luasql_pushinteger(L, *(ISC_INT64 *)(cur->out_sqlda->sqlvar[i].sqldata));