* [Tarantool-patches] [PATCH 0/2] First part of the expose box.error task @ 2020-01-30 16:42 Leonid Vasiliev 2020-01-30 16:42 ` [Tarantool-patches] [PATCH 1/2] Add a Lua backtrace to error Leonid Vasiliev 2020-01-30 16:42 ` [Tarantool-patches] [PATCH 2/2] Add the custom error type Leonid Vasiliev 0 siblings, 2 replies; 4+ messages in thread From: Leonid Vasiliev @ 2020-01-30 16:42 UTC (permalink / raw) To: alexander.turenko; +Cc: tarantool-patches https://github.com/tarantool/tarantool/issues/4398 https://github.com/tarantool/tarantool/tree/lvasiliev/gh-4398-expose-error-module The first part of the expose box.error task includes: 1) Lua backtrace has been added to errors of the box.error module 2) Custom error type has been added. It's allows to create a new error type in applications where the custom type is a subtype of error. The next parts of the expose box.error task will include: 1) Transparent marshalling through net.box 2) Lua wrappers for the box.error interface (it has to look like a https://github.com/tarantool/errors) Leonid Vasiliev (2): error: Add a Lua backtrace to error error: Add the custom error type src/box/errcode.h | 1 + src/box/error.cc | 45 ++++++++++++++++++ src/box/error.h | 24 ++++++++++ src/box/lua/error.cc | 99 ++++++++++++++++++++++++++++++--------- src/lib/core/diag.c | 1 + src/lib/core/diag.h | 1 + src/lib/core/exception.cc | 1 + src/lua/error.c | 42 ++++++++++++++++- src/lua/error.h | 3 ++ src/lua/error.lua | 46 +++++++++++++----- test/app/fiber.result | 35 ++++++++++---- test/app/fiber.test.lua | 11 ++++- test/box/misc.result | 51 +++++++++++++++++--- test/box/misc.test.lua | 19 +++++++- 14 files changed, 326 insertions(+), 53 deletions(-) -- 2.17.1 ^ permalink raw reply [flat|nested] 4+ messages in thread
* [Tarantool-patches] [PATCH 1/2] Add a Lua backtrace to error 2020-01-30 16:42 [Tarantool-patches] [PATCH 0/2] First part of the expose box.error task Leonid Vasiliev @ 2020-01-30 16:42 ` Leonid Vasiliev 2020-01-30 16:42 ` [Tarantool-patches] [PATCH 2/2] Add the custom error type Leonid Vasiliev 1 sibling, 0 replies; 4+ messages in thread From: Leonid Vasiliev @ 2020-01-30 16:42 UTC (permalink / raw) To: alexander.turenko; +Cc: tarantool-patches In accordance with https://github.com/tarantool/tarantool/issues/4398 we want to have a Lua backtrace for the box.error Needed for #4398 --- src/lib/core/diag.c | 1 + src/lib/core/diag.h | 1 + src/lib/core/exception.cc | 1 + src/lua/error.c | 42 ++++++++++++++++++++++++++++++++++++++- src/lua/error.h | 3 +++ src/lua/error.lua | 20 ++++++++++++++----- test/app/fiber.result | 35 ++++++++++++++++++++++++-------- test/app/fiber.test.lua | 11 +++++++++- test/box/misc.result | 34 +++++++++++++++++++++++-------- test/box/misc.test.lua | 10 +++++++++- 10 files changed, 134 insertions(+), 24 deletions(-) diff --git a/src/lib/core/diag.c b/src/lib/core/diag.c index c350abb4a..d13f329ad 100644 --- a/src/lib/core/diag.c +++ b/src/lib/core/diag.c @@ -53,6 +53,7 @@ error_create(struct error *e, e->line = 0; } e->errmsg[0] = '\0'; + e->lua_bt = NULL; } struct diag * diff --git a/src/lib/core/diag.h b/src/lib/core/diag.h index f763957c2..4a39fe16b 100644 --- a/src/lib/core/diag.h +++ b/src/lib/core/diag.h @@ -84,6 +84,7 @@ struct error { char file[DIAG_FILENAME_MAX]; /* Error description. */ char errmsg[DIAG_ERRMSG_MAX]; + char *lua_bt; }; static inline void diff --git a/src/lib/core/exception.cc b/src/lib/core/exception.cc index 76dcea553..d316d9bb3 100644 --- a/src/lib/core/exception.cc +++ b/src/lib/core/exception.cc @@ -42,6 +42,7 @@ extern "C" { static void exception_destroy(struct error *e) { + free(e->lua_bt); delete (Exception *) e; } diff --git a/src/lua/error.c b/src/lua/error.c index d82e78dc4..11afe97f8 100644 --- a/src/lua/error.c +++ b/src/lua/error.c @@ -34,8 +34,44 @@ #include <fiber.h> #include "utils.h" +#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. +*/ +static char* +traceback (lua_State *L) { + int top = lua_gettop(L); + + lua_getfield(L, LUA_GLOBALSINDEX, "debug"); + if (!lua_istable(L, -1)) { + lua_settop(L, top); + return NULL; + } + lua_getfield(L, -1, "traceback"); + if (!lua_isfunction(L, -1)) { + lua_settop(L, top); + return NULL; + } + + // call debug.traceback + lua_call(L, 0, 1); + + // get result of the debug.traceback call + if (!lua_isstring(L, -1)) { + lua_settop(L, top); + return NULL; + } + + char *bt = strdup(lua_tostring(L, -1)); + lua_settop(L, top); + + return bt; +} + struct error * luaL_iserror(struct lua_State *L, int narg) { @@ -53,7 +89,7 @@ luaL_iserror(struct lua_State *L, int narg) return e; } -static struct error * +struct error * luaL_checkerror(struct lua_State *L, int narg) { struct error *error = luaL_iserror(L, narg); @@ -85,6 +121,10 @@ luaT_pusherror(struct lua_State *L, struct error *e) * then set the finalizer. */ error_ref(e); + + if (e->lua_bt == NULL) + e->lua_bt = traceback(L); + assert(CTID_CONST_STRUCT_ERROR_REF != 0); struct error **ptr = (struct error **) luaL_pushcdata(L, CTID_CONST_STRUCT_ERROR_REF); diff --git a/src/lua/error.h b/src/lua/error.h index 64fa5eba3..16cdaf7fe 100644 --- a/src/lua/error.h +++ b/src/lua/error.h @@ -65,6 +65,9 @@ luaT_pusherror(struct lua_State *L, struct error *e); struct error * luaL_iserror(struct lua_State *L, int narg); +struct error * +luaL_checkerror(struct lua_State *L, int narg); + void tarantool_lua_error_init(struct lua_State *L); diff --git a/src/lua/error.lua b/src/lua/error.lua index 7f249864a..f296ecf94 100644 --- a/src/lua/error.lua +++ b/src/lua/error.lua @@ -24,6 +24,7 @@ struct error { char _file[DIAG_FILENAME_MAX]; /* Error description. */ char _errmsg[DIAG_ERRMSG_MAX]; + char *lua_bt; }; char * @@ -83,10 +84,18 @@ local function error_trace(err) return {} end return { - { file = ffi.string(err._file), line = tonumber(err._line) }; + { file = ffi.string(err._file), line = tonumber(err._line) } } end +local function error_backtrace(err) + local result = "Backtrace is absent" + if err.lua_bt ~= ffi.nullptr then + result = ffi.string(err.lua_bt) + end + return result +end + local function error_errno(err) local e = err._saved_errno if e == 0 then @@ -96,10 +105,11 @@ local function error_errno(err) end local error_fields = { - ["type"] = error_type; - ["message"] = error_message; - ["trace"] = error_trace; - ["errno"] = error_errno; + ["type"] = error_type, + ["message"] = error_message, + ["trace"] = error_trace, + ["errno"] = error_errno, + ["bt"] = error_backtrace } local function error_unpack(err) diff --git a/test/app/fiber.result b/test/app/fiber.result index 6d9604ad8..d25a266d5 100644 --- a/test/app/fiber.result +++ b/test/app/fiber.result @@ -1036,14 +1036,33 @@ st; --- - false ... -e:unpack(); ---- -- type: ClientError - code: 1 - message: Illegal parameters, oh my - trace: - - file: '[string "function err() box.error(box.error.ILLEGAL_PA..."]' - line: 1 +unpack_res = e:unpack(); +--- +... +unpack_res['code'] == 1; +--- +- true +... +unpack_res['trace'][1]['file'] == '[string "function err()' .. + ' box.error(box.error.ILLEGAL_PA..."]'; +--- +- true +... +unpack_res['trace'][1]['line'] == 1; +--- +- true +... +unpack_res['type'] == 'ClientError'; +--- +- true +... +unpack_res['message'] == 'Illegal parameters, oh my'; +--- +- true +... +unpack_res['bt'] == e.bt; +--- +- true ... flag = false; --- diff --git a/test/app/fiber.test.lua b/test/app/fiber.test.lua index 6df210d9c..c9cbd7bee 100644 --- a/test/app/fiber.test.lua +++ b/test/app/fiber.test.lua @@ -428,7 +428,16 @@ function test1() end; st, e = test1(); st; -e:unpack(); + +unpack_res = e:unpack(); + +unpack_res['code'] == 1; +unpack_res['trace'][1]['file'] == '[string "function err()' .. + ' box.error(box.error.ILLEGAL_PA..."]'; +unpack_res['trace'][1]['line'] == 1; +unpack_res['type'] == 'ClientError'; +unpack_res['message'] == 'Illegal parameters, oh my'; +unpack_res['bt'] == e.bt; flag = false; function test2() diff --git a/test/box/misc.result b/test/box/misc.result index 5ac5e0f26..b94ba5058 100644 --- a/test/box/misc.result +++ b/test/box/misc.result @@ -125,14 +125,32 @@ e --- - Illegal parameters, bla bla ... -e:unpack() ---- -- type: ClientError - code: 1 - message: Illegal parameters, bla bla - trace: - - file: '[C]' - line: 4294967295 +unpack_res = e:unpack() +--- +... +unpack_res['code'] == 1 +--- +- true +... +unpack_res['trace'][1]['file'] == '[C]' +--- +- true +... +unpack_res['trace'][1]['line'] == 4294967295 +--- +- true +... +unpack_res['type'] == 'ClientError' +--- +- true +... +unpack_res['message'] == 'Illegal parameters, bla bla' +--- +- true +... +unpack_res['bt'] == e.bt +--- +- true ... e.type --- diff --git a/test/box/misc.test.lua b/test/box/misc.test.lua index e1c2f990f..e82ac2cf9 100644 --- a/test/box/misc.test.lua +++ b/test/box/misc.test.lua @@ -35,7 +35,15 @@ box.error(box.error.ILLEGAL_PARAMS, "bla bla") box.error() e = box.error.last() e -e:unpack() + +unpack_res = e:unpack() +unpack_res['code'] == 1 +unpack_res['trace'][1]['file'] == '[C]' +unpack_res['trace'][1]['line'] == 4294967295 +unpack_res['type'] == 'ClientError' +unpack_res['message'] == 'Illegal parameters, bla bla' +unpack_res['bt'] == e.bt + e.type e.code e.message -- 2.17.1 ^ permalink raw reply [flat|nested] 4+ messages in thread
* [Tarantool-patches] [PATCH 2/2] Add the custom error type 2020-01-30 16:42 [Tarantool-patches] [PATCH 0/2] First part of the expose box.error task Leonid Vasiliev 2020-01-30 16:42 ` [Tarantool-patches] [PATCH 1/2] Add a Lua backtrace to error Leonid Vasiliev @ 2020-01-30 16:42 ` Leonid Vasiliev 2020-01-31 7:43 ` Leonid Vasiliev 1 sibling, 1 reply; 4+ messages in thread From: Leonid Vasiliev @ 2020-01-30 16:42 UTC (permalink / raw) To: alexander.turenko; +Cc: tarantool-patches 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 ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [Tarantool-patches] [PATCH 2/2] Add the custom error type 2020-01-30 16:42 ` [Tarantool-patches] [PATCH 2/2] Add the custom error type Leonid Vasiliev @ 2020-01-31 7:43 ` Leonid Vasiliev 0 siblings, 0 replies; 4+ messages in thread From: Leonid Vasiliev @ 2020-01-31 7:43 UTC (permalink / raw) To: alexander.turenko; +Cc: tarantool-patches Hi. Small diff was added. diff --git a/src/box/error.cc b/src/box/error.cc index 2e01f33ee..f903b72ec 100644 --- a/src/box/error.cc +++ b/src/box/error.cc @@ -91,7 +91,8 @@ 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); + strncpy(e->errmsg, reason, DIAG_ERRMSG_MAX); + e->errmsg[DIAG_ERRMSG_MAX - 1] = '\0'; diag_add_error(&fiber()->diag, e); return -1; } diff --git a/src/box/error.h b/src/box/error.h index fcb5a2a43..f5e72617c 100644 --- a/src/box/error.h +++ b/src/box/error.h @@ -283,7 +283,7 @@ public: const char* custom_type() { - return m_custom_type ?: "(nil)"; + return m_custom_type; } private: /** Custom type name*/ On 1/30/20 7:42 PM, Leonid Vasiliev wrote: > 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 > ---------------- > ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2020-01-31 7:43 UTC | newest] Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2020-01-30 16:42 [Tarantool-patches] [PATCH 0/2] First part of the expose box.error task Leonid Vasiliev 2020-01-30 16:42 ` [Tarantool-patches] [PATCH 1/2] Add a Lua backtrace to error Leonid Vasiliev 2020-01-30 16:42 ` [Tarantool-patches] [PATCH 2/2] Add the custom error type Leonid Vasiliev 2020-01-31 7:43 ` Leonid Vasiliev
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox