[Tarantool-patches] [PATCH V4 6/6] error: add error MsgPack encoding
Leonid Vasiliev
lvasiliev at tarantool.org
Thu Apr 16 20:38:45 MSK 2020
Closes #4398
@TarantoolBot document
Title: msgpack format for MP_ERROR
For sent an error over IProto MP_ERROR(0x03) subtype
has been added to MsgPack MP_EXT.
Now, if session setting 'error_marshaling_enabled'
is true error will be encoded as:
```
+--------+----------+========+
| MP_EXT | MP_ERROR | MP_MAP |
+--------+----------+========+
```
At the map passes all necessary parameters (depending
from the error type) to create a copy of the error
on the client side.
The necessary fields:
- Error type
- Trace(file name and line)
- Error message
- Errno
Additional fields:
- ClientError code
- Traceback (now only Lua traceback)
- Custom type
- Access Denied object type
- Access Denied object name
- Access Denied access type
---
src/box/CMakeLists.txt | 1 +
src/box/lua/init.c | 56 ++++++
src/box/lua/mp_error.cc | 373 +++++++++++++++++++++++++++++++++++
src/box/lua/mp_error.h | 49 +++++
src/box/lua/tuple.c | 16 --
src/lib/core/mp_extension_types.h | 1 +
src/lua/error.c | 2 -
src/lua/error.h | 3 +-
src/lua/msgpack.c | 3 +
src/lua/utils.c | 6 +-
test/box-tap/extended_error.test.lua | 157 +++++++++++++++
11 files changed, 646 insertions(+), 21 deletions(-)
create mode 100644 src/box/lua/mp_error.cc
create mode 100644 src/box/lua/mp_error.h
create mode 100755 test/box-tap/extended_error.test.lua
diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index 6688303..7581078 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -151,6 +151,7 @@ add_library(box STATIC
lua/stat.c
lua/ctl.c
lua/error.cc
+ lua/mp_error.cc
lua/session.c
lua/net_box.c
lua/xlog.c
diff --git a/src/box/lua/init.c b/src/box/lua/init.c
index 63e8b82..de09389 100644
--- a/src/box/lua/init.c
+++ b/src/box/lua/init.c
@@ -36,11 +36,13 @@
#include "lua/utils.h" /* luaT_error() */
#include "lua/trigger.h"
+#include "lua/msgpack.h"
#include "box/box.h"
#include "box/txn.h"
#include "box/func.h"
#include "box/vclock.h"
+#include "box/session.h"
#include "box/lua/error.h"
#include "box/lua/tuple.h"
@@ -62,6 +64,9 @@
#include "box/lua/execute.h"
#include "box/lua/key_def.h"
#include "box/lua/merger.h"
+#include "box/lua/mp_error.h"
+
+#include "mpstream/mpstream.h"
static uint32_t CTID_STRUCT_TXN_SAVEPOINT_PTR = 0;
@@ -386,6 +391,54 @@ static const struct luaL_Reg boxlib_backup[] = {
{NULL, NULL}
};
+/**
+ * A MsgPack extensions handler that supports tuples and errors encode.
+ */
+static enum mp_type
+luamp_encode_extension_box(struct lua_State *L, int idx,
+ struct mpstream *stream)
+{
+ struct tuple *tuple = luaT_istuple(L, idx);
+ if (tuple != NULL) {
+ tuple_to_mpstream(tuple, stream);
+ return MP_ARRAY;
+ }
+
+ if (current_session()->meta.serializer_opts.error_marshaling_enabled) {
+ struct error *err = luaL_iserror(L, idx);
+ if (err != NULL)
+ error_to_mpstream(err, stream);
+ }
+
+ return MP_EXT;
+}
+
+/**
+ * A MsgPack extensions handler that supports errors decode.
+ */
+static void
+luamp_decode_extension_box(struct lua_State *L, const char **data)
+{
+ assert(mp_typeof(**data) == MP_EXT);
+ int8_t ext_type;
+ uint32_t len = mp_decode_extl(data, &ext_type);
+
+ if(ext_type != MP_ERROR) {
+ luaL_error(L, "Unsuported MsgPack extension type: %u",
+ ext_type);
+ return;
+ }
+
+ struct error *err = error_unpack(data, len);
+ if (err == NULL) {
+ luaL_error(L, "Can not parse an error from MsgPack");
+ return;
+ }
+
+ luaT_pusherror(L, err);
+ return;
+}
+
#include "say.h"
void
@@ -426,6 +479,9 @@ box_lua_init(struct lua_State *L)
luaopen_merger(L);
lua_pop(L, 1);
+ luamp_set_encode_extension(luamp_encode_extension_box);
+ luamp_set_decode_extension(luamp_decode_extension_box);
+
/* Load Lua extension */
for (const char **s = lua_sources; *s; s += 2) {
const char *modname = *s;
diff --git a/src/box/lua/mp_error.cc b/src/box/lua/mp_error.cc
new file mode 100644
index 0000000..2e15206
--- /dev/null
+++ b/src/box/lua/mp_error.cc
@@ -0,0 +1,373 @@
+/*
+ * Copyright 2010-2020, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "box/lua/mp_error.h"
+#include "box/error.h"
+#include "mpstream/mpstream.h"
+#include "msgpuck.h"
+#include "mp_extension_types.h"
+
+/**
+ * Keys used for encode/decode MP_ERROR.
+ */
+enum mp_error_details {
+ MP_ERROR_DET_TYPE,
+ MP_ERROR_DET_FILE,
+ MP_ERROR_DET_LINE,
+ MP_ERROR_DET_REASON,
+ MP_ERROR_DET_ERRNO,
+ MP_ERROR_DET_CODE,
+ MP_ERROR_DET_CUSTOM_TYPE,
+ MP_ERROR_DET_AD_OBJ_TYPE,
+ MP_ERROR_DET_AD_OBJ_NAME,
+ MP_ERROR_DET_AD_ACCESS_TYPE
+};
+
+/**
+ * The structure is used for storing parameters
+ * during decoding MP_ERROR.
+ */
+struct mp_error {
+ uint32_t error_code;
+ uint32_t line;
+ uint32_t saved_errno;
+ char *error_type;
+ char *file;
+ char *reason;
+ char *custom_type;
+ char *ad_obj_type;
+ char *ad_obj_name;
+ char *ad_access_type;
+};
+
+static void
+mp_error_init(struct mp_error *mp_error)
+{
+ mp_error->error_code = 0;
+ mp_error->line = 0;
+ mp_error->saved_errno = 0;
+ mp_error->error_type = NULL;
+ mp_error->file = NULL;
+ mp_error->reason = NULL;
+ mp_error->custom_type = NULL;
+ mp_error->ad_obj_type = NULL;
+ mp_error->ad_obj_name = NULL;
+ mp_error->ad_access_type = NULL;
+}
+
+static void
+mp_error_cleanup(struct mp_error *mp_error)
+{
+ free(mp_error->error_type);
+ free(mp_error->file);
+ free(mp_error->reason);
+ free(mp_error->custom_type);
+ free(mp_error->ad_obj_type);
+ free(mp_error->ad_obj_name);
+ free(mp_error->ad_access_type);
+}
+
+void
+error_to_mpstream(struct error *error, struct mpstream *stream)
+{
+ uint32_t errcode = 0;
+ const char *custom_type = NULL;
+ const char *ad_obj_type = NULL;
+ const char *ad_obj_name = NULL;
+ const char *ad_access_type = NULL;
+
+ bool is_client = false;
+ bool is_custom = false;
+ bool is_access_denied = false;
+
+ if (strcmp(error->type->name, "ClientError") == 0) {
+ is_client = true;
+ } else if (strcmp(error->type->name, "CustomError") == 0) {
+ is_custom = true;
+ } else if (strcmp(error->type->name, "AccessDeniedError") == 0) {
+ is_access_denied = true;
+ }
+
+ uint32_t details_num = 5;
+ uint32_t data_size = 0;
+
+ data_size += mp_sizeof_uint(MP_ERROR_DET_TYPE);
+ data_size += mp_sizeof_str(strlen(error->type->name));
+ data_size += mp_sizeof_uint(MP_ERROR_DET_LINE);
+ data_size += mp_sizeof_uint(error->line);
+ data_size += mp_sizeof_uint(MP_ERROR_DET_FILE);
+ data_size += mp_sizeof_str(strlen(error->file));
+ data_size += mp_sizeof_uint(MP_ERROR_DET_REASON);
+ data_size += mp_sizeof_str(strlen(error->errmsg));
+ data_size += mp_sizeof_uint(MP_ERROR_DET_ERRNO);
+ data_size += mp_sizeof_uint(error->saved_errno);
+
+ if (is_client || is_access_denied || is_custom) {
+ ++details_num;
+ errcode = box_error_code(error);
+ data_size += mp_sizeof_uint(MP_ERROR_DET_CODE);
+ data_size += mp_sizeof_uint(errcode);
+ if (is_custom) {
+ ++details_num;
+ data_size += mp_sizeof_uint(MP_ERROR_DET_CUSTOM_TYPE);
+ custom_type = box_error_custom_type(error);
+ data_size += mp_sizeof_str(strlen(custom_type));
+ } else if (is_access_denied) {
+ AccessDeniedError *ad_err = type_cast(AccessDeniedError,
+ error);
+ details_num += 3;
+ ad_obj_type = ad_err->object_type();
+ ad_obj_name = ad_err->object_name();
+ ad_access_type = ad_err->access_type();
+ data_size += mp_sizeof_uint(MP_ERROR_DET_AD_OBJ_TYPE);
+ data_size += mp_sizeof_str(strlen(ad_obj_type));
+ data_size += mp_sizeof_uint(MP_ERROR_DET_AD_OBJ_NAME);
+ data_size += mp_sizeof_str(strlen(ad_obj_name));
+ data_size += mp_sizeof_uint(MP_ERROR_DET_AD_ACCESS_TYPE);
+ data_size += mp_sizeof_str(strlen(ad_access_type));
+ }
+ }
+
+ data_size += mp_sizeof_map(details_num);
+ uint32_t data_size_ext = mp_sizeof_ext(data_size);
+ char *ptr = mpstream_reserve(stream, data_size_ext);
+
+ char *data = ptr;
+ data = mp_encode_extl(data, MP_ERROR, data_size);
+ data = mp_encode_map(data, details_num);
+ data = mp_encode_uint(data, MP_ERROR_DET_TYPE);
+ data = mp_encode_str(data, error->type->name,
+ strlen(error->type->name));
+ data = mp_encode_uint(data, MP_ERROR_DET_LINE);
+ data = mp_encode_uint(data, error->line);
+ data = mp_encode_uint(data, MP_ERROR_DET_FILE);
+ data = mp_encode_str(data, error->file, strlen(error->file));
+ data = mp_encode_uint(data, MP_ERROR_DET_REASON);
+ data = mp_encode_str(data, error->errmsg, strlen(error->errmsg));
+ data = mp_encode_uint(data, MP_ERROR_DET_ERRNO);
+ data = mp_encode_uint(data, error->saved_errno);
+
+ if (is_client || is_access_denied || is_custom) {
+ data = mp_encode_uint(data, MP_ERROR_DET_CODE);
+ data = mp_encode_uint(data, errcode);
+ if (is_custom) {
+ data = mp_encode_uint(data, MP_ERROR_DET_CUSTOM_TYPE);
+ data = mp_encode_str(data, custom_type,
+ strlen(custom_type));
+ } else if (is_access_denied) {
+ data = mp_encode_uint(data, MP_ERROR_DET_AD_OBJ_TYPE);
+ data = mp_encode_str(data, ad_obj_type,
+ strlen(ad_obj_type));
+ data = mp_encode_uint(data, MP_ERROR_DET_AD_OBJ_NAME);
+ data = mp_encode_str(data, ad_obj_name,
+ strlen(ad_obj_name));
+ data = mp_encode_uint(data, MP_ERROR_DET_AD_ACCESS_TYPE);
+ data = mp_encode_str(data, ad_access_type,
+ strlen(ad_access_type));
+ }
+ }
+
+ assert(data == ptr + data_size_ext);
+ mpstream_advance(stream, data_size_ext);
+}
+
+static struct error *
+build_error(struct mp_error *mp_error)
+{
+ /*
+ * For create an error the "raw" constructor using
+ * because OOM error must be throwed in OOM case.
+ * Bulders returns a pointer to the static OOM error
+ * in OOM case.
+ */
+ struct error *err = NULL;
+
+ if (strcmp(mp_error->error_type, "ClientError") == 0) {
+ ClientError *e = new ClientError(mp_error->file, mp_error->line,
+ ER_UNKNOWN);
+ e->m_errcode = mp_error->error_code;
+ err = e;
+ } else if (strcmp(mp_error->error_type, "CustomError") == 0) {
+ err = new CustomError(mp_error->file, mp_error->line,
+ mp_error->custom_type);
+ } else if (strcmp(mp_error->error_type, "AccessDeniedError") == 0) {
+ err = new AccessDeniedError(mp_error->file, mp_error->line,
+ mp_error->ad_access_type,
+ mp_error->ad_obj_type,
+ mp_error->ad_obj_name, "");
+ } else if (strcmp(mp_error->error_type, "XlogError") == 0) {
+ err = new XlogError(&type_XlogError, mp_error->file,
+ mp_error->line);
+ error_format_msg(err, "%s", mp_error->reason);
+ } else if (strcmp(mp_error->error_type, "XlogGapError") == 0) {
+ err = new XlogGapError(mp_error->file, mp_error->line,
+ mp_error->reason);
+ } else if (strcmp(mp_error->error_type, "SystemError") == 0) {
+ err = new SystemError(mp_error->file, mp_error->line,
+ "%s", mp_error->reason);
+ } else if (strcmp(mp_error->error_type, "SocketError") == 0) {
+ err = new SocketError(mp_error->file, mp_error->line, "", "");
+ error_format_msg(err, "%s", mp_error->reason);
+ } else if (strcmp(mp_error->error_type, "OutOfMemory") == 0) {
+ err = new OutOfMemory(mp_error->file, mp_error->line,
+ 0, "", "");
+ error_format_msg(err, "%s", mp_error->reason);
+ } else if (strcmp(mp_error->error_type, "TimedOut") == 0) {
+ err = new TimedOut(mp_error->file, mp_error->line);
+ } else if (strcmp(mp_error->error_type, "ChannelIsClosed") == 0) {
+ err = new ChannelIsClosed(mp_error->file, mp_error->line);
+ } else if (strcmp(mp_error->error_type, "FiberIsCancelled") == 0) {
+ err = new FiberIsCancelled(mp_error->file, mp_error->line);
+ } else if (strcmp(mp_error->error_type, "LuajitError") == 0) {
+ err = new LuajitError(mp_error->file, mp_error->line,
+ mp_error->reason);
+ } else if (strcmp(mp_error->error_type, "IllegalParams") == 0) {
+ err = new IllegalParams(mp_error->file, mp_error->line,
+ "%s", mp_error->reason);
+ } else if (strcmp(mp_error->error_type, "CollationError") == 0) {
+ err = new CollationError(mp_error->file, mp_error->line,
+ "%s", mp_error->reason);
+ } else if (strcmp(mp_error->error_type, "SwimError") == 0) {
+ err = new SwimError(mp_error->file, mp_error->line,
+ "%s", mp_error->reason);
+ } else if (strcmp(mp_error->error_type, "CryptoError") == 0) {
+ err = new CryptoError(mp_error->file, mp_error->line,
+ "%s", mp_error->reason);
+ }
+
+ if (err) {
+ err->saved_errno = mp_error->saved_errno;
+ error_format_msg(err, "%s", mp_error->reason);
+ }
+
+ return err;
+}
+
+struct error *
+error_unpack(const char **data, uint32_t len)
+{
+ const char *end = *data + len;
+ if (mp_typeof(**data) != MP_MAP) {
+ diag_set(ClientError, ER_INVALID_MSGPACK,
+ "Invalid MP_ERROR format");
+ return NULL;
+ }
+
+ struct mp_error mp_err;
+ mp_error_init(&mp_err);
+
+ uint32_t map_size = mp_decode_map(data);
+
+ struct error *err = NULL;
+ for (uint32_t i = 0; i < map_size; ++i) {
+ if (mp_typeof(**data) != MP_UINT) {
+ diag_set(ClientError, ER_INVALID_MSGPACK,
+ "Invalid MP_ERROR MsgPack format");
+ return NULL;
+ }
+
+ uint8_t key = mp_decode_uint(data);
+ const char *str;
+ uint32_t str_len;
+ switch(key) {
+ case MP_ERROR_DET_TYPE:
+ if (mp_typeof(**data) != MP_STR)
+ goto error;
+ str = mp_decode_str(data, &str_len);
+ mp_err.error_type = strndup(str, str_len);
+ break;
+ case MP_ERROR_DET_FILE:
+ if (mp_typeof(**data) != MP_STR)
+ goto error;
+ str = mp_decode_str(data, &str_len);
+ mp_err.file = strndup(str, str_len);
+ break;
+ case MP_ERROR_DET_LINE:
+ if (mp_typeof(**data) != MP_UINT)
+ goto error;
+ mp_err.line = mp_decode_uint(data);
+ break;
+ case MP_ERROR_DET_REASON:
+ if (mp_typeof(**data) != MP_STR)
+ goto error;
+ str = mp_decode_str(data, &str_len);
+ mp_err.reason = strndup(str, str_len);
+ break;
+ case MP_ERROR_DET_ERRNO:
+ if (mp_typeof(**data) != MP_UINT)
+ goto error;
+ mp_err.saved_errno = mp_decode_uint(data);
+ break;
+ case MP_ERROR_DET_CODE:
+ if (mp_typeof(**data) != MP_UINT)
+ goto error;
+ mp_err.error_code = mp_decode_uint(data);
+ break;
+ case MP_ERROR_DET_CUSTOM_TYPE:
+ if (mp_typeof(**data) != MP_STR)
+ goto error;
+ str = mp_decode_str(data, &str_len);
+ mp_err.custom_type = strndup(str, str_len);
+ break;
+ case MP_ERROR_DET_AD_OBJ_TYPE:
+ if (mp_typeof(**data) != MP_STR)
+ goto error;
+ str = mp_decode_str(data, &str_len);
+ mp_err.ad_obj_type = strndup(str, str_len);
+ break;
+ case MP_ERROR_DET_AD_OBJ_NAME:
+ if (mp_typeof(**data) != MP_STR)
+ goto error;
+ str = mp_decode_str(data, &str_len);
+ mp_err.ad_obj_name = strndup(str, str_len);
+ break;
+ case MP_ERROR_DET_AD_ACCESS_TYPE:
+ if (mp_typeof(**data) != MP_STR)
+ goto error;
+ str = mp_decode_str(data, &str_len);
+ mp_err.ad_access_type = strndup(str, str_len);
+ break;
+ default:
+ mp_next(data);
+ }
+ }
+
+ (void)end;
+ assert(*data == end);
+
+ err = build_error(&mp_err);
+ mp_error_cleanup(&mp_err);
+ return err;
+
+error:
+ diag_set(ClientError, ER_INVALID_MSGPACK,
+ "Invalid MP_ERROR MsgPack format");
+ return NULL;
+}
diff --git a/src/box/lua/mp_error.h b/src/box/lua/mp_error.h
new file mode 100644
index 0000000..2aa5cb9
--- /dev/null
+++ b/src/box/lua/mp_error.h
@@ -0,0 +1,49 @@
+#pragma once
+/*
+ * Copyright 2010-2020, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+#include <stdint.h>
+
+struct mpstream;
+
+void
+error_to_mpstream(struct error *error, struct mpstream *stream);
+
+struct error *
+error_unpack(const char **data, uint32_t len);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
index aba906d..03b4b8a 100644
--- a/src/box/lua/tuple.c
+++ b/src/box/lua/tuple.c
@@ -290,20 +290,6 @@ tuple_to_mpstream(struct tuple *tuple, struct mpstream *stream)
mpstream_advance(stream, bsize);
}
-/* A MsgPack extensions handler that supports tuples */
-static enum mp_type
-luamp_encode_extension_box(struct lua_State *L, int idx,
- struct mpstream *stream)
-{
- struct tuple *tuple = luaT_istuple(L, idx);
- if (tuple != NULL) {
- tuple_to_mpstream(tuple, stream);
- return MP_ARRAY;
- }
-
- return MP_EXT;
-}
-
/**
* Convert a tuple into lua table. Named fields are stored as
* {name = value} pairs. Not named fields are stored as
@@ -582,8 +568,6 @@ box_lua_tuple_init(struct lua_State *L)
luaL_register_module(L, tuplelib_name, lbox_tuplelib);
lua_pop(L, 1);
- luamp_set_encode_extension(luamp_encode_extension_box);
-
tuple_serializer_update_options();
trigger_create(&tuple_serializer.update_trigger,
on_msgpack_serializer_update, NULL, NULL);
diff --git a/src/lib/core/mp_extension_types.h b/src/lib/core/mp_extension_types.h
index 7d42f21..e3ff9f5 100644
--- a/src/lib/core/mp_extension_types.h
+++ b/src/lib/core/mp_extension_types.h
@@ -43,6 +43,7 @@ enum mp_extension_type {
MP_UNKNOWN_EXTENSION = 0,
MP_DECIMAL = 1,
MP_UUID = 2,
+ MP_ERROR = 3,
mp_extension_type_MAX,
};
diff --git a/src/lua/error.c b/src/lua/error.c
index 18a990a..4a9ffc8 100644
--- a/src/lua/error.c
+++ b/src/lua/error.c
@@ -34,8 +34,6 @@
#include <fiber.h>
#include "utils.h"
-static int CTID_CONST_STRUCT_ERROR_REF = 0;
-
struct error *
luaL_iserror(struct lua_State *L, int narg)
{
diff --git a/src/lua/error.h b/src/lua/error.h
index 16cdaf7..4e4dc04 100644
--- a/src/lua/error.h
+++ b/src/lua/error.h
@@ -37,7 +37,6 @@
extern "C" {
#endif /* defined(__cplusplus) */
-
/** \cond public */
struct error;
@@ -62,6 +61,8 @@ void
luaT_pusherror(struct lua_State *L, struct error *e);
/** \endcond public */
+extern uint32_t CTID_CONST_STRUCT_ERROR_REF;
+
struct error *
luaL_iserror(struct lua_State *L, int narg);
diff --git a/src/lua/msgpack.c b/src/lua/msgpack.c
index b14361f..91560ef 100644
--- a/src/lua/msgpack.c
+++ b/src/lua/msgpack.c
@@ -195,6 +195,8 @@ restart: /* used by MP_EXT of unidentified subtype */
case MP_UUID:
mpstream_encode_uuid(stream, field->uuidval);
break;
+ case MP_ERROR:
+ return luamp_encode_extension(L, top, stream);
default:
/* Run trigger if type can't be encoded */
type = luamp_encode_extension(L, top, stream);
@@ -237,6 +239,7 @@ luamp_encode(struct lua_State *L, struct luaL_serializer *cfg,
return top_type;
}
+
void
luamp_decode(struct lua_State *L, struct luaL_serializer *cfg,
const char **data)
diff --git a/src/lua/utils.c b/src/lua/utils.c
index bd320f3..aaa8a3f 100644
--- a/src/lua/utils.c
+++ b/src/lua/utils.c
@@ -47,6 +47,7 @@ static uint32_t CTID_CHAR_PTR;
static uint32_t CTID_CONST_CHAR_PTR;
static uint32_t CTID_UUID;
uint32_t CTID_DECIMAL;
+uint32_t CTID_CONST_STRUCT_ERROR_REF;
void *
@@ -650,8 +651,6 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg,
struct serializer_opts *opts, int index,
struct luaL_field *field)
{
- /* opts will be used for encode MP_ERROR in the future */
- (void)opts;
if (index < 0)
index = lua_gettop(L) + index + 1;
@@ -759,6 +758,9 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg,
} else if (cd->ctypeid == CTID_UUID) {
field->ext_type = MP_UUID;
field->uuidval = (struct tt_uuid *) cdata;
+ } else if (cd->ctypeid == CTID_CONST_STRUCT_ERROR_REF &&
+ opts && opts->error_marshaling_enabled) {
+ field->ext_type = MP_ERROR;
} else {
field->ext_type = MP_UNKNOWN_EXTENSION;
}
diff --git a/test/box-tap/extended_error.test.lua b/test/box-tap/extended_error.test.lua
new file mode 100755
index 0000000..eb86bdc
--- /dev/null
+++ b/test/box-tap/extended_error.test.lua
@@ -0,0 +1,157 @@
+#! /usr/bin/env tarantool
+
+local netbox = require('net.box')
+local os = require('os')
+local tap = require('tap')
+local uri = require('uri')
+
+local test = tap.test('check an extended error')
+test:plan(4)
+
+
+function error_func()
+ box.error(box.error.PROC_LUA, "An old good error")
+end
+
+function custom_error_func()
+ box.error("My Custom Type", "A modern custom error")
+end
+
+function custom_error_func_2()
+ local err = box.error.new("My Custom Type", "A modern custom error")
+ return err
+end
+
+local function version_at_least(peer_version_str, major, minor, patch)
+ local major_p, minor_p, patch_p = string.match(peer_version_str,
+ "(%d)%.(%d)%.(%d)")
+ major_p = tonumber(major_p)
+ minor_p = tonumber(minor_p)
+ patch_p = tonumber(patch_p)
+
+ if major_p < major
+ or (major_p == major and minor_p < minor)
+ or (major_p == major and minor_p == minor and patch_p < patch) then
+ return false
+ end
+
+ return true
+end
+
+local function grant_func(user, name)
+ box.schema.func.create(name, {if_not_exists = true})
+ box.schema.user.grant(user, 'execute', 'function', name, {
+ if_not_exists = true
+ })
+end
+
+local function check_error(err, check_list)
+ if type(check_list) ~= 'table' then
+ return false
+ end
+
+ for k, v in pairs(check_list) do
+ if k == 'type' then
+ if err.base_type ~= v then
+ return false
+ end
+ elseif k == 'custom_type' then
+ if err.type ~= v then
+ return false
+ end
+ elseif k == 'message' then
+ if err.message ~= v then
+ return false
+ end
+ elseif k == 'trace' then
+ local trace = "File " .. err.trace[1]['file']
+ .. "\nLine " .. err.trace[1]['line']
+ if not string.match(trace, v) then
+ return false
+ end
+ elseif k == 'errno' then
+ if err.errno ~= v then
+ return false
+ end
+ elseif k == 'is_custom' then
+ if (err.base_type == 'CustomError') ~= v then
+ return false
+ end
+ else
+ return false
+ end
+ end
+
+ return true
+end
+
+local function test_old_transmission(host, port)
+ grant_func('guest', 'error_func')
+ grant_func('guest', 'custom_error_func_2')
+
+ local connection = netbox.connect(host, port)
+ local _, err = pcall(connection.call, connection, 'error_func')
+ local err_2 = connection:call('custom_error_func_2')
+
+
+ local check_list = {
+ type = 'ClientError',
+ message = 'An old good error',
+ trace = '^File builtin/box/net_box.lua\nLine %d+$',
+ is_custom = false
+ }
+
+ local check_result = check_error(err, check_list)
+ local check_result_2 = type(err_2) == 'string' and
+ err_2 == 'A modern custom error'
+
+ test:ok(check_result, 'Check the old transmission type(IPROTO_ERROR)')
+ test:ok(check_result_2, 'Check the old transmission type(IPROTO_OK)')
+
+ connection:close()
+end
+
+local function test_extended_transmission(host, port)
+ grant_func('guest', 'custom_error_func')
+ grant_func('guest', 'custom_error_func_2')
+ box.schema.user.grant('guest','read,write', 'space', '_session_settings')
+
+ local connection = netbox.connect(host, port)
+ connection.space._session_settings:update('error_marshaling_enabled',
+ {{'=', 2, true}})
+ local _, err = pcall(connection.call, connection, 'custom_error_func')
+ local err_2 = connection:call('custom_error_func_2')
+
+ local check_list = {
+ type = 'CustomError',
+ custom_type = 'My Custom Type',
+ message = 'A modern custom error',
+ trace = '^File builtin/box/net_box.lua\nLine %d+$',
+ is_custom = true
+ }
+
+ local check_list_2 = {
+ type = 'CustomError',
+ custom_type = 'My Custom Type',
+ message = 'A modern custom error',
+ trace = '.*extended_error.test.lua\nLine 21$',
+ is_custom = true
+ }
+ local check_result = check_error(err, check_list)
+ local check_result_2 = check_error(err_2, check_list_2)
+ test:ok(check_result, 'Check the extended transmission type(IPROTO_ERROR)')
+ test:ok(check_result_2, 'Check the extended transmission type(IPROTO_OK)')
+
+ connection:close()
+end
+
+box.cfg{
+ listen = os.getenv('LISTEN')
+}
+local host= uri.parse(box.cfg.listen).host or 'localhost'
+local port = uri.parse(box.cfg.listen).service
+
+test_extended_transmission(host, port)
+test_old_transmission(host, port)
+
+os.exit(test:check() and 0 or 1)
--
2.7.4
More information about the Tarantool-patches
mailing list