Skip to content

Commit 009f494

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

File tree

4 files changed

+324
-0
lines changed

4 files changed

+324
-0
lines changed

src/box/sql/mem.c

+14
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,18 @@ map_to_datetime(struct Mem *mem)
13081308
return 0;
13091309
}
13101310

1311+
/** Convert MEM from MAP to INTERVAL. */
1312+
static inline int
1313+
map_to_interval(struct Mem *mem)
1314+
{
1315+
assert(mem->type == MEM_TYPE_MAP);
1316+
struct interval itv;
1317+
if (interval_from_map(&itv, mem->z) != 0)
1318+
return -1;
1319+
mem_set_interval(mem, &itv);
1320+
return 0;
1321+
}
1322+
13111323
int
13121324
mem_to_int(struct Mem *mem)
13131325
{
@@ -1490,6 +1502,8 @@ mem_cast_explicit(struct Mem *mem, enum field_type type)
14901502
mem->flags = 0;
14911503
return 0;
14921504
case FIELD_TYPE_INTERVAL:
1505+
if (mem->type == MEM_TYPE_MAP)
1506+
return map_to_interval(mem);
14931507
if (mem->type != MEM_TYPE_INTERVAL)
14941508
return -1;
14951509
mem->flags = 0;

src/lib/core/datetime.c

+126
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,58 @@ get_double_from_mp(const char **data, double *value)
929929
return 0;
930930
}
931931

932+
/** Parse msgpack value and convert it to int32, if possible. */
933+
static int
934+
get_int32_from_mp(const char **data, int32_t *value)
935+
{
936+
switch (mp_typeof(**data)) {
937+
case MP_INT: {
938+
int64_t val = mp_decode_int(data);
939+
if (val < INT32_MIN)
940+
return -1;
941+
*value = val;
942+
break;
943+
}
944+
case MP_UINT: {
945+
uint64_t val = mp_decode_uint(data);
946+
if (val > INT32_MAX)
947+
return -1;
948+
*value = val;
949+
break;
950+
}
951+
case MP_DOUBLE: {
952+
double val = mp_decode_double(data);
953+
if (val > (double)INT32_MAX || val < (double)INT32_MIN)
954+
return -1;
955+
if (val != floor(val))
956+
return -1;
957+
*value = val;
958+
break;
959+
}
960+
case MP_EXT: {
961+
int8_t type;
962+
uint32_t len = mp_decode_extl(data, &type);
963+
if (type != MP_DECIMAL)
964+
return -1;
965+
decimal_t dec;
966+
if (decimal_unpack(data, len, &dec) == NULL)
967+
return -1;
968+
if (!decimal_is_int(&dec))
969+
return -1;
970+
int64_t val;
971+
if (decimal_to_int64(&dec, &val) == NULL)
972+
return -1;
973+
if (val < INT32_MIN || val > INT32_MAX)
974+
return -1;
975+
*value = val;
976+
break;
977+
}
978+
default:
979+
return -1;
980+
}
981+
return 0;
982+
}
983+
932984
/** Define field of DATETIME value from field of given MAP value.*/
933985
static int
934986
map_field_to_dt_field(struct dt_fields *fields, const char **data)
@@ -1050,3 +1102,77 @@ datetime_from_map(struct datetime *dt, const char *data)
10501102
}
10511103
return datetime_from_fields(dt, &fields);
10521104
}
1105+
1106+
/** Define field of INTERVAL value from field of given MAP value.*/
1107+
static int
1108+
map_field_to_itv_field(struct interval *itv, const char **data)
1109+
{
1110+
if (mp_typeof(**data) != MP_STR) {
1111+
mp_next(data);
1112+
mp_next(data);
1113+
return 0;
1114+
}
1115+
uint32_t size;
1116+
const char *str = mp_decode_str(data, &size);
1117+
double *dvalue = NULL;
1118+
int32_t *ivalue = NULL;
1119+
if (strncmp(str, "year", size) == 0) {
1120+
ivalue = &itv->year;
1121+
} else if (strncmp(str, "month", size) == 0) {
1122+
ivalue = &itv->month;
1123+
} else if (strncmp(str, "week", size) == 0) {
1124+
ivalue = &itv->week;
1125+
} else if (strncmp(str, "day", size) == 0) {
1126+
dvalue = &itv->day;
1127+
} else if (strncmp(str, "hour", size) == 0) {
1128+
dvalue = &itv->hour;
1129+
} else if (strncmp(str, "min", size) == 0) {
1130+
dvalue = &itv->min;
1131+
} else if (strncmp(str, "sec", size) == 0) {
1132+
dvalue = &itv->sec;
1133+
} else if (strncmp(str, "nsec", size) == 0) {
1134+
ivalue = &itv->nsec;
1135+
} else if (strncmp(str, "adjust", size) == 0) {
1136+
if (mp_typeof(**data) != MP_STR)
1137+
return -1;
1138+
uint32_t vsize;
1139+
const char *val = mp_decode_str(data, &vsize);
1140+
if (strncasecmp(val, "none", vsize) == 0)
1141+
itv->adjust = DT_LIMIT;
1142+
else if (strncasecmp(val, "last", vsize) == 0)
1143+
itv->adjust = DT_SNAP;
1144+
else if (strncasecmp(val, "excess", vsize) == 0)
1145+
itv->adjust = DT_EXCESS;
1146+
else
1147+
return -1;
1148+
return 0;
1149+
} else {
1150+
mp_next(data);
1151+
return 0;
1152+
}
1153+
if (dvalue != NULL) {
1154+
double val;
1155+
if (get_double_from_mp(data, &val) != 0)
1156+
return -1;
1157+
if (val != floor(val))
1158+
return -1;
1159+
*dvalue = val;
1160+
return 0;
1161+
}
1162+
assert(ivalue != NULL);
1163+
return get_int32_from_mp(data, ivalue);
1164+
}
1165+
1166+
int
1167+
interval_from_map(struct interval *itv, const char *data)
1168+
{
1169+
assert(mp_typeof(*data) == MP_MAP);
1170+
uint32_t len = mp_decode_map(&data);
1171+
memset(itv, 0, sizeof(*itv));
1172+
itv->adjust = DT_LIMIT;
1173+
for (uint32_t i = 0; i < len; ++i) {
1174+
if (map_field_to_itv_field(itv, &data) != 0)
1175+
return -1;
1176+
}
1177+
return interval_check_args(itv) == 0 ? 0 : -1;
1178+
}

