Tune SQLite build for redbean (#97)

redbean lua handlers that perform sql queries can do 400k qps.

We now use a separate compile-time options for SQLite, when building the
SQLite shell versus building the production web serving code. It doesn't
seem appropriate for something like redbean to include backups, progress
callbacks, query completion, profiling, EXPLAIN, ALTER, ANALYZE, VACUUM,
etc. since those tasks are better left to the sqlite3.com shell program.

Lua SQLite pointer APIs have been removed since we're not using threads.
The Lua APIs for installing update / commit / rollback hooks are removed
due to a general sense of disagreement and an overall lack of comfort.

Full-Text Search and R*Tree are as large as the rest of SQLite combined.
Turning those off keeps redbean under 1mb when built for MODE=tiny which
is nice for marketing purposes.

If you need something that was removed, file an issue, and we'll add it.
This commit is contained in:
Justine Tunney 2021-06-10 08:00:08 -07:00
parent eb08b9fbeb
commit 88806b79b1
9 changed files with 230 additions and 1284 deletions

View file

@ -30,6 +30,6 @@ int strncasecmp16(const char16_t *a, const char16_t *b, size_t n) {
int x, y;
size_t i = 0;
if (!n-- || a == b) return 0;
while ((x = tolower(a[i])) == (y = tolower(b[i])) && b[i] && i < n) ++i;
while ((x = towlower(a[i])) == (y = towlower(b[i])) && b[i] && i < n) ++i;
return x - y;
}

View file

@ -524,7 +524,9 @@ Expr *sqlite3ExprForVectorField(
}else{
if( pVector->op==TK_VECTOR ) pVector = pVector->x.pList->a[iField].pExpr;
pRet = sqlite3ExprDup(pParse->db, pVector, 0);
#ifndef SQLITE_OMIT_ALTERTABLE
sqlite3RenameTokenRemap(pParse, pRet, pVector);
#endif
}
return pRet;
}

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,7 @@
PKGS += THIRD_PARTY_SQLITE3
THIRD_PARTY_SQLITE3_ARTIFACTS += THIRD_PARTY_SQLITE3_A
THIRD_PARTY_SQLITE3 = $(THIRD_PARTY_SQLITE3_A_DEPS) $(THIRD_PARTY_SQLITE3_A) $(THIRD_PARTY_SQLITE3_COMS)
THIRD_PARTY_SQLITE3 = $(THIRD_PARTY_SQLITE3_A_DEPS) $(THIRD_PARTY_SQLITE3_A)
THIRD_PARTY_SQLITE3_A = o/$(MODE)/third_party/sqlite3/libsqlite3.a
THIRD_PARTY_SQLITE3_A_FILES := $(wildcard third_party/sqlite3/*)
THIRD_PARTY_SQLITE3_A_HDRS = $(filter %.h,$(THIRD_PARTY_SQLITE3_A_FILES))
@ -17,7 +17,10 @@ THIRD_PARTY_SQLITE3_A_SRCS = \
$(THIRD_PARTY_SQLITE3_A_SRCS_T)
THIRD_PARTY_SQLITE3_A_OBJS = \
$(THIRD_PARTY_SQLITE3_A_SRCS_C:%.c=o/$(MODE)/%.o)
$(filter-out %/shell.o,$(THIRD_PARTY_SQLITE3_A_SRCS_C:%.c=o/$(MODE)/%.o))
THIRD_PARTY_SQLITE3_SHELL_OBJS = \
$(THIRD_PARTY_SQLITE3_A_SRCS_C:%.c=o/$(MODE)/%.shell.o)
THIRD_PARTY_SQLITE3_COMS = \
o/$(MODE)/third_party/sqlite3/sqlite3.com
@ -52,8 +55,8 @@ THIRD_PARTY_SQLITE3_A_DEPS := \
o/$(MODE)/third_party/sqlite3/sqlite3.com.dbg: \
$(THIRD_PARTY_SQLITE3_A_DEPS) \
$(THIRD_PARTY_SQLITE3_A) \
o/$(MODE)/third_party/sqlite3/shell.o \
$(THIRD_PARTY_SQLITE3_SHELL_OBJS) \
o/$(MODE)/third_party/sqlite3/shell.shell.o \
$(CRT) \
$(APE)
-@$(APELINK)
@ -61,60 +64,89 @@ o/$(MODE)/third_party/sqlite3/sqlite3.com.dbg: \
$(THIRD_PARTY_SQLITE3_A): \
third_party/sqlite3/ \
$(THIRD_PARTY_SQLITE3_A).pkg \
$(filter-out %/shell.o,$(THIRD_PARTY_SQLITE3_A_OBJS))
$(THIRD_PARTY_SQLITE3_A_OBJS)
$(THIRD_PARTY_SQLITE3_A).pkg: \
$(filter-out %/shell.o,$(THIRD_PARTY_SQLITE3_A_OBJS)) \
$(THIRD_PARTY_SQLITE3_A_OBJS) \
$(foreach x,$(THIRD_PARTY_SQLITE3_A_DIRECTDEPS),$($(x)_A).pkg)
# https://www.sqlite.org/compile.html
THIRD_PARTY_SQLITE3_FLAGS = \
-DNDEBUG \
-DSQLITE_CORE \
-DSQLITE_OS_UNIX \
-DBUILD_sqlite \
-DHAVE_USLEEP \
-DHAVE_GMTIME_R \
-DHAVE_FDATASYNC \
-DHAVE_STRCHRNUL \
-DHAVE_LOCALTIME_R \
-DHAVE_MALLOC_USABLE_SIZE \
-DSQLITE_THREADSAFE=0 \
-DSQLITE_MAX_EXPR_DEPTH=0 \
-DSQLITE_DEFAULT_MEMSTATUS=0 \
-DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 \
-DSQLITE_LIKE_DOESNT_MATCH_BLOBS \
-DSQLITE_OMIT_TCL_VARIABLE \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_OMIT_DEPRECATED \
-DSQLITE_OMIT_SHARED_CACHE \
-DSQLITE_OMIT_AUTOINIT \
-DSQLITE_OMIT_GET_TABLE \
-DSQLITE_HAVE_C99_MATH_FUNCS \
-DSQLITE_ENABLE_MATH_FUNCTIONS
$(THIRD_PARTY_SQLITE3_A_OBJS): \
OVERRIDE_CFLAGS += \
-DNDEBUG \
-DSQLITE_CORE \
-DSQLITE_OS_UNIX \
-DBUILD_sqlite \
$(THIRD_PARTY_SQLITE3_FLAGS) \
-DSQLITE_OMIT_TRACE \
-DSQLITE_OMIT_VACUUM \
-DSQLITE_OMIT_EXPLAIN \
-DSQLITE_OMIT_ANALYZE \
-DSQLITE_OMIT_COMPLETE \
-DSQLITE_OMIT_ALTERTABLE \
-DSQLITE_OMIT_UPDATE_HOOK \
-DSQLITE_OMIT_AUTHORIZATION \
-DSQLITE_OMIT_PROGRESS_CALLBACK
$(THIRD_PARTY_SQLITE3_SHELL_OBJS): \
OVERRIDE_CFLAGS += \
$(THIRD_PARTY_SQLITE3_FLAGS) \
-DHAVE_READLINE=0 \
-DHAVE_EDITLINE=0 \
-DSQLITE_THREADSAFE=0 \
-DSQLITE_HAVE_ZLIB \
-DHAVE_MALLOC_USABLE_SIZE \
-DHAVE_FDATASYNC \
-DHAVE_STRCHRNUL \
-DHAVE_LOCALTIME_R \
-DHAVE_GMTIME_R \
-DHAVE_USLEEP \
-DSQLITE_HAVE_C99_MATH_FUNCS \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_ENABLE_MATH_FUNCTIONS \
-DSQLITE_ENABLE_FTS3 \
-DSQLITE_ENABLE_FTS4 \
-DSQLITE_ENABLE_FTS5 \
-DSQLITE_ENABLE_JSON1 \
-DSQLITE_ENABLE_GEOPOLY \
-DSQLITE_ENABLE_RTREE \
-DSQLITE_ENABLE_SESSION \
-DSQLITE_ENABLE_IOTRACE \
-DSQLITE_ENABLE_PREUPDATE_HOOK \
-DSQLITE_TEMP_STORE \
-DSQLITE_ENABLE_JSON1 \
-DSQLITE_ENABLE_RTREE \
-DSQLITE_ENABLE_COLUMN_METADATA \
-DSQLITE_ENABLE_EXPLAIN_COMMENTS \
-DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION \
-DSQLITE_ENABLE_SESSION \
-DSQLITE_ENABLE_STMTVTAB \
-DSQLITE_ENABLE_DBPAGE_VTAB \
-DSQLITE_ENABLE_DBSTAT_VTAB \
-DSQLITE_ENABLE_BYTECODE_VTAB \
-DSQLITE_ENABLE_OFFSET_SQL_FUNC \
-DSQLITE_ENABLE_DESERIALIZE
-DSQLITE_ENABLE_DESERIALIZE \
-DSQLITE_ENABLE_FTS3 \
-DSQLITE_ENABLE_FTS4 \
-DSQLITE_ENABLE_FTS5 \
-DSQLITE_ENABLE_RTREE \
-DSQLITE_ENABLE_GEOPOLY \
-DSQLITE_ENABLE_JSON1
o/$(MODE)/third_party/sqlite3/shell.o: \
o/$(MODE)/third_party/sqlite3/shell.shell.o: \
OVERRIDE_CFLAGS += \
-DSTACK_FRAME_UNLIMITED
o/$(MODE)/%.shell.o: %.c
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) $(OUTPUT_OPTION) $<
THIRD_PARTY_SQLITE3_LIBS = $(foreach x,$(THIRD_PARTY_SQLITE3_ARTIFACTS),$($(x)))
THIRD_PARTY_SQLITE3_SRCS = $(foreach x,$(THIRD_PARTY_SQLITE3_ARTIFACTS),$($(x)_SRCS))
THIRD_PARTY_SQLITE3_CHECKS = $(foreach x,$(THIRD_PARTY_SQLITE3_ARTIFACTS),$($(x)_CHECKS))
THIRD_PARTY_SQLITE3_OBJS = $(foreach x,$(THIRD_PARTY_SQLITE3_ARTIFACTS),$($(x)_OBJS))
$(THIRD_PARTY_SQLITE3_OBJS): third_party/sqlite3/sqlite3.mk
$(THIRD_PARTY_SQLITE3_SHELL_OBJS): third_party/sqlite3/sqlite3.mk
.PHONY: o/$(MODE)/third_party/sqlite3
o/$(MODE)/third_party/sqlite3: \

View file

@ -1,4 +1,5 @@
mymodule = require "mymodule"
sqlite3 = require "lsqlite3"
-- /.init.lua is loaded at startup in redbean's main process
HidePath('/usr/share/zoneinfo/')
@ -6,6 +7,18 @@ HidePath('/usr/share/zoneinfo/')
-- open a browser tab using explorer/open/xdg-open
-- LaunchBrowser('/tool/net/demo/index.html')
-- sql database (see sql.lua)
db = sqlite3.open_memory()
db:exec[[
CREATE TABLE test (
id INTEGER PRIMARY KEY,
content TEXT
);
INSERT INTO test (content) VALUES ('Hello World');
INSERT INTO test (content) VALUES ('Hello Lua');
INSERT INTO test (content) VALUES ('Hello Sqlite3');
]]
-- this intercepts all requests if it's defined
function OnHttpRequest()
if HasParam('magic') then

4
tool/net/demo/sql.lua Normal file
View file

@ -0,0 +1,4 @@
-- See .init.lua for CREATE TABLE setup.
for row in db:nrows("SELECT * FROM test") do
Write(row.id.." "..row.content.."<br>")
end

View file

@ -1,3 +1,4 @@
/* clang-format off */
/************************************************************************
* lsqlite3 *
* Copyright (C) 2002-2016 Tiago Dionizio, Doug Currie *
@ -25,55 +26,37 @@
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE *
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
************************************************************************/
#include "libc/calls/weirdtypes.h"
#include "libc/mem/mem.h"
#include "third_party/lua/lauxlib.h"
#include "third_party/lua/lua.h"
#include "third_party/lua/luaconf.h"
#include "third_party/sqlite3/sqlite3.h"
// attribution in binary form
asm(".ident\t\"\\n\\n\
lsqlite3 (MIT License)\\n\
Copyright 2002-2016 Tiago Dionizio, Doug Currie\"");
asm(".include \"libc/disclaimer.inc\"");
#include "libc/calls/weirdtypes.h"
#include "libc/mem/mem.h"
#include "third_party/lua/lua.h"
#include "third_party/lua/lauxlib.h"
#if LUA_VERSION_NUM > 501
/*
** Lua 5.2
*/
#ifndef lua_strlen
#define lua_strlen lua_rawlen
#endif
/* luaL_typerror always used with arg at ndx == NULL */
#define luaL_typerror(L,ndx,str) luaL_error(L,"bad argument %d (%s expected, got nil)",ndx,str)
/* luaL_register used once, so below expansion is OK for this case */
#define luaL_register(L,name,reg) lua_newtable(L);luaL_setfuncs(L,reg,0)
/* luaL_openlib always used with name == NULL */
#define luaL_openlib(L,name,reg,nup) luaL_setfuncs(L,reg,nup)
#if LUA_VERSION_NUM > 502
/*
** Lua 5.3
*/
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
#endif
#endif
#include "third_party/sqlite3/sqlite3.h"
/* compile time features */
#if !defined(SQLITE_OMIT_PROGRESS_CALLBACK)
#define SQLITE_OMIT_PROGRESS_CALLBACK 0
#endif
#if !defined(LSQLITE_OMIT_UPDATE_HOOK)
#define LSQLITE_OMIT_UPDATE_HOOK 0
#endif
#if defined(LSQLITE_OMIT_OPEN_V2)
#define SQLITE3_OPEN(L,filename,flags) sqlite3_open(L,filename)
#else
#define SQLITE3_OPEN(L,filename,flags) sqlite3_open_v2(L,filename,flags,NULL)
#endif
#define PUSH_INT64(L,i64in,fallback) \
do { \
sqlite_int64 i64 = i64in; \
lua_Integer i = (lua_Integer )i64; \
if (i == i64) lua_pushinteger(L, i);\
else { \
lua_Number n = (lua_Number)i64; \
if (n == i64) lua_pushnumber(L, n); \
else fallback; \
} \
} while (0)
typedef struct sdb sdb;
typedef struct sdb_vm sdb_vm;
@ -106,59 +89,14 @@ struct sdb {
/* references */
int busy_cb; /* busy callback */
int busy_udata;
int progress_cb; /* progress handler */
int progress_udata;
int trace_cb; /* trace callback */
int trace_udata;
#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK
int update_hook_cb; /* update_hook callback */
int update_hook_udata;
int commit_hook_cb; /* commit_hook callback */
int commit_hook_udata;
int rollback_hook_cb; /* rollback_hook callback */
int rollback_hook_udata;
#endif
};
static const char *sqlite_meta = ":sqlite3";
static const char *sqlite_vm_meta = ":sqlite3:vm";
static const char *sqlite_bu_meta = ":sqlite3:bu";
static const char *sqlite_ctx_meta = ":sqlite3:ctx";
static const char *const sqlite_meta = ":sqlite3";
static const char *const sqlite_vm_meta = ":sqlite3:vm";
static const char *const sqlite_bu_meta = ":sqlite3:bu";
static const char *const sqlite_ctx_meta = ":sqlite3:ctx";
static int sqlite_ctx_meta_ref;
/* Lua 5.3 introduced an integer type, but depending on the implementation, it could be 32
** or 64 bits (or something else?). This helper macro tries to do "the right thing."
*/
#if LUA_VERSION_NUM > 502
#define PUSH_INT64(L,i64in,fallback) \
do { \
sqlite_int64 i64 = i64in; \
lua_Integer i = (lua_Integer )i64; \
if (i == i64) lua_pushinteger(L, i);\
else { \
lua_Number n = (lua_Number)i64; \
if (n == i64) lua_pushnumber(L, n); \
else fallback; \
} \
} while (0)
#else
#define PUSH_INT64(L,i64in,fallback) \
do { \
sqlite_int64 i64 = i64in; \
lua_Number n = (lua_Number)i64; \
if (n == i64) lua_pushnumber(L, n); \
else fallback; \
} while (0)
#endif
/*
** =======================================================
** Database Virtual Machine Operations
@ -498,7 +436,7 @@ static int dbvm_get_named_types(lua_State *L) {
static int dbvm_bind_index(lua_State *L, sqlite3_stmt *vm, int index, int lindex) {
switch (lua_type(L, lindex)) {
case LUA_TSTRING:
return sqlite3_bind_text(vm, index, lua_tostring(L, lindex), lua_strlen(L, lindex), SQLITE_TRANSIENT);
return sqlite3_bind_text(vm, index, lua_tostring(L, lindex), lua_rawlen(L, lindex), SQLITE_TRANSIENT);
case LUA_TNUMBER:
#if LUA_VERSION_NUM > 502
if (lua_isinteger(L, lindex))
@ -548,7 +486,7 @@ static int dbvm_bind_blob(lua_State *L) {
sdb_vm *svm = lsqlite_checkvm(L, 1);
int index = luaL_checkint(L, 2);
const char *value = luaL_checkstring(L, 3);
int len = lua_strlen(L, 3);
int len = lua_rawlen(L, 3);
lua_pushinteger(L, sqlite3_bind_blob(svm->vm, index, value, len, SQLITE_TRANSIENT));
return 1;
@ -632,18 +570,6 @@ static sdb *newdb (lua_State *L) {
db->busy_cb =
db->busy_udata =
db->progress_cb =
db->progress_udata =
db->trace_cb =
db->trace_udata =
#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK
db->update_hook_cb =
db->update_hook_udata =
db->commit_hook_cb =
db->commit_hook_udata =
db->rollback_hook_cb =
db->rollback_hook_udata =
#endif
LUA_NOREF;
luaL_getmetatable(L, sqlite_meta);
@ -688,18 +614,6 @@ static int cleanupdb(lua_State *L, sdb *db) {
/* 'free' all references */
luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata);
luaL_unref(L, LUA_REGISTRYINDEX, db->progress_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->progress_udata);
luaL_unref(L, LUA_REGISTRYINDEX, db->trace_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->trace_udata);
#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK
luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_udata);
luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_udata);
luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_udata);
#endif
/* close database */
result = sqlite3_close(db->db);
@ -804,13 +718,6 @@ static int lcontext_set_aggregate_context(lua_State *L) {
return 0;
}
static int lcontext_aggregate_count(lua_State *L) {
lcontext *ctx = lsqlite_checkcontext(L, 1);
lcontext_check_aggregate(L, ctx);
lua_pushinteger(L, sqlite3_aggregate_count(ctx->ctx));
return 1;
}
#if 0
void *sqlite3_get_auxdata(sqlite3_context*, int);
void sqlite3_set_auxdata(sqlite3_context*, int, void*, void (*)(void*));
@ -828,7 +735,7 @@ static int lcontext_result(lua_State *L) {
sqlite3_result_double(ctx->ctx, luaL_checknumber(L, 2));
break;
case LUA_TSTRING:
sqlite3_result_text(ctx->ctx, luaL_checkstring(L, 2), lua_strlen(L, 2), SQLITE_TRANSIENT);
sqlite3_result_text(ctx->ctx, luaL_checkstring(L, 2), lua_rawlen(L, 2), SQLITE_TRANSIENT);
break;
case LUA_TNIL:
case LUA_TNONE:
@ -845,7 +752,7 @@ static int lcontext_result(lua_State *L) {
static int lcontext_result_blob(lua_State *L) {
lcontext *ctx = lsqlite_checkcontext(L, 1);
const char *blob = luaL_checkstring(L, 2);
int size = lua_strlen(L, 2);
int size = lua_rawlen(L, 2);
sqlite3_result_blob(ctx->ctx, (const void*)blob, size, SQLITE_TRANSIENT);
return 0;
}
@ -860,7 +767,7 @@ static int lcontext_result_double(lua_State *L) {
static int lcontext_result_error(lua_State *L) {
lcontext *ctx = lsqlite_checkcontext(L, 1);
const char *err = luaL_checkstring(L, 2);
int size = lua_strlen(L, 2);
int size = lua_rawlen(L, 2);
sqlite3_result_error(ctx->ctx, err, size);
return 0;
}
@ -881,7 +788,7 @@ static int lcontext_result_null(lua_State *L) {
static int lcontext_result_text(lua_State *L) {
lcontext *ctx = lsqlite_checkcontext(L, 1);
const char *text = luaL_checkstring(L, 2);
int size = lua_strlen(L, 2);
int size = lua_rawlen(L, 2);
sqlite3_result_text(ctx->ctx, text, size, SQLITE_TRANSIENT);
return 0;
}
@ -1031,7 +938,7 @@ static void db_sql_normal_function(sqlite3_context *context, int argc, sqlite3_v
if (lua_pcall(L, argc + 1, 0, 0)) {
const char *errmsg = lua_tostring(L, -1);
int size = lua_strlen(L, -1);
int size = lua_rawlen(L, -1);
sqlite3_result_error(context, errmsg, size);
}
@ -1223,461 +1130,6 @@ static int db_create_collation(lua_State *L) {
return 0;
}
// comment out `db_load_extension`, as cosmo can't resolve
// `sqlite3_enable_load_extension`, since it's built with
// `SQLITE_OMIT_LOAD_EXTENSION`, which disables extensions.
#if 0
/* Thanks to Wolfgang Oertl...
*/
static int db_load_extension(lua_State *L) {
sdb *db=lsqlite_checkdb(L,1);
const char *extname=luaL_optstring(L,2,NULL);
const char *entrypoint=luaL_optstring(L,3,NULL);
int result;
char *errmsg = NULL;
if (extname == NULL) {
result = sqlite3_enable_load_extension(db->db,0); /* disable extension loading */
}
else {
sqlite3_enable_load_extension(db->db,1); /* enable extension loading */
result = sqlite3_load_extension(db->db,extname,entrypoint,&errmsg);
}
if (result == SQLITE_OK) {
lua_pushboolean(L,1);
return 1;
}
lua_pushboolean(L,0); /* so, assert(load_extension(...)) works */
lua_pushstring(L,errmsg);
sqlite3_free(errmsg);
return 2;
}
#endif
/*
** trace callback:
** Params: database, callback function, userdata
**
** callback function:
** Params: userdata, sql
*/
static void db_trace_callback(void *user, const char *sql) {
sdb *db = (sdb*)user;
lua_State *L = db->L;
int top = lua_gettop(L);
/* setup lua callback call */
lua_rawgeti(L, LUA_REGISTRYINDEX, db->trace_cb); /* get callback */
lua_rawgeti(L, LUA_REGISTRYINDEX, db->trace_udata); /* get callback user data */
lua_pushstring(L, sql); /* traced sql statement */
/* call lua function */
lua_pcall(L, 2, 0, 0);
/* ignore any error generated by this function */
lua_settop(L, top);
}
static int db_trace(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
if (lua_gettop(L) < 2 || lua_isnil(L, 2)) {
luaL_unref(L, LUA_REGISTRYINDEX, db->trace_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->trace_udata);
db->trace_cb =
db->trace_udata = LUA_NOREF;
/* clear trace handler */
sqlite3_trace(db->db, NULL, NULL);
}
else {
luaL_checktype(L, 2, LUA_TFUNCTION);
/* make sure we have an userdata field (even if nil) */
lua_settop(L, 3);
luaL_unref(L, LUA_REGISTRYINDEX, db->trace_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->trace_udata);
db->trace_udata = luaL_ref(L, LUA_REGISTRYINDEX);
db->trace_cb = luaL_ref(L, LUA_REGISTRYINDEX);
/* set trace handler */
sqlite3_trace(db->db, db_trace_callback, db);
}
return 0;
}
#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK
/*
** update_hook callback:
** Params: database, callback function, userdata
**
** callback function:
** Params: userdata, {one of SQLITE_INSERT, SQLITE_DELETE, or SQLITE_UPDATE},
** database name, table name (containing the affected row), rowid of the row
*/
static void db_update_hook_callback(void *user, int op, char const *dbname, char const *tblname, sqlite3_int64 rowid) {
sdb *db = (sdb*)user;
lua_State *L = db->L;
int top = lua_gettop(L);
lua_Number n;
/* setup lua callback call */
lua_rawgeti(L, LUA_REGISTRYINDEX, db->update_hook_cb); /* get callback */
lua_rawgeti(L, LUA_REGISTRYINDEX, db->update_hook_udata); /* get callback user data */
lua_pushinteger(L, op);
lua_pushstring(L, dbname); /* update_hook database name */
lua_pushstring(L, tblname); /* update_hook database name */
PUSH_INT64(L, rowid, lua_pushfstring(L, "%ll", rowid));
/* call lua function */
lua_pcall(L, 5, 0, 0);
/* ignore any error generated by this function */
lua_settop(L, top);
}
static int db_update_hook(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
if (lua_gettop(L) < 2 || lua_isnil(L, 2)) {
luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_udata);
db->update_hook_cb =
db->update_hook_udata = LUA_NOREF;
/* clear update_hook handler */
sqlite3_update_hook(db->db, NULL, NULL);
}
else {
luaL_checktype(L, 2, LUA_TFUNCTION);
/* make sure we have an userdata field (even if nil) */
lua_settop(L, 3);
luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->update_hook_udata);
db->update_hook_udata = luaL_ref(L, LUA_REGISTRYINDEX);
db->update_hook_cb = luaL_ref(L, LUA_REGISTRYINDEX);
/* set update_hook handler */
sqlite3_update_hook(db->db, db_update_hook_callback, db);
}
return 0;
}
/*
** commit_hook callback:
** Params: database, callback function, userdata
**
** callback function:
** Params: userdata
** Returned value: Return false or nil to continue the COMMIT operation normally.
** return true (non false, non nil), then the COMMIT is converted into a ROLLBACK.
*/
static int db_commit_hook_callback(void *user) {
sdb *db = (sdb*)user;
lua_State *L = db->L;
int top = lua_gettop(L);
int rollback = 0;
/* setup lua callback call */
lua_rawgeti(L, LUA_REGISTRYINDEX, db->commit_hook_cb); /* get callback */
lua_rawgeti(L, LUA_REGISTRYINDEX, db->commit_hook_udata); /* get callback user data */
/* call lua function */
if (!lua_pcall(L, 1, 1, 0))
rollback = lua_toboolean(L, -1); /* use result if there was no error */
lua_settop(L, top);
return rollback;
}
static int db_commit_hook(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
if (lua_gettop(L) < 2 || lua_isnil(L, 2)) {
luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_udata);
db->commit_hook_cb =
db->commit_hook_udata = LUA_NOREF;
/* clear commit_hook handler */
sqlite3_commit_hook(db->db, NULL, NULL);
}
else {
luaL_checktype(L, 2, LUA_TFUNCTION);
/* make sure we have an userdata field (even if nil) */
lua_settop(L, 3);
luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->commit_hook_udata);
db->commit_hook_udata = luaL_ref(L, LUA_REGISTRYINDEX);
db->commit_hook_cb = luaL_ref(L, LUA_REGISTRYINDEX);
/* set commit_hook handler */
sqlite3_commit_hook(db->db, db_commit_hook_callback, db);
}
return 0;
}
/*
** rollback hook callback:
** Params: database, callback function, userdata
**
** callback function:
** Params: userdata
*/
static void db_rollback_hook_callback(void *user) {
sdb *db = (sdb*)user;
lua_State *L = db->L;
int top = lua_gettop(L);
/* setup lua callback call */
lua_rawgeti(L, LUA_REGISTRYINDEX, db->rollback_hook_cb); /* get callback */
lua_rawgeti(L, LUA_REGISTRYINDEX, db->rollback_hook_udata); /* get callback user data */
/* call lua function */
lua_pcall(L, 1, 0, 0);
/* ignore any error generated by this function */
lua_settop(L, top);
}
static int db_rollback_hook(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
if (lua_gettop(L) < 2 || lua_isnil(L, 2)) {
luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_udata);
db->rollback_hook_cb =
db->rollback_hook_udata = LUA_NOREF;
/* clear rollback_hook handler */
sqlite3_rollback_hook(db->db, NULL, NULL);
}
else {
luaL_checktype(L, 2, LUA_TFUNCTION);
/* make sure we have an userdata field (even if nil) */
lua_settop(L, 3);
luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->rollback_hook_udata);
db->rollback_hook_udata = luaL_ref(L, LUA_REGISTRYINDEX);
db->rollback_hook_cb = luaL_ref(L, LUA_REGISTRYINDEX);
/* set rollback_hook handler */
sqlite3_rollback_hook(db->db, db_rollback_hook_callback, db);
}
return 0;
}
#endif /* #if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK */
#if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK
/*
** progress handler:
** Params: database, number of opcodes, callback function, userdata
**
** callback function:
** Params: userdata
** returns: 0 to return immediatly and return SQLITE_ABORT, non-zero to continue
*/
static int db_progress_callback(void *user) {
int result = 1; /* abort by default */
sdb *db = (sdb*)user;
lua_State *L = db->L;
int top = lua_gettop(L);
lua_rawgeti(L, LUA_REGISTRYINDEX, db->progress_cb);
lua_rawgeti(L, LUA_REGISTRYINDEX, db->progress_udata);
/* call lua function */
if (!lua_pcall(L, 1, 1, 0))
result = lua_toboolean(L, -1);
lua_settop(L, top);
return result;
}
static int db_progress_handler(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
if (lua_gettop(L) < 2 || lua_isnil(L, 2)) {
luaL_unref(L, LUA_REGISTRYINDEX, db->progress_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->progress_udata);
db->progress_cb =
db->progress_udata = LUA_NOREF;
/* clear busy handler */
sqlite3_progress_handler(db->db, 0, NULL, NULL);
}
else {
int nop = luaL_checkint(L, 2); /* number of opcodes */
luaL_checktype(L, 3, LUA_TFUNCTION);
/* make sure we have an userdata field (even if nil) */
lua_settop(L, 4);
luaL_unref(L, LUA_REGISTRYINDEX, db->progress_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->progress_udata);
db->progress_udata = luaL_ref(L, LUA_REGISTRYINDEX);
db->progress_cb = luaL_ref(L, LUA_REGISTRYINDEX);
/* set progress callback */
sqlite3_progress_handler(db->db, nop, db_progress_callback, db);
}
return 0;
}
#else /* #if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK */
static int db_progress_handler(lua_State *L) {
lua_pushliteral(L, "progress callback support disabled at compile time");
lua_error(L);
return 0;
}
#endif /* #if !defined(SQLITE_OMIT_PROGRESS_CALLBACK) || !SQLITE_OMIT_PROGRESS_CALLBACK */
/* Online Backup API */
#if 0
sqlite3_backup *sqlite3_backup_init(
sqlite3 *pDest, /* Destination database handle */
const char *zDestName, /* Destination database name */
sqlite3 *pSource, /* Source database handle */
const char *zSourceName /* Source database name */
);
int sqlite3_backup_step(sqlite3_backup *p, int nPage);
int sqlite3_backup_finish(sqlite3_backup *p);
int sqlite3_backup_remaining(sqlite3_backup *p);
int sqlite3_backup_pagecount(sqlite3_backup *p);
#endif
struct sdb_bu {
sqlite3_backup *bu; /* backup structure */
};
static int cleanupbu(lua_State *L, sdb_bu *sbu) {
if (!sbu->bu) return 0; /* already finished */
/* remove table from registry */
lua_pushlightuserdata(L, sbu->bu);
lua_pushnil(L);
lua_rawset(L, LUA_REGISTRYINDEX);
lua_pushinteger(L, sqlite3_backup_finish(sbu->bu));
sbu->bu = NULL;
return 1;
}
static int lsqlite_backup_init(lua_State *L) {
sdb *target_db = lsqlite_checkdb(L, 1);
const char *target_nm = luaL_checkstring(L, 2);
sdb *source_db = lsqlite_checkdb(L, 3);
const char *source_nm = luaL_checkstring(L, 4);
sqlite3_backup *bu = sqlite3_backup_init(target_db->db, target_nm, source_db->db, source_nm);
if (NULL != bu) {
sdb_bu *sbu = (sdb_bu*)lua_newuserdata(L, sizeof(sdb_bu));
luaL_getmetatable(L, sqlite_bu_meta);
lua_setmetatable(L, -2); /* set metatable */
sbu->bu = bu;
/* create table from registry */
/* to prevent referenced databases from being garbage collected while bu is live */
lua_pushlightuserdata(L, bu);
lua_createtable(L, 2, 0);
/* add source and target dbs to table at indices 1 and 2 */
lua_pushvalue(L, 1); /* target db */
lua_rawseti(L, -2, 1);
lua_pushvalue(L, 3); /* source db */
lua_rawseti(L, -2, 2);
/* put table in registry with key lightuserdata bu */
lua_rawset(L, LUA_REGISTRYINDEX);
return 1;
}
else {
return 0;
}
}
static sdb_bu *lsqlite_getbu(lua_State *L, int index) {
sdb_bu *sbu = (sdb_bu*)luaL_checkudata(L, index, sqlite_bu_meta);
if (sbu == NULL) luaL_typerror(L, index, "sqlite database backup");
return sbu;
}
static sdb_bu *lsqlite_checkbu(lua_State *L, int index) {
sdb_bu *sbu = lsqlite_getbu(L, index);
if (sbu->bu == NULL) luaL_argerror(L, index, "attempt to use closed sqlite database backup");
return sbu;
}
static int dbbu_gc(lua_State *L) {
sdb_bu *sbu = lsqlite_getbu(L, 1);
if (sbu->bu != NULL) {
cleanupbu(L, sbu);
lua_pop(L, 1);
}
/* else ignore if already finished */
return 0;
}
static int dbbu_step(lua_State *L) {
sdb_bu *sbu = lsqlite_checkbu(L, 1);
int nPage = luaL_checkint(L, 2);
lua_pushinteger(L, sqlite3_backup_step(sbu->bu, nPage));
return 1;
}
static int dbbu_remaining(lua_State *L) {
sdb_bu *sbu = lsqlite_checkbu(L, 1);
lua_pushinteger(L, sqlite3_backup_remaining(sbu->bu));
return 1;
}
static int dbbu_pagecount(lua_State *L) {
sdb_bu *sbu = lsqlite_checkbu(L, 1);
lua_pushinteger(L, sqlite3_backup_pagecount(sbu->bu));
return 1;
}
static int dbbu_finish(lua_State *L) {
sdb_bu *sbu = lsqlite_checkbu(L, 1);
return cleanupbu(L, sbu);
}
/* end of Online Backup API */
/*
** busy handler:
** Params: database, callback function, userdata
@ -1840,7 +1292,7 @@ static int db_exec(lua_State *L) {
static int db_prepare(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
const char *sql = luaL_checkstring(L, 2);
int sql_len = lua_strlen(L, 2);
int sql_len = lua_rawlen(L, 2);
const char *sqltail;
sdb_vm *svm;
lua_settop(L,2); /* db,sql is on top of stack for call to newvm */
@ -2024,17 +1476,6 @@ static int db_close_vm(lua_State *L) {
return 0;
}
/* From: Wolfgang Oertl
When using lsqlite3 in a multithreaded environment, each thread has a separate Lua
environment, but full userdata structures can't be passed from one thread to another.
This is possible with lightuserdata, however. See: lsqlite_open_ptr().
*/
static int db_get_ptr(lua_State *L) {
sdb *db = lsqlite_checkdb(L, 1);
lua_pushlightuserdata(L, db->db);
return 1;
}
static int db_gc(lua_State *L) {
sdb *db = lsqlite_getdb(L, 1);
if (db->db != NULL) /* ignore closed databases */
@ -2053,37 +1494,10 @@ static int lsqlite_version(lua_State *L) {
return 1;
}
static int lsqlite_complete(lua_State *L) {
const char *sql = luaL_checkstring(L, 1);
lua_pushboolean(L, sqlite3_complete(sql));
return 1;
}
#ifndef _WIN32
static int lsqlite_temp_directory(lua_State *L) {
const char *oldtemp = sqlite3_temp_directory;
if (!lua_isnone(L, 1)) {
const char *temp = luaL_optstring(L, 1, NULL);
if (sqlite3_temp_directory) {
sqlite3_free((char*)sqlite3_temp_directory);
}
if (temp) {
sqlite3_temp_directory = sqlite3_mprintf("%s", temp);
}
else {
sqlite3_temp_directory = NULL;
}
}
lua_pushstring(L, oldtemp);
return 1;
}
#endif
static int lsqlite_do_open(lua_State *L, const char *filename, int flags) {
sdb *db = newdb(L); /* create and leave in stack */
if (SQLITE3_OPEN(filename, &db->db, flags) == SQLITE_OK) {
if (sqlite3_open_v2(filename, &db->db, flags, 0) == SQLITE_OK) {
/* database handle already in the stack - return it */
return 1;
}
@ -2110,30 +1524,6 @@ static int lsqlite_open_memory(lua_State *L) {
return lsqlite_do_open(L, ":memory:", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
}
/* From: Wolfgang Oertl
When using lsqlite3 in a multithreaded environment, each thread has a separate Lua
environment, but full userdata structures can't be passed from one thread to another.
This is possible with lightuserdata, however. See: db_get_ptr().
*/
static int lsqlite_open_ptr(lua_State *L) {
sqlite3 *db_ptr;
sdb *db;
int rc;
luaL_checktype(L, 1, LUA_TLIGHTUSERDATA);
db_ptr = lua_touserdata(L, 1);
/* This is the only API function that runs sqlite3SafetyCheck regardless of
* SQLITE_ENABLE_API_ARMOR and does almost nothing (without an SQL
* statement) */
rc = sqlite3_exec(db_ptr, NULL, NULL, NULL, NULL);
if (rc != SQLITE_OK)
luaL_argerror(L, 1, "not a valid SQLite3 pointer");
db = newdb(L); /* create and leave in stack */
db->db = db_ptr;
return 1;
}
static int lsqlite_newindex(lua_State *L) {
lua_pushliteral(L, "attempt to change readonly table");
lua_error(L);
@ -2247,19 +1637,9 @@ static const luaL_Reg dblib[] = {
{"create_function", db_create_function },
{"create_aggregate", db_create_aggregate },
{"create_collation", db_create_collation },
#if 0
{"load_extension", db_load_extension },
#endif
{"trace", db_trace },
{"progress_handler", db_progress_handler },
{"busy_timeout", db_busy_timeout },
{"busy_handler", db_busy_handler },
#if !defined(LSQLITE_OMIT_UPDATE_HOOK) || !LSQLITE_OMIT_UPDATE_HOOK
{"update_hook", db_update_hook },
{"commit_hook", db_commit_hook },
{"rollback_hook", db_rollback_hook },
#endif
{"prepare", db_prepare },
{"rows", db_rows },
@ -2270,7 +1650,6 @@ static const luaL_Reg dblib[] = {
{"execute", db_exec },
{"close", db_close },
{"close_vm", db_close_vm },
{"get_ptr", db_get_ptr },
{"__tostring", db_tostring },
{"__gc", db_gc },
@ -2331,7 +1710,6 @@ static const luaL_Reg ctxlib[] = {
{"get_aggregate_data", lcontext_get_aggregate_context },
{"set_aggregate_data", lcontext_set_aggregate_context },
{"aggregate_count", lcontext_aggregate_count },
{"result", lcontext_result },
{"result_null", lcontext_result_null },
@ -2346,30 +1724,11 @@ static const luaL_Reg ctxlib[] = {
{NULL, NULL}
};
static const luaL_Reg dbbulib[] = {
{"step", dbbu_step },
{"remaining", dbbu_remaining },
{"pagecount", dbbu_pagecount },
{"finish", dbbu_finish },
// {"__tostring", dbbu_tostring },
{"__gc", dbbu_gc },
{NULL, NULL}
};
static const luaL_Reg sqlitelib[] = {
{"lversion", lsqlite_lversion },
{"version", lsqlite_version },
{"complete", lsqlite_complete },
#ifndef _WIN32
{"temp_directory", lsqlite_temp_directory },
#endif
{"open", lsqlite_open },
{"open_memory", lsqlite_open_memory },
{"open_ptr", lsqlite_open_ptr },
{"backup_init", lsqlite_backup_init },
{"__newindex", lsqlite_newindex },
{NULL, NULL}
@ -2389,9 +1748,10 @@ static void create_meta(lua_State *L, const char *name, const luaL_Reg *lib) {
}
LUALIB_API int luaopen_lsqlite3(lua_State *L) {
sqlite3_initialize();
create_meta(L, sqlite_meta, dblib);
create_meta(L, sqlite_vm_meta, vmlib);
create_meta(L, sqlite_bu_meta, dbbulib);
create_meta(L, sqlite_ctx_meta, ctxlib);
luaL_getmetatable(L, sqlite_ctx_meta);

View file

@ -52,7 +52,6 @@ TOOL_NET_DEPS := \
o/$(MODE)/tool/net/net.pkg: \
$(TOOL_NET_OBJS) \
o/$(MODE)/tool/net/lsqlite3.o \
$(foreach x,$(TOOL_NET_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/tool/net/%.com.dbg: \
@ -96,6 +95,7 @@ o/$(MODE)/tool/net/redbean-demo.com: \
tool/net/demo/.init.lua \
tool/net/demo/.reload.lua \
tool/net/demo/.lua/mymodule.lua \
tool/net/demo/sql.lua \
tool/net/demo/404.html \
tool/net/demo/hello.lua \
tool/net/demo/index.html \
@ -118,7 +118,7 @@ o/$(MODE)/tool/net/redbean-demo.com: \
@$(COMPILE) -ARM -T$@ rm -rf o/$(MODE)/tool/net/.lua
@$(COMPILE) -ACP -T$@ cp -R tool/net/demo/.lua o/$(MODE)/tool/net/
@(cd o/$(MODE)/tool/net && zip -qr redbean-demo.com .lua)
@$(COMPILE) -AZIP -T$@ zip -qj $@ tool/net/demo/hello.lua
@$(COMPILE) -AZIP -T$@ zip -qj $@ tool/net/demo/hello.lua tool/net/demo/sql.lua
@echo "&lt;-- check out this lua server page" | $(COMPILE) -AZIP -T$@ zip -cqj $@ tool/net/demo/redbean.lua
@$(COMPILE) -AZIP -T$@ zip -qj $@ tool/net/demo/404.html tool/net/favicon.ico tool/net/redbean.png tool/net/demo/redbean-form.lua tool/net/demo/redbean-xhr.lua
@echo Uncompressed for HTTP Range requests | $(COMPILE) -AZIP -T$@ zip -cqj0 $@ tool/net/demo/seekable.txt

View file

@ -3644,11 +3644,11 @@ static const luaL_Reg kLuaFuncs[] = {
{"popcnt", LuaPopcnt}, //
};
extern int luaopen_lsqlite3(lua_State *L);
extern int luaopen_lsqlite3(lua_State *);
static const luaL_Reg kLuaLibs[] = {
{"re", LuaRe}, //
{"lsqlite3", luaopen_lsqlite3},
{"re", LuaRe}, //
{"lsqlite3", luaopen_lsqlite3}, //
};
static void LuaSetArgv(lua_State *L) {