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 447FA441851 for ; Tue, 24 Mar 2020 15:46:13 +0300 (MSK) From: Leonid Vasiliev Date: Tue, 24 Mar 2020 15:46:00 +0300 Message-Id: <56090ffde2ff472e2add51df13ebac93f5ee66b5.1585053743.git.lvasiliev@tarantool.org> In-Reply-To: References: In-Reply-To: References: Subject: [Tarantool-patches] [PATCH 2/6] error: Add the custom error type 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 A possibility to create an error with a custom subtype was added. In accordance with https://github.com/tarantool/tarantool/issues/4398 a custom error type has been added to the box.error. Now, it's possible to create an error with a custom subtype (string value) for use it in applications. @TarantoolBot document Title: error.custom_type A custom error type has been added to the box.error. Now, it's possible to create an error with a custom subtype (string value) for use it in applications. Example: err_custom = box.error.new(box.error.CUSTOM_ERROR, "My Custom Type", "Reason") Now: err_custom.type == "CustomError" err_custom.custom_type == "My Custom Type" err_custom.message == "User custom error: Reason" Needed for #4398 --- src/box/errcode.h | 1 + src/box/error.cc | 56 ++++++++++++++++++++++++++++++ src/box/error.h | 32 ++++++++++++++++++ src/box/lua/error.cc | 92 ++++++++++++++++++++++++++++++++++++++------------ src/lua/error.lua | 25 +++++++++----- test/box/misc.result | 19 +++++++++++ test/box/misc.test.lua | 9 +++++ 7 files changed, 204 insertions(+), 30 deletions(-) diff --git a/src/box/errcode.h b/src/box/errcode.h index 4441717..a2f23dd 100644 --- a/src/box/errcode.h +++ b/src/box/errcode.h @@ -265,6 +265,7 @@ struct errcode_record { /*210 */_(ER_SQL_PREPARE, "Failed to prepare SQL statement: %s") \ /*211 */_(ER_WRONG_QUERY_ID, "Prepared statement with id %u does not exist") \ /*212 */_(ER_SEQUENCE_NOT_STARTED, "Sequence '%s' is not started") \ + /*213 */_(ER_CUSTOM_ERROR, "User custom error: %s") \ /* * !IMPORTANT! Please follow instructions at start of the file diff --git a/src/box/error.cc b/src/box/error.cc index 47dce33..25e7eff 100644 --- a/src/box/error.cc +++ b/src/box/error.cc @@ -45,6 +45,16 @@ box_error_type(const box_error_t *e) return e->type->name; } +const char * +box_custom_error_type(const box_error_t *e) +{ + CustomError *custom_error = type_cast(CustomError, e); + if (custom_error) + return custom_error->custom_type(); + + return NULL; +} + uint32_t box_error_code(const box_error_t *e) { @@ -86,6 +96,17 @@ box_error_set(const char *file, unsigned line, uint32_t code, return -1; } +int +box_custom_error_set(const char *file, unsigned line, + const char *custom, const char *reason) +{ + struct error *e = BuildCustomError(file, line, custom); + strncpy(e->errmsg, reason, DIAG_ERRMSG_MAX); + e->errmsg[DIAG_ERRMSG_MAX - 1] = '\0'; + diag_add_error(&fiber()->diag, e); + return -1; +} + /* }}} */ struct rmean *rmean_error = NULL; @@ -253,3 +274,38 @@ BuildAccessDeniedError(const char *file, unsigned int line, return e; } } + +static struct method_info customerror_methods[] = { + make_method(&type_CustomError, "custom_type", &CustomError::custom_type), + METHODS_SENTINEL +}; + +const struct type_info type_CustomError = + make_type("CustomError", &type_ClientError, + customerror_methods); + +CustomError::CustomError(const char *file, unsigned int line, + const char *custom_type) + :ClientError(&type_CustomError, file, line, ER_CUSTOM_ERROR) +{ + error_format_msg(this, tnt_errcode_desc(m_errcode), + custom_type ?: ""); + + if (custom_type) { + strncpy(m_custom_type, custom_type, 63); + m_custom_type[63] = '\0'; + } else { + m_custom_type[0] = '\0'; + } +} + +struct error * +BuildCustomError(const char *file, unsigned int line, + const char *custom_type) +{ + try { + return new CustomError(file, line, custom_type); + } catch (OutOfMemory *e) { + return e; + } +} diff --git a/src/box/error.h b/src/box/error.h index b8c7cf7..3e0beb8 100644 --- a/src/box/error.h +++ b/src/box/error.h @@ -53,6 +53,9 @@ struct error * BuildXlogGapError(const char *file, unsigned line, const struct vclock *from, const struct vclock *to); +struct error * +BuildCustomError(const char *file, unsigned int line, const char *custom_type); + /** \cond public */ struct error; @@ -70,6 +73,14 @@ const char * box_error_type(const box_error_t *error); /** + * Return the error custom type, + * \param error + * \return pointer to custom error type. On error, return NULL + */ +const char * +box_custom_error_type(const box_error_t *e); + +/** * Return IPROTO error code * \param error error * \return enum box_error_code @@ -129,6 +140,10 @@ int box_error_set(const char *file, unsigned line, uint32_t code, const char *format, ...); +int +box_custom_error_set(const char *file, unsigned line, + const char *custom, const char *reason); + /** * A backward-compatible API define. */ @@ -140,6 +155,7 @@ box_error_set(const char *file, unsigned line, uint32_t code, extern const struct type_info type_ClientError; extern const struct type_info type_XlogError; extern const struct type_info type_AccessDeniedError; +extern const struct type_info type_CustomError; #if defined(__cplusplus) } /* extern "C" */ @@ -266,6 +282,22 @@ struct XlogGapError: public XlogError virtual void raise() { throw this; } }; +class CustomError: public ClientError +{ +public: + CustomError(const char *file, unsigned int line, + const char *custom_type); + + const char* + custom_type() + { + return m_custom_type; + } +private: + /** Custom type name*/ + char m_custom_type[64]; +}; + #endif /* defined(__cplusplus) */ #endif /* TARANTOOL_BOX_ERROR_H_INCLUDED */ diff --git a/src/box/lua/error.cc b/src/box/lua/error.cc index fc53a40..708d338 100644 --- a/src/box/lua/error.cc +++ b/src/box/lua/error.cc @@ -43,31 +43,35 @@ extern "C" { #include "box/error.h" static void -luaT_error_create(lua_State *L, int top_base) -{ - uint32_t code = 0; - const char *reason = NULL; - const char *file = ""; - unsigned line = 0; - lua_Debug info; +luaT_error_read_args(lua_State *L, int top_base, uint32_t *code, + const char **custom_type, const char **reason) { int top = lua_gettop(L); if (top >= top_base && lua_type(L, top_base) == LUA_TNUMBER) { - code = lua_tonumber(L, top_base); - reason = tnt_errcode_desc(code); + *code = lua_tonumber(L, top_base); + *reason = tnt_errcode_desc(*code); if (top > top_base) { + int shift = 1; + if (*code == ER_CUSTOM_ERROR) { + *custom_type = lua_tostring(L, top_base + 1); + shift = 2; + } + /* Call string.format(reason, ...) to format message */ lua_getglobal(L, "string"); if (lua_isnil(L, -1)) - goto raise; + return; lua_getfield(L, -1, "format"); if (lua_isnil(L, -1)) - goto raise; - lua_pushstring(L, reason); - for (int i = top_base + 1; i <= top; i++) + return; + + lua_pushstring(L, *reason); + int nargs = 1; + for (int i = top_base + shift; i <= top; ++i, ++nargs) lua_pushvalue(L, i); - lua_call(L, top - top_base + 1, 1); - reason = lua_tostring(L, -1); - } else if (strchr(reason, '%') != NULL) { + + lua_call(L, nargs, 1); + *reason = lua_tostring(L, -1); + } else if (strchr(*reason, '%') != NULL) { /* Missing arguments to format string */ luaL_error(L, "box.error(): bad arguments"); } @@ -75,12 +79,15 @@ luaT_error_create(lua_State *L, int top_base) if (lua_istable(L, top_base)) { /* A special case that rethrows raw error (used by net.box) */ lua_getfield(L, top_base, "code"); - code = lua_tonumber(L, -1); + *code = lua_tonumber(L, -1); lua_pop(L, 1); + if (*code == ER_CUSTOM_ERROR) { + lua_getfield(L, top_base, "custom_type"); + *custom_type = lua_tostring(L, -1); + lua_pop(L, 1); + } lua_getfield(L, top_base, "reason"); - reason = lua_tostring(L, -1); - if (reason == NULL) - reason = ""; + *reason = lua_tostring(L, -1); lua_pop(L, 1); } else if (luaL_iserror(L, top_base)) { lua_error(L); @@ -90,7 +97,21 @@ luaT_error_create(lua_State *L, int top_base) luaL_error(L, "box.error(): bad arguments"); } -raise: + return; +} + +static void +luaT_error_create(lua_State *L, int top_base) +{ + uint32_t code = 0; + const char *custom_type = NULL; + const char *reason = NULL; + + luaT_error_read_args(L, top_base, &code, &custom_type, &reason); + + const char *file = ""; + unsigned line = 0; + lua_Debug info; if (lua_getstack(L, 1, &info) && lua_getinfo(L, "Sl", &info)) { if (*info.short_src) { file = info.short_src; @@ -101,8 +122,16 @@ raise: } line = info.currentline; } + + if (reason == NULL) + reason = ""; say_debug("box.error() at %s:%i", file, line); - box_error_set(file, line, code, "%s", reason); + if (code == ER_CUSTOM_ERROR) { + box_custom_error_set(file, line, + custom_type ? custom_type : "", reason); + } else { + box_error_set(file, line, code, "%s", reason); + } } static int @@ -145,6 +174,21 @@ luaT_error_new(lua_State *L) } static int +luaT_error_custom_type(lua_State *L) +{ + struct error *e = luaL_checkerror(L, -1); + + const char *custom_type = box_custom_error_type(e); + if (custom_type == NULL) { + lua_pushfstring(L, "The error has't a custom type"); + return 1; + } + + lua_pushstring(L, custom_type); + return 1; +} + +static int luaT_error_clear(lua_State *L) { if (lua_gettop(L) >= 1) @@ -268,6 +312,10 @@ box_lua_error_init(struct lua_State *L) { lua_pushcfunction(L, luaT_error_new); lua_setfield(L, -2, "new"); } + { + lua_pushcfunction(L, luaT_error_custom_type); + lua_setfield(L, -2, "custom_type"); + } lua_setfield(L, -2, "__index"); } lua_setmetatable(L, -2); diff --git a/src/lua/error.lua b/src/lua/error.lua index 765ce73..26abec8 100644 --- a/src/lua/error.lua +++ b/src/lua/error.lua @@ -104,6 +104,10 @@ local function error_errno(err) return e end +local function error_custom_type(err) + return box.error.custom_type(err) +end + local error_fields = { ["type"] = error_type, ["message"] = error_message, @@ -148,11 +152,16 @@ local function error_serialize(err) return error_message(err) end +local function error_is_custom(err) + return ffi.string(err._type.name) == 'CustomError' +end + local error_methods = { - ["unpack"] = error_unpack; - ["raise"] = error_raise; - ["match"] = error_match; -- Tarantool 1.6 backward compatibility - ["__serialize"] = error_serialize; + ["unpack"] = error_unpack, + ["raise"] = error_raise, + ["match"] = error_match, -- Tarantool 1.6 backward compatibility + ["is_custom"] = error_is_custom, + ["__serialize"] = error_serialize } local function error_index(err, key) @@ -181,11 +190,11 @@ local function error_concat(lhs, rhs) end local error_mt = { - __index = error_index; - __tostring = error_message; - __concat = error_concat; + __index = error_index, + __tostring = error_message, + __concat = error_concat }; -ffi.metatype('struct error', error_mt); +ffi.metatype('struct error', error_mt) return error diff --git a/test/box/misc.result b/test/box/misc.result index f432d51..20d01ef 100644 --- a/test/box/misc.result +++ b/test/box/misc.result @@ -308,6 +308,24 @@ type(err.errno) --- - nil ... +-- +-- gh-4398-expose-error-module +-- +err_custom = box.error.new(box.error.CUSTOM_ERROR, "My Custom Type", "Reason") +--- +... +err_custom.type == "CustomError" +--- +- true +... +err_custom.custom_type == "My Custom Type" +--- +- true +... +err_custom.message == "User custom error: Reason" +--- +- true +... ---------------- -- # box.stat ---------------- @@ -655,6 +673,7 @@ t; 210: box.error.SQL_PREPARE 211: box.error.WRONG_QUERY_ID 212: box.error.SEQUENCE_NOT_STARTED + 213: box.error.CUSTOM_ERROR ... test_run:cmd("setopt delimiter ''"); --- diff --git a/test/box/misc.test.lua b/test/box/misc.test.lua index e82ac2c..92b3a12 100644 --- a/test/box/misc.test.lua +++ b/test/box/misc.test.lua @@ -99,6 +99,15 @@ type(err.errno) err = box.error.new(box.error.PROC_LUA, "errno") type(err.errno) +-- +-- gh-4398-expose-error-module +-- + +err_custom = box.error.new(box.error.CUSTOM_ERROR, "My Custom Type", "Reason") +err_custom.type == "CustomError" +err_custom.custom_type == "My Custom Type" +err_custom.message == "User custom error: Reason" + ---------------- -- # box.stat ---------------- -- 2.7.4