ODBC driver. No support for prepared statements yet. Note that named parameters must exactly match the placeholder in the SQL statement, i. e. :a or $a or @a needs to include the initial character. When using ? or ?n, use an index starting at 1.
816 lines
19 KiB
C
816 lines
19 KiB
C
/*
|
|
** LuaSQL, SQLite driver
|
|
** Author: Tiago Dionizio, Eduardo Quintao
|
|
** See Copyright Notice in license.html
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#include "sqlite3.h"
|
|
|
|
#include "lua.h"
|
|
#include "lauxlib.h"
|
|
|
|
|
|
#include "luasql.h"
|
|
|
|
#define LUASQL_ENVIRONMENT_SQLITE "SQLite3 environment"
|
|
#define LUASQL_CONNECTION_SQLITE "SQLite3 connection"
|
|
#define LUASQL_CURSOR_SQLITE "SQLite3 cursor"
|
|
|
|
typedef struct
|
|
{
|
|
short closed;
|
|
} env_data;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
short closed;
|
|
int env; /* reference to environment */
|
|
short auto_commit; /* 0 for manual commit */
|
|
unsigned int cur_counter;
|
|
sqlite3 *sql_conn;
|
|
} conn_data;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
short closed;
|
|
int conn; /* reference to connection */
|
|
int numcols; /* number of columns */
|
|
int colnames, coltypes; /* reference to column information tables */
|
|
conn_data *conn_data; /* reference to connection for cursor */
|
|
sqlite3_stmt *sql_vm;
|
|
} cur_data;
|
|
|
|
|
|
/*
|
|
** Check for valid environment.
|
|
*/
|
|
static env_data *getenvironment(lua_State *L) {
|
|
env_data *env = (env_data *)luaL_checkudata(L, 1, LUASQL_ENVIRONMENT_SQLITE);
|
|
luaL_argcheck(L, env != NULL, 1, LUASQL_PREFIX"environment expected");
|
|
luaL_argcheck(L, !env->closed, 1, LUASQL_PREFIX"environment is closed");
|
|
return env;
|
|
}
|
|
|
|
|
|
/*
|
|
** Check for valid connection.
|
|
*/
|
|
static conn_data *getconnection(lua_State *L) {
|
|
conn_data *conn = (conn_data *)luaL_checkudata (L, 1, LUASQL_CONNECTION_SQLITE);
|
|
luaL_argcheck(L, conn != NULL, 1, LUASQL_PREFIX"connection expected");
|
|
luaL_argcheck(L, !conn->closed, 1, LUASQL_PREFIX"connection is closed");
|
|
return conn;
|
|
}
|
|
|
|
|
|
/*
|
|
** Check for valid cursor.
|
|
*/
|
|
static cur_data *getcursor(lua_State *L) {
|
|
cur_data *cur = (cur_data *)luaL_checkudata (L, 1, LUASQL_CURSOR_SQLITE);
|
|
luaL_argcheck(L, cur != NULL, 1, LUASQL_PREFIX"cursor expected");
|
|
luaL_argcheck(L, !cur->closed, 1, LUASQL_PREFIX"cursor is closed");
|
|
return cur;
|
|
}
|
|
|
|
/*
|
|
** Closes the cursor and nullify all structure fields.
|
|
*/
|
|
static void cur_nullify(lua_State *L, cur_data *cur)
|
|
{
|
|
conn_data *conn;
|
|
|
|
/* Nullify structure fields. */
|
|
cur->closed = 1;
|
|
cur->sql_vm = NULL;
|
|
/* Decrement cursor counter on connection object */
|
|
lua_rawgeti (L, LUA_REGISTRYINDEX, cur->conn);
|
|
conn = lua_touserdata (L, -1);
|
|
conn->cur_counter--;
|
|
|
|
luaL_unref(L, LUA_REGISTRYINDEX, cur->conn);
|
|
luaL_unref(L, LUA_REGISTRYINDEX, cur->colnames);
|
|
luaL_unref(L, LUA_REGISTRYINDEX, cur->coltypes);
|
|
}
|
|
|
|
|
|
/*
|
|
** Finalizes the vm
|
|
** Return nil + errmsg or nil in case of sucess
|
|
*/
|
|
static int finalize(lua_State *L, cur_data *cur) {
|
|
const char *errmsg;
|
|
if (sqlite3_finalize(cur->sql_vm) != SQLITE_OK)
|
|
{
|
|
errmsg = sqlite3_errmsg(cur->conn_data->sql_conn);
|
|
cur_nullify(L, cur);
|
|
return luasql_faildirect(L, errmsg);
|
|
}
|
|
cur_nullify(L, cur);
|
|
lua_pushnil(L);
|
|
return 1;
|
|
}
|
|
|
|
|
|
static void push_column(lua_State *L, sqlite3_stmt *vm, int column) {
|
|
switch (sqlite3_column_type(vm, column)) {
|
|
case SQLITE_INTEGER:
|
|
#if LUA_VERSION_NUM >= 503
|
|
lua_pushinteger(L, sqlite3_column_int64(vm, column));
|
|
#else
|
|
// Preserves precision of integers up to 2^53.
|
|
lua_pushnumber(L, sqlite3_column_int64(vm, column));
|
|
#endif
|
|
break;
|
|
case SQLITE_FLOAT:
|
|
lua_pushnumber(L, sqlite3_column_double(vm, column));
|
|
break;
|
|
case SQLITE_TEXT:
|
|
lua_pushlstring(L, (const char *)sqlite3_column_text(vm, column),
|
|
(size_t)sqlite3_column_bytes(vm, column));
|
|
break;
|
|
case SQLITE_BLOB:
|
|
lua_pushlstring(L, sqlite3_column_blob(vm, column),
|
|
(size_t)sqlite3_column_bytes(vm, column));
|
|
break;
|
|
case SQLITE_NULL:
|
|
lua_pushnil(L);
|
|
break;
|
|
default:
|
|
luaL_error(L, LUASQL_PREFIX"Unrecognized column type");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Get another row of the given cursor.
|
|
*/
|
|
static int cur_fetch (lua_State *L) {
|
|
cur_data *cur = getcursor(L);
|
|
sqlite3_stmt *vm = cur->sql_vm;
|
|
int res;
|
|
|
|
if (vm == NULL)
|
|
return 0;
|
|
|
|
res = sqlite3_step(vm);
|
|
|
|
/* no more results? */
|
|
if (res == SQLITE_DONE)
|
|
return finalize(L, cur);
|
|
|
|
if (res != SQLITE_ROW)
|
|
return finalize(L, cur);
|
|
|
|
if (lua_istable (L, 2))
|
|
{
|
|
int i;
|
|
const char *opts = luaL_optstring(L, 3, "n");
|
|
|
|
if (strchr(opts, 'n') != NULL)
|
|
{
|
|
/* Copy values to numerical indices */
|
|
for (i = 0; i < cur->numcols;)
|
|
{
|
|
push_column(L, vm, i);
|
|
lua_rawseti(L, 2, ++i);
|
|
}
|
|
}
|
|
if (strchr(opts, 'a') != NULL)
|
|
{
|
|
/* Copy values to alphanumerical indices */
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, cur->colnames);
|
|
|
|
for (i = 0; i < cur->numcols; i++)
|
|
{
|
|
lua_rawgeti(L, -1, i+1);
|
|
push_column(L, vm, i);
|
|
lua_rawset (L, 2);
|
|
}
|
|
}
|
|
lua_pushvalue(L, 2);
|
|
return 1; /* return table */
|
|
}
|
|
else
|
|
{
|
|
int i;
|
|
luaL_checkstack (L, cur->numcols, LUASQL_PREFIX"too many columns");
|
|
for (i = 0; i < cur->numcols; ++i)
|
|
push_column(L, vm, i);
|
|
return cur->numcols; /* return #numcols values */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** Cursor object collector function
|
|
*/
|
|
static int cur_gc(lua_State *L)
|
|
{
|
|
cur_data *cur = (cur_data *)luaL_checkudata(L, 1, LUASQL_CURSOR_SQLITE);
|
|
if (cur != NULL && !(cur->closed))
|
|
{
|
|
sqlite3_finalize(cur->sql_vm);
|
|
cur_nullify(L, cur);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
** Close the cursor on top of the stack.
|
|
** Return 1
|
|
*/
|
|
static int cur_close(lua_State *L)
|
|
{
|
|
cur_data *cur = (cur_data *)luaL_checkudata(L, 1, LUASQL_CURSOR_SQLITE);
|
|
luaL_argcheck(L, cur != NULL, 1, LUASQL_PREFIX"cursor expected");
|
|
if (cur->closed) {
|
|
lua_pushboolean(L, 0);
|
|
return 1;
|
|
}
|
|
sqlite3_finalize(cur->sql_vm);
|
|
cur_nullify(L, cur);
|
|
lua_pushboolean(L, 1);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** Return the list of field names.
|
|
*/
|
|
static int cur_getcolnames(lua_State *L)
|
|
{
|
|
cur_data *cur = getcursor(L);
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, cur->colnames);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** Return the list of field types.
|
|
*/
|
|
static int cur_getcoltypes(lua_State *L)
|
|
{
|
|
cur_data *cur = getcursor(L);
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, cur->coltypes);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** Create a new Cursor object and push it on top of the stack.
|
|
*/
|
|
/* static int create_cursor(lua_State *L, int conn, sqlite3_stmt *sql_vm,
|
|
int numcols, const char **row, const char **col_info)*/
|
|
static int create_cursor(lua_State *L, int o, conn_data *conn,
|
|
sqlite3_stmt *sql_vm, int numcols)
|
|
{
|
|
int i;
|
|
cur_data *cur = (cur_data*)lua_newuserdata(L, sizeof(cur_data));
|
|
luasql_setmeta (L, LUASQL_CURSOR_SQLITE);
|
|
|
|
/* increment cursor count for the connection creating this cursor */
|
|
conn->cur_counter++;
|
|
|
|
/* fill in structure */
|
|
cur->closed = 0;
|
|
cur->conn = LUA_NOREF;
|
|
cur->numcols = numcols;
|
|
cur->colnames = LUA_NOREF;
|
|
cur->coltypes = LUA_NOREF;
|
|
cur->sql_vm = sql_vm;
|
|
cur->conn_data = conn;
|
|
|
|
lua_pushvalue(L, o);
|
|
cur->conn = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
/* create table with column names */
|
|
lua_newtable(L);
|
|
for (i = 0; i < numcols;)
|
|
{
|
|
lua_pushstring(L, sqlite3_column_name(sql_vm, i));
|
|
lua_rawseti(L, -2, ++i);
|
|
}
|
|
cur->colnames = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
/* create table with column types */
|
|
lua_newtable(L);
|
|
for (i = 0; i < numcols;)
|
|
{
|
|
lua_pushstring(L, sqlite3_column_decltype(sql_vm, i));
|
|
lua_rawseti(L, -2, ++i);
|
|
}
|
|
cur->coltypes = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** Connection object collector function
|
|
*/
|
|
static int conn_gc(lua_State *L)
|
|
{
|
|
conn_data *conn = (conn_data *)luaL_checkudata(L, 1, LUASQL_CONNECTION_SQLITE);
|
|
if (conn != NULL && !(conn->closed))
|
|
{
|
|
if (conn->cur_counter > 0)
|
|
return luaL_error (L, LUASQL_PREFIX"there are open cursors");
|
|
|
|
/* Nullify structure fields. */
|
|
conn->closed = 1;
|
|
luaL_unref(L, LUA_REGISTRYINDEX, conn->env);
|
|
sqlite3_close(conn->sql_conn);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
** Close a Connection object.
|
|
*/
|
|
static int conn_close(lua_State *L)
|
|
{
|
|
conn_data *conn = (conn_data *)luaL_checkudata(L, 1, LUASQL_CONNECTION_SQLITE);
|
|
luaL_argcheck (L, conn != NULL, 1, LUASQL_PREFIX"connection expected");
|
|
if (conn->closed)
|
|
{
|
|
lua_pushboolean(L, 0);
|
|
return 1;
|
|
}
|
|
conn_gc(L);
|
|
lua_pushboolean(L, 1);
|
|
return 1;
|
|
}
|
|
|
|
static int conn_escape(lua_State *L)
|
|
{
|
|
const char *from = luaL_checklstring (L, 2, 0);
|
|
char *escaped = sqlite3_mprintf("%q", from);
|
|
if (escaped == NULL)
|
|
{
|
|
lua_pushnil(L);
|
|
}
|
|
else
|
|
{
|
|
lua_pushstring(L, escaped);
|
|
sqlite3_free(escaped);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** Bind one parameter.
|
|
** Supported are the data types nil, string, boolean, number.
|
|
*/
|
|
static int set_param(lua_State *L, sqlite3_stmt *vm, int param_nr, int arg)
|
|
{
|
|
int tt = lua_type(L, arg);
|
|
int rc = 0;
|
|
|
|
switch (tt) {
|
|
case LUA_TNIL:
|
|
rc = sqlite3_bind_null(vm, param_nr);
|
|
break;
|
|
|
|
case LUA_TSTRING: {
|
|
size_t s_len;
|
|
const char *s = lua_tolstring(L, arg, &s_len);
|
|
rc = sqlite3_bind_null(vm, param_nr);
|
|
rc = sqlite3_bind_text(vm, param_nr, s, s_len, SQLITE_TRANSIENT);
|
|
break;
|
|
}
|
|
|
|
case LUA_TBOOLEAN: {
|
|
int val = lua_tointeger(L, arg);
|
|
rc = sqlite3_bind_int(vm, param_nr, val);
|
|
}
|
|
break;
|
|
|
|
case LUA_TNUMBER:
|
|
if (lua_isinteger(L, arg)) {
|
|
lua_Integer val = lua_tointeger(L, arg);
|
|
rc = sqlite3_bind_int64(vm, param_nr, val);
|
|
} else {
|
|
double val = lua_tonumber(L, arg);
|
|
rc = sqlite3_bind_double(vm, param_nr, val);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
luaL_error(L, LUASQL_PREFIX"unhandled data type %s in paramter binding",
|
|
lua_typename(L, tt));
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int raw_readparams_args(lua_State *L, sqlite3_stmt *vm, int arg, int ltop)
|
|
{
|
|
int param_count, param_nr, rc = 0;
|
|
|
|
param_count = sqlite3_bind_parameter_count(vm);
|
|
if (ltop - arg + 1 != param_count)
|
|
luaL_error(L, LUASQL_PREFIX"wrong number of parameters: expected=%d, given=%d",
|
|
param_count, ltop - arg + 1);
|
|
|
|
for (param_nr=1; param_nr <= param_count; param_nr ++, arg ++) {
|
|
rc = set_param(L, vm, param_nr, arg);
|
|
if (rc)
|
|
break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Bind all parameters from the given table.
|
|
** The table indices can be integers or strings.
|
|
** Unbound parameters, or duplicate bindings are not detected.
|
|
*/
|
|
static int raw_readparams_table(lua_State *L, sqlite3_stmt *vm, int arg)
|
|
{
|
|
int param_nr, rc = 0, tt;
|
|
|
|
lua_pushnil(L);
|
|
|
|
while (lua_next(L, arg)) { // [arg]=table, [-2]=key, [-1]=val
|
|
tt = lua_type(L, -2);
|
|
if (tt == LUA_TNUMBER && lua_isinteger(L, -2)) {
|
|
param_nr = lua_tointeger(L, -2);
|
|
} else {
|
|
const char *param_name = lua_tostring(L, -2);
|
|
param_nr = sqlite3_bind_parameter_index(vm, param_name);
|
|
if (param_nr == 0)
|
|
luaL_error(L, LUASQL_PREFIX"binding to invalid parameter name %s\n",
|
|
param_name);
|
|
}
|
|
set_param(L, vm, param_nr, -1);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
** Execute an SQL statement.
|
|
** Return a Cursor object if the statement is a query, otherwise
|
|
** return the number of tuples affected by the statement.
|
|
*/
|
|
static int conn_execute(lua_State *L)
|
|
{
|
|
conn_data *conn = getconnection(L);
|
|
const char *statement = luaL_checkstring(L, 2);
|
|
int res;
|
|
sqlite3_stmt *vm;
|
|
const char *errmsg;
|
|
int numcols;
|
|
const char *tail;
|
|
|
|
#if SQLITE_VERSION_NUMBER > 3006013
|
|
res = sqlite3_prepare_v2(conn->sql_conn, statement, -1, &vm, &tail);
|
|
#else
|
|
res = sqlite3_prepare(conn->sql_conn, statement, -1, &vm, &tail);
|
|
#endif
|
|
if (res != SQLITE_OK)
|
|
{
|
|
errmsg = sqlite3_errmsg(conn->sql_conn);
|
|
return luasql_faildirect(L, errmsg);
|
|
}
|
|
|
|
/* Bind parameters (if any) */
|
|
int ltop = lua_gettop(L);
|
|
if (ltop > 2) {
|
|
if (ltop == 3 && lua_type(L, 3) == LUA_TTABLE) {
|
|
res = raw_readparams_table(L, vm, 3);
|
|
} else if (ltop >= 3) {
|
|
res = raw_readparams_args(L, vm, 3, ltop);
|
|
} else {
|
|
luaL_error(L, LUASQL_PREFIX"parameters are either one table or positional");
|
|
}
|
|
if (res)
|
|
return res;
|
|
}
|
|
|
|
/* process first result to retrive query information and type */
|
|
res = sqlite3_step(vm);
|
|
numcols = sqlite3_column_count(vm);
|
|
|
|
/* real query? if empty, must have numcols!=0 */
|
|
if ((res == SQLITE_ROW) || ((res == SQLITE_DONE) && numcols))
|
|
{
|
|
sqlite3_reset(vm);
|
|
return create_cursor(L, 1, conn, vm, numcols);
|
|
}
|
|
|
|
if (res == SQLITE_DONE) /* and numcols==0, INSERT,UPDATE,DELETE statement */
|
|
{
|
|
sqlite3_finalize(vm);
|
|
/* return number of columns changed */
|
|
lua_pushnumber(L, sqlite3_changes(conn->sql_conn));
|
|
return 1;
|
|
}
|
|
|
|
/* error */
|
|
errmsg = sqlite3_errmsg(conn->sql_conn);
|
|
sqlite3_finalize(vm);
|
|
return luasql_faildirect(L, errmsg);
|
|
}
|
|
|
|
|
|
/*
|
|
** Commit the current transaction.
|
|
*/
|
|
static int conn_commit(lua_State *L)
|
|
{
|
|
char *errmsg;
|
|
conn_data *conn = getconnection(L);
|
|
int res;
|
|
const char *sql = "COMMIT";
|
|
|
|
if (conn->auto_commit == 0) sql = "COMMIT;BEGIN";
|
|
|
|
res = sqlite3_exec(conn->sql_conn, sql, NULL, NULL, &errmsg);
|
|
|
|
if (res != SQLITE_OK)
|
|
{
|
|
lua_pushnil(L);
|
|
lua_pushliteral(L, LUASQL_PREFIX);
|
|
lua_pushstring(L, errmsg);
|
|
sqlite3_free(errmsg);
|
|
lua_concat(L, 2);
|
|
return 2;
|
|
}
|
|
lua_pushboolean(L, 1);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** Rollback the current transaction.
|
|
*/
|
|
static int conn_rollback(lua_State *L)
|
|
{
|
|
char *errmsg;
|
|
conn_data *conn = getconnection(L);
|
|
int res;
|
|
const char *sql = "ROLLBACK";
|
|
|
|
if (conn->auto_commit == 0) sql = "ROLLBACK;BEGIN";
|
|
|
|
res = sqlite3_exec(conn->sql_conn, sql, NULL, NULL, &errmsg);
|
|
if (res != SQLITE_OK)
|
|
{
|
|
lua_pushnil(L);
|
|
lua_pushliteral(L, LUASQL_PREFIX);
|
|
lua_pushstring(L, errmsg);
|
|
sqlite3_free(errmsg);
|
|
lua_concat(L, 2);
|
|
return 2;
|
|
}
|
|
lua_pushboolean(L, 1);
|
|
return 1;
|
|
}
|
|
|
|
static int conn_getlastautoid(lua_State *L)
|
|
{
|
|
conn_data *conn = getconnection(L);
|
|
lua_pushnumber(L, sqlite3_last_insert_rowid(conn->sql_conn));
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** Set "auto commit" property of the connection.
|
|
** If 'true', then rollback current transaction.
|
|
** If 'false', then start a new transaction.
|
|
*/
|
|
static int conn_setautocommit(lua_State *L)
|
|
{
|
|
conn_data *conn = getconnection(L);
|
|
if (lua_toboolean(L, 2))
|
|
{
|
|
conn->auto_commit = 1;
|
|
/* undo active transaction - ignore errors */
|
|
(void) sqlite3_exec(conn->sql_conn, "ROLLBACK", NULL, NULL, NULL);
|
|
}
|
|
else
|
|
{
|
|
char *errmsg;
|
|
int res;
|
|
conn->auto_commit = 0;
|
|
res = sqlite3_exec(conn->sql_conn, "BEGIN", NULL, NULL, &errmsg);
|
|
if (res != SQLITE_OK)
|
|
{
|
|
lua_pushliteral(L, LUASQL_PREFIX);
|
|
lua_pushstring(L, errmsg);
|
|
sqlite3_free(errmsg);
|
|
lua_concat(L, 2);
|
|
lua_error(L);
|
|
}
|
|
}
|
|
lua_pushboolean(L, 1);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** Create a new Connection object and push it on top of the stack.
|
|
*/
|
|
static int create_connection(lua_State *L, int env, sqlite3 *sql_conn)
|
|
{
|
|
conn_data *conn = (conn_data*)lua_newuserdata(L, sizeof(conn_data));
|
|
luasql_setmeta(L, LUASQL_CONNECTION_SQLITE);
|
|
|
|
/* fill in structure */
|
|
conn->closed = 0;
|
|
conn->env = LUA_NOREF;
|
|
conn->auto_commit = 1;
|
|
conn->sql_conn = sql_conn;
|
|
conn->cur_counter = 0;
|
|
lua_pushvalue (L, env);
|
|
conn->env = luaL_ref (L, LUA_REGISTRYINDEX);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** Connects to a data source.
|
|
*/
|
|
static int env_connect(lua_State *L)
|
|
{
|
|
const char *sourcename;
|
|
sqlite3 *conn;
|
|
const char *errmsg;
|
|
int res;
|
|
bool readOnlyMode = false;
|
|
int mode;
|
|
|
|
getenvironment(L); /* validate environment */
|
|
|
|
if (lua_isboolean(L, 4)) {
|
|
if (lua_toboolean(L, 4)) {
|
|
readOnlyMode = true;
|
|
}
|
|
}
|
|
|
|
sourcename = luaL_checkstring(L, 2);
|
|
#if SQLITE_VERSION_NUMBER > 3006013
|
|
if (strstr(sourcename, ":memory:")) /* TODO: rework this and get/add param 'flag' for sqlite3_open_v2 - see TODO below */
|
|
{
|
|
if (readOnlyMode) {
|
|
mode = SQLITE_OPEN_READONLY | SQLITE_OPEN_MEMORY;
|
|
} else {
|
|
mode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_MEMORY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (readOnlyMode) {
|
|
mode = SQLITE_OPEN_READONLY;
|
|
} else {
|
|
mode = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
|
|
}
|
|
}
|
|
res = sqlite3_open_v2(sourcename, &conn, mode, NULL);
|
|
#else
|
|
res = sqlite3_open(sourcename, &conn);
|
|
#endif
|
|
if (res != SQLITE_OK)
|
|
{
|
|
errmsg = sqlite3_errmsg(conn);
|
|
luasql_faildirect(L, errmsg);
|
|
sqlite3_close(conn);
|
|
return 2;
|
|
}
|
|
|
|
if (lua_isnumber(L, 3)) {
|
|
sqlite3_busy_timeout(conn, lua_tonumber(L,3)); /* TODO: remove this */
|
|
}
|
|
|
|
return create_connection(L, 1, conn);
|
|
}
|
|
|
|
|
|
/*
|
|
** Environment object collector function.
|
|
*/
|
|
static int env_gc (lua_State *L)
|
|
{
|
|
env_data *env = (env_data *)luaL_checkudata(L, 1, LUASQL_ENVIRONMENT_SQLITE);
|
|
if (env != NULL && !(env->closed))
|
|
env->closed = 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
** Close environment object.
|
|
*/
|
|
static int env_close (lua_State *L)
|
|
{
|
|
env_data *env = (env_data *)luaL_checkudata(L, 1, LUASQL_ENVIRONMENT_SQLITE);
|
|
luaL_argcheck(L, env != NULL, 1, LUASQL_PREFIX"environment expected");
|
|
if (env->closed) {
|
|
lua_pushboolean(L, 0);
|
|
return 1;
|
|
}
|
|
env_gc(L);
|
|
lua_pushboolean(L, 1);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** Sets the timeout for a lock in the connection.
|
|
static int opts_settimeout (lua_State *L)
|
|
{
|
|
conn_data *conn = getconnection(L);
|
|
int milisseconds = luaL_checknumber(L, 2);
|
|
lua_pushnumber(L, sqlite3_busy_timeout(conn->sql_conn, milisseconds));
|
|
return 1;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
** Create metatables for each class of object.
|
|
*/
|
|
static void create_metatables (lua_State *L)
|
|
{
|
|
struct luaL_Reg environment_methods[] = {
|
|
{"__gc", env_gc},
|
|
{"close", env_close},
|
|
{"connect", env_connect},
|
|
{NULL, NULL},
|
|
};
|
|
struct luaL_Reg connection_methods[] = {
|
|
{"__gc", conn_gc},
|
|
{"close", conn_close},
|
|
{"escape", conn_escape},
|
|
// {"prepare", conn_prepare},
|
|
{"execute", conn_execute},
|
|
{"commit", conn_commit},
|
|
{"rollback", conn_rollback},
|
|
{"setautocommit", conn_setautocommit},
|
|
{"getlastautoid", conn_getlastautoid},
|
|
{NULL, NULL},
|
|
};
|
|
struct luaL_Reg cursor_methods[] = {
|
|
{"__gc", cur_gc},
|
|
{"close", cur_close},
|
|
{"getcolnames", cur_getcolnames},
|
|
{"getcoltypes", cur_getcoltypes},
|
|
{"fetch", cur_fetch},
|
|
{NULL, NULL},
|
|
};
|
|
luasql_createmeta(L, LUASQL_ENVIRONMENT_SQLITE, environment_methods);
|
|
luasql_createmeta(L, LUASQL_CONNECTION_SQLITE, connection_methods);
|
|
luasql_createmeta(L, LUASQL_CURSOR_SQLITE, cursor_methods);
|
|
lua_pop (L, 3);
|
|
}
|
|
|
|
/*
|
|
** Creates an Environment and returns it.
|
|
*/
|
|
static int create_environment (lua_State *L)
|
|
{
|
|
env_data *env = (env_data *)lua_newuserdata(L, sizeof(env_data));
|
|
luasql_setmeta(L, LUASQL_ENVIRONMENT_SQLITE);
|
|
|
|
/* fill in structure */
|
|
env->closed = 0;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
** Creates the metatables for the objects and registers the
|
|
** driver open method.
|
|
*/
|
|
LUASQL_API int luaopen_luasql_sqlite3(lua_State *L)
|
|
{
|
|
struct luaL_Reg driver[] = {
|
|
{"sqlite3", create_environment},
|
|
{NULL, NULL},
|
|
};
|
|
create_metatables (L);
|
|
lua_newtable (L);
|
|
luaL_setfuncs (L, driver, 0);
|
|
luasql_set_info (L);
|
|
lua_pushliteral (L, "_CLIENTVERSION");
|
|
lua_pushliteral (L, SQLITE_VERSION);
|
|
lua_settable (L, -3);
|
|
return 1;
|
|
}
|