|
@@ -1,10 +1,10 @@
|
|
|
/************************************************************************
|
|
|
* lsqlite3 *
|
|
|
-* Copyright (C) 2002-2015 Tiago Dionizio, Doug Currie *
|
|
|
+* Copyright (C) 2002-2016 Tiago Dionizio, Doug Currie *
|
|
|
* All rights reserved. *
|
|
|
* Author : Tiago Dionizio <tiago.dionizio@ist.utl.pt> *
|
|
|
* Author : Doug Currie <doug.currie@alum.mit.edu> *
|
|
|
-* 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 *
|
|
@@ -30,7 +30,9 @@
|
|
|
#include <string.h>
|
|
|
#include <assert.h>
|
|
|
|
|
|
-#include "civetweb_lua.h"
|
|
|
+#define LUA_LIB
|
|
|
+#include "lua.h"
|
|
|
+#include "lauxlib.h"
|
|
|
|
|
|
#if LUA_VERSION_NUM > 501
|
|
|
/*
|
|
@@ -61,10 +63,15 @@
|
|
|
#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 */
|
|
@@ -116,6 +123,7 @@ 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;
|
|
|
|
|
@@ -190,7 +198,7 @@ struct sdb_vm {
|
|
|
|
|
|
/* 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 */
|
|
@@ -202,12 +210,12 @@ static sdb_vm *newvm(lua_State *L, sdb *db) {
|
|
|
svm->temp = 0;
|
|
|
|
|
|
/* add an entry on the database table: svm -> db to keep db live while svm is live */
|
|
|
- lua_pushlightuserdata(L, db);
|
|
|
- lua_rawget(L, LUA_REGISTRYINDEX);
|
|
|
- lua_pushlightuserdata(L, svm);
|
|
|
- lua_pushvalue(L, -5); /* the db for this vm */
|
|
|
- lua_rawset(L, -3);
|
|
|
- lua_pop(L, 1);
|
|
|
+ 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;
|
|
|
}
|
|
@@ -371,7 +379,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);
|
|
@@ -385,7 +393,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);
|
|
@@ -399,7 +407,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);
|
|
@@ -451,7 +459,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);
|
|
@@ -466,7 +474,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));
|
|
@@ -922,6 +930,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:
|
|
|
*/
|
|
@@ -1535,6 +1551,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
|
|
@@ -1637,7 +1769,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;) {
|
|
@@ -1730,14 +1862,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);
|
|
@@ -1880,6 +2013,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 */
|
|
@@ -1904,7 +2048,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;
|
|
|
|
|
@@ -1925,10 +2069,10 @@ 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;
|
|
|
}
|
|
@@ -1947,11 +2091,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 = 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) {
|
|
@@ -2035,6 +2204,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 }
|
|
|
};
|
|
@@ -2051,6 +2231,7 @@ 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 },
|
|
@@ -2076,6 +2257,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 },
|
|
@@ -2151,15 +2333,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}
|
|
@@ -2178,22 +2375,17 @@ static void create_meta(lua_State *L, const char *name, const luaL_Reg *lib) {
|
|
|
lua_pop(L, 1);
|
|
|
}
|
|
|
|
|
|
-static int luaopen_sqlitelib (lua_State *L) {
|
|
|
- luaL_newlibtable(L, sqlitelib);
|
|
|
- luaL_setfuncs(L, sqlitelib, 0);
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
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);
|
|
|
sqlite_ctx_meta_ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
|
|
- /* register global sqlite3 library */
|
|
|
- luaL_requiref(L, "sqlite3", luaopen_sqlitelib, 1);
|
|
|
+ /* register (local) sqlite metatable */
|
|
|
+ luaL_register(L, "sqlite3", sqlitelib);
|
|
|
|
|
|
{
|
|
|
int i = 0;
|