Use C++ compiled Lua

This commit is contained in:
Tiger Wang 2021-06-23 13:39:48 +01:00
parent 4435d7e429
commit 9aeab26b00
2 changed files with 387 additions and 163 deletions

View File

@ -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()

View File

@ -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 <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 *
@ -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 <stdlib.h>
#include <string.h>
@ -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;
}
@ -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
@ -1275,18 +1317,16 @@ static void db_update_hook_callback(void *user, int op, char const *dbname, char
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);
@ -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