[PATCH v1 3/3] box: extend box.schema.func with persistent folder

Kirill Shcherbatov kshcherbatov at tarantool.org
Tue May 14 16:29:22 MSK 2019


Now all persistent Lua functions are available with
box.schema.func.persistent table.

@TarantoolBot document
Title: Persistent Lua functions in core

Lua functions managed by Tarantool are called 'persistent'.
They are stored in snapshoot and are available after server
restart.
Persistent Lua functions are exported in box.schema.func.persistent
folder. They may be called via .call exported object method or
via netbox:call.

Some restrictions must be accounted writing such routines:
1. only limited set of Lua functions and modules are available:
-assert -error -pairs -ipairs -next -pcall -xpcall -type
-print -select -string -tonumber -tostring -unpack -math -utf8;
2. global variables are forbidden.

Example:
lua_code = [[function(a, b) return a + b end]]
box.schema.func.create('sum', {lua_code = lua_code})
box.schema.func.persistent.sum
---
- call: 'function: 0x4014b7f8'
  name: sum
  id: 2
...
box.schema.func.persistent.sum.call(1, 2)
---
- 3
...

Closes #4182
Needed for #1260
---
 src/box/alter.cc                  |   6 +-
 src/box/func.c                    | 116 ++++++++++++++++++++++++++++++
 src/box/func.h                    |   6 ++
 src/box/lua/init.c                |   2 +
 src/box/lua/schema.lua            |   1 +
 src/box/schema.cc                 |   1 +
 src/box/schema.h                  |  16 +++--
 test/box/persistent_func.result   |  22 ++++++
 test/box/persistent_func.test.lua |   5 ++
 9 files changed, 169 insertions(+), 6 deletions(-)

diff --git a/src/box/alter.cc b/src/box/alter.cc
index 0d23dbf0e..ee944163c 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -2467,7 +2467,9 @@ static void
 func_cache_remove_func(struct trigger *trigger, void * /* event */)
 {
 	struct func *old_func = (struct func *) trigger->data;
-	func_cache_delete(old_func->def->fid);
+	uint32_t fid = old_func->def->fid;
+	func_cache_delete(fid);
+	trigger_run_xc(&on_alter_func, (void *)(uintptr_t)fid);
 	func_delete(old_func);
 }
 
@@ -2479,6 +2481,7 @@ func_cache_replace_func(struct trigger *trigger, void * /* event */)
 	struct func *old_func;
 	func_cache_replace(new_func, &old_func);
 	func_delete(old_func);
+	trigger_run_xc(&on_alter_func, (void *)(uintptr_t)new_func->def->fid);
 }
 
 /**
@@ -2511,6 +2514,7 @@ on_replace_dd_func(struct trigger * /* trigger */, void *event)
 		func_cache_replace(func, &old_func);
 		assert(old_func == NULL);
 		func_guard.is_active = false;
+		trigger_run_xc(&on_alter_func, (void *)(uintptr_t)fid);
 		struct trigger *on_rollback =
 			txn_alter_trigger_new(func_cache_remove_func, func);
 		txn_on_rollback(txn, on_rollback);
diff --git a/src/box/func.c b/src/box/func.c
index b68c089ad..ee9edebb1 100644
--- a/src/box/func.c
+++ b/src/box/func.c
@@ -32,10 +32,12 @@
 #include "box/lua/util.h"
 #include "trivia/config.h"
 #include "assoc.h"
+#include "lua/trigger.h"
 #include "lua/utils.h"
 #include "error.h"
 #include "diag.h"
 #include "fiber.h"
