Files
Domoticz/lua-sqlite3-0.4.1/libluasqlite3.c
2025-03-06 11:09:58 +01:00

1642 lines
38 KiB
C

/*
* Author: Michael Roth <mroth@nessie.de>
*
* Copyright (c) 2004, 2005, 2006 Michael Roth <mroth@nessie.de>
*
* 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.
*
*/
#include <stdio.h>
#include <sqlite3.h>
#include <lua.h>
#include <lauxlib.h>
/*
* Not exported to Lua:
*
* sqlite3_get_table Really doesn't make sense, doesn't it?
*
* sqlite3_mprintf Won't work because I don't know now how to pass a variable
* numbers of arguments from lua to a C vararg function.
*
* sqlite3_user_data Makes no sense to export, only for internal usage.
*
*/
#define IS_INT(n) ( ( (lua_Number) ((int)(n)) ) == (n) )
#define CAST(type, arg) ( (type)(arg) )
#define FUNC(name) static int name (lua_State * L)
#define CB_DATA(ptr) CAST(CB_Data *, (ptr))
#define KEY(ptr, id) CAST( void*, CAST(char*,(ptr))+(id) )
#define KEY_KEY2VALUE_TABLE(p) KEY((p), 1)
#define KEY_FUNCTION_TABLE(p) KEY((p), 2)
#define KEY_COLLATION_TABLE(p) KEY((p), 3)
#define KEY_COLLNEEDED_DATA(p) KEY((p), 4)
#define KEY_AUTHORIZER_DATA(p) KEY((p), 5)
#define KEY_PROGRESS_DATA(p) KEY((p), 6)
#define KEY_TRACE_DATA(p) KEY((p), 7)
#define KEY_BUSY_DATA(p) KEY((p), 8)
#define KEY_COMMIT_DATA(p) KEY((p), 9)
#define KEY_XFUNC(p) KEY((p), 1)
#define KEY_XSTEP(p) KEY((p), 2)
#define KEY_XFINAL(p) KEY((p), 3)
#define KEY_XCOMPARE(p) KEY((p), 1)
#define KEY_XNEEDED(p) KEY((p), 1)
#define KEY_XAUTH(p) KEY((p), 1)
#define KEY_XPROGRESS(p) KEY((p), 1)
#define KEY_XTRACE(p) KEY((p), 1)
#define KEY_XBUSY(p) KEY((p), 1)
#define KEY_XCOMMIT(p) KEY((p), 1)
typedef struct
{
sqlite3 * sqlite3;
lua_State * L;
int key2value_pos; /* Used by callback wrappers to find the key2value array on the lua stack */
} DB;
typedef struct
{
DB * db;
sqlite3_stmt * stmt;
} Stmt;
typedef struct
{
DB * db;
} CB_Data;
static void push_private_table(lua_State * L, void * table_key);
static void delete_private_value(lua_State * L, void * value_key);
static CB_Data * new_cb_data(lua_State * L, DB * db);
static CB_Data * get_cb_data(lua_State * L, DB * db, void * data_key);
static CB_Data * get_named_cb_data(lua_State * L, DB * db, void * table_key, int name_pos);
#define get_function_cb_data(L, db, name_pos) get_named_cb_data((L), (db), KEY_FUNCTION_TABLE(db), name_pos)
#define get_collation_cb_data(L, db, name_pos) get_named_cb_data((L), (db), KEY_COLLATION_TABLE(db), name_pos)
#define get_collneeded_cb_data(L, db) get_cb_data((L), (db), KEY_COLLNEEDED_DATA(db))
#define get_authorizer_cb_data(L, db) get_cb_data((L), (db), KEY_AUTHORIZER_DATA(db))
#define get_progress_cb_data(L, db) get_cb_data((L), (db), KEY_PROGRESS_DATA(db))
#define get_trace_cb_data(L, db) get_cb_data((L), (db), KEY_TRACE_DATA(db))
#define get_busy_cb_data(L, db) get_cb_data((L), (db), KEY_BUSY_DATA(db))
#define get_commit_cb_data(L, db) get_cb_data((L), (db), KEY_COMMIT_DATA(db))
static void register_callback(lua_State * L, DB * db, void * cb_key, int callback_pos);
static void init_callback_usage(lua_State * L, DB * db);
static void push_callback(lua_State * L, DB * db, void * cb_key);
static int pop_break_condition(lua_State * L);
static void push_nil_or_string(lua_State * L, const char * str);
static void push_private_table(lua_State * L, void * table_key)
{
lua_pushlightuserdata(L, table_key);
lua_rawget(L, LUA_REGISTRYINDEX);
if (lua_isnil(L, -1))
{
lua_pop(L, 1);
lua_newtable(L);
lua_pushlightuserdata(L, table_key);
lua_pushvalue(L, -2);
lua_rawset(L, LUA_REGISTRYINDEX);
}
}
static void delete_private_value(lua_State * L, void * value_key)
{
lua_pushlightuserdata(L, value_key);
lua_rawget(L, LUA_REGISTRYINDEX);
if (!lua_isnil(L, -1))
{
lua_pushlightuserdata(L, value_key);
lua_pushnil(L);
lua_rawset(L, LUA_REGISTRYINDEX);
}
lua_pop(L, 1);
}
static CB_Data * new_cb_data(lua_State * L, DB * db)
{
CB_Data * cb_data = lua_newuserdata(L, sizeof(CB_Data));
cb_data->db = db;
return cb_data;
}
static CB_Data * get_cb_data(lua_State * L, DB * db, void * data_key)
{
CB_Data * cb_data;
lua_pushlightuserdata(L, data_key);
lua_rawget(L, LUA_REGISTRYINDEX);
if (lua_isnil(L, -1))
{
lua_pushlightuserdata(L, data_key);
cb_data = new_cb_data(L, db);
lua_rawset(L, LUA_REGISTRYINDEX);
}
else
cb_data = lua_touserdata(L, -1);
lua_pop(L, 1);
return cb_data;
}
static CB_Data * get_named_cb_data(lua_State * L, DB * db, void * table_key, int name_pos)
{
CB_Data * cb_data;
push_private_table(L, table_key);
lua_pushvalue(L, name_pos);
lua_rawget(L, -2);
if (lua_isnil(L, -1))
{
lua_pushvalue(L, name_pos);
cb_data = new_cb_data(L, db);
lua_rawset(L, LUA_REGISTRYINDEX);
}
else
cb_data = lua_touserdata(L, -1);
lua_pop(L, 2);
return cb_data;
}
static void register_callback(lua_State * L, DB * db, void * cb_key, int callback_pos)
{
push_private_table(L, KEY_KEY2VALUE_TABLE(db));
lua_pushlightuserdata(L, cb_key);
lua_pushvalue(L, callback_pos);
lua_rawset(L, -3);
lua_pop(L, 1);
}
static void init_callback_usage(lua_State * L, DB * db)
{
db->L = L;
db->key2value_pos = 0; /* lazy initialized in push_callback() */
}
static void push_callback(lua_State * L, DB * db, void * cb_key)
{
if (db->key2value_pos == 0) /* lazy initializing of the key2value table */
{
push_private_table(L, KEY_KEY2VALUE_TABLE(db));
db->key2value_pos = lua_gettop(L);
}
lua_pushlightuserdata(L, cb_key);
lua_rawget(L, db->key2value_pos);
}
static int pop_break_condition(lua_State * L)
{
int result;
if (lua_isnil(L, -1))
result = 0;
else if (lua_isboolean(L, -1))
result = lua_toboolean(L, -1);
else if (lua_isnumber(L, -1))
result = lua_tonumber(L, -1);
else
result = 1;
lua_pop(L, 1);
return result;
}
static void push_nil_or_string(lua_State * L, const char * str)
{
if (str)
lua_pushstring(L, str);
else
lua_pushnil(L);
}
/*
* Error Handling
* ==============
*
* We try to work hard to be bullet proof. We even try to function
* correctly in low memory situations.
* Usage errors of the API when detected raise an error also
* memory errors and such.
* Errors signaled from the sqlite library will result in the
* appropriate return codes.
*
*/
static void report_error(lua_State * L, const char * msg)
{
lua_settop(L, 0); /* Clear the stack to make sure, our error message will get a chance */
lua_pushstring(L, msg);
lua_error(L);
}
#define checkany(L, narg) ( luaL_checkany((L), (narg)) )
#define checkstr(L, narg) ( luaL_checklstring((L), (narg), 0) )
#define checknumber(L, narg) ( luaL_checknumber((L), (narg)) )
#define checkint(L, narg) ( (int) checknumber((L), (narg)) )
#define checkdouble(L, narg) ( (double) checknumber((L), (narg)) )
static void * checkudata(lua_State * L, int narg)
{
if (!lua_isuserdata(L, narg))
luaL_typerror(L, narg, "userdata");
return lua_touserdata(L, narg);
}
#define checkcontext(L, narg) ( (sqlite3_context *) checkudata((L), (narg)) )
#define checkvalues(L, narg) ( (sqlite3_value **) checkudata((L), (narg)) )
#define checkstmt(L, narg) ( (Stmt *) checkudata((L), (narg)) )
#define checkdb(L, narg) ( (DB *) checkudata((L), (narg)) )
static sqlite3_stmt * checkstmt_stmt(lua_State * L, int narg)
{
return checkstmt(L, narg)->stmt;
}
static sqlite3 * checkdb_sqlite3(lua_State * L, int narg)
{
return checkdb(L, narg)->sqlite3;
}
static int checknilornoneorfunc(lua_State * L, int narg)
{
if (lua_isnil(L, narg) || lua_isnone(L, narg))
return 0;
if (lua_isfunction(L, narg))
return 1;
luaL_typerror(L, narg, "nil, none or function");
return 0; /* never reached, make compiler happy... */
}
FUNC( l_sqlite3_bind_null )
{
lua_pushnumber(L, sqlite3_bind_null(checkstmt_stmt(L, 1), checkint(L, 2)) );
return 1;
}
FUNC( l_sqlite3_bind_text )
{
lua_pushnumber(L, sqlite3_bind_text(checkstmt_stmt(L, 1), checkint(L, 2), checkstr(L, 3), lua_strlen(L, 3), SQLITE_TRANSIENT) );
return 1;
}
FUNC( l_sqlite3_bind_blob )
{
lua_pushnumber(L, sqlite3_bind_blob(checkstmt_stmt(L, 1), checkint(L, 2), checkstr(L, 3), lua_strlen(L, 3), SQLITE_TRANSIENT) );
return 1;
}
FUNC( l_sqlite3_bind_int )
{
lua_pushnumber(L, sqlite3_bind_int(checkstmt_stmt(L, 1), checkint(L, 2), checkint(L, 3)) );
return 1;
}
FUNC( l_sqlite3_bind_double )
{
lua_pushnumber(L, sqlite3_bind_double(checkstmt_stmt(L, 1), checkint(L, 2), checkdouble(L, 3)) );
return 1;
}
FUNC( l_sqlite3_bind_number )
{
sqlite3_stmt * stmt = checkstmt_stmt(L, 1);
int index = checkint(L, 2);
lua_Number number = checknumber(L, 3);
if (IS_INT(number))
lua_pushnumber(L, sqlite3_bind_int(stmt, index, (int)number) );
else
lua_pushnumber(L, sqlite3_bind_double(stmt, index, (double)number) );
return 1;
}
FUNC( l_sqlite3_bind )
{
sqlite3_stmt * stmt = checkstmt_stmt(L, 1);
int index = checkint(L, 2);
switch(lua_type(L, 3))
{
case LUA_TNONE:
case LUA_TNIL:
lua_pushnumber(L, sqlite3_bind_null(stmt, index) );
break;
case LUA_TNUMBER:
{
lua_Number number = lua_tonumber(L, 3);
if (IS_INT(number))
lua_pushnumber(L, sqlite3_bind_int(stmt, index, (int)number) );
else
lua_pushnumber(L, sqlite3_bind_double(stmt, index, (double)number) );
}
break;
case LUA_TBOOLEAN:
if (lua_toboolean(L, 3))
lua_pushnumber(L, sqlite3_bind_int(stmt, index, 1) );
else
lua_pushnumber(L, sqlite3_bind_int(stmt, index, 0) );
break;
case LUA_TSTRING:
lua_pushnumber(L, sqlite3_bind_text(stmt, index, lua_tostring(L, 3), lua_strlen(L, 3), SQLITE_TRANSIENT) );
break;
default:
luaL_argerror(L, 3, "nil, boolean, number or string expected");
}
return 1;
}
FUNC( l_sqlite3_bind_parameter_count )
{
lua_pushnumber(L, sqlite3_bind_parameter_count(checkstmt_stmt(L, 1)) );
return 1;
}
FUNC( l_sqlite3_bind_parameter_name )
{
const char * name = sqlite3_bind_parameter_name(checkstmt_stmt(L, 1), checkint(L, 2));
if (name)
lua_pushstring(L, name);
else
lua_pushnil(L);
return 1;
}
FUNC( l_sqlite3_bind_parameter_name_x )
{
const char * name = sqlite3_bind_parameter_name(checkstmt_stmt(L, 1), checkint(L, 2));
if (name && *name)
lua_pushstring(L, name + 1); /* Ignore leading '$' or ':' */
else
lua_pushnil(L);
return 1;
}
FUNC( l_sqlite3_busy_timeout )
{
DB * db = checkdb(L, 1);
int timeout = checkint(L, 2);
delete_private_value(L, KEY_BUSY_DATA(db));
lua_pushnumber(L, sqlite3_busy_timeout(db->sqlite3, timeout) );
return 1;
}
FUNC( l_sqlite3_changes )
{
lua_pushnumber(L, sqlite3_changes(checkdb_sqlite3(L, 1)) );
return 1;
}
FUNC( l_sqlite3_close )
{
DB * db = checkdb(L, 1);
delete_private_value(L, KEY_KEY2VALUE_TABLE(db));
delete_private_value(L, KEY_FUNCTION_TABLE(db));
delete_private_value(L, KEY_COLLATION_TABLE(db));
delete_private_value(L, KEY_COLLNEEDED_DATA(db));
delete_private_value(L, KEY_AUTHORIZER_DATA(db));
delete_private_value(L, KEY_PROGRESS_DATA(db));
delete_private_value(L, KEY_TRACE_DATA(db));
delete_private_value(L, KEY_BUSY_DATA(db));
delete_private_value(L, KEY_COMMIT_DATA(db));
lua_pushnumber(L, sqlite3_close(db->sqlite3) );
return 1;
}
typedef const char * (*column_text_blob_t)(sqlite3_stmt *, int);
static int l_sqlite3_column_text_or_blob(lua_State * L, column_text_blob_t column_text_blob)
{
sqlite3_stmt * stmt = checkstmt_stmt(L, 1);
int column = checkint(L, 2);
lua_pushlstring(L, column_text_blob(stmt, column), sqlite3_column_bytes(stmt, column));
return 1;
}
FUNC( l_sqlite3_column_blob )
{
return l_sqlite3_column_text_or_blob(L, (column_text_blob_t)sqlite3_column_blob);
}
FUNC( l_sqlite3_column_text )
{
return l_sqlite3_column_text_or_blob(L, (column_text_blob_t)sqlite3_column_text); /* FIXME: remove cast when API changes!!! */
}
FUNC( l_sqlite3_column_int )
{
lua_pushnumber(L, sqlite3_column_int(checkstmt_stmt(L, 1), checkint(L, 2)) );
return 1;
}
FUNC( l_sqlite3_column_double )
{
lua_pushnumber(L, sqlite3_column_double(checkstmt_stmt(L, 1), checkint(L, 2)) );
return 1;
}
FUNC( l_sqlite3_column_number )
{
sqlite3_stmt * stmt = checkstmt_stmt(L, 1);
int column = checkint(L, 2);
if ( sqlite3_column_type(stmt, column) == SQLITE_INTEGER )
lua_pushnumber(L, sqlite3_column_int(stmt, column) );
else
lua_pushnumber(L,sqlite3_column_double(stmt, column) );
return 1;
}
static void push_column(lua_State * L, sqlite3_stmt * stmt, int column)
{
switch(sqlite3_column_type(stmt, column))
{
case SQLITE_NULL:
lua_pushnil(L);
break;
case SQLITE_INTEGER:
lua_pushnumber(L, sqlite3_column_int(stmt, column));
break;
case SQLITE_FLOAT:
lua_pushnumber(L, sqlite3_column_double(stmt, column));
break;
case SQLITE_TEXT:
lua_pushlstring(L, sqlite3_column_text(stmt, column), sqlite3_column_bytes(stmt, column));
break;
case SQLITE_BLOB:
lua_pushlstring(L, sqlite3_column_blob(stmt, column), sqlite3_column_bytes(stmt, column));
break;
default:
lua_pushboolean(L, 0);
}
}
FUNC( l_sqlite3_column )
{
push_column(L, checkstmt_stmt(L, 1), checkint(L, 2));
return 1;
}
/*
* mode: 0 = direct, 1 = integer, 2 = alphanumeric
*/
static int l_sqlite3_row_mode(lua_State * L, int mode)
{
/* Old code / Just a reminder / To be removed:
** checkargs(L, 1, 2, CHECK_PTR, CHECK_NILTABLE, 0);
*/
sqlite3_stmt * stmt = checkstmt_stmt(L, 1);
int num_columns = sqlite3_data_count(stmt); /* Maybe wrong state, so don't use sqlite3_column_count */
int index;
/* XXX Should really be cleaned up... Fixme! */
if (mode == 0)
lua_checkstack(L, num_columns);
else
if (!lua_istable(L, -1))
lua_newtable(L);
for (index=0; index<num_columns; index++)
switch(mode)
{
case 0: /* direct mode */
push_column(L, stmt, index);
break;
case 1: /* integer mode */
push_column(L, stmt, index);
lua_rawseti(L, -2, index+1);
break;
case 2: /* alphanumeric mode */
lua_pushstring(L, sqlite3_column_name(stmt, index));
push_column(L, stmt, index);
lua_rawset(L, -3);
break;
default:
report_error(L, "libluasqlite3: Internal error in sqlite3_row_mode");
}
if (mode)
return 1;
else
return num_columns;
}
FUNC( l_sqlite3_drow )
{
return l_sqlite3_row_mode(L, 0);
}
FUNC( l_sqlite3_irow )
{
return l_sqlite3_row_mode(L, 1);
}
FUNC( l_sqlite3_arow )
{
return l_sqlite3_row_mode(L, 2);
}
FUNC( l_sqlite3_column_type )
{
lua_pushnumber(L, sqlite3_column_type(checkstmt_stmt(L, 1), checkint(L, 2)) );
return 1;
}
FUNC( l_sqlite3_column_count )
{
lua_pushnumber(L, sqlite3_column_count(checkstmt_stmt(L, 1)) );
return 1;
}
static int l_sqlite3_column_info(lua_State * L, const char * (*info_func)(sqlite3_stmt*,int) )
{
const char * info = info_func(checkstmt_stmt(L, 1), checkint(L, 2));
if (info)
lua_pushstring(L, info);
else
lua_pushstring(L, "");
return 1;
}
FUNC( l_sqlite3_column_decltype )
{
return l_sqlite3_column_info(L, sqlite3_column_decltype);
}
FUNC( l_sqlite3_column_name )
{
return l_sqlite3_column_info(L, sqlite3_column_name);
}
FUNC( l_sqlite3_complete )
{
lua_pushboolean(L, sqlite3_complete(checkstr(L, 1)) );
return 1;
}
FUNC( l_sqlite3_data_count )
{
lua_pushnumber(L, sqlite3_data_count(checkstmt_stmt(L, 1)) );
return 1;
}
FUNC( l_sqlite3_errcode )
{
lua_pushnumber(L, sqlite3_errcode(checkdb_sqlite3(L, 1)) );
return 1;
}
FUNC( l_sqlite3_errmsg )
{
lua_pushstring(L, sqlite3_errmsg(checkdb_sqlite3(L, 1)) );
return 1;
}
FUNC( l_sqlite3_finalize )
{
lua_pushnumber(L, sqlite3_finalize(checkstmt_stmt(L, 1)) );
return 1;
}
FUNC( l_sqlite3_interrupt )
{
sqlite3_interrupt(checkdb_sqlite3(L, 1));
return 0;
}
FUNC( l_sqlite3_last_insert_rowid )
{
lua_pushnumber(L, sqlite3_last_insert_rowid(checkdb_sqlite3(L, 1)) );
return 1;
}
FUNC( l_sqlite3_open )
{
sqlite3 * sqlite3 = 0;
int error = sqlite3_open(checkstr(L, 1), &sqlite3);
lua_pushnumber(L, error);
if (sqlite3)
{
DB * db = (DB *) lua_newuserdata(L, sizeof(DB));
db->sqlite3 = sqlite3;
}
else
lua_pushnil(L);
return 2; /* error code, database */
}
FUNC( l_sqlite3_prepare )
{
/* XXX This piece of code is not so nice. This piece should be redone... */
DB * db = checkdb(L, 1);
const char * sql = checkstr(L, 2);
int sql_size = lua_strlen(L, 2);
const char * leftover = 0;
sqlite3_stmt * sqlite3_stmt = 0;
int error, leftover_size;
Stmt * stmt;
init_callback_usage(L, db); /* Needed by trace handler... FIXME: maybe to be removed... */
error = sqlite3_prepare(db->sqlite3, sql, sql_size, &sqlite3_stmt, &leftover);
leftover_size = leftover ? sql + sql_size - leftover : 0;
lua_pushnumber(L, error);
stmt = lua_newuserdata(L, sizeof(Stmt));
stmt->db = checkdb(L, 1);
stmt->stmt = sqlite3_stmt;
if (leftover_size > 0)
lua_pushlstring(L, leftover, leftover_size);
else
lua_pushnil(L);
return 3; /* error code, statement, left over */
}
FUNC( l_sqlite3_reset )
{
lua_pushnumber(L, sqlite3_reset(checkstmt_stmt(L, 1)) );
return 1;
}
FUNC( l_sqlite3_step )
{
Stmt * stmt = checkstmt(L, 1);
init_callback_usage(L, stmt->db);
lua_pushnumber(L, sqlite3_step(stmt->stmt) );
return 1;
}
FUNC( l_sqlite3_total_changes )
{
lua_pushnumber(L, sqlite3_total_changes(checkdb_sqlite3(L, 1)) );
return 1;
}
static int exec_callback_wrapper(void * cb_data, int num_columns, char ** values, char ** names)
{
int index;
lua_State * L = (lua_State *) cb_data;
lua_pushvalue(L, 3); /* Callback function, resulting stack position 4 */
lua_newtable(L); /* Value array, resulting stack position 5 */
lua_newtable(L); /* Names array, resulting stack position 6 */
for(index=0; index<num_columns; index++)
{
lua_pushstring(L, values[index]); /* Value */
lua_rawseti(L, 5, index+1); /* C-index are 0 based, Lua index are 1 based... */
lua_pushstring(L, names[index]); /* Name */
lua_rawseti(L, 6, index+1);
}
if ( lua_pcall(L, 2, 1, 0) ) /* In: 2 arrays, Out: result code, On error: leave */
{
lua_pop(L, 1); /* delete error message */
return 1;
}
return pop_break_condition(L);
}
FUNC( l_sqlite3_exec )
{
DB * db = checkdb(L, 1);
sqlite3_callback cb;
void * cb_data;
if ( checknilornoneorfunc(L, 3) )
{
cb = exec_callback_wrapper;
cb_data = L;
}
else
{
cb = 0;
cb_data = 0;
}
init_callback_usage(L, db);
lua_pushnumber(L, sqlite3_exec(db->sqlite3, checkstr(L, 2), cb, cb_data, 0) );
return 1;
}
static void func_callback_wrapper(int which, sqlite3_context * ctx, int num_args, sqlite3_value ** values)
{
CB_Data * cb_data = sqlite3_user_data(ctx);
DB * db = cb_data->db;
lua_State * L = db->L;
switch(which)
{
case 0: push_callback(L, db, KEY_XFUNC(cb_data)); break;
case 1: push_callback(L, db, KEY_XSTEP(cb_data)); break;
case 2: push_callback(L, db, KEY_XFINAL(cb_data)); break;
}
if (lua_isnil(L, -1))
{
lua_pop(L, 1);
fprintf(stderr, "libluasqlite3: func_callback_wrapper: Warning: function is null\n");
return;
}
lua_pushlightuserdata(L, ctx);
if (values)
{
lua_pushnumber(L, num_args);
lua_pushlightuserdata(L, values);
}
if (lua_pcall(L, values ? 3 : 1, 0, 0))
{
fprintf(stderr, "libluasqlite3: func_callback_wrapper: Warning: user function error: %s\n", lua_tostring(L, -1));
sqlite3_result_error(ctx, lua_tostring(L, -1), lua_strlen(L, -1));
lua_pop(L, 1);
}
}
static void xfunc_callback_wrapper(sqlite3_context * ctx, int num_args, sqlite3_value ** values)
{
func_callback_wrapper(0, ctx, num_args, values);
}
static void xstep_callback_wrapper(sqlite3_context * ctx, int num_args, sqlite3_value ** values)
{
func_callback_wrapper(1, ctx, num_args, values);
}
static void xfinal_callback_wrapper(sqlite3_context * ctx)
{
func_callback_wrapper(2, ctx, 0, 0);
}
FUNC( l_sqlite3_create_function )
{
DB * db = checkdb(L, 1);
CB_Data * cb_data = get_function_cb_data(L, db, 2);
void (*xfunc)(sqlite3_context *, int, sqlite3_value **) = 0;
void (*xstep)(sqlite3_context *, int, sqlite3_value **) = 0;
void (*xfinal)(sqlite3_context *) = 0;
if ( checknilornoneorfunc(L, 4) )
xfunc = xfunc_callback_wrapper;
else
xfunc = 0;
if ( checknilornoneorfunc(L, 5) )
xstep = xstep_callback_wrapper;
else
xstep = 0;
if ( checknilornoneorfunc(L, 6) )
xfinal = xfinal_callback_wrapper;
else
xfinal = 0;
register_callback(L, db, KEY_XFUNC(cb_data), 4);
register_callback(L, db, KEY_XSTEP(cb_data), 5);
register_callback(L, db, KEY_XFINAL(cb_data), 6);
lua_pushnumber(L,
sqlite3_create_function (
db->sqlite3,
checkstr(L, 2),
checkint(L, 3),
SQLITE_UTF8,
cb_data,
xfunc,
xstep,
xfinal ));
return 1;
}
int xcompare_callback_wrapper(void * cb_data, int len_a, const void * str_a, int len_b, const void * str_b)
{
DB * db = CB_DATA(cb_data)->db;
lua_State * L = db->L;
int result;
push_callback(L, db, KEY_XCOMPARE(cb_data));
lua_pushlstring(L, str_a, len_a);
lua_pushlstring(L, str_b, len_b);
if ( lua_pcall(L, 2, 1, 0) )
result = 0; /* No way to signal errors to sqlite */
else
result = (int) lua_tonumber(L, -1);
lua_pop(L, 1);
return result;
}
FUNC( l_sqlite3_create_collation )
{
DB * db = checkdb(L, 1);
CB_Data * cb_data = get_collation_cb_data(L, db, 2);
int (*xcompare)(void *, int, const void *, int, const void *);
if ( checknilornoneorfunc(L, 3) )
xcompare = xcompare_callback_wrapper;
else
xcompare = 0;
register_callback(L, db, KEY_XCOMPARE(cb_data), 3);
lua_pushnumber(L, sqlite3_create_collation(
db->sqlite3, checkstr(L, 2), SQLITE_UTF8, cb_data, xcompare) );
return 1;
}
void xneeded_callback_wrapper(void * cb_data, sqlite3 * sqlite3, int eTextRep, const char * collation_name)
{
DB * db = CB_DATA(cb_data)->db;
lua_State * L = db->L;
push_callback(L, db, KEY_XNEEDED(cb_data));
lua_pushstring(L, collation_name);
if (lua_pcall(L, 1, 0, 0))
lua_pop(L, 1);
}
FUNC( l_sqlite3_collation_needed )
{
DB * db = checkdb(L, 1);
CB_Data * cb_data = get_collneeded_cb_data(L, db);
void (*xneeded)(void *, sqlite3 *, int eTextRep, const char *);
if ( checknilornoneorfunc(L, 2) )
xneeded = xneeded_callback_wrapper;
else
xneeded = 0;
register_callback(L, db, KEY_XNEEDED(cb_data), 2);
lua_pushnumber(L, sqlite3_collation_needed(db->sqlite3, cb_data, xneeded) );
return 1;
}
static void xtrace_callback_wrapper(void * cb_data, const char * str)
{
DB * db = CB_DATA(cb_data)->db;
lua_State * L = db->L;
push_callback(L, db, KEY_XTRACE(cb_data));
lua_pushstring(L, str);
if ( lua_pcall(L, 1, 0, 0) )
lua_pop(L, 1); /* pop error message and delete it (errors are ignored) */
}
FUNC( l_sqlite3_trace )
{
DB * db = checkdb(L, 1);
CB_Data * cb_data = get_trace_cb_data(L, db);
void (*xtrace)(void *, const char *);
if ( checknilornoneorfunc(L, 2) )
xtrace = xtrace_callback_wrapper;
else
xtrace = 0;
register_callback(L, db, KEY_XTRACE(cb_data), 2);
sqlite3_trace(db->sqlite3, xtrace, cb_data);
lua_pushnumber(L, SQLITE_OK);
return 1;
}
FUNC( l_sqlite3_result_null )
{
sqlite3_result_null(checkcontext(L, 1));
return 0;
}
FUNC( l_sqlite3_result_error )
{
sqlite3_result_error(checkcontext(L, 1), checkstr(L, 2), lua_strlen(L, 2));
return 0;
}
FUNC( l_sqlite3_result_double )
{
sqlite3_result_double(checkcontext(L, 1), checkdouble(L, 2));
return 0;
}
FUNC( l_sqlite3_result_int )
{
sqlite3_result_int(checkcontext(L, 1), checkint(L, 2));
return 0;
}
FUNC( l_sqlite3_result_number )
{
lua_Number number = checknumber(L, 2);
if (IS_INT(number))
sqlite3_result_int(checkcontext(L, 1), (int)number);
else
sqlite3_result_double(checkcontext(L, 1), (double)number);
return 0;
}
FUNC( l_sqlite3_result_blob )
{
sqlite3_result_blob(checkcontext(L, 1), checkstr(L, 2), lua_strlen(L, 2), SQLITE_TRANSIENT);
return 0;
}
FUNC( l_sqlite3_result_text )
{
sqlite3_result_text(checkcontext(L, 1), checkstr(L, 2), lua_strlen(L, 2), SQLITE_TRANSIENT);
return 0;
}
FUNC( l_sqlite3_result_value )
{
sqlite3_value ** values = checkvalues(L, 2);
int index = checkint(L, 3);
sqlite3_result_value(checkcontext(L, 1), values[index] );
return 0;
}
FUNC( l_sqlite3_result )
{
sqlite3_context * context = checkcontext(L, 1);
switch(lua_type(L, 2))
{
case LUA_TNONE:
case LUA_TNIL:
sqlite3_result_null(context);
break;
case LUA_TNUMBER:
{
lua_Number number = lua_tonumber(L, 2);
if (IS_INT(number))
sqlite3_result_int(context, (int)number);
else
sqlite3_result_double(context, (double)number);
}
break;
case LUA_TBOOLEAN:
if (lua_toboolean(L, 2))
sqlite3_result_int(context, 1);
else
sqlite3_result_int(context, 0);
break;
case LUA_TSTRING:
sqlite3_result_text(context, lua_tostring(L, 2), lua_strlen(L, 2), SQLITE_TRANSIENT);
break;
default:
report_error(L, "libluasqlite3: Api usage error: Invalid argument to l_sqlite3_result:");
}
return 0;
}
FUNC( l_sqlite3_aggregate_count )
{
lua_pushnumber(L, sqlite3_aggregate_count(checkcontext(L, 1)) );
return 1;
}
FUNC( l_sqlite3_aggregate_context )
{
lua_pushlightuserdata(L, sqlite3_aggregate_context(checkcontext(L, 1), 1));
return 1;
}
FUNC( l_sqlite3_value_int )
{
sqlite3_value ** values = checkvalues(L, 1);
int index = checkint(L, 2);
lua_pushnumber(L, sqlite3_value_int(values[index]) );
return 1;
}
FUNC( l_sqlite3_value_double )
{
sqlite3_value ** values = checkvalues(L, 1);
int index = checkint(L, 2);
lua_pushnumber(L, sqlite3_value_double(values[index]) );
return 1;
}
FUNC( l_sqlite3_value_number )
{
sqlite3_value ** values = checkvalues(L, 1);
int index = checkint(L, 2);
sqlite3_value * value = values[index];
if (sqlite3_value_type(value) == SQLITE_INTEGER)
lua_pushnumber(L, sqlite3_value_int(value) );
else
lua_pushnumber(L, sqlite3_value_double(value) );
return 1;
}
FUNC( l_sqlite3_value_blob )
{
sqlite3_value ** values = checkvalues(L, 1);
int index = checkint(L, 2);
lua_pushlstring(L, sqlite3_value_blob(values[index]), sqlite3_value_bytes(values[index]) );
return 1;
}
FUNC( l_sqlite3_value_text )
{
sqlite3_value ** values = checkvalues(L, 1);
int index = checkint(L, 2);
lua_pushlstring(L, sqlite3_value_text(values[index]), sqlite3_value_bytes(values[index]) );
return 1;
}
FUNC( l_sqlite3_value )
{
sqlite3_value ** values = checkvalues(L, 1);
int index = checkint(L, 2);
sqlite3_value * value = values[index];
switch(sqlite3_value_type(value))
{
case SQLITE_INTEGER:
lua_pushnumber(L, sqlite3_value_int(value) );
break;
case SQLITE_FLOAT:
lua_pushnumber(L, sqlite3_value_double(value) );
break;
case SQLITE_TEXT:
lua_pushlstring(L, sqlite3_value_text(value), sqlite3_value_bytes(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:
report_error(L, "libluasqlite3: Internal error: Unknonw SQLITE data type.");
}
return 1;
}
FUNC( l_sqlite3_value_type )
{
sqlite3_value ** values = checkvalues(L, 1);
int index = checkint(L, 2);
lua_pushnumber(L, sqlite3_value_type(values[index]) );
return 1;
}
FUNC( l_sqlite3_libversion )
{
lua_pushstring(L, sqlite3_libversion() );
return 1;
}
int xcommit_callback_wrapper(void * cb_data)
{
DB * db = CB_DATA(cb_data)->db;
lua_State * L = db->L;
push_callback(L, db, KEY_XCOMMIT(cb_data));
if ( lua_pcall(L, 0, 1, 0) )
{
lua_pop(L, 1);
return 1; /* on errors, rollback */
}
return pop_break_condition(L);
}
FUNC( l_sqlite3_commit_hook )
{
DB * db = checkdb(L, 1);
CB_Data * cb_data = get_commit_cb_data(L, db);
int (*xcommit)(void *);
if ( checknilornoneorfunc(L, 1) )
xcommit = xcommit_callback_wrapper;
else
xcommit = 0;
register_callback(L, db, KEY_XCOMMIT(cb_data), 2);
sqlite3_commit_hook(db->sqlite3, xcommit, cb_data);
lua_pushnumber(L, sqlite3_errcode(db->sqlite3) );
return 1;
}
int xprogress_callback_wrapper(void * cb_data)
{
DB * db = CB_DATA(cb_data)->db;
lua_State * L = db->L;
push_callback(L, db, KEY_XPROGRESS(cb_data));
if ( lua_pcall(L, 0, 1, 0) )
{
lua_pop(L, 1);
return 1; /* on errors, rollback */
}
return pop_break_condition(L);
return 1;
}
FUNC( l_sqlite3_progress_handler )
{
DB * db = checkdb(L, 1);
CB_Data * cb_data = get_progress_cb_data(L, db);
int (*xprogress)(void *);
if ( checknilornoneorfunc(L, 1) )
xprogress = xprogress_callback_wrapper;
else
xprogress = 0;
register_callback(L, db, KEY_XPROGRESS(cb_data), 3);
sqlite3_progress_handler(db->sqlite3, checkint(L, 2), xprogress, cb_data);
lua_pushnumber(L, sqlite3_errcode(db->sqlite3) );
return 1;
}
int xbusy_callback_wrapper(void * cb_data, int num_called)
{
DB * db = CB_DATA(cb_data)->db;
lua_State * L = db->L;
push_callback(L, db, KEY_XBUSY(cb_data));
lua_pushnumber(L, num_called);
if ( lua_pcall(L, 1, 1, 0) )
{
lua_pop(L, 1);
return 0; /* On errors, sqlite should return SQLITE_BUSY */
}
return pop_break_condition(L); /* WARNING: In reality, the semantic is inverted !!!*/
}
FUNC( l_sqlite3_busy_handler )
{
DB * db = checkdb(L, 1);
CB_Data * cb_data = get_busy_cb_data(L, db);
int (*xbusy)(void *, int);
if ( checknilornoneorfunc(L, 2) )
xbusy = xbusy_callback_wrapper;
else
xbusy = 0;
register_callback(L, db, KEY_XBUSY(cb_data), 2);
lua_pushnumber(L, sqlite3_busy_handler(db->sqlite3, xbusy, cb_data) );
return 1;
}
int xauth_callback_wrapper(void * cb_data, int auth_request, const char * name1, const char * name2, const char * db_name, const char * trigger_name)
{
DB * db = CB_DATA(cb_data)->db;
lua_State * L = db->L;
int result;
push_callback(L, db, KEY_XAUTH(cb_data));
lua_pushnumber(L, auth_request);
push_nil_or_string(L, name1);
push_nil_or_string(L, name2);
push_nil_or_string(L, db_name);
push_nil_or_string(L, trigger_name);
if ( lua_pcall(L, 5, 1, 0) )
{
lua_pop(L, 1);
return SQLITE_DENY; /* On errors, sqlite should deny access */
}
if (lua_isnumber(L, -1))
result = lua_tonumber(L, -1);
else
result = SQLITE_DENY; /* Wrong result values should deny access */
lua_pop(L, 1);
return result;
}
FUNC( l_sqlite3_set_authorizer )
{
DB * db = checkdb(L, 1);
CB_Data * cb_data = get_authorizer_cb_data(L, db);
int (*xauth)(void *, int, const char *, const char *, const char *, const char *);
if ( checknilornoneorfunc(L, 2) )
xauth = xauth_callback_wrapper;
else
xauth = 0;
register_callback(L, db, KEY_XAUTH(cb_data), 2);
lua_pushnumber(L, sqlite3_set_authorizer(db->sqlite3, xauth, cb_data) );
return 1;
}
typedef struct { char * name; int (*func)(lua_State *); } f_entry;
typedef struct { char * name; int value; } d_entry;
static void f(lua_State * L, f_entry entries[])
{
int index;
lua_newtable(L);
for( index=0; entries[index].name; index++)
{
lua_pushstring(L, entries[index].name);
lua_pushcfunction(L, entries[index].func);
lua_rawset(L, -3);
}
}
static void d(lua_State * L, d_entry entries[])
{
int index;
lua_newtable(L);
for( index=0; entries[index].name; index++)
{
lua_pushstring(L, entries[index].name);
lua_pushnumber(L, entries[index].value);
lua_rawset(L, -3);
}
}
f_entry api_entries[] = {
{ "bind_null", l_sqlite3_bind_null },
{ "bind_text", l_sqlite3_bind_text },
{ "bind_blob", l_sqlite3_bind_blob },
{ "bind_int", l_sqlite3_bind_int },
{ "bind_double", l_sqlite3_bind_double },
{ "bind_number", l_sqlite3_bind_number },
{ "bind", l_sqlite3_bind },
{ "bind_parameter_name", l_sqlite3_bind_parameter_name },
{ "bind_parameter_name_x", l_sqlite3_bind_parameter_name_x },
{ "bind_parameter_count", l_sqlite3_bind_parameter_count },
{ "busy_timeout", l_sqlite3_busy_timeout },
{ "changes", l_sqlite3_changes },
{ "close", l_sqlite3_close },
{ "column_blob", l_sqlite3_column_blob },
{ "column_text", l_sqlite3_column_text },
{ "column_int", l_sqlite3_column_int },
{ "column_double", l_sqlite3_column_double },
{ "column_number", l_sqlite3_column_number },
{ "column", l_sqlite3_column },
{ "column_type", l_sqlite3_column_type },
{ "column_count", l_sqlite3_column_count },
{ "column_decltype", l_sqlite3_column_decltype },
{ "column_name", l_sqlite3_column_name },
{ "complete", l_sqlite3_complete },
{ "data_count", l_sqlite3_data_count },
{ "errcode", l_sqlite3_errcode },
{ "errmsg", l_sqlite3_errmsg },
{ "finalize", l_sqlite3_finalize },
{ "interrupt", l_sqlite3_interrupt },
{ "last_insert_rowid", l_sqlite3_last_insert_rowid },
{ "open", l_sqlite3_open },
{ "prepare", l_sqlite3_prepare },
{ "reset", l_sqlite3_reset },
{ "step", l_sqlite3_step },
{ "total_changes", l_sqlite3_total_changes },
{ "exec", l_sqlite3_exec },
{ "create_function", l_sqlite3_create_function },
{ "create_collation", l_sqlite3_create_collation },
{ "trace", l_sqlite3_trace },
{ "collation_needed", l_sqlite3_collation_needed },
{ "result_null", l_sqlite3_result_null },
{ "result_error", l_sqlite3_result_error },
{ "result_double", l_sqlite3_result_double },
{ "result_int", l_sqlite3_result_int },
{ "result_number", l_sqlite3_result_number },
{ "result_blob", l_sqlite3_result_blob },
{ "result_text", l_sqlite3_result_text },
{ "result_value", l_sqlite3_result_value },
{ "result", l_sqlite3_result },
{ "aggregate_count", l_sqlite3_aggregate_count },
{ "aggregate_context", l_sqlite3_aggregate_context },
{ "value_int", l_sqlite3_value_int },
{ "value_double", l_sqlite3_value_double },
{ "value_number", l_sqlite3_value_number },
{ "value_blob", l_sqlite3_value_blob },
{ "value_text", l_sqlite3_value_text },
{ "value", l_sqlite3_value },
{ "value_type", l_sqlite3_value_type },
{ "libversion", l_sqlite3_libversion },
{ "commit_hook", l_sqlite3_commit_hook },
{ "progress_handler", l_sqlite3_progress_handler },
{ "busy_handler", l_sqlite3_busy_handler },
{ "set_authorizer", l_sqlite3_set_authorizer },
{ "drow", l_sqlite3_drow },
{ "irow", l_sqlite3_irow },
{ "arow", l_sqlite3_arow },
{ 0, 0 }
};
d_entry error_entries[] = {
{ "OK", SQLITE_OK },
{ "ERROR", SQLITE_ERROR },
{ "INTERNAL", SQLITE_INTERNAL },
{ "PERM", SQLITE_PERM },
{ "ABORT", SQLITE_ABORT },
{ "BUSY", SQLITE_BUSY },
{ "LOCKED", SQLITE_LOCKED },
{ "NOMEM", SQLITE_NOMEM },
{ "READONLY", SQLITE_READONLY },
{ "INTERRUPT", SQLITE_INTERRUPT },
{ "IOERR", SQLITE_IOERR },
{ "CORRUPT", SQLITE_CORRUPT },
{ "NOTFOUND", SQLITE_NOTFOUND },
{ "FULL", SQLITE_FULL },
{ "CANTOPEN", SQLITE_CANTOPEN },
{ "PROTOCOL", SQLITE_PROTOCOL },
{ "EMPTY", SQLITE_EMPTY },
{ "SCHEMA", SQLITE_SCHEMA },
{ "TOOBIG", SQLITE_TOOBIG },
{ "CONSTRAINT", SQLITE_CONSTRAINT },
{ "MISMATCH", SQLITE_MISMATCH },
{ "MISUSE", SQLITE_MISUSE },
{ "NOLFS", SQLITE_NOLFS },
{ "AUTH", SQLITE_AUTH },
{ "ROW", SQLITE_ROW },
{ "DONE", SQLITE_DONE },
{ "DENY", SQLITE_DENY },
{ "IGNORE", SQLITE_IGNORE },
{ 0, 0 }
};
d_entry type_entries[] = {
{ "INTEGER", SQLITE_INTEGER },
{ "INT", SQLITE_INTEGER },
{ "FLOAT", SQLITE_FLOAT },
{ "DOUBLE", SQLITE_FLOAT },
{ "TEXT", SQLITE_TEXT },
{ "BLOB", SQLITE_BLOB },
{ "NULL", SQLITE_NULL },
{ 0, 0 }
};
d_entry auth_entries[] = {
{ "CREATE_INDEX", SQLITE_CREATE_INDEX },
{ "CREATE_TABLE", SQLITE_CREATE_TABLE },
{ "CREATE_TRIGGER", SQLITE_CREATE_TRIGGER },
{ "CREATE_VIEW", SQLITE_CREATE_VIEW },
{ "CREATE_TEMP_INDEX", SQLITE_CREATE_TEMP_INDEX },
{ "CREATE_TEMP_TABLE", SQLITE_CREATE_TEMP_TABLE },
{ "CREATE_TEMP_TRIGGER", SQLITE_CREATE_TEMP_TRIGGER },
{ "CREATE_TEMP_VIEW", SQLITE_CREATE_TEMP_VIEW },
{ "DROP_INDEX", SQLITE_DROP_INDEX },
{ "DROP_TABLE", SQLITE_DROP_TABLE },
{ "DROP_TRIGGER", SQLITE_DROP_TRIGGER },
{ "DROP_VIEW", SQLITE_DROP_VIEW },
{ "DROP_TEMP_INDEX", SQLITE_DROP_TEMP_INDEX },
{ "DROP_TEMP_TABLE", SQLITE_DROP_TEMP_TABLE },
{ "DROP_TEMP_TRIGGER", SQLITE_DROP_TEMP_TRIGGER },
{ "DROP_TEMP_VIEW", SQLITE_DROP_TEMP_VIEW },
{ "INSERT", SQLITE_INSERT },
{ "PRAGMA", SQLITE_PRAGMA },
{ "READ", SQLITE_READ },
{ "SELECT", SQLITE_SELECT },
{ "TRANSACTION", SQLITE_TRANSACTION },
{ "UPDATE", SQLITE_UPDATE },
{ "ATTACH", SQLITE_ATTACH },
{ "DETACH", SQLITE_DETACH },
{ 0, 0 }
};
int luaopen_sqlite3(lua_State * L)
{
f(L, api_entries);
d(L, error_entries);
d(L, type_entries);
d(L, auth_entries);
return 4; /* api, error codes, type codes, auth requests */
}