[Tarantool-patches] [PATCH 4/4] box: introduce indices by UUID

Serge Petrenko sergepetrenko at tarantool.org
Sat Apr 4 02:02:52 MSK 2020


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 <math.h>
 #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<FIELD_TYPE_DECIMAL>(def);
 		break;
+	case FIELD_TYPE_UUID:
+		key_def_set_hint_func<FIELD_TYPE_UUID>(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)



More information about the Tarantool-patches mailing list