From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Serge Petrenko Subject: [PATCH v3 6/6] decimal: allow to index decimals Date: Tue, 20 Aug 2019 20:10:04 +0300 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit To: vdavydov.dev@gmail.com, kostja@tarantool.org Cc: tarantool-patches@freelists.org, Serge Petrenko List-ID: Closes #4333 @TarantoolBot document Title: Document decimal field type. Decimals may now be stored in spaces. A corresponding field type is introduced: 'decimal'. Decimal values are also allowed in 'scalar', 'any' and 'number' fields. 'decimal' field type is appropriate for both memtx HASH and TREE indices, as well as for vinyl TREE index. ``` To create an index 'pk' over a decimal field, say ``` tarantool> box.space.test:create_index('pk', {parts={1, 'decimal'}}) --- - unique: true parts: - type: decimal is_nullable: false fieldno: 1 id: 0 space_id: 512 type: TREE name: pk ... ``` Now you can insert some decimal values: ``` tarantool> for i = 1,10 do > box.space.test:insert{decimal.new((i-5)/10)} > end --- ... ``` tarantool> box.space.test:select{} --- - - [-0.4] - [-0.3] - [-0.2] - [-0.1] - [0] - [0.1] - [0.2] - [0.3] - [0.4] - [0.5] ... ``` Decimals may alse be inserted into `scalar` and `number` fields. In this case all the number values are sorted correctly: ``` tarantool> box.schema.space.create('test') tarantool> box.space.test:create_index('pk', {parts={1, 'number'}}) tarantool> box.space.test:insert{-1.0001, 'number'} --- - [-1.0001, 'number'] ... tarantool> box.space.test:insert{decimal.new(-1.00001), 'decimal'} --- - [-1.00001, 'decimal'] ... tarantool> box.space.test:insert{-1, 'number'} --- - [-1, 'number'] ... tarantool> box.space.test:insert{decimal.new(-0.999), 'decimal'} --- - [-0.999, 'decimal'] ... tarantool> box.space.test:insert{-0.998, 'number'} --- - [-0.998, 'number'] ... tarantool> box.space.test:insert{-0.9, 'number'} --- - [-0.9, 'number'] ... tarantool> box.space.test:insert{-0.95, 'number'} --- - [-0.95, 'number'] ... tarantool> box.space.test:insert{decimal.new(-0.92), 'decimal'} --- - [-0.92, 'decimal'] ... tarantool> box.space.test:insert{decimal.new(-0.971), 'decimal'} --- - [-0.971, 'decimal'] ... tarantool> box.space.test:select{} --- - - [-1.0001, 'number'] - [-1.00001, 'decimal'] - [-1, 'number'] - [-0.999, 'decimal'] - [-0.998, 'number'] - [-0.971, 'decimal'] - [-0.95, 'number'] - [-0.92, 'decimal'] - [-0.9, 'number'] ... ``` Uniqueness is also preserved between decimals and other number types: ``` tarantool> box.space.test:insert{-0.92} --- - error: Duplicate key exists in unique index 'pk' in space 'test' ... tarantool> box.space.test:insert{decimal.new(-0.9)} --- - error: Duplicate key exists in unique index 'pk' in space 'test' ... ``` You can also set decimal fields in space format: ``` tarantool> _ = box.schema.space.create('test') --- ... tarantool> _ = box.space.test:create_index('pk') --- ... tarantool> box.space.test:format{{name='id', type='unsigned'}, {name='balance', type='decimal'}} --- ... tarantool> box.space.test:insert{1} --- - error: Tuple field 2 required by space format is missing ... tarantool> box.space.test:insert{1, 'string'} --- - error: 'Tuple field 2 type does not match one required by operation: expected decimal' ... tarantool> box.space.test:insert{1, 1.2345} --- - error: 'Tuple field 2 type does not match one required by operation: expected decimal' ... tarantool> box.space.test:insert{1, decimal.new('1337.420')} --- - [1, 1337.420] ... ``` --- src/box/field_def.c | 45 ++- src/box/field_def.h | 23 +- src/box/key_def.h | 3 +- src/box/tuple_compare.cc | 198 ++++++++- src/box/tuple_format.c | 3 +- src/lib/core/decimal.h | 8 + src/lib/core/mp_decimal.h | 8 + test/engine/ddl.result | 85 +++- test/engine/ddl.test.lua | 40 +- test/engine/decimal.result | 275 +++++++++++++ test/engine/decimal.test.lua | 83 ++++ test/engine/iterator.result | 730 ++++++++++++++++++++++++++++++++++ test/engine/iterator.test.lua | 22 + 13 files changed, 1496 insertions(+), 27 deletions(-) diff --git a/src/box/field_def.c b/src/box/field_def.c index 346042b98..da766d586 100644 --- a/src/box/field_def.c +++ b/src/box/field_def.c @@ -32,6 +32,7 @@ #include "field_def.h" #include "trivia/util.h" #include "key_def.h" +#include "mp_extension_types.h" const char *mp_type_strs[] = { /* .MP_NIL = */ "nil", @@ -47,6 +48,11 @@ const char *mp_type_strs[] = { /* .MP_EXT = */ "extension", }; +/* + * messagepack types supported by given field types. + * MP_EXT requires to parse extension type to check + * compatibility with field type. + */ const uint32_t field_mp_type[] = { /* [FIELD_TYPE_ANY] = */ UINT32_MAX, /* [FIELD_TYPE_UNSIGNED] = */ 1U << MP_UINT, @@ -59,10 +65,25 @@ const uint32_t field_mp_type[] = { /* [FIELD_TYPE_SCALAR] = */ (1U << MP_UINT) | (1U << MP_INT) | (1U << MP_FLOAT) | (1U << MP_DOUBLE) | (1U << MP_STR) | (1U << MP_BIN) | (1U << MP_BOOL), + /* [FIELD_TYPE_DECIMAL] = */ 0, /* only MP_DECIMAL is supported */ /* [FIELD_TYPE_ARRAY] = */ 1U << MP_ARRAY, /* [FIELD_TYPE_MAP] = */ (1U << MP_MAP), }; +const uint32_t field_ext_type[] = { + /* [FIELD_TYPE_ANY] = */ UINT32_MAX ^ (1U << MP_UNKNOWN_EXTENSION), + /* [FIELD_TYPE_UNSIGNED] = */ 0, + /* [FIELD_TYPE_STRING] = */ 0, + /* [FIELD_TYPE_NUMBER] = */ 1U << MP_DECIMAL, + /* [FIELD_TYPE_INTEGER] = */ 0, + /* [FIELD_TYPE_BOOLEAN] = */ 0, + /* [FIELD_TYPE_VARBINARY] = */ 0, + /* [FIELD_TYPE_SCALAR] = */ 1U << MP_DECIMAL, + /* [FIELD_TYPE_DECIMAL] = */ 1U << MP_DECIMAL, + /* [FIELD_TYPE_ARRAY] = */ 0, + /* [FIELD_TYPE_MAP] = */ 0, +}; + const char *field_type_strs[] = { /* [FIELD_TYPE_ANY] = */ "any", /* [FIELD_TYPE_UNSIGNED] = */ "unsigned", @@ -72,6 +93,7 @@ const char *field_type_strs[] = { /* [FIELD_TYPE_BOOLEAN] = */ "boolean", /* [FIELD_TYPE_VARBINARY] = */"varbinary", /* [FIELD_TYPE_SCALAR] = */ "scalar", + /* [FIELD_TYPE_DECIMAL] = */ "decimal", /* [FIELD_TYPE_ARRAY] = */ "array", /* [FIELD_TYPE_MAP] = */ "map", }; @@ -98,17 +120,18 @@ 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 ARRAY MAP */ -/* ANY */ true, false, false, false, false, false, false, false, false, false, -/* UNSIGNED */ true, true, false, true, true, false, false, true, false, false, -/* STRING */ true, false, true, false, false, false, false, true, false, false, -/* NUMBER */ true, false, false, true, false, false, false, true, false, false, -/* INTEGER */ true, false, false, true, true, false, false, true, false, false, -/* BOOLEAN */ true, false, false, false, false, true, false, true, false, false, -/* VARBINARY*/ true, false, false, false, false, false, true, true, false, false, -/* SCALAR */ true, false, false, false, false, false, false, true, false, false, -/* ARRAY */ true, false, false, false, false, false, false, false, true, false, -/* MAP */ true, false, false, false, false, false, false, false, false, true, + /* 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, }; bool diff --git a/src/box/field_def.h b/src/box/field_def.h index c1a7ec0a9..fdedc9622 100644 --- a/src/box/field_def.h +++ b/src/box/field_def.h @@ -58,6 +58,7 @@ enum field_type { FIELD_TYPE_BOOLEAN, FIELD_TYPE_VARBINARY, FIELD_TYPE_SCALAR, + FIELD_TYPE_DECIMAL, FIELD_TYPE_ARRAY, FIELD_TYPE_MAP, field_type_MAX @@ -109,8 +110,9 @@ field_type_by_name(const char *name, size_t len); /* MsgPack type names */ extern const char *mp_type_strs[]; -/** A helper table for field_mp_type_is_compatible */ +/** Two helper tables for field_mp_type_is_compatible */ extern const uint32_t field_mp_type[]; +extern const uint32_t field_ext_type[]; extern const struct opt_def field_def_reg[]; extern const struct field_def field_def_default; @@ -144,13 +146,26 @@ struct field_def { /** Checks if mp_type (MsgPack) is compatible with field type. */ static inline bool -field_mp_type_is_compatible(enum field_type type, enum mp_type mp_type, +field_mp_type_is_compatible(enum field_type type, const char *data, bool is_nullable) { assert(type < field_type_MAX); + enum mp_type mp_type = mp_typeof(*data); assert((size_t)mp_type < CHAR_BIT * sizeof(*field_mp_type)); - uint32_t mask = field_mp_type[type] | (is_nullable * (1U << MP_NIL)); - return (mask & (1U << mp_type)) != 0; + uint32_t mask; + if (mp_type != MP_EXT) { + mask = field_mp_type[type] | (is_nullable * (1U << MP_NIL)); + return (mask & (1U << mp_type)) != 0; + } else { + int8_t ext_type; + mp_decode_extl(&data, &ext_type); + if (ext_type >= 0) { + mask = field_ext_type[type]; + return (mask & (1U << ext_type)) != 0; + } else { + return false; + } + } } static inline bool diff --git a/src/box/key_def.h b/src/box/key_def.h index c6e33e4ad..f4d9e76f2 100644 --- a/src/box/key_def.h +++ b/src/box/key_def.h @@ -528,8 +528,7 @@ static inline int key_part_validate(enum field_type key_type, const char *key, uint32_t field_no, bool is_nullable) { - if (unlikely(!field_mp_type_is_compatible(key_type, mp_typeof(*key), - is_nullable))) { + if (unlikely(!field_mp_type_is_compatible(key_type, key, is_nullable))) { diag_set(ClientError, ER_KEY_PART_TYPE, field_no, field_type_strs[key_type]); return -1; diff --git a/src/box/tuple_compare.cc b/src/box/tuple_compare.cc index b7b54e21a..e621f3c3c 100644 --- a/src/box/tuple_compare.cc +++ b/src/box/tuple_compare.cc @@ -33,6 +33,9 @@ #include "coll/coll.h" #include "trivia/util.h" /* NOINLINE */ #include +#include "lib/core/decimal.h" +#include "lib/core/mp_decimal.h" +#include "lib/core/mp_extension_types.h" /* {{{ tuple_compare */ @@ -87,7 +90,12 @@ static enum mp_class mp_classes[] = { /* .MP_BOOL = */ MP_CLASS_BOOL, /* .MP_FLOAT = */ MP_CLASS_NUMBER, /* .MP_DOUBLE = */ MP_CLASS_NUMBER, - /* .MP_BIN = */ MP_CLASS_BIN + /* .MP_EXT = */ mp_class_max, +}; + +static enum mp_class mp_ext_classes[] = { + /* .MP_UNKNOWN_EXTENSION = */ mp_class_max, /* unsupported */ + /* .MP_DECIMAL = */ MP_CLASS_NUMBER, }; #define COMPARE_RESULT(a, b) (a < b ? -1 : a > b) @@ -98,6 +106,15 @@ mp_classof(enum mp_type type) return mp_classes[type]; } +static enum mp_class +mp_extension_class(const char *data) +{ + assert(mp_typeof(*data) == MP_EXT); + int8_t type; + mp_decode_extl(&data, &type); + return mp_ext_classes[type]; +} + static int mp_compare_bool(const char *field_a, const char *field_b) { @@ -264,13 +281,107 @@ mp_compare_double_any_number(double lhs, const char *rhs, return k * COMPARE_RESULT(lqbit, rqbit); } +static int +mp_compare_decimal(const char *lhs, const char *rhs) +{ + decimal_t lhs_dec, rhs_dec; + decimal_t *ret; + ret = mp_decode_decimal(&lhs, &lhs_dec); + assert(ret != NULL); + ret = mp_decode_decimal(&rhs, &rhs_dec); + assert(ret != NULL); + (void)ret; + return decimal_compare(&lhs_dec, &rhs_dec); + +} + +static int +mp_compare_decimal_any_number(decimal_t *lhs, const char *rhs, + enum mp_type rhs_type, int k) +{ + decimal_t rhs_dec; + switch (rhs_type) { + case MP_FLOAT: + { + double d = mp_decode_float(&rhs); + decimal_from_double(&rhs_dec, d); + break; + } + case MP_DOUBLE: + { + double d = mp_decode_double(&rhs); + decimal_from_double(&rhs_dec, d); + break; + } + case MP_INT: + { + int64_t num = mp_decode_int(&rhs); + decimal_from_int64(&rhs_dec, num); + break; + } + case MP_UINT: + { + uint64_t num = mp_decode_uint(&rhs); + decimal_from_uint64(&rhs_dec, num); + break; + } + case MP_EXT: + { + int8_t ext_type; + uint32_t len = mp_decode_extl(&rhs, &ext_type); + switch (ext_type) { + case MP_DECIMAL: + decimal_unpack(&rhs, len, &rhs_dec); + break; + default: + unreachable(); + } + break; + } + default: + unreachable(); + } + return k * decimal_compare(lhs, &rhs_dec); +} + static int mp_compare_number_with_type(const char *lhs, enum mp_type lhs_type, const char *rhs, enum mp_type rhs_type) { - assert(mp_classof(lhs_type) == MP_CLASS_NUMBER); - assert(mp_classof(rhs_type) == MP_CLASS_NUMBER); + assert(mp_classof(lhs_type) == MP_CLASS_NUMBER || + mp_extension_class(lhs) == MP_CLASS_NUMBER); + assert(mp_classof(rhs_type) == MP_CLASS_NUMBER || + mp_extension_class(rhs) == MP_CLASS_NUMBER); + /* + * Test decimals first, so that we don't have to + * account for them in other comparators. + */ + decimal_t dec; + if (rhs_type == MP_EXT) { + int8_t ext_type; + uint32_t len = mp_decode_extl(&rhs, &ext_type); + switch (ext_type) { + case MP_DECIMAL: + return mp_compare_decimal_any_number( + decimal_unpack(&rhs, len, &dec), lhs, lhs_type, -1 + ); + default: + unreachable(); + } + } + if (lhs_type == MP_EXT) { + int8_t ext_type; + uint32_t len = mp_decode_extl(&lhs, &ext_type); + switch (ext_type) { + case MP_DECIMAL: + return mp_compare_decimal_any_number( + decimal_unpack(&lhs, len, &dec), rhs, rhs_type, 1 + ); + default: + unreachable(); + } + } if (rhs_type == MP_FLOAT) { return mp_compare_double_any_number( mp_decode_float(&rhs), lhs, lhs_type, -1 @@ -348,8 +459,12 @@ static int mp_compare_scalar_with_type(const char *field_a, enum mp_type a_type, const char *field_b, enum mp_type b_type) { - enum mp_class a_class = mp_classof(a_type); - enum mp_class b_class = mp_classof(b_type); + enum mp_class a_class = mp_classof(a_type) < mp_class_max ? + mp_classof(a_type) : + mp_extension_class(field_a); + enum mp_class b_class = mp_classof(b_type) < mp_class_max ? + mp_classof(b_type) : + mp_extension_class(field_b); if (a_class != b_class) return COMPARE_RESULT(a_class, b_class); mp_compare_f cmp = mp_class_comparators[a_class]; @@ -410,6 +525,8 @@ tuple_compare_field(const char *field_a, const char *field_b, return coll != NULL ? mp_compare_scalar_coll(field_a, field_b, coll) : mp_compare_scalar(field_a, field_b); + case FIELD_TYPE_DECIMAL: + return mp_compare_decimal(field_a, field_b); default: unreachable(); return 0; @@ -443,6 +560,9 @@ tuple_compare_field_with_type(const char *field_a, enum mp_type a_type, mp_compare_scalar_coll(field_a, field_b, coll) : mp_compare_scalar_with_type(field_a, a_type, field_b, b_type); + case FIELD_TYPE_DECIMAL: + return mp_compare_number_with_type(field_a, a_type, + field_b, b_type); default: unreachable(); return 0; @@ -1502,6 +1622,24 @@ hint_double(double d) return hint_create(MP_CLASS_NUMBER, val); } +static inline hint_t +hint_decimal(decimal_t *dec) +{ + uint64_t val = 0; + int64_t num; + if (decimal_to_int64(dec, &num) && + num >= HINT_VALUE_INT_MIN && num <= HINT_VALUE_INT_MAX) { + val = num - HINT_VALUE_INT_MIN; + } else if (!(dec->bits & DECNEG)) { + val = HINT_VALUE_MAX; + } + /* + * In case the number is negative and out of bounds, val + * remains zero. + */ + return hint_create(MP_CLASS_NUMBER, val); +} + static inline uint64_t hint_str_raw(const char *s, uint32_t len) { @@ -1578,12 +1716,43 @@ field_hint_number(const char *field) return hint_double(mp_decode_float(&field)); case MP_DOUBLE: return hint_double(mp_decode_double(&field)); + case MP_EXT: + { + int8_t ext_type; + uint32_t len = mp_decode_extl(&field, &ext_type); + switch (ext_type) { + case MP_DECIMAL: + { + decimal_t dec; + return hint_decimal(decimal_unpack(&field, len, &dec)); + } + default: + unreachable(); + } + } default: unreachable(); } return HINT_NONE; } +static inline hint_t +field_hint_decimal(const char *field) +{ + assert(mp_typeof(*field) == MP_EXT); + int8_t ext_type; + uint32_t len = mp_decode_extl(&field, &ext_type); + switch (ext_type) { + case MP_DECIMAL: + { + decimal_t dec; + return hint_decimal(decimal_unpack(&field, len, &dec)); + } + default: + unreachable(); + } +} + static inline hint_t field_hint_string(const char *field, struct coll *coll) { @@ -1623,6 +1792,20 @@ field_hint_scalar(const char *field, struct coll *coll) case MP_BIN: len = mp_decode_binl(&field); return hint_bin(field, len); + case MP_EXT: + { + int8_t ext_type; + uint32_t len = mp_decode_extl(&field, &ext_type); + switch (ext_type) { + case MP_DECIMAL: + { + decimal_t dec; + return hint_decimal(decimal_unpack(&field, len, &dec)); + } + default: + unreachable(); + } + } default: unreachable(); } @@ -1650,6 +1833,8 @@ field_hint(const char *field, struct coll *coll) return field_hint_varbinary(field); case FIELD_TYPE_SCALAR: return field_hint_scalar(field, coll); + case FIELD_TYPE_DECIMAL: + return field_hint_decimal(field); default: unreachable(); } @@ -1755,6 +1940,9 @@ key_def_set_hint_func(struct key_def *def) case FIELD_TYPE_SCALAR: key_def_set_hint_func(def); break; + case FIELD_TYPE_DECIMAL: + key_def_set_hint_func(def); + break; default: /* Invalid key definition. */ def->key_hint = NULL; diff --git a/src/box/tuple_format.c b/src/box/tuple_format.c index 514d5d9c0..154535ab1 100644 --- a/src/box/tuple_format.c +++ b/src/box/tuple_format.c @@ -1169,8 +1169,7 @@ tuple_format_iterator_next(struct tuple_format_iterator *it, * defined in format. */ bool is_nullable = tuple_field_is_nullable(field); - if (!field_mp_type_is_compatible(field->type, mp_typeof(*entry->data), - is_nullable) != 0) { + if (!field_mp_type_is_compatible(field->type, entry->data, is_nullable) != 0) { diag_set(ClientError, ER_FIELD_TYPE, tuple_field_path(field), field_type_strs[field->type]); diff --git a/src/lib/core/decimal.h b/src/lib/core/decimal.h index 77b6dbfd0..9a162c73c 100644 --- a/src/lib/core/decimal.h +++ b/src/lib/core/decimal.h @@ -37,6 +37,10 @@ #include "third_party/decNumber/decNumber.h" #include +#if defined(__cplusplus) +extern "C" { +#endif /* defined(__cplusplus) */ + typedef decNumber decimal_t; /** @@ -239,4 +243,8 @@ decimal_pack(char *data, const decimal_t *dec); decimal_t * decimal_unpack(const char **data, uint32_t len, decimal_t *dec); +#if defined(__cplusplus) +} /* extern "C" */ +#endif /* defined(__cplusplus) */ + #endif /* TARANTOOL_LIB_CORE_DECIMAL_H_INCLUDED */ diff --git a/src/lib/core/mp_decimal.h b/src/lib/core/mp_decimal.h index a991a5f16..778529068 100644 --- a/src/lib/core/mp_decimal.h +++ b/src/lib/core/mp_decimal.h @@ -34,6 +34,10 @@ #include "decimal.h" #include +#if defined(__cplusplus) +extern "C" { +#endif /* defined(__cplusplus) */ + /** * \brief Calculate exact buffer size needed to store a decimal * pointed to by \a dec. @@ -59,4 +63,8 @@ mp_decode_decimal(const char **data, decimal_t *dec); char * mp_encode_decimal(char *data, const decimal_t *dec); +#if defined(__cplusplus) +} /* extern "C" */ +#endif /* defined(__cplusplus) */ + #endif diff --git a/test/engine/ddl.result b/test/engine/ddl.result index fa3f6051f..67b22ed9e 100644 --- a/test/engine/ddl.result +++ b/test/engine/ddl.result @@ -1034,6 +1034,9 @@ s:drop() -- -- gh-2800: space formats checking is broken. -- +decimal = require('decimal') +--- +... -- Ensure that vinyl correctly process field count change. s = box.schema.space.create('test', {engine = engine, field_count = 2}) --- @@ -1092,13 +1095,16 @@ format[9] = {name = 'field9', type = 'array'} format[10] = {name = 'field10', type = 'map'} --- ... +format[11] = {name = 'field11', type = 'decimal'} +--- +... s = box.schema.space.create('test', {engine = engine, format = format}) --- ... pk = s:create_index('pk') --- ... -t = s:replace{1, {2}, 3, '4', 5.5, -6, true, -8, {9, 9}, {val = 10}} +t = s:replace{1, {2}, 3, '4', 5.5, -6, true, -8, {9, 9}, {val = 10}, decimal.new(-11.11)} --- ... inspector:cmd("setopt delimiter ';'") @@ -1160,6 +1166,11 @@ fail_format_change(3, 'map') --- - 'Tuple field 3 type does not match one required by operation: expected map' ... +-- unsigned --X--> decimal +fail_format_change(3, 'decimal') +--- +- 'Tuple field 3 type does not match one required by operation: expected decimal' +... -- string -----> any ok_format_change(4, 'any') --- @@ -1173,6 +1184,11 @@ fail_format_change(4, 'boolean') --- - 'Tuple field 4 type does not match one required by operation: expected boolean' ... +-- string --X--> decimal +fail_format_change(4, 'decimal') +--- +- 'Tuple field 4 type does not match one required by operation: expected decimal' +... -- number -----> any ok_format_change(5, 'any') --- @@ -1186,6 +1202,11 @@ fail_format_change(5, 'integer') --- - 'Tuple field 5 type does not match one required by operation: expected integer' ... +-- number --X--> decimal +fail_format_change(5, 'decimal') +--- +- 'Tuple field 5 type does not match one required by operation: expected decimal' +... -- integer -----> any ok_format_change(6, 'any') --- @@ -1203,6 +1224,11 @@ fail_format_change(6, 'unsigned') --- - 'Tuple field 6 type does not match one required by operation: expected unsigned' ... +-- integer --X--> decimal +fail_format_change(6, 'decimal') +--- +- 'Tuple field 6 type does not match one required by operation: expected decimal' +... -- boolean -----> any ok_format_change(7, 'any') --- @@ -1216,6 +1242,11 @@ fail_format_change(7, 'string') --- - 'Tuple field 7 type does not match one required by operation: expected string' ... +-- boolead --X--> decimal +fail_format_change(7, 'decimal') +--- +- 'Tuple field 7 type does not match one required by operation: expected decimal' +... -- scalar -----> any ok_format_change(8, 'any') --- @@ -1225,6 +1256,11 @@ fail_format_change(8, 'unsigned') --- - 'Tuple field 8 type does not match one required by operation: expected unsigned' ... +-- scalar --X--> decimal +fail_format_change(8, 'decimal') +--- +- 'Tuple field 8 type does not match one required by operation: expected decimal' +... -- array -----> any ok_format_change(9, 'any') --- @@ -1234,6 +1270,11 @@ fail_format_change(9, 'scalar') --- - 'Tuple field 9 type does not match one required by operation: expected scalar' ... +-- arary --X--> decimal +fail_format_change(9, 'decimal') +--- +- 'Tuple field 9 type does not match one required by operation: expected decimal' +... -- map -----> any ok_format_change(10, 'any') --- @@ -1243,6 +1284,48 @@ fail_format_change(10, 'scalar') --- - 'Tuple field 10 type does not match one required by operation: expected scalar' ... +-- map --X--> decimal +fail_format_change(10, 'decimal') +--- +- 'Tuple field 10 type does not match one required by operation: expected decimal' +... +-- decimal ----> any +ok_format_change(11, 'any') +--- +... +-- decimal ----> number +ok_format_change(11, 'number') +--- +... +-- decimal ----> scalar +ok_format_change(11, 'scalar') +--- +... +-- decimal --X--> string +fail_format_change(11, 'string') +--- +- 'Tuple field 11 type does not match one required by operation: expected string' +... +-- decimal --X--> integer +fail_format_change(11, 'integer') +--- +- 'Tuple field 11 type does not match one required by operation: expected integer' +... +-- decimal --X--> unsigned +fail_format_change(11, 'unsigned') +--- +- 'Tuple field 11 type does not match one required by operation: expected unsigned' +... +-- decimal --X--> map +fail_format_change(11, 'map') +--- +- 'Tuple field 11 type does not match one required by operation: expected map' +... +-- decimal --X--> array +fail_format_change(11, 'array') +--- +- 'Tuple field 11 type does not match one required by operation: expected array' +... s:drop() --- ... diff --git a/test/engine/ddl.test.lua b/test/engine/ddl.test.lua index d15bf1f58..e761966d7 100644 --- a/test/engine/ddl.test.lua +++ b/test/engine/ddl.test.lua @@ -355,6 +355,8 @@ s:drop() -- gh-2800: space formats checking is broken. -- +decimal = require('decimal') + -- Ensure that vinyl correctly process field count change. s = box.schema.space.create('test', {engine = engine, field_count = 2}) pk = s:create_index('pk') @@ -376,9 +378,11 @@ format[7] = {name = 'field7', type = 'boolean'} format[8] = {name = 'field8', type = 'scalar'} format[9] = {name = 'field9', type = 'array'} format[10] = {name = 'field10', type = 'map'} +format[11] = {name = 'field11', type = 'decimal'} + s = box.schema.space.create('test', {engine = engine, format = format}) pk = s:create_index('pk') -t = s:replace{1, {2}, 3, '4', 5.5, -6, true, -8, {9, 9}, {val = 10}} +t = s:replace{1, {2}, 3, '4', 5.5, -6, true, -8, {9, 9}, {val = 10}, decimal.new(-11.11)} inspector:cmd("setopt delimiter ';'") function fail_format_change(fieldno, new_type) @@ -415,6 +419,8 @@ ok_format_change(3, 'integer') ok_format_change(3, 'scalar') -- unsigned --X--> map fail_format_change(3, 'map') +-- unsigned --X--> decimal +fail_format_change(3, 'decimal') -- string -----> any ok_format_change(4, 'any') @@ -422,6 +428,8 @@ ok_format_change(4, 'any') ok_format_change(4, 'scalar') -- string --X--> boolean fail_format_change(4, 'boolean') +-- string --X--> decimal +fail_format_change(4, 'decimal') -- number -----> any ok_format_change(5, 'any') @@ -429,6 +437,8 @@ ok_format_change(5, 'any') ok_format_change(5, 'scalar') -- number --X--> integer fail_format_change(5, 'integer') +-- number --X--> decimal +fail_format_change(5, 'decimal') -- integer -----> any ok_format_change(6, 'any') @@ -438,6 +448,8 @@ ok_format_change(6, 'number') ok_format_change(6, 'scalar') -- integer --X--> unsigned fail_format_change(6, 'unsigned') +-- integer --X--> decimal +fail_format_change(6, 'decimal') -- boolean -----> any ok_format_change(7, 'any') @@ -445,22 +457,46 @@ ok_format_change(7, 'any') ok_format_change(7, 'scalar') -- boolean --X--> string fail_format_change(7, 'string') +-- boolead --X--> decimal +fail_format_change(7, 'decimal') -- scalar -----> any ok_format_change(8, 'any') -- scalar --X--> unsigned fail_format_change(8, 'unsigned') +-- scalar --X--> decimal +fail_format_change(8, 'decimal') -- array -----> any ok_format_change(9, 'any') -- array --X--> scalar fail_format_change(9, 'scalar') +-- arary --X--> decimal +fail_format_change(9, 'decimal') -- map -----> any ok_format_change(10, 'any') -- map --X--> scalar fail_format_change(10, 'scalar') - +-- map --X--> decimal +fail_format_change(10, 'decimal') + +-- decimal ----> any +ok_format_change(11, 'any') +-- decimal ----> number +ok_format_change(11, 'number') +-- decimal ----> scalar +ok_format_change(11, 'scalar') +-- decimal --X--> string +fail_format_change(11, 'string') +-- decimal --X--> integer +fail_format_change(11, 'integer') +-- decimal --X--> unsigned +fail_format_change(11, 'unsigned') +-- decimal --X--> map +fail_format_change(11, 'map') +-- decimal --X--> array +fail_format_change(11, 'array') s:drop() -- Check new fields adding. diff --git a/test/engine/decimal.result b/test/engine/decimal.result index f8888d7c9..415868c89 100644 --- a/test/engine/decimal.result +++ b/test/engine/decimal.result @@ -12,6 +12,9 @@ engine = test_run:get_cfg('engine') decimal = require('decimal') | --- | ... +ffi = require('ffi') + | --- + | ... _ = box.schema.space.create('test', {engine=engine}) | --- @@ -76,6 +79,278 @@ box.space.test:select{} | - [3, 3.3] | - [4, 1234567890123456789.9876543210987654321, 1.2345] | ... + +box.space.test:drop() + | --- + | ... + +-- check decimal indexes +_ = box.schema.space.create('test', {engine=engine}) + | --- + | ... +_ = box.space.test:create_index('pk', {parts={1,'decimal'}}) + | --- + | ... + +test_run:cmd('setopt delimiter ";"') + | --- + | - true + | ... +for i = 0,16 do + box.space.test:insert{decimal.new((i-8)/4)} +end; + | --- + | ... +test_run:cmd('setopt delimiter ""'); + | --- + | - true + | ... + +box.space.test:select{} + | --- + | - - [-2] + | - [-1.75] + | - [-1.5] + | - [-1.25] + | - [-1] + | - [-0.75] + | - [-0.5] + | - [-0.25] + | - [0] + | - [0.25] + | - [0.5] + | - [0.75] + | - [1] + | - [1.25] + | - [1.5] + | - [1.75] + | - [2] + | ... + +-- check invalid values +box.space.test:insert{1.23} + | --- + | - error: 'Tuple field 1 type does not match one required by operation: expected decimal' + | ... +box.space.test:insert{'str'} + | --- + | - error: 'Tuple field 1 type does not match one required by operation: expected decimal' + | ... +box.space.test:insert{ffi.new('uint64_t', 0)} + | --- + | - error: 'Tuple field 1 type does not match one required by operation: expected decimal' + | ... +-- check duplicates +box.space.test:insert{decimal.new(0)} + | --- + | - error: Duplicate key exists in unique index 'pk' in space 'test' + | ... + +box.space.test.index.pk:drop() + | --- + | ... + +_ = box.space.test:create_index('pk', {parts={1, 'number'}}) + | --- + | ... + +test_run:cmd('setopt delimiter ";"') + | --- + | - true + | ... +for i = 0, 32 do + local val = (i - 16) / 8 + if i % 2 == 1 then val = decimal.new(val) end + box.space.test:insert{val} +end; + | --- + | ... +test_run:cmd('setopt delimiter ""'); + | --- + | - true + | ... + +box.space.test:select{} + | --- + | - - [-2] + | - [-1.875] + | - [-1.75] + | - [-1.625] + | - [-1.5] + | - [-1.375] + | - [-1.25] + | - [-1.125] + | - [-1] + | - [-0.875] + | - [-0.75] + | - [-0.625] + | - [-0.5] + | - [-0.375] + | - [-0.25] + | - [-0.125] + | - [0] + | - [0.125] + | - [0.25] + | - [0.375] + | - [0.5] + | - [0.625] + | - [0.75] + | - [0.875] + | - [1] + | - [1.125] + | - [1.25] + | - [1.375] + | - [1.5] + | - [1.625] + | - [1.75] + | - [1.875] + | - [2] + | ... + +-- check duplicates +box.space.test:insert{-2} + | --- + | - error: Duplicate key exists in unique index 'pk' in space 'test' + | ... +box.space.test:insert{decimal.new(-2)} + | --- + | - error: Duplicate key exists in unique index 'pk' in space 'test' + | ... +box.space.test:insert{decimal.new(-1.875)} + | --- + | - error: Duplicate key exists in unique index 'pk' in space 'test' + | ... +box.space.test:insert{-1.875} + | --- + | - error: Duplicate key exists in unique index 'pk' in space 'test' + | ... + +box.space.test.index.pk:drop() + | --- + | ... + +_ = box.space.test:create_index('pk') + | --- + | ... +test_run:cmd('setopt delimiter ";"') + | --- + | - true + | ... +for i = 1,10 do + box.space.test:insert{i, decimal.new(i/10)} +end; + | --- + | ... +test_run:cmd('setopt delimiter ""'); + | --- + | - true + | ... + +-- a bigger test with a secondary index this time. +box.space.test:insert{11, 'str'} + | --- + | - [11, 'str'] + | ... +box.space.test:insert{12, 0.63} + | --- + | - [12, 0.63] + | ... +box.space.test:insert{13, 0.57} + | --- + | - [13, 0.57] + | ... +box.space.test:insert{14, 0.33} + | --- + | - [14, 0.33] + | ... +box.space.test:insert{16, 0.71} + | --- + | - [16, 0.71] + | ... + +_ = box.space.test:create_index('sk', {parts={2, 'scalar'}}) + | --- + | ... +box.space.test.index.sk:select{} + | --- + | - - [1, 0.1] + | - [2, 0.2] + | - [3, 0.3] + | - [14, 0.33] + | - [4, 0.4] + | - [5, 0.5] + | - [13, 0.57] + | - [6, 0.6] + | - [12, 0.63] + | - [7, 0.7] + | - [16, 0.71] + | - [8, 0.8] + | - [9, 0.9] + | - [10, 1] + | - [11, 'str'] + | ... + +box.space.test:drop() + | --- + | ... + +-- check index alter +_ = box.schema.space.create('test', {engine=engine}) + | --- + | ... +_ = box.space.test:create_index('pk') + | --- + | ... +_ = box.space.test:create_index('sk', {parts={2, 'number'}}) + | --- + | ... +box.space.test:insert{1, decimal.new(-2)} + | --- + | - [1, -2] + | ... +box.space.test:insert{2, -5} + | --- + | - [2, -5] + | ... +-- failure +box.space.test.index.sk:alter{parts={2, 'decimal'}} + | --- + | - error: 'Tuple field 2 type does not match one required by operation: expected decimal' + | ... +_ = box.space.test:delete{2} + | --- + | ... +box.space.test.index.sk:alter{parts={2, 'decimal'}} + | --- + | ... +box.space.test:insert{3, decimal.new(3)} + | --- + | - [3, 3] + | ... +--failure +box.space.test:insert{4, 'string'} + | --- + | - error: 'Tuple field 2 type does not match one required by operation: expected decimal' + | ... +-- failure +box.space.test:insert{2, -5} + | --- + | - error: 'Tuple field 2 type does not match one required by operation: expected decimal' + | ... +box.space.test.index.sk:alter{parts={2, 'number'}} + | --- + | ... +box.space.test:insert{2, -5} + | --- + | - [2, -5] + | ... +box.space.test.index.sk:select{} + | --- + | - - [2, -5] + | - [1, -2] + | - [3, 3] + | ... + box.space.test:drop() | --- | ... diff --git a/test/engine/decimal.test.lua b/test/engine/decimal.test.lua index 1b14871b0..3763bf0a3 100644 --- a/test/engine/decimal.test.lua +++ b/test/engine/decimal.test.lua @@ -3,6 +3,7 @@ test_run = env.new() engine = test_run:get_cfg('engine') decimal = require('decimal') +ffi = require('ffi') _ = box.schema.space.create('test', {engine=engine}) _ = box.space.test:create_index('pk') @@ -18,4 +19,86 @@ a:next(2) a:slice(-2) box.space.test:replace{3, decimal.new(3.3)} box.space.test:select{} + +box.space.test:drop() + +-- check decimal indexes +_ = box.schema.space.create('test', {engine=engine}) +_ = box.space.test:create_index('pk', {parts={1,'decimal'}}) + +test_run:cmd('setopt delimiter ";"') +for i = 0,16 do + box.space.test:insert{decimal.new((i-8)/4)} +end; +test_run:cmd('setopt delimiter ""'); + +box.space.test:select{} + +-- check invalid values +box.space.test:insert{1.23} +box.space.test:insert{'str'} +box.space.test:insert{ffi.new('uint64_t', 0)} +-- check duplicates +box.space.test:insert{decimal.new(0)} + +box.space.test.index.pk:drop() + +_ = box.space.test:create_index('pk', {parts={1, 'number'}}) + +test_run:cmd('setopt delimiter ";"') +for i = 0, 32 do + local val = (i - 16) / 8 + if i % 2 == 1 then val = decimal.new(val) end + box.space.test:insert{val} +end; +test_run:cmd('setopt delimiter ""'); + +box.space.test:select{} + +-- check duplicates +box.space.test:insert{-2} +box.space.test:insert{decimal.new(-2)} +box.space.test:insert{decimal.new(-1.875)} +box.space.test:insert{-1.875} + +box.space.test.index.pk:drop() + +_ = box.space.test:create_index('pk') +test_run:cmd('setopt delimiter ";"') +for i = 1,10 do + box.space.test:insert{i, decimal.new(i/10)} +end; +test_run:cmd('setopt delimiter ""'); + +-- a bigger test with a secondary index this time. +box.space.test:insert{11, 'str'} +box.space.test:insert{12, 0.63} +box.space.test:insert{13, 0.57} +box.space.test:insert{14, 0.33} +box.space.test:insert{16, 0.71} + +_ = box.space.test:create_index('sk', {parts={2, 'scalar'}}) +box.space.test.index.sk:select{} + +box.space.test:drop() + +-- check index alter +_ = box.schema.space.create('test', {engine=engine}) +_ = box.space.test:create_index('pk') +_ = box.space.test:create_index('sk', {parts={2, 'number'}}) +box.space.test:insert{1, decimal.new(-2)} +box.space.test:insert{2, -5} +-- failure +box.space.test.index.sk:alter{parts={2, 'decimal'}} +_ = box.space.test:delete{2} +box.space.test.index.sk:alter{parts={2, 'decimal'}} +box.space.test:insert{3, decimal.new(3)} +--failure +box.space.test:insert{4, 'string'} +-- failure +box.space.test:insert{2, -5} +box.space.test.index.sk:alter{parts={2, 'number'}} +box.space.test:insert{2, -5} +box.space.test.index.sk:select{} + box.space.test:drop() diff --git a/test/engine/iterator.result b/test/engine/iterator.result index 6e03fbbbd..63dc262bd 100644 --- a/test/engine/iterator.result +++ b/test/engine/iterator.result @@ -1471,6 +1471,736 @@ t space:drop() --- ... +-- iterator (decimal) +decimal = require('decimal') +--- +... +space = box.schema.space.create('test', { engine = engine }) +--- +... +index = space:create_index('primary', { type = 'tree', parts = {1, 'decimal'} }) +--- +... +for key = 1, 100 do space:replace{decimal.new((key-50)/10)} end +--- +... +t = {} for state, v in index:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end +--- +... +t +--- +- - [-4.9] + - [-4.8] + - [-4.7] + - [-4.6] + - [-4.5] + - [-4.4] + - [-4.3] + - [-4.2] + - [-4.1] + - [-4] + - [-3.9] + - [-3.8] + - [-3.7] + - [-3.6] + - [-3.5] + - [-3.4] + - [-3.3] + - [-3.2] + - [-3.1] + - [-3] + - [-2.9] + - [-2.8] + - [-2.7] + - [-2.6] + - [-2.5] + - [-2.4] + - [-2.3] + - [-2.2] + - [-2.1] + - [-2] + - [-1.9] + - [-1.8] + - [-1.7] + - [-1.6] + - [-1.5] + - [-1.4] + - [-1.3] + - [-1.2] + - [-1.1] + - [-1] + - [-0.9] + - [-0.8] + - [-0.7] + - [-0.6] + - [-0.5] + - [-0.4] + - [-0.3] + - [-0.2] + - [-0.1] + - [0] + - [0.1] + - [0.2] + - [0.3] + - [0.4] + - [0.5] + - [0.6] + - [0.7] + - [0.8] + - [0.9] + - [1] + - [1.1] + - [1.2] + - [1.3] + - [1.4] + - [1.5] + - [1.6] + - [1.7] + - [1.8] + - [1.9] + - [2] + - [2.1] + - [2.2] + - [2.3] + - [2.4] + - [2.5] + - [2.6] + - [2.7] + - [2.8] + - [2.9] + - [3] + - [3.1] + - [3.2] + - [3.3] + - [3.4] + - [3.5] + - [3.6] + - [3.7] + - [3.8] + - [3.9] + - [4] + - [4.1] + - [4.2] + - [4.3] + - [4.4] + - [4.5] + - [4.6] + - [4.7] + - [4.8] + - [4.9] + - [5] +... +t = {} for state, v in index:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end +--- +... +t +--- +- - [-4.9] + - [-4.8] + - [-4.7] + - [-4.6] + - [-4.5] + - [-4.4] + - [-4.3] + - [-4.2] + - [-4.1] + - [-4] + - [-3.9] + - [-3.8] + - [-3.7] + - [-3.6] + - [-3.5] + - [-3.4] + - [-3.3] + - [-3.2] + - [-3.1] + - [-3] + - [-2.9] + - [-2.8] + - [-2.7] + - [-2.6] + - [-2.5] + - [-2.4] + - [-2.3] + - [-2.2] + - [-2.1] + - [-2] + - [-1.9] + - [-1.8] + - [-1.7] + - [-1.6] + - [-1.5] + - [-1.4] + - [-1.3] + - [-1.2] + - [-1.1] + - [-1] + - [-0.9] + - [-0.8] + - [-0.7] + - [-0.6] + - [-0.5] + - [-0.4] + - [-0.3] + - [-0.2] + - [-0.1] + - [0] + - [0.1] + - [0.2] + - [0.3] + - [0.4] + - [0.5] + - [0.6] + - [0.7] + - [0.8] + - [0.9] + - [1] + - [1.1] + - [1.2] + - [1.3] + - [1.4] + - [1.5] + - [1.6] + - [1.7] + - [1.8] + - [1.9] + - [2] + - [2.1] + - [2.2] + - [2.3] + - [2.4] + - [2.5] + - [2.6] + - [2.7] + - [2.8] + - [2.9] + - [3] + - [3.1] + - [3.2] + - [3.3] + - [3.4] + - [3.5] + - [3.6] + - [3.7] + - [3.8] + - [3.9] + - [4] + - [4.1] + - [4.2] + - [4.3] + - [4.4] + - [4.5] + - [4.6] + - [4.7] + - [4.8] + - [4.9] + - [5] +... +t = {} for state, v in index:pairs(decimal.new(-0.6), {iterator = 'GE'}) do table.insert(t, v) end +--- +... +t +--- +- - [-0.6] + - [-0.5] + - [-0.4] + - [-0.3] + - [-0.2] + - [-0.1] + - [0] + - [0.1] + - [0.2] + - [0.3] + - [0.4] + - [0.5] + - [0.6] + - [0.7] + - [0.8] + - [0.9] + - [1] + - [1.1] + - [1.2] + - [1.3] + - [1.4] + - [1.5] + - [1.6] + - [1.7] + - [1.8] + - [1.9] + - [2] + - [2.1] + - [2.2] + - [2.3] + - [2.4] + - [2.5] + - [2.6] + - [2.7] + - [2.8] + - [2.9] + - [3] + - [3.1] + - [3.2] + - [3.3] + - [3.4] + - [3.5] + - [3.6] + - [3.7] + - [3.8] + - [3.9] + - [4] + - [4.1] + - [4.2] + - [4.3] + - [4.4] + - [4.5] + - [4.6] + - [4.7] + - [4.8] + - [4.9] + - [5] +... +t = {} for state, v in index:pairs(decimal.new(-0.6), {iterator = 'GT'}) do table.insert(t, v) end +--- +... +t +--- +- - [-0.5] + - [-0.4] + - [-0.3] + - [-0.2] + - [-0.1] + - [0] + - [0.1] + - [0.2] + - [0.3] + - [0.4] + - [0.5] + - [0.6] + - [0.7] + - [0.8] + - [0.9] + - [1] + - [1.1] + - [1.2] + - [1.3] + - [1.4] + - [1.5] + - [1.6] + - [1.7] + - [1.8] + - [1.9] + - [2] + - [2.1] + - [2.2] + - [2.3] + - [2.4] + - [2.5] + - [2.6] + - [2.7] + - [2.8] + - [2.9] + - [3] + - [3.1] + - [3.2] + - [3.3] + - [3.4] + - [3.5] + - [3.6] + - [3.7] + - [3.8] + - [3.9] + - [4] + - [4.1] + - [4.2] + - [4.3] + - [4.4] + - [4.5] + - [4.6] + - [4.7] + - [4.8] + - [4.9] + - [5] +... +t = {} for state, v in index:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end +--- +... +t +--- +- - [5] + - [4.9] + - [4.8] + - [4.7] + - [4.6] + - [4.5] + - [4.4] + - [4.3] + - [4.2] + - [4.1] + - [4] + - [3.9] + - [3.8] + - [3.7] + - [3.6] + - [3.5] + - [3.4] + - [3.3] + - [3.2] + - [3.1] + - [3] + - [2.9] + - [2.8] + - [2.7] + - [2.6] + - [2.5] + - [2.4] + - [2.3] + - [2.2] + - [2.1] + - [2] + - [1.9] + - [1.8] + - [1.7] + - [1.6] + - [1.5] + - [1.4] + - [1.3] + - [1.2] + - [1.1] + - [1] + - [0.9] + - [0.8] + - [0.7] + - [0.6] + - [0.5] + - [0.4] + - [0.3] + - [0.2] + - [0.1] + - [0] + - [-0.1] + - [-0.2] + - [-0.3] + - [-0.4] + - [-0.5] + - [-0.6] + - [-0.7] + - [-0.8] + - [-0.9] + - [-1] + - [-1.1] + - [-1.2] + - [-1.3] + - [-1.4] + - [-1.5] + - [-1.6] + - [-1.7] + - [-1.8] + - [-1.9] + - [-2] + - [-2.1] + - [-2.2] + - [-2.3] + - [-2.4] + - [-2.5] + - [-2.6] + - [-2.7] + - [-2.8] + - [-2.9] + - [-3] + - [-3.1] + - [-3.2] + - [-3.3] + - [-3.4] + - [-3.5] + - [-3.6] + - [-3.7] + - [-3.8] + - [-3.9] + - [-4] + - [-4.1] + - [-4.2] + - [-4.3] + - [-4.4] + - [-4.5] + - [-4.6] + - [-4.7] + - [-4.8] + - [-4.9] +... +t = {} for state, v in index:pairs(decimal.new(2.7), {iterator = 'LE'}) do table.insert(t, v) end +--- +... +t +--- +- - [2.7] + - [2.6] + - [2.5] + - [2.4] + - [2.3] + - [2.2] + - [2.1] + - [2] + - [1.9] + - [1.8] + - [1.7] + - [1.6] + - [1.5] + - [1.4] + - [1.3] + - [1.2] + - [1.1] + - [1] + - [0.9] + - [0.8] + - [0.7] + - [0.6] + - [0.5] + - [0.4] + - [0.3] + - [0.2] + - [0.1] + - [0] + - [-0.1] + - [-0.2] + - [-0.3] + - [-0.4] + - [-0.5] + - [-0.6] + - [-0.7] + - [-0.8] + - [-0.9] + - [-1] + - [-1.1] + - [-1.2] + - [-1.3] + - [-1.4] + - [-1.5] + - [-1.6] + - [-1.7] + - [-1.8] + - [-1.9] + - [-2] + - [-2.1] + - [-2.2] + - [-2.3] + - [-2.4] + - [-2.5] + - [-2.6] + - [-2.7] + - [-2.8] + - [-2.9] + - [-3] + - [-3.1] + - [-3.2] + - [-3.3] + - [-3.4] + - [-3.5] + - [-3.6] + - [-3.7] + - [-3.8] + - [-3.9] + - [-4] + - [-4.1] + - [-4.2] + - [-4.3] + - [-4.4] + - [-4.5] + - [-4.6] + - [-4.7] + - [-4.8] + - [-4.9] +... +t = {} for state, v in index:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end +--- +... +t +--- +- - [5] + - [4.9] + - [4.8] + - [4.7] + - [4.6] + - [4.5] + - [4.4] + - [4.3] + - [4.2] + - [4.1] + - [4] + - [3.9] + - [3.8] + - [3.7] + - [3.6] + - [3.5] + - [3.4] + - [3.3] + - [3.2] + - [3.1] + - [3] + - [2.9] + - [2.8] + - [2.7] + - [2.6] + - [2.5] + - [2.4] + - [2.3] + - [2.2] + - [2.1] + - [2] + - [1.9] + - [1.8] + - [1.7] + - [1.6] + - [1.5] + - [1.4] + - [1.3] + - [1.2] + - [1.1] + - [1] + - [0.9] + - [0.8] + - [0.7] + - [0.6] + - [0.5] + - [0.4] + - [0.3] + - [0.2] + - [0.1] + - [0] + - [-0.1] + - [-0.2] + - [-0.3] + - [-0.4] + - [-0.5] + - [-0.6] + - [-0.7] + - [-0.8] + - [-0.9] + - [-1] + - [-1.1] + - [-1.2] + - [-1.3] + - [-1.4] + - [-1.5] + - [-1.6] + - [-1.7] + - [-1.8] + - [-1.9] + - [-2] + - [-2.1] + - [-2.2] + - [-2.3] + - [-2.4] + - [-2.5] + - [-2.6] + - [-2.7] + - [-2.8] + - [-2.9] + - [-3] + - [-3.1] + - [-3.2] + - [-3.3] + - [-3.4] + - [-3.5] + - [-3.6] + - [-3.7] + - [-3.8] + - [-3.9] + - [-4] + - [-4.1] + - [-4.2] + - [-4.3] + - [-4.4] + - [-4.5] + - [-4.6] + - [-4.7] + - [-4.8] + - [-4.9] +... +t = {} for state, v in index:pairs(decimal.new(2.7), {iterator = 'LT'}) do table.insert(t, v) end +--- +... +t +--- +- - [2.6] + - [2.5] + - [2.4] + - [2.3] + - [2.2] + - [2.1] + - [2] + - [1.9] + - [1.8] + - [1.7] + - [1.6] + - [1.5] + - [1.4] + - [1.3] + - [1.2] + - [1.1] + - [1] + - [0.9] + - [0.8] + - [0.7] + - [0.6] + - [0.5] + - [0.4] + - [0.3] + - [0.2] + - [0.1] + - [0] + - [-0.1] + - [-0.2] + - [-0.3] + - [-0.4] + - [-0.5] + - [-0.6] + - [-0.7] + - [-0.8] + - [-0.9] + - [-1] + - [-1.1] + - [-1.2] + - [-1.3] + - [-1.4] + - [-1.5] + - [-1.6] + - [-1.7] + - [-1.8] + - [-1.9] + - [-2] + - [-2.1] + - [-2.2] + - [-2.3] + - [-2.4] + - [-2.5] + - [-2.6] + - [-2.7] + - [-2.8] + - [-2.9] + - [-3] + - [-3.1] + - [-3.2] + - [-3.3] + - [-3.4] + - [-3.5] + - [-3.6] + - [-3.7] + - [-3.8] + - [-3.9] + - [-4] + - [-4.1] + - [-4.2] + - [-4.3] + - [-4.4] + - [-4.5] + - [-4.6] + - [-4.7] + - [-4.8] + - [-4.9] +... +space:drop() +--- +... -- iterator multi-part (num, num) space = box.schema.space.create('test', { engine = engine }) --- diff --git a/test/engine/iterator.test.lua b/test/engine/iterator.test.lua index 9ff51ebe1..2f83d19dd 100644 --- a/test/engine/iterator.test.lua +++ b/test/engine/iterator.test.lua @@ -48,6 +48,28 @@ t = {} for state, v in index:pairs(77, {iterator = 'LT'}) do table.insert(t, v) t space:drop() +-- iterator (decimal) +decimal = require('decimal') +space = box.schema.space.create('test', { engine = engine }) +index = space:create_index('primary', { type = 'tree', parts = {1, 'decimal'} }) +for key = 1, 100 do space:replace{decimal.new((key-50)/10)} end +t = {} for state, v in index:pairs({}, {iterator = 'ALL'}) do table.insert(t, v) end +t +t = {} for state, v in index:pairs({}, {iterator = 'GE'}) do table.insert(t, v) end +t +t = {} for state, v in index:pairs(decimal.new(-0.6), {iterator = 'GE'}) do table.insert(t, v) end +t +t = {} for state, v in index:pairs(decimal.new(-0.6), {iterator = 'GT'}) do table.insert(t, v) end +t +t = {} for state, v in index:pairs({}, {iterator = 'LE'}) do table.insert(t, v) end +t +t = {} for state, v in index:pairs(decimal.new(2.7), {iterator = 'LE'}) do table.insert(t, v) end +t +t = {} for state, v in index:pairs({}, {iterator = 'LT'}) do table.insert(t, v) end +t +t = {} for state, v in index:pairs(decimal.new(2.7), {iterator = 'LT'}) do table.insert(t, v) end +t +space:drop() -- iterator multi-part (num, num) space = box.schema.space.create('test', { engine = engine }) -- 2.20.1 (Apple Git-117)