2013-04-07 15:28:32 -04:00
|
|
|
/************************************************************************
|
|
|
|
* lsqlite3 *
|
|
|
|
* Copyright (C) 2002-2013 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 *
|
|
|
|
* *
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining *
|
|
|
|
* a copy of this software and associated documentation files (the *
|
|
|
|
* "Software"), to deal in the Software without restriction, including *
|
|
|
|
* without limitation the rights to use, copy, modify, merge, publish, *
|
|
|
|
* distribute, sublicense, and/or sell copies of the Software, and to *
|
|
|
|
* permit persons to whom the Software is furnished to do so, subject to *
|
|
|
|
* the following conditions: *
|
|
|
|
* *
|
|
|
|
* The above copyright notice and this permission notice shall be *
|
|
|
|
* included in all copies or substantial portions of the Software. *
|
|
|
|
* *
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
|
|
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
|
|
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
|
|
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY *
|
|
|
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, *
|
|
|
|
* 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>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
#define LUA_LIB
|
2013-11-27 03:17:25 -05:00
|
|
|
#include "lua/src/lua.h"
|
|
|
|
#include "lua/src/lauxlib.h"
|
2013-04-07 15:28:32 -04:00
|
|
|
|
|
|
|
#if LUA_VERSION_NUM > 501
|
|
|
|
//
|
|
|
|
// Lua 5.2
|
|
|
|
//
|
|
|
|
#define lua_strlen lua_rawlen
|
|
|
|
// 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
|
|
|
|
#define luaL_register(L,name,reg) lua_newtable(L);luaL_setfuncs(L,reg,0)
|
|
|
|
// luaL_openlib always used with name == NULL
|
|
|
|
#define luaL_openlib(L,name,reg,nup) luaL_setfuncs(L,reg,nup)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "sqlite3.h"
|
|
|
|
|
|
|
|
/* compile time features */
|
|
|
|
#if !defined(SQLITE_OMIT_PROGRESS_CALLBACK)
|
|
|
|
#define SQLITE_OMIT_PROGRESS_CALLBACK 0
|
|
|
|
#endif
|
|
|
|
#if !defined(LSQLITE_OMIT_UPDATE_HOOK)
|
|
|
|
#define LSQLITE_OMIT_UPDATE_HOOK 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
typedef struct sdb sdb;
|
|
|
|
typedef struct sdb_vm sdb_vm;
|
|
|
|
typedef struct sdb_func sdb_func;
|
|
|
|
|
|
|
|
/* to use as C user data so i know what function sqlite is calling */
|
|
|
|
struct sdb_func {
|
|
|
|
/* references to associated lua values */
|
|
|
|
int fn_step;
|
|
|
|
int fn_finalize;
|
|
|
|
int udata;
|
|
|
|
|
|
|
|
sdb *db;
|
|
|
|
char aggregate;
|
|
|
|
|
|
|
|
sdb_func *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* information about database */
|
|
|
|
struct sdb {
|
|
|
|
/* associated lua state */
|
|
|
|
lua_State *L;
|
|
|
|
/* sqlite database handle */
|
|
|
|
sqlite3 *db;
|
|
|
|
|
|
|
|
/* sql functions stack usage */
|
|
|
|
sdb_func *func; /* top SQL function being called */
|
|
|
|
|
|
|
|
/* references */
|
|
|
|
int busy_cb; /* busy callback */
|
|
|
|
int busy_udata;
|
|
|
|
|
|
|
|
int progress_cb; /* progress handler */
|
|
|
|
int progress_udata;
|
|
|
|
|
|
|
|
int trace_cb; /* trace callback */
|
|
|
|
int trace_udata;
|
|
|
|
|
|
|
|
#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK
|
|
|
|
|
|
|
|
int update_hook_cb; /* update_hook callback */
|
|
|
|
int update_hook_udata;
|
|
|
|
|
|
|
|
int commit_hook_cb; /* commit_hook callback */
|
|
|
|
int commit_hook_udata;
|
|
|
|
|
|
|
|
int rollback_hook_cb; /* rollback_hook callback */
|
|
|
|
int rollback_hook_udata;
|
|
|
|
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *sqlite_meta = ":sqlite3";
|
|
|
|
static const char *sqlite_vm_meta = ":sqlite3:vm";
|
|
|
|
static const char *sqlite_ctx_meta = ":sqlite3:ctx";
|
|
|
|
static int sqlite_ctx_meta_ref;
|
|
|
|
|
|
|
|
/*
|
|
|
|
** =======================================================
|
|
|
|
** Database Virtual Machine Operations
|
|
|
|
** =======================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SQLITE_FLOAT:
|
|
|
|
lua_pushnumber(L, sqlite3_column_double(vm, idx));
|
|
|
|
break;
|
|
|
|
case SQLITE_TEXT:
|
|
|
|
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));
|
|
|
|
break;
|
|
|
|
case SQLITE_NULL:
|
|
|
|
lua_pushnil(L);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
lua_pushnil(L);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* virtual machine information */
|
|
|
|
struct sdb_vm {
|
|
|
|
sdb *db; /* associated database handle */
|
|
|
|
sqlite3_stmt *vm; /* virtual machine */
|
|
|
|
|
|
|
|
/* sqlite3_step info */
|
|
|
|
int columns; /* number of columns in result */
|
|
|
|
char has_values; /* true when step succeeds */
|
|
|
|
|
|
|
|
char temp; /* temporary vm used in db:rows */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* called with 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));
|
|
|
|
|
|
|
|
luaL_getmetatable(L, sqlite_vm_meta);
|
|
|
|
lua_setmetatable(L, -2); /* set metatable */
|
|
|
|
|
|
|
|
svm->db = db;
|
|
|
|
svm->columns = 0;
|
|
|
|
svm->has_values = 0;
|
|
|
|
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);
|
|
|
|
|
|
|
|
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);
|
|
|
|
lua_pushlightuserdata(L, svm);
|
|
|
|
lua_pushnil(L);
|
|
|
|
lua_rawset(L, -3);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
|
|
|
svm->columns = 0;
|
|
|
|
svm->has_values = 0;
|
|
|
|
|
|
|
|
if (!svm->vm) return 0;
|
|
|
|
|
|
|
|
lua_pushnumber(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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static sdb_vm *lsqlite_getvm(lua_State *L, int index) {
|
|
|
|
sdb_vm *svm = (sdb_vm*)luaL_checkudata(L, index, sqlite_vm_meta);
|
|
|
|
if (svm == NULL) luaL_argerror(L, index, "bad sqlite virtual machine");
|
|
|
|
return svm;
|
|
|
|
}
|
|
|
|
|
|
|
|
static sdb_vm *lsqlite_checkvm(lua_State *L, int index) {
|
|
|
|
sdb_vm *svm = lsqlite_getvm(L, index);
|
|
|
|
if (svm->vm == NULL) luaL_argerror(L, index, "attempt to use closed sqlite virtual machine");
|
|
|
|
return svm;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_isopen(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_getvm(L, 1);
|
|
|
|
lua_pushboolean(L, svm->vm != NULL ? 1 : 0);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_tostring(lua_State *L) {
|
|
|
|
char buff[39];
|
|
|
|
sdb_vm *svm = lsqlite_getvm(L, 1);
|
|
|
|
if (svm->vm == NULL)
|
|
|
|
strcpy(buff, "closed");
|
|
|
|
else
|
|
|
|
sprintf(buff, "%p", svm);
|
|
|
|
lua_pushfstring(L, "sqlite virtual machine (%s)", buff);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_gc(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_getvm(L, 1);
|
|
|
|
if (svm->vm != NULL) /* ignore closed vms */
|
|
|
|
cleanupvm(L, svm);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_step(lua_State *L) {
|
|
|
|
int result;
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
|
|
|
|
result = stepvm(L, svm);
|
|
|
|
svm->has_values = result == SQLITE_ROW ? 1 : 0;
|
|
|
|
svm->columns = sqlite3_data_count(svm->vm);
|
|
|
|
|
|
|
|
lua_pushnumber(L, result);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_finalize(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
return cleanupvm(L, svm);
|
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dbvm_check_contents(lua_State *L, sdb_vm *svm) {
|
|
|
|
if (!svm->has_values) {
|
|
|
|
luaL_error(L, "misuse of function");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dbvm_check_index(lua_State *L, sdb_vm *svm, int index) {
|
|
|
|
if (index < 0 || index >= svm->columns) {
|
|
|
|
luaL_error(L, "index out of range [0..%d]", svm->columns - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dbvm_check_bind_index(lua_State *L, sdb_vm *svm, int index) {
|
|
|
|
if (index < 1 || index > sqlite3_bind_parameter_count(svm->vm)) {
|
|
|
|
luaL_error(L, "bind index out of range [1..%d]", sqlite3_bind_parameter_count(svm->vm));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** =======================================================
|
|
|
|
** Virtual Machine - generic info
|
|
|
|
** =======================================================
|
|
|
|
*/
|
|
|
|
static int dbvm_columns(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
lua_pushnumber(L, sqlite3_column_count(svm->vm));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** =======================================================
|
|
|
|
** Virtual Machine - getters
|
|
|
|
** =======================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int dbvm_get_value(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
int index = luaL_checkint(L, 2);
|
|
|
|
dbvm_check_contents(L, svm);
|
|
|
|
dbvm_check_index(L, svm, index);
|
|
|
|
vm_push_column(L, svm->vm, index);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_get_name(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
int index = (int)luaL_checknumber(L, 2);
|
|
|
|
dbvm_check_index(L, svm, index);
|
|
|
|
lua_pushstring(L, sqlite3_column_name(svm->vm, index));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_get_type(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
int index = (int)luaL_checknumber(L, 2);
|
|
|
|
dbvm_check_index(L, svm, index);
|
|
|
|
lua_pushstring(L, sqlite3_column_decltype(svm->vm, index));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_get_values(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
sqlite3_stmt *vm = svm->vm;
|
|
|
|
int columns = svm->columns;
|
|
|
|
int n;
|
|
|
|
dbvm_check_contents(L, svm);
|
|
|
|
|
|
|
|
lua_newtable(L);
|
|
|
|
for (n = 0; n < columns;) {
|
|
|
|
vm_push_column(L, vm, n++);
|
|
|
|
lua_rawseti(L, -2, n);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_get_names(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
sqlite3_stmt *vm = svm->vm;
|
|
|
|
int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */
|
|
|
|
int n;
|
|
|
|
|
|
|
|
lua_newtable(L);
|
|
|
|
for (n = 0; n < columns;) {
|
|
|
|
lua_pushstring(L, sqlite3_column_name(vm, n++));
|
|
|
|
lua_rawseti(L, -2, n);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_get_types(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
sqlite3_stmt *vm = svm->vm;
|
|
|
|
int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */
|
|
|
|
int n;
|
|
|
|
|
|
|
|
lua_newtable(L);
|
|
|
|
for (n = 0; n < columns;) {
|
|
|
|
lua_pushstring(L, sqlite3_column_decltype(vm, n++));
|
|
|
|
lua_rawseti(L, -2, n);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_get_uvalues(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
sqlite3_stmt *vm = svm->vm;
|
|
|
|
int columns = svm->columns;
|
|
|
|
int n;
|
|
|
|
dbvm_check_contents(L, svm);
|
|
|
|
|
|
|
|
lua_checkstack(L, columns);
|
|
|
|
for (n = 0; n < columns; ++n)
|
|
|
|
vm_push_column(L, vm, n);
|
|
|
|
return columns;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_get_unames(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
sqlite3_stmt *vm = svm->vm;
|
|
|
|
int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */
|
|
|
|
int n;
|
|
|
|
|
|
|
|
lua_checkstack(L, columns);
|
|
|
|
for (n = 0; n < columns; ++n)
|
|
|
|
lua_pushstring(L, sqlite3_column_name(vm, n));
|
|
|
|
return columns;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_get_utypes(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
sqlite3_stmt *vm = svm->vm;
|
|
|
|
int columns = sqlite3_column_count(vm); /* valid as soon as statement prepared */
|
|
|
|
int n;
|
|
|
|
|
|
|
|
lua_checkstack(L, columns);
|
|
|
|
for (n = 0; n < columns; ++n)
|
|
|
|
lua_pushstring(L, sqlite3_column_decltype(vm, n));
|
|
|
|
return columns;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_get_named_values(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
sqlite3_stmt *vm = svm->vm;
|
|
|
|
int columns = svm->columns;
|
|
|
|
int n;
|
|
|
|
dbvm_check_contents(L, svm);
|
|
|
|
|
|
|
|
lua_newtable(L);
|
|
|
|
for (n = 0; n < columns; ++n) {
|
|
|
|
lua_pushstring(L, sqlite3_column_name(vm, n));
|
|
|
|
vm_push_column(L, vm, n);
|
|
|
|
lua_rawset(L, -3);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_get_named_types(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
sqlite3_stmt *vm = svm->vm;
|
|
|
|
int columns = sqlite3_column_count(vm);
|
|
|
|
int n;
|
|
|
|
|
|
|
|
lua_newtable(L);
|
|
|
|
for (n = 0; n < columns; ++n) {
|
|
|
|
lua_pushstring(L, sqlite3_column_name(vm, n));
|
|
|
|
lua_pushstring(L, sqlite3_column_decltype(vm, n));
|
|
|
|
lua_rawset(L, -3);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** =======================================================
|
|
|
|
** Virtual Machine - Bind
|
|
|
|
** =======================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int dbvm_bind_index(lua_State *L, sqlite3_stmt *vm, int index, int lindex) {
|
|
|
|
switch (lua_type(L, lindex)) {
|
|
|
|
case LUA_TSTRING:
|
|
|
|
return sqlite3_bind_text(vm, index, lua_tostring(L, lindex), lua_strlen(L, lindex), SQLITE_TRANSIENT);
|
|
|
|
case LUA_TNUMBER:
|
|
|
|
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);
|
|
|
|
case LUA_TNONE:
|
|
|
|
case LUA_TNIL:
|
|
|
|
return sqlite3_bind_null(vm, index);
|
|
|
|
default:
|
|
|
|
luaL_error(L, "index (%d) - invalid data type for bind (%s)", index, lua_typename(L, lua_type(L, lindex)));
|
|
|
|
return SQLITE_MISUSE; /*!*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
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);
|
|
|
|
dbvm_check_bind_index(L, svm, index);
|
|
|
|
lua_pushstring(L, sqlite3_bind_parameter_name(svm->vm, index));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_bind(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
sqlite3_stmt *vm = svm->vm;
|
|
|
|
int index = luaL_checkint(L, 2);
|
|
|
|
int result;
|
|
|
|
|
|
|
|
dbvm_check_bind_index(L, svm, index);
|
|
|
|
result = dbvm_bind_index(L, vm, index, 3);
|
|
|
|
|
|
|
|
lua_pushnumber(L, result);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_bind_blob(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
int index = luaL_checkint(L, 2);
|
|
|
|
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));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_bind_values(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
sqlite3_stmt *vm = svm->vm;
|
|
|
|
int top = lua_gettop(L);
|
|
|
|
int result, n;
|
|
|
|
|
|
|
|
if (top - 1 != sqlite3_bind_parameter_count(vm))
|
|
|
|
luaL_error(L,
|
|
|
|
"incorrect number of parameters to bind (%d given, %d to bind)",
|
|
|
|
top - 1,
|
|
|
|
sqlite3_bind_parameter_count(vm)
|
|
|
|
);
|
|
|
|
|
|
|
|
for (n = 2; n <= top; ++n) {
|
|
|
|
if ((result = dbvm_bind_index(L, vm, n - 1, n)) != SQLITE_OK) {
|
|
|
|
lua_pushnumber(L, result);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_pushnumber(L, SQLITE_OK);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dbvm_bind_names(lua_State *L) {
|
|
|
|
sdb_vm *svm = lsqlite_checkvm(L, 1);
|
|
|
|
sqlite3_stmt *vm = svm->vm;
|
|
|
|
int count = sqlite3_bind_parameter_count(vm);
|
|
|
|
const char *name;
|
|
|
|
int result, n;
|
|
|
|
luaL_checktype(L, 2, LUA_TTABLE);
|
|
|
|
|
|
|
|
for (n = 1; n <= count; ++n) {
|
|
|
|
name = sqlite3_bind_parameter_name(vm, n);
|
|
|
|
if (name && (name[0] == ':' || name[0] == '$')) {
|
|
|
|
lua_pushstring(L, ++name);
|
|
|
|
lua_gettable(L, 2);
|
|
|
|
result = dbvm_bind_index(L, vm, n, -1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
lua_pushnumber(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);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_pushnumber(L, SQLITE_OK);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** =======================================================
|
|
|
|
** Database (internal management)
|
|
|
|
** =======================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
** When creating database handles, always creates a `closed' database handle
|
|
|
|
** before opening the actual database; so, if there is a memory error, the
|
|
|
|
** database is not left opened.
|
|
|
|
**
|
|
|
|
** Creates a new 'table' and leaves it in the stack
|
|
|
|
*/
|
|
|
|
static sdb *newdb (lua_State *L) {
|
|
|
|
sdb *db = (sdb*)lua_newuserdata(L, sizeof(sdb));
|
|
|
|
db->L = L;
|
|
|
|
db->db = NULL; /* database handle is currently `closed' */
|
|
|
|
db->func = NULL;
|
|
|
|
|
|
|
|
db->busy_cb =
|
|
|
|
db->busy_udata =
|
|
|
|
db->progress_cb =
|
|
|
|
db->progress_udata =
|
|
|
|
db->trace_cb =
|
|
|
|
db->trace_udata =
|
|
|
|
#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK
|
|
|
|
db->update_hook_cb =
|
|
|
|
db->update_hook_udata =
|
|
|
|
db->commit_hook_cb =
|
|
|
|
db->commit_hook_udata =
|
|
|
|
db->rollback_hook_cb =
|
|
|
|
db->rollback_hook_udata =
|
|
|
|
#endif
|
|
|
|
LUA_NOREF;
|
|
|
|
|
|
|
|
luaL_getmetatable(L, sqlite_meta);
|
|
|
|
lua_setmetatable(L, -2); /* set metatable */
|
|
|
|
|
|
|
|
/* to keep track of 'open' virtual machines */
|
|
|
|
lua_pushlightuserdata(L, db);
|
|
|
|
lua_newtable(L);
|
|
|
|
lua_rawset(L, LUA_REGISTRYINDEX);
|
|
|
|
|
|
|
|
return db;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cleanupdb(lua_State *L, sdb *db) {
|
|
|
|
sdb_func *func;
|
|
|
|
sdb_func *func_next;
|
|
|
|
int top;
|
|
|
|
int result;
|
|
|
|
|
|
|
|
/* free associated virtual machines */
|
|
|
|
lua_pushlightuserdata(L, db);
|
|
|
|
lua_rawget(L, LUA_REGISTRYINDEX);
|
|
|
|
|
|
|
|
/* close all used handles */
|
|
|
|
top = lua_gettop(L);
|
|
|
|
lua_pushnil(L);
|
|
|
|
while (lua_next(L, -2)) {
|
|
|
|
sdb_vm *svm = lua_touserdata(L, -2); /* key: vm; val: sql text */
|
|
|
|
cleanupvm(L, svm);
|
|
|
|
|
|
|
|
lua_settop(L, top);
|
|
|
|
lua_pushnil(L);
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_pop(L, 1); /* pop vm table */
|
|
|
|
|
|
|
|
/* remove entry in lua registry table */
|
|
|
|
lua_pushlightuserdata(L, db);
|
|
|
|
lua_pushnil(L);
|
|
|
|
lua_rawset(L, LUA_REGISTRYINDEX);
|
|
|
|
|
|
|
|
/* 'free' all references */
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb);
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata);
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, db->progress_cb);
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, db->progress_udata);
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, db->trace_cb);
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, db->trace_udata);
|
|
|
|
#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_cb);
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_udata);
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_cb);
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_udata);
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_cb);
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_udata);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* close database */
|
|
|
|
result = sqlite3_close(db->db);
|
|
|
|
db->db = NULL;
|
|
|
|
|
|
|
|
/* free associated memory with created functions */
|
|
|
|
func = db->func;
|
|
|
|
while (func) {
|
|
|
|
func_next = func->next;
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, func->fn_step);
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, func->fn_finalize);
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, func->udata);
|
|
|
|
free(func);
|
|
|
|
func = func_next;
|
|
|
|
}
|
|
|
|
db->func = NULL;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static sdb *lsqlite_getdb(lua_State *L, int index) {
|
|
|
|
sdb *db = (sdb*)luaL_checkudata(L, index, sqlite_meta);
|
|
|
|
if (db == NULL) luaL_typerror(L, index, "sqlite database");
|
|
|
|
return db;
|
|
|
|
}
|
|
|
|
|
|
|
|
static sdb *lsqlite_checkdb(lua_State *L, int index) {
|
|
|
|
sdb *db = lsqlite_getdb(L, index);
|
|
|
|
if (db->db == NULL) luaL_argerror(L, index, "attempt to use closed sqlite database");
|
|
|
|
return db;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
** =======================================================
|
|
|
|
** User Defined Functions - Context Methods
|
|
|
|
** =======================================================
|
|
|
|
*/
|
|
|
|
typedef struct {
|
|
|
|
sqlite3_context *ctx;
|
|
|
|
int ud;
|
|
|
|
} lcontext;
|
|
|
|
|
|
|
|
static lcontext *lsqlite_make_context(lua_State *L) {
|
|
|
|
lcontext *ctx = (lcontext*)lua_newuserdata(L, sizeof(lcontext));
|
|
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, sqlite_ctx_meta_ref);
|
|
|
|
lua_setmetatable(L, -2);
|
|
|
|
ctx->ctx = NULL;
|
|
|
|
ctx->ud = LUA_NOREF;
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
static lcontext *lsqlite_getcontext(lua_State *L, int index) {
|
|
|
|
lcontext *ctx = (lcontext*)luaL_checkudata(L, index, sqlite_ctx_meta);
|
|
|
|
if (ctx == NULL) luaL_typerror(L, index, "sqlite context");
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
static lcontext *lsqlite_checkcontext(lua_State *L, int index) {
|
|
|
|
lcontext *ctx = lsqlite_getcontext(L, index);
|
|
|
|
if (ctx->ctx == NULL) luaL_argerror(L, index, "invalid sqlite context");
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int lcontext_tostring(lua_State *L) {
|
|
|
|
char buff[39];
|
|
|
|
lcontext *ctx = lsqlite_getcontext(L, 1);
|
|
|
|
if (ctx->ctx == NULL)
|
|
|
|
strcpy(buff, "closed");
|
|
|
|
else
|
|
|
|
sprintf(buff, "%p", ctx->ctx);
|
|
|
|
lua_pushfstring(L, "sqlite function context (%s)", buff);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void lcontext_check_aggregate(lua_State *L, lcontext *ctx) {
|
|
|
|
sdb_func *func = (sdb_func*)sqlite3_user_data(ctx->ctx);
|
|
|
|
if (!func->aggregate) {
|
|
|
|
luaL_error(L, "attempt to call aggregate method from scalar function");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int lcontext_user_data(lua_State *L) {
|
|
|
|
lcontext *ctx = lsqlite_checkcontext(L, 1);
|
|
|
|
sdb_func *func = (sdb_func*)sqlite3_user_data(ctx->ctx);
|
|
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, func->udata);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int lcontext_get_aggregate_context(lua_State *L) {
|
|
|
|
lcontext *ctx = lsqlite_checkcontext(L, 1);
|
|
|
|
lcontext_check_aggregate(L, ctx);
|
|
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, ctx->ud);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int lcontext_set_aggregate_context(lua_State *L) {
|
|
|
|
lcontext *ctx = lsqlite_checkcontext(L, 1);
|
|
|
|
lcontext_check_aggregate(L, ctx);
|
|
|
|
lua_settop(L, 2);
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud);
|
|
|
|
ctx->ud = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
void *sqlite3_get_auxdata(sqlite3_context*, int);
|
|
|
|
void sqlite3_set_auxdata(sqlite3_context*, int, void*, void (*)(void*));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int lcontext_result(lua_State *L) {
|
|
|
|
lcontext *ctx = lsqlite_checkcontext(L, 1);
|
|
|
|
switch (lua_type(L, 2)) {
|
|
|
|
case LUA_TNUMBER:
|
|
|
|
sqlite3_result_double(ctx->ctx, luaL_checknumber(L, 2));
|
|
|
|
break;
|
|
|
|
case LUA_TSTRING:
|
|
|
|
sqlite3_result_text(ctx->ctx, luaL_checkstring(L, 2), lua_strlen(L, 2), SQLITE_TRANSIENT);
|
|
|
|
break;
|
|
|
|
case LUA_TNIL:
|
|
|
|
case LUA_TNONE:
|
|
|
|
sqlite3_result_null(ctx->ctx);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
luaL_error(L, "invalid result type %s", lua_typename(L, 2));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int lcontext_result_blob(lua_State *L) {
|
|
|
|
lcontext *ctx = lsqlite_checkcontext(L, 1);
|
|
|
|
const char *blob = luaL_checkstring(L, 2);
|
|
|
|
int size = lua_strlen(L, 2);
|
|
|
|
sqlite3_result_blob(ctx->ctx, (const void*)blob, size, SQLITE_TRANSIENT);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int lcontext_result_double(lua_State *L) {
|
|
|
|
lcontext *ctx = lsqlite_checkcontext(L, 1);
|
|
|
|
double d = luaL_checknumber(L, 2);
|
|
|
|
sqlite3_result_double(ctx->ctx, d);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int lcontext_result_error(lua_State *L) {
|
|
|
|
lcontext *ctx = lsqlite_checkcontext(L, 1);
|
|
|
|
const char *err = luaL_checkstring(L, 2);
|
|
|
|
int size = lua_strlen(L, 2);
|
|
|
|
sqlite3_result_error(ctx->ctx, err, size);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int lcontext_result_int(lua_State *L) {
|
|
|
|
lcontext *ctx = lsqlite_checkcontext(L, 1);
|
|
|
|
int i = luaL_checkint(L, 2);
|
|
|
|
sqlite3_result_int(ctx->ctx, i);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int lcontext_result_null(lua_State *L) {
|
|
|
|
lcontext *ctx = lsqlite_checkcontext(L, 1);
|
|
|
|
sqlite3_result_null(ctx->ctx);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int lcontext_result_text(lua_State *L) {
|
|
|
|
lcontext *ctx = lsqlite_checkcontext(L, 1);
|
|
|
|
const char *text = luaL_checkstring(L, 2);
|
|
|
|
int size = lua_strlen(L, 2);
|
|
|
|
sqlite3_result_text(ctx->ctx, text, size, SQLITE_TRANSIENT);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** =======================================================
|
|
|
|
** Database Methods
|
|
|
|
** =======================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int db_isopen(lua_State *L) {
|
|
|
|
sdb *db = lsqlite_getdb(L, 1);
|
|
|
|
lua_pushboolean(L, db->db != NULL ? 1 : 0);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int db_changes(lua_State *L) {
|
|
|
|
sdb *db = lsqlite_checkdb(L, 1);
|
|
|
|
lua_pushnumber(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));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int db_errcode(lua_State *L) {
|
|
|
|
sdb *db = lsqlite_checkdb(L, 1);
|
|
|
|
lua_pushnumber(L, sqlite3_errcode(db->db));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int db_errmsg(lua_State *L) {
|
|
|
|
sdb *db = lsqlite_checkdb(L, 1);
|
|
|
|
lua_pushstring(L, sqlite3_errmsg(db->db));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int db_interrupt(lua_State *L) {
|
|
|
|
sdb *db = lsqlite_checkdb(L, 1);
|
|
|
|
sqlite3_interrupt(db->db);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Registering SQL functions:
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void db_push_value(lua_State *L, sqlite3_value *value) {
|
|
|
|
switch (sqlite3_value_type(value)) {
|
|
|
|
case SQLITE_TEXT:
|
|
|
|
lua_pushlstring(L, (const char*)sqlite3_value_text(value), sqlite3_value_bytes(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));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SQLITE_FLOAT:
|
|
|
|
lua_pushnumber(L, sqlite3_value_double(value));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SQLITE_BLOB:
|
|
|
|
lua_pushlstring(L, sqlite3_value_blob(value), sqlite3_value_bytes(value));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SQLITE_NULL:
|
|
|
|
lua_pushnil(L);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* things done properly (SQLite + Lua SQLite)
|
|
|
|
** this should never happen */
|
|
|
|
lua_pushnil(L);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** callback functions used when calling registered sql functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* scalar function to be called
|
|
|
|
** callback params: context, values... */
|
|
|
|
static void db_sql_normal_function(sqlite3_context *context, int argc, sqlite3_value **argv) {
|
|
|
|
sdb_func *func = (sdb_func*)sqlite3_user_data(context);
|
|
|
|
lua_State *L = func->db->L;
|
|
|
|
int n;
|
|
|
|
lcontext *ctx;
|
|
|
|
|
|
|
|
int top = lua_gettop(L);
|
|
|
|
|
|
|
|
/* ensure there is enough space in the stack */
|
|
|
|
lua_checkstack(L, argc + 3);
|
|
|
|
|
|
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, func->fn_step); /* function to call */
|
|
|
|
|
|
|
|
if (!func->aggregate) {
|
|
|
|
ctx = lsqlite_make_context(L); /* push context - used to set results */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* reuse context userdata value */
|
|
|
|
void *p = sqlite3_aggregate_context(context, 1);
|
|
|
|
/* i think it is OK to use assume that using a light user data
|
|
|
|
** as an entry on LUA REGISTRY table will be unique */
|
|
|
|
lua_pushlightuserdata(L, p);
|
|
|
|
lua_rawget(L, LUA_REGISTRYINDEX); /* context table */
|
|
|
|
|
|
|
|
if (lua_isnil(L, -1)) { /* not yet created? */
|
|
|
|
lua_pop(L, 1);
|
|
|
|
ctx = lsqlite_make_context(L);
|
|
|
|
lua_pushlightuserdata(L, p);
|
|
|
|
lua_pushvalue(L, -2);
|
|
|
|
lua_rawset(L, LUA_REGISTRYINDEX);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ctx = lsqlite_getcontext(L, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* push params */
|
|
|
|
for (n = 0; n < argc; ++n) {
|
|
|
|
db_push_value(L, argv[n]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set context */
|
|
|
|
ctx->ctx = context;
|
|
|
|
|
|
|
|
if (lua_pcall(L, argc + 1, 0, 0)) {
|
|
|
|
const char *errmsg = lua_tostring(L, -1);
|
|
|
|
int size = lua_strlen(L, -1);
|
|
|
|
sqlite3_result_error(context, errmsg, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* invalidate context */
|
|
|
|
ctx->ctx = NULL;
|
|
|
|
|
|
|
|
if (!func->aggregate) {
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud);
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_settop(L, top);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void db_sql_finalize_function(sqlite3_context *context) {
|
|
|
|
sdb_func *func = (sdb_func*)sqlite3_user_data(context);
|
|
|
|
lua_State *L = func->db->L;
|
|
|
|
void *p = sqlite3_aggregate_context(context, 1); /* minimal mem usage */
|
|
|
|
lcontext *ctx;
|
|
|
|
int top = lua_gettop(L);
|
|
|
|
|
|
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, func->fn_finalize); /* function to call */
|
|
|
|
|
|
|
|
/* i think it is OK to use assume that using a light user data
|
|
|
|
** as an entry on LUA REGISTRY table will be unique */
|
|
|
|
lua_pushlightuserdata(L, p);
|
|
|
|
lua_rawget(L, LUA_REGISTRYINDEX); /* context table */
|
|
|
|
|
|
|
|
if (lua_isnil(L, -1)) { /* not yet created? - shouldn't happen in finalize function */
|
|
|
|
lua_pop(L, 1);
|
|
|
|
ctx = lsqlite_make_context(L);
|
|
|
|
lua_pushlightuserdata(L, p);
|
|
|
|
lua_pushvalue(L, -2);
|
|
|
|
lua_rawset(L, LUA_REGISTRYINDEX);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ctx = lsqlite_getcontext(L, -1);
|
|
|
|
|
|
|
|
/* set context */
|
|
|
|
ctx->ctx = context;
|
|
|
|
|
|
|
|
if (lua_pcall(L, 1, 0, 0)) {
|
|
|
|
sqlite3_result_error(context, lua_tostring(L, -1), -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* invalidate context */
|
|
|
|
ctx->ctx = NULL;
|
|
|
|
|
|
|
|
/* cleanup context */
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, ctx->ud);
|
|
|
|
/* remove it from registry */
|
|
|
|
lua_pushlightuserdata(L, p);
|
|
|
|
lua_pushnil(L);
|
|
|
|
lua_rawset(L, LUA_REGISTRYINDEX);
|
|
|
|
|
|
|
|
lua_settop(L, top);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** Register a normal function
|
|
|
|
** Params: db, function name, number arguments, [ callback | step, finalize], user data
|
|
|
|
** Returns: true on sucess
|
|
|
|
**
|
|
|
|
** Normal function:
|
|
|
|
** Params: context, params
|
|
|
|
**
|
|
|
|
** Aggregate function:
|
|
|
|
** Params of step: context, params
|
|
|
|
** Params of finalize: context
|
|
|
|
*/
|
|
|
|
static int db_register_function(lua_State *L, int aggregate) {
|
|
|
|
sdb *db = lsqlite_checkdb(L, 1);
|
|
|
|
const char *name;
|
|
|
|
int args;
|
|
|
|
int result;
|
|
|
|
sdb_func *func;
|
|
|
|
|
|
|
|
/* safety measure */
|
|
|
|
if (aggregate) aggregate = 1;
|
|
|
|
|
|
|
|
name = luaL_checkstring(L, 2);
|
|
|
|
|