Skip to content

Commit

Permalink
box, datetime: add messagepack support for datetime
Browse files Browse the repository at this point in the history
Serialize datetime_t as newly introduced MP_EXT type.
It saves 1 required integer field and upto 2 optional
unsigned fields in very compact fashion.
- secs is required field;
- but nsec, offset are both optional;

* json, yaml serialization formats, lua output mode
  supported;

Part of tarantool#5941
Part of tarantool#5946
  • Loading branch information
tsafin committed Jul 27, 2021
1 parent f0f123f commit e1961ba
Show file tree
Hide file tree
Showing 20 changed files with 312 additions and 22 deletions.
34 changes: 19 additions & 15 deletions src/box/field_def.c
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = {
Expand All @@ -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[] = {
Expand All @@ -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[] = {
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/box/field_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ enum field_type {
FIELD_TYPE_UUID,
FIELD_TYPE_ARRAY,
FIELD_TYPE_MAP,
FIELD_TYPE_DATETIME,
field_type_MAX
};

Expand Down
7 changes: 6 additions & 1 deletion src/box/lua/serialize_lua.c
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ static int
dump_node(struct lua_dumper *d, struct node *nd, int indent)
{
struct luaL_field *field = &nd->field;
char buf[FPCONV_G_FMT_BUFSIZE];
char buf[FPCONV_G_FMT_BUFSIZE + 8];
int ltype = lua_type(d->L, -1);
const char *str = NULL;
size_t len = 0;
Expand Down Expand Up @@ -861,6 +861,11 @@ dump_node(struct lua_dumper *d, struct node *nd, int indent)
str = tt_uuid_str(field->uuidval);
len = UUID_STR_LEN;
break;
case MP_DATETIME:
nd->mask |= NODE_QUOTE;
str = buf;
len = datetime_to_string(field->dateval, buf, sizeof buf);
break;
default:
d->err = EINVAL;
snprintf(d->err_msg, sizeof(d->err_msg),
Expand Down
7 changes: 6 additions & 1 deletion src/box/msgpack.c
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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:
Expand All @@ -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:
Expand Down
20 changes: 20 additions & 0 deletions src/box/tuple_compare.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -390,6 +394,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)
{
datetime_t lhs_dt, rhs_dt;
datetime_t *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,
Expand All @@ -398,6 +415,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,
};
Expand Down Expand Up @@ -478,6 +496,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;
Expand Down
2 changes: 2 additions & 0 deletions src/exports.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion src/lib/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,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})
Expand Down
161 changes: 161 additions & 0 deletions src/lib/core/datetime.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,132 @@

#include "trivia/util.h"
#include "datetime.h"
#include "msgpuck.h"
#include "mp_extension_types.h"

/*
Datetime MessagePack serialization schema is MP_EXT (0xC7 for 1 byte length)
extension, which creates container of 1 to 3 integers.
+----+---+-----------+====~~~~~~~====+-----~~~~~~~~-------+....~~~~~~~....+
|0xC7| 4 |len (uint8)| seconds (int) | nanoseconds (uint) | offset (uint) |
+----+---+-----------+====~~~~~~~====+-----~~~~~~~~-------+....~~~~~~~....+
MessagePack extension MP_EXT (0xC7), after 1-byte length, contains:
- signed integer seconds part (required). Depending on the value of
seconds it may be from 1 to 8 bytes positive or negative integer number;
- [optional] fraction time in nanoseconds as unsigned integer.
If this value is 0 then it's not saved (unless there is offset field,
as below);
- [optional] timzeone offset in minutes as unsigned integer.
If this field is 0 then it's not saved.
*/

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 datetime_t *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 datetime_t *
datetime_unpack(const char **data, uint32_t len, struct datetime_t *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 datetime_t *
mp_decode_datetime(const char **data, struct datetime_t *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 datetime_t *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 datetime_t *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 datetime_t * date, char *buf, uint32_t len)
Expand Down Expand Up @@ -83,3 +209,38 @@ datetime_to_string(const struct datetime_t * date, char *buf, uint32_t len)
}
return (buf - src);
}

int
mp_snprint_datetime(char *buf, int size, const char **data, uint32_t len)
{
struct datetime_t 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 datetime_t date;

if (datetime_unpack(data, len, &date) == NULL)
return -1;

char buf[48];
datetime_to_string(&date, buf, sizeof buf);

return fprintf(file, "%s", buf);
}

int datetime_compare(const struct datetime_t *lhs, const struct datetime_t *rhs)
{
int result = COMPARE_RESULT(lhs->secs, rhs->secs);
if (result != 0)
return result;

return COMPARE_RESULT(lhs->nsec, rhs->nsec);
}

Loading

0 comments on commit e1961ba

Please sign in to comment.