[PATCH v3 2/6] box: rework box_lua_{call, eval} to use input port
Kirill Shcherbatov
kshcherbatov at tarantool.org
Thu Jun 13 17:08:22 MSK 2019
Re-factor box_lua_call and box_lua_eval so that they don't take
call_request. This approach is more scalable: in case of a
functional index, the user expects to see a tuple with field
names so we should be able to pass not only raw msgpack, but
also a tuple to a Lua call so we need an universal way to pass
arguments to _call methods.
To pass a tuple msgpack introduced a new port_msgpack: the port
class with dump_lua method.
Needed for #4182, #1260
---
src/box/call.c | 46 ++++++----------
src/box/call.h | 4 --
src/box/execute.c | 1 +
src/box/func.c | 27 ++++++++--
src/box/func.h | 11 ++--
src/box/lua/call.c | 121 +++++++++++++++++++++++++++++++-----------
src/box/lua/call.h | 8 ++-
src/box/lua/execute.c | 7 +--
src/box/lua/execute.h | 4 +-
src/box/lua/misc.cc | 5 +-
src/box/port.c | 3 +-
src/box/port.h | 15 ++++++
src/lib/core/port.h | 15 ++++--
13 files changed, 184 insertions(+), 83 deletions(-)
diff --git a/src/box/call.c b/src/box/call.c
index 56da53fb3..1ab3993da 100644
--- a/src/box/call.c
+++ b/src/box/call.c
@@ -105,33 +105,6 @@ access_check_func(const char *name, uint32_t name_len, struct func **funcp)
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_module_reload(const char *name)
{
@@ -199,11 +172,15 @@ box_process_call(struct call_request *request, struct port *port)
}
int rc;
+ struct port in_port;
+ port_msgpack_create(&in_port, request->args,
+ request->args_end - request->args);
if (func && func->def->language == FUNC_LANGUAGE_C) {
- rc = box_c_call(func, request, port);
+ rc = func_call(func, &in_port, port);
} else {
- rc = box_lua_call(request, port);
+ rc = box_lua_call(name, name_len, &in_port, port);
}
+ port_destroy(&in_port);
/* Restore the original user */
if (orig_credentials)
fiber_set_user(fiber(), orig_credentials);
@@ -229,16 +206,23 @@ box_process_eval(struct call_request *request, struct port *port)
/* Check permissions */
if (access_check_universe(PRIV_X) != 0)
return -1;
- if (box_lua_eval(request, port) != 0) {
+ struct port in_port;
+ port_msgpack_create(&in_port, request->args,
+ request->args_end - request->args);
+ const char *expr = request->expr;
+ uint32_t expr_len = mp_decode_strl(&expr);
+ if (box_lua_eval(expr, expr_len, &in_port, port) != 0) {
+ port_destroy(&in_port);
txn_rollback();
return -1;
}
if (in_txn()) {
diag_set(ClientError, ER_FUNCTION_TX_ACTIVE);
+ port_destroy(&in_port);
txn_rollback();
return -1;
}
-
+ port_destroy(&in_port);
return 0;
}
diff --git a/src/box/call.h b/src/box/call.h
index 1b54551be..45580bc9d 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;
-};
-
/**
* Reload loadable module by name.
*
diff --git a/src/box/execute.c b/src/box/execute.c
index e81cc32aa..a91c939fc 100644
--- a/src/box/execute.c
+++ b/src/box/execute.c
@@ -108,6 +108,7 @@ const struct port_vtab port_sql_vtab = {
/* .dump_msgpack_16 = */ NULL,
/* .dump_lua = */ port_sql_dump_lua,
/* .dump_plain = */ NULL,
+ /* .get_msgpack = */ NULL,
/* .destroy = */ port_sql_destroy,
};
diff --git a/src/box/func.c b/src/box/func.c
index d1b8879ad..740fad3d7 100644
--- a/src/box/func.c
+++ b/src/box/func.c
@@ -34,6 +34,9 @@
#include "lua/utils.h"
#include "error.h"
#include "diag.h"
+#include "fiber.h"
+#include "port.h"
+#include "txn.h"
#include <dlfcn.h>
/**
@@ -433,21 +436,39 @@ func_load(struct func *func)
}
int
-func_call(struct func *func, box_function_ctx_t *ctx, const char *args,
- const char *args_end)
+func_call(struct func *func, struct port *in_port, struct port *out_port)
{
+ assert(func != NULL && func->def->language == FUNC_LANGUAGE_C);
+ /* Transaction is not started. */
+ assert(!in_txn());
if (func->func == NULL) {
if (func_load(func) != 0)
return -1;
}
+ uint32_t args_sz;
+ const char *args = port_get_msgpack(in_port, &args_sz);
+ if (args == NULL)
+ return -1;
+
+ port_tuple_create(out_port);
+ box_function_ctx_t ctx = { out_port };
+
/* 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);
+ int rc = func->func(&ctx, args, args + args_sz);
--module->calls;
module_gc(module);
+ 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(out_port);
+ return -1;
+ }
return rc;
}
diff --git a/src/box/func.h b/src/box/func.h
index fa4d738ab..d38a2b283 100644
--- a/src/box/func.h
+++ b/src/box/func.h
@@ -42,6 +42,12 @@
extern "C" {
#endif /* defined(__cplusplus) */
+struct port;
+
+struct box_function_ctx {
+ struct port *port;
+};
+
/**
* Dynamic shared module.
*/
@@ -104,11 +110,10 @@ void
func_delete(struct func *func);
/**
- * Call stored C function using @a args.
+ * Call function with arguments represented with given in_port.
*/
int
-func_call(struct func *func, box_function_ctx_t *ctx, const char *args,
- const char *args_end);
+func_call(struct func *func, struct port *in_port, struct port *out_port);
/**
* Reload dynamically loadable module.
diff --git a/src/box/lua/call.c b/src/box/lua/call.c
index 04020ef6f..e32845095 100644
--- a/src/box/lua/call.c
+++ b/src/box/lua/call.c
@@ -266,6 +266,55 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
return root.size;
}
+static const struct port_vtab port_msgpack_vtab;
+
+void
+port_msgpack_create(struct port *base, const char *data, uint32_t data_sz)
+{
+ struct port_msgpack *port_msgpack = (struct port_msgpack *) base;
+ memset(port_msgpack, 0, sizeof(*port_msgpack));
+ port_msgpack->vtab = &port_msgpack_vtab;
+ port_msgpack->data = data;
+ port_msgpack->data_sz = data_sz;
+}
+
+static void
+port_msgpack_dump_lua(struct port *base, struct lua_State *L, bool is_flat)
+{
+ assert(is_flat == true);
+ struct port_msgpack *port = (struct port_msgpack *) base;
+ assert(port->vtab == &port_msgpack_vtab);
+
+ const char *args = port->data;
+ uint32_t arg_count = mp_decode_array(&args);
+ for (uint32_t i = 0; i < arg_count; i++)
+ luamp_decode(L, luaL_msgpack_default, &args);
+}
+
+static const char *
+port_msgpack_get_msgpack(struct port *base, uint32_t *size)
+{
+ struct port_msgpack *port = (struct port_msgpack *) base;
+ assert(port->vtab == &port_msgpack_vtab);
+ *size = port->data_sz;
+ return port->data;
+}
+
+static void
+port_msgpack_destroy(struct port *base)
+{
+ (void) base;
+}
+
+static const struct port_vtab port_msgpack_vtab = {
+ .dump_msgpack = NULL,
+ .dump_msgpack_16 = NULL,
+ .dump_lua = port_msgpack_dump_lua,
+ .dump_plain = NULL,
+ .get_msgpack = port_msgpack_get_msgpack,
+ .destroy = port_msgpack_destroy,
+};
+
static const struct port_vtab port_lua_vtab;
void
@@ -282,28 +331,31 @@ port_lua_create(struct port *port, struct lua_State *L)
port_lua->ref = -1;
}
+struct lua_process_ctx {
+ const char *name;
+ uint32_t name_len;
+};
+
static int
execute_lua_call(lua_State *L)
{
- struct call_request *request = (struct call_request *)
- lua_topointer(L, 1);
+ struct port *in_port = (struct port *) lua_topointer(L, 1);
+ struct lua_process_ctx *ctx =
+ (struct lua_process_ctx *) lua_topointer(L, 2);
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);
+ const char *name = ctx->name;
+ uint32_t name_len = ctx->name_len;
int oc = 0; /* how many objects are on stack after box_lua_find */
/* Try to find a function by name in Lua */
oc = box_lua_find(L, name, name + name_len);
/* 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");
+ int top = lua_gettop(L);
+ port_dump_lua(in_port, L, true);
+ int arg_count = lua_gettop(L) - top;
- 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);
}
@@ -311,24 +363,23 @@ execute_lua_call(lua_State *L)
static int
execute_lua_eval(lua_State *L)
{
- struct call_request *request = (struct call_request *)
- lua_topointer(L, 1);
+ struct port *in_port = (struct port *) lua_topointer(L, 1);
+ struct lua_process_ctx *ctx =
+ (struct lua_process_ctx *) lua_topointer(L, 2);
lua_settop(L, 0); /* clear the stack to simplify the logic below */
/* Compile expression */
- const char *expr = request->expr;
- uint32_t expr_len = mp_decode_strl(&expr);
+ const char *expr = ctx->name;
+ uint32_t expr_len = ctx->name_len;
if (luaL_loadbuffer(L, expr, expr_len, "=eval")) {
diag_set(LuajitError, lua_tostring(L, -1));
luaT_error(L);
}
/* Unpack arguments */
- const char *args = request->args;
- uint32_t arg_count = mp_decode_array(&args);
- luaL_checkstack(L, arg_count, "eval: out of stack");
- for (uint32_t i = 0; i < arg_count; i++)
- luamp_decode(L, luaL_msgpack_default, &args);
+ int top = lua_gettop(L);
+ port_dump_lua(in_port, L, true);
+ int arg_count = lua_gettop(L) - top;
/* Call compiled code */
lua_call(L, arg_count, LUA_MULTRET);
@@ -429,37 +480,47 @@ static const struct port_vtab port_lua_vtab = {
.dump_msgpack_16 = port_lua_dump_16,
.dump_lua = NULL,
.dump_plain = port_lua_dump_plain,
+ .get_msgpack = NULL,
.destroy = port_lua_destroy,
};
static inline int
-box_process_lua(struct call_request *request, struct port *base,
- lua_CFunction handler)
+box_process_lua(lua_CFunction handler, void *arg, struct port *in_port,
+ struct port *out_port)
{
lua_State *L = lua_newthread(tarantool_L);
int coro_ref = luaL_ref(tarantool_L, LUA_REGISTRYINDEX);
- port_lua_create(base, L);
- ((struct port_lua *) base)->ref = coro_ref;
+ port_lua_create(out_port, L);
+ ((struct port_lua *) out_port)->ref = coro_ref;
lua_pushcfunction(L, handler);
- lua_pushlightuserdata(L, request);
- if (luaT_call(L, 1, LUA_MULTRET) != 0) {
- port_lua_destroy(base);
+ lua_pushlightuserdata(L, in_port);
+ lua_pushlightuserdata(L, arg);
+ if (luaT_call(L, 2, LUA_MULTRET) != 0) {
+ port_lua_destroy(out_port);
return -1;
}
return 0;
}
int
-box_lua_call(struct call_request *request, struct port *port)
+box_lua_call(const char *name, uint32_t name_len,
+ struct port *in_port, struct port *out_port)
{
- return box_process_lua(request, port, execute_lua_call);
+ struct lua_process_ctx ctx;
+ ctx.name = name;
+ ctx.name_len = name_len;
+ return box_process_lua(execute_lua_call, &ctx, in_port, out_port);
}
int
-box_lua_eval(struct call_request *request, struct port *port)
+box_lua_eval(const char *expr, uint32_t expr_len,
+ struct port *in_port, struct port *out_port)
{
- return box_process_lua(request, port, execute_lua_eval);
+ struct lua_process_ctx ctx;
+ ctx.name = expr;
+ ctx.name_len = expr_len;
+ return box_process_lua(execute_lua_eval, &ctx, in_port, out_port);
}
static int
diff --git a/src/box/lua/call.h b/src/box/lua/call.h
index 0542123da..1801d5090 100644
--- a/src/box/lua/call.h
+++ b/src/box/lua/call.h
@@ -35,6 +35,8 @@
extern "C" {
#endif /* defined(__cplusplus) */
+#include <stdint.h>
+
struct lua_State;
void
@@ -48,10 +50,12 @@ struct call_request;
* (implementation of 'CALL' command code).
*/
int
-box_lua_call(struct call_request *request, struct port *port);
+box_lua_call(const char *name, uint32_t name_len,
+ struct port *in_port, struct port *out_port);
int
-box_lua_eval(struct call_request *request, struct port *port);
+box_lua_eval(const char *expr, uint32_t expr_len,
+ struct port *in_port, struct port *out_port);
#if defined(__cplusplus)
} /* extern "C" */
diff --git a/src/box/lua/execute.c b/src/box/lua/execute.c
index 239aba47b..187bdb73d 100644
--- a/src/box/lua/execute.c
+++ b/src/box/lua/execute.c
@@ -39,8 +39,9 @@ lua_sql_get_metadata(struct sql_stmt *stmt, struct lua_State *L,
}
void
-port_sql_dump_lua(struct port *port, struct lua_State *L)
+port_sql_dump_lua(struct port *port, struct lua_State *L, bool is_flat)
{
+ assert(is_flat == false);
assert(port->vtab == &port_sql_vtab);
struct sql *db = sql_get();
struct sql_stmt *stmt = ((struct port_sql *)port)->stmt;
@@ -49,7 +50,7 @@ port_sql_dump_lua(struct port *port, struct lua_State *L)
lua_createtable(L, 0, 2);
lua_sql_get_metadata(stmt, L, column_count);
lua_setfield(L, -2, "metadata");
- port_tuple_vtab.dump_lua(port, L);
+ port_tuple_vtab.dump_lua(port, L, is_flat);
lua_setfield(L, -2, "rows");
} else {
assert(((struct port_tuple *)port)->size == 0);
@@ -262,7 +263,7 @@ lbox_execute(struct lua_State *L)
if (sql_prepare_and_execute(sql, length, bind, bind_count, &port,
&fiber()->gc) != 0)
return luaT_error(L);
- port_dump_lua(&port, L);
+ port_dump_lua(&port, L, false);
port_destroy(&port);
return 1;
}
diff --git a/src/box/lua/execute.h b/src/box/lua/execute.h
index bc4df19f5..23e193fa4 100644
--- a/src/box/lua/execute.h
+++ b/src/box/lua/execute.h
@@ -30,6 +30,8 @@
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
+#include <stdbool.h>
+
struct port;
struct sql_bind;
struct lua_State;
@@ -42,7 +44,7 @@ struct lua_State;
* @param L Lua stack.
*/
void
-port_sql_dump_lua(struct port *port, struct lua_State *L);
+port_sql_dump_lua(struct port *port, struct lua_State *L, bool is_flat);
/**
* Parse Lua table of SQL parameters.
diff --git a/src/box/lua/misc.cc b/src/box/lua/misc.cc
index 8de7401fa..15681c377 100644
--- a/src/box/lua/misc.cc
+++ b/src/box/lua/misc.cc
@@ -61,8 +61,9 @@ lbox_encode_tuple_on_gc(lua_State *L, int idx, size_t *p_len)
* but implemented here to eliminate port.c dependency on Lua.
*/
extern "C" void
-port_tuple_dump_lua(struct port *base, struct lua_State *L)
+port_tuple_dump_lua(struct port *base, struct lua_State *L, bool is_flat)
{
+ assert(is_flat == false);
struct port_tuple *port = port_tuple(base);
lua_createtable(L, port->size, 0);
struct port_tuple_entry *pe = port->first;
@@ -109,7 +110,7 @@ lbox_select(lua_State *L)
* table always crashed the first (can't be fixed with pcall).
* https://github.com/tarantool/tarantool/issues/1182
*/
- port_dump_lua(&port, L);
+ port_dump_lua(&port, L, false);
port_destroy(&port);
return 1; /* lua table with tuples */
}
diff --git a/src/box/port.c b/src/box/port.c
index 99046449a..7f552bcfe 100644
--- a/src/box/port.c
+++ b/src/box/port.c
@@ -125,7 +125,7 @@ port_tuple_dump_msgpack(struct port *base, struct obuf *out)
}
extern void
-port_tuple_dump_lua(struct port *base, struct lua_State *L);
+port_tuple_dump_lua(struct port *base, struct lua_State *L, bool is_flat);
void
port_init(void)
@@ -145,5 +145,6 @@ const struct port_vtab port_tuple_vtab = {
.dump_msgpack_16 = port_tuple_dump_msgpack_16,
.dump_lua = port_tuple_dump_lua,
.dump_plain = NULL,
+ .get_msgpack = NULL,
.destroy = port_tuple_destroy,
};
diff --git a/src/box/port.h b/src/box/port.h
index f18803660..4c0c3cf7a 100644
--- a/src/box/port.h
+++ b/src/box/port.h
@@ -32,6 +32,7 @@
*/
#include "trivia/util.h"
#include <port.h>
+#include <stdbool.h>
#if defined(__cplusplus)
extern "C" {
@@ -80,6 +81,20 @@ port_tuple_create(struct port *port);
int
port_tuple_add(struct port *port, struct tuple *tuple);
+/** Port implementation used for storing raw data. */
+struct port_msgpack {
+ const struct port_vtab *vtab;
+ const char *data;
+ uint32_t data_sz;
+};
+
+static_assert(sizeof(struct port_msgpack) <= sizeof(struct port),
+ "sizeof(struct port_msgpack) must be <= sizeof(struct port)");
+
+/** Initialize a port to dump row data. */
+void
+port_msgpack_create(struct port *port, const char *data, uint32_t data_sz);
+
/** Port for storing the result of a Lua CALL/EVAL. */
struct port_lua {
const struct port_vtab *vtab;
diff --git a/src/lib/core/port.h b/src/lib/core/port.h
index 8ace40fc5..2f1bc5ed1 100644
--- a/src/lib/core/port.h
+++ b/src/lib/core/port.h
@@ -30,6 +30,7 @@
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
+#include <stdbool.h>
#include <stdint.h>
#if defined(__cplusplus)
@@ -63,7 +64,7 @@ struct port_vtab {
*/
int (*dump_msgpack_16)(struct port *port, struct obuf *out);
/** Dump the content of a port to Lua stack. */
- void (*dump_lua)(struct port *port, struct lua_State *L);
+ void (*dump_lua)(struct port *port, struct lua_State *L, bool is_flat);
/**
* Dump a port content as a plain text into a buffer,
* allocated inside.
@@ -74,6 +75,8 @@ struct port_vtab {
* @retval not nil Plain text.
*/
const char *(*dump_plain)(struct port *port, uint32_t *size);
+ /** Get the content of a port as a msgpack data. */
+ const char *(*get_msgpack)(struct port *port, uint32_t *size);
/** Destroy a port and release associated resources. */
void (*destroy)(struct port *port);
};
@@ -109,9 +112,9 @@ port_dump_msgpack_16(struct port *port, struct obuf *out)
}
static inline void
-port_dump_lua(struct port *port, struct lua_State *L)
+port_dump_lua(struct port *port, struct lua_State *L, bool is_flat)
{
- port->vtab->dump_lua(port, L);
+ port->vtab->dump_lua(port, L, is_flat);
}
static inline const char *
@@ -120,6 +123,12 @@ port_dump_plain(struct port *port, uint32_t *size)
return port->vtab->dump_plain(port, size);
}
+static inline const char *
+port_get_msgpack(struct port *port, uint32_t *size)
+{
+ return port->vtab->get_msgpack(port, size);
+}
+
#if defined(__cplusplus)
} /* extern "C" */
#endif /* defined __cplusplus */
--
2.21.0
More information about the Tarantool-patches
mailing list