[Tarantool-patches] [PATCH 2/2] Add the custom error type
Leonid Vasiliev
lvasiliev at tarantool.org
Thu Jan 30 19:42:37 MSK 2020
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
More information about the Tarantool-patches
mailing list