Tarantool development patches archive
 help / color / mirror / Atom feed
From: Kirill Shcherbatov <kshcherbatov@tarantool.org>
To: tarantool-patches@freelists.org, vdavydov.dev@gmail.com
Cc: Kirill Shcherbatov <kshcherbatov@tarantool.org>
Subject: [PATCH v1 6/8] box: export _func functions with box.func folder
Date: Thu, 30 May 2019 13:45:33 +0300	[thread overview]
Message-ID: <928eff71554a492f736b8c7595f2a060f70364e5.1559212747.git.kshcherbatov@tarantool.org> (raw)
In-Reply-To: <cover.1559212747.git.kshcherbatov@tarantool.org>

Closes #4182
Needed for #1260

@TarantoolBot document
Title: Persistent Lua functions

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.

Moreover all functions and their metadata are now exported in
box.func table (not only persistent). Persistent Lua functions
have box.func.FUNCNAME.call() method to execute it in interactive
console.
Persistent Lua functions are also available with net.box call.

Example:
lua_code = [[function(a, b) return a + b end]]
box.schema.func.create('sum', {
                       body = lua_code, is_deterministic = true,
                       returns = 'integer', is_deterministic = true})
box.func.sum
- call: 'function: 0x4016b6a0'
  returns: integer
  is_deterministic: true
  id: 2
  language: LUA
  name: sum
  is_persistent: true
  setuid: false

box.func.sum.call(1, 2)
- 3

-- netbox connection call
conn:call("sum", {1, 3})
- 3
---
 src/box/alter.cc                  |   6 +-
 src/box/func.c                    | 119 ++++++++++++++++++++++++++++++
 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/misc.result              |   1 +
 test/box/persistent_func.result   |  22 ++++++
 test/box/persistent_func.test.lua |   5 ++
 10 files changed, 173 insertions(+), 6 deletions(-)

diff --git a/src/box/alter.cc b/src/box/alter.cc
index c9446bfe0..28644a667 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -2507,7 +2507,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);
 }
 
@@ -2520,6 +2522,7 @@ func_cache_replace_func(struct trigger *trigger, void * /* event */)
 	func_cache_replace(new_func, &old_func);
 	assert(old_func != NULL);
 	func_delete(old_func);