+#include "schema.h"
 #include <dlfcn.h>
 
 /**
@@ -563,3 +565,117 @@ func_capture_module(struct func *new_func, struct func *old_func)
 	old_func->module = NULL;
 	old_func->func = NULL;
 }
+
+static void
+box_lua_func_new(struct lua_State *L, struct func *func)
+{
+	lua_getfield(L, LUA_GLOBALSINDEX, "box");
+	lua_getfield(L, -1, "schema");
+	if (!lua_istable(L, -1)) {
+		lua_pop(L, 1); /* pop nil */
+		lua_newtable(L);
+		lua_setfield(L, -2, "schema");
+		lua_getfield(L, -1, "schema");
+	}
+	lua_getfield(L, -1, "func");
+	if (!lua_istable(L, -1)) {
+		lua_pop(L, 1); /* pop nil */
+		lua_newtable(L);
+		lua_setfield(L, -2, "func");
+		lua_getfield(L, -1, "func");
+	}
+	lua_getfield(L, -1, "persistent");
+	if (!lua_istable(L, -1)) {
+		lua_pop(L, 1); /* pop nil */
+		lua_newtable(L);
+		lua_setfield(L, -2, "persistent");
+		lua_getfield(L, -1, "persistent");
+	}
+	lua_rawgeti(L, -1, func->def->fid);
+	if (lua_isnil(L, -1)) {
+		/*
+		 * If the function already exists, modify it,
+		 * rather than create a new one -- to not
+		 * invalidate Lua variable references to old func
+		 * outside the box.schema.func[].
+		 */
+		lua_pop(L, 1);
+		lua_newtable(L);
+		lua_rawseti(L, -2, func->def->fid);
+		lua_rawgeti(L, -1, func->def->fid);
+	} else {
+		/* Clear the reference to old space by old name. */
+		lua_getfield(L, -1, "name");
+		lua_pushnil(L);
+		lua_settable(L, -4);
+	}
+
+	int top = lua_gettop(L);
+	lua_pushstring(L, "id");
+	lua_pushnumber(L, func->def->fid);
+	lua_settable(L, top);
+
+	lua_pushstring(L, "name");
+	lua_pushstring(L, func->def->name);
+	lua_settable(L, top);
+
+	lua_pushstring(L, "call");
+	lua_rawgeti(L, LUA_REGISTRYINDEX, func->lua_func_ref);
+	lua_settable(L, top);
+
+	lua_setfield(L, -2, func->def->name);
+
+	lua_pop(L, 4); /* box, schema, func, persistent */
+}
+
+
+static void
+box_lua_func_delete(struct lua_State *L, uint32_t fid)
+{
+	lua_getfield(L, LUA_GLOBALSINDEX, "box");
+	lua_getfield(L, -1, "schema");
+	lua_getfield(L, -1, "func");
+	lua_getfield(L, -1, "persistent");
+	lua_rawgeti(L, -1, fid);
+	if (!lua_isnil(L, -1)) {
+		lua_getfield(L, -1, "name");
+		lua_pushnil(L);
+		lua_rawset(L, -4);
+		lua_pop(L, 1); /* pop func */
+
+		lua_pushnil(L);
+		lua_rawseti(L, -2, fid);
+	} else {
+		lua_pop(L, 1);
+	}
+	lua_pop(L, 4); /* box, schema, func, persistent */
+}
+
+static void
+box_lua_func_new_or_delete(struct trigger *trigger, void *event)
+{
+	struct lua_State *L = (struct lua_State *) trigger->data;
+	uint32_t fid = (uint32_t) event;
+	struct func *func = func_by_id(fid);
+	/* Export only persistent Lua functions. */
+	if (func != NULL && func->def->language == FUNC_LANGUAGE_LUA &&
+	    func->def->lua_code != NULL)
+		box_lua_func_new(L, func);
+	else
+		box_lua_func_delete(L, fid);
+}
+
+static struct trigger on_alter_func_in_lua = {
+	RLIST_LINK_INITIALIZER, box_lua_func_new_or_delete, NULL, NULL
+};
+
+void
+box_lua_func_init(struct lua_State *L)
+{
+	/*
+	 * Register the trigger that will push persistent
+	 * Lua functions objects to Lua.
+	 */
+	on_alter_func_in_lua.data = L;
+	trigger_add(&on_alter_func, &on_alter_func_in_lua);
+}
diff --git a/src/box/func.h b/src/box/func.h
index b00ed5690..21c1087cf 100644
--- a/src/box/func.h
+++ b/src/box/func.h
@@ -134,6 +134,12 @@ func_capture_module(struct func *new_func, struct func *old_func);
 int
 module_reload(const char *package, const char *package_end, struct module **module);
 
+struct lua_State;
+
+/** Initialize func export triggers. */
+void
+box_lua_func_init(struct lua_State *L);
+
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
diff --git a/src/box/lua/init.c b/src/box/lua/init.c
index 76b987b4b..67a822eb4 100644
--- a/src/box/lua/init.c
+++ b/src/box/lua/init.c
@@ -39,6 +39,7 @@
 
 #include "box/box.h"
 #include "box/txn.h"
+#include "box/func.h"
 #include "box/vclock.h"
 
 #include "box/lua/error.h"
@@ -315,6 +316,7 @@ box_lua_init(struct lua_State *L)
 	box_lua_xlog_init(L);
 	box_lua_execute_init(L);
 	luaopen_net_box(L);
+	box_lua_func_init(L);
 	lua_pop(L, 1);
 	tarantool_lua_console_init(L);
 	lua_pop(L, 1);
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index ca7da6501..94eb6af90 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1977,6 +1977,7 @@ local function object_name(object_type, object_id)
 end
 
 box.schema.func = {}
