From: Serge Petrenko <sergepetrenko@tarantool.org> To: Vladimir Davydov <vdavydov.dev@gmail.com> Cc: tarantool-patches@freelists.org, kostja@tarantool.org Subject: Re: [PATCH v2 8/8] decimal: allow to index decimals Date: Thu, 8 Aug 2019 16:42:40 +0300 [thread overview] Message-ID: <A62B655D-6FE2-4CED-A6C0-B0FFBF892E04@tarantool.org> (raw) In-Reply-To: <8fb249e461a35b692113a02acbbaf61b32dc9739.1565263272.git.sergepetrenko@tarantool.org> Sorry, a tiny fix for test. Didn’t notice the problem on my local machine. diff --git a/test/engine/decimal.result b/test/engine/decimal.result index 4ced88c24..415868c89 100644 --- a/test/engine/decimal.result +++ b/test/engine/decimal.result @@ -317,7 +317,7 @@ 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:delete{2} | --- | ... box.space.test.index.sk:alter{parts={2, 'decimal'}} diff --git a/test/engine/decimal.test.lua b/test/engine/decimal.test.lua index 4e38650dd..3763bf0a3 100644 --- a/test/engine/decimal.test.lua +++ b/test/engine/decimal.test.lua @@ -90,7 +90,7 @@ 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:delete{2} box.space.test.index.sk:alter{parts={2, 'decimal'}} box.space.test:insert{3, decimal.new(3)} --failure -- Serge Petrenko sergepetrenko@tarantool.org > 8 авг. 2019 г., в 14:55, Serge Petrenko <sergepetrenko@tarantool.org> написал(а): > > 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 | 30 ++-- > src/box/field_def.h | 1 + > src/box/tuple_compare.cc | 104 ++++++++++++- > 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 +++++++++++ > 9 files changed, 617 insertions(+), 17 deletions(-) > > diff --git a/src/box/field_def.c b/src/box/field_def.c > index 2fad81d42..b1ecb1f19 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 "lua/utils.h" > > const char *mp_type_strs[] = { > /* .MP_NIL = */ "nil", > @@ -52,13 +53,14 @@ const uint32_t field_mp_type[] = { > /* [FIELD_TYPE_UNSIGNED] = */ 1U << MP_FIELD_UINT, > /* [FIELD_TYPE_STRING] = */ 1U << MP_FIELD_STR, > /* [FIELD_TYPE_NUMBER] = */ (1U << MP_FIELD_UINT) | (1U << MP_FIELD_INT) | > - (1U << MP_FIELD_FLOAT) | (1U << MP_FIELD_DOUBLE), > + (1U << MP_FIELD_FLOAT) | (1U << MP_FIELD_DOUBLE) | (1U << MP_FIELD_DECIMAL), > /* [FIELD_TYPE_INTEGER] = */ (1U << MP_FIELD_UINT) | (1U << MP_FIELD_INT), > /* [FIELD_TYPE_BOOLEAN] = */ 1U << MP_FIELD_BOOL, > /* [FIELD_TYPE_VARBINARY] = */ 1U << MP_FIELD_BIN, > /* [FIELD_TYPE_SCALAR] = */ (1U << MP_FIELD_UINT) | (1U << MP_FIELD_INT) | > (1U << MP_FIELD_FLOAT) | (1U << MP_FIELD_DOUBLE) | (1U << MP_FIELD_STR) | > - (1U << MP_FIELD_BIN) | (1U << MP_FIELD_BOOL), > + (1U << MP_FIELD_BIN) | (1U << MP_FIELD_BOOL) | (1U << MP_FIELD_DECIMAL), > + /* [FIELD_TYPE_DECIMAL] = */ 1U << MP_FIELD_DECIMAL, > /* [FIELD_TYPE_ARRAY] = */ 1U << MP_FIELD_ARRAY, > /* [FIELD_TYPE_MAP] = */ (1U << MP_FIELD_MAP), > }; > @@ -72,6 +74,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 +101,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 2f21f3cfc..abd8ca28b 100644 > --- a/src/box/field_def.h > +++ b/src/box/field_def.h > @@ -59,6 +59,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 > diff --git a/src/box/tuple_compare.cc b/src/box/tuple_compare.cc > index b4932db6d..990b584c4 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 <math.h> > +#include "lib/core/decimal.h" > +#include "lib/core/mp_decimal.h" > +#include "lib/core/mp_user_types.h" > > /* {{{ tuple_compare */ > > @@ -88,7 +91,7 @@ static enum mp_class mp_classes[] = { > /* .MP_FIELD_BOOL = */ MP_CLASS_BOOL, > /* .MP_FIELD_FLOAT = */ MP_CLASS_NUMBER, > /* .MP_FIELD_DOUBLE = */ MP_CLASS_NUMBER, > - /* .MP_FIELD_BIN = */ MP_CLASS_BIN > + /* .MP_FIELD_DECIMAL = */ MP_CLASS_NUMBER > }; > > #define COMPARE_RESULT(a, b) (a < b ? -1 : a > b) > @@ -265,6 +268,45 @@ mp_compare_double_any_number(double lhs, const char *rhs, > return k * COMPARE_RESULT(lqbit, rqbit); > } > > +static int > +mp_compare_decimal_any_number(decimal_t *lhs, const char *rhs, > + enum mp_field_type rhs_type, int k) > +{ > + decimal_t rhs_dec; > + switch (rhs_type) { > + case MP_FIELD_FLOAT: > + { > + double d = mp_decode_float(&rhs); > + decimal_from_double(&rhs_dec, d); > + break; > + } > + case MP_FIELD_DOUBLE: > + { > + double d = mp_decode_double(&rhs); > + decimal_from_double(&rhs_dec, d); > + break; > + } > + case MP_FIELD_INT: > + { > + int64_t num = mp_decode_int(&rhs); > + decimal_from_int64(&rhs_dec, num); > + break; > + } > + case MP_FIELD_UINT: > + { > + uint64_t num = mp_decode_uint(&rhs); > + decimal_from_uint64(&rhs_dec, num); > + break; > + } > + case MP_FIELD_DECIMAL: > + mp_decode_decimal(&rhs, &rhs_dec); > + break; > + default: > + unreachable(); > + } > + return k * decimal_compare(lhs, &rhs_dec); > +} > + > static int > mp_compare_number_with_type(const char *lhs, enum mp_field_type lhs_type, > const char *rhs, enum mp_field_type rhs_type) > @@ -272,6 +314,21 @@ mp_compare_number_with_type(const char *lhs, enum mp_field_type lhs_type, > assert(mp_classof(lhs_type) == MP_CLASS_NUMBER); > assert(mp_classof(rhs_type) == 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_FIELD_DECIMAL) { > + return mp_compare_decimal_any_number( > + mp_decode_decimal(&rhs, &dec), lhs, lhs_type, -1 > + ); > + } > + if (lhs_type == MP_FIELD_DECIMAL) { > + return mp_compare_decimal_any_number( > + mp_decode_decimal(&lhs, &dec), rhs, rhs_type, 1 > + ); > + } > if (rhs_type == MP_FIELD_FLOAT) { > return mp_compare_double_any_number( > mp_decode_float(&rhs), lhs, lhs_type, -1 > @@ -412,6 +469,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_number(field_a, field_b); > default: > unreachable(); > return 0; > @@ -445,6 +504,8 @@ tuple_compare_field_with_type(const char *field_a, enum mp_field_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(field_a, field_b); > default: > unreachable(); > return 0; > @@ -1504,6 +1565,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) > { > @@ -1580,12 +1659,25 @@ field_hint_number(const char *field) > return hint_double(mp_decode_float(&field)); > case MP_FIELD_DOUBLE: > return hint_double(mp_decode_double(&field)); > + case MP_FIELD_DECIMAL: > + { > + decimal_t dec; > + return hint_decimal(mp_decode_decimal(&field, &dec)); > + } > default: > unreachable(); > } > return HINT_NONE; > } > > +static inline hint_t > +field_hint_decimal(const char *field) > +{ > + assert(msgpack_to_field_type(field) == MP_FIELD_DECIMAL); > + decimal_t dec; > + return hint_decimal(mp_decode_decimal(&field, &dec)); > +} > + > static inline hint_t > field_hint_string(const char *field, struct coll *coll) > { > @@ -1625,6 +1717,11 @@ field_hint_scalar(const char *field, struct coll *coll) > case MP_FIELD_BIN: > len = mp_decode_binl(&field); > return hint_bin(field, len); > + case MP_FIELD_DECIMAL: > + { > + decimal_t dec; > + return hint_decimal(mp_decode_decimal(&field, &dec)); > + } > default: > unreachable(); > } > @@ -1652,6 +1749,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(); > } > @@ -1757,6 +1856,9 @@ key_def_set_hint_func(struct key_def *def) > case FIELD_TYPE_SCALAR: > key_def_set_hint_func<FIELD_TYPE_SCALAR>(def); > break; > + case FIELD_TYPE_DECIMAL: > + key_def_set_hint_func<FIELD_TYPE_DECIMAL>(def); > + break; > default: > /* Invalid key definition. */ > def->key_hint = NULL; > diff --git a/src/lib/core/decimal.h b/src/lib/core/decimal.h > index cf41a5052..72c9fd4b5 100644 > --- a/src/lib/core/decimal.h > +++ b/src/lib/core/decimal.h > @@ -37,6 +37,10 @@ > #include "third_party/decNumber/decNumber.h" > #include <stdint.h> > > +#if defined(__cplusplus) > +extern "C" { > +#endif /* defined(__cplusplus) */ > + > typedef decNumber decimal_t; > > /** > @@ -233,4 +237,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 <stdint.h> > > +#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..4ced88c24 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..4e38650dd 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() > -- > 2.20.1 (Apple Git-117) >
next prev parent reply other threads:[~2019-08-08 13:42 UTC|newest] Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top 2019-08-08 11:55 [PATCH v2 0/8] Decimal indices Serge Petrenko 2019-08-08 11:55 ` [PATCH v2 1/8] lua: fix decimal comparison with nil Serge Petrenko 2019-08-12 21:16 ` Konstantin Osipov 2019-08-14 11:00 ` Vladimir Davydov 2019-08-14 22:17 ` Konstantin Osipov 2019-08-08 11:55 ` [PATCH v2 2/8] decimal: fix encoding numbers with positive exponent Serge Petrenko 2019-08-12 21:18 ` Konstantin Osipov 2019-08-13 9:00 ` [tarantool-patches] " Serge Petrenko 2019-08-14 22:21 ` Konstantin Osipov 2019-08-14 11:56 ` Vladimir Davydov 2019-08-08 11:55 ` [PATCH v2 3/8] lua/pickle: fix a typo Serge Petrenko 2019-08-12 21:18 ` Konstantin Osipov 2019-08-14 11:12 ` Vladimir Davydov 2019-08-14 11:15 ` Serge Petrenko 2019-08-08 11:55 ` [PATCH v2 4/8] lua: rework luaL_field types to support msgpack extensions Serge Petrenko 2019-08-12 21:23 ` Konstantin Osipov 2019-08-13 13:15 ` [tarantool-patches] " Serge Petrenko 2019-08-14 22:23 ` Konstantin Osipov 2019-08-15 8:27 ` Serge Petrenko 2019-08-16 8:06 ` Konstantin Osipov 2019-08-08 11:55 ` [PATCH v2 5/8] box: rework field_def and tuple_compare to work with mp_field_type instead of mp_type Serge Petrenko 2019-08-12 21:28 ` Konstantin Osipov 2019-08-08 11:55 ` [PATCH v2 6/8] decimal: allow to encode/decode decimals as MsgPack Serge Petrenko 2019-08-12 21:29 ` Konstantin Osipov 2019-08-12 21:34 ` Konstantin Osipov 2019-08-13 14:01 ` Serge Petrenko 2019-08-14 22:25 ` Konstantin Osipov 2019-08-08 11:55 ` [PATCH v2 7/8] decimal: add conversions to (u)int64_t Serge Petrenko 2019-08-12 21:39 ` Konstantin Osipov 2019-08-13 14:18 ` Serge Petrenko 2019-08-14 22:26 ` Konstantin Osipov 2019-08-14 22:29 ` Konstantin Osipov 2019-08-08 11:55 ` [PATCH v2 8/8] decimal: allow to index decimals Serge Petrenko 2019-08-08 13:42 ` Serge Petrenko [this message] 2019-08-12 21:41 ` Konstantin Osipov
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=A62B655D-6FE2-4CED-A6C0-B0FFBF892E04@tarantool.org \ --to=sergepetrenko@tarantool.org \ --cc=kostja@tarantool.org \ --cc=tarantool-patches@freelists.org \ --cc=vdavydov.dev@gmail.com \ --subject='Re: [PATCH v2 8/8] decimal: allow to index decimals' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox