diff --git a/third_party/sqlite3/sqlite3.mk b/third_party/sqlite3/sqlite3.mk index 85acb556d..7023426a0 100644 --- a/third_party/sqlite3/sqlite3.mk +++ b/third_party/sqlite3/sqlite3.mk @@ -126,7 +126,9 @@ THIRD_PARTY_SQLITE3_FLAGS = \ -DSQLITE_HAVE_C99_MATH_FUNCS \ -DSQLITE_ENABLE_MATH_FUNCTIONS \ -DSQLITE_ENABLE_JSON1 \ - -DSQLITE_ENABLE_DESERIALIZE + -DSQLITE_ENABLE_DESERIALIZE \ + -DSQLITE_ENABLE_PREUPDATE_HOOK \ + -DSQLITE_ENABLE_SESSION ifeq ($(MODE),dbg) THIRD_PARTY_SQLITE3_CPPFLAGS_DEBUG = -DSQLITE_DEBUG @@ -136,7 +138,6 @@ $(THIRD_PARTY_SQLITE3_A_OBJS): private \ OVERRIDE_CFLAGS += \ $(THIRD_PARTY_SQLITE3_FLAGS) \ $(THIRD_PARTY_SQLITE3_CPPFLAGS_DEBUG) \ - -DSQLITE_OMIT_UPDATE_HOOK $(THIRD_PARTY_SQLITE3_SHELL_OBJS): private \ OVERRIDE_CFLAGS += \ @@ -146,11 +147,9 @@ $(THIRD_PARTY_SQLITE3_SHELL_OBJS): private \ -DHAVE_EDITLINE=0 \ -DSQLITE_HAVE_ZLIB \ -DSQLITE_ENABLE_IOTRACE \ - -DSQLITE_ENABLE_PREUPDATE_HOOK \ -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 \ diff --git a/tool/net/lsqlite3.c b/tool/net/lsqlite3.c index 0e5798a6a..886439c22 100644 --- a/tool/net/lsqlite3.c +++ b/tool/net/lsqlite3.c @@ -119,7 +119,9 @@ 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 const char *const sqlite_ses_meta = ":sqlite3:ses"; static int sqlite_ctx_meta_ref; +static int sqlite_ses_meta_ref; /* ** ======================================================= @@ -1728,6 +1730,104 @@ static int db_deserialize(lua_State *L) { return 0; } +/* +** ======================================================= +** Session functions +** ======================================================= +*/ + +typedef struct { + sqlite3_session *ses; +} lsession; + +static lsession *lsqlite_makesession(lua_State *L, sqlite3_session *ses) { + lsession *lses = (lsession*)lua_newuserdata(L, sizeof(lsession)); + lua_rawgeti(L, LUA_REGISTRYINDEX, sqlite_ses_meta_ref); + lua_setmetatable(L, -2); + lses->ses = ses; + return lses; +} + +static lsession *lsqlite_getsession(lua_State *L, int index) { + return (lsession *)luaL_checkudata(L, index, sqlite_ses_meta); +} + +static lsession *lsqlite_checksession(lua_State *L, int index) { + lsession *lses = lsqlite_getsession(L, index); + if (lses->ses == NULL) luaL_argerror(L, index, "invalid sqlite session"); + return lses; +} + +static int lsession_attach(lua_State *L) { + lsession *lses = lsqlite_checksession(L, 1); + const char *zTab = luaL_optstring(L, 2, NULL); + + if (sqlite3session_attach(lses->ses, zTab) != SQLITE_OK) { + lua_pushnil(L); + } else { + lua_pushboolean(L, 1); + } + return 1; +} + +static int lsession_isempty(lua_State *L) { + lsession *lses = lsqlite_checksession(L, 1); + lua_pushboolean(L, sqlite3session_isempty(lses->ses)); + return 1; +} + +static int lsession_changeset(lua_State *L) { + lsession *lses = lsqlite_checksession(L, 1); + int size; + void *buf; + if (sqlite3session_changeset(lses->ses, &size, &buf) != SQLITE_OK) { + lua_pushnil(L); + } else { + lua_pushlstring(L, buf, size); + sqlite3_free(buf); + } + return 1; +} + +static int lsession_tostring(lua_State *L) { + char buff[30]; + lsession *lses = lsqlite_getsession(L, 1); + if (lses->ses == NULL) + strcpy(buff, "closed"); + else + sprintf(buff, "%p", lses->ses); + lua_pushfstring(L, "sqlite session (%s)", buff); + return 1; +} + +static int lsession_delete(lua_State *L) { + lsession *lses = lsqlite_getsession(L, 1); + if (lses->ses != NULL) { + sqlite3session_delete(lses->ses); + lses->ses = NULL; + } + return 0; +} + +static int lsession_gc(lua_State *L) { + return lsession_delete(L); +} + +static int db_create_session(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + const char *zDb = luaL_optstring(L, 2, "main"); + sqlite3_session *ses; + + if (sqlite3session_create(db->db, zDb, &ses) != SQLITE_OK) { + lua_pushnil(L); + lua_pushinteger(L, sqlite3_errcode(db->db)); + return 2; + } + + (void)lsqlite_makesession(L, ses); + return 1; +} + /* ** ======================================================= ** General library functions @@ -1859,6 +1959,19 @@ static const struct { SC(OPEN_SHAREDCACHE) SC(OPEN_PRIVATECACHE) + /* session constants */ + SC(CHANGESETSTART_INVERT) + SC(CHANGESETAPPLY_NOSAVEPOINT) + SC(CHANGESETAPPLY_INVERT) + SC(CHANGESET_DATA) + SC(CHANGESET_NOTFOUND) + SC(CHANGESET_CONFLICT) + SC(CHANGESET_CONSTRAINT) + SC(CHANGESET_FOREIGN_KEY) + SC(CHANGESET_OMIT) + SC(CHANGESET_REPLACE) + SC(CHANGESET_ABORT) + /* terminator */ { NULL, 0 } }; @@ -1900,6 +2013,8 @@ static const luaL_Reg dblib[] = { {"serialize", db_serialize }, {"deserialize", db_deserialize }, + {"create_session", db_create_session }, + {"__tostring", db_tostring }, {"__gc", db_gc }, @@ -1973,6 +2088,17 @@ static const luaL_Reg ctxlib[] = { {NULL, NULL} }; +static const luaL_Reg seslib[] = { + {"attach", lsession_attach }, + {"changeset", lsession_changeset }, + {"isempty", lsession_isempty }, + {"delete", lsession_delete }, + + {"__tostring", lsession_tostring }, + {"__gc", lsession_gc }, + {NULL, NULL} +}; + static const luaL_Reg sqlitelib[] = { {"lversion", lsqlite_lversion }, {"version", lsqlite_version }, @@ -2002,10 +2128,14 @@ LUALIB_API int luaopen_lsqlite3(lua_State *L) { create_meta(L, sqlite_meta, dblib); create_meta(L, sqlite_vm_meta, vmlib); create_meta(L, sqlite_ctx_meta, ctxlib); + create_meta(L, sqlite_ses_meta, seslib); luaL_getmetatable(L, sqlite_ctx_meta); sqlite_ctx_meta_ref = luaL_ref(L, LUA_REGISTRYINDEX); + luaL_getmetatable(L, sqlite_ses_meta); + sqlite_ses_meta_ref = luaL_ref(L, LUA_REGISTRYINDEX); + /* register (local) sqlite metatable */ luaL_register(L, "sqlite3", sqlitelib); diff --git a/tool/net/net.mk b/tool/net/net.mk index 4afa51bf1..b29a02bec 100644 --- a/tool/net/net.mk +++ b/tool/net/net.mk @@ -128,6 +128,10 @@ o/$(MODE)/tool/net/redbean.com: \ @$(MAKE_SYMTAB_ZIP) @$(TOOL_NET_REDBEAN_STANDARD_ASSETS_ZIP) +o/$(MODE)/tool/net/lsqlite3.o: private \ + OVERRIDE_CFLAGS += \ + -DSQLITE_ENABLE_SESSION + # REDBEAN-DEMO.COM # # This redbean-demo.com program is the same as redbean.com except it