From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id 60A1D6EC55; Thu, 15 Jul 2021 11:21:24 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 60A1D6EC55 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1626337284; bh=3dNXVx/LPQ5F4J/QdkPiLYHdW+ae4UBkzC5dUgne1qY=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=tkl5uiEJosLzRzpt3kLLYJKSFx/nGSMhNotKsw/qHWEMllFPHNm4Zk7J6oqgLVvT4 tEpK+bu8jOp1MBgv/m/fpJv09Mrg06eSixUpKIf2iohuHoDs9HIH6YhHbHV+J0cx7p hUtW/Sbyrr79JC0ZlBTgezvKboMJ6pcdzUHtsbS4= Received: from smtp57.i.mail.ru (smtp57.i.mail.ru [217.69.128.37]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id AAE146EC70 for ; Thu, 15 Jul 2021 11:18:54 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org AAE146EC70 Received: by smtp57.i.mail.ru with esmtpa (envelope-from ) id 1m3wa5-00070N-E2; Thu, 15 Jul 2021 11:18:54 +0300 To: v.shpilevoy@tarantool.org Date: Thu, 15 Jul 2021 11:18:11 +0300 Message-Id: X-Mailer: git-send-email 2.29.2 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-4EC0790: 10 X-7564579A: EEAE043A70213CC8 X-77F55803: 4F1203BC0FB41BD941C43E597735A9C3FDAB68B812060C779F2375474EF21E8A182A05F5380850403F8C7D6CE92ED115C061D0CB33BC9A25CE711CB56BB34CF2EF073EDC55A481C5 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE75DF2B1F23425CAE5EA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637358384D8B7467A5C8638F802B75D45FF36EB9D2243A4F8B5A6FCA7DBDB1FC311F39EFFDF887939037866D6147AF826D81AC48A37916B7C30AFF842ADFAD012CB117882F4460429724CE54428C33FAD305F5C1EE8F4F765FCAA867293B0326636D2E47CDBA5A96583BD4B6F7A4D31EC0BC014FD901B82EE079FA2833FD35BB23D27C277FBC8AE2E8BAA867293B0326636D2E47CDBA5A96583BA9C0B312567BB2376E601842F6C81A19E625A9149C048EEC24E1E72F37C03A0FCB1554B277F7060D8FC6C240DEA7642DBF02ECDB25306B2B78CF848AE20165D0A6AB1C7CE11FEE365B78C30F681404D9735652A29929C6CC4224003CC836476EA7A3FFF5B025636E2021AF6380DFAD1A18204E546F3947CB11811A4A51E3B096D1867E19FE1407959CC434672EE6371089D37D7C0E48F6C8AA50765F7900637AD0424077D726551EFF80C71ABB335746BA297DBC24807EABDAD6C7F3747799A X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C414F749A5E30D975CE68746B1F2AB10C66ECFC6E21FFDC17B32743D04F2CF9C2B9C2B6934AE262D3EE7EAB7254005DCED7532B743992DF240BDC6A1CF3F042BAD6DF99611D93F60EF3033054805BDE987699F904B3F4130E343918A1A30D5E7FCCB5012B2E24CD356 X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D34D8FD11F74BDD6E8DF4FA6A1AD945B9BFC985B25ECFF1D090C857E6915A3A1ADAE03706967D2F531B1D7E09C32AA3244CE06CA989F11507880D29F8BAF729305E97FE24653F78E66883B48618A63566E0 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojbL9S8ysBdXjnYxxC9kpVbzfEjLET5sfk X-Mailru-Sender: B5B6A6EBBD94DAD8DA84A184D75F19DB2E7B7B20C3E75D0F2BF8D213EA334E3A932997F95CA482A31EC9E4A2C82A33BC8C24925A86E657CE0C70AEE3C9A96FBAB3D7EE8ED63280BE112434F685709FCF0DA7A0AF5A3A8387 X-Mras: Ok Subject: [Tarantool-patches] [RFC PATCH 05/13] box: add messagepack support for datetime X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Timur Safin via Tarantool-patches Reply-To: Timur Safin Cc: tarantool-patches@dev.tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" EDIT HERE * implemented mp_snprint_datetime and mp_fprint_datetime for generic extended messagepack types stringization * json and yaml serialization for DATETIME data type --- src/box/field_def.c | 34 +++-- src/box/field_def.h | 1 + src/box/msgpack.c | 7 +- src/box/tuple_compare.cc | 24 ++++ src/exports.h | 2 + src/lib/core/CMakeLists.txt | 4 +- src/lib/core/datetime.h | 37 ++++- src/lib/core/mp_datetime.c | 232 ++++++++++++++++++++++++++++++ src/lib/core/mp_extension_types.h | 1 + src/lib/mpstream/mpstream.c | 11 ++ src/lib/mpstream/mpstream.h | 4 + src/lua/datetime.c | 6 + src/lua/datetime.h | 2 +- src/lua/msgpack.c | 12 ++ src/lua/msgpackffi.lua | 8 ++ src/lua/serializer.c | 4 + src/lua/serializer.h | 2 + third_party/lua-cjson/lua_cjson.c | 8 ++ third_party/lua-yaml/lyaml.cc | 6 +- 19 files changed, 385 insertions(+), 20 deletions(-) create mode 100644 src/lib/core/mp_datetime.c diff --git a/src/box/field_def.c b/src/box/field_def.c index 51acb8025..33606a52d 100644 --- a/src/box/field_def.c +++ b/src/box/field_def.c @@ -67,11 +67,12 @@ const uint32_t field_mp_type[] = { /* [FIELD_TYPE_VARBINARY] = */ 1U << MP_BIN, /* [FIELD_TYPE_SCALAR] = */ (1U << MP_UINT) | (1U << MP_INT) | (1U << MP_FLOAT) | (1U << MP_DOUBLE) | (1U << MP_STR) | - (1U << MP_BIN) | (1U << MP_BOOL), + (1U << MP_BIN) | (1U << MP_BOOL) | (1U << MP_DATETIME), /* [FIELD_TYPE_DECIMAL] = */ 0, /* only MP_DECIMAL is supported */ /* [FIELD_TYPE_UUID] = */ 0, /* only MP_UUID is supported */ /* [FIELD_TYPE_ARRAY] = */ 1U << MP_ARRAY, /* [FIELD_TYPE_MAP] = */ (1U << MP_MAP), + /* [FIELD_TYPE_DATETIME] = */ 0, /* only MP_DATETIME is supported */ }; const uint32_t field_ext_type[] = { @@ -88,6 +89,7 @@ const uint32_t field_ext_type[] = { /* [FIELD_TYPE_UUID] = */ 1U << MP_UUID, /* [FIELD_TYPE_ARRAY] = */ 0, /* [FIELD_TYPE_MAP] = */ 0, + /* [FIELD_TYPE_DATETIME] = */ 1U << MP_DATETIME, }; const char *field_type_strs[] = { @@ -104,6 +106,7 @@ const char *field_type_strs[] = { /* [FIELD_TYPE_UUID] = */ "uuid", /* [FIELD_TYPE_ARRAY] = */ "array", /* [FIELD_TYPE_MAP] = */ "map", + /* [FIELD_TYPE_DATETIME] = */ "datetime", }; const char *on_conflict_action_strs[] = { @@ -128,20 +131,21 @@ field_type_by_name_wrapper(const char *str, uint32_t len) * values can be stored in the j type. */ static const bool field_type_compatibility[] = { - /* ANY UNSIGNED STRING NUMBER DOUBLE INTEGER BOOLEAN VARBINARY SCALAR DECIMAL UUID ARRAY MAP */ -/* ANY */ true, false, false, false, false, false, false, false, false, false, false, false, false, -/* UNSIGNED */ true, true, false, true, false, true, false, false, true, false, false, false, false, -/* STRING */ true, false, true, false, false, false, false, false, true, false, false, false, false, -/* NUMBER */ true, false, false, true, false, false, false, false, true, false, false, false, false, -/* DOUBLE */ true, false, false, true, true, false, false, false, true, false, false, false, false, -/* INTEGER */ true, false, false, true, false, true, false, false, true, false, false, false, false, -/* BOOLEAN */ true, false, false, false, false, false, true, false, true, false, false, false, false, -/* VARBINARY*/ true, false, false, false, false, false, false, true, true, false, false, false, false, -/* SCALAR */ true, false, false, false, false, false, false, false, true, false, false, false, false, -/* DECIMAL */ true, false, false, true, false, false, false, false, true, true, false, false, false, -/* UUID */ true, false, false, false, false, false, false, false, false, false, true, false, false, -/* ARRAY */ true, false, false, false, false, false, false, false, false, false, false, true, false, -/* MAP */ true, false, false, false, false, false, false, false, false, false, false, false, true, + /* ANY UNSIGNED STRING NUMBER DOUBLE INTEGER BOOLEAN VARBINARY SCALAR DECIMAL UUID ARRAY MAP DATETIME */ +/* ANY */ true, false, false, false, false, false, false, false, false, false, false, false, false, false, +/* UNSIGNED */ true, true, false, true, false, true, false, false, true, false, false, false, false, false, +/* STRING */ true, false, true, false, false, false, false, false, true, false, false, false, false, false, +/* NUMBER */ true, false, false, true, false, false, false, false, true, false, false, false, false, false, +/* DOUBLE */ true, false, false, true, true, false, false, false, true, false, false, false, false, false, +/* INTEGER */ true, false, false, true, false, true, false, false, true, false, false, false, false, false, +/* BOOLEAN */ true, false, false, false, false, false, true, false, true, false, false, false, false, false, +/* VARBINARY*/ true, false, false, false, false, false, false, true, true, false, false, false, false, false, +/* SCALAR */ true, false, false, false, false, false, false, false, true, false, false, false, false, false, +/* DECIMAL */ true, false, false, true, false, false, false, false, true, true, false, false, false, false, +/* UUID */ true, false, false, false, false, false, false, false, false, false, true, false, false, false, +/* ARRAY */ true, false, false, false, false, false, false, false, false, false, false, true, false, false, +/* MAP */ true, false, false, false, false, false, false, false, false, false, false, false, true, false, +/* DATETIME */ true, false, false, false, false, false, false, false, false, false, false, false, false, true, }; bool diff --git a/src/box/field_def.h b/src/box/field_def.h index c5cfe5e86..120b2a93d 100644 --- a/src/box/field_def.h +++ b/src/box/field_def.h @@ -63,6 +63,7 @@ enum field_type { FIELD_TYPE_UUID, FIELD_TYPE_ARRAY, FIELD_TYPE_MAP, + FIELD_TYPE_DATETIME, field_type_MAX }; diff --git a/src/box/msgpack.c b/src/box/msgpack.c index 1723dea4c..e53af548c 100644 --- a/src/box/msgpack.c +++ b/src/box/msgpack.c @@ -1,5 +1,5 @@ /* - * Copyright 2020, Tarantool AUTHORS, please see AUTHORS file. + * Copyright 2020-2021, Tarantool AUTHORS, please see AUTHORS file. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following @@ -35,6 +35,7 @@ #include "mp_decimal.h" #include "uuid/mp_uuid.h" #include "mp_error.h" +#include "datetime.h" static int msgpack_fprint_ext(FILE *file, const char **data, int depth) @@ -47,6 +48,8 @@ msgpack_fprint_ext(FILE *file, const char **data, int depth) return mp_fprint_decimal(file, data, len); case MP_UUID: return mp_fprint_uuid(file, data, len); + case MP_DATETIME: + return mp_fprint_datetime(file, data, len); case MP_ERROR: return mp_fprint_error(file, data, depth); default: @@ -65,6 +68,8 @@ msgpack_snprint_ext(char *buf, int size, const char **data, int depth) return mp_snprint_decimal(buf, size, data, len); case MP_UUID: return mp_snprint_uuid(buf, size, data, len); + case MP_DATETIME: + return mp_snprint_datetime(buf, size, data, len); case MP_ERROR: return mp_snprint_error(buf, size, data, depth); default: diff --git a/src/box/tuple_compare.cc b/src/box/tuple_compare.cc index 98938fb39..39c9dd6e9 100644 --- a/src/box/tuple_compare.cc +++ b/src/box/tuple_compare.cc @@ -36,6 +36,7 @@ #include "lib/core/decimal.h" #include "lib/core/mp_decimal.h" #include "uuid/mp_uuid.h" +#include "core/datetime.h" #include "lib/core/mp_extension_types.h" /* {{{ tuple_compare */ @@ -76,6 +77,7 @@ enum mp_class { MP_CLASS_STR, MP_CLASS_BIN, MP_CLASS_UUID, + MP_CLASS_DATETIME, MP_CLASS_ARRAY, MP_CLASS_MAP, mp_class_max, @@ -99,6 +101,8 @@ static enum mp_class mp_ext_classes[] = { /* .MP_UNKNOWN_EXTENSION = */ mp_class_max, /* unsupported */ /* .MP_DECIMAL = */ MP_CLASS_NUMBER, /* .MP_UUID = */ MP_CLASS_UUID, + /* .MP_ERROR = */ mp_class_max, + /* .MP_DATETIME = */ MP_CLASS_DATETIME, }; static enum mp_class @@ -299,6 +303,8 @@ mp_compare_number_with_type(const char *lhs, enum mp_type lhs_type, return mp_compare_decimal_any_number( decimal_unpack(&rhs, len, &dec), lhs, lhs_type, -1 ); + case MP_DATETIME: + // FIXME default: unreachable(); } @@ -311,6 +317,8 @@ mp_compare_number_with_type(const char *lhs, enum mp_type lhs_type, return mp_compare_decimal_any_number( decimal_unpack(&lhs, len, &dec), rhs, rhs_type, 1 ); + case MP_DATETIME: + // FIXME default: unreachable(); } @@ -390,6 +398,19 @@ mp_compare_uuid(const char *field_a, const char *field_b) return memcmp(field_a + 2, field_b + 2, UUID_PACKED_LEN); } +static int +mp_compare_datetime(const char *lhs, const char *rhs) +{ + t_datetime_tz lhs_dt, rhs_dt; + t_datetime_tz *ret; + ret = mp_decode_datetime(&lhs, &lhs_dt); + assert(ret != NULL); + ret = mp_decode_datetime(&rhs, &rhs_dt); + assert(ret != NULL); + (void)ret; + return datetime_compare(&lhs_dt, &rhs_dt); +} + typedef int (*mp_compare_f)(const char *, const char *); static mp_compare_f mp_class_comparators[] = { /* .MP_CLASS_NIL = */ NULL, @@ -398,6 +419,7 @@ static mp_compare_f mp_class_comparators[] = { /* .MP_CLASS_STR = */ mp_compare_str, /* .MP_CLASS_BIN = */ mp_compare_bin, /* .MP_CLASS_UUID = */ mp_compare_uuid, + /* .MP_CLASS_DATETIME=*/ mp_compare_datetime, /* .MP_CLASS_ARRAY = */ NULL, /* .MP_CLASS_MAP = */ NULL, }; @@ -478,6 +500,8 @@ tuple_compare_field(const char *field_a, const char *field_b, return mp_compare_decimal(field_a, field_b); case FIELD_TYPE_UUID: return mp_compare_uuid(field_a, field_b); + case FIELD_TYPE_DATETIME: + return mp_compare_datetime(field_a, field_b); default: unreachable(); return 0; diff --git a/src/exports.h b/src/exports.h index db40c03a4..7397010e0 100644 --- a/src/exports.h +++ b/src/exports.h @@ -531,6 +531,8 @@ EXPORT(uri_format) EXPORT(uri_parse) EXPORT(uuid_nil) EXPORT(uuid_unpack) +EXPORT(datetime_unpack) +EXPORT(datetime_pack) EXPORT(dt_from_rdn) EXPORT(dt_from_yd) EXPORT(dt_from_ymd) diff --git a/src/lib/core/CMakeLists.txt b/src/lib/core/CMakeLists.txt index 2cd4d0b4f..f1801dcb0 100644 --- a/src/lib/core/CMakeLists.txt +++ b/src/lib/core/CMakeLists.txt @@ -30,6 +30,7 @@ set(core_sources decimal.c mp_decimal.c cord_buf.c + mp_datetime.c ) if (TARGET_OS_NETBSD) @@ -43,7 +44,8 @@ add_library(core STATIC ${core_sources}) target_link_libraries(core salad small uri decNumber bit ${LIBEV_LIBRARIES} ${LIBEIO_LIBRARIES} ${LIBCORO_LIBRARIES} - ${MSGPUCK_LIBRARIES} ${ICU_LIBRARIES}) + ${MSGPUCK_LIBRARIES} ${ICU_LIBRARIES} + ${LIBCDT_LIBRARIES}) if (ENABLE_BACKTRACE AND NOT TARGET_OS_DARWIN) target_link_libraries(core gcc_s ${UNWIND_LIBRARIES}) diff --git a/src/lib/core/datetime.h b/src/lib/core/datetime.h index 837ed346c..bd23f33a3 100644 --- a/src/lib/core/datetime.h +++ b/src/lib/core/datetime.h @@ -30,14 +30,20 @@ * SUCH DAMAGE. */ -#include +#include #include #include +#include #if defined(__cplusplus) extern "C" { #endif /* defined(__cplusplus) */ +#ifndef SECS_PER_DAY +#define SECS_PER_DAY 86400 +#define NANOS_PER_SEC 1000000000 +#endif + /** * datetime structure consisting of: */ @@ -55,6 +61,35 @@ struct t_datetime_duration { int nsec; ///< nanoseconds delta }; +int +datetime_compare(const struct t_datetime_tz * lhs, + const struct t_datetime_tz * rhs); + + +struct t_datetime_tz * +datetime_unpack(const char **data, uint32_t len, struct t_datetime_tz *date); + +char * +datetime_pack(char *data, const struct t_datetime_tz *date); + +uint32_t +mp_sizeof_datetime(const struct t_datetime_tz *date); + +struct t_datetime_tz * +mp_decode_datetime(const char **data, struct t_datetime_tz *date); + +char * +mp_encode_datetime(char *data, const struct t_datetime_tz *date); + +int +datetime_to_string(const struct t_datetime_tz * date, char *buf, uint32_t len); + +int +mp_snprint_datetime(char *buf, int size, const char **data, uint32_t len); + +int +mp_fprint_datetime(FILE *file, const char **data, uint32_t len); + #if defined(__cplusplus) } /* extern "C" */ #endif /* defined(__cplusplus) */ diff --git a/src/lib/core/mp_datetime.c b/src/lib/core/mp_datetime.c new file mode 100644 index 000000000..2cf3fd79b --- /dev/null +++ b/src/lib/core/mp_datetime.c @@ -0,0 +1,232 @@ +/* + * Copyright 2021, Tarantool AUTHORS, please see AUTHORS file. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include "trivia/util.h" +#include "datetime.h" +#include "msgpuck.h" +#include "mp_extension_types.h" + +static inline uint32_t +mp_sizeof_Xint(int64_t n) +{ + return n < 0 ? mp_sizeof_int(n) : mp_sizeof_uint(n); +} + +static inline char * +mp_encode_Xint(char *data, int64_t v) +{ + return v < 0 ? mp_encode_int(data, v) : mp_encode_uint(data, v); +} + +static inline int64_t +mp_decode_Xint(const char **data) +{ + switch (mp_typeof(**data)) { + case MP_UINT: + return (int64_t)mp_decode_uint(data); + case MP_INT: + return mp_decode_int(data); + default: + mp_unreachable(); + } + return 0; +} + +uint32_t +mp_sizeof_datetime(const struct t_datetime_tz *date) +{ + uint32_t sz = mp_sizeof_Xint(date->secs); + + // even if nanosecs == 0 we need to output anything + // if we have non-null tz offset + if (date->nsec != 0 || date->offset != 0) + sz += mp_sizeof_Xint(date->nsec); + if (date->offset) + sz += mp_sizeof_Xint(date->offset); + + return sz; +} + +struct t_datetime_tz * +datetime_unpack(const char **data, uint32_t len, struct t_datetime_tz *date) +{ + const char * svp = *data; + + memset(date, 0, sizeof(*date)); + + date->secs = mp_decode_Xint(data); + + len -= *data - svp; + if (len <= 0) + return date; + + svp = *data; + date->secs = mp_decode_Xint(data); + len -= *data - svp; + + if (len <= 0) + return date; + + date->offset = mp_decode_Xint(data); + + return date; +} + +struct t_datetime_tz * +mp_decode_datetime(const char **data, struct t_datetime_tz *date) +{ + if (mp_typeof(**data) != MP_EXT) + return NULL; + + int8_t type; + uint32_t len = mp_decode_extl(data, &type); + + if (type != MP_DATETIME || len == 0) { + return NULL; + } + return datetime_unpack(data, len, date); +} + +char * +datetime_pack(char *data, const struct t_datetime_tz *date) +{ + data = mp_encode_Xint(data, date->secs); + if (date->nsec != 0 || date->offset != 0) + data = mp_encode_Xint(data, date->nsec); + if (date->offset) + data = mp_encode_Xint(data, date->offset); + + return data; +} + +char * +mp_encode_datetime(char *data, const struct t_datetime_tz *date) +{ + uint32_t len = mp_sizeof_datetime(date); + + data = mp_encode_extl(data, MP_DATETIME, len); + + return datetime_pack(data, date); +} + +int +datetime_to_string(const struct t_datetime_tz * date, char *buf, uint32_t len) +{ + char * src = buf; + int offset = date->offset; + int secs = date->secs + offset * 60; + dt_t dt = dt_from_rdn((secs / SECS_PER_DAY) + 719163); + + int year, month, day, sec, ns, sign; + dt_to_ymd(dt, &year, &month, &day); + + int hour = (secs / 3600) % 24, + minute = (secs / 60) % 60; + ; + sec = secs % 60; + ns = date->nsec; + uint32_t sz; + sz = snprintf(buf, len, "%04d-%02d-%02dT%02d:%02d", + year, month, day, hour, minute); + buf += sz; len -= sz; + if (sec || ns) { + sz = snprintf(buf, len, ":%02d", sec); + buf += sz; len -= sz; + if (ns) { + if ((ns % 1000000) == 0) + sz = snprintf(buf, len, ".%03d", ns / 1000000); + else if ((ns % 1000) == 0) + sz = snprintf(buf, len, ".%06d", ns / 1000); + else + sz = snprintf(buf, len, ".%09d", ns); + buf += sz; len -= sz; + } + } + if (offset == 0) { + strncpy(buf, "Z", len); + buf++; + len--; + } + else { + if (offset < 0) + sign = '-', offset = -offset; + else + sign = '+'; + + sz = snprintf(buf, len, "%c%02d:%02d", sign, offset / 60, offset % 60); + buf += sz; len -= sz; + } + return (buf - src); +} +int +mp_snprint_datetime(char *buf, int size, const char **data, uint32_t len) +{ + struct t_datetime_tz date = {0}; + + if (datetime_unpack(data, len, &date) == NULL) + return -1; + + return datetime_to_string(&date, buf, size); +} + +int +mp_fprint_datetime(FILE *file, const char **data, uint32_t len) +{ + struct t_datetime_tz date; + + if (datetime_unpack(data, len, &date) == NULL) + return -1; + + char buf[128]; + datetime_to_string(&date, buf, sizeof buf); + + return fprintf(file, "%s", buf); +} + +static inline int +adjusted_secs(int secs, int offset) +{ + return secs - offset * 60; +} + +int +datetime_compare(const struct t_datetime_tz * lhs, + const struct t_datetime_tz * rhs) +{ + int result = COMPARE_RESULT(adjusted_secs(lhs->secs, lhs->offset), + adjusted_secs(rhs->secs, rhs->offset)); + if (result != 0) + return result; + + return COMPARE_RESULT(lhs->nsec, rhs->nsec); +} diff --git a/src/lib/core/mp_extension_types.h b/src/lib/core/mp_extension_types.h index e3ff9f5d0..3b7eaee7c 100644 --- a/src/lib/core/mp_extension_types.h +++ b/src/lib/core/mp_extension_types.h @@ -44,6 +44,7 @@ enum mp_extension_type { MP_DECIMAL = 1, MP_UUID = 2, MP_ERROR = 3, + MP_DATETIME = 4, mp_extension_type_MAX, }; diff --git a/src/lib/mpstream/mpstream.c b/src/lib/mpstream/mpstream.c index 70ca29889..8c92b4049 100644 --- a/src/lib/mpstream/mpstream.c +++ b/src/lib/mpstream/mpstream.c @@ -35,6 +35,7 @@ #include "msgpuck.h" #include "mp_decimal.h" #include "uuid/mp_uuid.h" +#include "core/datetime.h" void mpstream_reserve_slow(struct mpstream *stream, size_t size) @@ -208,6 +209,16 @@ mpstream_encode_uuid(struct mpstream *stream, const struct tt_uuid *uuid) mpstream_advance(stream, pos - data); } +void +mpstream_encode_datetime(struct mpstream *stream, const struct t_datetime_tz *val) +{ + char *data = mpstream_reserve(stream, mp_sizeof_datetime(val)); + if (data == NULL) + return; + char *pos = mp_encode_datetime(data, val); + mpstream_advance(stream, pos - data); +} + void mpstream_memcpy(struct mpstream *stream, const void *src, uint32_t n) { diff --git a/src/lib/mpstream/mpstream.h b/src/lib/mpstream/mpstream.h index a60add143..39e8fd956 100644 --- a/src/lib/mpstream/mpstream.h +++ b/src/lib/mpstream/mpstream.h @@ -39,6 +39,7 @@ extern "C" { #endif /* defined(__cplusplus) */ struct tt_uuid; +struct t_datetime_tz; /** * Ask the allocator to reserve at least size bytes. It can reserve @@ -145,6 +146,9 @@ mpstream_encode_decimal(struct mpstream *stream, const decimal_t *val); void mpstream_encode_uuid(struct mpstream *stream, const struct tt_uuid *uuid); +void +mpstream_encode_datetime(struct mpstream *stream, const struct t_datetime_tz *dt); + /** Copies n bytes from memory area src to stream. */ void mpstream_memcpy(struct mpstream *stream, const void *src, uint32_t n); diff --git a/src/lua/datetime.c b/src/lua/datetime.c index 37a8bc020..215cb89fb 100644 --- a/src/lua/datetime.c +++ b/src/lua/datetime.c @@ -39,6 +39,12 @@ uint32_t CTID_DATETIME_TZ = 0; uint32_t CTID_DURATION = 0; +struct t_datetime_tz * +luaL_pushdatetime(struct lua_State *L) +{ + return luaL_pushcdata(L, CTID_DATETIME_TZ); +} + void tarantool_lua_datetime_init(struct lua_State *L) { diff --git a/src/lua/datetime.h b/src/lua/datetime.h index d290a6d13..38ba53561 100644 --- a/src/lua/datetime.h +++ b/src/lua/datetime.h @@ -43,7 +43,7 @@ extern uint32_t CTID_DURATION; struct lua_State; struct t_datetime_tz* -lua_pushdatetime(struct lua_State *L); +luaL_pushdatetime(struct lua_State *L); void tarantool_lua_datetime_init(struct lua_State *L); diff --git a/src/lua/msgpack.c b/src/lua/msgpack.c index b6ecf2b1e..dd2c41056 100644 --- a/src/lua/msgpack.c +++ b/src/lua/msgpack.c @@ -46,6 +46,7 @@ #include "lib/core/decimal.h" /* decimal_unpack() */ #include "lib/uuid/mp_uuid.h" /* mp_decode_uuid() */ #include "lib/core/mp_extension_types.h" +#include "datetime.h" #include "cord_buf.h" #include @@ -200,6 +201,9 @@ restart: /* used by MP_EXT of unidentified subtype */ break; case MP_ERROR: return luamp_encode_extension(L, top, stream); + case MP_DATETIME: + mpstream_encode_datetime(stream, field->dateval); + break; default: /* Run trigger if type can't be encoded */ type = luamp_encode_extension(L, top, stream); @@ -333,6 +337,14 @@ luamp_decode(struct lua_State *L, struct luaL_serializer *cfg, goto ext_decode_err; return; } + case MP_DATETIME: + { + struct t_datetime_tz * date = luaL_pushdatetime(L); + date = datetime_unpack(data, len, date); + if (date == NULL) + goto ext_decode_err; + return; + } default: /* reset data to the extension header */ *data = svp; diff --git a/src/lua/msgpackffi.lua b/src/lua/msgpackffi.lua index 1d54f11b8..2613cf216 100644 --- a/src/lua/msgpackffi.lua +++ b/src/lua/msgpackffi.lua @@ -36,6 +36,8 @@ decimal_t * decimal_unpack(const char **data, uint32_t len, decimal_t *dec); struct tt_uuid * uuid_unpack(const char **data, uint32_t len, struct tt_uuid *uuid); +struct t_datetime_tz * +datetime_unpack(const char **data, uint32_t len, struct t_datetime_tz *date); ]]) local strict_alignment = (jit.arch == 'arm') @@ -513,6 +515,12 @@ local ext_decoder = { builtin.uuid_unpack(data, len, uuid) return uuid end, + -- MP_DATETIME + [4] = function(data, len) + local dt = ffi.new("struct t_datetime_tz") + builtin.datetime_unpack(data, len, dt) + return dt + end, } local function decode_ext(data) diff --git a/src/lua/serializer.c b/src/lua/serializer.c index 8db6746a3..55120a725 100644 --- a/src/lua/serializer.c +++ b/src/lua/serializer.c @@ -41,6 +41,7 @@ #include "lib/core/mp_extension_types.h" #include "lua/error.h" +#include "datetime.h" #include "trivia/util.h" #include "diag.h" #include "serializer_opts.h" @@ -540,6 +541,9 @@ luaL_tofield(struct lua_State *L, struct luaL_serializer *cfg, } else if (cd->ctypeid == CTID_UUID) { field->ext_type = MP_UUID; field->uuidval = (struct tt_uuid *) cdata; + } else if (cd->ctypeid == CTID_DATETIME_TZ) { + field->ext_type = MP_DATETIME; + field->dateval = (struct t_datetime_tz *) cdata; } else if (cd->ctypeid == CTID_CONST_STRUCT_ERROR_REF && opts != NULL && opts->error_marshaling_enabled) { diff --git a/src/lua/serializer.h b/src/lua/serializer.h index 0a0501a74..ec62c723f 100644 --- a/src/lua/serializer.h +++ b/src/lua/serializer.h @@ -52,6 +52,7 @@ extern "C" { #include #include "trigger.h" +#include "lib/core/datetime.h" #include "lib/core/decimal.h" /* decimal_t */ #include "lib/core/mp_extension_types.h" #include "lua/error.h" @@ -223,6 +224,7 @@ struct luaL_field { uint32_t size; decimal_t *decval; struct tt_uuid *uuidval; + struct t_datetime_tz *dateval; }; enum mp_type type; /* subtypes of MP_EXT */ diff --git a/third_party/lua-cjson/lua_cjson.c b/third_party/lua-cjson/lua_cjson.c index 5123b9a74..d5ddc4ea0 100644 --- a/third_party/lua-cjson/lua_cjson.c +++ b/third_party/lua-cjson/lua_cjson.c @@ -52,6 +52,7 @@ #include "mp_extension_types.h" /* MP_DECIMAL, MP_UUID */ #include "tt_static.h" #include "uuid/tt_uuid.h" /* tt_uuid_to_string(), UUID_STR_LEN */ +#include "core/datetime.h" #include "cord_buf.h" typedef enum { @@ -426,6 +427,13 @@ static void json_append_data(lua_State *l, struct luaL_serializer *cfg, case MP_UUID: return json_append_string(cfg, json, tt_uuid_str(field.uuidval), UUID_STR_LEN); + + case MP_DATETIME: + { + char buf[128]; + size_t sz = datetime_to_string(field.dateval, buf, sizeof buf); + return json_append_string(cfg, json, buf, sz); + } default: assert(false); } diff --git a/third_party/lua-yaml/lyaml.cc b/third_party/lua-yaml/lyaml.cc index 5469e9f4f..b76a45dfb 100644 --- a/third_party/lua-yaml/lyaml.cc +++ b/third_party/lua-yaml/lyaml.cc @@ -617,7 +617,7 @@ static int dump_node(struct lua_yaml_dumper *dumper) yaml_event_t ev; yaml_scalar_style_t style = YAML_PLAIN_SCALAR_STYLE; int is_binary = 0; - char buf[FPCONV_G_FMT_BUFSIZE]; + char buf[FPCONV_G_FMT_BUFSIZE + 8]; // FIXME - need extra space for datetime struct luaL_field field; bool unused; (void) unused; @@ -707,6 +707,10 @@ static int dump_node(struct lua_yaml_dumper *dumper) str = tt_uuid_str(field.uuidval); len = UUID_STR_LEN; break; + case MP_DATETIME: + len = datetime_to_string(field.dateval, buf, sizeof buf); + str = buf; + break; default: assert(0); /* checked by luaL_checkfield() */ } -- 2.29.2