From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtp47.i.mail.ru (smtp47.i.mail.ru [94.100.177.107]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 8BD454696C3 for ; Sat, 4 Apr 2020 02:03:13 +0300 (MSK) From: Serge Petrenko Date: Sat, 4 Apr 2020 02:02:52 +0300 Message-Id: <0bbcecda4f9f2a016eef86f780138ce9ed6afee8.1585954494.git.sergepetrenko@tarantool.org> In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [Tarantool-patches] [PATCH 4/4] box: introduce indices by UUID List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: v.shpilevoy@tarantool.org Cc: tarantool-patches@dev.tarantool.org It is now possible to create an index over UUID values, either returned by something like `uuid.new()` or 36- and 32- byte strings in format "88ebbb9a-8c18-480f-bd04-dd5345f1573c" or "88ebbb9a8c18480fbd04dd5345f1573c". 16-byte binary strings are also supported. Closes #4268 Closes #2916 @TarantoolBot document Title: Document uuid field type. There's a new field type -- UUID, it accepts values returned by `uuid.new()`, as well as strings representing uuids, like `88ebbb9a-8c18-480f-bd04-dd5345f1573c` and `88ebbb9a8c18480fbd04dd5345f1573c` and 16 byte binary values, encoded in msgpack. The index may be either unique or non-unique, nullable or non-nullable, and may be a primary key. To create an index over a uuid field for space `test`, say: ``` box.space.test:create_index("pk", {parts={1, 'uuid'}}) ``` Now you may insert uuids into the space: ``` tarantool> box.space.test:insert{uuid.new()} --- - [e631fdcc-0e8a-4d2f-83fd-b0ce6762b13f] ... tarantool> box.space.test:insert{"64d22e4d-ac92-4a23-899a-e59f34af5479"} --- - ['64d22e4d-ac92-4a23-899a-e59f34af5479'] ... tarantool> box.space.test:insert{"856dc1177dcc49969f1407f8c6c8a371"} --- - ['856dc1177dcc49969f1407f8c6c8a371'] ... tarantool> box.space.test:select{} --- - - ['64d22e4d-ac92-4a23-899a-e59f34af5479'] - ['856dc1177dcc49969f1407f8c6c8a371'] - [e631fdcc-0e8a-4d2f-83fd-b0ce6762b13f] ... ``` --- src/box/field_def.c | 66 ++++++++++++---- src/box/field_def.h | 16 ++++ src/box/key_def.h | 3 +- src/box/tuple_compare.cc | 123 ++++++++++++++++++++++++++++++ src/box/tuple_format.c | 3 +- test/engine/ddl.result | 97 ++++++++++++++++++++++- test/engine/ddl.test.lua | 42 +++++++++- test/engine/gh-4268-uuid.result | 58 ++++++++++++++ test/engine/gh-4268-uuid.test.lua | 30 ++++++++ 9 files changed, 421 insertions(+), 17 deletions(-) create mode 100644 test/engine/gh-4268-uuid.result create mode 100644 test/engine/gh-4268-uuid.test.lua diff --git a/src/box/field_def.c b/src/box/field_def.c index fde4e5a00..c03f26a47 100644 --- a/src/box/field_def.c +++ b/src/box/field_def.c @@ -33,6 +33,8 @@ #include "trivia/util.h" #include "key_def.h" #include "mp_extension_types.h" +#include "mp_uuid.h" +#include "uuid/tt_uuid.h" const char *mp_type_strs[] = { /* .MP_NIL = */ "nil", @@ -67,6 +69,7 @@ const uint32_t field_mp_type[] = { (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_UUID] = */ (1U << MP_STR) | (1U << MP_BIN), /* [FIELD_TYPE_ARRAY] = */ 1U << MP_ARRAY, /* [FIELD_TYPE_MAP] = */ (1U << MP_MAP), }; @@ -82,6 +85,7 @@ const uint32_t field_ext_type[] = { /* [FIELD_TYPE_VARBINARY] = */ 0, /* [FIELD_TYPE_SCALAR] = */ 1U << MP_DECIMAL, /* [FIELD_TYPE_DECIMAL] = */ 1U << MP_DECIMAL, + /* [FIELD_TYPE_UUID] = */ 1U << MP_UUID, /* [FIELD_TYPE_ARRAY] = */ 0, /* [FIELD_TYPE_MAP] = */ 0, }; @@ -97,6 +101,7 @@ const char *field_type_strs[] = { /* [FIELD_TYPE_VARBINARY] = */"varbinary", /* [FIELD_TYPE_SCALAR] = */ "scalar", /* [FIELD_TYPE_DECIMAL] = */ "decimal", + /* [FIELD_TYPE_UUID] = */ "uuid", /* [FIELD_TYPE_ARRAY] = */ "array", /* [FIELD_TYPE_MAP] = */ "map", }; @@ -123,19 +128,20 @@ 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 DOUBLE INTEGER BOOLEAN VARBINARY SCALAR DECIMAL ARRAY MAP */ -/* ANY */ true, false, false, false, false, false, false, false, false, false, false, false, -/* UNSIGNED */ true, true, false, true, false, true, false, false, true, false, false, false, -/* STRING */ true, false, true, false, false, false, false, false, true, false, false, false, -/* NUMBER */ true, false, false, true, false, false, false, false, true, false, false, false, -/* DOUBLE */ true, false, false, true, true, false, false, false, true, false, false, false, -/* INTEGER */ true, false, false, true, false, true, false, false, true, false, false, false, -/* BOOLEAN */ true, false, false, false, false, false, true, false, true, false, false, false, -/* VARBINARY*/ true, false, false, false, false, false, false, true, true, false, false, false, -/* SCALAR */ true, false, false, false, false, false, false, false, true, false, false, false, -/* DECIMAL */ true, false, false, true, false, false, false, false, true, true, false, false, -/* ARRAY */ true, false, false, false, false, false, false, false, false, false, true, false, -/* MAP */ true, false, false, false, false, false, false, false, false, false, false, true, + /* ANY UNSIGNED STRING NUMBER DOUBLE INTEGER BOOLEAN VARBINARY SCALAR DECIMAL UUID ARRAY MAP */ +/* ANY */ true, false, false, false, false, false, false, false, false, false, false, false, false, +/* UNSIGNED */ true, true, false, true, false, true, false, false, true, false, false, false, false, +/* STRING */ true, false, true, false, false, false, false, false, true, false, false, false, false, +/* NUMBER */ true, false, false, true, false, false, false, false, true, false, false, false, false, +/* DOUBLE */ true, false, false, true, true, false, false, false, true, false, false, false, false, +/* INTEGER */ true, false, false, true, false, true, false, false, true, false, false, false, false, +/* BOOLEAN */ true, false, false, false, false, false, true, false, true, false, false, false, false, +/* VARBINARY*/ true, false, false, false, false, false, false, true, true, false, false, false, false, +/* SCALAR */ true, false, false, false, false, false, false, false, true, false, false, false, false, +/* DECIMAL */ true, false, false, true, false, false, false, false, true, true, false, false, false, +/* UUID */ true, false, false, false, false, false, false, false, false, false, true, false, false, +/* ARRAY */ true, false, false, false, false, false, false, false, false, false, false, true, false, +/* MAP */ true, false, false, false, false, false, false, false, false, false, false, false, true, }; bool @@ -183,3 +189,37 @@ field_type_by_name(const char *name, size_t len) return FIELD_TYPE_ANY; return field_type_MAX; } + +/* + * UUID field only accepts Msgpack-encoded UUID, + * 36 byte strings in format 0a998917-06f4-4b66-ad12-22c701dc8c91, + * 32 byte hex strings and 16 byte binstrings, containing valid + * UUIDs. + */ +bool +field_uuid_value_is_compatible(const char *data) +{ + const char *str; + uint32_t len; + struct tt_uuid uuid; + switch (mp_typeof(*data)) { + case MP_STR: + str = mp_decode_str(&data, &len); + return tt_uuid_from_lstring(str, len, &uuid) == 0; + case MP_BIN: + str = mp_decode_bin(&data, &len); + return uuid_unpack(&str, len, &uuid) != NULL; + case MP_EXT: + /* + * Only MP_UUID is allowed. All values are + * correct. + */ + return true; + default: + /* + * Must be checked earlier in + * field_mp_type_is_compatible() + */ + unreachable(); + } +} diff --git a/src/box/field_def.h b/src/box/field_def.h index 8e82369f1..94bfbf5f8 100644 --- a/src/box/field_def.h +++ b/src/box/field_def.h @@ -60,6 +60,7 @@ enum field_type { FIELD_TYPE_VARBINARY, FIELD_TYPE_SCALAR, FIELD_TYPE_DECIMAL, + FIELD_TYPE_UUID, FIELD_TYPE_ARRAY, FIELD_TYPE_MAP, field_type_MAX @@ -182,6 +183,21 @@ field_mp_type_is_compatible(enum field_type type, const char *data, } } +bool +field_uuid_value_is_compatible(const char *data); + +/** Check whether a value may be stored in field of given type. */ +static inline bool +field_value_is_compatible(enum field_type type, const char *data) +{ + switch (type) { + case FIELD_TYPE_UUID: + return field_uuid_value_is_compatible(data); + default: + return true; + } +} + static inline bool action_is_nullable(enum on_conflict_action nullable_action) { diff --git a/src/box/key_def.h b/src/box/key_def.h index f4d9e76f2..6a84f1885 100644 --- a/src/box/key_def.h +++ b/src/box/key_def.h @@ -528,7 +528,8 @@ 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, key, is_nullable))) { + if (unlikely(!field_mp_type_is_compatible(key_type, key, is_nullable) || + !field_value_is_compatible(key_type, key))) { 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 3f8a0ce24..aad69340d 100644 --- a/src/box/tuple_compare.cc +++ b/src/box/tuple_compare.cc @@ -35,6 +35,7 @@ #include #include "lib/core/decimal.h" #include "lib/core/mp_decimal.h" +#include "lib/core/mp_uuid.h" #include "lib/core/mp_extension_types.h" /* {{{ tuple_compare */ @@ -74,6 +75,7 @@ enum mp_class { MP_CLASS_NUMBER, MP_CLASS_STR, MP_CLASS_BIN, + MP_CLASS_UUID, MP_CLASS_ARRAY, MP_CLASS_MAP, mp_class_max, @@ -96,6 +98,7 @@ static enum mp_class mp_classes[] = { static enum mp_class mp_ext_classes[] = { /* .MP_UNKNOWN_EXTENSION = */ mp_class_max, /* unsupported */ /* .MP_DECIMAL = */ MP_CLASS_NUMBER, + /* .MP_UUID = */ MP_CLASS_UUID, }; static enum mp_class @@ -110,6 +113,7 @@ mp_extension_class(const char *data) assert(mp_typeof(*data) == MP_EXT); int8_t type; mp_decode_extl(&data, &type); + assert(type >= 0 && type < mp_extension_type_MAX); return mp_ext_classes[type]; } @@ -378,6 +382,61 @@ mp_compare_bin(const char *field_a, const char *field_b) return COMPARE_RESULT(size_a, size_b); } +static int +mp_compare_uuid_with_type(const char *field_a, enum mp_type type_a, + const char *field_b, enum mp_type type_b) +{ + struct tt_uuid uuid_a, uuid_b; + struct tt_uuid *ret; + const char *str; + uint32_t len; + int rc; + switch (type_a) { + case MP_STR: + str = mp_decode_str(&field_a, &len); + rc = tt_uuid_from_lstring(str, len, &uuid_a); + assert(rc == 0); + break; + case MP_BIN: + str = mp_decode_bin(&field_a, &len); + ret = uuid_unpack(&str, len, &uuid_a); + assert(ret != NULL); + break; + case MP_EXT: + ret = mp_decode_uuid(&field_a, &uuid_a); + assert(ret != NULL); + break; + default: + unreachable(); + } + switch (type_b) { + case MP_STR: + str = mp_decode_str(&field_b, &len); + rc = tt_uuid_from_lstring(str, len, &uuid_b); + assert(rc == 0); + break; + case MP_BIN: + str = mp_decode_bin(&field_b, &len); + ret = uuid_unpack(&str, len, &uuid_b); + assert(ret != NULL); + break; + case MP_EXT: + ret = mp_decode_uuid(&field_b, &uuid_b); + assert(ret != NULL); + break; + default: + unreachable(); + } + return tt_uuid_compare(&uuid_a, &uuid_b); +} + +static inline int +mp_compare_uuid(const char *field_a, const char *field_b) +{ + return mp_compare_uuid_with_type(field_a, mp_typeof(*field_a), + field_b, mp_typeof(*field_b)); +} + typedef int (*mp_compare_f)(const char *, const char *); static mp_compare_f mp_class_comparators[] = { /* .MP_CLASS_NIL = */ NULL, @@ -463,6 +522,8 @@ tuple_compare_field(const char *field_a, const char *field_b, mp_compare_scalar(field_a, field_b); case FIELD_TYPE_DECIMAL: return mp_compare_decimal(field_a, field_b); + case FIELD_TYPE_UUID: + return mp_compare_uuid(field_a, field_b); default: unreachable(); return 0; @@ -501,6 +562,9 @@ tuple_compare_field_with_type(const char *field_a, enum mp_type a_type, case FIELD_TYPE_DECIMAL: return mp_compare_number_with_type(field_a, a_type, field_b, b_type); + case FIELD_TYPE_UUID: + return mp_compare_uuid_with_type(field_a, a_type, + field_b, b_type); default: unreachable(); return 0; @@ -1578,6 +1642,21 @@ hint_decimal(decimal_t *dec) return hint_create(MP_CLASS_NUMBER, val); } +static inline hint_t +hint_uuid(struct tt_uuid *uuid) +{ + /* Simply take the first part of the UUID as hint. */ + uint64_t val = 0; + val |= uuid->time_low; + val <<= sizeof(uuid->time_mid) * CHAR_BIT; + val |= uuid->time_mid; + val <<= sizeof(uuid->time_hi_and_version) * CHAR_BIT; + val |= uuid->time_hi_and_version; + /* Make space for class representation. */ + val >>= HINT_CLASS_BITS; + return hint_create(MP_CLASS_UUID, val); +} + static inline uint64_t hint_str_raw(const char *s, uint32_t len) { @@ -1698,6 +1777,45 @@ field_hint_decimal(const char *field) } } +static inline hint_t +field_hint_uuid(const char *field) +{ + const char *str; + uint32_t len; + struct tt_uuid uuid; + struct tt_uuid *ret; + switch(mp_typeof(*field)) { + case MP_STR: + { + str = mp_decode_str(&field, &len); + int rc = tt_uuid_from_lstring(str, len, &uuid); + assert(rc == 0); + (void) rc; + break; + } + case MP_BIN: + str = mp_decode_bin(&field, &len); + /* + * Binary UUID representation shares the same + * format with UUID data after MP_EXT header. + */ + ret = uuid_unpack(&str, len, &uuid); + assert(ret != NULL); + break; + case MP_EXT: + /* + * Must be a UUID. Otherwise mp_decode_uuid() will + * return NULL. + */ + ret = mp_decode_uuid(&field, &uuid); + assert(ret != NULL); + break; + default: + unreachable(); + } + return hint_uuid(&uuid); +} + static inline hint_t field_hint_string(const char *field, struct coll *coll) { @@ -1782,6 +1900,8 @@ field_hint(const char *field, struct coll *coll) return field_hint_scalar(field, coll); case FIELD_TYPE_DECIMAL: return field_hint_decimal(field); + case FIELD_TYPE_UUID: + return field_hint_uuid(field); default: unreachable(); } @@ -1893,6 +2013,9 @@ key_def_set_hint_func(struct key_def *def) case FIELD_TYPE_DECIMAL: key_def_set_hint_func(def); break; + case FIELD_TYPE_UUID: + 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 312c96621..b8595d579 100644 --- a/src/box/tuple_format.c +++ b/src/box/tuple_format.c @@ -1170,7 +1170,8 @@ 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, entry->data, is_nullable) != 0) { + if (!field_mp_type_is_compatible(field->type, entry->data, is_nullable) != 0 || + !field_value_is_compatible(field->type, entry->data)) { diag_set(ClientError, ER_FIELD_TYPE, tuple_field_path(field), field_type_strs[field->type]); diff --git a/test/engine/ddl.result b/test/engine/ddl.result index 67b22ed9e..b7c04aafe 100644 --- a/test/engine/ddl.result +++ b/test/engine/ddl.result @@ -1037,6 +1037,9 @@ s:drop() decimal = require('decimal') --- ... +uuid = require('uuid') +--- +... -- Ensure that vinyl correctly process field count change. s = box.schema.space.create('test', {engine = engine, field_count = 2}) --- @@ -1098,13 +1101,16 @@ format[10] = {name = 'field10', type = 'map'} format[11] = {name = 'field11', type = 'decimal'} --- ... +format[12] = {name = 'field12', type = 'uuid'} +--- +... 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}, decimal.new(-11.11)} +t = s:replace{1, {2}, 3, '4', 5.5, -6, true, -8, {9, 9}, {val = 10}, decimal.new(-11.11), uuid.new()} --- ... inspector:cmd("setopt delimiter ';'") @@ -1171,6 +1177,11 @@ fail_format_change(3, 'decimal') --- - 'Tuple field 3 type does not match one required by operation: expected decimal' ... +-- unsigned --X--> uuid +fail_format_change(3, 'uuid') +--- +- 'Tuple field 3 type does not match one required by operation: expected uuid' +... -- string -----> any ok_format_change(4, 'any') --- @@ -1189,6 +1200,11 @@ fail_format_change(4, 'decimal') --- - 'Tuple field 4 type does not match one required by operation: expected decimal' ... +-- string --X--> uuid +fail_format_change(4, 'uuid') +--- +- 'Tuple field 4 type does not match one required by operation: expected uuid' +... -- number -----> any ok_format_change(5, 'any') --- @@ -1207,6 +1223,11 @@ fail_format_change(5, 'decimal') --- - 'Tuple field 5 type does not match one required by operation: expected decimal' ... +-- number --X--> uuid +fail_format_change(5, 'uuid') +--- +- 'Tuple field 5 type does not match one required by operation: expected uuid' +... -- integer -----> any ok_format_change(6, 'any') --- @@ -1229,6 +1250,11 @@ fail_format_change(6, 'decimal') --- - 'Tuple field 6 type does not match one required by operation: expected decimal' ... +-- integer --X--> uuid +fail_format_change(6, 'uuid') +--- +- 'Tuple field 6 type does not match one required by operation: expected uuid' +... -- boolean -----> any ok_format_change(7, 'any') --- @@ -1247,6 +1273,11 @@ fail_format_change(7, 'decimal') --- - 'Tuple field 7 type does not match one required by operation: expected decimal' ... +-- boolean --X--> uuid +fail_format_change(7, 'uuid') +--- +- 'Tuple field 7 type does not match one required by operation: expected uuid' +... -- scalar -----> any ok_format_change(8, 'any') --- @@ -1261,6 +1292,11 @@ fail_format_change(8, 'decimal') --- - 'Tuple field 8 type does not match one required by operation: expected decimal' ... +-- scalar --X--> uuid +fail_format_change(8, 'uuid') +--- +- 'Tuple field 8 type does not match one required by operation: expected uuid' +... -- array -----> any ok_format_change(9, 'any') --- @@ -1275,6 +1311,11 @@ fail_format_change(9, 'decimal') --- - 'Tuple field 9 type does not match one required by operation: expected decimal' ... +-- array --X--> uuid +fail_format_change(9, 'uuid') +--- +- 'Tuple field 9 type does not match one required by operation: expected uuid' +... -- map -----> any ok_format_change(10, 'any') --- @@ -1289,6 +1330,11 @@ fail_format_change(10, 'decimal') --- - 'Tuple field 10 type does not match one required by operation: expected decimal' ... +-- map --X--> uuid +fail_format_change(10, 'uuid') +--- +- 'Tuple field 10 type does not match one required by operation: expected uuid' +... -- decimal ----> any ok_format_change(11, 'any') --- @@ -1326,6 +1372,55 @@ fail_format_change(11, 'array') --- - 'Tuple field 11 type does not match one required by operation: expected array' ... +-- decimal --X--> uuid +fail_format_change(11, 'uuid') +--- +- 'Tuple field 11 type does not match one required by operation: expected uuid' +... +-- uuid ----> any +ok_format_change(12, 'any') +--- +... +-- uuid --X--> number +fail_format_change(12, 'number') +--- +- 'Tuple field 12 type does not match one required by operation: expected number' +... +-- uuid --X--> scalar +fail_format_change(12, 'scalar') +--- +- 'Tuple field 12 type does not match one required by operation: expected scalar' +... +-- uuid --X--> string +fail_format_change(12, 'string') +--- +- 'Tuple field 12 type does not match one required by operation: expected string' +... +-- uuid --X--> integer +fail_format_change(12, 'integer') +--- +- 'Tuple field 12 type does not match one required by operation: expected integer' +... +-- uuid --X--> unsigned +fail_format_change(12, 'unsigned') +--- +- 'Tuple field 12 type does not match one required by operation: expected unsigned' +... +-- uuid --X--> map +fail_format_change(12, 'map') +--- +- 'Tuple field 12 type does not match one required by operation: expected map' +... +-- uuid --X--> array +fail_format_change(12, 'array') +--- +- 'Tuple field 12 type does not match one required by operation: expected array' +... +-- uuid --X--> decimal +fail_format_change(12, 'decimal') +--- +- 'Tuple field 12 type does not match one required by operation: expected decimal' +... s:drop() --- ... diff --git a/test/engine/ddl.test.lua b/test/engine/ddl.test.lua index e761966d7..7d408807f 100644 --- a/test/engine/ddl.test.lua +++ b/test/engine/ddl.test.lua @@ -356,6 +356,7 @@ s:drop() -- decimal = require('decimal') +uuid = require('uuid') -- Ensure that vinyl correctly process field count change. s = box.schema.space.create('test', {engine = engine, field_count = 2}) @@ -379,10 +380,11 @@ format[8] = {name = 'field8', type = 'scalar'} format[9] = {name = 'field9', type = 'array'} format[10] = {name = 'field10', type = 'map'} format[11] = {name = 'field11', type = 'decimal'} +format[12] = {name = 'field12', type = 'uuid'} 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}, decimal.new(-11.11)} +t = s:replace{1, {2}, 3, '4', 5.5, -6, true, -8, {9, 9}, {val = 10}, decimal.new(-11.11), uuid.new()} inspector:cmd("setopt delimiter ';'") function fail_format_change(fieldno, new_type) @@ -421,6 +423,8 @@ ok_format_change(3, 'scalar') fail_format_change(3, 'map') -- unsigned --X--> decimal fail_format_change(3, 'decimal') +-- unsigned --X--> uuid +fail_format_change(3, 'uuid') -- string -----> any ok_format_change(4, 'any') @@ -430,6 +434,8 @@ ok_format_change(4, 'scalar') fail_format_change(4, 'boolean') -- string --X--> decimal fail_format_change(4, 'decimal') +-- string --X--> uuid +fail_format_change(4, 'uuid') -- number -----> any ok_format_change(5, 'any') @@ -439,6 +445,8 @@ ok_format_change(5, 'scalar') fail_format_change(5, 'integer') -- number --X--> decimal fail_format_change(5, 'decimal') +-- number --X--> uuid +fail_format_change(5, 'uuid') -- integer -----> any ok_format_change(6, 'any') @@ -450,6 +458,8 @@ ok_format_change(6, 'scalar') fail_format_change(6, 'unsigned') -- integer --X--> decimal fail_format_change(6, 'decimal') +-- integer --X--> uuid +fail_format_change(6, 'uuid') -- boolean -----> any ok_format_change(7, 'any') @@ -459,6 +469,8 @@ ok_format_change(7, 'scalar') fail_format_change(7, 'string') -- boolead --X--> decimal fail_format_change(7, 'decimal') +-- boolean --X--> uuid +fail_format_change(7, 'uuid') -- scalar -----> any ok_format_change(8, 'any') @@ -466,6 +478,8 @@ ok_format_change(8, 'any') fail_format_change(8, 'unsigned') -- scalar --X--> decimal fail_format_change(8, 'decimal') +-- scalar --X--> uuid +fail_format_change(8, 'uuid') -- array -----> any ok_format_change(9, 'any') @@ -473,6 +487,8 @@ ok_format_change(9, 'any') fail_format_change(9, 'scalar') -- arary --X--> decimal fail_format_change(9, 'decimal') +-- array --X--> uuid +fail_format_change(9, 'uuid') -- map -----> any ok_format_change(10, 'any') @@ -480,6 +496,8 @@ ok_format_change(10, 'any') fail_format_change(10, 'scalar') -- map --X--> decimal fail_format_change(10, 'decimal') +-- map --X--> uuid +fail_format_change(10, 'uuid') -- decimal ----> any ok_format_change(11, 'any') @@ -497,6 +515,28 @@ fail_format_change(11, 'unsigned') fail_format_change(11, 'map') -- decimal --X--> array fail_format_change(11, 'array') +-- decimal --X--> uuid +fail_format_change(11, 'uuid') + +-- uuid ----> any +ok_format_change(12, 'any') +-- uuid --X--> number +fail_format_change(12, 'number') +-- uuid --X--> scalar +fail_format_change(12, 'scalar') +-- uuid --X--> string +fail_format_change(12, 'string') +-- uuid --X--> integer +fail_format_change(12, 'integer') +-- uuid --X--> unsigned +fail_format_change(12, 'unsigned') +-- uuid --X--> map +fail_format_change(12, 'map') +-- uuid --X--> array +fail_format_change(12, 'array') +-- uuid --X--> decimal +fail_format_change(12, 'decimal') + s:drop() -- Check new fields adding. diff --git a/test/engine/gh-4268-uuid.result b/test/engine/gh-4268-uuid.result new file mode 100644 index 000000000..928204507 --- /dev/null +++ b/test/engine/gh-4268-uuid.result @@ -0,0 +1,58 @@ +-- test-run result file version 2 +env = require('test_run') + | --- + | ... +test_run = env.new() + | --- + | ... +engine = test_run:get_cfg('engine') + | --- + | ... + +uuid = require('uuid') + | --- + | ... +ffi = require('ffi') + | --- + | ... + +-- check uuid indices +_ = box.schema.space.create('test', {engine=engine}) + | --- + | ... +_ = box.space.test:create_index('pk', {parts={1,'uuid'}}) + | --- + | ... + +-- uuid indices support cdata uuids, 36- and 32- byte strings +-- and 16 byte binary strings +for i = 1,16 do\ + box.space.test:insert{uuid.new()}\ + box.space.test:insert{tostring(uuid.new())}\ + box.space.test:insert{tostring(uuid.new()):gsub('-', '')}\ +end + | --- + | ... + +a = box.space.test:select{} + | --- + | ... +err = nil + | --- + | ... +for i = 1, #a - 1 do\ + if tostring(a[i][1]):gsub('-', '') >= tostring(a[i+1][1]):gsub('-', '')\ + then err = {a[i][1], a[i+1][1]}\ + end\ +end + | --- + | ... + +err + | --- + | - null + | ... + +box.space.test:drop() + | --- + | ... diff --git a/test/engine/gh-4268-uuid.test.lua b/test/engine/gh-4268-uuid.test.lua new file mode 100644 index 000000000..66c928553 --- /dev/null +++ b/test/engine/gh-4268-uuid.test.lua @@ -0,0 +1,30 @@ +env = require('test_run') +test_run = env.new() +engine = test_run:get_cfg('engine') + +uuid = require('uuid') +ffi = require('ffi') + +-- check uuid indices +_ = box.schema.space.create('test', {engine=engine}) +_ = box.space.test:create_index('pk', {parts={1,'uuid'}}) + +-- uuid indices support cdata uuids, 36- and 32- byte strings +-- and 16 byte binary strings +for i = 1,16 do\ + box.space.test:insert{uuid.new()}\ + box.space.test:insert{tostring(uuid.new())}\ + box.space.test:insert{tostring(uuid.new()):gsub('-', '')}\ +end + +a = box.space.test:select{} +err = nil +for i = 1, #a - 1 do\ + if tostring(a[i][1]):gsub('-', '') >= tostring(a[i+1][1]):gsub('-', '')\ + then err = {a[i][1], a[i+1][1]}\ + end\ +end + +err + +box.space.test:drop() -- 2.21.1 (Apple Git-122.3)