diff --git a/changelogs/unreleased/gh-7483-ignore-mpack-types.md b/changelogs/unreleased/gh-7483-ignore-mpack-types.md new file mode 100644 index 0000000000000000000000000000000000000000..780672671bae16f46e4491f25aa32b307f47a877 --- /dev/null +++ b/changelogs/unreleased/gh-7483-ignore-mpack-types.md @@ -0,0 +1,4 @@ +## bugfix/box + +* Fixed the inability to insert an integral number into a double-formatted + field (gh-7483). diff --git a/src/box/field_def.c b/src/box/field_def.c index 6b14c009204b1961304fd13bb70627052360dcf3..469fd0fc22bffb72045518b76c2e582b4fd3dbb7 100644 --- a/src/box/field_def.c +++ b/src/box/field_def.c @@ -69,7 +69,8 @@ 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_DOUBLE] = */ (1U << MP_UINT) | (1U << MP_INT) | + (1U << MP_FLOAT) | (1U << MP_DOUBLE), /* [FIELD_TYPE_INTEGER] = */ (1U << MP_UINT) | (1U << MP_INT), /* [FIELD_TYPE_BOOLEAN] = */ 1U << MP_BOOL, /* [FIELD_TYPE_VARBINARY] = */ 1U << MP_BIN, diff --git a/src/box/key_def.h b/src/box/key_def.h index 03d6718a779faac65c6b5dba70937dc153932e73..942e8868daa4c780a22e47f9ef5792f4a4879da6 100644 --- a/src/box/key_def.h +++ b/src/box/key_def.h @@ -1071,6 +1071,7 @@ tuple_compare_with_key(struct tuple *tuple, hint_t tuple_hint, /** * Compute hash of a tuple field. + * @param type - type of the field key part * @param ph1 - pointer to running hash * @param pcarry - pointer to carry * @param field - pointer to field data @@ -1082,7 +1083,7 @@ tuple_compare_with_key(struct tuple *tuple, hint_t tuple_hint, */ uint32_t tuple_hash_field(uint32_t *ph1, uint32_t *pcarry, const char **field, - struct coll *coll); + enum field_type type, struct coll *coll); /** * Compute hash of a key part. diff --git a/src/box/tuple_bloom.c b/src/box/tuple_bloom.c index 25fca26861a4e421592013ee0c2f1a3fe2db75a5..f92ace38c4bdcb4534769b4b4c27a7abf59fc390 100644 --- a/src/box/tuple_bloom.c +++ b/src/box/tuple_bloom.c @@ -141,6 +141,7 @@ tuple_bloom_builder_add_key(struct tuple_bloom_builder *builder, for (uint32_t i = 0; i < key_def->part_count; i++) { total_size += tuple_hash_field(&h, &carry, &key, + key_def->parts[i].type, key_def->parts[i].coll); uint32_t hash = PMurHash32_Result(h, carry, total_size); if (tuple_hash_array_add(&builder->parts[i], hash) != 0) @@ -249,6 +250,7 @@ tuple_bloom_maybe_has_key(const struct tuple_bloom *bloom, for (uint32_t i = 0; i < part_count; i++) { total_size += tuple_hash_field(&h, &carry, &key, + key_def->parts[i].type, key_def->parts[i].coll); uint32_t hash = PMurHash32_Result(h, carry, total_size); if (!bloom_maybe_has(&bloom->parts[i], hash)) diff --git a/src/box/tuple_compare.cc b/src/box/tuple_compare.cc index fc34797a425acc96cce25019a68bef3e06d44169..83d0cc0b1191aa4eca6d26dc1e7b29ebeb74db88 100644 --- a/src/box/tuple_compare.cc +++ b/src/box/tuple_compare.cc @@ -105,6 +105,23 @@ mp_extension_class(const char *data) return mp_ext_classes[type]; } +/* + * @brief Read the msgpack value at \p field (whatever it is: int, uint, float + * or double) as double. + * @param field the field to read the value from + * @retval the read-and-cast result + */ +static double +mp_read_as_double(const char *field) +{ + double result = NAN; + assert(mp_classof(mp_typeof(*field)) == MP_CLASS_NUMBER); + /* This can only fail on non-numeric msgpack field, so it shouldn't. */ + if (mp_read_double_lossy(&field, &result) == -1) + unreachable(); + return result; +} + static int mp_compare_bool(const char *field_a, const char *field_b) { @@ -114,10 +131,10 @@ mp_compare_bool(const char *field_a, const char *field_b) } static int -mp_compare_double(const char *field_a, const char *field_b) +mp_compare_as_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); + double a_val = mp_read_as_double(field_a); + double b_val = mp_read_as_double(field_b); return COMPARE_RESULT(a_val, b_val); } @@ -492,7 +509,7 @@ tuple_compare_field(const char *field_a, const char *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); + return mp_compare_as_double(field_a, field_b); case FIELD_TYPE_BOOLEAN: return mp_compare_bool(field_a, field_b); case FIELD_TYPE_VARBINARY: @@ -532,7 +549,7 @@ tuple_compare_field_with_type(const char *field_a, enum mp_type a_type, 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); + return mp_compare_as_double(field_a, field_b); case FIELD_TYPE_BOOLEAN: return mp_compare_bool(field_a, field_b); case FIELD_TYPE_VARBINARY: @@ -1837,8 +1854,7 @@ field_hint_integer(const char *field) static inline hint_t field_hint_double(const char *field) { - assert(mp_typeof(*field) == MP_DOUBLE); - return hint_double(mp_decode_double(&field)); + return hint_double(mp_read_as_double(field)); } static inline hint_t diff --git a/src/box/tuple_hash.cc b/src/box/tuple_hash.cc index 9d6f9ba10ce6bb515e97199f0e6ce23ba59bf236..1c921dda125a72f3a10d3c0b76cd6f90aaa2cb23 100644 --- a/src/box/tuple_hash.cc +++ b/src/box/tuple_hash.cc @@ -46,6 +46,17 @@ template <int TYPE> static inline uint32_t field_hash(uint32_t *ph, uint32_t *pcarry, const char **field) { + /* + * For string key field we should hash the string contents excluding + * MsgPack format identifier. The overload is defined below. + */ + static_assert(TYPE != FIELD_TYPE_STRING, "See the comment above."); + /* + * For double key field we should cast the MsgPack value (whatever it + * is: int, uint, float or double) to double, encode it as msgpack + * double and hash it. No overload for it now. + */ + static_assert(TYPE != FIELD_TYPE_DOUBLE, "See the comment above."); /* * (!) All fields, except TYPE_STRING hashed **including** MsgPack format * identifier (e.g. 0xcc). This was done **intentionally** @@ -221,7 +232,7 @@ template <bool has_optional_parts, bool has_json_paths> uint32_t tuple_hash_slowpath(struct tuple *tuple, struct key_def *key_def); -uint32_t +static uint32_t key_hash_slowpath(const char *key, struct key_def *key_def); void @@ -276,12 +287,41 @@ key_def_set_hash_func(struct key_def *key_def) { uint32_t tuple_hash_field(uint32_t *ph1, uint32_t *pcarry, const char **field, - struct coll *coll) + enum field_type type, struct coll *coll) { - char buf[9]; /* enough to store MP_INT/MP_UINT */ + char buf[9]; /* enough to store MP_INT/MP_UINT/MP_DOUBLE */ const char *f = *field; uint32_t size; + /* + * MsgPack values of double key field are casted to double, encoded + * as msgpack double and hashed. This assures the same value being + * written as int, uint, float or double has the same hash for this + * type of key. + * + * We create and hash msgpack instead of just hashing the double itself + * for backward compatibility: so a user having a vinyl database with + * double-key index won't have to rebuild it after tarantool update. + * + * XXX: It looks like something like this should also be done for + * number key fields so that e. g. zero given as int, uint, float, + * double and decimal have the same hash. + */ + if (type == FIELD_TYPE_DOUBLE) { + double value; + /* + * This will only fail if the mp_type is not numeric, which is + * impossible here (see field_mp_plain_type_is_compatible). + */ + if (mp_read_double_lossy(&f, &value) == -1) + unreachable(); + char *double_msgpack_end = mp_encode_double(buf, value); + size = double_msgpack_end - buf; + assert(size <= sizeof(buf)); + PMurHash32_Process(ph1, pcarry, buf, size); + return size; + } + switch (mp_typeof(**field)) { case MP_STR: /* @@ -355,7 +395,7 @@ tuple_hash_key_part(uint32_t *ph1, uint32_t *pcarry, struct tuple *tuple, const char *field = tuple_field_by_part(tuple, part, multikey_idx); if (field == NULL) return tuple_hash_null(ph1, pcarry); - return tuple_hash_field(ph1, pcarry, &field, part->coll); + return tuple_hash_field(ph1, pcarry, &field, part->type, part->coll); } template <bool has_optional_parts, bool has_json_paths> @@ -386,6 +426,7 @@ tuple_hash_slowpath(struct tuple *tuple, struct key_def *key_def) total_size += tuple_hash_null(&h, &carry); } else { total_size += tuple_hash_field(&h, &carry, &field, + key_def->parts[0].type, key_def->parts[0].coll); } for (uint32_t part_id = 1; part_id < key_def->part_count; part_id++) { @@ -409,6 +450,7 @@ tuple_hash_slowpath(struct tuple *tuple, struct key_def *key_def) } else { total_size += tuple_hash_field(&h, &carry, &field, + key_def->parts[part_id].type, key_def->parts[part_id].coll); } prev_fieldno = key_def->parts[part_id].fieldno; @@ -417,7 +459,7 @@ tuple_hash_slowpath(struct tuple *tuple, struct key_def *key_def) return PMurHash32_Result(h, carry, total_size); } -uint32_t +static uint32_t key_hash_slowpath(const char *key, struct key_def *key_def) { uint32_t h = HASH_SEED; @@ -426,7 +468,8 @@ key_hash_slowpath(const char *key, struct key_def *key_def) for (struct key_part *part = key_def->parts; part < key_def->parts + key_def->part_count; part++) { - total_size += tuple_hash_field(&h, &carry, &key, part->coll); + total_size += tuple_hash_field(&h, &carry, &key, part->type, + part->coll); } return PMurHash32_Result(h, carry, total_size); diff --git a/test/engine-luatest/gh_7483_insert_int_into_double_field_test.lua b/test/engine-luatest/gh_7483_insert_int_into_double_field_test.lua new file mode 100644 index 0000000000000000000000000000000000000000..56e1044c6f578cbf167c1d9d428d1e2956c0a76c --- /dev/null +++ b/test/engine-luatest/gh_7483_insert_int_into_double_field_test.lua @@ -0,0 +1,67 @@ +local server = require('luatest.server') +local t = require('luatest') + +local g = t.group('gh-7483-insert-int-into-double-field-test', { + {engine = 'memtx', index_type = 'tree'}, + {engine = 'memtx', index_type = 'hash'}, + {engine = 'vinyl', index_type = 'tree'}, +}) + +g.before_all(function(cg) + cg.server = server:new() + cg.server:start() +end) + +g.after_all(function(cg) + cg.server:drop() +end) + +g.after_each(function(cg) + cg.server:exec(function() + if box.space.s ~= nil then + box.space.s:drop() + end + end) +end) + +g.test_insert_int_into_double_field = function(cg) + cg.server:exec(function (engine, index_type) + local ffi = require('ffi') + + local s = box.schema.space.create('s', { engine = engine }) + s:create_index('s_idx', {type = index_type, parts = {1, 'double'}}) + + -- For double-typed key we cast it to double before hashing. + -- So 1 and 1.0 are interpret as the same value. + local int_1 = 1 + local double_1 = ffi.new('double', 1.0) + local errdup = 'Duplicate key exists in unique index "s_idx" in space ' + ..'"s" with old tuple - ' + + -- Ok, new integer (1). + s:insert({int_1}) + + -- Fail, duplicate of 1. + t.assert_error_msg_contains(errdup, s.insert, s, {double_1}) + + -- Select the integer. + t.assert_equals(s:select({double_1}), {{int_1}}); + + -- 2 ** 63 - 1 can't be directly converted to double, + -- it becomes 2 ** 63 on cast. + local int_2e63_minus_1 = 0x7fffffffffffffffULL + local double_2e63 = ffi.new('double', int_2e63_minus_1) + + -- Successfully insert the integer into the double field. + s:insert({int_2e63_minus_1}) + + -- Insert of double-casted integer should fail since it's recognized as + -- a duplicate of the integer inserted above (despite the fact that + -- integer is not exactly equal to the resulting double, it becomes + -- equal on cast to double in index). + t.assert_error_msg_contains(errdup, s.insert, s, {double_2e63}) + + -- Select of the double should retrieve the inserted integer. + t.assert_equals(s:select({double_2e63}), {{int_2e63_minus_1}}) + end, {cg.params.engine, cg.params.index_type}) +end diff --git a/test/engine/insert.result b/test/engine/insert.result index ffff3df85d3c29b1acab9b85a652d3cd9fe48246..f8d128e0cbcabb22f8797b0a07efcce674293933 100644 --- a/test/engine/insert.result +++ b/test/engine/insert.result @@ -746,8 +746,7 @@ _ = s:create_index('ii') --- ... -- --- If number of Lua type NUMBER is not integer, than it could be --- inserted in DOUBLE field. +-- A number of Lua type NUMBER can be inserted in DOUBLE field. -- s:insert({1, 1.1}) --- @@ -762,33 +761,31 @@ 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. +-- Integers of Lua type NUMBER and CDATA of type int64 or uint64 can +-- also be inserted even if they can't be loselessly converted to +-- double. They're stored in the space "as is". -- s:insert({4, 1}) --- -- error: 'Tuple field 2 (d) type does not match one required by operation: expected - double, got unsigned' +- [4, 1] ... -s:insert({5, -9223372036854775800ULL}) +s:insert({5, 9223372036854775800ULL}) --- -- error: 'Tuple field 2 (d) type does not match one required by operation: expected - double, got unsigned' +- [5, 9223372036854775800] ... s:insert({6, 18000000000000000000ULL}) --- -- error: 'Tuple field 2 (d) type does not match one required by operation: expected - double, got unsigned' +- [6, 18000000000000000000] ... -- --- 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. +-- One can also cast a value to 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)}) +s:insert({8, ffi.cast('double', -9223372036854775800LL)}) --- - [8, -9223372036854775808] ... @@ -813,6 +810,9 @@ s:select() - - [1, 1.1] - [2, 2.5] - [3, -3.0009] + - [4, 1] + - [5, 9223372036854775800] + - [6, 18000000000000000000] - [7, 1] - [8, -9223372036854775808] - [9, 123] @@ -829,13 +829,13 @@ dd:select(1.1) - - [1, 1.1] - [11, 1.1] ... -dd:select(1) +dd:select(-9223372036854775800LL) --- -- error: 'Supplied key type of part 0 does not match index part type: expected double' +- - [8, -9223372036854775808] ... -dd:select(ffi.cast('double', 1)) +dd:select(ffi.cast('double', -9223372036854775800LL)) --- -- - [7, 1] +- - [8, -9223372036854775808] ... -- Make sure the comparisons work correctly. dd:select(1.1, {iterator = 'ge'}) @@ -844,6 +844,8 @@ dd:select(1.1, {iterator = 'ge'}) - [11, 1.1] - [2, 2.5] - [9, 123] + - [5, 9223372036854775800] + - [6, 18000000000000000000] - [10, 18000000000000000000] ... dd:select(1.1, {iterator = 'le'}) @@ -851,6 +853,7 @@ dd:select(1.1, {iterator = 'le'}) - - [11, 1.1] - [1, 1.1] - [7, 1] + - [4, 1] - [12, -3.0009] - [3, -3.0009] - [8, -9223372036854775808] @@ -859,11 +862,14 @@ dd:select(ffi.cast('double', 1.1), {iterator = 'gt'}) --- - - [2, 2.5] - [9, 123] + - [5, 9223372036854775800] + - [6, 18000000000000000000] - [10, 18000000000000000000] ... dd:select(ffi.cast('double', 1.1), {iterator = 'lt'}) --- - - [7, 1] + - [4, 1] - [12, -3.0009] - [3, -3.0009] - [8, -9223372036854775808] @@ -874,6 +880,8 @@ dd:select(1.1, {iterator = 'all'}) - [11, 1.1] - [2, 2.5] - [9, 123] + - [5, 9223372036854775800] + - [6, 18000000000000000000] - [10, 18000000000000000000] ... dd:select(1.1, {iterator = 'eq'}) @@ -886,6 +894,14 @@ dd:select(1.1, {iterator = 'req'}) - - [11, 1.1] - [1, 1.1] ... +s:delete(4) +--- +- [4, 1] +... +s:delete(6) +--- +- [6, 18000000000000000000] +... s:delete(11) --- - [11, 1.1] @@ -900,31 +916,27 @@ ddd = s:create_index('ddd', {parts = {{2, 'double'}}}) ... s:update(1, {{'=', 2, 2}}) --- -- error: 'Tuple field 2 (d) type does not match one required by operation: expected - double, got unsigned' +- [1, 2] ... s:insert({22, 22}) --- -- error: 'Tuple field 2 (d) type does not match one required by operation: expected - double, got unsigned' +- [22, 22] ... s:upsert({10, 100}, {{'=', 2, 2}}) --- -- error: 'Tuple field 2 (d) type does not match one required by operation: expected - double, got unsigned' +- error: Duplicate key exists in unique index "ddd" in space "s" with old tuple - + [1, 2] and new tuple - [10, 2] ... -s:upsert({100, 100}, {{'=', 2, 2}}) +s:upsert({101, 100}, {{'=', 2, 11}}) --- -- error: 'Tuple field 2 (d) type does not match one required by operation: expected - double, got unsigned' ... ddd:update(1, {{'=', 1, 70}}) --- -- error: 'Supplied key type of part 0 does not match index part type: expected double' +- error: Attempt to modify a tuple field which is part of primary index in space 's' ... ddd:delete(1) --- -- error: 'Supplied key type of part 0 does not match index part type: expected double' +- [7, 1] ... s:update(2, {{'=', 2, 2.55}}) --- @@ -948,13 +960,13 @@ s:get(10) --- - [10, 2.2] ... -ddd:update(1.1, {{'=', 3, 111}}) +ddd:update(2, {{'=', 3, 111}}) --- -- [1, 1.1, 111] +- [1, 2, 111] ... -ddd:delete(1.1) +ddd:delete(2) --- -- [1, 1.1, 111] +- [1, 2, 111] ... s:update(2, {{'=', 2, ffi.cast('double', 255)}}) --- @@ -978,9 +990,9 @@ s:get(200) --- - [200, 222] ... -ddd:update(ffi.cast('double', 1), {{'=', 3, 7}}) +ddd:update(22, {{'=', 3, 7}}) --- -- [7, 1, 7] +- [22, 22, 7] ... ddd:delete(ffi.cast('double', 123)) --- @@ -990,11 +1002,12 @@ s:select() --- - - [2, 255] - [3, -3.0009] - - [7, 1, 7] + - [5, 9223372036854775800] - [8, -9223372036854775808] - [10, 2.2] - - [22, 22] + - [22, 22, 7] - [100, 100.5] + - [101, 100] - [200, 222] ... s:drop() diff --git a/test/engine/insert.test.lua b/test/engine/insert.test.lua index 2ffc8cd0a067c772a45c4a30e5db2ce2ddf184ae..31b6bffc66700ff7b1f48f0304cdb5590d2200d5 100644 --- a/test/engine/insert.test.lua +++ b/test/engine/insert.test.lua @@ -130,27 +130,27 @@ 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. +-- A number of Lua type NUMBER can 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. +-- Integers of Lua type NUMBER and CDATA of type int64 or uint64 can +-- also be inserted even if they can't be loselessly converted to +-- double. They're stored in the space "as is". -- s:insert({4, 1}) -s:insert({5, -9223372036854775800ULL}) +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. +-- One can also cast a value to 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({8, ffi.cast('double', -9223372036854775800LL)}) s:insert({9, ffi.cast('double', tonumber('123'))}) s:insert({10, ffi.cast('double', tonumber64('18000000000000000000'))}) s:insert({11, ffi.cast('double', 1.1)}) @@ -161,8 +161,8 @@ 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)) +dd:select(-9223372036854775800LL) +dd:select(ffi.cast('double', -9223372036854775800LL)) -- Make sure the comparisons work correctly. dd:select(1.1, {iterator = 'ge'}) @@ -173,6 +173,8 @@ dd:select(1.1, {iterator = 'all'}) dd:select(1.1, {iterator = 'eq'}) dd:select(1.1, {iterator = 'req'}) +s:delete(4) +s:delete(6) s:delete(11) s:delete(12) @@ -182,7 +184,7 @@ 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}}) +s:upsert({101, 100}, {{'=', 2, 11}}) ddd:update(1, {{'=', 1, 70}}) ddd:delete(1) @@ -194,8 +196,8 @@ s:get(100) s:upsert({10, 100.5}, {{'=', 2, 2.2}}) s:get(10) -ddd:update(1.1, {{'=', 3, 111}}) -ddd:delete(1.1) +ddd:update(2, {{'=', 3, 111}}) +ddd:delete(2) s:update(2, {{'=', 2, ffi.cast('double', 255)}}) s:replace({22, ffi.cast('double', 22)}) @@ -204,7 +206,7 @@ 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:update(22, {{'=', 3, 7}}) ddd:delete(ffi.cast('double', 123)) s:select() diff --git a/test/sql-tap/cast.test.lua b/test/sql-tap/cast.test.lua index 55f2d269078a7b0181c4c7364f258deee07d2ce8..8b1f53d3d82cf69d1226613a66c8449107f2ede2 100755 --- a/test/sql-tap/cast.test.lua +++ b/test/sql-tap/cast.test.lua @@ -1145,12 +1145,15 @@ test:do_catchsql_test( }) -- Make sure that search using index in field type number work right. +-- NOTE: Both 9999999999999999 and 10000000000000001 become the same value +-- in index comparison because of double-cast, so we use -or-equal variants +-- of iterators. Don't expect to have precise comparisons in double indexes. test:do_execsql_test( "cast-11", [[ CREATE TABLE t6(d DOUBLE PRIMARY KEY); INSERT INTO t6 VALUES(10000000000000000); - SELECT d FROM t6 WHERE d < 10000000000000001 and d > 9999999999999999; + SELECT d FROM t6 WHERE d <= 10000000000000001 and d >= 9999999999999999; DROP TABLE t6; ]], { 10000000000000000 diff --git a/test/vinyl/upsert.result b/test/vinyl/upsert.result index 2db448c56c906dd2d284221dfd67b7233263083a..a2df181e373cc26fe86a52e05f6d6dfc658069bd 100644 --- a/test/vinyl/upsert.result +++ b/test/vinyl/upsert.result @@ -1187,12 +1187,12 @@ box.snapshot() --- - ok ... --- Can't assign integer to float field. First operation is still applied. +-- Can assign integer to float field. -- -s:upsert({1, 1, 1, 2.5, decimal.new(1.1)}, {{'+', 4, 4}}) +s:upsert({1, 1, 1, 2.5, decimal.new(1.1)}, {{'=', 4, 4}}) --- ... -s:upsert({1, 1, 1, 2.5, decimal.new(1.1)}, {{'=', 4, 4}}) +s:upsert({1, 1, 1, 2.5, decimal.new(1.1)}, {{'+', 4, 4}}) --- ... -- Can't add floating point to integer (result is floating point). @@ -1209,7 +1209,7 @@ box.snapshot() ... s:select() --- -- - [1, 1, 1, 5.1, 1.1] +- - [1, 1, 1, 8, 1.1] - [2, 6, 1, 1.1, 1.1] ... -- Integer overflow check. @@ -1234,7 +1234,7 @@ box.snapshot() ... s:select() --- -- - [1, 1, 9223372036854775809, 5.1, 1.1] +- - [1, 1, 9223372036854775809, 8, 1.1] - [2, 8, 1, 1.1, 1.1] ... -- Decimals do not fit into numerics and vice versa. @@ -1257,7 +1257,7 @@ box.snapshot() ... s:select() --- -- - [1, 1, 9223372036854775809, 5.1, 2.1] +- - [1, 1, 9223372036854775809, 8, 2.1] - [2, 8, 1, 1.1, 1.1] ... s:drop() diff --git a/test/vinyl/upsert.test.lua b/test/vinyl/upsert.test.lua index 4d0e35920f26c29d2fffcc986463243fd32ae883..30bfa799ec1e729fa7d22e0cec1bb8b8b8847c45 100644 --- a/test/vinyl/upsert.test.lua +++ b/test/vinyl/upsert.test.lua @@ -495,10 +495,10 @@ pk = s:create_index('pk') s:replace{1, 1, 1, 1.1, decimal.new(1.1) } s:replace{2, 1, 1, 1.1, decimal.new(1.1)} box.snapshot() --- Can't assign integer to float field. First operation is still applied. +-- Can assign integer to float field. -- -s:upsert({1, 1, 1, 2.5, decimal.new(1.1)}, {{'+', 4, 4}}) s:upsert({1, 1, 1, 2.5, decimal.new(1.1)}, {{'=', 4, 4}}) +s:upsert({1, 1, 1, 2.5, decimal.new(1.1)}, {{'+', 4, 4}}) -- Can't add floating point to integer (result is floating point). -- s:upsert({2, 1, 1, 2.5, decimal.new(1.1)}, {{'+', 2, 5}})