+box.schema.func.persistent = {}
 box.schema.func.create = function(name, opts)
     opts = opts or {}
     check_param_table(opts, { setuid = 'boolean',
diff --git a/src/box/schema.cc b/src/box/schema.cc
index aae92874c..97c994c26 100644
--- a/src/box/schema.cc
+++ b/src/box/schema.cc
@@ -72,6 +72,7 @@ uint32_t space_cache_version = 0;
 struct rlist on_schema_init = RLIST_HEAD_INITIALIZER(on_schema_init);
 struct rlist on_alter_space = RLIST_HEAD_INITIALIZER(on_alter_space);
 struct rlist on_alter_sequence = RLIST_HEAD_INITIALIZER(on_alter_sequence);
+struct rlist on_alter_func = RLIST_HEAD_INITIALIZER(on_alter_func);
 
 /**
  * Lock of scheme modification
diff --git a/src/box/schema.h b/src/box/schema.h
index bc15c8a2c..3a3daffcf 100644
--- a/src/box/schema.h
+++ b/src/box/schema.h
@@ -130,6 +130,11 @@ int
 schema_find_id(uint32_t system_space_id, uint32_t index_id, const char *name,
 	       uint32_t len, uint32_t *object_id);
 
+struct func;
+
+struct func *
+func_by_id(uint32_t fid);
+
 #if defined(__cplusplus)
 } /* extern "C" */
 
@@ -178,11 +183,6 @@ func_cache_replace(struct func *new_func, struct func **old_func);
 void
 func_cache_delete(uint32_t fid);
 
-struct func;
-
-struct func *
-func_by_id(uint32_t fid);
-
 static inline struct func *
 func_cache_find(uint32_t fid)
 {
@@ -243,6 +243,12 @@ extern struct rlist on_alter_sequence;
  */
 extern struct rlist on_access_denied;
 
+/**
+ * Triggers fired after committing a change in _func space.
+ * It is passed the txn statement that altered the space.
+ */
+extern struct rlist on_alter_func;
+
 /**
  * Context passed to on_access_denied trigger.
  */
diff --git a/test/box/persistent_func.result b/test/box/persistent_func.result
index ba544394e..5455b7eb4 100644
--- a/test/box/persistent_func.result
+++ b/test/box/persistent_func.result
@@ -43,6 +43,15 @@ box.schema.func.exists('test')
 ---
 - true
 ...
+box.schema.func.persistent.test ~= nil
+---
+- true
+...
+box.schema.func.persistent.test.call({address = "Moscow Dolgoprudny"})
+---
+- - - moscow
+  - - dolgoprudny
+...
 conn:call("test", {{address = "Moscow Dolgoprudny"}})
 ---
 - [['moscow'], ['dolgoprudny']]
@@ -144,6 +153,15 @@ test_run = require('test_run').new()
 conn = net.connect(box.cfg.listen)
 ---
 ...
+box.schema.func.persistent.test ~= nil
+---
+- true
+...
+box.schema.func.persistent.test.call({address = "Moscow Dolgoprudny"})
+---
+- - - moscow
+  - - dolgoprudny
+...
 conn:call("test", {{address = "Moscow Dolgoprudny"}})
 ---
 - [['moscow'], ['dolgoprudny']]
@@ -154,6 +172,10 @@ conn:close()
 box.schema.func.drop('test')
 ---
 ...
+box.schema.func.persistent.test == nil
+---
+- true
+...
 box.schema.func.exists('test')
 ---
 - false
diff --git a/test/box/persistent_func.test.lua b/test/box/persistent_func.test.lua
index 67a315197..e87b366a9 100644
--- a/test/box/persistent_func.test.lua
+++ b/test/box/persistent_func.test.lua
@@ -21,6 +21,8 @@ test_run:cmd("setopt delimiter ''");
 box.schema.func.create('test', {lua_code = lua_code, language = "C"})
 box.schema.func.create('test', {lua_code = lua_code})
 box.schema.func.exists('test')
+box.schema.func.persistent.test ~= nil
+box.schema.func.persistent.test.call({address = "Moscow Dolgoprudny"})
 conn:call("test", {{address = "Moscow Dolgoprudny"}})
 
 -- Test that monkey-patch attack is not possible.
@@ -71,8 +73,11 @@ test_run:cmd("restart server default")
 net = require('net.box')
 test_run = require('test_run').new()
 conn = net.connect(box.cfg.listen)
+box.schema.func.persistent.test ~= nil
+box.schema.func.persistent.test.call({address = "Moscow Dolgoprudny"})
 conn:call("test", {{address = "Moscow Dolgoprudny"}})
 conn:close()
 box.schema.func.drop('test')
+box.schema.func.persistent.test == nil
 box.schema.func.exists('test')
 box.schema.user.revoke('guest', 'execute', 'universe')
-- 
2.21.0




More information about the Tarantool-patches mailing list