From a06a9cff25fc241487a592a3116b408a9f7ad954 Mon Sep 17 00:00:00 2001 From: Paul Kulchenko Date: Wed, 5 Oct 2022 22:36:59 -0700 Subject: [PATCH] Add apply_changeset to SQLite Lua API --- tool/net/lsqlite3.c | 92 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 80 insertions(+), 12 deletions(-) diff --git a/tool/net/lsqlite3.c b/tool/net/lsqlite3.c index 886439c22..fd94138f2 100644 --- a/tool/net/lsqlite3.c +++ b/tool/net/lsqlite3.c @@ -1789,17 +1789,6 @@ static int lsession_changeset(lua_State *L) { 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) { @@ -1813,6 +1802,17 @@ static int lsession_gc(lua_State *L) { return lsession_delete(L); } +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 db_create_session(lua_State *L) { sdb *db = lsqlite_checkdb(L, 1); const char *zDb = luaL_optstring(L, 2, "main"); @@ -1828,6 +1828,73 @@ static int db_create_session(lua_State *L) { return 1; } +static int changeset_conflict_cb = LUA_NOREF; +static int changeset_conflict_udata = LUA_NOREF; + +static int db_changeset_conflict_callback( + void *user, /* Copy of sixth arg to _apply_v2() */ + int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */ + sqlite3_changeset_iter *p /* Handle describing change and conflict */ +) { + // return REPLACE code if no conflict callback is provided + if (changeset_conflict_cb == LUA_NOREF) return SQLITE_CHANGESET_REPLACE; + sdb *db = (sdb*)user; + lua_State *L = db->L; + int top = lua_gettop(L); + int result, isint; + + lua_rawgeti(L, LUA_REGISTRYINDEX, changeset_conflict_cb); /* get callback */ + lua_rawgeti(L, LUA_REGISTRYINDEX, changeset_conflict_udata); /* get callback user data */ + lua_pushinteger(L, eConflict); + + if (lua_pcall(L, 2, 1, 0) != LUA_OK) return lua_error(L); + + result = lua_tointegerx(L, -1, &isint); /* use result if there was no error */ + if (!isint) { + lua_pushliteral(L, "non-integer returned from conflict callback"); + return lua_error(L); + } + + lua_settop(L, top); + return result; +} + +static int db_apply_changeset(lua_State *L) { + sdb *db = lsqlite_checkdb(L, 1); + const char *cset = luaL_checkstring(L, 2); + int nset = lua_rawlen(L, 2); + int rc; + + // TBD: does this *also* need to be done in cleanupvm? + if (changeset_conflict_cb != LUA_NOREF) { + luaL_unref(L, LUA_REGISTRYINDEX, changeset_conflict_cb); + luaL_unref(L, LUA_REGISTRYINDEX, changeset_conflict_udata); + + changeset_conflict_cb = + changeset_conflict_udata = LUA_NOREF; + } + + if (lua_gettop(L) >= 3 && !lua_isnil(L, 3)) { + luaL_checktype(L, 3, LUA_TFUNCTION); + lua_settop(L, 4); + changeset_conflict_udata = luaL_ref(L, LUA_REGISTRYINDEX); + changeset_conflict_cb = luaL_ref(L, LUA_REGISTRYINDEX); + } + + rc = sqlite3changeset_apply_v2(db->db, nset, cset, NULL, + db_changeset_conflict_callback, + db, 0, 0, 0); + + if (rc != SQLITE_OK) { + lua_pushnil(L); + lua_pushinteger(L, sqlite3_errcode(db->db)); + return 2; + } + + lua_pushboolean(L, 1); + return 1; +} + /* ** ======================================================= ** General library functions @@ -2014,6 +2081,7 @@ static const luaL_Reg dblib[] = { {"deserialize", db_deserialize }, {"create_session", db_create_session }, + {"apply_changeset", db_apply_changeset }, {"__tostring", db_tostring }, {"__gc", db_gc }, @@ -2095,7 +2163,7 @@ static const luaL_Reg seslib[] = { {"delete", lsession_delete }, {"__tostring", lsession_tostring }, - {"__gc", lsession_gc }, + {"__gc", lsession_gc }, {NULL, NULL} };