Skip to content

Commit 1ccb1c2

Browse files
ImeevMAkyukhin
authored andcommitted
sql: introduce cast from MAP to DATETIME
This patch introduces explicit cast from MAP to DATETIME. Follow-up tarantool#6773 NO_DOC=DATETIME has already been introduced. NO_CHANGELOG=DATETIME has already been introduced.
1 parent 42c3633 commit 1ccb1c2

File tree

4 files changed

+500
-0
lines changed

4 files changed

+500
-0
lines changed

src/box/sql/mem.c

+14
Original file line numberDiff line numberDiff line change
@@ -1296,6 +1296,18 @@ uuid_to_bin(struct Mem *mem)
12961296
return mem_copy_bin(mem, (char *)&mem->u.uuid, UUID_LEN);
12971297
}
12981298

1299+
/** Convert MEM from MAP to DATETIME. */
1300+
static inline int
1301+
map_to_datetime(struct Mem *mem)
1302+
{
1303+
assert(mem->type == MEM_TYPE_MAP);
1304+
struct datetime dt;
1305+
if (datetime_from_map(&dt, mem->z) != 0)
1306+
return -1;
1307+
mem_set_datetime(mem, &dt);
1308+
return 0;
1309+
}
1310+
12991311
int
13001312
mem_to_int(struct Mem *mem)
13011313
{
@@ -1471,6 +1483,8 @@ mem_cast_explicit(struct Mem *mem, enum field_type type)
14711483
case FIELD_TYPE_DATETIME:
14721484
if (mem->type == MEM_TYPE_STR)
14731485
return str_to_datetime(mem);
1486+
if (mem->type == MEM_TYPE_MAP)
1487+
return map_to_datetime(mem);
14741488
if (mem->type != MEM_TYPE_DATETIME)
14751489
return -1;
14761490
mem->flags = 0;

src/lib/core/datetime.c

+189
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* Copyright 2021, Tarantool AUTHORS, please see AUTHORS file.
55
*/
66

7+
#include <math.h>
78
#include <assert.h>
89
#include <limits.h>
910
#include <string.h>
@@ -12,11 +13,14 @@
1213
#include <inttypes.h>
1314

1415
#define DT_PARSE_ISO_TNT
16+
#include "decimal.h"
17+
#include "msgpuck.h"
1518
#include "c-dt/dt.h"
1619
#include "datetime.h"
1720
#include "trivia/util.h"
1821
#include "tzcode/tzcode.h"
1922
#include "tzcode/timezone.h"
23+
#include "mp_extension_types.h"
2024

2125
#include "fiber.h"
2226

@@ -861,3 +865,188 @@ interval_interval_add(struct interval *lhs, const struct interval *rhs)
861865
lhs->nsec += rhs->nsec;
862866
return interval_check_args(lhs);
863867
}
868+
869+
/** This structure contains information about the given date and time fields. */
870+
struct dt_fields {
871+
/* Specified year. */
872+
double year;
873+
/* Specified month. */
874+
double month;
875+
/* Specified day. */
876+
double day;
877+
/* Specified hour. */
878+
double hour;
879+
/* Specified minute. */
880+
double min;
881+
/* Specified second. */
882+
double sec;
883+
/* Specified millisecond. */
884+
double msec;
885+
/* Specified microsecond. */
886+
double usec;
887+
/* Specified nanosecond. */
888+
double nsec;
889+
/* Specified timestamp. */
890+
double timestamp;
891+
/* Specified timezone offset. */
892+
double tzoffset;
893+
/* Number of given fields among msec, usec and nsec. */
894+
int count_usec;
895+
/* True, if any of year, month, day, hour, min or sec is specified. */
896+
bool is_ymdhms;
897+
/* True, if timestamp is specified. */
898+
bool is_ts;
899+
};
900+
901+
/** Parse msgpack value and convert it to double, if possible. */
902+
static int
903+
get_double_from_mp(const char **data, double *value)
904+
{
905+
switch (mp_typeof(**data)) {
906+
case MP_INT:
907+
*value = mp_decode_int(data);
908+
break;
909+
case MP_UINT:
910+
*value = mp_decode_uint(data);
911+
break;
912+
case MP_DOUBLE:
913+
*value = mp_decode_double(data);
914+
break;
915+
case MP_EXT: {
916+
int8_t type;
917+
uint32_t len = mp_decode_extl(data, &type);
918+
if (type != MP_DECIMAL)
919+
return -1;
920+
decimal_t dec;
921+
if (decimal_unpack(data, len, &dec) == NULL)
922+
return -1;
923+
*value = atof(decimal_str(&dec));
924+
break;
925+
}
926+
default:
927+
return -1;
928+
}
929+
return 0;
930+
}
931+
932+
/** Define field of DATETIME value from field of given MAP value.*/
933+
static int
934+
map_field_to_dt_field(struct dt_fields *fields, const char **data)
935+
{
936+
if (mp_typeof(**data) != MP_STR) {
937+
mp_next(data);
938+
mp_next(data);
939+
return 0;
940+
}
941+
uint32_t size;
942+
const char *str = mp_decode_str(data, &size);
943+
double *value;
944+
if (strncmp(str, "year", size) == 0) {
945+
value = &fields->year;
946+
fields->is_ymdhms = true;
947+
} else if (strncmp(str, "month", size) == 0) {
948+
value = &fields->month;
949+
fields->is_ymdhms = true;
950+
} else if (strncmp(str, "day", size) == 0) {
951+
value = &fields->day;
952+
fields->is_ymdhms = true;
953+
} else if (strncmp(str, "hour", size) == 0) {
954+
value = &fields->hour;
955+
fields->is_ymdhms = true;
956+
} else if (strncmp(str, "min", size) == 0) {
957+
value = &fields->min;
958+
fields->is_ymdhms = true;
959+
} else if (strncmp(str, "sec", size) == 0) {
960+
value = &fields->sec;
961+
fields->is_ymdhms = true;
962+
} else if (strncmp(str, "msec", size) == 0) {
963+
value = &fields->msec;
964+
++fields->count_usec;
965+
} else if (strncmp(str, "usec", size) == 0) {
966+
value = &fields->usec;
967+
++fields->count_usec;
968+
} else if (strncmp(str, "nsec", size) == 0) {
969+
value = &fields->nsec;
970+
++fields->count_usec;
971+
} else if (strncmp(str, "timestamp", size) == 0) {
972+
value = &fields->timestamp;
973+
fields->is_ts = true;
974+
} else if (strncmp(str, "tzoffset", size) == 0) {
975+
value = &fields->tzoffset;
976+
} else {
977+
mp_next(data);
978+
return 0;
979+
}
980+
return get_double_from_mp(data, value);
981+
}
982+
983+
/** Create a DATETIME value using fields of the DATETIME. */
984+
static int
985+
datetime_from_fields(struct datetime *dt, const struct dt_fields *fields)
986+
{
987+
if (fields->count_usec > 1)
988+
return -1;
989+
double nsec = fields->msec * 1000000 + fields->usec * 1000 +
990+
fields->nsec;
991+
if (nsec < 0 || nsec > 1000000000)
992+
return -1;
993+
if (fields->tzoffset < -720 || fields->tzoffset > 840)
994+
return -1;
995+
if (fields->timestamp < (double)INT32_MIN * SECS_PER_DAY ||
996+
fields->timestamp > (double)INT32_MAX * SECS_PER_DAY)
997+
return -1;
998+
if (fields->is_ts) {
999+
if (fields->is_ymdhms)
1000+
return -1;
1001+
double timestamp = floor(fields->timestamp);
1002+
double frac = fields->timestamp - timestamp;
1003+
if (frac != 0) {
1004+
if (fields->count_usec > 0)
1005+
return -1;
1006+
nsec = frac * 1000000000;
1007+
}
1008+
dt->epoch = timestamp;
1009+
dt->nsec = nsec;
1010+
dt->tzoffset = fields->tzoffset;
1011+
dt->tzindex = 0;
1012+
return 0;
1013+
}
1014+
if (fields->year < MIN_DATE_YEAR || fields->year > MAX_DATE_YEAR)
1015+
return -1;
1016+
if (fields->month < 1 || fields->month > 12)
1017+
return -1;
1018+
if (fields->day < 1 ||
1019+
fields->day > dt_days_in_month(fields->year, fields->month))
1020+
return -1;
1021+
if (fields->hour < 0 || fields->hour > 23)
1022+
return -1;
1023+
if (fields->min < 0 || fields->min > 59)
1024+
return -1;
1025+
if (fields->sec < 0 || fields->sec > 60)
1026+
return -1;
1027+
double days = dt_from_ymd(fields->year, fields->month, fields->day) -
1028+
DT_EPOCH_1970_OFFSET;
1029+
dt->epoch = days * SECS_PER_DAY + fields->hour * 3600 +
1030+
fields->min * 60 + fields->sec;
1031+
dt->nsec = nsec;
1032+
dt->tzoffset = fields->tzoffset;
1033+
dt->tzindex = 0;
1034+
return 0;
1035+
}
1036+
1037+
int
1038+
datetime_from_map(struct datetime *dt, const char *data)
1039+
{
1040+
assert(mp_typeof(*data) == MP_MAP);
1041+
uint32_t len = mp_decode_map(&data);
1042+
struct dt_fields fields;
1043+
memset(&fields, 0, sizeof(fields));
1044+
fields.year = 1970;
1045+
fields.month = 1;
1046+
fields.day = 1;
1047+
for (uint32_t i = 0; i < len; ++i) {
1048+
if (map_field_to_dt_field(&fields, &data) != 0)
1049+
return -1;
1050+
}
1051+
return datetime_from_fields(dt, &fields);
1052+
}

src/lib/core/datetime.h

+4
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,10 @@ datetime_usec(const struct datetime *date)
381381
return datetime_nsec(date) / 1000;
382382
}
383383

384+
/** Parse MAP value and construct DATETIME value. */
385+
int
386+
datetime_from_map(struct datetime *dt, const char *data);
387+
384388
#if defined(__cplusplus)
385389
} /* extern "C" */
386390
#endif /* defined(__cplusplus) */

0 commit comments

Comments
 (0)