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 v2 4/9] box: rework func object as a function frontend
Date: Thu,  6 Jun 2019 15:04:00 +0300	[thread overview]
Message-ID: <8902e130a9cd1ac295391fc9cd315ad4cfa2bafa.1559822429.git.kshcherbatov@tarantool.org> (raw)
In-Reply-To: <cover.1559822429.git.kshcherbatov@tarantool.org>

The function func object used to provide a call method only for
C functions. In scope of this patch it reworked to be a uniform
function call frontend both for C and Lua functions.

Needed for #4182, #1260
---
 src/box/call.c     | 136 +-----------------
 src/box/call.h     |   4 -
 src/box/func.c     | 337 ++++++++++++++++++++++++++++++++++++++++-----
 src/box/func.h     |  29 +++-
 src/box/lua/call.c |  39 ------
 5 files changed, 332 insertions(+), 213 deletions(-)

diff --git a/src/box/call.c b/src/box/call.c
index 982c95cbf..3e64118c9 100644
--- a/src/box/call.c
+++ b/src/box/call.c
@@ -43,149 +43,17 @@
 #include "small/obuf.h"
 #include "tt_static.h"
 
-/**
- * Find a function by name and check "EXECUTE" permissions.
- *
- * @param name function name
- * @param name_len length of @a name
- * @param[out] funcp function object
- * Sic: *pfunc == NULL means that perhaps the user has a global
- * "EXECUTE" privilege, so no specific grant to a function.
- *
- * @retval -1 on access denied
- * @retval  0 on success
- */
-static inline int
-access_check_func(const char *name, uint32_t name_len, struct func **funcp)
-{
-	struct func *func = func_by_name(name, name_len);
-	struct credentials *credentials = effective_user();
-	/*
-	 * If the user has universal access, don't bother with checks.
-	 * No special check for ADMIN user is necessary
-	 * since ADMIN has universal access.
-	 */
-	if ((credentials->universal_access & (PRIV_X | PRIV_U)) ==
-	    (PRIV_X | PRIV_U)) {
-
-		*funcp = func;
-		return 0;
-	}
-	user_access_t access = PRIV_X | PRIV_U;
-	/* Check access for all functions. */
-	access &= ~entity_access_get(SC_FUNCTION)[credentials->auth_token].effective;
-	user_access_t func_access = access & ~credentials->universal_access;
-	if (func == NULL ||
-	    /* Check for missing Usage access, ignore owner rights. */
-	    func_access & PRIV_U ||
-	    /* Check for missing specific access, respect owner rights. */
-	    (func->def->uid != credentials->uid &&
-	    func_access & ~func->access[credentials->auth_token].effective)) {
-
-		/* Access violation, report error. */
-		struct user *user = user_find(credentials->uid);
-		if (user != NULL) {
-			if (!(access & credentials->universal_access)) {
-				diag_set(AccessDeniedError,
-					 priv_name(PRIV_U),
-					 schema_object_name(SC_UNIVERSE), "",
-					 user->def->name);
-			} else {
-				diag_set(AccessDeniedError,
-					 priv_name(PRIV_X),
-					 schema_object_name(SC_FUNCTION),
-					 tt_cstr(name, name_len),
-					 user->def->name);
-			}
-		}
-		return -1;
-	}
-
-	*funcp = func;
-	return 0;
-}
-
-static int
-box_c_call(struct func *func, struct call_request *request, struct port *port)
-{
-	assert(func != NULL && func->def->language == FUNC_LANGUAGE_C);
-
-	/* Create a call context */
-	port_tuple_create(port);
-	box_function_ctx_t ctx = { port };
-
-	/* Clear all previous errors */
-	diag_clear(&fiber()->diag);
-	assert(!in_txn()); /* transaction is not started */
-
-	/* Call function from the shared library */
-	int rc = func_call(func, &ctx, request->args, request->args_end);
-	func = NULL; /* May be deleted by DDL */
-	if (rc != 0) {
-		if (diag_last_error(&fiber()->diag) == NULL) {
-			/* Stored procedure forget to set diag  */
-			diag_set(ClientError, ER_PROC_C, "unknown error");
-		}
-		port_destroy(port);
-		return -1;
-	}
-	return 0;
-}
-
 int
 box_process_call(struct call_request *request, struct port *port)
 {
 	rmean_collect(rmean_box, IPROTO_CALL, 1);
-	/**
-	 * Find the function definition and check access.
-	 */
 	const char *name = request->name;
 	assert(name != NULL);
 	uint32_t name_len = mp_decode_strl(&name);
 
-	struct func *func = NULL;
-	/**
-	 * Sic: func == NULL means that perhaps the user has a global
-	 * "EXECUTE" privilege, so no specific grant to a function.
-	 */
-	if (access_check_func(name, name_len, &func) != 0)
-		return -1; /* permission denied */
-
-	/**
-	 * Change the current user id if the function is
-	 * a set-definer-uid one. If the function is not
-	 * defined, it's obviously not a setuid one.
-	 */
-	struct credentials *orig_credentials = NULL;
-	if (func && func->def->setuid) {
-		orig_credentials = effective_user();
-		/* Remember and change the current user id. */
-		if (func->owner_credentials.auth_token >= BOX_USER_MAX) {
-			/*
-			 * Fill the cache upon first access, since
-			 * when func is created, no user may
-			 * be around to fill it (recovery of
-			 * system spaces from a snapshot).
-			 */
-			struct user *owner = user_find(func->def->uid);
-			if (owner == NULL)
-				return -1;
-			credentials_init(&func->owner_credentials,
-					 owner->auth_token,
-					 owner->def->uid);
-		}
-		fiber_set_user(fiber(), &func->owner_credentials);
-	}
-
 	int rc;
-	if (func && func->def->language == FUNC_LANGUAGE_C) {
-		rc = box_c_call(func, request, port);
-	} else {
-		rc = box_lua_call(request, port);
-	}
-	/* Restore the original user */
-	if (orig_credentials)
-		fiber_set_user(fiber(), orig_credentials);
+	rc = box_func_execute_by_name(name, name_len, port, request->args,
+				      request->args_end);
 
 	if (rc != 0) {
 		txn_rollback();
diff --git a/src/box/call.h b/src/box/call.h
index 4b3b8614c..05032c0d8 100644
--- a/src/box/call.h
+++ b/src/box/call.h
@@ -38,10 +38,6 @@ extern "C" {
 struct port;
 struct call_request;
 
-struct box_function_ctx {
-	struct port *port;
-};
-
 int
 box_process_call(struct call_request *request, struct port *port);
 
diff --git a/src/box/func.c b/src/box/func.c
index 5051286a3..71c6bb6eb 100644
--- a/src/box/func.c
+++ b/src/box/func.c
@@ -32,7 +32,12 @@
 #include "trivia/config.h"
 #include "assoc.h"
 #include "session.h"
+#include "lua/msgpack.h"
 #include "lua/utils.h"
+#include "schema.h"
+#include "txn.h"
+#include "tt_static.h"
+#include "port.h"
 #include "error.h"
 #include "diag.h"
 #include <dlfcn.h>
@@ -326,8 +331,8 @@ module_reload(const char *package, const char *package_end, struct module **modu
 	rlist_foreach_entry_safe(func, &old_module->funcs, item, tmp_func) {
 		struct func_name name;
 		func_split_name(func->def->name, &name);
-		func->func = module_sym(new_module, name.sym);
-		if (func->func == NULL)
+		func->c_func = module_sym(new_module, name.sym);
+		if (func->c_func == NULL)
 			goto restore;
 		func->module = new_module;
 		rlist_move(&new_module->funcs, &func->item);
@@ -347,8 +352,8 @@ restore:
 	do {
 		struct func_name name;
 		func_split_name(func->def->name, &name);
-		func->func = module_sym(old_module, name.sym);
-		if (func->func == NULL) {
+		func->c_func = module_sym(old_module, name.sym);
+		if (func->c_func == NULL) {
 			/*
 			 * Something strange was happen, an early loaden
 			 * function was not found in an old dso.
@@ -378,6 +383,25 @@ box_module_reload(const char *name)
 	return -1;
 }
 
+/** Private virtual method table for func object. */
+struct func_vtab {
+	/** Load function runtime environment. */
+	int (*load)(struct func *func);
+	/** Unload function runtime environment. */
+	void (*unload)(struct func *func);
+	/** Call function with given arguments. */
+	int (*call)(struct func *func, struct port *port, const char *args,
+		    const char *args_end);
+	/**
+	 * Capture a given old_func's runtime and reuse in a given
+	 * new function if possible.
+	 */
+	void (*reuse_runtime)(struct func *new_func, struct func *old_func);
+};
+
+static struct func_vtab func_c_vtab;
+static struct func_vtab func_lua_vtab;
+
 struct func *
 func_new(struct func_def *def)
 {
@@ -401,15 +425,44 @@ func_new(struct func_def *def)
 	 * checks (see user_has_data()).
 	 */
 	func->owner_credentials.auth_token = BOX_USER_MAX; /* invalid value */
-	func->func = NULL;
-	func->module = NULL;
+	if (def->language == FUNC_LANGUAGE_C) {
+		func->c_func = NULL;
+		func->module = NULL;
+		func->vtab = &func_c_vtab;
+		/*
+		 * Due to the fact that the function load cause
+		 * loading of a shared library, this action is
+		 * performed on demand: so we guarantee that it is
+		 * performed by the user who has the authority
+		 * to do it.
+		 */
+	} else {
+		assert(def->language == FUNC_LANGUAGE_LUA);
+		func->vtab = &func_lua_vtab;
+	}
 	return func;
 }
 
+void
+func_delete(struct func *func)
+{
+	func->vtab->unload(func);
+	free(func->def);
+	free(func);
+}
+
+void
+func_reuse_runtime(struct func *new_func, struct func *old_func)
+{
+	new_func->vtab->reuse_runtime(new_func, old_func);
+}
+
 static void
-func_unload(struct func *func)
+func_c_unload(struct func *func)
 {
-	if (func->module) {
+	assert(func->vtab == &func_c_vtab);
+	assert(func != NULL && func->def->language == FUNC_LANGUAGE_C);
+	if (func->module != NULL) {
 		rlist_del(&func->item);
 		if (rlist_empty(&func->module->funcs)) {
 			struct func_name name;
@@ -418,8 +471,8 @@ func_unload(struct func *func)
 		}
 		module_gc(func->module);
 	}
+	func->c_func = NULL;
 	func->module = NULL;
-	func->func = NULL;
 }
 
 /**
@@ -427,9 +480,10 @@ func_unload(struct func *func)
  * symbol from it).
  */
 static int
-func_load(struct func *func)
+func_c_load(struct func *func)
 {
-	assert(func->func == NULL);
+	assert(func != NULL && func->def->language == FUNC_LANGUAGE_C);
+	assert(func->vtab == &func_c_vtab);
 
 	struct func_name name;
 	func_split_name(func->def->name, &name);
@@ -447,46 +501,265 @@ func_load(struct func *func)
 		}
 	}
 
-	func->func = module_sym(module, name.sym);
-	if (func->func == NULL)
+	func->c_func = module_sym(module, name.sym);
+	if (func->c_func == NULL)
 		return -1;
 	func->module = module;
 	rlist_add(&module->funcs, &func->item);
 	return 0;
 }
 
-int
-func_call(struct func *func, box_function_ctx_t *ctx, const char *args,
-	  const char *args_end)
+static int
+func_c_call(struct func *func, struct port *port, const char *args,
+	    const char *args_end)
 {
-	if (func->func == NULL) {
-		if (func_load(func) != 0)
-			return -1;
-	}
+	assert(func != NULL && func->def->language == FUNC_LANGUAGE_C);
+	assert(func->vtab == &func_c_vtab);
+	assert(!in_txn());
+
+	port_tuple_create(port);
+	if (func->c_func == NULL && func_c_load(func) != 0)
+		return -1;
 
 	/* Module can be changed after function reload. */
 	struct module *module = func->module;
 	assert(module != NULL);
 	++module->calls;
-	int rc = func->func(ctx, args, args_end);
+	box_function_ctx_t ctx = { port };
+	int rc = func->c_func(&ctx, args, args_end);
 	--module->calls;
 	module_gc(module);
+	if (rc != 0 && diag_last_error(&fiber()->diag) == NULL) {
+		/* Stored procedure forget to set diag  */
+		diag_set(ClientError, ER_PROC_C, "unknown error");
+	}
 	return rc;
 }
 
-void
-func_delete(struct func *func)
+static void
+func_c_reuse_runtime(struct func *new_func, struct func *old_func)
 {
-	func_unload(func);
-	free(func->def);
-	free(func);
-}
+	assert(new_func != NULL && new_func->def->language == FUNC_LANGUAGE_C);
+	assert(new_func->vtab == &func_c_vtab);
+	assert(old_func != NULL && old_func->def->language == FUNC_LANGUAGE_C);
+	assert(old_func->vtab == &func_c_vtab);
 
-void
-func_reuse_runtime(struct func *new_func, struct func *old_func)
-{
 	new_func->module = old_func->module;
-	new_func->func = old_func->func;
+	new_func->c_func = old_func->c_func;
 	old_func->module = NULL;
-	old_func->func = NULL;
+	old_func->c_func = NULL;
+}
+
+static struct func_vtab func_c_vtab = {
+	.load = func_c_load,
+	.unload = func_c_unload,
+	.call = func_c_call,
+	.reuse_runtime = func_c_reuse_runtime,
+};
+
+static void
+func_lua_unload(struct func *func)
+{
+	assert(func != NULL && func->def->language == FUNC_LANGUAGE_LUA);
+	assert(func->vtab == &func_lua_vtab);
+	(void) func;
+}
+
+struct func_lua_call_impl_ctx {
+	const char *args;
+	const char *args_end;
+	const char *func_name;
+	const char *func_name_end;
+};
+
+static int
+func_lua_call_impl_cb(struct lua_State *L)
+{
+	struct func_lua_call_impl_ctx *ctx =
+		(struct func_lua_call_impl_ctx *) lua_topointer(L, 1);
+	lua_settop(L, 0);
+
+	int oc = 0;
+	if (luaT_func_find(L, ctx->func_name, ctx->func_name_end, &oc) != 0) {
+		diag_set(ClientError, ER_NO_SUCH_PROC,
+			 ctx->func_name_end - ctx->func_name, ctx->func_name);
+		return luaT_error(L);
+	}
+
+	const char *args = ctx->args;
+	uint32_t arg_count = mp_decode_array(&args);
+	luaL_checkstack(L, arg_count, "call: out of stack");
+	for (uint32_t i = 0; i < arg_count; i++)
+		luamp_decode(L, luaL_msgpack_default, &args);
+	lua_call(L, arg_count + oc - 1, LUA_MULTRET);
+	return lua_gettop(L);
+}
+
+static inline int
+func_lua_call_impl(lua_CFunction handler, struct func_lua_call_impl_ctx *ctx,
+		   struct port *port)
+{
+	struct lua_State *L = lua_newthread(tarantool_L);
+	int coro_ref = luaL_ref(tarantool_L, LUA_REGISTRYINDEX);
+	port_lua_create(port, L);
+	((struct port_lua *) port)->ref = coro_ref;
+
+	lua_pushcfunction(L, handler);
+	lua_pushlightuserdata(L, ctx);
+	if (luaT_call(L, 1, LUA_MULTRET) != 0)
+		return -1;
+	return 0;
+}
+
+static int
+func_lua_load(struct func *func)
+{
+	assert(func != NULL && func->def->language == FUNC_LANGUAGE_LUA);
+	assert(func->vtab == &func_lua_vtab);
+	(void) func;
+	return 0;
+}
+
+static inline int
+func_lua_call(struct func *func, struct port *port, const char *args,
+	      const char *args_end)
+{
+	assert(func != NULL && func->def->language == FUNC_LANGUAGE_LUA);
+	assert(func->vtab == &func_lua_vtab);
+
+	struct func_lua_call_impl_ctx ctx;
+	ctx.func_name = func->def->name;
+	ctx.func_name_end = func->def->name + strlen(func->def->name);
+	ctx.args = args;
+	ctx.args_end = args_end;
+	return func_lua_call_impl(func_lua_call_impl_cb, &ctx, port);
+}
+
+static void
+func_lua_reuse_runtime(struct func *new_func, struct func *old_func)
+{
+	assert(new_func != NULL && new_func->def->language == FUNC_LANGUAGE_LUA);
+	assert(new_func->vtab == &func_c_vtab);
+	assert(old_func != NULL && old_func->def->language == FUNC_LANGUAGE_LUA);
+	assert(old_func->vtab == &func_c_vtab);
+	(void)new_func;
+	(void)old_func;
+}
+
+static struct func_vtab func_lua_vtab = {
+	.load = func_lua_load,
+	.unload = func_lua_unload,
+	.call = func_lua_call,
+	.reuse_runtime = func_lua_reuse_runtime,
+};
+
+/** Check "EXECUTE" permissions for a given function. */
+static int
+func_access_check(struct func *func, const char *func_name)
+{
+	struct credentials *credentials = effective_user();
+	/*
+	 * If the user has universal access, don't bother with checks.
+	 * No special check for ADMIN user is necessary
+	 * since ADMIN has universal access.
+	 */
+	if ((credentials->universal_access & (PRIV_X | PRIV_U)) ==
+	    (PRIV_X | PRIV_U))
+		return 0;
+	user_access_t access = PRIV_X | PRIV_U;
+	/* Check access for all functions. */
+	access &= ~entity_access_get(SC_FUNCTION)[credentials->auth_token].effective;
+	user_access_t func_access = access & ~credentials->universal_access;
+	if (func == NULL ||
+	    /* Check for missing Usage access, ignore owner rights. */
+	    func_access & PRIV_U ||
+	    /* Check for missing specific access, respect owner rights. */
+	    (func->def->uid != credentials->uid &&
+	    func_access & ~func->access[credentials->auth_token].effective)) {
+		/* Access violation, report error. */
+		struct user *user = user_find(credentials->uid);
+		if (user != NULL) {
+			if (!(access & credentials->universal_access)) {
+				diag_set(AccessDeniedError, priv_name(PRIV_U),
+					 schema_object_name(SC_UNIVERSE), "",
+					 user->def->name);
+			} else {
+				diag_set(AccessDeniedError, priv_name(PRIV_X),
+					 schema_object_name(SC_FUNCTION),
+					 func_name, user->def->name);
+			}
+		}
+		return -1;
+	}
+	return 0;
+}
+
+int
+func_execute(struct func *func, struct port *port, const char *args,
+	     const char *args_end)
+{
+	assert(func_access_check(func, func->def->name) == 0);
+	/**
+	 * Change the current user id if the function is
+	 * a set-definer-uid one. If the function is not
+	 * defined, it's obviously not a setuid one.
+	 */
+	struct credentials *orig_credentials = NULL;
+	if (func->def->setuid) {
+		orig_credentials = effective_user();
+		/* Remember and change the current user id. */
+		if (func->owner_credentials.auth_token >= BOX_USER_MAX) {
+			/*
+			 * Fill the cache upon first access, since
+			 * when func is created, no user may
+			 * be around to fill it (recovery of
+			 * system spaces from a snapshot).
+			 */
+			struct user *owner = user_find(func->def->uid);
+			if (owner == NULL)
+				return -1;
+			credentials_init(&func->owner_credentials,
+					 owner->auth_token,
+					 owner->def->uid);
+		}
+		fiber_set_user(fiber(), &func->owner_credentials);
+	}
+	int rc = func->vtab->call(func, port, args, args_end);
+	if (rc != 0)
+		port_destroy(port);
+	/* Restore the original user */
+	if (orig_credentials)
+		fiber_set_user(fiber(), orig_credentials);
+	return rc;
+}
+
+int
+box_func_execute_by_name(const char *name, uint32_t name_len, struct port *port,
+			 const char *args, const char *args_end)
+{
+	struct func *func = func_by_name(name, name_len);
+	if (func_access_check(func, tt_cstr(name, name_len)) != 0)
+		return -1;
+	/* Clear all previous errors */
+	diag_clear(&fiber()->diag);
+	int rc = 0;
+	if (func != NULL) {
+		rc = func_execute(func, port, args, args_end);
+	} else {
+		/*
+		 * A user that has "universe" access may call
+		 * any Lua function, even not registered when
+		 * it exists.
+		 */
+		struct func_lua_call_impl_ctx ctx;
+		ctx.func_name = name;
+		ctx.func_name_end = name + name_len;
+		ctx.args = args;
+		ctx.args_end = args_end;
+		rc = func_lua_call_impl(func_lua_call_impl_cb, &ctx, port);
+		if (rc != 0)
+			port_destroy(port);
+	}
+	assert(rc == 0 || diag_last_error(&fiber()->diag) != NULL);
+	return rc;
 }
diff --git a/src/box/func.h b/src/box/func.h
index 82ac046b7..931718bba 100644
--- a/src/box/func.h
+++ b/src/box/func.h
@@ -42,6 +42,9 @@
 extern "C" {
 #endif /* defined(__cplusplus) */
 
+struct port;
+struct func_vtab;
+
 /**
  * Dynamic shared module.
  */
@@ -61,6 +64,8 @@ struct module {
  */
 struct func {
 	struct func_def *def;
+	/** Virtual method table. */
+	struct func_vtab *vtab;
 	/**
 	 * Anchor for module membership.
 	 */
@@ -68,7 +73,7 @@ struct func {
 	/**
 	 * For C functions, the body of the function.
 	 */
-	box_function_f func;
+	box_function_f c_func;
 	/**
 	 * Each stored function keeps a handle to the
 	 * dynamic library for the C callback.
@@ -97,6 +102,10 @@ module_init(void);
 void
 module_free(void);
 
+struct box_function_ctx {
+	struct port *port;
+};
+
 struct func *
 func_new(struct func_def *def);
 
@@ -104,11 +113,12 @@ void
 func_delete(struct func *func);
 
 /**
- * Call stored C function using @a args.
+ * Execute given function.
+ * The current user must have access to execute it.
  */
 int
-func_call(struct func *func, box_function_ctx_t *ctx, const char *args,
-	  const char *args_end);
+func_execute(struct func *func, struct port *port, const char *args,
+	     const char *args_end);
 
 /**
  * Capture a given old_func's runtime an reuse in a given new
@@ -127,6 +137,17 @@ func_reuse_runtime(struct func *new_func, struct func *old_func);
 int
 box_module_reload(const char *name);
 
+/**
+ * Check the current user privileges and execute a Tarantool
+ * function by the given name.
+ * When function object is not registered in function cache and
+ * the user has "universe" privileges, lookup for a given name
+ * in a Lua global namespace and execute when exists.
+ */
+int
+box_func_execute_by_name(const char *name, uint32_t name_len, struct port *port,
+			 const char *args, const char *args_end);
+
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
diff --git a/src/box/lua/call.c b/src/box/lua/call.c
index 9edb5bf7e..4d4521363 100644
--- a/src/box/lua/call.c
+++ b/src/box/lua/call.c
@@ -221,39 +221,6 @@ port_lua_create(struct port *port, struct lua_State *L)
 	port_lua->ref = -1;
 }
 
-static int
-execute_lua_call(lua_State *L)
-{
-	struct call_request *request = (struct call_request *)
-		lua_topointer(L, 1);
-	lua_settop(L, 0); /* clear the stack to simplify the logic below */
-
-	const char *name = request->name;
-	uint32_t name_len = mp_decode_strl(&name);
-
-	/*
-	 * How many objects are on stack after
-	 * luaT_func_find call.
-	 */
-	int oc = 0;
-	/* Try to find a function by name in Lua */
-	if (luaT_func_find(L, name, name + name_len, &oc) != 0) {
-		diag_set(ClientError, ER_NO_SUCH_PROC, name_len, name);
-		return luaT_error(L);
-	}
-
-	/* Push the rest of args (a tuple). */
-	const char *args = request->args;
-
-	uint32_t arg_count = mp_decode_array(&args);
-	luaL_checkstack(L, arg_count, "call: out of stack");
-
-	for (uint32_t i = 0; i < arg_count; i++)
-		luamp_decode(L, luaL_msgpack_default, &args);
-	lua_call(L, arg_count + oc - 1, LUA_MULTRET);
-	return lua_gettop(L);
-}
-
 static int
 execute_lua_eval(lua_State *L)
 {
@@ -396,12 +363,6 @@ box_process_lua(struct call_request *request, struct port *base,
 	return 0;
 }
 
-int
-box_lua_call(struct call_request *request, struct port *port)
-{
-	return box_process_lua(request, port, execute_lua_call);
-}
-
 int
 box_lua_eval(struct call_request *request, struct port *port)
 {
-- 
2.21.0

  parent reply	other threads:[~2019-06-06 12:04 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-06-06 12:03 [PATCH v2 0/9] box: rework functions machinery Kirill Shcherbatov
2019-06-06 12:03 ` [PATCH v2 1/9] box: refactor box_lua_find helper Kirill Shcherbatov
2019-06-10  9:17   ` Vladimir Davydov
2019-06-06 12:03 ` [PATCH v2 2/9] box: move box_module_reload routine to func.c Kirill Shcherbatov
2019-06-10  9:19   ` Vladimir Davydov
2019-06-06 12:03 ` [PATCH v2 3/9] box: rework func cache update machinery Kirill Shcherbatov
2019-06-10  9:44   ` Vladimir Davydov
2019-06-06 12:04 ` Kirill Shcherbatov [this message]
2019-06-10 10:32   ` [PATCH v2 4/9] box: rework func object as a function frontend Vladimir Davydov
2019-06-06 12:04 ` [PATCH v2 5/9] schema: rework _func system space format Kirill Shcherbatov
2019-06-10 12:10   ` Vladimir Davydov
2019-06-06 12:04 ` [PATCH v2 6/9] box: load persistent Lua functions on creation Kirill Shcherbatov
2019-06-10 12:19   ` Vladimir Davydov
2019-06-06 12:04 ` [PATCH v2 7/9] box: sandbox option for persistent functions Kirill Shcherbatov
2019-06-10 14:06   ` Vladimir Davydov
2019-06-10 14:15     ` [tarantool-patches] " Kirill Shcherbatov
2019-06-10 14:41       ` Vladimir Davydov
2019-06-06 12:04 ` [PATCH v2 8/9] box: implement lua_port dump to region and to Lua Kirill Shcherbatov
2019-06-10 14:24   ` Vladimir Davydov
2019-06-06 12:04 ` [PATCH v2 9/9] box: export _func functions with box.func folder Kirill Shcherbatov
2019-06-10 15:18   ` Vladimir Davydov
2019-06-10  9:14 ` [PATCH v2 0/9] box: rework functions machinery 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=8902e130a9cd1ac295391fc9cd315ad4cfa2bafa.1559822429.git.kshcherbatov@tarantool.org \
    --to=kshcherbatov@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --cc=vdavydov.dev@gmail.com \
    --subject='Re: [PATCH v2 4/9] box: rework func object as a function frontend' \
    /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