<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">15 авг. 2019 г., в 1:23, Konstantin Osipov <<a href="mailto:kostja@tarantool.org" class="">kostja@tarantool.org</a>> написал(а):</div><br class="Apple-interchange-newline"><div class=""><div class="">* Serge Petrenko <<a href="mailto:sergepetrenko@tarantool.org" class="">sergepetrenko@tarantool.org</a>> [19/08/13 16:44]:<br class=""><blockquote type="cite" class="">Sorry for the confusion. I guess mp_field_type is just a bad name.<br class="">It has nothing to do with enum field_type. It just represents all the<br class="">messagepack types, including the extension types.<br class=""></blockquote><br class="">No it doesn't. Please read here what messagepack types are:<br class=""><a href="https://en.wikipedia.org/wiki/MessagePack" class="">https://en.wikipedia.org/wiki/MessagePack</a><br class=""><br class="">It stores messagepack format codes and extension types. This is<br class="">confusing.<br class=""><br class=""><blockquote type="cite" class="">It helps to eliminate a switch for extension type every time we get an<br class="">MP_EXT. So, it only deals with mp_type and mp_user_type.<br class=""></blockquote><br class="">I get that.<br class=""><br class=""><blockquote type="cite" class=""><blockquote type="cite" class="">Look at mp_type when you can, and when you get MP_EXT or is<br class="">otherwise confused, look at field_type?<br class=""></blockquote><br class="">That’s what I’ve done in previous revision, but Vladimir didn’t like it.<br class="">And having implemented both variants, I agree that this enum simplifies<br class="">the code quite a lot. This is especially noticeable with patch 6.<br class=""><br class="">So, my proposal is to change the name to something better.<br class="">value_type? mp_value_type maybe?<br class=""></blockquote><br class="">Could you please show me the previous version of the patch?<br class=""></div></div></blockquote><div><br class=""></div><div>Here’s the old version of the patch on encoding/decoding decimals in lua.</div><div>This patch is based on top of the one introducing mp_encode/decode_decimal,</div><div>they’re not squashed yet in this revision.</div><div><br class=""></div><div>=================================================</div><div>It is now possible to insert decimals into spaces, but only into<br class="">unindexed fields.<br class=""><br class="">Part-of #4333<br class="">---<br class="">extra/exports | 4 ++<br class="">src/lib/core/decimal.c | 10 +++-<br class="">src/lib/core/mp_user_types.h | 3 +-<br class="">src/lib/core/mpstream.c | 11 ++++<br class="">src/lib/core/mpstream.h | 4 ++<br class="">src/lua/decimal.c | 10 ++--<br class="">src/lua/decimal.h | 5 ++<br class="">src/lua/msgpack.c | 52 +++++++++++++----<br class="">src/lua/msgpackffi.lua | 33 +++++++++++<br class="">src/lua/utils.c | 16 ++++-<br class="">src/lua/utils.h | 8 ++-<br class="">test/app/decimal.result | 106 +++++++++++++++++-----------------<br class="">test/app/msgpack.result | 41 +++++++++++++<br class="">test/app/msgpack.test.lua | 15 +++++<br class="">third_party/lua-yaml/<a href="http://lyaml.cc" class="">lyaml.cc</a> | 6 ++<br class="">15 files changed, 248 insertions(+), 76 deletions(-)<br class=""><br class="">diff --git a/extra/exports b/extra/exports<br class="">index b8c42c0df..7b84a1452 100644<br class="">--- a/extra/exports<br class="">+++ b/extra/exports<br class="">@@ -62,8 +62,12 @@ PMurHash32_Result<br class="">crc32_calc<br class="">mp_encode_double<br class="">mp_encode_float<br class="">+mp_encode_decimal<br class="">mp_decode_double<br class="">mp_decode_float<br class="">+mp_decode_extl<br class="">+mp_sizeof_decimal<br class="">+decimal_unpack<br class=""><br class="">log_type<br class="">say_set_log_level<br class="">diff --git a/src/lib/core/decimal.c b/src/lib/core/decimal.c<br class="">index 1423ae418..2c69a773c 100644<br class="">--- a/src/lib/core/decimal.c<br class="">+++ b/src/lib/core/decimal.c<br class="">@@ -33,6 +33,7 @@<br class="">#include "third_party/decNumber/decContext.h"<br class="">#include "third_party/decNumber/decPacked.h"<br class="">#include "lib/core/tt_static.h"<br class="">+#include "lib/msgpuck/msgpuck.h"<br class="">#include <stddef.h><br class="">#include <stdlib.h><br class="">#include <float.h> /* DBL_DIG */<br class="">@@ -312,12 +313,15 @@ char *<br class="">decimal_pack(char *data, const decimal_t *dec)<br class="">{<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>uint32_t len = decimal_len(dec);<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span>*data++ = decimal_scale(dec);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>/* reserve space for resulting scale */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>char *svp = data++;<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>len--;<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>int32_t scale;<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>char *tmp = (char *)decPackedFromNumber((uint8_t *)data, len, &scale, dec);<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>assert(tmp == data);<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span>assert(scale == (int32_t)decimal_scale(dec));<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>/* scale may be negative, when exponent is > 0 */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>assert(scale == (int32_t)decimal_scale(dec) || scale < 0);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>mp_store_u8(svp, (int8_t)scale);<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>(void)tmp;<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>data += len;<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>return data;<br class="">@@ -326,7 +330,7 @@ decimal_pack(char *data, const decimal_t *dec)<br class="">decimal_t *<br class="">decimal_unpack(const char **data, uint32_t len, decimal_t *dec)<br class="">{<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span>int32_t scale = *((*data)++);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>int32_t scale = (int8_t)mp_load_u8(data);<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>len--;<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>decimal_t *res = decPackedToNumber((uint8_t *)*data, len, &scale, dec);<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>if (res)<br class="">diff --git a/src/lib/core/mp_user_types.h b/src/lib/core/mp_user_types.h<br class="">index 9158b40d3..8211e3e79 100644<br class="">--- a/src/lib/core/mp_user_types.h<br class="">+++ b/src/lib/core/mp_user_types.h<br class="">@@ -32,7 +32,8 @@<br class=""> */<br class=""><br class="">enum mp_user_type {<br class="">- MP_DECIMAL = 0<br class="">+ MP_UNKNOWN = 0,<br class="">+ MP_DECIMAL = 1<br class="">};<br class=""><br class="">#endif<br class="">diff --git a/src/lib/core/mpstream.c b/src/lib/core/mpstream.c<br class="">index 8b7276ab1..a46b7962b 100644<br class="">--- a/src/lib/core/mpstream.c<br class="">+++ b/src/lib/core/mpstream.c<br class="">@@ -33,6 +33,7 @@<br class="">#include <assert.h><br class="">#include <stdint.h><br class="">#include "msgpuck.h"<br class="">+#include "mp_decimal.h"<br class=""><br class="">void<br class="">mpstream_reserve_slow(struct mpstream *stream, size_t size)<br class="">@@ -186,6 +187,16 @@ mpstream_encode_binl(struct mpstream *stream, uint32_t len)<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>mpstream_advance(stream, pos - data);<br class="">}<br class=""><br class="">+void<br class="">+mpstream_encode_decimal(struct mpstream *stream, decimal_t *val)<br class="">+{<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>char *data = mpstream_reserve(stream, mp_sizeof_decimal(val));<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>if (data == NULL)<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>char *pos = mp_encode_decimal(data, val);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>mpstream_advance(stream, pos - data);<br class="">+}<br class="">+<br class="">void<br class="">mpstream_memcpy(struct mpstream *stream, const void *src, uint32_t n)<br class="">{<br class="">diff --git a/src/lib/core/mpstream.h b/src/lib/core/mpstream.h<br class="">index 69fa76f7f..44af28cb5 100644<br class="">--- a/src/lib/core/mpstream.h<br class="">+++ b/src/lib/core/mpstream.h<br class="">@@ -32,6 +32,7 @@<br class=""> */<br class=""><br class="">#include "diag.h"<br class="">+#include "decimal.h"<br class=""><br class="">#if defined(__cplusplus)<br class="">extern "C" {<br class="">@@ -136,6 +137,9 @@ mpstream_encode_bool(struct mpstream *stream, bool val);<br class="">void<br class="">mpstream_encode_binl(struct mpstream *stream, uint32_t len);<br class=""><br class="">+void<br class="">+mpstream_encode_decimal(struct mpstream *stream, decimal_t *val);<br class="">+<br class="">/** Copies n bytes from memory area src to stream. */<br class="">void<br class="">mpstream_memcpy(struct mpstream *stream, const void *src, uint32_t n);<br class="">diff --git a/src/lua/decimal.c b/src/lua/decimal.c<br class="">index e548cdb9d..ab8d85f75 100644<br class="">--- a/src/lua/decimal.c<br class="">+++ b/src/lua/decimal.c<br class="">@@ -31,7 +31,7 @@<br class=""><br class="">#include "lua/decimal.h"<br class="">#include "lib/core/decimal.h"<br class="">-#include "lua/utils.h"<br class="">+#include "lua/utils.h" /* CTID_DECIMAL, ... */<br class=""><br class="">#include <lua.h><br class="">#include <lauxlib.h><br class="">@@ -69,16 +69,18 @@ ldecimal_##name(struct lua_State *L) {<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>\<br class="">static int<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>\<br class="">ldecimal_##name(struct lua_State *L) {<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>\<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>assert(lua_gettop(L) == 2);<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>\<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>if (lua_isnil(L, 1) || lua_isnil(L, 2)) {<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>\<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>lua_pushboolean(L, false);<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>\<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return 1;<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>\<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>}<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>\<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>decimal_t *lhs = lua_todecimal(L, 1);<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>\<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>decimal_t *rhs = lua_todecimal(L, 2);<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>\<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>lua_pushboolean(L, decimal_compare(lhs, rhs) cmp 0);<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>\<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>return 1;<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>\<br class="">}<br class=""><br class="">-static uint32_t CTID_DECIMAL;<br class="">-<br class="">/** Push a new decimal on the stack and return a pointer to it. */<br class="">-static decimal_t *<br class="">+decimal_t *<br class="">lua_pushdecimal(struct lua_State *L)<br class="">{<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>decimal_t *res = luaL_pushcdata(L, CTID_DECIMAL);<br class="">diff --git a/src/lua/decimal.h b/src/lua/decimal.h<br class="">index 0485d11ef..b5c0e54b4 100644<br class="">--- a/src/lua/decimal.h<br class="">+++ b/src/lua/decimal.h<br class="">@@ -31,12 +31,17 @@<br class="">#ifndef TARANTOOL_LUA_DECIMAL_H_INCLUDED<br class="">#define TARANTOOL_LUA_DECIMAL_H_INCLUDED<br class=""><br class="">+#include "lib/core/decimal.h"<br class="">+<br class="">#if defined(__cplusplus)<br class="">extern "C" {<br class="">#endif /* defined(__cplusplus) */<br class=""><br class="">struct lua_State;<br class=""><br class="">+decimal_t *<br class="">+lua_pushdecimal(struct lua_State *L);<br class="">+<br class="">void<br class="">tarantool_lua_decimal_init(struct lua_State *L);<br class=""><br class="">diff --git a/src/lua/msgpack.c b/src/lua/msgpack.c<br class="">index 2126988eb..3cb7d7dcd 100644<br class="">--- a/src/lua/msgpack.c<br class="">+++ b/src/lua/msgpack.c<br class="">@@ -41,6 +41,10 @@<br class="">#include <small/region.h><br class="">#include <small/ibuf.h><br class=""><br class="">+#include "lua/decimal.h"<br class="">+#include "lib/core/decimal.h"<br class="">+#include "lib/core/mp_user_types.h"<br class="">+<br class="">#include <fiber.h><br class=""><br class="">void<br class="">@@ -175,16 +179,23 @@ restart: /* used by MP_EXT */<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>assert(lua_gettop(L) == top);<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return MP_ARRAY;<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>case MP_EXT:<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* Run trigger if type can't be encoded */<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>type = luamp_encode_extension(L, top, stream);<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>if (type != MP_EXT)<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return type; /* Value has been packed by the trigger */<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* Try to convert value to serializable type */<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>luaL_convertfield(L, cfg, top, field);<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* handled by luaL_convertfield */<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>assert(field->type != MP_EXT);<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>assert(lua_gettop(L) == top);<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>goto restart;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>switch (field->ext_type)<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>{<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>case MP_DECIMAL:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>mpstream_encode_decimal(stream, field->decval);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return MP_EXT;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>case MP_UNKNOWN:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* Run trigger if type can't be encoded */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>type = luamp_encode_extension(L, top, stream);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>if (type != MP_EXT)<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return type; /* Value has been packed by the trigger */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* Try to convert value to serializable type */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>luaL_convertfield(L, cfg, top, field);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* handled by luaL_convertfield */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>assert(field->type != MP_EXT || field->ext_type != MP_UNKNOWN);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>assert(lua_gettop(L) == top);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>goto restart;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>return MP_EXT;<br class="">}<br class="">@@ -283,9 +294,28 @@ luamp_decode(struct lua_State *L, struct luaL_serializer *cfg,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return;<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>case MP_EXT:<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>luamp_decode_extension(L, data);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>{<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>uint32_t len;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>int8_t type;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>len = mp_decode_extl(data, &type);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>switch (type) {<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>case MP_DECIMAL:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>{<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>decimal_t *dec = lua_pushdecimal(L);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>dec = decimal_unpack(data, len, dec);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>if (dec == NULL) {<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>lua_pop(L, -1);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>luaL_error(L, "msgpack.decode: "<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> "invalid MsgPack.");<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>default:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>luamp_decode_extension(L, data);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>break;<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">}<br class=""><br class=""><br class="">diff --git a/src/lua/msgpackffi.lua b/src/lua/msgpackffi.lua<br class="">index bfeedbc4b..4c799eed0 100644<br class="">--- a/src/lua/msgpackffi.lua<br class="">+++ b/src/lua/msgpackffi.lua<br class="">@@ -17,10 +17,18 @@ char *<br class="">mp_encode_float(char *data, float num);<br class="">char *<br class="">mp_encode_double(char *data, double num);<br class="">+char *<br class="">+mp_encode_decimal(char *data, decimal_t *dec);<br class="">+uint32_t<br class="">+mp_sizeof_decimal(const decimal_t *dec);<br class="">float<br class="">mp_decode_float(const char **data);<br class="">double<br class="">mp_decode_double(const char **data);<br class="">+uint32_t<br class="">+mp_decode_extl(const char **data, int8_t *type);<br class="">+decimal_t *<br class="">+decimal_unpack(const char **data, uint32_t len, decimal_t *dec);<br class="">]])<br class=""><br class="">local strict_alignment = (jit.arch == 'arm')<br class="">@@ -117,6 +125,11 @@ local function encode_double(buf, num)<br class=""> builtin.mp_encode_double(p, num)<br class="">end<br class=""><br class="">+local function encode_decimal(buf, num)<br class="">+ local p = buf:alloc(builtin.mp_sizeof_decimal(num))<br class="">+ builtin.mp_encode_decimal(p, num)<br class="">+end<br class="">+<br class="">local function encode_int(buf, num)<br class=""> if num >= 0 then<br class=""> if num <= 0x7f then<br class="">@@ -294,6 +307,7 @@ on_encode(ffi.typeof('const unsigned char'), encode_int)<br class="">on_encode(ffi.typeof('bool'), encode_bool_cdata)<br class="">on_encode(ffi.typeof('float'), encode_float)<br class="">on_encode(ffi.typeof('double'), encode_double)<br class="">+on_encode(ffi.typeof('decimal_t'), encode_decimal)<br class=""><br class="">--------------------------------------------------------------------------------<br class="">-- Decoder<br class="">@@ -473,6 +487,23 @@ local function decode_map(data, size)<br class=""> return setmetatable(map, msgpack.map_mt)<br class="">end<br class=""><br class="">+local function decode_ext(data)<br class="">+ local t = ffi.new("int8_t[1]")<br class="">+ -- mp_decode_extl and mp_decode_decimal<br class="">+ -- need type code<br class="">+ data[0] = data[0] - 1<br class="">+ local old_data = data[0]<br class="">+ local len = builtin.mp_decode_extl(data, t)<br class="">+ --MP_DECIMAL<br class="">+ if t[0] == 1 then<br class="">+ local num = ffi.new("decimal_t")<br class="">+ builtin.decimal_unpack(data, len, num)<br class="">+ return num<br class="">+ else<br class="">+ error("Unsupported extension type")<br class="">+ end<br class="">+end<br class="">+<br class="">local decoder_hint = {<br class=""> --[[{{{ MP_BIN]]<br class=""> [0xc4] = function(data) return decode_str(data, decode_u8(data)) end;<br class="">@@ -528,6 +559,8 @@ decode_r = function(data)<br class=""> return false<br class=""> elseif c == 0xc3 then<br class=""> return true<br class="">+ elseif c >= 0xd4 and c <= 0xd8 or c >= 0xc7 and c <= 0xc9 then<br class="">+ return decode_ext(data)<br class=""> else<br class=""> local fun = decoder_hint[c];<br class=""> assert (type(fun) == "function")<br class="">diff --git a/src/lua/utils.c b/src/lua/utils.c<br class="">index 0a4bcf517..47cf030ab 100644<br class="">--- a/src/lua/utils.c<br class="">+++ b/src/lua/utils.c<br class="">@@ -45,6 +45,8 @@ static uint32_t CTID_STRUCT_IBUF;<br class="">static uint32_t CTID_STRUCT_IBUF_PTR;<br class="">uint32_t CTID_CHAR_PTR;<br class="">uint32_t CTID_CONST_CHAR_PTR;<br class="">+uint32_t CTID_DECIMAL;<br class="">+<br class=""><br class="">void *<br class="">luaL_pushcdata(struct lua_State *L, uint32_t ctypeid)<br class="">@@ -723,6 +725,12 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* Fall through */<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>default:<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>field->type = MP_EXT;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>if (cd->ctypeid == CTID_DECIMAL) {<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>field->ext_type = MP_DECIMAL;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>field->decval = (decimal_t *) cdata;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>} else {<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>field->ext_type = MP_UNKNOWN;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return 0;<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">@@ -754,6 +762,7 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, int index,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* Fall through */<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>default:<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>field->type = MP_EXT;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>field->ext_type = MP_UNKNOWN;<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">#undef CHECK_NUMBER<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>return 0;<br class="">@@ -765,7 +774,7 @@ luaL_convertfield(struct lua_State *L, struct luaL_serializer *cfg, int idx,<br class="">{<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>if (idx < 0)<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>idx = lua_gettop(L) + idx + 1;<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span>assert(field->type == MP_EXT); /* must be called after tofield() */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>assert(field->type == MP_EXT && field->ext_type == MP_UNKNOWN); /* must be called after tofield() */<br class=""><br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>if (cfg->encode_load_metatables) {<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>int type = lua_type(L, idx);<br class="">@@ -782,10 +791,11 @@ luaL_convertfield(struct lua_State *L, struct luaL_serializer *cfg, int idx,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class=""><br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span>if (field->type == MP_EXT && cfg->encode_use_tostring)<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>if (field->type == MP_EXT && field->ext_type == MP_UNKNOWN &&<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span> cfg->encode_use_tostring)<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>lua_field_tostring(L, cfg, idx, field);<br class=""><br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span>if (field->type != MP_EXT)<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>if (field->type != MP_EXT || field->ext_type != MP_UNKNOWN)<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return;<br class=""><br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>if (cfg->encode_invalid_as_nil) {<br class="">diff --git a/src/lua/utils.h b/src/lua/utils.h<br class="">index 7e7cdc0c6..3ca43292b 100644<br class="">--- a/src/lua/utils.h<br class="">+++ b/src/lua/utils.h<br class="">@@ -54,6 +54,8 @@ extern "C" {<br class="">#include <lj_meta.h><br class=""><br class="">#include "lua/error.h"<br class="">+#include "lib/core/mp_user_types.h"<br class="">+#include "lib/core/decimal.h" /* decimal_t */<br class=""><br class="">struct lua_State;<br class="">struct ibuf;<br class="">@@ -69,6 +71,8 @@ extern struct ibuf *tarantool_lua_ibuf;<br class=""><br class="">extern uint32_t CTID_CONST_CHAR_PTR;<br class="">extern uint32_t CTID_CHAR_PTR;<br class="">+extern uint32_t CTID_DECIMAL;<br class="">+<br class=""><br class="">/** \cond public */<br class=""><br class="">@@ -286,8 +290,10 @@ struct luaL_field {<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>bool bval;<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* Array or map. */<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>uint32_t size;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>decimal_t *decval;<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>};<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>enum mp_type type;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>enum mp_user_type ext_type;<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>bool compact; /* a flag used by YAML serializer */<br class="">};<br class=""><br class="">@@ -373,7 +379,7 @@ luaL_checkfield(struct lua_State *L, struct luaL_serializer *cfg, int idx,<br class="">{<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>if (luaL_tofield(L, cfg, idx, field) < 0)<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>luaT_error(L);<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span>if (field->type != MP_EXT)<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>if (field->type != MP_EXT || field->ext_type != MP_UNKNOWN)<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return;<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>luaL_convertfield(L, cfg, idx, field);<br class="">}<br class="">diff --git a/test/app/decimal.result b/test/app/decimal.result<br class="">index 53ec73edc..334c9fbab 100644<br class="">--- a/test/app/decimal.result<br class="">+++ b/test/app/decimal.result<br class="">@@ -12,77 +12,77 @@ ffi = require('ffi')<br class="">-- check various constructors<br class="">decimal.new('1234.5678')<br class=""> | ---<br class="">- | - '1234.5678'<br class="">+ | - 1234.5678<br class=""> | ...<br class="">decimal.new('1e6')<br class=""> | ---<br class="">- | - '1000000'<br class="">+ | - 1000000<br class=""> | ...<br class="">decimal.new('-6.234612e2')<br class=""> | ---<br class="">- | - '-623.4612'<br class="">+ | - -623.4612<br class=""> | ...<br class="">-- check (u)int16/32/64_t<br class="">decimal.new(2ULL ^ 63)<br class=""> | ---<br class="">- | - '9223372036854775808'<br class="">+ | - 9223372036854775808<br class=""> | ...<br class="">decimal.new(123456789123456789ULL)<br class=""> | ---<br class="">- | - '123456789123456789'<br class="">+ | - 123456789123456789<br class=""> | ...<br class="">decimal.new(-123456789123456789LL)<br class=""> | ---<br class="">- | - '-123456789123456789'<br class="">+ | - -123456789123456789<br class=""> | ...<br class="">decimal.new(ffi.new('uint8_t', 231))<br class=""> | ---<br class="">- | - '231'<br class="">+ | - 231<br class=""> | ...<br class="">decimal.new(ffi.new('int8_t', -113))<br class=""> | ---<br class="">- | - '-113'<br class="">+ | - -113<br class=""> | ...<br class="">decimal.new(ffi.new('uint16_t', 65535))<br class=""> | ---<br class="">- | - '65535'<br class="">+ | - 65535<br class=""> | ...<br class="">decimal.new(ffi.new('int16_t', -31263))<br class=""> | ---<br class="">- | - '-31263'<br class="">+ | - -31263<br class=""> | ...<br class="">decimal.new(ffi.new('uint32_t', 4123123123))<br class=""> | ---<br class="">- | - '4123123123'<br class="">+ | - 4123123123<br class=""> | ...<br class="">decimal.new(ffi.new('int32_t', -2123123123))<br class=""> | ---<br class="">- | - '-2123123123'<br class="">+ | - -2123123123<br class=""> | ...<br class="">decimal.new(ffi.new('float', 128.5))<br class=""> | ---<br class="">- | - '128.5'<br class="">+ | - 128.5<br class=""> | ...<br class="">decimal.new(ffi.new('double', 128.5))<br class=""> | ---<br class="">- | - '128.5'<br class="">+ | - 128.5<br class=""> | ...<br class=""><br class="">decimal.new(1)<br class=""> | ---<br class="">- | - '1'<br class="">+ | - 1<br class=""> | ...<br class="">decimal.new(-1)<br class=""> | ---<br class="">- | - '-1'<br class="">+ | - -1<br class=""> | ...<br class="">decimal.new(2^64)<br class=""> | ---<br class="">- | - '18446744073709600000'<br class="">+ | - 18446744073709600000<br class=""> | ...<br class="">decimal.new(2^(-20))<br class=""> | ---<br class="">- | - '0.00000095367431640625'<br class="">+ | - 0.00000095367431640625<br class=""> | ...<br class=""><br class="">-- incorrect constructions<br class="">@@ -128,38 +128,38 @@ a = decimal.new('10')<br class=""> | ...<br class="">a<br class=""> | ---<br class="">- | - '10'<br class="">+ | - 10<br class=""> | ...<br class="">b = decimal.new('0.1')<br class=""> | ---<br class=""> | ...<br class="">b<br class=""> | ---<br class="">- | - '0.1'<br class="">+ | - 0.1<br class=""> | ...<br class="">a + b<br class=""> | ---<br class="">- | - '10.1'<br class="">+ | - 10.1<br class=""> | ...<br class="">a - b<br class=""> | ---<br class="">- | - '9.9'<br class="">+ | - 9.9<br class=""> | ...<br class="">a * b<br class=""> | ---<br class="">- | - '1.0'<br class="">+ | - 1.0<br class=""> | ...<br class="">a / b<br class=""> | ---<br class="">- | - '100'<br class="">+ | - 100<br class=""> | ...<br class="">a ^ b<br class=""> | ---<br class="">- | - '1.2589254117941672104239541063958006061'<br class="">+ | - 1.2589254117941672104239541063958006061<br class=""> | ...<br class="">b ^ a<br class=""> | ---<br class="">- | - '0.0000000001'<br class="">+ | - 0.0000000001<br class=""> | ...<br class="">-a + -b == -(a + b)<br class=""> | ---<br class="">@@ -167,11 +167,11 @@ b ^ a<br class=""> | ...<br class="">a<br class=""> | ---<br class="">- | - '10'<br class="">+ | - 10<br class=""> | ...<br class="">b<br class=""> | ---<br class="">- | - '0.1'<br class="">+ | - 0.1<br class=""> | ...<br class=""><br class="">a < b<br class="">@@ -216,28 +216,28 @@ a ~= b<br class=""> | ...<br class="">a<br class=""> | ---<br class="">- | - '10'<br class="">+ | - 10<br class=""> | ...<br class="">b<br class=""> | ---<br class="">- | - '0.1'<br class="">+ | - 0.1<br class=""> | ...<br class=""><br class="">decimal.sqrt(a)<br class=""> | ---<br class="">- | - '3.1622776601683793319988935444327185337'<br class="">+ | - 3.1622776601683793319988935444327185337<br class=""> | ...<br class="">decimal.ln(a)<br class=""> | ---<br class="">- | - '2.3025850929940456840179914546843642076'<br class="">+ | - 2.3025850929940456840179914546843642076<br class=""> | ...<br class="">decimal.log10(a)<br class=""> | ---<br class="">- | - '1'<br class="">+ | - 1<br class=""> | ...<br class="">decimal.exp(a)<br class=""> | ---<br class="">- | - '22026.465794806716516957900645284244366'<br class="">+ | - 22026.465794806716516957900645284244366<br class=""> | ...<br class="">a == decimal.ln(decimal.exp(a))<br class=""> | ---<br class="">@@ -261,7 +261,7 @@ a + -a == 0<br class=""> | ...<br class="">a<br class=""> | ---<br class="">- | - '10'<br class="">+ | - 10<br class=""> | ...<br class=""><br class="">a = decimal.new('1.1234567891234567891234567891234567891')<br class="">@@ -269,7 +269,7 @@ a = decimal.new('1.1234567891234567891234567891234567891')<br class=""> | ...<br class="">a<br class=""> | ---<br class="">- | - '1.1234567891234567891234567891234567891'<br class="">+ | - 1.1234567891234567891234567891234567891<br class=""> | ...<br class="">decimal.precision(a)<br class=""> | ---<br class="">@@ -285,7 +285,7 @@ decimal.round(a, 37) == a<br class=""> | ...<br class="">a<br class=""> | ---<br class="">- | - '1.1234567891234567891234567891234567891'<br class="">+ | - 1.1234567891234567891234567891234567891<br class=""> | ...<br class="">a = decimal.round(a, 36)<br class=""> | ---<br class="">@@ -309,19 +309,19 @@ decimal.round(a, -5) == a<br class=""> | ...<br class="">decimal.round(a, 7)<br class=""> | ---<br class="">- | - '1.1234568'<br class="">+ | - 1.1234568<br class=""> | ...<br class="">decimal.round(a, 3)<br class=""> | ---<br class="">- | - '1.123'<br class="">+ | - 1.123<br class=""> | ...<br class="">decimal.round(a, 0)<br class=""> | ---<br class="">- | - '1'<br class="">+ | - 1<br class=""> | ...<br class="">a<br class=""> | ---<br class="">- | - '1.123456789123456789123456789123456789'<br class="">+ | - 1.123456789123456789123456789123456789<br class=""> | ...<br class=""><br class="">decimal.ln(0)<br class="">@@ -334,7 +334,7 @@ decimal.ln(-1)<br class=""> | ...<br class="">decimal.ln(1)<br class=""> | ---<br class="">- | - '0'<br class="">+ | - 0<br class=""> | ...<br class="">decimal.log10(0)<br class=""> | ---<br class="">@@ -346,7 +346,7 @@ decimal.log10(-1)<br class=""> | ...<br class="">decimal.log10(1)<br class=""> | ---<br class="">- | - '0'<br class="">+ | - 0<br class=""> | ...<br class="">decimal.exp(88)<br class=""> | ---<br class="">@@ -354,7 +354,7 @@ decimal.exp(88)<br class=""> | ...<br class="">decimal.exp(87)<br class=""> | ---<br class="">- | - '60760302250568721495223289381302760753'<br class="">+ | - 60760302250568721495223289381302760753<br class=""> | ...<br class="">decimal.sqrt(-5)<br class=""> | ---<br class="">@@ -362,7 +362,7 @@ decimal.sqrt(-5)<br class=""> | ...<br class="">decimal.sqrt(5)<br class=""> | ---<br class="">- | - '2.2360679774997896964091736687312762354'<br class="">+ | - 2.2360679774997896964091736687312762354<br class=""> | ...<br class=""><br class="">-- various incorrect operands<br class="">@@ -408,11 +408,11 @@ a ^ 2<br class=""> | ...<br class="">a ^ 1.9<br class=""> | ---<br class="">- | - '1258925411794167210423954106395800606.1'<br class="">+ | - 1258925411794167210423954106395800606.1<br class=""> | ...<br class="">a * '1e18'<br class=""> | ---<br class="">- | - '10000000000000000000000000000000000000'<br class="">+ | - 10000000000000000000000000000000000000<br class=""> | ...<br class="">a = decimal.new(string.rep('9', 38))<br class=""> | ---<br class="">@@ -435,7 +435,7 @@ a + '0.5'<br class=""> | ...<br class="">a + '0.4'<br class=""> | ---<br class="">- | - '99999999999999999999999999999999999999'<br class="">+ | - 99999999999999999999999999999999999999<br class=""> | ...<br class="">a / 0.5<br class=""> | ---<br class="">@@ -451,7 +451,7 @@ a = decimal.new('-13')<br class=""> | ...<br class="">a ^ 2<br class=""> | ---<br class="">- | - '169'<br class="">+ | - 169<br class=""> | ...<br class="">-- fractional powers are allowed only for positive numbers<br class="">a ^ 2.5<br class="">@@ -462,17 +462,17 @@ a ^ 2.5<br class="">-- check correct rounding when scale = 0<br class="">decimal.round(decimal.new(0.9), 0)<br class=""> | ---<br class="">- | - '1'<br class="">+ | - 1<br class=""> | ...<br class="">decimal.round(decimal.new(9.9), 0)<br class=""> | ---<br class="">- | - '10'<br class="">+ | - 10<br class=""> | ...<br class="">decimal.round(decimal.new(99.9), 0)<br class=""> | ---<br class="">- | - '100'<br class="">+ | - 100<br class=""> | ...<br class="">decimal.round(decimal.new(99.4), 0)<br class=""> | ---<br class="">- | - '99'<br class="">+ | - 99<br class=""> | ...<br class="">diff --git a/test/app/msgpack.result b/test/app/msgpack.result<br class="">index a67c05d38..4b5aec784 100644<br class="">--- a/test/app/msgpack.result<br class="">+++ b/test/app/msgpack.result<br class="">@@ -252,3 +252,44 @@ msgpack.decode(ffi.cast('char *', '\x04\x05\x06'), -1)<br class="">---<br class="">- error: 'msgpack.decode: size can''t be negative'<br class="">...<br class="">+--<br class="">+-- gh-4333: msgpack encode/decode decimals.<br class="">+--<br class="">+decimal = require('decimal')<br class="">+---<br class="">+...<br class="">+a = decimal.new('1e37')<br class="">+---<br class="">+...<br class="">+b = decimal.new('1e-38')<br class="">+---<br class="">+...<br class="">+c = decimal.new('1')<br class="">+---<br class="">+...<br class="">+d = decimal.new('0.1234567')<br class="">+---<br class="">+...<br class="">+e = decimal.new('123.4567')<br class="">+---<br class="">+...<br class="">+msgpack.decode(msgpack.encode(a)) == a<br class="">+---<br class="">+- true<br class="">+...<br class="">+msgpack.decode(msgpack.encode(b)) == b<br class="">+---<br class="">+- true<br class="">+...<br class="">+msgpack.decode(msgpack.encode(c)) == c<br class="">+---<br class="">+- true<br class="">+...<br class="">+msgpack.decode(msgpack.encode(d)) == d<br class="">+---<br class="">+- true<br class="">+...<br class="">+msgpack.decode(msgpack.encode(e)) == e<br class="">+---<br class="">+- true<br class="">+...<br class="">diff --git a/test/app/msgpack.test.lua b/test/app/msgpack.test.lua<br class="">index e0880ac22..9224d870a 100644<br class="">--- a/test/app/msgpack.test.lua<br class="">+++ b/test/app/msgpack.test.lua<br class="">@@ -84,3 +84,18 @@ size = msgpack.encode(100, buf)<br class="">-- is not negative.<br class="">--<br class="">msgpack.decode(ffi.cast('char *', '\x04\x05\x06'), -1)<br class="">+<br class="">+--<br class="">+-- gh-4333: msgpack encode/decode decimals.<br class="">+--<br class="">+decimal = require('decimal')<br class="">+a = decimal.new('1e37')<br class="">+b = decimal.new('1e-38')<br class="">+c = decimal.new('1')<br class="">+d = decimal.new('0.1234567')<br class="">+e = decimal.new('123.4567')<br class="">+msgpack.decode(msgpack.encode(a)) == a<br class="">+msgpack.decode(msgpack.encode(b)) == b<br class="">+msgpack.decode(msgpack.encode(c)) == c<br class="">+msgpack.decode(msgpack.encode(d)) == d<br class="">+msgpack.decode(msgpack.encode(e)) == e<br class="">diff --git a/third_party/lua-yaml/<a href="http://lyaml.cc" class="">lyaml.cc</a> b/third_party/lua-yaml/<a href="http://lyaml.cc" class="">lyaml.cc</a><br class="">index 46c98bde1..8ff2867e3 100644<br class="">--- a/third_party/lua-yaml/<a href="http://lyaml.cc" class="">lyaml.cc</a><br class="">+++ b/third_party/lua-yaml/<a href="http://lyaml.cc" class="">lyaml.cc</a><br class="">@@ -49,6 +49,7 @@ extern "C" {<br class="">#include "b64.h"<br class="">} /* extern "C" */<br class="">#include "lua/utils.h"<br class="">+#include "lib/core/decimal.h"<br class=""><br class="">#define LUAYAML_TAG_PREFIX "tag:<a href="http://yaml.org" class="">yaml.org</a>,2002:"<br class=""><br class="">@@ -693,6 +694,11 @@ static int dump_node(struct lua_yaml_dumper *dumper)<br class=""> len = 4;<br class=""> break;<br class=""> case MP_EXT:<br class="">+ if (field.ext_type == MP_DECIMAL) {<br class="">+ str = decimal_to_string(field.decval);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span> len = strlen(str);<br class="">+ break;<br class="">+ }<br class=""> assert(0); /* checked by luaL_checkfield() */<br class=""> break;<br class=""> }<br class="">-- <br class="">2.20.1 (Apple Git-117)</div><div><br class=""></div><div><br class=""></div><div><br class=""></div><div><br class=""></div><div>=======================================</div><div><br class=""></div><div>Here’s the old version of the patch adding decimal indices</div><div><br class=""></div>=======================================</div><div>Indices can now be built over decimal fields.<br class="">A new field type - 'decimal' is introduced.<br class="">Decimal values may be stored in 'decimal' columns, as well as in<br class="">'scalar' and 'any' columns.<br class=""><br class="">Closes #4333<br class=""><br class="">@TarantoolBot document<br class="">Title: Document decimal field type.<br class=""><br class="">Decimals may now be stored in spaces. A corresponding field type is<br class="">introduced: 'decimal'. Decimal values are also allowed in 'scalar' and<br class="">'number' fields.<br class=""><br class="">'decimal' field type is appropriate for both memtx HASH and TREE<br class="">indices, as well as for vinyl TREE index.<br class="">---<br class="">src/box/field_def.c | 43 +++++--<br class="">src/box/field_def.h | 16 ++-<br class="">src/box/key_def.h | 2 +-<br class="">src/box/<a href="http://tuple_compare.cc" class="">tuple_compare.cc</a> | 160 ++++++++++++++++++++++++-<br class="">src/box/tuple_format.c | 2 +-<br class="">src/lib/core/decimal.h | 8 ++<br class="">src/lib/core/mp_decimal.h | 8 ++<br class="">test/engine/decimal.result | 226 +++++++++++++++++++++++++++++++++++<br class="">test/engine/decimal.test.lua | 65 ++++++++++<br class="">9 files changed, 510 insertions(+), 20 deletions(-)<br class="">create mode 100644 test/engine/decimal.result<br class="">create mode 100644 test/engine/decimal.test.lua<br class=""><br class="">diff --git a/src/box/field_def.c b/src/box/field_def.c<br class="">index 346042b98..da06e6bde 100644<br class="">--- a/src/box/field_def.c<br class="">+++ b/src/box/field_def.c<br class="">@@ -52,17 +52,32 @@ const uint32_t field_mp_type[] = {<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_UNSIGNED] = */ 1U << MP_UINT,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_STRING] = */ 1U << MP_STR,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_NUMBER] = */ (1U << MP_UINT) | (1U << MP_INT) |<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>(1U << MP_FLOAT) | (1U << MP_DOUBLE),<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>(1U << MP_FLOAT) | (1U << MP_DOUBLE) | (1U << MP_EXT),<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_INTEGER] = */ (1U << MP_UINT) | (1U << MP_INT),<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_BOOLEAN] = */ 1U << MP_BOOL,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_VARBINARY] = */ 1U << MP_BIN,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_SCALAR] = */ (1U << MP_UINT) | (1U << MP_INT) |<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>(1U << MP_FLOAT) | (1U << MP_DOUBLE) | (1U << MP_STR) |<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>(1U << MP_BIN) | (1U << MP_BOOL),<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>(1U << MP_BIN) | (1U << MP_BOOL) | (1U << MP_EXT),<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_DECIMAL] = */ 1U << MP_EXT,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_ARRAY] = */ 1U << MP_ARRAY,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_MAP] = */ (1U << MP_MAP),<br class="">};<br class=""><br class="">+const uint32_t field_ext_type[] = {<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_ANY] = */ UINT32_MAX & ~(1U << MP_UNKNOWN),<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_UNSIGNED] = */ 0,<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_STRING] = */ 0,<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_NUMBER] = */ 1U << MP_DECIMAL,<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_INTEGER] = */ 0,<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_BOOLEAN] = */ 0,<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_VARBINARY] = */ 0,<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_SCALAR] = */ 1U << MP_DECIMAL,<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_DECIMAL] = */ 1U << MP_DECIMAL,<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_ARRAY] = */ 0,<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_MAP] = */ 0,<br class="">+};<br class="">+<br class="">const char *field_type_strs[] = {<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_ANY] = */ "any",<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_UNSIGNED] = */ "unsigned",<br class="">@@ -72,6 +87,7 @@ const char *field_type_strs[] = {<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_BOOLEAN] = */ "boolean",<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_VARBINARY] = */"varbinary",<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_SCALAR] = */ "scalar",<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_DECIMAL] = */ "decimal",<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_ARRAY] = */ "array",<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* [FIELD_TYPE_MAP] = */ "map",<br class="">};<br class="">@@ -98,17 +114,18 @@ field_type_by_name_wrapper(const char *str, uint32_t len)<br class=""> * values can be stored in the j type.<br class=""> */<br class="">static const bool field_type_compatibility[] = {<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span> /* ANY UNSIGNED STRING NUMBER INTEGER BOOLEAN VARBINARY SCALAR ARRAY MAP */<br class="">-/* ANY */ true, false, false, false, false, false, false, false, false, false,<br class="">-/* UNSIGNED */ true, true, false, true, true, false, false, true, false, false,<br class="">-/* STRING */ true, false, true, false, false, false, false, true, false, false,<br class="">-/* NUMBER */ true, false, false, true, false, false, false, true, false, false,<br class="">-/* INTEGER */ true, false, false, true, true, false, false, true, false, false,<br class="">-/* BOOLEAN */ true, false, false, false, false, true, false, true, false, false,<br class="">-/* VARBINARY*/ true, false, false, false, false, false, true, true, false, false,<br class="">-/* SCALAR */ true, false, false, false, false, false, false, true, false, false,<br class="">-/* ARRAY */ true, false, false, false, false, false, false, false, true, false,<br class="">-/* MAP */ true, false, false, false, false, false, false, false, false, true,<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span> /* ANY UNSIGNED STRING NUMBER INTEGER BOOLEAN VARBINARY SCALAR DECIMAL ARRAY MAP */<br class="">+/* ANY */ true, false, false, false, false, false, false, false, false, false, false,<br class="">+/* UNSIGNED */ true, true, false, true, true, false, false, true, false, false, false,<br class="">+/* STRING */ true, false, true, false, false, false, false, true, false, false, false,<br class="">+/* NUMBER */ true, false, false, true, false, false, false, true, false, false, false,<br class="">+/* INTEGER */ true, false, false, true, true, false, false, true, false, false, false,<br class="">+/* BOOLEAN */ true, false, false, false, false, true, false, true, false, false, false,<br class="">+/* VARBINARY*/ true, false, false, false, false, false, true, true, false, false, false,<br class="">+/* SCALAR */ true, false, false, false, false, false, false, true, false, false, false,<br class="">+/* DECIMAL */ true, false, false, true, false, false, false, true, true, false, false,<br class="">+/* ARRAY */ true, false, false, false, false, false, false, false, false, true, false,<br class="">+/* MAP */ true, false, false, false, false, false, false, false, false, false, true,<br class="">};<br class=""><br class="">bool<br class="">diff --git a/src/box/field_def.h b/src/box/field_def.h<br class="">index c1a7ec0a9..10dc9fc13 100644<br class="">--- a/src/box/field_def.h<br class="">+++ b/src/box/field_def.h<br class="">@@ -37,6 +37,7 @@<br class="">#include <limits.h><br class="">#include <msgpuck.h><br class="">#include "opt_def.h"<br class="">+#include "lib/core/mp_user_types.h"<br class=""><br class="">#if defined(__cplusplus)<br class="">extern "C" {<br class="">@@ -58,6 +59,7 @@ enum field_type {<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>FIELD_TYPE_BOOLEAN,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>FIELD_TYPE_VARBINARY,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>FIELD_TYPE_SCALAR,<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>FIELD_TYPE_DECIMAL,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>FIELD_TYPE_ARRAY,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>FIELD_TYPE_MAP,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>field_type_MAX<br class="">@@ -109,8 +111,9 @@ field_type_by_name(const char *name, size_t len);<br class="">/* MsgPack type names */<br class="">extern const char *mp_type_strs[];<br class=""><br class="">-/** A helper table for field_mp_type_is_compatible */<br class="">+/** Two helper tables for field_mp_type_is_compatible */<br class="">extern const uint32_t field_mp_type[];<br class="">+extern const uint32_t field_ext_type[];<br class=""><br class="">extern const struct opt_def field_def_reg[];<br class="">extern const struct field_def field_def_default;<br class="">@@ -142,15 +145,20 @@ struct field_def {<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>struct Expr *default_value_expr;<br class="">};<br class=""><br class="">-/** Checks if mp_type (MsgPack) is compatible with field type. */<br class="">+/** Checks if mp_typeof(data) is compatible with field type. */<br class="">static inline bool<br class="">-field_mp_type_is_compatible(enum field_type type, enum mp_type mp_type,<br class="">+field_mp_type_is_compatible(enum field_type type, const char *data,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> bool is_nullable)<br class="">{<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>enum mp_type mp_type = mp_typeof(*data);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>int8_t ext_type = MP_UNKNOWN;<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>assert(type < field_type_MAX);<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>assert((size_t)mp_type < CHAR_BIT * sizeof(*field_mp_type));<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>uint32_t mask = field_mp_type[type] | (is_nullable * (1U << MP_NIL));<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span>return (mask & (1U << mp_type)) != 0;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>if (mp_type != MP_EXT)<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return (mask & (1U << mp_type)) != 0;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>mp_decode_extl(&data, &ext_type);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>return (field_ext_type[type] & (1U << ext_type)) != 0;<br class="">}<br class=""><br class="">static inline bool<br class="">diff --git a/src/box/key_def.h b/src/box/key_def.h<br class="">index f4a1a8fd1..ed3354ade 100644<br class="">--- a/src/box/key_def.h<br class="">+++ b/src/box/key_def.h<br class="">@@ -505,7 +505,7 @@ static inline int<br class="">key_part_validate(enum field_type key_type, const char *key,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> uint32_t field_no, bool is_nullable)<br class="">{<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span>if (unlikely(!field_mp_type_is_compatible(key_type, mp_typeof(*key),<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>if (unlikely(!field_mp_type_is_compatible(key_type, key,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> is_nullable))) {<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>diag_set(ClientError, ER_KEY_PART_TYPE, field_no,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> field_type_strs[key_type]);<br class="">diff --git a/src/box/<a href="http://tuple_compare.cc" class="">tuple_compare.cc</a> b/src/box/<a href="http://tuple_compare.cc" class="">tuple_compare.cc</a><br class="">index 95a0f58c9..6ab28f662 100644<br class="">--- a/src/box/<a href="http://tuple_compare.cc" class="">tuple_compare.cc</a><br class="">+++ b/src/box/<a href="http://tuple_compare.cc" class="">tuple_compare.cc</a><br class="">@@ -33,6 +33,9 @@<br class="">#include "coll/coll.h"<br class="">#include "trivia/util.h" /* NOINLINE */<br class="">#include <math.h><br class="">+#include "lib/core/decimal.h"<br class="">+#include "lib/core/mp_decimal.h"<br class="">+#include "lib/core/mp_user_types.h"<br class=""><br class="">/* {{{ tuple_compare */<br class=""><br class="">@@ -87,7 +90,7 @@ static enum mp_class mp_classes[] = {<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* .MP_BOOL = */ MP_CLASS_BOOL,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* .MP_FLOAT = */ MP_CLASS_NUMBER,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>/* .MP_DOUBLE = */ MP_CLASS_NUMBER,<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span>/* .MP_BIN = */ MP_CLASS_BIN<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>/* .MP_EXT = */ MP_CLASS_NUMBER /* requires additional parsing */<br class="">};<br class=""><br class="">#define COMPARE_RESULT(a, b) (a < b ? -1 : a > b)<br class="">@@ -264,6 +267,50 @@ mp_compare_double_any_number(double lhs, const char *rhs,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>return k * COMPARE_RESULT(lqbit, rqbit);<br class="">}<br class=""><br class="">+static int<br class="">+mp_compare_decimal_any_number(decimal_t *lhs, const char *rhs,<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> enum mp_type rhs_type, int k)<br class="">+{<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>decimal_t rhs_dec;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>switch (rhs_type) {<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>case MP_FLOAT:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>{<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>double d = mp_decode_float(&rhs);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>decimal_from_double(&rhs_dec, d);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>break;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>case MP_DOUBLE:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>{<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>double d = mp_decode_double(&rhs);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>decimal_from_double(&rhs_dec, d);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>break;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>case MP_INT:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>{<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>int64_t num = mp_decode_int(&rhs);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>decimal_from_int64(&rhs_dec, num);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>break;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>case MP_UINT:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>{<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>uint64_t num = mp_decode_uint(&rhs);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>decimal_from_uint64(&rhs_dec, num);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>break;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>case MP_EXT:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>{<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>int8_t ext_type;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>uint32_t len = mp_decode_extl(&rhs, &ext_type);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>assert(ext_type == MP_DECIMAL);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>decimal_unpack(&rhs, len, &rhs_dec);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>break;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>default:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>unreachable();<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>return k * decimal_compare(lhs, &rhs_dec);<br class="">+}<br class="">+<br class="">static int<br class="">mp_compare_number_with_type(const char *lhs, enum mp_type lhs_type,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> const char *rhs, enum mp_type rhs_type)<br class="">@@ -271,6 +318,21 @@ mp_compare_number_with_type(const char *lhs, enum mp_type lhs_type,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>assert(mp_classof(lhs_type) == MP_CLASS_NUMBER);<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>assert(mp_classof(rhs_type) == MP_CLASS_NUMBER);<br class=""><br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>/*<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span> * test decimals first, so that we don't have to<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span> * account for them in other comarators.<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span> */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>decimal_t dec;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>if (rhs_type == MP_EXT) {<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return mp_compare_decimal_any_number(<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>mp_decode_decimal(&rhs, &dec), lhs, lhs_type, -1<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>if (lhs_type == MP_EXT) {<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return mp_compare_decimal_any_number(<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>mp_decode_decimal(&lhs, &dec), rhs, rhs_type, 1<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>if (rhs_type == MP_FLOAT) {<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return mp_compare_double_any_number(<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>mp_decode_float(&rhs), lhs, lhs_type, -1<br class="">@@ -410,6 +472,8 @@ tuple_compare_field(const char *field_a, const char *field_b,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return coll != NULL ?<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> mp_compare_scalar_coll(field_a, field_b, coll) :<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> mp_compare_scalar(field_a, field_b);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>case FIELD_TYPE_DECIMAL:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return mp_compare_number(field_a, field_b);<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>default:<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>unreachable();<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return 0;<br class="">@@ -443,6 +507,8 @@ tuple_compare_field_with_type(const char *field_a, enum mp_type a_type,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> mp_compare_scalar_coll(field_a, field_b, coll) :<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> mp_compare_scalar_with_type(field_a, a_type,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> field_b, b_type);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>case FIELD_TYPE_DECIMAL:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return mp_compare_number(field_a, field_b);<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>default:<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>unreachable();<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return 0;<br class="">@@ -1356,6 +1422,25 @@ static const comparator_with_key_signature cmp_wk_arr[] = {<br class="">#define HINT_VALUE_DOUBLE_MAX<span class="Apple-tab-span" style="white-space: pre;"> </span>(exp2(HINT_VALUE_BITS - 1) - 1)<br class="">#define HINT_VALUE_DOUBLE_MIN<span class="Apple-tab-span" style="white-space: pre;"> </span>(-exp2(HINT_VALUE_BITS - 1))<br class=""><br class="">+/**<br class="">+ * Max and min decimal numbers whose integral parts fit<br class="">+ * in a hint value.<br class="">+ */<br class="">+static const decimal_t HINT_VALUE_DECIMAL_MAX = {<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>18,<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* decimal digits */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>0,<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* exponent */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>0,<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* no special bits. */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>{487, 423, 303, 752, 460, 576}<span class="Apple-tab-span" style="white-space: pre;"> </span>/* 576,460,752,303,423,488 = 2^59 - 1 */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* 59 == HINT_VALUE_BITS - 1 */<br class="">+};<br class="">+<br class="">+static const decimal_t HINT_VALUE_DECIMAL_MIN = {<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>18,<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* decimal digits */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>0,<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* exponent */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>0x80,<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* negative bit */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>{488, 423, 303, 752, 460, 576}<span class="Apple-tab-span" style="white-space: pre;"> </span>/* 576,460,752,303,423,488 = 2^59 */<br class="">+};<br class="">+<br class="">/*<br class=""> * HINT_CLASS_BITS should be big enough to store any mp_class value.<br class=""> * Note, ((1 << HINT_CLASS_BITS) - 1) is reserved for HINT_NONE.<br class="">@@ -1415,6 +1500,25 @@ hint_double(double d)<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>return hint_create(MP_CLASS_NUMBER, val);<br class="">}<br class=""><br class="">+static inline hint_t<br class="">+hint_decimal(decimal_t *dec)<br class="">+{<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>uint64_t val = 0;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>int64_t num;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>if (decimal_compare(dec, &HINT_VALUE_DECIMAL_MAX) >= 0)<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>val = HINT_VALUE_MAX;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>else if (decimal_compare(dec, &HINT_VALUE_DECIMAL_MIN) <= 0)<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>val = 0;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>else {<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>dec = decimal_to_int64(dec, &num);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* We've checked boundaries above. */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>assert(dec != NULL);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>val = num - HINT_VALUE_INT_MIN;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">+<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>return hint_create(MP_CLASS_NUMBER, val);<br class="">+}<br class="">+<br class="">static inline uint64_t<br class="">hint_str_raw(const char *s, uint32_t len)<br class="">{<br class="">@@ -1491,12 +1595,42 @@ field_hint_number(const char *field)<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return hint_double(mp_decode_float(&field));<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>case MP_DOUBLE:<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return hint_double(mp_decode_double(&field));<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>case MP_EXT:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>{<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>int8_t ext_type;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>uint32_t len = mp_decode_extl(&field, &ext_type);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>switch(ext_type) {<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>case MP_DECIMAL:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>{<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>decimal_t dec;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>decimal_t *res;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/*<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> * The effect of mp_decode_extl() +<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> * decimal_unpack() is the same that<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> * the one of mp_decode_decimal().<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>res = decimal_unpack(&field, len, &dec);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>assert(res != NULL);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return hint_decimal(res);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>default:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>unreachable();<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>default:<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>unreachable();<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>return HINT_NONE;<br class="">}<br class=""><br class="">+static inline hint_t<br class="">+field_hint_decimal(const char *field)<br class="">+{<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>assert(mp_typeof(*field) == MP_EXT);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>decimal_t dec;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>return hint_decimal(mp_decode_decimal(&field, &dec));<br class="">+}<br class="">+<br class="">static inline hint_t<br class="">field_hint_string(const char *field, struct coll *coll)<br class="">{<br class="">@@ -1536,6 +1670,25 @@ field_hint_scalar(const char *field, struct coll *coll)<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>case MP_BIN:<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>len = mp_decode_binl(&field);<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return hint_bin(field, len);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>case MP_EXT:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>{<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>int8_t ext_type;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>uint32_t len = mp_decode_extl(&field, &ext_type);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>switch(ext_type) {<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>case MP_DECIMAL:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>{<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>decimal_t dec;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/*<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> * The effect of mp_decode_extl() +<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> * decimal_unpack() is the same that<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> * the one of mp_decode_decimal().<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> */<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return hint_decimal(decimal_unpack(&field, len, &dec));<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>default:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>unreachable();<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>default:<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>unreachable();<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">@@ -1563,6 +1716,8 @@ field_hint(const char *field, struct coll *coll)<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return field_hint_varbinary(field);<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>case FIELD_TYPE_SCALAR:<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return field_hint_scalar(field, coll);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>case FIELD_TYPE_DECIMAL:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>return field_hint_decimal(field);<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>default:<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>unreachable();<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>}<br class="">@@ -1668,6 +1823,9 @@ key_def_set_hint_func(struct key_def *def)<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>case FIELD_TYPE_SCALAR:<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>key_def_set_hint_func<FIELD_TYPE_SCALAR>(def);<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>break;<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>case FIELD_TYPE_DECIMAL:<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>key_def_set_hint_func<FIELD_TYPE_DECIMAL>(def);<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>break;<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>default:<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>/* Invalid key definition. */<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>def->key_hint = NULL;<br class="">diff --git a/src/box/tuple_format.c b/src/box/tuple_format.c<br class="">index 02fadf1cf..5bb659b87 100644<br class="">--- a/src/box/tuple_format.c<br class="">+++ b/src/box/tuple_format.c<br class="">@@ -1165,7 +1165,7 @@ tuple_format_iterator_next(struct tuple_format_iterator *it,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span> * defined in format.<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span> */<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span>bool is_nullable = tuple_field_is_nullable(field);<br class="">-<span class="Apple-tab-span" style="white-space: pre;"> </span>if (!field_mp_type_is_compatible(field->type, mp_typeof(*entry->data),<br class="">+<span class="Apple-tab-span" style="white-space: pre;"> </span>if (!field_mp_type_is_compatible(field->type, entry->data,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> is_nullable) != 0) {<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span>diag_set(ClientError, ER_FIELD_TYPE,<br class=""><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span><span class="Apple-tab-span" style="white-space: pre;"> </span> tuple_field_path(field),<br class="">diff --git a/src/lib/core/decimal.h b/src/lib/core/decimal.h<br class="">index a4e7683c7..6a152b08c 100644<br class="">--- a/src/lib/core/decimal.h<br class="">+++ b/src/lib/core/decimal.h<br class="">@@ -37,6 +37,10 @@<br class="">#include "third_party/decNumber/decNumber.h"<br class="">#include <stdint.h><br class=""><br class="">+#if defined(__cplusplus)<br class="">+extern "C" {<br class="">+#endif /* defined(__cplusplus) */<br class="">+<br class="">typedef decNumber decimal_t;<br class=""><br class="">/**<br class="">@@ -207,4 +211,8 @@ decimal_pack(char *data, const decimal_t *dec);<br class="">decimal_t *<br class="">decimal_unpack(const char **data, uint32_t len, decimal_t *dec);<br class=""><br class="">+#if defined(__cplusplus)<br class="">+} /* extern "C" */<br class="">+#endif /* defined(__cplusplus) */<br class="">+<br class="">#endif /* TARANTOOL_LIB_CORE_DECIMAL_H_INCLUDED */<br class="">diff --git a/src/lib/core/mp_decimal.h b/src/lib/core/mp_decimal.h<br class="">index a991a5f16..778529068 100644<br class="">--- a/src/lib/core/mp_decimal.h<br class="">+++ b/src/lib/core/mp_decimal.h<br class="">@@ -34,6 +34,10 @@<br class="">#include "decimal.h"<br class="">#include <stdint.h><br class=""><br class="">+#if defined(__cplusplus)<br class="">+extern "C" {<br class="">+#endif /* defined(__cplusplus) */<br class="">+<br class="">/**<br class=""> * \brief Calculate exact buffer size needed to store a decimal<br class=""> * pointed to by \a dec.<br class="">@@ -59,4 +63,8 @@ mp_decode_decimal(const char **data, decimal_t *dec);<br class="">char *<br class="">mp_encode_decimal(char *data, const decimal_t *dec);<br class=""><br class="">+#if defined(__cplusplus)<br class="">+} /* extern "C" */<br class="">+#endif /* defined(__cplusplus) */<br class="">+<br class="">#endif<br class="">diff --git a/test/engine/decimal.result b/test/engine/decimal.result<br class="">new file mode 100644<br class="">index 000000000..16cd335e0<br class="">--- /dev/null<br class="">+++ b/test/engine/decimal.result<br class="">@@ -0,0 +1,226 @@<br class="">+-- test-run result file version 2<br class="">+env = require('test_run')<br class="">+ | ---<br class="">+ | ...<br class="">+test_run = env.new()<br class="">+ | ---<br class="">+ | ...<br class="">+engine = test_run:get_cfg('engine')<br class="">+ | ---<br class="">+ | ...<br class="">+<br class="">+decimal = require('decimal')<br class="">+ | ---<br class="">+ | ...<br class="">+ffi = require('ffi')<br class="">+ | ---<br class="">+ | ...<br class="">+<br class="">+_ = box.schema.space.create('test', {engine=engine})<br class="">+ | ---<br class="">+ | ...<br class="">+_ = box.space.test:create_index('pk', {parts={1,'decimal'}})<br class="">+ | ---<br class="">+ | ...<br class="">+<br class="">+test_run:cmd('setopt delimiter ";"')<br class="">+ | ---<br class="">+ | - true<br class="">+ | ...<br class="">+for i = 0,16 do<br class="">+ box.space.test:insert{decimal.new((i-8)/4)}<br class="">+end;<br class="">+ | ---<br class="">+ | ...<br class="">+test_run:cmd('setopt delimiter ""');<br class="">+ | ---<br class="">+ | - true<br class="">+ | ...<br class="">+<br class="">+box.space.test:select{}<br class="">+ | ---<br class="">+ | - - [-2]<br class="">+ | - [-1.75]<br class="">+ | - [-1.5]<br class="">+ | - [-1.25]<br class="">+ | - [-1]<br class="">+ | - [-0.75]<br class="">+ | - [-0.5]<br class="">+ | - [-0.25]<br class="">+ | - [0]<br class="">+ | - [0.25]<br class="">+ | - [0.5]<br class="">+ | - [0.75]<br class="">+ | - [1]<br class="">+ | - [1.25]<br class="">+ | - [1.5]<br class="">+ | - [1.75]<br class="">+ | - [2]<br class="">+ | ...<br class="">+<br class="">+-- check invalid values<br class="">+box.space.test:insert{1.23}<br class="">+ | ---<br class="">+ | - error: 'Tuple field 1 type does not match one required by operation: expected decimal'<br class="">+ | ...<br class="">+box.space.test:insert{'str'}<br class="">+ | ---<br class="">+ | - error: 'Tuple field 1 type does not match one required by operation: expected decimal'<br class="">+ | ...<br class="">+box.space.test:insert{ffi.new('uint64_t', 0)}<br class="">+ | ---<br class="">+ | - error: 'Tuple field 1 type does not match one required by operation: expected decimal'<br class="">+ | ...<br class="">+-- check duplicates<br class="">+box.space.test:insert{decimal.new(0)}<br class="">+ | ---<br class="">+ | - error: Duplicate key exists in unique index 'pk' in space 'test'<br class="">+ | ...<br class="">+<br class="">+box.space.test.index.pk:drop()<br class="">+ | ---<br class="">+ | ...<br class="">+<br class="">+_ = box.space.test:create_index('pk', {parts={1, 'number'}})<br class="">+ | ---<br class="">+ | ...<br class="">+<br class="">+test_run:cmd('setopt delimiter ";"')<br class="">+ | ---<br class="">+ | - true<br class="">+ | ...<br class="">+for i = 0, 32 do<br class="">+ local val = (i - 16) / 8<br class="">+ if i % 2 == 1 then val = decimal.new(val) end<br class="">+ box.space.test:insert{val}<br class="">+end;<br class="">+ | ---<br class="">+ | ...<br class="">+test_run:cmd('setopt delimiter ""');<br class="">+ | ---<br class="">+ | - true<br class="">+ | ...<br class="">+<br class="">+box.space.test:select{}<br class="">+ | ---<br class="">+ | - - [-2]<br class="">+ | - [-1.875]<br class="">+ | - [-1.75]<br class="">+ | - [-1.625]<br class="">+ | - [-1.5]<br class="">+ | - [-1.375]<br class="">+ | - [-1.25]<br class="">+ | - [-1.125]<br class="">+ | - [-1]<br class="">+ | - [-0.875]<br class="">+ | - [-0.75]<br class="">+ | - [-0.625]<br class="">+ | - [-0.5]<br class="">+ | - [-0.375]<br class="">+ | - [-0.25]<br class="">+ | - [-0.125]<br class="">+ | - [0]<br class="">+ | - [0.125]<br class="">+ | - [0.25]<br class="">+ | - [0.375]<br class="">+ | - [0.5]<br class="">+ | - [0.625]<br class="">+ | - [0.75]<br class="">+ | - [0.875]<br class="">+ | - [1]<br class="">+ | - [1.125]<br class="">+ | - [1.25]<br class="">+ | - [1.375]<br class="">+ | - [1.5]<br class="">+ | - [1.625]<br class="">+ | - [1.75]<br class="">+ | - [1.875]<br class="">+ | - [2]<br class="">+ | ...<br class="">+<br class="">+-- check duplicates<br class="">+box.space.test:insert{-2}<br class="">+ | ---<br class="">+ | - error: Duplicate key exists in unique index 'pk' in space 'test'<br class="">+ | ...<br class="">+box.space.test:insert{decimal.new(-2)}<br class="">+ | ---<br class="">+ | - error: Duplicate key exists in unique index 'pk' in space 'test'<br class="">+ | ...<br class="">+box.space.test:insert{decimal.new(-1.875)}<br class="">+ | ---<br class="">+ | - error: Duplicate key exists in unique index 'pk' in space 'test'<br class="">+ | ...<br class="">+box.space.test:insert{-1.875}<br class="">+ | ---<br class="">+ | - error: Duplicate key exists in unique index 'pk' in space 'test'<br class="">+ | ...<br class="">+<br class="">+box.space.test.index.pk:drop()<br class="">+ | ---<br class="">+ | ...<br class="">+<br class="">+_ = box.space.test:create_index('pk')<br class="">+ | ---<br class="">+ | ...<br class="">+test_run:cmd('setopt delimiter ";"')<br class="">+ | ---<br class="">+ | - true<br class="">+ | ...<br class="">+for i = 1,10 do<br class="">+ box.space.test:insert{i, decimal.new(i/10)}<br class="">+end;<br class="">+ | ---<br class="">+ | ...<br class="">+test_run:cmd('setopt delimiter ""');<br class="">+ | ---<br class="">+ | - true<br class="">+ | ...<br class="">+<br class="">+-- a bigger test with a secondary index this time.<br class="">+box.space.test:insert{11, 'str'}<br class="">+ | ---<br class="">+ | - [11, 'str']<br class="">+ | ...<br class="">+box.space.test:insert{12, 0.63}<br class="">+ | ---<br class="">+ | - [12, 0.63]<br class="">+ | ...<br class="">+box.space.test:insert{13, 0.57}<br class="">+ | ---<br class="">+ | - [13, 0.57]<br class="">+ | ...<br class="">+box.space.test:insert{14, 0.33}<br class="">+ | ---<br class="">+ | - [14, 0.33]<br class="">+ | ...<br class="">+box.space.test:insert{16, 0.71}<br class="">+ | ---<br class="">+ | - [16, 0.71]<br class="">+ | ...<br class="">+<br class="">+_ = box.space.test:create_index('sk', {parts={2, 'scalar'}})<br class="">+ | ---<br class="">+ | ...<br class="">+<a href="http://box.space.test.index.sk" class="">box.space.test.index.sk</a>:select{}<br class="">+ | ---<br class="">+ | - - [1, 0.1]<br class="">+ | - [2, 0.2]<br class="">+ | - [3, 0.3]<br class="">+ | - [14, 0.33]<br class="">+ | - [4, 0.4]<br class="">+ | - [5, 0.5]<br class="">+ | - [13, 0.57]<br class="">+ | - [6, 0.6]<br class="">+ | - [12, 0.63]<br class="">+ | - [7, 0.7]<br class="">+ | - [16, 0.71]<br class="">+ | - [8, 0.8]<br class="">+ | - [9, 0.9]<br class="">+ | - [10, 1]<br class="">+ | - [11, 'str']<br class="">+ | ...<br class="">+<br class="">+box.space.test:drop()<br class="">+ | ---<br class="">+ | ...<br class="">diff --git a/test/engine/decimal.test.lua b/test/engine/decimal.test.lua<br class="">new file mode 100644<br class="">index 000000000..52a300e72<br class="">--- /dev/null<br class="">+++ b/test/engine/decimal.test.lua<br class="">@@ -0,0 +1,65 @@<br class="">+env = require('test_run')<br class="">+test_run = env.new()<br class="">+engine = test_run:get_cfg('engine')<br class="">+<br class="">+decimal = require('decimal')<br class="">+ffi = require('ffi')<br class="">+<br class="">+_ = box.schema.space.create('test', {engine=engine})<br class="">+_ = box.space.test:create_index('pk', {parts={1,'decimal'}})<br class="">+<br class="">+test_run:cmd('setopt delimiter ";"')<br class="">+for i = 0,16 do<br class="">+ box.space.test:insert{decimal.new((i-8)/4)}<br class="">+end;<br class="">+test_run:cmd('setopt delimiter ""');<br class="">+<br class="">+box.space.test:select{}<br class="">+<br class="">+-- check invalid values<br class="">+box.space.test:insert{1.23}<br class="">+box.space.test:insert{'str'}<br class="">+box.space.test:insert{ffi.new('uint64_t', 0)}<br class="">+-- check duplicates<br class="">+box.space.test:insert{decimal.new(0)}<br class="">+<br class="">+box.space.test.index.pk:drop()<br class="">+<br class="">+_ = box.space.test:create_index('pk', {parts={1, 'number'}})<br class="">+<br class="">+test_run:cmd('setopt delimiter ";"')<br class="">+for i = 0, 32 do<br class="">+ local val = (i - 16) / 8<br class="">+ if i % 2 == 1 then val = decimal.new(val) end<br class="">+ box.space.test:insert{val}<br class="">+end;<br class="">+test_run:cmd('setopt delimiter ""');<br class="">+<br class="">+box.space.test:select{}<br class="">+<br class="">+-- check duplicates<br class="">+box.space.test:insert{-2}<br class="">+box.space.test:insert{decimal.new(-2)}<br class="">+box.space.test:insert{decimal.new(-1.875)}<br class="">+box.space.test:insert{-1.875}<br class="">+<br class="">+box.space.test.index.pk:drop()<br class="">+<br class="">+_ = box.space.test:create_index('pk')<br class="">+test_run:cmd('setopt delimiter ";"')<br class="">+for i = 1,10 do<br class="">+ box.space.test:insert{i, decimal.new(i/10)}<br class="">+end;<br class="">+test_run:cmd('setopt delimiter ""');<br class="">+<br class="">+-- a bigger test with a secondary index this time.<br class="">+box.space.test:insert{11, 'str'}<br class="">+box.space.test:insert{12, 0.63}<br class="">+box.space.test:insert{13, 0.57}<br class="">+box.space.test:insert{14, 0.33}<br class="">+box.space.test:insert{16, 0.71}<br class="">+<br class="">+_ = box.space.test:create_index('sk', {parts={2, 'scalar'}})<br class="">+<a href="http://box.space.test.index.sk" class="">box.space.test.index.sk</a>:select{}<br class="">+<br class="">+box.space.test:drop()<br class="">-- <br class="">2.20.1 (Apple Git-117)</div><div><br class=""><blockquote type="cite" class=""><div class=""><div class=""><br class="">-- <br class="">Konstantin Osipov, Moscow, Russia<br class=""></div></div></blockquote><div><br class=""></div><br class=""><div><div class="">--</div><div class="">Serge Petrenko</div><div class=""><a href="mailto:sergepetrenko@tarantool.org" class="">sergepetrenko@tarantool.org</a></div></div></div><br class=""></body></html>