Tarantool development patches archive
 help / color / mirror / Atom feed
* [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