+	trigger_run_xc(&on_alter_func, (void *)(uintptr_t)new_func->def->fid);
 }
 
 /**
@@ -2552,6 +2555,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 31db7b477..b5b68daac 100644
--- a/src/box/func.c
+++ b/src/box/func.c
@@ -31,10 +31,12 @@
 #include "func.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>
 
 /**
@@ -536,3 +538,120 @@ 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, "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_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, "is_persistent");
+	lua_pushboolean(L, func_def_is_persistent(func->def));
+	lua_settable(L, top);
+
+	lua_pushstring(L, "setuid");
+	lua_pushboolean(L, func->def->setuid);
+	lua_settable(L, top);
+
+	lua_pushstring(L, "is_deterministic");
+	lua_pushboolean(L, func->def->is_deterministic);
+	lua_settable(L, top);
+
+	lua_pushstring(L, "language");
+	lua_pushstring(L, func_language_strs[func->def->language]);
+	lua_settable(L, top);
+
+	lua_pushstring(L, "returns");
+	lua_pushstring(L, field_type_strs[func->def->returns]);
+	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, 2); /* box, func */
+}
+
+
+static void
+box_lua_func_delete(struct lua_State *L, uint32_t fid)
+{
+	lua_getfield(L, LUA_GLOBALSINDEX, "box");
+	lua_getfield(L, -1, "func");
+	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, 2); /* box, func */
+}
+
+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)(uintptr_t)event;
+	struct func *func = func_by_id(fid);
+	/* Export only persistent Lua functions. */
+	if (func != 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 7c3e81c51..3dfabbf40 100644
--- a/src/box/func.h
+++ b/src/box/func.h
@@ -136,6 +136,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 Lua export trigger for _func objects. */
+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 960ea0fc7..2599a24a3 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -2101,6 +2101,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 b70992297..7f6086210 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 ffc41f401..78d48d3da 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/misc.result b/test/box/misc.result
index 4fcd13a78..ee51283bb 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -65,6 +65,7 @@ t
   - error
   - execute
   - feedback
+  - func
   - index
   - info
   - internal
diff --git a/test/box/persistent_func.result b/test/box/persistent_func.result
index 1b3eaa8b2..c18be4a3b 100644
--- a/test/box/persistent_func.result
+++ b/test/box/persistent_func.result
@@ -43,6 +43,15 @@ box.schema.func.exists('test')
 ---
 - true
 ...
+box.func.test ~= nil
+---
+- true
+...
+box.func.test.call({address = "Moscow Dolgoprudny"})
+---
+- - - moscow
+  - - dolgoprudny
+...
 conn:call("test", {{address = "Moscow Dolgoprudny"}})
 ---
 - [['moscow'], ['dolgoprudny']]
@@ -147,6 +156,15 @@ test_run = require('test_run').new()
 conn = net.connect(box.cfg.listen)
 ---
 ...
+box.func.test ~= nil
+---
+- true
+...
+box.func.test.call({address = "Moscow Dolgoprudny"})
+---
+- - - moscow
+  - - dolgoprudny
+...
 conn:call("test", {{address = "Moscow Dolgoprudny"}})
 ---
 - [['moscow'], ['dolgoprudny']]
@@ -161,6 +179,10 @@ box.schema.func.exists('test')
 box.schema.func.drop('test')
 ---
 ...
+box.func.test ~= nil
+---
+- false
+...
 box.schema.func.exists('test')
 ---
 - false
diff --git a/test/box/persistent_func.test.lua b/test/box/persistent_func.test.lua
index 363d687ce..048afa815 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', {body = body, language = "C"})
 box.schema.func.create('test', {body = body})
 box.schema.func.exists('test')
+box.func.test ~= nil
+box.func.test.call({address = "Moscow Dolgoprudny"})
 conn:call("test", {{address = "Moscow Dolgoprudny"}})
 box.schema.func.create('test2', {body = body, is_deterministic = true})
 
@@ -72,10 +74,13 @@ test_run:cmd("restart server default")
 net = require('net.box')
 test_run = require('test_run').new()
 conn = net.connect(box.cfg.listen)
+box.func.test ~= nil
+box.func.test.call({address = "Moscow Dolgoprudny"})
 conn:call("test", {{address = "Moscow Dolgoprudny"}})
 conn:close()
 box.schema.func.exists('test')
 box.schema.func.drop('test')
+box.func.test ~= nil
 box.schema.func.exists('test')
 box.schema.func.drop('body_monkey')
 box.schema.func.drop('json_serializer')
-- 
2.21.0

  parent reply	other threads:[~2019-05-30 10:45 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-05-30 10:45 [PATCH v1 0/8] box: functional indexes Kirill Shcherbatov
2019-05-30 10:45 ` [PATCH v1 1/8] box: refactor box_lua_find helper Kirill Shcherbatov
2019-05-30 10:45 ` [PATCH v1 2/8] box: rework func cache update machinery Kirill Shcherbatov
2019-05-30 10:45 ` [PATCH v1 3/8] schema: rework _func system space format Kirill Shcherbatov
2019-05-30 12:06   ` [tarantool-patches] " Konstantin Osipov
2019-06-03 16:14     ` Vladimir Davydov
2019-05-30 13:10   ` Vladislav Shpilevoy
2019-05-30 10:45 ` [PATCH v1 4/8] box: load persistent Lua functions on creation Kirill Shcherbatov
2019-05-31  8:16   ` [tarantool-patches] " Konstantin Osipov
2019-06-03  8:26     ` [tarantool-patches] " Kirill Shcherbatov
2019-05-30 10:45 ` [PATCH v1 5/8] netbox: call persistent functions in netbox Kirill Shcherbatov
2019-05-30 10:45 ` Kirill Shcherbatov [this message]
2019-06-03 16:22   ` [PATCH v1 6/8] box: export _func functions with box.func folder Vladimir Davydov
2019-06-03 16:24   ` Vladimir Davydov
2019-05-30 10:45 ` [PATCH v1 7/8] box: introduce memtx_slab_alloc helper Kirill Shcherbatov
2019-05-30 10:45 ` [PATCH v1 8/8] box: introduce functional indexes in memtx Kirill Shcherbatov
2019-05-30 11:18 ` [tarantool-patches] [PATCH v1 0/8] box: functional indexes Vladislav Shpilevoy
2019-05-30 11:43   ` [tarantool-patches] " Kirill Shcherbatov
2019-05-30 11:47 ` [tarantool-patches] " Konstantin Osipov
2019-06-03 15:55 ` Vladimir Davydov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=928eff71554a492f736b8c7595f2a060f70364e5.1559212747.git.kshcherbatov@tarantool.org \
    --to=kshcherbatov@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --cc=vdavydov.dev@gmail.com \
    --subject='Re: [PATCH v1 6/8] box: export _func functions with box.func folder' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox