[Tarantool-patches] [PATCH 6/6] error: Transmit an error through IPROTO_OK as object
Leonid Vasiliev
lvasiliev at tarantool.org
Tue Mar 24 15:46:04 MSK 2020
An ability to transfer an error through IPROTO_OK as object has been added.
Accordinally to https://github.com/tarantool/tarantool/issues/4398
ability to transfer an error through net.box (an IPROTO_OK case)
has been added. The error will be transmit through net.box as object
if the extended transfer error format is used. In this case
for serialization will be used a __serialize_ex method.
@TarantoolBot document
Title: error: transmit an error object throw IPROTO_OK
The error will be transmitted through IPROTO_OK as an object
if the extended transmission error format is used
(before it was transmitted as a string).
---
src/box/execute.c | 1 +
src/box/lua/call.c | 29 ++++++++++++---------
src/box/lua/error.cc | 13 ++++++++++
src/box/lua/execute.c | 2 +-
src/box/lua/net_box.lua | 10 ++++++++
src/box/lua/tuple.c | 12 ++++-----
src/box/session.cc | 4 +++
src/box/session.h | 17 +++---------
src/box/sql/func.c | 4 ++-
src/lib/core/mp_extension_types.h | 4 +--
src/lib/core/port.h | 4 +--
src/lua/error.c | 2 --
src/lua/error.h | 3 ++-
src/lua/error.lua | 22 +++++++++++++++-
src/lua/msgpack.c | 26 ++++++++++---------
src/lua/msgpack.h | 8 +++---
src/lua/utils.c | 28 +++++++++++++++-----
src/lua/utils.h | 27 +++++++++++++++++--
test/box-tap/extended_error.test.lua | 50 +++++++++++++++++++++++++++++++++---
19 files changed, 199 insertions(+), 67 deletions(-)
diff --git a/src/box/execute.c b/src/box/execute.c
index 24f8529..e48c694 100644
--- a/src/box/execute.c
+++ b/src/box/execute.c
@@ -49,6 +49,7 @@
#include "box/sql_stmt_cache.h"
#include "session.h"
#include "rmean.h"
+#include "lua/utils.h"
const char *sql_info_key_strs[] = {
"row_count",
diff --git a/src/box/lua/call.c b/src/box/lua/call.c
index f1bbde7..8e2d5d2 100644
--- a/src/box/lua/call.c
+++ b/src/box/lua/call.c
@@ -46,6 +46,7 @@
#include "small/obuf.h"
#include "trivia/util.h"
#include "mpstream.h"
+#include "box/session.h"
/**
* A helper to find a Lua function by name and put it
@@ -174,7 +175,7 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
*/
for (int i = 1; i <= nrets; ++i) {
struct luaL_field field;
- if (luaL_tofield(L, cfg, i, &field) < 0)
+ if (luaL_tofield(L, cfg, NULL, i, &field) < 0)
return luaT_error(L);
struct tuple *tuple;
if (field.type == MP_EXT &&
@@ -188,11 +189,11 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
*/
lua_pushvalue(L, i);
mpstream_encode_array(stream, 1);
- luamp_encode_r(L, cfg, stream, &field, 0);
+ luamp_encode_r(L, cfg, NULL, stream, &field, 0);
lua_pop(L, 1);
} else {
/* `return ..., array, ...` */
- luamp_encode(L, cfg, stream, i);
+ luamp_encode(L, cfg, NULL, stream, i);
}
}
return nrets;
@@ -203,7 +204,7 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
* Inspect the first result
*/
struct luaL_field root;
- if (luaL_tofield(L, cfg, 1, &root) < 0)
+ if (luaL_tofield(L, cfg, NULL, 1, &root) < 0)
return luaT_error(L);
struct tuple *tuple;
if (root.type == MP_EXT && (tuple = luaT_istuple(L, 1)) != NULL) {
@@ -217,7 +218,7 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
*/
mpstream_encode_array(stream, 1);
assert(lua_gettop(L) == 1);
- luamp_encode_r(L, cfg, stream, &root, 0);
+ luamp_encode_r(L, cfg, NULL, stream, &root, 0);
return 1;
}
@@ -233,7 +234,7 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
for (uint32_t t = 1; t <= root.size; t++) {
lua_rawgeti(L, 1, t);
struct luaL_field field;
- if (luaL_tofield(L, cfg, -1, &field) < 0)
+ if (luaL_tofield(L, cfg, NULL, -1, &field) < 0)
return luaT_error(L);
if (field.type == MP_EXT && (tuple = luaT_istuple(L, -1))) {
tuple_to_mpstream(tuple, stream);
@@ -249,13 +250,13 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
* Encode the first field of tuple using
* existing information from luaL_tofield
*/
- luamp_encode_r(L, cfg, stream, &field, 0);
+ luamp_encode_r(L, cfg, NULL, stream, &field, 0);
lua_pop(L, 1);
assert(lua_gettop(L) == 1);
/* Encode remaining fields as usual */
for (uint32_t f = 2; f <= root.size; f++) {
lua_rawgeti(L, 1, f);
- luamp_encode(L, cfg, stream, -1);
+ luamp_encode(L, cfg, NULL, stream, -1);
lua_pop(L, 1);
}
return 1;
@@ -265,10 +266,10 @@ luamp_encode_call_16(lua_State *L, struct luaL_serializer *cfg,
* { tuple/array, ..., { scalar }, ... }`
*/
mpstream_encode_array(stream, 1);
- luamp_encode_r(L, cfg, stream, &field, 0);
+ luamp_encode_r(L, cfg, NULL, stream, &field, 0);
} else {
/* `return { tuple/array, ..., tuple/array, ... }` */
- luamp_encode_r(L, cfg, stream, &field, 0);
+ luamp_encode_r(L, cfg, NULL, stream, &field, 0);
}
lua_pop(L, 1);
assert(lua_gettop(L) == 1);
@@ -379,6 +380,7 @@ execute_lua_eval(lua_State *L)
struct encode_lua_ctx {
struct port_lua *port;
struct mpstream *stream;
+ struct luaL_serializer_ctx *serializer_ctx;
};
static int
@@ -393,8 +395,10 @@ encode_lua_call(lua_State *L)
*/
struct luaL_serializer *cfg = luaL_msgpack_default;
int size = lua_gettop(ctx->port->L);
- for (int i = 1; i <= size; ++i)
- luamp_encode(ctx->port->L, cfg, ctx->stream, i);
+ for (int i = 1; i <= size; ++i) {
+ luamp_encode(ctx->port->L, cfg, ctx->serializer_ctx,
+ ctx->stream, i);
+ }
ctx->port->size = size;
mpstream_flush(ctx->stream);
return 0;
@@ -429,6 +433,7 @@ port_lua_do_dump(struct port *base, struct mpstream *stream,
struct encode_lua_ctx ctx;
ctx.port = port;
ctx.stream = stream;
+ ctx.serializer_ctx = ¤t_session()->serializer_ctx;
struct lua_State *L = tarantool_L;
int top = lua_gettop(L);
if (lua_cpcall(L, handler, &ctx) != 0) {
diff --git a/src/box/lua/error.cc b/src/box/lua/error.cc
index 4a66335..efe9e8e 100644
--- a/src/box/lua/error.cc
+++ b/src/box/lua/error.cc
@@ -189,6 +189,15 @@ luaT_error_custom_type(lua_State *L)
}
static int
+luaT_error_code(lua_State *L)
+{
+ struct error *e = luaL_checkerror(L, -1);
+ const uint32_t code = box_error_code(e);
+ lua_pushinteger(L, code);
+ return 1;
+}
+
+static int
luaT_error_set_lua_bt(lua_State *L)
{
if (lua_gettop(L) < 2)
@@ -337,6 +346,10 @@ box_lua_error_init(struct lua_State *L) {
lua_pushcfunction(L, luaT_error_set_lua_bt);
lua_setfield(L, -2, "set_lua_bt");
}
+ {
+ lua_pushcfunction(L, luaT_error_code);
+ lua_setfield(L, -2, "error_code");
+ }
lua_setfield(L, -2, "__index");
}
lua_setmetatable(L, -2);
diff --git a/src/box/lua/execute.c b/src/box/lua/execute.c
index b4843b2..b5db3c9 100644
--- a/src/box/lua/execute.c
+++ b/src/box/lua/execute.c
@@ -328,7 +328,7 @@ lua_sql_bind_decode(struct lua_State *L, struct sql_bind *bind, int idx, int i)
bind->name = NULL;
bind->name_len = 0;
}
- if (luaL_tofield(L, luaL_msgpack_default, -1, &field) < 0)
+ if (luaL_tofield(L, luaL_msgpack_default, NULL, -1, &field) < 0)
return -1;
switch (field.type) {
case MP_UINT:
diff --git a/src/box/lua/net_box.lua b/src/box/lua/net_box.lua
index 7fd317c..281b465 100644
--- a/src/box/lua/net_box.lua
+++ b/src/box/lua/net_box.lua
@@ -329,6 +329,15 @@ local function create_transport(host, port, user, password, callback,
return nil, box.error.new({code = self.errno,
reason = self.response})
elseif not self.id then
+ if type(self.response) == 'table' then
+ for k, v in pairs(self.response) do
+ if type(v) == 'table' and v['error_magic']
+ and v['error_magic'] == 13371338 then
+ local err = parse_extended_error(v)
+ self.response[k] = err
+ end
+ end
+ end
return self.response
elseif not worker_fiber then
return nil, box.error.new(E_NO_CONNECTION)
@@ -632,6 +641,7 @@ local function create_transport(host, port, user, password, callback,
ffi.copy(wpos, body_rpos, body_len)
body_len = tonumber(body_len)
if status == IPROTO_OK_KEY then
+ -- tuta interesno
request.response = body_len
requests[id] = nil
request.id = nil
diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
index 1e3c3d6..18efd1e 100644
--- a/src/box/lua/tuple.c
+++ b/src/box/lua/tuple.c
@@ -120,7 +120,7 @@ luaT_tuple_new(struct lua_State *L, int idx, box_tuple_format_t *format)
int argc = lua_gettop(L);
mpstream_encode_array(&stream, argc);
for (int k = 1; k <= argc; ++k) {
- luamp_encode(L, luaL_msgpack_default, &stream, k);
+ luamp_encode(L, luaL_msgpack_default, NULL, &stream, k);
}
} else {
/* Create the tuple from a Lua table. */
@@ -252,18 +252,18 @@ luamp_convert_key(struct lua_State *L, struct luaL_serializer *cfg,
return tuple_to_mpstream(tuple, stream);
struct luaL_field field;
- if (luaL_tofield(L, cfg, index, &field) < 0)
+ if (luaL_tofield(L, cfg, NULL, index, &field) < 0)
luaT_error(L);
if (field.type == MP_ARRAY) {
lua_pushvalue(L, index);
- luamp_encode_r(L, cfg, stream, &field, 0);
+ luamp_encode_r(L, cfg, NULL, stream, &field, 0);
lua_pop(L, 1);
} else if (field.type == MP_NIL) {
mpstream_encode_array(stream, 0);
} else {
mpstream_encode_array(stream, 1);
lua_pushvalue(L, index);
- luamp_encode_r(L, cfg, stream, &field, 0);
+ luamp_encode_r(L, cfg, NULL, stream, &field, 0);
lua_pop(L, 1);
}
}
@@ -275,7 +275,7 @@ luamp_encode_tuple(struct lua_State *L, struct luaL_serializer *cfg,
struct tuple *tuple = luaT_istuple(L, index);
if (tuple != NULL) {
return tuple_to_mpstream(tuple, stream);
- } else if (luamp_encode(L, cfg, stream, index) != MP_ARRAY) {
+ } else if (luamp_encode(L, cfg, NULL, stream, index) != MP_ARRAY) {
diag_set(ClientError, ER_TUPLE_NOT_ARRAY);
luaT_error(L);
}
@@ -435,7 +435,7 @@ lbox_tuple_transform(struct lua_State *L)
mpstream_encode_array(&stream, 3);
mpstream_encode_str(&stream, "!");
mpstream_encode_uint(&stream, offset);
- luamp_encode(L, luaL_msgpack_default, &stream, i);
+ luamp_encode(L, luaL_msgpack_default, NULL, &stream, i);
}
mpstream_flush(&stream);
diff --git a/src/box/session.cc b/src/box/session.cc
index c0224ca..6e13fd2 100644
--- a/src/box/session.cc
+++ b/src/box/session.cc
@@ -148,6 +148,9 @@ session_create(enum session_type type)
/* Set default negotiation parameters */
session->neg_param.err_format_ver = ERR_FORMAT_DEF;
+ /* Set default Lua serializer context */
+ session->serializer_ctx.err_format_ver = ERR_FORMAT_DEF;
+
/* For on_connect triggers. */
credentials_create(&session->credentials, guest_user);
struct mh_i64ptr_node_t node;
@@ -383,5 +386,6 @@ session_update_neg_parameters(struct session *session,
const struct negotiation_params *params)
{
session->neg_param.err_format_ver = params->err_format_ver;
+ session->serializer_ctx.err_format_ver = params->err_format_ver;
return 0;
}
diff --git a/src/box/session.h b/src/box/session.h
index c775fd0..91b6850 100644
--- a/src/box/session.h
+++ b/src/box/session.h
@@ -36,6 +36,7 @@
#include "fiber.h"
#include "user.h"
#include "authentication.h"
+#include "lua/utils.h"
#if defined(__cplusplus)
extern "C" {
@@ -81,20 +82,8 @@ union session_meta {
};
/**
- * An error transmission formats
- */
-enum error_formats {
- /** Default(old) format */
- ERR_FORMAT_DEF,
- /** Extended format */
- ERR_FORMAT_EX,
- /** The max version of error format */
- ERR_FORMAT_UNK
-};
-
-/**
* Parameters which may be changed at negotiation phase of session
-*/
+ */
struct negotiation_params {
/** Version of a format for an error transmission */
uint8_t err_format_ver;
@@ -132,6 +121,8 @@ struct session {
struct trigger fiber_on_stop;
/** Negotiation parameters */
struct negotiation_params neg_param;
+ /** Session Lua serializer context */
+ struct luaL_serializer_ctx serializer_ctx;
};
struct session_vtab {
diff --git a/src/box/sql/func.c b/src/box/sql/func.c
index 6e724c8..bbc1f6f 100644
--- a/src/box/sql/func.c
+++ b/src/box/sql/func.c
@@ -257,8 +257,10 @@ port_lua_get_vdbemem(struct port *base, uint32_t *size)
return NULL;
for (int i = 0; i < argc; i++) {
struct luaL_field field;
- if (luaL_tofield(L, luaL_msgpack_default, -1 - i, &field) < 0)
+ if (luaL_tofield(L, luaL_msgpack_default,
+ NULL, -1 - i, &field) < 0) {
goto error;
+ }
switch (field.type) {
case MP_BOOL:
mem_set_bool(&val[i], field.bval);
diff --git a/src/lib/core/mp_extension_types.h b/src/lib/core/mp_extension_types.h
index bc9873f..0c0fae0 100644
--- a/src/lib/core/mp_extension_types.h
+++ b/src/lib/core/mp_extension_types.h
@@ -40,8 +40,8 @@
* You may assign values in range [0, 127]
*/
enum mp_extension_type {
- MP_UNKNOWN_EXTENSION = 0,
- MP_DECIMAL = 1,
+ MP_UNKNOWN_EXTENSION = 0,
+ MP_DECIMAL = 1,
};
#endif
diff --git a/src/lib/core/port.h b/src/lib/core/port.h
index bfdfa46..2aa9338 100644
--- a/src/lib/core/port.h
+++ b/src/lib/core/port.h
@@ -136,9 +136,9 @@ port_dump_msgpack(struct port *port, struct obuf *out)
}
static inline int
-port_dump_msgpack_16(struct port *port, struct obuf *out)
+port_dump_msgpack_16(struct port *port, struct obuf *buf)
{
- return port->vtab->dump_msgpack_16(port, out);
+ return port->vtab->dump_msgpack_16(port, buf);
}
static inline void
diff --git a/src/lua/error.c b/src/lua/error.c
index 3a12e20..c72f835 100644
--- a/src/lua/error.c
+++ b/src/lua/error.c
@@ -36,8 +36,6 @@
#include <string.h>
-static int CTID_CONST_STRUCT_ERROR_REF = 0;
-
/*
* Memory for the traceback string is obtained with malloc,
* and can be freed with free.
diff --git a/src/lua/error.h b/src/lua/error.h
index 16cdaf7..4e4dc04 100644
--- a/src/lua/error.h
+++ b/src/lua/error.h
@@ -37,7 +37,6 @@
extern "C" {
#endif /* defined(__cplusplus) */
-
/** \cond public */
struct error;
@@ -62,6 +61,8 @@ void
luaT_pusherror(struct lua_State *L, struct error *e);
/** \endcond public */
+extern uint32_t CTID_CONST_STRUCT_ERROR_REF;
+
struct error *
luaL_iserror(struct lua_State *L, int narg);
diff --git a/src/lua/error.lua b/src/lua/error.lua
index 26abec8..da6b3cc 100644
--- a/src/lua/error.lua
+++ b/src/lua/error.lua
@@ -33,6 +33,14 @@ int
exception_get_int(struct error *e, const struct method_info *method);
]]
+-- error details
+local error_details = {
+ REASON = 0,
+ CODE = 1,
+ BACKTRACE = 2,
+ CUSTOM_TYPE = 3
+}
+
local REFLECTION_CACHE = {}
local function reflection_enumerate(err)
@@ -152,6 +160,17 @@ local function error_serialize(err)
return error_message(err)
end
+local function error_serialize_ex(err)
+ local result = {
+ ["error_magic"] = 13371338,
+ [error_details.CODE] = box.error.error_code(err),
+ [error_details.CUSTOM_TYPE] = error_custom_type(err),
+ [error_details.REASON] = error_message(err),
+ [error_details.BACKTRACE] = error_backtrace(err)
+ }
+ return result
+end
+
local function error_is_custom(err)
return ffi.string(err._type.name) == 'CustomError'
end
@@ -161,7 +180,8 @@ local error_methods = {
["raise"] = error_raise,
["match"] = error_match, -- Tarantool 1.6 backward compatibility
["is_custom"] = error_is_custom,
- ["__serialize"] = error_serialize
+ ["__serialize"] = error_serialize,
+ ["__serialize_ex"] = error_serialize_ex
}
local function error_index(err, key)
diff --git a/src/lua/msgpack.c b/src/lua/msgpack.c
index edbc15b..e1f0e8c 100644
--- a/src/lua/msgpack.c
+++ b/src/lua/msgpack.c
@@ -108,8 +108,8 @@ luamp_set_decode_extension(luamp_decode_extension_f handler)
enum mp_type
luamp_encode_r(struct lua_State *L, struct luaL_serializer *cfg,
- struct mpstream *stream, struct luaL_field *field,
- int level)
+ struct luaL_serializer_ctx *ctx, struct mpstream *stream,
+ struct luaL_field *field, int level)
{
int top = lua_gettop(L);
enum mp_type type;
@@ -154,13 +154,13 @@ restart: /* used by MP_EXT */
lua_pushnil(L); /* first key */
while (lua_next(L, top) != 0) {
lua_pushvalue(L, -2); /* push a copy of key to top */
- if (luaL_tofield(L, cfg, lua_gettop(L), field) < 0)
+ if (luaL_tofield(L, cfg, ctx, lua_gettop(L), field) < 0)
return luaT_error(L);
- luamp_encode_r(L, cfg, stream, field, level + 1);
+ luamp_encode_r(L, cfg, ctx, stream, field, level + 1);
lua_pop(L, 1); /* pop a copy of key */
- if (luaL_tofield(L, cfg, lua_gettop(L), field) < 0)
+ if (luaL_tofield(L, cfg, ctx, lua_gettop(L), field) < 0)
return luaT_error(L);
- luamp_encode_r(L, cfg, stream, field, level + 1);
+ luamp_encode_r(L, cfg, ctx, stream, field, level + 1);
lua_pop(L, 1); /* pop value */
}
assert(lua_gettop(L) == top);
@@ -179,9 +179,9 @@ restart: /* used by MP_EXT */
mpstream_encode_array(stream, size);
for (uint32_t i = 0; i < size; i++) {
lua_rawgeti(L, top, i + 1);
- if (luaL_tofield(L, cfg, top + 1, field) < 0)
+ if (luaL_tofield(L, cfg, ctx, top + 1, field) < 0)
return luaT_error(L);
- luamp_encode_r(L, cfg, stream, field, level + 1);
+ luamp_encode_r(L, cfg, ctx, stream, field, level + 1);
lua_pop(L, 1);
}
assert(lua_gettop(L) == top);
@@ -209,7 +209,8 @@ restart: /* used by MP_EXT */
enum mp_type
luamp_encode(struct lua_State *L, struct luaL_serializer *cfg,
- struct mpstream *stream, int index)
+ struct luaL_serializer_ctx *ctx, struct mpstream *stream,
+ int index)
{
int top = lua_gettop(L);
if (index < 0)
@@ -221,9 +222,9 @@ luamp_encode(struct lua_State *L, struct luaL_serializer *cfg,
}
struct luaL_field field;
- if (luaL_tofield(L, cfg, lua_gettop(L), &field) < 0)
+ if (luaL_tofield(L, cfg, ctx, lua_gettop(L), &field) < 0)
return luaT_error(L);
- enum mp_type top_type = luamp_encode_r(L, cfg, stream, &field, 0);
+ enum mp_type top_type = luamp_encode_r(L, cfg, ctx, stream, &field, 0);
if (!on_top) {
lua_remove(L, top + 1); /* remove a value copy */
@@ -232,6 +233,7 @@ luamp_encode(struct lua_State *L, struct luaL_serializer *cfg,
return top_type;
}
+
void
luamp_decode(struct lua_State *L, struct luaL_serializer *cfg,
const char **data)
@@ -354,7 +356,7 @@ lua_msgpack_encode(lua_State *L)
mpstream_init(&stream, buf, ibuf_reserve_cb, ibuf_alloc_cb,
luamp_error, L);
- luamp_encode(L, cfg, &stream, 1);
+ luamp_encode(L, cfg, NULL, &stream, 1);
mpstream_flush(&stream);
if (index > 1) {
diff --git a/src/lua/msgpack.h b/src/lua/msgpack.h
index d0ade30..e5c7a94 100644
--- a/src/lua/msgpack.h
+++ b/src/lua/msgpack.h
@@ -43,6 +43,7 @@ extern "C" {
struct luaL_serializer;
struct mpstream;
+struct luaL_serializer_ctx;
/**
* Default instance of msgpack serializer (msgpack = require('msgpack')).
@@ -63,12 +64,13 @@ enum { LUAMP_ALLOC_FACTOR = 256 };
/* low-level function needed for execute_lua_call() */
enum mp_type
luamp_encode_r(struct lua_State *L, struct luaL_serializer *cfg,
- struct mpstream *stream, struct luaL_field *field,
- int level);
+ struct luaL_serializer_ctx *ctx, struct mpstream *stream,
+ struct luaL_field *field, int level);
enum mp_type
luamp_encode(struct lua_State *L, struct luaL_serializer *cfg,
- struct mpstream *stream, int index);
+ struct luaL_serializer_ctx *ctx, struct mpstream *stream,
+ int index);
void
luamp_decode(struct lua_State *L, struct luaL_serializer *cfg,
diff --git a/src/lua/utils.c b/src/lua/utils.c
index de14c77..9a2b0b9 100644
--- a/src/lua/utils.c
+++ b/src/lua/utils.c
@@ -46,6 +46,7 @@ static uint32_t CTID_STRUCT_IBUF_PTR;
static uint32_t CTID_CHAR_PTR;
static uint32_t CTID_CONST_CHAR_PTR;
uint32_t CTID_DECIMAL;
+uint32_t CTID_CONST_STRUCT_ERROR_REF;
void *
@@ -436,16 +437,21 @@ lua_field_inspect_ucdata(struct lua_State *L, struct luaL_serializer *cfg,
int top = lua_gettop(L);
lua_pushcfunction(L, lua_gettable_wrapper);
lua_pushvalue(L, idx);
- lua_pushliteral(L, LUAL_SERIALIZE);
+ const char *serialize = LUAL_SERIALIZE;
+ if (field->use_serialize_ex)
+ serialize = LUAL_SERIALIZE_EX;
+
+ lua_pushstring(L, serialize);
+
if (lua_pcall(L, 2, 1, 0) == 0 && !lua_isnil(L, -1)) {
if (!lua_isfunction(L, -1))
- luaL_error(L, "invalid " LUAL_SERIALIZE " value");
+ luaL_error(L, "invalid %s value", serialize);
/* copy object itself */
lua_pushvalue(L, idx);
lua_pcall(L, 1, 1, 0);
/* replace obj with the unpacked value */
lua_replace(L, idx);
- if (luaL_tofield(L, cfg, idx, field) < 0)
+ if (luaL_tofield(L, cfg, NULL, idx, field) < 0)
luaT_error(L);
} /* else ignore lua_gettable exceptions */
lua_settop(L, top); /* remove temporary objects */
@@ -508,7 +514,7 @@ lua_field_try_serialize(struct lua_State *L)
/* copy object itself */
lua_pushvalue(L, 1);
lua_call(L, 1, 1);
- s->is_error = (luaL_tofield(L, cfg, -1, field) != 0);
+ s->is_error = (luaL_tofield(L, cfg, NULL, -1, field) != 0);
s->is_value_returned = true;
return 1;
}
@@ -634,12 +640,13 @@ lua_field_tostring(struct lua_State *L, struct luaL_serializer *cfg, int idx,
lua_call(L, 1, 1);
lua_replace(L, idx);
lua_settop(L, top);
- if (luaL_tofield(L, cfg, idx, field) < 0)
+ if (luaL_tofield(L, cfg, NULL, idx, field) < 0)
luaT_error(L);
}
int
-luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
+luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg,
+ struct luaL_serializer_ctx *ctx, int index,
struct luaL_field *field)
{
if (index < 0)
@@ -649,6 +656,8 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
double intpart;
size_t size;
+ field->use_serialize_ex = false;
+
#define CHECK_NUMBER(x) ({ \
if (!isfinite(x) && !cfg->encode_invalid_numbers) { \
if (!cfg->encode_invalid_as_nil) { \
@@ -746,6 +755,11 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
if (cd->ctypeid == CTID_DECIMAL) {
field->ext_type = MP_DECIMAL;
field->decval = (decimal_t *) cdata;
+ } else if (cd->ctypeid == CTID_CONST_STRUCT_ERROR_REF
+ && ctx
+ && ctx->err_format_ver == ERR_FORMAT_EX) {
+ field->ext_type = MP_UNKNOWN_EXTENSION;
+ field->use_serialize_ex = true;
} else {
field->ext_type = MP_UNKNOWN_EXTENSION;
}
@@ -781,6 +795,7 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
default:
field->type = MP_EXT;
field->ext_type = MP_UNKNOWN_EXTENSION;
+ field->use_serialize_ex = false;
}
#undef CHECK_NUMBER
return 0;
@@ -792,6 +807,7 @@ luaL_convertfield(struct lua_State *L, struct luaL_serializer *cfg, int idx,
{
if (idx < 0)
idx = lua_gettop(L) + idx + 1;
+
assert(field->type == MP_EXT && field->ext_type == MP_UNKNOWN_EXTENSION); /* must be called after tofield() */
if (cfg->encode_load_metatables) {
diff --git a/src/lua/utils.h b/src/lua/utils.h
index 0b36727..9e02003 100644
--- a/src/lua/utils.h
+++ b/src/lua/utils.h
@@ -266,12 +266,32 @@ struct luaL_serializer {
struct rlist on_update;
};
+/**
+ * An error serialization formats
+ */
+enum error_formats {
+ /** Default(old) format */
+ ERR_FORMAT_DEF,
+ /** Extended format */
+ ERR_FORMAT_EX,
+ /** The max version of error format */
+ ERR_FORMAT_UNK
+};
+
+/**
+ * A serializer context (additional settings for a serializer)
+ */
+struct luaL_serializer_ctx {
+ uint8_t err_format_ver;
+};
+
extern int luaL_nil_ref;
extern int luaL_map_metatable_ref;
extern int luaL_array_metatable_ref;
#define LUAL_SERIALIZER "serializer"
#define LUAL_SERIALIZE "__serialize"
+#define LUAL_SERIALIZE_EX "__serialize_ex"
struct luaL_serializer *
luaL_newserializer(struct lua_State *L, const char *modname, const luaL_Reg *reg);
@@ -325,6 +345,7 @@ struct luaL_field {
/* subtypes of MP_EXT */
enum mp_extension_type ext_type;
bool compact; /* a flag used by YAML serializer */
+ bool use_serialize_ex;
};
/**
@@ -361,6 +382,7 @@ struct luaL_field {
*
* @param L stack
* @param cfg configuration
+ * @param serializer_ctx the Lua serializer context
* @param index stack index
* @param field conversion result
*
@@ -368,7 +390,8 @@ struct luaL_field {
* @retval -1 Error.
*/
int
-luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,
+luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg,
+ struct luaL_serializer_ctx *ctx, int index,
struct luaL_field *field);
/**
@@ -407,7 +430,7 @@ static inline void
luaL_checkfield(struct lua_State *L, struct luaL_serializer *cfg, int idx,
struct luaL_field *field)
{
- if (luaL_tofield(L, cfg, idx, field) < 0)
+ if (luaL_tofield(L, cfg, NULL, idx, field) < 0)
luaT_error(L);
if (field->type != MP_EXT || field->ext_type != MP_UNKNOWN_EXTENSION)
return;
diff --git a/test/box-tap/extended_error.test.lua b/test/box-tap/extended_error.test.lua
index 58a4984..b38a7ab 100755
--- a/test/box-tap/extended_error.test.lua
+++ b/test/box-tap/extended_error.test.lua
@@ -6,7 +6,7 @@ local tap = require('tap')
local uri = require('uri')
local test = tap.test('check an extended error')
-test:plan(2)
+test:plan(4)
function error_func()
@@ -17,6 +17,12 @@ function custom_error_func()
box.error(box.error.CUSTOM_ERROR, "My Custom Type", "A modern custom error")
end
+function custom_error_func_2()
+ local err = box.error.new(box.error.CUSTOM_ERROR,
+ "My Custom Type", "A modern custom error")
+ return err
+end
+
local function version_at_least(peer_version_str, major, minor, patch)
local major_p, minor_p, patch_p = string.match(peer_version_str,
"(%d)%.(%d)%.(%d)")
@@ -86,9 +92,11 @@ end
local function test_old_transmission(host, port)
grant_func('guest', 'error_func')
+ grant_func('guest', 'custom_error_func_2')
local connection = netbox.connect(host, port)
local _, err = pcall(connection.call, connection, 'error_func')
+ local err_2 = connection:call('custom_error_func_2')
local backtrace_pattern =
"^stack traceback:\n" ..
@@ -109,15 +117,22 @@ local function test_old_transmission(host, port)
}
local check_result = check_error(err, check_list)
- test:ok(check_result, 'Check the old transmission type')
+ local check_result_2 = type(err_2) == 'string'
+ and err_2 == 'User custom error: A modern custom error'
+
+ test:ok(check_result, 'Check the old transmission type(IPROTO_ERROR)')
+ test:ok(check_result_2, 'Check the old transmission type(IPROTO_OK)')
+
connection:close()
end
local function test_extended_transmission(host, port)
grant_func('guest', 'custom_error_func')
+ grant_func('guest', 'custom_error_func_2')
local connection = netbox.connect(host, port, {error_extended = true})
local _, err = pcall(connection.call, connection, 'custom_error_func')
+ local err_2 = connection:call('custom_error_func_2')
local backtrace_pattern =
"^Remote%(.+:%d+%):\n" ..
@@ -135,6 +150,21 @@ local function test_extended_transmission(host, port)
".*box%-tap/extended_error.test.lua:%d+: in function 'test_extended_transmission'\n" ..
".*box%-tap/extended_error.test.lua:%d+: in main chunk$"
+ local backtrace_pattern_2 =
+ "^Remote%(.+:%d+%):\n" ..
+ "stack traceback:\n" ..
+ "%s+%[C%]: in function 'new'\n" ..
+ ".*box%-tap/extended_error.test.lua:%d+: in function <.*box%-tap/extended_error.test.lua:%d+>\n" ..
+ "%s+%[C%]: at 0x%w+\n" ..
+ "End Remote\n" ..
+ "stack traceback:\n" ..
+ "%s+builtin/box/net_box.lua:%d+: in function 'parse_extended_error'\n" ..
+ "%s+builtin/box/net_box.lua:%d+: in function 'perform_request'\n" ..
+ "%s+builtin/box/net_box.lua:%d+: in function '_request'\n" ..
+ "%s+builtin/box/net_box.lua:%d+: in function 'call'\n" ..
+ ".*box%-tap/extended_error.test.lua:%d+: in function 'test_extended_transmission'\n" ..
+ ".*box%-tap/extended_error.test.lua:%d+: in main chunk$"
+
local check_list = {
type = 'CustomError',
custom_type = 'My Custom Type',
@@ -144,8 +174,20 @@ local function test_extended_transmission(host, port)
is_custom = true
}
+ local check_list_2 = {
+ type = 'CustomError',
+ custom_type = 'My Custom Type',
+ message = 'User custom error: A modern custom error',
+ trace = '^File builtin/box/net_box.lua\nLine %d+$',
+ bt = backtrace_pattern_2,
+ is_custom = true
+ }
+
local check_result = check_error(err, check_list)
- test:ok(check_result, 'Check the extended transmission type')
+ local check_result_2 = check_error(err_2, check_list_2)
+ test:ok(check_result, 'Check the extended transmission type(IPROTO_ERROR)')
+ test:ok(check_result_2, 'Check the extended transmission type(IPROTO_OK)')
+
connection:close()
end
@@ -162,6 +204,8 @@ if version_at_least(box.info.version, 2, 4, 1) then
else
test:ok(true, 'Current version of tarantool(' .. tarantool_ver .. ')' ..
' don\'t support extended transmission')
+ test:ok(true, 'Current version of tarantool(' .. tarantool_ver .. ')' ..
+ ' don\'t support extended transmission')
end
test_old_transmission(host, port)
--
2.7.4
More information about the Tarantool-patches
mailing list