Update lsqlite3 to add update hook support for redbean (#383)

This commit is contained in:
Paul Kulchenko 2022-04-09 12:36:44 -07:00 committed by GitHub
parent 668dc42bac
commit 183b3ed6a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -89,6 +89,15 @@ struct sdb {
/* references */ /* references */
int busy_cb; /* busy callback */ int busy_cb; /* busy callback */
int busy_udata; int busy_udata;
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;
}; };
static const char *const sqlite_meta = ":sqlite3"; static const char *const sqlite_meta = ":sqlite3";
@ -570,6 +579,12 @@ static sdb *newdb (lua_State *L) {
db->busy_cb = db->busy_cb =
db->busy_udata = db->busy_udata =
db->update_hook_cb =
db->update_hook_udata =
db->commit_hook_cb =
db->commit_hook_udata =
db->rollback_hook_cb =
db->rollback_hook_udata =
LUA_NOREF; LUA_NOREF;
luaL_getmetatable(L, sqlite_meta); luaL_getmetatable(L, sqlite_meta);
@ -614,6 +629,12 @@ static int cleanupdb(lua_State *L, sdb *db) {
/* 'free' all references */ /* 'free' all references */
luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb); luaL_unref(L, LUA_REGISTRYINDEX, db->busy_cb);
luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata); luaL_unref(L, LUA_REGISTRYINDEX, db->busy_udata);
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);
/* close database */ /* close database */
result = sqlite3_close(db->db); result = sqlite3_close(db->db);
@ -1130,6 +1151,182 @@ static int db_create_collation(lua_State *L) {
return 0; return 0;
} }
/*
** 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;
}
/* /*
** busy handler: ** busy handler:
** Params: database, callback function, userdata ** Params: database, callback function, userdata
@ -1640,6 +1837,9 @@ static const luaL_Reg dblib[] = {
{"busy_timeout", db_busy_timeout }, {"busy_timeout", db_busy_timeout },
{"busy_handler", db_busy_handler }, {"busy_handler", db_busy_handler },
{"update_hook", db_update_hook },
{"commit_hook", db_commit_hook },
{"rollback_hook", db_rollback_hook },
{"prepare", db_prepare }, {"prepare", db_prepare },
{"rows", db_rows }, {"rows", db_rows },