[patches] [PATCH 2/2] schema: allow to define multiple types for one space field

Vladislav Shpilevoy v.shpilevoy at tarantool.org
Wed Feb 14 21:27:52 MSK 2018


On a single field two restrictions exist: tuple format and index part.

The proposal is to allow to use different types for different restrictions,
if these types are compatible. And for tuples validation use the most
strict type.

For example, one field can be number in space format, unsigned in one index,
integer in another index and scalar in a third index. Configuration like can be
allowed now, because all of these types can store unsigned. And unsigned is
used to validate tuples.

Besides, it allows incrementaly DDL. For example, if you want to change a
type of a field, which is used by several indexes, then it is impossible on
a non-empty space, even if a new type is less strict than the old one.

Closes #3008

Signed-off-by: Vladislav Shpilevoy <v.shpilevoy at tarantool.org>
---
 src/box/tuple_format.c  |  11 ++-
 test/box/alter.result   | 189 ++++++++++++++++++++++++++++++++++++++++++++++--
 test/box/alter.test.lua |  49 ++++++++++++-
 3 files changed, 240 insertions(+), 9 deletions(-)

diff --git a/src/box/tuple_format.c b/src/box/tuple_format.c
index 1d6499748..f0eca24bb 100644
--- a/src/box/tuple_format.c
+++ b/src/box/tuple_format.c
@@ -93,11 +93,16 @@ tuple_format_create(struct tuple_format *format, struct key_def * const *keys,
 			/*
 			 * Check that there are no conflicts
 			 * between index part types and space
-			 * fields.
+			 * fields. If a part type is compatible
+			 * with field's one, then the part type is
+			 * more strict and the part type must be
+			 * used in tuple_format.
 			 */
-			if (field->type == FIELD_TYPE_ANY) {
+			if (field_type1_contains_type2(field->type,
+						       part->type)) {
 				field->type = part->type;
-			} else if (field->type != part->type) {
+			} else if (! field_type1_contains_type2(part->type,
+								field->type)) {
 				const char *name;
 				int fieldno = part->fieldno + TUPLE_INDEX_BASE;
 				if (part->fieldno >= field_count) {
diff --git a/test/box/alter.result b/test/box/alter.result
index 0f49c44bf..ee7342daa 100644
--- a/test/box/alter.result
+++ b/test/box/alter.result
@@ -896,11 +896,6 @@ sk1 = s:create_index('sk1', { parts = { 2, 'unsigned' } })
 - error: Field 'field2' has type 'string' in space format, but type 'unsigned' in
     index definition
 ...
-sk2 = s:create_index('sk2', { parts = { 3, 'number' } })
----
-- error: Field 'field3' has type 'scalar' in space format, but type 'number' in index
-    definition
-...
 -- Check space format conflicting with index parts.
 sk3 = s:create_index('sk3', { parts = { 2, 'string' } })
 ---
@@ -1875,6 +1870,190 @@ box.space.test2:drop()
 ---
 ...
 --
+-- gh-3008: allow multiple types on the same field.
+--
+format = {}
+---
+...
+format[1] = {name = 'field1', type = 'unsigned'}
+---
+...
+format[2] = {name = 'field2', type = 'scalar'}
+---
+...
+format[3] = {name = 'field3', type = 'integer'}
+---
+...
+s = box.schema.create_space('test', {format = format})
+---
+...
+pk = s:create_index('pk')
+---
+...
+sk1 = s:create_index('sk1', {parts = {{2, 'number'}}})
+---
+...
+sk2 = s:create_index('sk2', {parts = {{2, 'integer'}}})
+---
+...
+sk3 = s:create_index('sk3', {parts = {{2, 'unsigned'}}})
+---
+...
+sk4 = s:create_index('sk4', {parts = {{3, 'number'}}})
+---
+...
+s:format()
+---
+- [{'name': 'field1', 'type': 'unsigned'}, {'name': 'field2', 'type': 'scalar'}, {
+    'name': 'field3', 'type': 'integer'}]
+...
+s:replace{1, '100', -20.2}
+---
+- error: 'Tuple field 2 type does not match one required by operation: expected unsigned'
+...
+s:replace{1, 100, -20.2}
+---
+- error: 'Tuple field 3 type does not match one required by operation: expected integer'
+...
+s:replace{1, 100, -20}
+---
+- [1, 100, -20]
+...
+s:replace{2, 50, 0}
+---
+- [2, 50, 0]
+...
+s:replace{3, 150, -60}
+---
+- [3, 150, -60]
+...
+s:replace{4, 0, 120}
+---
+- [4, 0, 120]
+...
+pk:select{}
+---
+- - [1, 100, -20]
+  - [2, 50, 0]
+  - [3, 150, -60]
+  - [4, 0, 120]
+...
+sk1:select{}
+---
+- - [4, 0, 120]
+  - [2, 50, 0]
+  - [1, 100, -20]
+  - [3, 150, -60]
+...
+sk2:select{}
+---
+- - [4, 0, 120]
+  - [2, 50, 0]
+  - [1, 100, -20]
+  - [3, 150, -60]
+...
+sk3:select{}
+---
+- - [4, 0, 120]
+  - [2, 50, 0]
+  - [1, 100, -20]
+  - [3, 150, -60]
+...
+sk4:select{}
+---
+- - [3, 150, -60]
+  - [1, 100, -20]
+  - [2, 50, 0]
+  - [4, 0, 120]
+...
+sk1:alter{parts = {{2, 'unsigned'}}}
+---
+...
+sk2:alter{parts = {{2, 'unsigned'}}}
+---
+...
+sk4:alter{parts = {{3, 'integer'}}}
+---
+...
+s:replace{1, 50.5, 1.5}
+---
+- error: 'Tuple field 2 type does not match one required by operation: expected unsigned'
+...
+s:replace{1, 50, 1.5}
+---
+- error: 'Tuple field 3 type does not match one required by operation: expected integer'
+...
+s:replace{5, 5, 5}
+---
+- [5, 5, 5]
+...
+sk1:select{}
+---
+- - [4, 0, 120]
+  - [5, 5, 5]
+  - [2, 50, 0]
+  - [1, 100, -20]
+  - [3, 150, -60]
+...
+sk2:select{}
+---
+- - [4, 0, 120]
+  - [5, 5, 5]
+  - [2, 50, 0]
+  - [1, 100, -20]
+  - [3, 150, -60]
+...
+sk3:select{}
+---
+- - [4, 0, 120]
+  - [5, 5, 5]
+  - [2, 50, 0]
+  - [1, 100, -20]
+  - [3, 150, -60]
+...
+sk4:select{}
+---
+- - [3, 150, -60]
+  - [1, 100, -20]
+  - [2, 50, 0]
+  - [5, 5, 5]
+  - [4, 0, 120]
+...
+sk1:drop()
+---
+...
+sk2:drop()
+---
+...
+sk3:drop()
+---
+...
+-- Remove 'unsigned' constraints from indexes, and 'scalar' now
+-- can be inserted in the second field.
+s:replace{1, true, 100}
+---
+- [1, true, 100]
+...
+s:select{}
+---
+- - [1, true, 100]
+  - [2, 50, 0]
+  - [3, 150, -60]
+  - [4, 0, 120]
+  - [5, 5, 5]
+...
+sk4:select{}
+---
+- - [3, 150, -60]
+  - [2, 50, 0]
+  - [5, 5, 5]
+  - [1, true, 100]
+  - [4, 0, 120]
+...
+s:drop()
+---
+...
+--
 -- gh-2914: Allow any space name which consists of printable characters
 --
 identifier = require("identifier")
diff --git a/test/box/alter.test.lua b/test/box/alter.test.lua
index 775407fe5..c3a835960 100644
--- a/test/box/alter.test.lua
+++ b/test/box/alter.test.lua
@@ -344,7 +344,6 @@ format = { { name='field1', type='unsigned' }, { name='field2', type='string' },
 s = box.schema.space.create('test', { format = format })
 pk = s:create_index('pk')
 sk1 = s:create_index('sk1', { parts = { 2, 'unsigned' } })
-sk2 = s:create_index('sk2', { parts = { 3, 'number' } })
 
 -- Check space format conflicting with index parts.
 sk3 = s:create_index('sk3', { parts = { 2, 'string' } })
@@ -724,6 +723,54 @@ box.space.test2:select{}
 box.space.test1:drop()
 box.space.test2:drop()
 
+--
+-- gh-3008: allow multiple types on the same field.
+--
+format = {}
+format[1] = {name = 'field1', type = 'unsigned'}
+format[2] = {name = 'field2', type = 'scalar'}
+format[3] = {name = 'field3', type = 'integer'}
+s = box.schema.create_space('test', {format = format})
+pk = s:create_index('pk')
+sk1 = s:create_index('sk1', {parts = {{2, 'number'}}})
+sk2 = s:create_index('sk2', {parts = {{2, 'integer'}}})
+sk3 = s:create_index('sk3', {parts = {{2, 'unsigned'}}})
+sk4 = s:create_index('sk4', {parts = {{3, 'number'}}})
+s:format()
+s:replace{1, '100', -20.2}
+s:replace{1, 100, -20.2}
+s:replace{1, 100, -20}
+s:replace{2, 50, 0}
+s:replace{3, 150, -60}
+s:replace{4, 0, 120}
+pk:select{}
+sk1:select{}
+sk2:select{}
+sk3:select{}
+sk4:select{}
+
+sk1:alter{parts = {{2, 'unsigned'}}}
+sk2:alter{parts = {{2, 'unsigned'}}}
+sk4:alter{parts = {{3, 'integer'}}}
+s:replace{1, 50.5, 1.5}
+s:replace{1, 50, 1.5}
+s:replace{5, 5, 5}
+sk1:select{}
+sk2:select{}
+sk3:select{}
+sk4:select{}
+
+sk1:drop()
+sk2:drop()
+sk3:drop()
+-- Remove 'unsigned' constraints from indexes, and 'scalar' now
+-- can be inserted in the second field.
+s:replace{1, true, 100}
+s:select{}
+sk4:select{}
+
+s:drop()
+
 --
 -- gh-2914: Allow any space name which consists of printable characters
 --
-- 
2.14.3 (Apple Git-98)




More information about the Tarantool-patches mailing list