1642 lines
38 KiB
C
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 */
|
|
}
|
|
|
|
|
|
|