src/lib/core/datetime.h

+4
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,10 @@ datetime_usec(const struct datetime *date)
385385
int
386386
datetime_from_map(struct datetime *dt, const char *data);
387387

388+
/** Parse MAP value and construct INTERVAL value. */
389+
int
390+
interval_from_map(struct interval *itv, const char *data);
391+
388392
#if defined(__cplusplus)
389393
} /* extern "C" */
390394
#endif /* defined(__cplusplus) */

test/sql-luatest/datetime_test.lua

+180
Original file line numberDiff line numberDiff line change
@@ -2680,3 +2680,183 @@ g.test_datetime_33_3 = function()
26802680
t.assert_equals(box.execute(sql).rows, {{dt1}})
26812681
end)
26822682
end
2683+
2684+
-- Make sure cast from MAP to INTERVAL works as intended.
2685+
2686+
--
2687+
-- The result of CAST() from MAP value to INTERVAL must be equal to the result
2688+
-- of calling require('datetime').interval.new() with the corresponding table as
2689+
-- an argument.
2690+
--
2691+
g.test_datetime_34_1 = function()
2692+
g.server:exec(function()
2693+
local t = require('luatest')
2694+
local itv = require('datetime').interval
2695+
local v = setmetatable({}, { __serialize = 'map' })
2696+
local sql = [[SELECT CAST(#v AS INTERVAL);]]
2697+
t.assert_equals(box.execute(sql, {{['#v'] = v}}).rows, {{itv.new(v)}})
2698+
2699+
v.something = 1
2700+
t.assert_equals(box.execute(sql, {{['#v'] = v}}).rows, {{itv.new(v)}})
2701+
2702+
v = {year = 1, month = 1, week = 1, day = 1, hour = 1, min = 1, sec = 1,
2703+
nsec = 1, adjust = 'none'}
2704+
t.assert_equals(box.execute(sql, {{['#v'] = v}}).rows, {{itv.new(v)}})
2705+
end)
2706+
end
2707+
2708+
--
2709+
-- Make sure an error is thrown if the INTERVAL value cannot be constructed from
2710+
-- the corresponding table.
2711+
--
2712+
g.test_datetime_34_2 = function()
2713+
g.server:exec(function()
2714+
local t = require('luatest')
2715+
local sql = [[SELECT CAST(#v AS INTERVAL);]]
2716+
2717+
-- "year" cannot be more than 11759221.
2718+
local v = {year = 11759222}
2719+
local _, err = box.execute(sql, {{['#v'] = v}})
2720+
local res = [[Type mismatch: can not convert ]]..
2721+
[[map({"year": 11759222}) to interval]]
2722+
t.assert_equals(err.message, res)
2723+
2724+
-- "year" cannot be less than -11759221.
2725+
v = {year = -11759222}
2726+
_, err = box.execute(sql, {{['#v'] = v}})
2727+
res = [[Type mismatch: can not convert ]]..
2728+
[[map({"year": -11759222}) to interval]]
2729+
t.assert_equals(err.message, res)
2730+
2731+
-- "month" cannot be more than 141110652.
2732+
v = {month = 141110653}
2733+
_, err = box.execute(sql, {{['#v'] = v}})
2734+
res = [[Type mismatch: can not convert ]]..
2735+
[[map({"month": 141110653}) to interval]]
2736+
t.assert_equals(err.message, res)
2737+
2738+
-- "month" cannot be less than -141110652.
2739+
v = {month = -141110653}
2740+
_, err = box.execute(sql, {{['#v'] = v}})
2741+
res = [[Type mismatch: can not convert ]]..
2742+
[[map({"month": -141110653}) to interval]]
2743+
t.assert_equals(err.message, res)
2744+
2745+
-- "week" cannot be more than 613579352.
2746+
v = {week = 613579353}
2747+
_, err = box.execute(sql, {{['#v'] = v}})
2748+
res = [[Type mismatch: can not convert ]]..
2749+
[[map({"week": 613579353}) to interval]]
2750+
t.assert_equals(err.message, res)
2751+
2752+
-- "week" cannot be less than -613579352.
2753+
v = {week = -613579353}
2754+
_, err = box.execute(sql, {{['#v'] = v}})
2755+
res = [[Type mismatch: can not convert ]]..
2756+
[[map({"week": -613579353}) to interval]]
2757+
t.assert_equals(err.message, res)
2758+
2759+
-- "day" cannot be more than 4295055470.
2760+
v = {day = 4295055471}
2761+
_, err = box.execute(sql, {{['#v'] = v}})
2762+
res = [[Type mismatch: can not convert ]]..
2763+
[[map({"day": 4295055471}) to interval]]
2764+
t.assert_equals(err.message, res)
2765+
2766+
-- "day" cannot be less than -4295055470.
2767+
v = {day = -4295055471}
2768+
_, err = box.execute(sql, {{['#v'] = v}})
2769+
res = [[Type mismatch: can not convert ]]..
2770+
[[map({"day": -4295055471}) to interval]]
2771+
t.assert_equals(err.message, res)
2772+
2773+
-- "hour" cannot be more than 103081331286.
2774+
v = {hour = 103081331287}
2775+
_, err = box.execute(sql, {{['#v'] = v}})
2776+
res = [[Type mismatch: can not convert ]]..
2777+
[[map({"hour": 103081331287}) to interval]]
2778+
t.assert_equals(err.message, res)
2779+
2780+
-- "hour" cannot be less than 103081331286.
2781+
v = {hour = -103081331287}
2782+
_, err = box.execute(sql, {{['#v'] = v}})
2783+
res = [[Type mismatch: can not convert ]]..
2784+
[[map({"hour": -103081331287}) to interval]]
2785+
t.assert_equals(err.message, res)
2786+
2787+
-- "min" cannot be more than 6184879877160.
2788+
v = {min = 6184879877161}
2789+
_, err = box.execute(sql, {{['#v'] = v}})
2790+
res = [[Type mismatch: can not convert ]]..
2791+
[[map({"min": 6184879877161}) to interval]]
2792+
t.assert_equals(err.message, res)
2793+
2794+
-- "min" cannot be less than -6184879877160.
2795+
v = {min = -6184879877161}
2796+
_, err = box.execute(sql, {{['#v'] = v}})
2797+
res = [[Type mismatch: can not convert ]]..
2798+
[[map({"min": -6184879877161}) to interval]]
2799+
t.assert_equals(err.message, res)
2800+
2801+
-- "sec" cannot be more than 371092792629600.
2802+
v = {sec = 371092792629601}
2803+
_, err = box.execute(sql, {{['#v'] = v}})
2804+
res = [[Type mismatch: can not convert ]]..
2805+
[[map({"sec": 371092792629601}) to interval]]
2806+
t.assert_equals(err.message, res)
2807+
2808+
-- "sec" cannot be less than -371092792629600.
2809+
v = {sec = -371092792629601}
2810+
_, err = box.execute(sql, {{['#v'] = v}})
2811+
res = [[Type mismatch: can not convert ]]..
2812+
[[map({"sec": -371092792629601}) to interval]]
2813+
t.assert_equals(err.message, res)
2814+
2815+
-- "nsec" cannot be more than 2147483647.
2816+
v = {nsec = 2147483648}
2817+
_, err = box.execute(sql, {{['#v'] = v}})
2818+
res = [[Type mismatch: can not convert map({"nsec": 2147483648}) ]]..
2819+
[[to interval]]
2820+
t.assert_equals(err.message, res)
2821+
2822+
-- "nsec" cannot be less than -2147483647.
2823+
v = {nsec = -2147483648}
2824+
_, err = box.execute(sql, {{['#v'] = v}})
2825+
res = [[Type mismatch: can not convert map({"nsec": -2147483648}) ]]..
2826+
[[to interval]]
2827+
t.assert_equals(err.message, res)
2828+
2829+
-- "adjust" cannot be anything other than "none", "excess", "last".
2830+
v = {adjust = 1}
2831+
_, err = box.execute(sql, {{['#v'] = v}})
2832+
res = [[Type mismatch: can not convert map({"adjust": 1}) ]]..
2833+
[[to interval]]
2834+
t.assert_equals(err.message, res)
2835+
2836+
v = {adjust = 'asd'}
2837+
_, err = box.execute(sql, {{['#v'] = v}})
2838+
res = [[Type mismatch: can not convert map({"adjust": "asd"}) ]]..
2839+
[[to interval]]
2840+
t.assert_equals(err.message, res)
2841+
end)
2842+
end
2843+
2844+
--
2845+
-- Make sure that any of the DECIMAL, INTEGER, and DOUBLE values can be used as
2846+
-- values in the MAP converted to a INTERVAL.
2847+
--
2848+
g.test_datetime_34_3 = function()
2849+
g.server:exec(function()
2850+
local t = require('luatest')
2851+
local itv = require('datetime').interval
2852+
local dt1 = itv.new({year = 1})
2853+
local sql = [[SELECT CAST({'year': 1.0} AS INTERVAL);]]
2854+
t.assert_equals(box.execute(sql).rows, {{dt1}})
2855+
2856+
sql = [[SELECT CAST({'year': 1.e0} AS INTERVAL);]]
2857+
t.assert_equals(box.execute(sql).rows, {{dt1}})
2858+
2859+
sql = [[SELECT CAST({'year': 1} AS INTERVAL);]]
2860+
t.assert_equals(box.execute(sql).rows, {{dt1}})
2861+
end)
2862+
end

0 commit comments

Comments
 (0)