diff --git a/src/box/field_def.c b/src/box/field_def.c index da766d586fdbbf873db4fd9b69a015957eed90f8..fde4e5a00abb5610759c1474e681a24782db57fe 100644 --- a/src/box/field_def.c +++ b/src/box/field_def.c @@ -59,6 +59,7 @@ const uint32_t field_mp_type[] = { /* [FIELD_TYPE_STRING] = */ 1U << MP_STR, /* [FIELD_TYPE_NUMBER] = */ (1U << MP_UINT) | (1U << MP_INT) | (1U << MP_FLOAT) | (1U << MP_DOUBLE), + /* [FIELD_TYPE_DOUBLE] = */ 1U << MP_DOUBLE, /* [FIELD_TYPE_INTEGER] = */ (1U << MP_UINT) | (1U << MP_INT), /* [FIELD_TYPE_BOOLEAN] = */ 1U << MP_BOOL, /* [FIELD_TYPE_VARBINARY] = */ 1U << MP_BIN, @@ -75,6 +76,7 @@ const uint32_t field_ext_type[] = { /* [FIELD_TYPE_UNSIGNED] = */ 0, /* [FIELD_TYPE_STRING] = */ 0, /* [FIELD_TYPE_NUMBER] = */ 1U << MP_DECIMAL, + /* [FIELD_TYPE_DOUBLE] = */ 0, /* [FIELD_TYPE_INTEGER] = */ 0, /* [FIELD_TYPE_BOOLEAN] = */ 0, /* [FIELD_TYPE_VARBINARY] = */ 0, @@ -89,6 +91,7 @@ const char *field_type_strs[] = { /* [FIELD_TYPE_UNSIGNED] = */ "unsigned", /* [FIELD_TYPE_STRING] = */ "string", /* [FIELD_TYPE_NUMBER] = */ "number", + /* [FIELD_TYPE_DOUBLE] = */ "double", /* [FIELD_TYPE_INTEGER] = */ "integer", /* [FIELD_TYPE_BOOLEAN] = */ "boolean", /* [FIELD_TYPE_VARBINARY] = */"varbinary", @@ -120,18 +123,19 @@ 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 INTEGER BOOLEAN VARBINARY SCALAR DECIMAL ARRAY MAP */ -/* ANY */ true, false, false, false, false, false, false, false, false, false, false, -/* UNSIGNED */ true, true, false, true, true, false, false, true, false, false, false, -/* STRING */ true, false, true, false, false, false, false, true, false, false, false, -/* NUMBER */ true, false, false, true, false, false, false, true, false, false, false, -/* INTEGER */ true, false, false, true, true, false, false, true, false, false, false, -/* BOOLEAN */ true, false, false, false, false, true, false, true, false, false, false, -/* VARBINARY*/ true, false, false, false, false, false, true, true, false, false, false, -/* SCALAR */ true, false, false, false, false, false, false, true, false, false, false, -/* DECIMAL */ true, false, false, true, false, false, false, true, true, false, false, -/* ARRAY */ true, false, false, false, false, false, false, false, false, true, false, -/* MAP */ true, false, false, false, false, false, false, false, false, false, true, + /* ANY UNSIGNED STRING NUMBER DOUBLE INTEGER BOOLEAN VARBINARY SCALAR DECIMAL ARRAY MAP */ +/* ANY */ true, false, false, false, false, false, false, false, false, false, false, false, +/* UNSIGNED */ true, true, false, true, false, true, false, false, true, false, false, false, +/* STRING */ true, false, true, false, false, false, false, false, true, false, false, false, +/* NUMBER */ true, false, false, true, false, false, false, false, true, false, false, false, +/* DOUBLE */ true, false, false, true, true, false, false, false, true, false, false, false, +/* INTEGER */ true, false, false, true, false, true, false, false, true, false, false, false, +/* BOOLEAN */ true, false, false, false, false, false, true, false, true, false, false, false, +/* VARBINARY*/ true, false, false, false, false, false, false, true, true, false, false, false, +/* SCALAR */ true, false, false, false, false, false, false, false, true, false, false, false, +/* DECIMAL */ true, false, false, true, false, false, false, false, true, true, false, false, +/* ARRAY */ true, false, false, false, false, false, false, false, false, false, true, false, +/* MAP */ true, 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 49c2998f57853bc097d65ce8a073ac5bb420ba07..8e82369f13462f6dcf609c848718716b67715b93 100644 --- a/src/box/field_def.h +++ b/src/box/field_def.h @@ -54,6 +54,7 @@ enum field_type { FIELD_TYPE_UNSIGNED, FIELD_TYPE_STRING, FIELD_TYPE_NUMBER, + FIELD_TYPE_DOUBLE, FIELD_TYPE_INTEGER, FIELD_TYPE_BOOLEAN, FIELD_TYPE_VARBINARY, diff --git a/src/box/tuple_compare.cc b/src/box/tuple_compare.cc index 9573ff1ec3c5d1c0f94bab938ef0fb6999360365..3f8a0ce244cec02618ddae45c2d91f6a627ad066 100644 --- a/src/box/tuple_compare.cc +++ b/src/box/tuple_compare.cc @@ -121,6 +121,14 @@ mp_compare_bool(const char *field_a, const char *field_b) return COMPARE_RESULT(a_val, b_val); } +static int +mp_compare_double(const char *field_a, const char *field_b) +{ + double a_val = mp_decode_double(&field_a); + double b_val = mp_decode_double(&field_b); + return COMPARE_RESULT(a_val, b_val); +} + static int mp_compare_integer_with_type(const char *field_a, enum mp_type a_type, const char *field_b, enum mp_type b_type) @@ -443,6 +451,8 @@ tuple_compare_field(const char *field_a, const char *field_b, mp_typeof(*field_b)); case FIELD_TYPE_NUMBER: return mp_compare_number(field_a, field_b); + case FIELD_TYPE_DOUBLE: + return mp_compare_double(field_a, field_b); case FIELD_TYPE_BOOLEAN: return mp_compare_bool(field_a, field_b); case FIELD_TYPE_VARBINARY: @@ -477,6 +487,8 @@ tuple_compare_field_with_type(const char *field_a, enum mp_type a_type, case FIELD_TYPE_NUMBER: return mp_compare_number_with_type(field_a, a_type, field_b, b_type); + case FIELD_TYPE_DOUBLE: + return mp_compare_double(field_a, field_b); case FIELD_TYPE_BOOLEAN: return mp_compare_bool(field_a, field_b); case FIELD_TYPE_VARBINARY: @@ -1630,6 +1642,13 @@ field_hint_integer(const char *field) return HINT_NONE; } +static inline hint_t +field_hint_double(const char *field) +{ + assert(mp_typeof(*field) == MP_DOUBLE); + return hint_double(mp_decode_double(&field)); +} + static inline hint_t field_hint_number(const char *field) { @@ -1753,6 +1772,8 @@ field_hint(const char *field, struct coll *coll) return field_hint_integer(field); case FIELD_TYPE_NUMBER: return field_hint_number(field); + case FIELD_TYPE_DOUBLE: + return field_hint_double(field); case FIELD_TYPE_STRING: return field_hint_string(field, coll); case FIELD_TYPE_VARBINARY: @@ -1857,6 +1878,9 @@ key_def_set_hint_func(struct key_def *def) case FIELD_TYPE_NUMBER: key_def_set_hint_func<FIELD_TYPE_NUMBER>(def); break; + case FIELD_TYPE_DOUBLE: + key_def_set_hint_func<FIELD_TYPE_DOUBLE>(def); + break; case FIELD_TYPE_STRING: key_def_set_hint_func<FIELD_TYPE_STRING>(def); break; diff --git a/test/engine/insert.result b/test/engine/insert.result index 1015b6a6d86d636ce0b6d91d3b099deb2d915ef5..13ffe8ceb19ab16f2ad5de9a5820356b9498cb00 100644 --- a/test/engine/insert.result +++ b/test/engine/insert.result @@ -730,3 +730,261 @@ s:drop() fiber = nil --- ... +-- gh-3812: Make sure that DOUBLE field type works properly. +ffi = require('ffi') +--- +... +s = box.schema.space.create('s', {format = {{'i', 'integer'}, {'d', 'double'}}}) +--- +... +_ = s:create_index('ii') +--- +... +-- +-- If number of Lua type NUMBER is not integer, than it could be +-- inserted in DOUBLE field. +-- +s:insert({1, 1.1}) +--- +- [1, 1.1] +... +s:insert({2, 2.5}) +--- +- [2, 2.5] +... +s:insert({3, -3.0009}) +--- +- [3, -3.0009] +... +-- +-- Integers of Lua type NUMBER and CDATA of type int64 or uint64 +-- cannot be inserted into this field. +-- +s:insert({4, 1}) +--- +- error: 'Tuple field 2 type does not match one required by operation: expected double' +... +s:insert({5, -9223372036854775800ULL}) +--- +- error: 'Tuple field 2 type does not match one required by operation: expected double' +... +s:insert({6, 18000000000000000000ULL}) +--- +- error: 'Tuple field 2 type does not match one required by operation: expected double' +... +-- +-- To insert an integer, we must cast it to a CDATA of type DOUBLE +-- using ffi.cast(). Non-integers can also be inserted this way. +-- +s:insert({7, ffi.cast('double', 1)}) +--- +- [7, 1] +... +s:insert({8, ffi.cast('double', -9223372036854775808)}) +--- +- [8, -9223372036854775808] +... +s:insert({9, ffi.cast('double', tonumber('123'))}) +--- +- [9, 123] +... +s:insert({10, ffi.cast('double', tonumber64('18000000000000000000'))}) +--- +- [10, 18000000000000000000] +... +s:insert({11, ffi.cast('double', 1.1)}) +--- +- [11, 1.1] +... +s:insert({12, ffi.cast('double', -3.0009)}) +--- +- [12, -3.0009] +... +s:select() +--- +- - [1, 1.1] + - [2, 2.5] + - [3, -3.0009] + - [7, 1] + - [8, -9223372036854775808] + - [9, 123] + - [10, 18000000000000000000] + - [11, 1.1] + - [12, -3.0009] +... +-- The same rules apply to the key of this field: +dd = s:create_index('dd', {unique = false, parts = {{2, 'double'}}}) +--- +... +dd:select(1.1) +--- +- - [1, 1.1] + - [11, 1.1] +... +dd:select(1) +--- +- error: 'Supplied key type of part 0 does not match index part type: expected double' +... +dd:select(ffi.cast('double', 1)) +--- +- - [7, 1] +... +-- Make sure the comparisons work correctly. +dd:select(1.1, {iterator = 'ge'}) +--- +- - [1, 1.1] + - [11, 1.1] + - [2, 2.5] + - [9, 123] + - [10, 18000000000000000000] +... +dd:select(1.1, {iterator = 'le'}) +--- +- - [11, 1.1] + - [1, 1.1] + - [7, 1] + - [12, -3.0009] + - [3, -3.0009] + - [8, -9223372036854775808] +... +dd:select(ffi.cast('double', 1.1), {iterator = 'gt'}) +--- +- - [2, 2.5] + - [9, 123] + - [10, 18000000000000000000] +... +dd:select(ffi.cast('double', 1.1), {iterator = 'lt'}) +--- +- - [7, 1] + - [12, -3.0009] + - [3, -3.0009] + - [8, -9223372036854775808] +... +dd:select(1.1, {iterator = 'all'}) +--- +- - [1, 1.1] + - [11, 1.1] + - [2, 2.5] + - [9, 123] + - [10, 18000000000000000000] +... +dd:select(1.1, {iterator = 'eq'}) +--- +- - [1, 1.1] + - [11, 1.1] +... +dd:select(1.1, {iterator = 'req'}) +--- +- - [11, 1.1] + - [1, 1.1] +... +s:delete(11) +--- +- [11, 1.1] +... +s:delete(12) +--- +- [12, -3.0009] +... +-- Make sure that other operations are working correctly: +ddd = s:create_index('ddd', {parts = {{2, 'double'}}}) +--- +... +s:update(1, {{'=', 2, 2}}) +--- +- error: 'Tuple field 2 type does not match one required by operation: expected double' +... +s:insert({22, 22}) +--- +- error: 'Tuple field 2 type does not match one required by operation: expected double' +... +s:upsert({10, 100}, {{'=', 2, 2}}) +--- +- error: 'Tuple field 2 type does not match one required by operation: expected double' +... +s:upsert({100, 100}, {{'=', 2, 2}}) +--- +- error: 'Tuple field 2 type does not match one required by operation: expected double' +... +ddd:update(1, {{'=', 1, 70}}) +--- +- error: 'Supplied key type of part 0 does not match index part type: expected double' +... +ddd:delete(1) +--- +- error: 'Supplied key type of part 0 does not match index part type: expected double' +... +s:update(2, {{'=', 2, 2.55}}) +--- +- [2, 2.55] +... +s:replace({22, 22.22}) +--- +- [22, 22.22] +... +s:upsert({100, 100.5}, {{'=', 2, 2}}) +--- +... +s:get(100) +--- +- [100, 100.5] +... +s:upsert({10, 100.5}, {{'=', 2, 2.2}}) +--- +... +s:get(10) +--- +- [10, 2.2] +... +ddd:update(1.1, {{'=', 3, 111}}) +--- +- [1, 1.1, 111] +... +ddd:delete(1.1) +--- +- [1, 1.1, 111] +... +s:update(2, {{'=', 2, ffi.cast('double', 255)}}) +--- +- [2, 255] +... +s:replace({22, ffi.cast('double', 22)}) +--- +- [22, 22] +... +s:upsert({200, ffi.cast('double', 200)}, {{'=', 2, 222}}) +--- +... +s:get(200) +--- +- [200, 200] +... +s:upsert({200, ffi.cast('double', 200)}, {{'=', 2, ffi.cast('double', 222)}}) +--- +... +s:get(200) +--- +- [200, 222] +... +ddd:update(ffi.cast('double', 1), {{'=', 3, 7}}) +--- +- [7, 1, 7] +... +ddd:delete(ffi.cast('double', 123)) +--- +- [9, 123] +... +s:select() +--- +- - [2, 255] + - [3, -3.0009] + - [7, 1, 7] + - [8, -9223372036854775808] + - [10, 2.2] + - [22, 22] + - [100, 100.5] + - [200, 222] +... +s:drop() +--- +... diff --git a/test/engine/insert.test.lua b/test/engine/insert.test.lua index 39b150169b82637913ac0546a5baa77dbe58bbb1..2ffc8cd0a067c772a45c4a30e5db2ce2ddf184ae 100644 --- a/test/engine/insert.test.lua +++ b/test/engine/insert.test.lua @@ -123,3 +123,90 @@ s:select{} s:drop() fiber = nil +-- gh-3812: Make sure that DOUBLE field type works properly. +ffi = require('ffi') + +s = box.schema.space.create('s', {format = {{'i', 'integer'}, {'d', 'double'}}}) +_ = s:create_index('ii') + +-- +-- If number of Lua type NUMBER is not integer, than it could be +-- inserted in DOUBLE field. +-- +s:insert({1, 1.1}) +s:insert({2, 2.5}) +s:insert({3, -3.0009}) + +-- +-- Integers of Lua type NUMBER and CDATA of type int64 or uint64 +-- cannot be inserted into this field. +-- +s:insert({4, 1}) +s:insert({5, -9223372036854775800ULL}) +s:insert({6, 18000000000000000000ULL}) + +-- +-- To insert an integer, we must cast it to a CDATA of type DOUBLE +-- using ffi.cast(). Non-integers can also be inserted this way. +-- +s:insert({7, ffi.cast('double', 1)}) +s:insert({8, ffi.cast('double', -9223372036854775808)}) +s:insert({9, ffi.cast('double', tonumber('123'))}) +s:insert({10, ffi.cast('double', tonumber64('18000000000000000000'))}) +s:insert({11, ffi.cast('double', 1.1)}) +s:insert({12, ffi.cast('double', -3.0009)}) + +s:select() + +-- The same rules apply to the key of this field: +dd = s:create_index('dd', {unique = false, parts = {{2, 'double'}}}) +dd:select(1.1) +dd:select(1) +dd:select(ffi.cast('double', 1)) + +-- Make sure the comparisons work correctly. +dd:select(1.1, {iterator = 'ge'}) +dd:select(1.1, {iterator = 'le'}) +dd:select(ffi.cast('double', 1.1), {iterator = 'gt'}) +dd:select(ffi.cast('double', 1.1), {iterator = 'lt'}) +dd:select(1.1, {iterator = 'all'}) +dd:select(1.1, {iterator = 'eq'}) +dd:select(1.1, {iterator = 'req'}) + +s:delete(11) +s:delete(12) + +-- Make sure that other operations are working correctly: +ddd = s:create_index('ddd', {parts = {{2, 'double'}}}) + +s:update(1, {{'=', 2, 2}}) +s:insert({22, 22}) +s:upsert({10, 100}, {{'=', 2, 2}}) +s:upsert({100, 100}, {{'=', 2, 2}}) + +ddd:update(1, {{'=', 1, 70}}) +ddd:delete(1) + +s:update(2, {{'=', 2, 2.55}}) +s:replace({22, 22.22}) +s:upsert({100, 100.5}, {{'=', 2, 2}}) +s:get(100) +s:upsert({10, 100.5}, {{'=', 2, 2.2}}) +s:get(10) + +ddd:update(1.1, {{'=', 3, 111}}) +ddd:delete(1.1) + +s:update(2, {{'=', 2, ffi.cast('double', 255)}}) +s:replace({22, ffi.cast('double', 22)}) +s:upsert({200, ffi.cast('double', 200)}, {{'=', 2, 222}}) +s:get(200) +s:upsert({200, ffi.cast('double', 200)}, {{'=', 2, ffi.cast('double', 222)}}) +s:get(200) + +ddd:update(ffi.cast('double', 1), {{'=', 3, 7}}) +ddd:delete(ffi.cast('double', 123)) + +s:select() + +s:drop()