From: Leonid Vasiliev <lvasiliev@tarantool.org> To: v.shpilevoy@tarantool.org Cc: tarantool-patches@dev.tarantool.org Subject: [Tarantool-patches] [PATCH V3 7/7] error: add error MsgPack encoding Date: Wed, 15 Apr 2020 12:32:02 +0300 [thread overview] Message-ID: <fb22bb10a83fb954ccc33aa74ec655d7529619f7.1586934134.git.lvasiliev@tarantool.org> (raw) In-Reply-To: <cover.1586934134.git.lvasiliev@tarantool.org> In-Reply-To: <cover.1586934134.git.lvasiliev@tarantool.org> Closes #4398 @TarantoolBot document Title: msgpack format for MP_ERROR For sent an error over IProto MP_ERROR(0x03) subtype has been added to MsgPack MP_EXT. Now, if session setting 'error_marshaling_enabled' is true error will be encoded as: ``` +--------+----------+========+ | MP_EXT | MP_ERROR | MP_MAP | +--------+----------+========+ ``` At the map passes all necessary parameters (depending from the error type) to create a copy of the error on the client side. The necessary fields: - Error type - Trace(file name and line) - Error message - Errno Additional fields: - ClientError code - Traceback (now only Lua traceback) - Custom type - Access Denied object type - Access Denied object name - Access Denied access type --- src/box/CMakeLists.txt | 1 + src/box/lua/init.c | 56 +++++ src/box/lua/mp_error.cc | 476 +++++++++++++++++++++++++++++++++++ src/box/lua/mp_error.h | 49 ++++ src/box/lua/tuple.c | 16 -- src/lib/core/mp_extension_types.h | 1 + src/lua/error.c | 2 - src/lua/error.h | 3 +- src/lua/msgpack.c | 3 + src/lua/utils.c | 6 +- test/box-tap/extended_error.test.lua | 182 ++++++++++++++ 11 files changed, 774 insertions(+), 21 deletions(-) create mode 100644 src/box/lua/mp_error.cc create mode 100644 src/box/lua/mp_error.h create mode 100755 test/box-tap/extended_error.test.lua diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index 6688303..7581078 100644 --- a/src/box/CMakeLists.txt +++ b/src/box/CMakeLists.txt @@ -151,6 +151,7 @@ add_library(box STATIC lua/stat.c lua/ctl.c lua/error.cc + lua/mp_error.cc lua/session.c lua/net_box.c lua/xlog.c diff --git a/src/box/lua/init.c b/src/box/lua/init.c index 63e8b82..d7e97a4 100644 --- a/src/box/lua/init.c +++ b/src/box/lua/init.c @@ -36,11 +36,13 @@ #include "lua/utils.h" /* luaT_error() */ #include "lua/trigger.h" +#include "lua/msgpack.h" #include "box/box.h" #include "box/txn.h" #include "box/func.h" #include "box/vclock.h" +#include "box/session.h" #include "box/lua/error.h" #include "box/lua/tuple.h" @@ -62,6 +64,9 @@ #include "box/lua/execute.h" #include "box/lua/key_def.h" #include "box/lua/merger.h" +#include "box/lua/mp_error.h" + +#include "mpstream/mpstream.h" static uint32_t CTID_STRUCT_TXN_SAVEPOINT_PTR = 0; @@ -386,6 +391,54 @@ static const struct luaL_Reg boxlib_backup[] = { {NULL, NULL} }; +/** + * A MsgPack extensions handler that supports tuples and errors encode. + */ +static enum mp_type +luamp_encode_extension_box(struct lua_State *L, int idx, + struct mpstream *stream) +{ + struct tuple *tuple = luaT_istuple(L, idx); + if (tuple != NULL) { + tuple_to_mpstream(tuple, stream); + return MP_ARRAY; + } + + if (current_session()->meta.serializer_opts.error_marshaling_enabled) { + struct error *err = luaL_iserror(L, idx); + if (err != NULL) + error_to_mpstream(err, stream); + } + + return MP_EXT; +} + +/** + * A MsgPack extensions handler that supports errors decode. + */ +static void +luamp_decode_extension_box(struct lua_State *L, const char **data) +{ + assert(mp_typeof(**data) == MP_EXT); + int8_t ext_type; + uint32_t len = mp_decode_extl(data, &ext_type); + + if(ext_type != MP_ERROR) { + luaL_error(L, "Unsuported MsgPack extension type: %u", + ext_type); + return; + } + + struct error *err = error_unpack(data, len); + if (err == NULL) { + luaL_error(L, "Can't parse an error from MsgPack"); + return; + } + + luaT_pusherror(L, err); + return; +} + #include "say.h" void @@ -426,6 +479,9 @@ box_lua_init(struct lua_State *L) luaopen_merger(L); lua_pop(L, 1); + luamp_set_encode_extension(luamp_encode_extension_box); + luamp_set_decode_extension(luamp_decode_extension_box); + /* Load Lua extension */ for (const char **s = lua_sources; *s; s += 2) { const char *modname = *s; diff --git a/src/box/lua/mp_error.cc b/src/box/lua/mp_error.cc new file mode 100644 index 0000000..f99ed2e --- /dev/null +++ b/src/box/lua/mp_error.cc @@ -0,0 +1,476 @@ +/* + * Copyright 2010-2020, Tarantool AUTHORS, please see AUTHORS file. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "box/lua/mp_error.h" +#include "box/error.h" +#include "mpstream/mpstream.h" +#include "msgpuck.h" +#include "mp_extension_types.h" + +/** + * Keys used for encode/decode MP_ERROR. + */ +enum mp_error_details { + MP_ERROR_DET_TYPE, + MP_ERROR_DET_FILE, + MP_ERROR_DET_LINE, + MP_ERROR_DET_REASON, + MP_ERROR_DET_ERRNO, + MP_ERROR_DET_CODE, + MP_ERROR_DET_TRACEBACK, + MP_ERROR_DET_CUSTOM_TYPE, + MP_ERROR_DET_AD_OBJ_TYPE, + MP_ERROR_DET_AD_OBJ_NAME, + MP_ERROR_DET_AD_ACCESS_TYPE +}; + +/** + * Types of errors that can be transmitted using MP_ERROR. + */ +enum mp_error_types { + MP_ERROR_TYPE_UNKNOWN, + MP_ERROR_TYPE_CLIENT, + MP_ERROR_TYPE_CUSTOM, + MP_ERROR_TYPE_ACCESS_DENIED, + MP_ERROR_TYPE_XLOG, + MP_ERROR_TYPE_XLOG_GAP, + MP_ERROR_TYPE_SYSTEM, + MP_ERROR_TYPE_SOCKET, + MP_ERROR_TYPE_OOM, + MP_ERROR_TYPE_TIMED_OUT, + MP_ERROR_TYPE_CHANNEL_IS_CLOSED, + MP_ERROR_TYPE_FIBER_IS_CANCELLED, + MP_ERROR_TYPE_LUAJIT, + MP_ERROR_TYPE_ILLEGAL_PARAMS, + MP_ERROR_TYPE_COLLATION, + MP_ERROR_TYPE_SWIM, + MP_ERROR_TYPE_CRYPTO +}; + +/** + * The structure is used for storing parameters + * during decoding MP_ERROR. + */ +struct mp_error { + uint32_t error_code; + enum mp_error_types error_type; + uint32_t line; + uint32_t saved_errno; + char *file; + char *traceback; + char *reason; + char *custom_type; + char *ad_obj_type; + char *ad_obj_name; + char *ad_access_type; +}; + +static void +mp_error_init(struct mp_error *mp_error) +{ + mp_error->error_type = MP_ERROR_TYPE_UNKNOWN; + mp_error->file = NULL; + mp_error->traceback = NULL; + mp_error->reason = NULL; + mp_error->custom_type = NULL; + mp_error->ad_obj_type = NULL; + mp_error->ad_obj_name = NULL; + mp_error->ad_access_type = NULL; +} + +static void +mp_error_cleanup(struct mp_error *mp_error) +{ + mp_error->error_type = MP_ERROR_TYPE_UNKNOWN; + free(mp_error->file); + free(mp_error->traceback); + free(mp_error->reason); + free(mp_error->custom_type); + free(mp_error->ad_obj_type); + free(mp_error->ad_obj_name); + free(mp_error->ad_access_type); +} + +static uint8_t +mp_error_type_from_str(const char *type_str) +{ + if (type_str == NULL) { + return MP_ERROR_TYPE_UNKNOWN; + } else if (strcmp(type_str, "ClientError") == 0) { + return MP_ERROR_TYPE_CLIENT; + } else if (strcmp(type_str, "CustomError") == 0) { + return MP_ERROR_TYPE_CUSTOM; + } else if (strcmp(type_str, "AccessDeniedError") == 0) { + return MP_ERROR_TYPE_ACCESS_DENIED; + } else if (strcmp(type_str, "XlogError") == 0) { + return MP_ERROR_TYPE_XLOG; + } else if (strcmp(type_str, "XlogGapError") == 0) { + return MP_ERROR_TYPE_XLOG_GAP; + } else if (strcmp(type_str, "SystemError") == 0) { + return MP_ERROR_TYPE_SYSTEM; + } else if (strcmp(type_str, "SocketError") == 0) { + return MP_ERROR_TYPE_SOCKET; + } else if (strcmp(type_str, "OutOfMemory") == 0) { + return MP_ERROR_TYPE_OOM; + } else if (strcmp(type_str, "TimedOut") == 0) { + return MP_ERROR_TYPE_TIMED_OUT; + } else if (strcmp(type_str, "ChannelIsClosed") == 0) { + return MP_ERROR_TYPE_CHANNEL_IS_CLOSED; + } else if (strcmp(type_str, "FiberIsCancelled") == 0) { + return MP_ERROR_TYPE_FIBER_IS_CANCELLED; + } else if (strcmp(type_str, "LuajitError") == 0) { + return MP_ERROR_TYPE_LUAJIT; + } else if (strcmp(type_str, "IllegalParams") == 0) { + return MP_ERROR_TYPE_ILLEGAL_PARAMS; + } else if (strcmp(type_str, "CollationError") == 0) { + return MP_ERROR_TYPE_COLLATION; + } else if (strcmp(type_str, "SwimError") == 0) { + return MP_ERROR_TYPE_SWIM; + } else if (strcmp(type_str, "CryptoError") == 0) { + return MP_ERROR_TYPE_CRYPTO; + } + + return MP_ERROR_TYPE_UNKNOWN; +} + +void +error_to_mpstream(struct error *error, struct mpstream *stream) +{ + uint8_t err_type = mp_error_type_from_str(box_error_type(error)); + + uint32_t errcode; + const char *custom_type = NULL; + const char *ad_obj_type = NULL; + const char *ad_obj_name = NULL; + const char *ad_access_type = NULL; + + uint32_t details_num = 5; + + uint32_t data_size = 0; + + data_size += mp_sizeof_uint(MP_ERROR_DET_TYPE); + data_size += mp_sizeof_uint(err_type); + data_size += mp_sizeof_uint(MP_ERROR_DET_LINE); + data_size += mp_sizeof_uint(error->line); + data_size += mp_sizeof_uint(MP_ERROR_DET_FILE); + data_size += mp_sizeof_str(strlen(error->file)); + data_size += mp_sizeof_uint(MP_ERROR_DET_REASON); + data_size += mp_sizeof_str(strlen(error->errmsg)); + data_size += mp_sizeof_uint(MP_ERROR_DET_ERRNO); + data_size += mp_sizeof_uint(error->saved_errno); + + if (error->traceback) { + ++details_num; + data_size += mp_sizeof_uint(MP_ERROR_DET_TRACEBACK); + data_size += mp_sizeof_str(strlen(error->traceback)); + } + + if (err_type == MP_ERROR_TYPE_CLIENT || + err_type == MP_ERROR_TYPE_ACCESS_DENIED || + err_type == MP_ERROR_TYPE_CUSTOM) { + ++details_num; + errcode = box_error_code(error); + data_size += mp_sizeof_uint(MP_ERROR_DET_CODE); + data_size += mp_sizeof_uint(errcode); + if (err_type == MP_ERROR_TYPE_CUSTOM) { + ++details_num; + data_size += mp_sizeof_uint(MP_ERROR_DET_CUSTOM_TYPE); + custom_type = box_error_custom_type(error); + data_size += mp_sizeof_str(strlen(custom_type)); + } else if (err_type == MP_ERROR_TYPE_ACCESS_DENIED) { + AccessDeniedError *ad_err = type_cast(AccessDeniedError, + error); + details_num += 3; + ad_obj_type = ad_err->object_type(); + ad_obj_name = ad_err->object_name(); + ad_access_type = ad_err->access_type(); + data_size += mp_sizeof_uint(MP_ERROR_DET_AD_OBJ_TYPE); + data_size += mp_sizeof_str(strlen(ad_obj_type)); + data_size += mp_sizeof_uint(MP_ERROR_DET_AD_OBJ_NAME); + data_size += mp_sizeof_str(strlen(ad_obj_name)); + data_size += mp_sizeof_uint(MP_ERROR_DET_AD_ACCESS_TYPE); + data_size += mp_sizeof_str(strlen(ad_access_type)); + } + } + + data_size += mp_sizeof_map(details_num); + uint32_t data_size_ext = mp_sizeof_ext(data_size); + char *ptr = mpstream_reserve(stream, data_size_ext); + + char *data = ptr; + data = mp_encode_extl(data, MP_ERROR, data_size); + data = mp_encode_map(data, details_num); + data = mp_encode_uint(data, MP_ERROR_DET_TYPE); + data = mp_encode_uint(data, err_type); + data = mp_encode_uint(data, MP_ERROR_DET_LINE); + data = mp_encode_uint(data, error->line); + data = mp_encode_uint(data, MP_ERROR_DET_FILE); + data = mp_encode_str(data, error->file, strlen(error->file)); + data = mp_encode_uint(data, MP_ERROR_DET_REASON); + data = mp_encode_str(data, error->errmsg, strlen(error->errmsg)); + data = mp_encode_uint(data, MP_ERROR_DET_ERRNO); + data = mp_encode_uint(data, error->saved_errno); + if(error->traceback) { + data = mp_encode_uint(data, MP_ERROR_DET_TRACEBACK); + data = mp_encode_str(data, error->traceback, + strlen(error->traceback)); + } + + if (err_type == MP_ERROR_TYPE_CLIENT || + err_type == MP_ERROR_TYPE_ACCESS_DENIED || + err_type == MP_ERROR_TYPE_CUSTOM) { + data = mp_encode_uint(data, MP_ERROR_DET_CODE); + data = mp_encode_uint(data, errcode); + if (err_type == MP_ERROR_TYPE_CUSTOM) { + data = mp_encode_uint(data, MP_ERROR_DET_CUSTOM_TYPE); + data = mp_encode_str(data, custom_type, + strlen(custom_type)); + } else if (err_type == MP_ERROR_TYPE_ACCESS_DENIED) { + data = mp_encode_uint(data, MP_ERROR_DET_AD_OBJ_TYPE); + data = mp_encode_str(data, ad_obj_type, + strlen(ad_obj_type)); + data = mp_encode_uint(data, MP_ERROR_DET_AD_OBJ_NAME); + data = mp_encode_str(data, ad_obj_name, + strlen(ad_obj_name)); + data = mp_encode_uint(data, MP_ERROR_DET_AD_ACCESS_TYPE); + data = mp_encode_str(data, ad_access_type, + strlen(ad_access_type)); + } + } + + assert(data == ptr + data_size_ext); + mpstream_advance(stream, data_size_ext); +} + +static struct error * +build_error(struct mp_error *mp_error) +{ + /* + * For create an error the "raw" constructor using + * because OOM error must be throwed in OOM case. + * Bulders returns a pointer to the static OOM error + * in OOM case. + */ + struct error *err; + switch (mp_error->error_type) { + case MP_ERROR_TYPE_UNKNOWN: + err = NULL; + break; + case MP_ERROR_TYPE_CLIENT: + { + ClientError *e = new ClientError(mp_error->file, mp_error->line, + ER_UNKNOWN); + e->m_errcode = mp_error->error_code; + err = e; + break; + } + case MP_ERROR_TYPE_CUSTOM: + err = new CustomError(mp_error->file, mp_error->line, + mp_error->custom_type); + break; + case MP_ERROR_TYPE_ACCESS_DENIED: + err = new AccessDeniedError(mp_error->file, mp_error->line, + mp_error->ad_access_type, + mp_error->ad_obj_type, + mp_error->ad_obj_name, ""); + break; + case MP_ERROR_TYPE_XLOG: + err = new XlogError(&type_XlogError, mp_error->file, + mp_error->line); + error_format_msg(err, "%s", mp_error->reason); + break; + case MP_ERROR_TYPE_XLOG_GAP: + err = new XlogGapError(mp_error->file, mp_error->line, + mp_error->reason); + break; + case MP_ERROR_TYPE_SYSTEM: + err = new SystemError(mp_error->file, mp_error->line, + "%s", mp_error->reason); + break; + case MP_ERROR_TYPE_SOCKET: + err = new SocketError(mp_error->file, mp_error->line, "", ""); + error_format_msg(err, "%s", mp_error->reason); + break; + case MP_ERROR_TYPE_OOM: + err = new OutOfMemory(mp_error->file, mp_error->line, + 0, "", ""); + error_format_msg(err, "%s", mp_error->reason); + break; + case MP_ERROR_TYPE_TIMED_OUT: + err = new TimedOut(mp_error->file, mp_error->line); + break; + case MP_ERROR_TYPE_CHANNEL_IS_CLOSED: + err = new ChannelIsClosed(mp_error->file, mp_error->line); + break; + case MP_ERROR_TYPE_FIBER_IS_CANCELLED: + err = new FiberIsCancelled(mp_error->file, mp_error->line); + break; + case MP_ERROR_TYPE_LUAJIT: + err = new LuajitError(mp_error->file, mp_error->line, + mp_error->reason); + break; + case MP_ERROR_TYPE_ILLEGAL_PARAMS: + err = new IllegalParams(mp_error->file, mp_error->line, + "%s", mp_error->reason); + break; + case MP_ERROR_TYPE_COLLATION: + err = new CollationError(mp_error->file, mp_error->line, + "%s", mp_error->reason); + break; + case MP_ERROR_TYPE_SWIM: + err = new SwimError(mp_error->file, mp_error->line, + "%s", mp_error->reason); + break; + case MP_ERROR_TYPE_CRYPTO: + err = new CryptoError(mp_error->file, mp_error->line, + "%s", mp_error->reason); + break; + default: + unreachable(); + break; + } + + if (mp_error->traceback) + error_set_traceback(err, mp_error->traceback); + else + err->is_traceback_enabled = false; + + err->saved_errno = mp_error->saved_errno; + error_format_msg(err, "%s", mp_error->reason); + + return err; +} + +struct error * +error_unpack(const char **data, uint32_t len) +{ + const char *svp = *data; + if (mp_typeof(**data) != MP_MAP) { + diag_set(ClientError, ER_INVALID_MSGPACK, + "Invalid MP_ERROR format"); + return NULL; + } + + struct mp_error mp_err; + mp_error_init(&mp_err); + + uint32_t map_size = mp_decode_map(data); + + struct error *err = NULL; + for (uint32_t i = 0; i < map_size; ++i) { + if (mp_typeof(**data) != MP_UINT) { + diag_set(ClientError, ER_INVALID_MSGPACK, + "Invalid MP_ERROR MsgPack format"); + return NULL; + } + + uint8_t key = mp_decode_uint(data); + const char *str; + uint32_t str_len; + switch(key) { + case MP_ERROR_DET_TYPE: + if (mp_typeof(**data) != MP_UINT) + goto error; + mp_err.error_type = (enum mp_error_types) + mp_decode_uint(data); + break; + case MP_ERROR_DET_FILE: + if (mp_typeof(**data) != MP_STR) + goto error; + str = mp_decode_str(data, &str_len); + mp_err.file = strndup(str, str_len); + break; + case MP_ERROR_DET_LINE: + if (mp_typeof(**data) != MP_UINT) + goto error; + mp_err.line = mp_decode_uint(data); + break; + case MP_ERROR_DET_REASON: + if (mp_typeof(**data) != MP_STR) + goto error; + str = mp_decode_str(data, &str_len); + mp_err.reason = strndup(str, str_len); + break; + case MP_ERROR_DET_ERRNO: + if (mp_typeof(**data) != MP_UINT) + goto error; + mp_err.saved_errno = mp_decode_uint(data); + break; + case MP_ERROR_DET_CODE: + if (mp_typeof(**data) != MP_UINT) + goto error; + mp_err.error_code = mp_decode_uint(data); + break; + case MP_ERROR_DET_TRACEBACK: + if (mp_typeof(**data) != MP_STR) + goto error; + str = mp_decode_str(data, &str_len); + mp_err.traceback = strndup(str, str_len); + break; + case MP_ERROR_DET_CUSTOM_TYPE: + if (mp_typeof(**data) != MP_STR) + goto error; + str = mp_decode_str(data, &str_len); + mp_err.custom_type = strndup(str, str_len); + break; + case MP_ERROR_DET_AD_OBJ_TYPE: + if (mp_typeof(**data) != MP_STR) + goto error; + str = mp_decode_str(data, &str_len); + mp_err.ad_obj_type = strndup(str, str_len); + break; + case MP_ERROR_DET_AD_OBJ_NAME: + if (mp_typeof(**data) != MP_STR) + goto error; + str = mp_decode_str(data, &str_len); + mp_err.ad_obj_name = strndup(str, str_len); + break; + case MP_ERROR_DET_AD_ACCESS_TYPE: + if (mp_typeof(**data) != MP_STR) + goto error; + str = mp_decode_str(data, &str_len); + mp_err.ad_access_type = strndup(str, str_len); + break; + default: + mp_next(data); + } + } + + assert(*data == svp + len); + + err = build_error(&mp_err); + mp_error_cleanup(&mp_err); + return err; + +error: + diag_set(ClientError, ER_INVALID_MSGPACK, + "Invalid MP_ERROR MsgPack format"); + return NULL; +} diff --git a/src/box/lua/mp_error.h b/src/box/lua/mp_error.h new file mode 100644 index 0000000..2aa5cb9 --- /dev/null +++ b/src/box/lua/mp_error.h @@ -0,0 +1,49 @@ +#pragma once +/* + * Copyright 2010-2020, Tarantool AUTHORS, please see AUTHORS file. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(__cplusplus) +extern "C" { +#endif /* defined(__cplusplus) */ + +#include <stdint.h> + +struct mpstream; + +void +error_to_mpstream(struct error *error, struct mpstream *stream); + +struct error * +error_unpack(const char **data, uint32_t len); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif /* defined(__cplusplus) */ diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c index aba906d..03b4b8a 100644 --- a/src/box/lua/tuple.c +++ b/src/box/lua/tuple.c @@ -290,20 +290,6 @@ tuple_to_mpstream(struct tuple *tuple, struct mpstream *stream) mpstream_advance(stream, bsize); } -/* A MsgPack extensions handler that supports tuples */ -static enum mp_type -luamp_encode_extension_box(struct lua_State *L, int idx, - struct mpstream *stream) -{ - struct tuple *tuple = luaT_istuple(L, idx); - if (tuple != NULL) { - tuple_to_mpstream(tuple, stream); - return MP_ARRAY; - } - - return MP_EXT; -} - /** * Convert a tuple into lua table. Named fields are stored as * {name = value} pairs. Not named fields are stored as @@ -582,8 +568,6 @@ box_lua_tuple_init(struct lua_State *L) luaL_register_module(L, tuplelib_name, lbox_tuplelib); lua_pop(L, 1); - luamp_set_encode_extension(luamp_encode_extension_box); - tuple_serializer_update_options(); trigger_create(&tuple_serializer.update_trigger, on_msgpack_serializer_update, NULL, NULL); diff --git a/src/lib/core/mp_extension_types.h b/src/lib/core/mp_extension_types.h index 7d42f21..e3ff9f5 100644 --- a/src/lib/core/mp_extension_types.h +++ b/src/lib/core/mp_extension_types.h @@ -43,6 +43,7 @@ enum mp_extension_type { MP_UNKNOWN_EXTENSION = 0, MP_DECIMAL = 1, MP_UUID = 2, + MP_ERROR = 3, mp_extension_type_MAX, }; diff --git a/src/lua/error.c b/src/lua/error.c index a21548c..192d537 100644 --- a/src/lua/error.c +++ b/src/lua/error.c @@ -34,8 +34,6 @@ #include <fiber.h> #include "utils.h" -static int CTID_CONST_STRUCT_ERROR_REF = 0; - struct error * luaL_iserror(struct lua_State *L, int narg) { 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/msgpack.c b/src/lua/msgpack.c index b14361f..91560ef 100644 --- a/src/lua/msgpack.c +++ b/src/lua/msgpack.c @@ -195,6 +195,8 @@ restart: /* used by MP_EXT of unidentified subtype */ case MP_UUID: mpstream_encode_uuid(stream, field->uuidval); break; + case MP_ERROR: + return luamp_encode_extension(L, top, stream); default: /* Run trigger if type can't be encoded */ type = luamp_encode_extension(L, top, stream); @@ -237,6 +239,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) diff --git a/src/lua/utils.c b/src/lua/utils.c index bd320f3..aaa8a3f 100644 --- a/src/lua/utils.c +++ b/src/lua/utils.c @@ -47,6 +47,7 @@ static uint32_t CTID_CHAR_PTR; static uint32_t CTID_CONST_CHAR_PTR; static uint32_t CTID_UUID; uint32_t CTID_DECIMAL; +uint32_t CTID_CONST_STRUCT_ERROR_REF; void * @@ -650,8 +651,6 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, struct serializer_opts *opts, int index, struct luaL_field *field) { - /* opts will be used for encode MP_ERROR in the future */ - (void)opts; if (index < 0) index = lua_gettop(L) + index + 1; @@ -759,6 +758,9 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, } else if (cd->ctypeid == CTID_UUID) { field->ext_type = MP_UUID; field->uuidval = (struct tt_uuid *) cdata; + } else if (cd->ctypeid == CTID_CONST_STRUCT_ERROR_REF && + opts && opts->error_marshaling_enabled) { + field->ext_type = MP_ERROR; } else { field->ext_type = MP_UNKNOWN_EXTENSION; } diff --git a/test/box-tap/extended_error.test.lua b/test/box-tap/extended_error.test.lua new file mode 100755 index 0000000..41dd84e --- /dev/null +++ b/test/box-tap/extended_error.test.lua @@ -0,0 +1,182 @@ +#! /usr/bin/env tarantool + +local netbox = require('net.box') +local os = require('os') +local tap = require('tap') +local uri = require('uri') + +local test = tap.test('check an extended error') +test:plan(4) + + +function error_func() + box.error(box.error.PROC_LUA, "An old good error") +end + +function custom_error_func() + box.error("My Custom Type", "A modern custom error") +end + +function custom_error_func_2() + local err = box.error.new("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)") + major_p = tonumber(major_p) + minor_p = tonumber(minor_p) + patch_p = tonumber(patch_p) + + if major_p < major + or (major_p == major and minor_p < minor) + or (major_p == major and minor_p == minor and patch_p < patch) then + return false + end + + return true +end + +local function grant_func(user, name) + box.schema.func.create(name, {if_not_exists = true}) + box.schema.user.grant(user, 'execute', 'function', name, { + if_not_exists = true + }) +end + +local function check_error(err, check_list) + if type(check_list) ~= 'table' then + return false + end + + for k, v in pairs(check_list) do + if k == 'type' then + if err.base_type ~= v then + return false + end + elseif k == 'custom_type' then + if err.type ~= v then + return false + end + elseif k == 'message' then + if err.message ~= v then + return false + end + elseif k == 'traceback' then + if not string.match(err.traceback, v) then + return false + end + elseif k == 'trace' then + local trace = "File " .. err.trace[1]['file'] + .. "\nLine " .. err.trace[1]['line'] + if not string.match(trace, v) then + return false + end + elseif k == 'errno' then + if err.errno ~= v then + return false + end + elseif k == 'is_custom' then + if (err.base_type == 'CustomError') ~= v then + return false + end + else + return false + end + end + + return true +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) + box.error.cfg({traceback_supplementation = true}) + local _, err = pcall(connection.call, connection, 'error_func') + local err_2 = connection:call('custom_error_func_2') + + + local check_list = { + type = 'ClientError', + message = 'An old good error', + trace = '^File builtin/box/net_box.lua\nLine %d+$', + is_custom = false + } + + local check_result = check_error(err, check_list) + local check_result_2 = type(err_2) == 'string' and err_2 == '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') + box.schema.user.grant('guest','read,write', 'space', '_session_settings') + box.error.cfg({traceback_enable = true}) + + local connection = netbox.connect(host, port) + connection.space._session_settings:update('error_marshaling_enabled', + {{'=', 2, true}}) + local _, err = pcall(connection.call, connection, 'custom_error_func') + local err_2 = connection:call('custom_error_func_2') + + local tb_1 = "^stack traceback:\n" .. + "%s+%[C%]: in function 'new'\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 <builtin/box/net_box.lua:%d+>\n" .. + "%s+%[C%]: in function 'pcall'\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', + message = 'A modern custom error', + trace = '^File builtin/box/net_box.lua\nLine %d+$', + traceback = tb_1, + is_custom = true + } + + local tb_2 = "^stack traceback:" .. + "%s+%[C%]: in function 'new'\n" .. + ".*extended_error.test.lua:%d+:" .. + " in function <.*extended_error.test.lua:%d+>\n" .. + "%s+%[C%]: at 0x.*" + + local check_list_2 = { + type = 'CustomError', + custom_type = 'My Custom Type', + message = 'A modern custom error', + trace = '.*extended_error.test.lua\nLine 21$', + traceback = tb_2, + is_custom = true + } + local check_result = check_error(err, check_list) + 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 + +box.cfg{ + listen = os.getenv('LISTEN') +} +local host= uri.parse(box.cfg.listen).host or 'localhost' +local port = uri.parse(box.cfg.listen).service + +test_extended_transmission(host, port) +test_old_transmission(host, port) + +os.exit(test:check() and 0 or 1) -- 2.7.4
next prev parent reply other threads:[~2020-04-15 9:32 UTC|newest] Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top 2020-04-15 9:31 [Tarantool-patches] [PATCH V3 0/7] Extending error functionality Leonid Vasiliev 2020-04-15 9:31 ` [Tarantool-patches] [PATCH V3 1/7] error: add a Lua traceback to error Leonid Vasiliev 2020-04-15 9:31 ` [Tarantool-patches] [PATCH V3 2/7] error: add custom error type Leonid Vasiliev 2020-04-15 9:31 ` [Tarantool-patches] [PATCH V3 3/7] error: send custom type in IProto Leonid Vasiliev 2020-04-15 9:31 ` [Tarantool-patches] [PATCH V3 4/7] session: add offset to SQL session settings array Leonid Vasiliev 2020-04-15 9:32 ` [Tarantool-patches] [PATCH V3 5/7] error: add session setting for error type marshaling Leonid Vasiliev 2020-04-15 9:32 ` [Tarantool-patches] [PATCH V3 6/7] error: update constructors of some errors Leonid Vasiliev 2020-04-15 9:32 ` Leonid Vasiliev [this message] 2020-04-15 15:08 ` [Tarantool-patches] [PATCH V3 7/7] error: add error MsgPack encoding lvasiliev
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=fb22bb10a83fb954ccc33aa74ec655d7529619f7.1586934134.git.lvasiliev@tarantool.org \ --to=lvasiliev@tarantool.org \ --cc=tarantool-patches@dev.tarantool.org \ --cc=v.shpilevoy@tarantool.org \ --subject='Re: [Tarantool-patches] [PATCH V3 7/7] error: add error MsgPack encoding' \ /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