diff --git a/CMakeLists.txt b/CMakeLists.txt index fc14f1d..e16b88b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,5 @@ cmake_minimum_required (VERSION 3.13) project (lsqlite C) -add_library(lsqlite lsqlite3.c) +add_library(lsqlite lsqlite3.cpp) target_link_libraries(lsqlite PUBLIC sqlite3 lualib) - -# FreeBSD requires us to define this to get POSIX 2001 standard -if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") - target_compile_definitions(lsqlite PRIVATE _XOPEN_SOURCE=600) -endif() - -if (UNIX) - target_link_libraries(lsqlite PRIVATE ${DYNAMIC_LOADER}) -endif() diff --git a/lsqlite3.c b/lsqlite3.cpp similarity index 81% rename from lsqlite3.c rename to lsqlite3.cpp index 4c81b58..990f6d4 100644 --- a/lsqlite3.c +++ b/lsqlite3.cpp @@ -1,10 +1,10 @@ /************************************************************************ * lsqlite3 * -* Copyright (C) 2002-2013 Tiago Dionizio, Doug Currie * +* Copyright (C) 2002-2016 Tiago Dionizio, Doug Currie * * All rights reserved. * * Author : Tiago Dionizio * * Author : Doug Currie * -* Library : lsqlite3 - a SQLite 3 database binding for Lua 5 * +* Library : lsqlite3 - an SQLite 3 database binding for Lua 5 * * * * Permission is hereby granted, free of charge, to any person obtaining * * a copy of this software and associated documentation files (the * @@ -25,19 +25,6 @@ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ************************************************************************/ -// Slightly modified by _Xoft to compile in MSVC - - - - -// 2013_04_07 _X: Added the following #define-s so that MSVC doesn't complain about non-secure stuff: -#define _CRT_SECURE_NO_WARNINGS -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - - - #include #include @@ -48,16 +35,25 @@ extern "C" { #include "lauxlib.h" #if LUA_VERSION_NUM > 501 -// -// Lua 5.2 -// +/* +** Lua 5.2 +*/ +#ifndef lua_strlen #define lua_strlen lua_rawlen -// luaL_typerror always used with arg at ndx == NULL +#endif +/* luaL_typerror always used with arg at ndx == NULL */ #define luaL_typerror(L,ndx,str) luaL_error(L,"bad argument %d (%s expected, got nil)",ndx,str) -// luaL_register used once, so below expansion is OK for this case +/* luaL_register used once, so below expansion is OK for this case */ #define luaL_register(L,name,reg) lua_newtable(L);luaL_setfuncs(L,reg,0) -// luaL_openlib always used with name == NULL +/* luaL_openlib always used with name == NULL */ #define luaL_openlib(L,name,reg,nup) luaL_setfuncs(L,reg,nup) + +#if LUA_VERSION_NUM > 502 +/* +** Lua 5.3 +*/ +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#endif #endif #include "sqlite3.h" @@ -69,9 +65,15 @@ extern "C" { #if !defined(LSQLITE_OMIT_UPDATE_HOOK) #define LSQLITE_OMIT_UPDATE_HOOK 0 #endif +#if defined(LSQLITE_OMIT_OPEN_V2) + #define SQLITE3_OPEN(L,filename,flags) sqlite3_open(L,filename) +#else + #define SQLITE3_OPEN(L,filename,flags) sqlite3_open_v2(L,filename,flags,NULL) +#endif typedef struct sdb sdb; typedef struct sdb_vm sdb_vm; +typedef struct sdb_bu sdb_bu; typedef struct sdb_func sdb_func; /* to use as C user data so i know what function sqlite is calling */ @@ -123,9 +125,36 @@ struct sdb { static const char *sqlite_meta = ":sqlite3"; static const char *sqlite_vm_meta = ":sqlite3:vm"; +static const char *sqlite_bu_meta = ":sqlite3:bu"; static const char *sqlite_ctx_meta = ":sqlite3:ctx"; static int sqlite_ctx_meta_ref; +/* Lua 5.3 introduced an integer type, but depending on the implementation, it could be 32 +** or 64 bits (or something else?). This helper macro tries to do "the right thing." +*/ + +#if LUA_VERSION_NUM > 502 +#define PUSH_INT64(L,i64in,fallback) \ + do { \ + sqlite_int64 i64 = i64in; \ + lua_Integer i = (lua_Integer )i64; \ + if (i == i64) lua_pushinteger(L, i);\ + else { \ + lua_Number n = (lua_Number)i64; \ + if (n == i64) lua_pushnumber(L, n); \ + else fallback; \ + } \ + } while (0) +#else +#define PUSH_INT64(L,i64in,fallback) \ + do { \ + sqlite_int64 i64 = i64in; \ + lua_Number n = (lua_Number)i64; \ + if (n == i64) lua_pushnumber(L, n); \ + else fallback; \ + } while (0) +#endif + /* ** ======================================================= ** Database Virtual Machine Operations @@ -135,14 +164,9 @@ static int sqlite_ctx_meta_ref; static void vm_push_column(lua_State *L, sqlite3_stmt *vm, int idx) { switch (sqlite3_column_type(vm, idx)) { case SQLITE_INTEGER: - { - sqlite_int64 i64 = sqlite3_column_int64(vm, idx); - lua_Number n = (lua_Number)i64; - if (n == i64) - lua_pushnumber(L, n); - else - lua_pushlstring(L, (const char*)sqlite3_column_text(vm, idx), sqlite3_column_bytes(vm, idx)); - } + PUSH_INT64(L, sqlite3_column_int64(vm, idx) + , lua_pushlstring(L, (const char*)sqlite3_column_text(vm, idx) + , sqlite3_column_bytes(vm, idx))); break; case SQLITE_FLOAT: lua_pushnumber(L, sqlite3_column_double(vm, idx)); @@ -151,7 +175,7 @@ static void vm_push_column(lua_State *L, sqlite3_stmt *vm, int idx) { lua_pushlstring(L, (const char*)sqlite3_column_text(vm, idx), sqlite3_column_bytes(vm, idx)); break; case SQLITE_BLOB: - lua_pushlstring(L, sqlite3_column_blob(vm, idx), sqlite3_column_bytes(vm, idx)); + lua_pushlstring(L, (const char*)sqlite3_column_blob(vm, idx), sqlite3_column_bytes(vm, idx)); break; case SQLITE_NULL: lua_pushnil(L); @@ -174,9 +198,9 @@ struct sdb_vm { char temp; /* temporary vm used in db:rows */ }; -/* called with sql text on the lua stack */ +/* called with db,sql text on the lua stack */ static sdb_vm *newvm(lua_State *L, sdb *db) { - sdb_vm *svm = (sdb_vm*)lua_newuserdata(L, sizeof(sdb_vm)); + sdb_vm *svm = (sdb_vm*)lua_newuserdata(L, sizeof(sdb_vm)); /* db sql svm_ud -- */ luaL_getmetatable(L, sqlite_vm_meta); lua_setmetatable(L, -2); /* set metatable */ @@ -187,18 +211,19 @@ static sdb_vm *newvm(lua_State *L, sdb *db) { svm->vm = NULL; svm->temp = 0; - /* add an entry on the database table: svm -> sql text */ - lua_pushlightuserdata(L, db); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_pushlightuserdata(L, svm); - lua_pushvalue(L, -4); /* the sql text */ - lua_rawset(L, -3); - lua_pop(L, 1); + /* add an entry on the database table: svm -> db to keep db live while svm is live */ + lua_pushlightuserdata(L, db); /* db sql svm_ud db_lud -- */ + lua_rawget(L, LUA_REGISTRYINDEX); /* db sql svm_ud reg[db_lud] -- */ + lua_pushlightuserdata(L, svm); /* db sql svm_ud reg[db_lud] svm_lud -- */ + lua_pushvalue(L, -5); /* db sql svm_ud reg[db_lud] svm_lud db -- */ + lua_rawset(L, -3); /* (reg[db_lud])[svm_lud] = db ; set the db for this vm */ + lua_pop(L, 1); /* db sql svm_ud -- */ return svm; } static int cleanupvm(lua_State *L, sdb_vm *svm) { + /* remove entry in database table - no harm if not present in the table */ lua_pushlightuserdata(L, svm->db); lua_rawget(L, LUA_REGISTRYINDEX); @@ -212,40 +237,13 @@ static int cleanupvm(lua_State *L, sdb_vm *svm) { if (!svm->vm) return 0; - lua_pushnumber(L, sqlite3_finalize(svm->vm)); + lua_pushinteger(L, sqlite3_finalize(svm->vm)); svm->vm = NULL; return 1; } static int stepvm(lua_State *L, sdb_vm *svm) { - int result; - int loop_limit = 3; - while ( loop_limit-- ) { - result = sqlite3_step(svm->vm); - if ( result==SQLITE_ERROR ) { - result = sqlite3_reset (svm->vm); - } - if ( result==SQLITE_SCHEMA ) { - sqlite3_stmt *vn; - const char *sql; - /* recover sql text */ - lua_pushlightuserdata(L, svm->db); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_pushlightuserdata(L, svm); - lua_rawget(L, -2); /* sql text */ - sql = luaL_checkstring(L, -1); - /* re-prepare */ - result = sqlite3_prepare(svm->db->db, sql, -1, &vn, NULL); - if (result != SQLITE_OK) break; - sqlite3_transfer_bindings(svm->vm, vn); - sqlite3_finalize(svm->vm); - svm->vm = vn; - lua_pop(L,2); - } else { - break; - } - } - return result; + return sqlite3_step(svm->vm); } static sdb_vm *lsqlite_getvm(lua_State *L, int index) { @@ -292,7 +290,7 @@ static int dbvm_step(lua_State *L) { svm->has_values = result == SQLITE_ROW ? 1 : 0; svm->columns = sqlite3_data_count(svm->vm); - lua_pushnumber(L, result); + lua_pushinteger(L, result); return 1; } @@ -304,7 +302,7 @@ static int dbvm_finalize(lua_State *L) { static int dbvm_reset(lua_State *L) { sdb_vm *svm = lsqlite_checkvm(L, 1); sqlite3_reset(svm->vm); - lua_pushnumber(L, sqlite3_errcode(svm->db->db)); + lua_pushinteger(L, sqlite3_errcode(svm->db->db)); return 1; } @@ -326,6 +324,14 @@ static void dbvm_check_bind_index(lua_State *L, sdb_vm *svm, int index) { } } +static int dbvm_last_insert_rowid(lua_State *L) { + sdb_vm *svm = lsqlite_checkvm(L, 1); + /* conversion warning: int64 -> luaNumber */ + sqlite_int64 rowid = sqlite3_last_insert_rowid(svm->db->db); + PUSH_INT64(L, rowid, lua_pushfstring(L, "%ll", rowid)); + return 1; +} + /* ** ======================================================= ** Virtual Machine - generic info @@ -333,7 +339,7 @@ static void dbvm_check_bind_index(lua_State *L, sdb_vm *svm, int index) { */ static int dbvm_columns(lua_State *L) { sdb_vm *svm = lsqlite_checkvm(L, 1); - lua_pushnumber(L, sqlite3_column_count(svm->vm)); + lua_pushinteger(L, sqlite3_column_count(svm->vm)); return 1; } @@ -354,7 +360,7 @@ static int dbvm_get_value(lua_State *L) { static int dbvm_get_name(lua_State *L) { sdb_vm *svm = lsqlite_checkvm(L, 1); - int index = (int)luaL_checknumber(L, 2); + int index = luaL_checknumber(L, 2); dbvm_check_index(L, svm, index); lua_pushstring(L, sqlite3_column_name(svm->vm, index)); return 1; @@ -362,7 +368,7 @@ static int dbvm_get_name(lua_State *L) { static int dbvm_get_type(lua_State *L) { sdb_vm *svm = lsqlite_checkvm(L, 1); - int index = (int)luaL_checknumber(L, 2); + int index = luaL_checknumber(L, 2); dbvm_check_index(L, svm, index); lua_pushstring(L, sqlite3_column_decltype(svm->vm, index)); return 1; @@ -375,7 +381,7 @@ static int dbvm_get_values(lua_State *L) { int n; dbvm_check_contents(L, svm); - lua_newtable(L); + lua_createtable(L, columns, 0); for (n = 0; n < columns;) { vm_push_column(L, vm, n++); lua_rawseti(L, -2, n); @@ -389,7 +395,7 @@ static int dbvm_get_names(lua_State *L) { int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ int n; - lua_newtable(L); + lua_createtable(L, columns, 0); for (n = 0; n < columns;) { lua_pushstring(L, sqlite3_column_name(vm, n++)); lua_rawseti(L, -2, n); @@ -403,7 +409,7 @@ static int dbvm_get_types(lua_State *L) { int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */ int n; - lua_newtable(L); + lua_createtable(L, columns, 0); for (n = 0; n < columns;) { lua_pushstring(L, sqlite3_column_decltype(vm, n++)); lua_rawseti(L, -2, n); @@ -455,7 +461,7 @@ static int dbvm_get_named_values(lua_State *L) { int n; dbvm_check_contents(L, svm); - lua_newtable(L); + lua_createtable(L, 0, columns); for (n = 0; n < columns; ++n) { lua_pushstring(L, sqlite3_column_name(vm, n)); vm_push_column(L, vm, n); @@ -470,7 +476,7 @@ static int dbvm_get_named_types(lua_State *L) { int columns = sqlite3_column_count(vm); int n; - lua_newtable(L); + lua_createtable(L, 0, columns); for (n = 0; n < columns; ++n) { lua_pushstring(L, sqlite3_column_name(vm, n)); lua_pushstring(L, sqlite3_column_decltype(vm, n)); @@ -490,6 +496,10 @@ static int dbvm_bind_index(lua_State *L, sqlite3_stmt *vm, int index, int lindex case LUA_TSTRING: return sqlite3_bind_text(vm, index, lua_tostring(L, lindex), lua_strlen(L, lindex), SQLITE_TRANSIENT); case LUA_TNUMBER: +#if LUA_VERSION_NUM > 502 + if (lua_isinteger(L, lindex)) + return sqlite3_bind_int64(vm, index, lua_tointeger(L, lindex)); +#endif return sqlite3_bind_double(vm, index, lua_tonumber(L, lindex)); case LUA_TBOOLEAN: return sqlite3_bind_int(vm, index, lua_toboolean(L, lindex) ? 1 : 0); @@ -505,13 +515,13 @@ static int dbvm_bind_index(lua_State *L, sqlite3_stmt *vm, int index, int lindex static int dbvm_bind_parameter_count(lua_State *L) { sdb_vm *svm = lsqlite_checkvm(L, 1); - lua_pushnumber(L, sqlite3_bind_parameter_count(svm->vm)); + lua_pushinteger(L, sqlite3_bind_parameter_count(svm->vm)); return 1; } static int dbvm_bind_parameter_name(lua_State *L) { sdb_vm *svm = lsqlite_checkvm(L, 1); - int index = (int)luaL_checknumber(L, 2); + int index = luaL_checknumber(L, 2); dbvm_check_bind_index(L, svm, index); lua_pushstring(L, sqlite3_bind_parameter_name(svm->vm, index)); return 1; @@ -526,7 +536,7 @@ static int dbvm_bind(lua_State *L) { dbvm_check_bind_index(L, svm, index); result = dbvm_bind_index(L, vm, index, 3); - lua_pushnumber(L, result); + lua_pushinteger(L, result); return 1; } @@ -536,7 +546,7 @@ static int dbvm_bind_blob(lua_State *L) { const char *value = luaL_checkstring(L, 3); int len = lua_strlen(L, 3); - lua_pushnumber(L, sqlite3_bind_blob(svm->vm, index, value, len, SQLITE_TRANSIENT)); + lua_pushinteger(L, sqlite3_bind_blob(svm->vm, index, value, len, SQLITE_TRANSIENT)); return 1; } @@ -555,12 +565,12 @@ static int dbvm_bind_values(lua_State *L) { for (n = 2; n <= top; ++n) { if ((result = dbvm_bind_index(L, vm, n - 1, n)) != SQLITE_OK) { - lua_pushnumber(L, result); + lua_pushinteger(L, result); return 1; } } - lua_pushnumber(L, SQLITE_OK); + lua_pushinteger(L, SQLITE_OK); return 1; } @@ -581,19 +591,19 @@ static int dbvm_bind_names(lua_State *L) { lua_pop(L, 1); } else { - lua_pushnumber(L, n); + lua_pushinteger(L, n); lua_gettable(L, 2); result = dbvm_bind_index(L, vm, n, -1); lua_pop(L, 1); } if (result != SQLITE_OK) { - lua_pushnumber(L, result); + lua_pushinteger(L, result); return 1; } } - lua_pushnumber(L, SQLITE_OK); + lua_pushinteger(L, SQLITE_OK); return 1; } @@ -621,7 +631,7 @@ static sdb *newdb (lua_State *L) { db->progress_cb = db->progress_udata = db->trace_cb = - db->trace_udata = + db->trace_udata = #if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK db->update_hook_cb = db->update_hook_udata = @@ -657,7 +667,7 @@ static int cleanupdb(lua_State *L, sdb *db) { top = lua_gettop(L); lua_pushnil(L); while (lua_next(L, -2)) { - sdb_vm *svm = lua_touserdata(L, -2); /* key: vm; val: sql text */ + sdb_vm *svm = (sdb_vm*)lua_touserdata(L, -2); /* key: vm; val: sql text */ cleanupvm(L, svm); lua_settop(L, top); @@ -793,7 +803,7 @@ static int lcontext_set_aggregate_context(lua_State *L) { static int lcontext_aggregate_count(lua_State *L) { lcontext *ctx = lsqlite_checkcontext(L, 1); lcontext_check_aggregate(L, ctx); - lua_pushnumber(L, sqlite3_aggregate_count(ctx->ctx)); + lua_pushinteger(L, sqlite3_aggregate_count(ctx->ctx)); return 1; } @@ -806,6 +816,11 @@ static int lcontext_result(lua_State *L) { lcontext *ctx = lsqlite_checkcontext(L, 1); switch (lua_type(L, 2)) { case LUA_TNUMBER: +#if LUA_VERSION_NUM > 502 + if (lua_isinteger(L, 2)) + sqlite3_result_int64(ctx->ctx, luaL_checkinteger(L, 2)); + else +#endif sqlite3_result_double(ctx->ctx, luaL_checknumber(L, 2)); break; case LUA_TSTRING: @@ -883,29 +898,25 @@ static int db_last_insert_rowid(lua_State *L) { sdb *db = lsqlite_checkdb(L, 1); /* conversion warning: int64 -> luaNumber */ sqlite_int64 rowid = sqlite3_last_insert_rowid(db->db); - lua_Number n = (lua_Number)rowid; - if (n == rowid) - lua_pushnumber(L, n); - else - lua_pushfstring(L, "%ll", rowid); + PUSH_INT64(L, rowid, lua_pushfstring(L, "%ll", rowid)); return 1; } static int db_changes(lua_State *L) { sdb *db = lsqlite_checkdb(L, 1); - lua_pushnumber(L, sqlite3_changes(db->db)); + lua_pushinteger(L, sqlite3_changes(db->db)); return 1; } static int db_total_changes(lua_State *L) { sdb *db = lsqlite_checkdb(L, 1); - lua_pushnumber(L, sqlite3_total_changes(db->db)); + lua_pushinteger(L, sqlite3_total_changes(db->db)); return 1; } static int db_errcode(lua_State *L) { sdb *db = lsqlite_checkdb(L, 1); - lua_pushnumber(L, sqlite3_errcode(db->db)); + lua_pushinteger(L, sqlite3_errcode(db->db)); return 1; } @@ -921,6 +932,14 @@ static int db_interrupt(lua_State *L) { return 0; } +static int db_db_filename(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + const char *db_name = luaL_checkstring(L, 2); + // sqlite3_db_filename may return NULL, in that case Lua pushes nil... + lua_pushstring(L, sqlite3_db_filename(db->db, db_name)); + return 1; +} + /* ** Registering SQL functions: */ @@ -932,14 +951,9 @@ static void db_push_value(lua_State *L, sqlite3_value *value) { break; case SQLITE_INTEGER: - { - sqlite_int64 i64 = sqlite3_value_int64(value); - lua_Number n = (lua_Number)i64; - if (n == i64) - lua_pushnumber(L, n); - else - lua_pushlstring(L, (const char*)sqlite3_value_text(value), sqlite3_value_bytes(value)); - } + PUSH_INT64(L, sqlite3_value_int64(value) + , lua_pushlstring(L, (const char*)sqlite3_value_text(value) + , sqlite3_value_bytes(value))); break; case SQLITE_FLOAT: @@ -947,7 +961,7 @@ static void db_push_value(lua_State *L, sqlite3_value *value) { break; case SQLITE_BLOB: - lua_pushlstring(L, sqlite3_value_blob(value), sqlite3_value_bytes(value)); + lua_pushlstring(L, (const char*)sqlite3_value_blob(value), sqlite3_value_bytes(value)); break; case SQLITE_NULL: @@ -1165,8 +1179,8 @@ static int collwrapper(scc *co,int l1,const void *p1, int res=0; lua_State *L=co->L; lua_rawgeti(L,LUA_REGISTRYINDEX,co->ref); - lua_pushlstring(L,p1,l1); - lua_pushlstring(L,p2,l2); + lua_pushlstring(L,(const char*)p1,l1); + lua_pushlstring(L,(const char*)p2,l2); if (lua_pcall(L,2,1,0)==0) res=(int)lua_tonumber(L,-1); lua_pop(L,1); return res; @@ -1205,6 +1219,34 @@ static int db_create_collation(lua_State *L) { return 0; } +/* Thanks to Wolfgang Oertl... +*/ +static int db_load_extension(lua_State *L) { + sdb *db=lsqlite_checkdb(L,1); + const char *extname=luaL_optstring(L,2,NULL); + const char *entrypoint=luaL_optstring(L,3,NULL); + int result; + char *errmsg = NULL; + + if (extname == NULL) { + result = sqlite3_enable_load_extension(db->db,0); /* disable extension loading */ + } + else { + sqlite3_enable_load_extension(db->db,1); /* enable extension loading */ + result = sqlite3_load_extension(db->db,extname,entrypoint,&errmsg); + } + + if (result == SQLITE_OK) { + lua_pushboolean(L,1); + return 1; + } + + lua_pushboolean(L,0); /* so, assert(load_extension(...)) works */ + lua_pushstring(L,errmsg); + sqlite3_free(errmsg); + return 2; +} + /* ** trace callback: ** Params: database, callback function, userdata @@ -1268,25 +1310,23 @@ static int db_trace(lua_State *L) { ** Params: database, callback function, userdata ** ** callback function: -** Params: userdata, {one of SQLITE_INSERT, SQLITE_DELETE, or SQLITE_UPDATE}, +** Params: userdata, {one of SQLITE_INSERT, SQLITE_DELETE, or SQLITE_UPDATE}, ** database name, table name (containing the affected row), rowid of the row */ static void db_update_hook_callback(void *user, int op, char const *dbname, char const *tblname, sqlite3_int64 rowid) { sdb *db = (sdb*)user; lua_State *L = db->L; int top = lua_gettop(L); - lua_Number n = (lua_Number)rowid; + lua_Number n; /* setup lua callback call */ lua_rawgeti(L, LUA_REGISTRYINDEX, db->update_hook_cb); /* get callback */ lua_rawgeti(L, LUA_REGISTRYINDEX, db->update_hook_udata); /* get callback user data */ - lua_pushnumber(L, (lua_Number )op); + lua_pushinteger(L, op); lua_pushstring(L, dbname); /* update_hook database name */ lua_pushstring(L, tblname); /* update_hook database name */ - if (n == rowid) - lua_pushnumber(L, n); - else - lua_pushfstring(L, "%ll", rowid); + + PUSH_INT64(L, rowid, lua_pushfstring(L, "%ll", rowid)); /* call lua function */ lua_pcall(L, 5, 0, 0); @@ -1334,7 +1374,7 @@ static int db_update_hook(lua_State *L) { ** callback function: ** Params: userdata ** Returned value: Return false or nil to continue the COMMIT operation normally. -** return true (non false, non nil), then the COMMIT is converted into a ROLLBACK. +** return true (non false, non nil), then the COMMIT is converted into a ROLLBACK. */ static int db_commit_hook_callback(void *user) { sdb *db = (sdb*)user; @@ -1513,6 +1553,122 @@ static int db_progress_handler(lua_State *L) { #endif /* #if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK */ +/* Online Backup API */ +#if 0 +sqlite3_backup *sqlite3_backup_init( + sqlite3 *pDest, /* Destination database handle */ + const char *zDestName, /* Destination database name */ + sqlite3 *pSource, /* Source database handle */ + const char *zSourceName /* Source database name */ +); +int sqlite3_backup_step(sqlite3_backup *p, int nPage); +int sqlite3_backup_finish(sqlite3_backup *p); +int sqlite3_backup_remaining(sqlite3_backup *p); +int sqlite3_backup_pagecount(sqlite3_backup *p); +#endif + +struct sdb_bu { + sqlite3_backup *bu; /* backup structure */ +}; + +static int cleanupbu(lua_State *L, sdb_bu *sbu) { + + if (!sbu->bu) return 0; /* already finished */ + + /* remove table from registry */ + lua_pushlightuserdata(L, sbu->bu); + lua_pushnil(L); + lua_rawset(L, LUA_REGISTRYINDEX); + + lua_pushinteger(L, sqlite3_backup_finish(sbu->bu)); + sbu->bu = NULL; + + return 1; +} + +static int lsqlite_backup_init(lua_State *L) { + + sdb *target_db = lsqlite_checkdb(L, 1); + const char *target_nm = luaL_checkstring(L, 2); + sdb *source_db = lsqlite_checkdb(L, 3); + const char *source_nm = luaL_checkstring(L, 4); + + sqlite3_backup *bu = sqlite3_backup_init(target_db->db, target_nm, source_db->db, source_nm); + + if (NULL != bu) { + sdb_bu *sbu = (sdb_bu*)lua_newuserdata(L, sizeof(sdb_bu)); + + luaL_getmetatable(L, sqlite_bu_meta); + lua_setmetatable(L, -2); /* set metatable */ + sbu->bu = bu; + + /* create table from registry */ + /* to prevent referenced databases from being garbage collected while bu is live */ + lua_pushlightuserdata(L, bu); + lua_createtable(L, 2, 0); + /* add source and target dbs to table at indices 1 and 2 */ + lua_pushvalue(L, 1); /* target db */ + lua_rawseti(L, -2, 1); + lua_pushvalue(L, 3); /* source db */ + lua_rawseti(L, -2, 2); + /* put table in registry with key lightuserdata bu */ + lua_rawset(L, LUA_REGISTRYINDEX); + + return 1; + } + else { + return 0; + } +} + +static sdb_bu *lsqlite_getbu(lua_State *L, int index) { + sdb_bu *sbu = (sdb_bu*)luaL_checkudata(L, index, sqlite_bu_meta); + if (sbu == NULL) luaL_typerror(L, index, "sqlite database backup"); + return sbu; +} + +static sdb_bu *lsqlite_checkbu(lua_State *L, int index) { + sdb_bu *sbu = lsqlite_getbu(L, index); + if (sbu->bu == NULL) luaL_argerror(L, index, "attempt to use closed sqlite database backup"); + return sbu; +} + +static int dbbu_gc(lua_State *L) { + sdb_bu *sbu = lsqlite_getbu(L, 1); + if (sbu->bu != NULL) { + cleanupbu(L, sbu); + lua_pop(L, 1); + } + /* else ignore if already finished */ + return 0; +} + +static int dbbu_step(lua_State *L) { + sdb_bu *sbu = lsqlite_checkbu(L, 1); + int nPage = luaL_checkint(L, 2); + lua_pushinteger(L, sqlite3_backup_step(sbu->bu, nPage)); + return 1; +} + +static int dbbu_remaining(lua_State *L) { + sdb_bu *sbu = lsqlite_checkbu(L, 1); + lua_pushinteger(L, sqlite3_backup_remaining(sbu->bu)); + return 1; +} + +static int dbbu_pagecount(lua_State *L) { + sdb_bu *sbu = lsqlite_checkbu(L, 1); + lua_pushinteger(L, sqlite3_backup_pagecount(sbu->bu)); + return 1; +} + +static int dbbu_finish(lua_State *L) { + sdb_bu *sbu = lsqlite_checkbu(L, 1); + return cleanupbu(L, sbu); +} + +/* end of Online Backup API */ + /* ** busy handler: ** Params: database, callback function, userdata @@ -1529,7 +1685,7 @@ static int db_busy_callback(void *user, int tries) { lua_rawgeti(L, LUA_REGISTRYINDEX, db->busy_cb); lua_rawgeti(L, LUA_REGISTRYINDEX, db->busy_udata); - lua_pushnumber(L, tries); + lua_pushinteger(L, tries); /* call lua function */ if (!lua_pcall(L, 2, 1, 0)) @@ -1602,7 +1758,7 @@ static int db_exec_callback(void* user, int columns, char **data, char **names) lua_pushvalue(L, 3); /* function to call */ lua_pushvalue(L, 4); /* user data */ - lua_pushnumber(L, columns); /* total number of rows in result */ + lua_pushinteger(L, columns); /* total number of rows in result */ /* column values */ lua_pushvalue(L, 6); @@ -1615,7 +1771,7 @@ static int db_exec_callback(void* user, int columns, char **data, char **names) lua_pushvalue(L, 5); if (lua_isnil(L, -1)) { lua_pop(L, 1); - lua_newtable(L); + lua_createtable(L, columns, 0); lua_pushvalue(L, -1); lua_replace(L, 5); for (n = 0; n < columns;) { @@ -1626,8 +1782,14 @@ static int db_exec_callback(void* user, int columns, char **data, char **names) /* call lua function */ if (!lua_pcall(L, 4, 1, 0)) { + +#if LUA_VERSION_NUM > 502 + if (lua_isinteger(L, -1)) + result = lua_tointeger(L, -1); + else +#endif if (lua_isnumber(L, -1)) - result = (int)lua_tonumber(L, -1); + result = lua_tonumber(L, -1); } lua_settop(L, top); @@ -1658,7 +1820,7 @@ static int db_exec(lua_State *L) { result = sqlite3_exec(db->db, sql, NULL, NULL, NULL); } - lua_pushnumber(L, result); + lua_pushinteger(L, result); return 1; } @@ -1672,14 +1834,14 @@ static int db_prepare(lua_State *L) { int sql_len = lua_strlen(L, 2); const char *sqltail; sdb_vm *svm; - lua_settop(L,2); /* sql is on top of stack for call to newvm */ + lua_settop(L,2); /* db,sql is on top of stack for call to newvm */ svm = newvm(L, db); - if (sqlite3_prepare(db->db, sql, sql_len, &svm->vm, &sqltail) != SQLITE_OK) { - cleanupvm(L, svm); - + if (sqlite3_prepare_v2(db->db, sql, sql_len, &svm->vm, &sqltail) != SQLITE_OK) { lua_pushnil(L); - lua_pushnumber(L, sqlite3_errcode(db->db)); + lua_pushinteger(L, sqlite3_errcode(db->db)); + if (cleanupvm(L, svm) == 1) + lua_pop(L, 1); /* this should not happen since sqlite3_prepare_v2 will not set ->vm on error */ return 2; } @@ -1702,14 +1864,15 @@ static int db_do_next_row(lua_State *L, int packed) { if (result == SQLITE_ROW) { if (packed) { - lua_newtable(L); if (packed == 1) { + lua_createtable(L, columns, 0); for (i = 0; i < columns;) { vm_push_column(L, vm, i); lua_rawseti(L, -2, ++i); } } else { + lua_createtable(L, 0, columns); for (i = 0; i < columns; ++i) { lua_pushstring(L, sqlite3_column_name(vm, i)); vm_push_column(L, vm, i); @@ -1780,14 +1943,14 @@ static int db_do_rows(lua_State *L, int(*f)(lua_State *)) { sdb *db = lsqlite_checkdb(L, 1); const char *sql = luaL_checkstring(L, 2); sdb_vm *svm; - lua_settop(L,2); /* sql is on top of stack for call to newvm */ + lua_settop(L,2); /* db,sql is on top of stack for call to newvm */ svm = newvm(L, db); svm->temp = 1; - if (sqlite3_prepare(db->db, sql, -1, &svm->vm, NULL) != SQLITE_OK) { - cleanupvm(L, svm); - + if (sqlite3_prepare_v2(db->db, sql, -1, &svm->vm, NULL) != SQLITE_OK) { lua_pushstring(L, sqlite3_errmsg(svm->db->db)); + if (cleanupvm(L, svm) == 1) + lua_pop(L, 1); /* this should not happen since sqlite3_prepare_v2 will not set ->vm on error */ lua_error(L); } @@ -1822,7 +1985,7 @@ static int db_tostring(lua_State *L) { static int db_close(lua_State *L) { sdb *db = lsqlite_checkdb(L, 1); - lua_pushnumber(L, cleanupdb(L, db)); + lua_pushinteger(L, cleanupdb(L, db)); return 1; } @@ -1838,7 +2001,7 @@ static int db_close_vm(lua_State *L) { /* close all used handles */ lua_pushnil(L); while (lua_next(L, -2)) { - sdb_vm *svm = lua_touserdata(L, -2); /* key: vm; val: sql text */ + sdb_vm *svm = (sdb_vm*)lua_touserdata(L, -2); /* key: vm; val: sql text */ if ((!temp || svm->temp) && svm->vm) { @@ -1852,6 +2015,17 @@ static int db_close_vm(lua_State *L) { return 0; } +/* From: Wolfgang Oertl +When using lsqlite3 in a multithreaded environment, each thread has a separate Lua +environment, but full userdata structures can't be passed from one thread to another. +This is possible with lightuserdata, however. See: lsqlite_open_ptr(). +*/ +static int db_get_ptr(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + lua_pushlightuserdata(L, db->db); + return 1; +} + static int db_gc(lua_State *L) { sdb *db = lsqlite_getdb(L, 1); if (db->db != NULL) /* ignore closed databases */ @@ -1876,7 +2050,7 @@ static int lsqlite_complete(lua_State *L) { return 1; } -#ifndef WIN32 +#ifndef _WIN32 static int lsqlite_temp_directory(lua_State *L) { const char *oldtemp = sqlite3_temp_directory; @@ -1897,17 +2071,17 @@ static int lsqlite_temp_directory(lua_State *L) { } #endif -static int lsqlite_do_open(lua_State *L, const char *filename) { +static int lsqlite_do_open(lua_State *L, const char *filename, int flags) { sdb *db = newdb(L); /* create and leave in stack */ - if (sqlite3_open(filename, &db->db) == SQLITE_OK) { + if (SQLITE3_OPEN(filename, &db->db, flags) == SQLITE_OK) { /* database handle already in the stack - return it */ return 1; } /* failed to open database */ lua_pushnil(L); /* push nil */ - lua_pushnumber(L, sqlite3_errcode(db->db)); + lua_pushinteger(L, sqlite3_errcode(db->db)); lua_pushstring(L, sqlite3_errmsg(db->db)); /* push error message */ /* clean things up */ @@ -1919,11 +2093,36 @@ static int lsqlite_do_open(lua_State *L, const char *filename) { static int lsqlite_open(lua_State *L) { const char *filename = luaL_checkstring(L, 1); - return lsqlite_do_open(L, filename); + int flags = luaL_optinteger(L, 2, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + return lsqlite_do_open(L, filename, flags); } static int lsqlite_open_memory(lua_State *L) { - return lsqlite_do_open(L, ":memory:"); + return lsqlite_do_open(L, ":memory:", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); +} + +/* From: Wolfgang Oertl +When using lsqlite3 in a multithreaded environment, each thread has a separate Lua +environment, but full userdata structures can't be passed from one thread to another. +This is possible with lightuserdata, however. See: db_get_ptr(). +*/ +static int lsqlite_open_ptr(lua_State *L) { + sqlite3 *db_ptr; + sdb *db; + int rc; + + luaL_checktype(L, 1, LUA_TLIGHTUSERDATA); + db_ptr = (sqlite3*)lua_touserdata(L, 1); + /* This is the only API function that runs sqlite3SafetyCheck regardless of + * SQLITE_ENABLE_API_ARMOR and does almost nothing (without an SQL + * statement) */ + rc = sqlite3_exec(db_ptr, NULL, NULL, NULL, NULL); + if (rc != SQLITE_OK) + luaL_argerror(L, 1, "not a valid SQLite3 pointer"); + + db = newdb(L); /* create and leave in stack */ + db->db = db_ptr; + return 1; } static int lsqlite_newindex(lua_State *L) { @@ -1932,6 +2131,18 @@ static int lsqlite_newindex(lua_State *L) { return 0; } +#ifndef LSQLITE_VERSION +/* should be defined in rockspec, but just in case... */ +#define LSQLITE_VERSION "unknown" +#endif + +/* Version number of this library +*/ +static int lsqlite_lversion(lua_State *L) { + lua_pushstring(L, LSQLITE_VERSION); + return 1; +} + /* ** ======================================================= ** Register functions @@ -1995,6 +2206,17 @@ static const struct { SC(FUNCTION ) SC(SAVEPOINT ) + /* file open flags */ + SC(OPEN_READONLY) + SC(OPEN_READWRITE) + SC(OPEN_CREATE) + SC(OPEN_URI) + SC(OPEN_MEMORY) + SC(OPEN_NOMUTEX) + SC(OPEN_FULLMUTEX) + SC(OPEN_SHAREDCACHE) + SC(OPEN_PRIVATECACHE) + /* terminator */ { NULL, 0 } }; @@ -2011,10 +2233,12 @@ static const luaL_Reg dblib[] = { {"errmsg", db_errmsg }, {"error_message", db_errmsg }, {"interrupt", db_interrupt }, + {"db_filename", db_db_filename }, {"create_function", db_create_function }, {"create_aggregate", db_create_aggregate }, {"create_collation", db_create_collation }, + {"load_extension", db_load_extension }, {"trace", db_trace }, {"progress_handler", db_progress_handler }, @@ -2035,6 +2259,7 @@ static const luaL_Reg dblib[] = { {"execute", db_exec }, {"close", db_close }, {"close_vm", db_close_vm }, + {"get_ptr", db_get_ptr }, {"__tostring", db_tostring }, {"__gc", db_gc }, @@ -2075,6 +2300,8 @@ static const luaL_Reg vmlib[] = { {"urows", dbvm_urows }, {"nrows", dbvm_nrows }, + {"last_insert_rowid", dbvm_last_insert_rowid }, + /* compatibility names (added by request) */ {"idata", dbvm_get_values }, {"inames", dbvm_get_names }, @@ -2108,14 +2335,30 @@ static const luaL_Reg ctxlib[] = { {NULL, NULL} }; +static const luaL_Reg dbbulib[] = { + + {"step", dbbu_step }, + {"remaining", dbbu_remaining }, + {"pagecount", dbbu_pagecount }, + {"finish", dbbu_finish }, + +// {"__tostring", dbbu_tostring }, + {"__gc", dbbu_gc }, + {NULL, NULL} +}; + static const luaL_Reg sqlitelib[] = { + {"lversion", lsqlite_lversion }, {"version", lsqlite_version }, {"complete", lsqlite_complete }, -#ifndef WIN32 +#ifndef _WIN32 {"temp_directory", lsqlite_temp_directory }, #endif {"open", lsqlite_open }, {"open_memory", lsqlite_open_memory }, + {"open_ptr", lsqlite_open_ptr }, + + {"backup_init", lsqlite_backup_init }, {"__newindex", lsqlite_newindex }, {NULL, NULL} @@ -2137,6 +2380,7 @@ static void create_meta(lua_State *L, const char *name, const luaL_Reg *lib) { LUALIB_API int luaopen_lsqlite3(lua_State *L) { create_meta(L, sqlite_meta, dblib); create_meta(L, sqlite_vm_meta, vmlib); + create_meta(L, sqlite_bu_meta, dbbulib); create_meta(L, sqlite_ctx_meta, ctxlib); luaL_getmetatable(L, sqlite_ctx_meta); @@ -2150,7 +2394,7 @@ LUALIB_API int luaopen_lsqlite3(lua_State *L) { /* add constants to global table */ while (sqlite_constants[i].name) { lua_pushstring(L, sqlite_constants[i].name); - lua_pushnumber(L, sqlite_constants[i].value); + lua_pushinteger(L, sqlite_constants[i].value); lua_rawset(L, -3); ++i; } @@ -2162,14 +2406,3 @@ LUALIB_API int luaopen_lsqlite3(lua_State *L) { return 1; } - - - - -#ifdef __cplusplus -} // extern "C" -#endif // __cplusplus - - - -