From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtp18.mail.ru (smtp18.mail.ru [94.100.176.155]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 9517943E88D for ; Tue, 24 Mar 2020 15:46:18 +0300 (MSK) From: Leonid Vasiliev Date: Tue, 24 Mar 2020 15:46:04 +0300 Message-Id: <13e0f6ab780c701621aa6b772a09c199870e0124.1585053743.git.lvasiliev@tarantool.org> In-Reply-To: References: In-Reply-To: References: Subject: [Tarantool-patches] [PATCH 6/6] error: Transmit an error through IPROTO_OK as object List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: alexander.turenko@tarantool.org Cc: tarantool-patches@dev.tarantool.org 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 -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