From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtpng2.m.smailru.net (smtpng2.m.smailru.net [94.100.179.3]) (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 A958146970E for ; Thu, 30 Jan 2020 19:42:46 +0300 (MSK) From: Leonid Vasiliev Date: Thu, 30 Jan 2020 19:42:37 +0300 Message-Id: <374477cbe80079687eefc9cd0d90f890dcbaec24.1580401020.git.lvasiliev@tarantool.org> In-Reply-To: References: In-Reply-To: References: Subject: [Tarantool-patches] [PATCH 2/2] 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 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 custom subtype (string value) for use it in applications. Needed for #4398 --- src/box/errcode.h | 1 + src/box/error.cc | 45 +++++++++++++++++++ src/box/error.h | 24 ++++++++++ src/box/lua/error.cc | 99 ++++++++++++++++++++++++++++++++---------- src/lua/error.lua | 26 +++++++---- test/box/misc.result | 19 ++++++++ test/box/misc.test.lua | 9 ++++ 7 files changed, 193 insertions(+), 30 deletions(-) diff --git a/src/box/errcode.h b/src/box/errcode.h index 6f6e28c6c..ac4d69dda 100644 --- a/src/box/errcode.h +++ b/src/box/errcode.h @@ -264,6 +264,7 @@ struct errcode_record { /*209 */_(ER_SESSION_SETTING_INVALID_VALUE, "Session setting %s expected a value of type %s") \ /*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_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 47dce3305..2e01f33ee 100644 --- a/src/box/error.cc +++ b/src/box/error.cc @@ -86,6 +86,16 @@ 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); + strcpy(e->errmsg, reason); + diag_add_error(&fiber()->diag, e); + return -1; +} + /* }}} */ struct rmean *rmean_error = NULL; @@ -253,3 +263,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 b8c7cf73d..fcb5a2a43 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; @@ -129,6 +132,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 +147,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 +274,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 ?: "(nil)"; + } +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 fc53a40f4..f69aaa5cb 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 @@ -144,6 +173,28 @@ luaT_error_new(lua_State *L) return luaT_error_last(L); } +static int +luaT_error_custom_type(lua_State *L) +{ + struct error *e = luaL_checkerror(L, -1); + + if (e->type == NULL || e->type->name == NULL + || strcmp(e->type->name, "CustomError")) { + lua_pushfstring(L, "%s has't a custom type",e->type->name); + return 1;; + } + + const struct method_info *custom_type_m = + type_method_by_name(e->type, "custom_type"); + assert(custom_type_m != NULL); + + const char *custom_type = exception_get_string(e, custom_type_m); + assert(custom_type != NULL); + + lua_pushstring(L, custom_type); + return 1; +} + static int luaT_error_clear(lua_State *L) { @@ -268,6 +319,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 f296ecf94..6056718cc 100644 --- a/src/lua/error.lua +++ b/src/lua/error.lua @@ -104,8 +104,13 @@ 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, + ["custom_type"] = error_custom_type, ["message"] = error_message, ["trace"] = error_trace, ["errno"] = error_errno, @@ -148,11 +153,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 +191,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 b94ba5058..96c12784c 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 ---------------- @@ -654,6 +672,7 @@ t; 209: box.error.SESSION_SETTING_INVALID_VALUE 210: box.error.SQL_PREPARE 211: box.error.WRONG_QUERY_ID + 212: box.error.CUSTOM_ERROR ... test_run:cmd("setopt delimiter ''"); --- diff --git a/test/box/misc.test.lua b/test/box/misc.test.lua index e82ac2cf9..92b3a12f3 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.17.1