Tarantool development patches archive
 help / color / mirror / Atom feed
* [Tarantool-patches] [PATCH v2] key_def: support composite types extraction
@ 2020-09-26 21:53 Ilya Kosarev
  2020-10-01 11:47 ` Alexander Turenko
  0 siblings, 1 reply; 3+ messages in thread
From: Ilya Kosarev @ 2020-09-26 21:53 UTC (permalink / raw)
  To: alexander.turenko, alyapunov; +Cc: tarantool-patches

key_def didn't support key definitions with array, map, varbinary & any
fields. Thus they couldn't be extracted with
key_def_object:extract_key(). Since the restriction existed due to
impossibility of such types comparison, this patch removes the
restriction for the fields extraction and only leaves it for
comparison.

Closes #4538
---
Branch: https://github.com/tarantool/tarantool/tree/i.kosarev/gh-4538-key_def-extract-arrays
Issue: https://github.com/tarantool/tarantool/issues/4538

@ChangeLog:
 * Composite types extraction is now supported in key_def (gh-4538).

Changes in v2:
 - type check is now only performed on key_def creation
 - more tests added

 src/box/key_def.h             | 10 ++++-
 src/box/lua/key_def.c         | 26 ++++++------
 src/box/tuple_compare.cc      | 11 +++++
 test/box-tap/key_def.test.lua | 76 ++++++++++++++++++++++++++++++++---
 4 files changed, 104 insertions(+), 19 deletions(-)

diff --git a/src/box/key_def.h b/src/box/key_def.h
index f4d9e76f2..afea32cc8 100644
--- a/src/box/key_def.h
+++ b/src/box/key_def.h
@@ -255,8 +255,14 @@ struct key_def {
 	 * path index key_def::multikey_path.
 	 * Valid when key_def->is_multikey is true,
 	 * undefined otherwise.
-	*/
+	 */
 	uint32_t multikey_fieldno;
+	/**
+	 * First part type not supported for comparison.
+	 * Valid if key_def does not support comparison
+	 * (key_def->tuple_compare* == NULL), undefined othewise.
+	 */
+	enum field_type unsupported_type;
 	/** The size of the 'parts' array. */
 	uint32_t part_count;
 	/** Description of parts of a multipart index. */
@@ -650,6 +656,7 @@ tuple_compare(struct tuple *tuple_a, hint_t tuple_a_hint,
 	      struct tuple *tuple_b, hint_t tuple_b_hint,
 	      struct key_def *key_def)
 {
+	assert(key_def->tuple_compare != NULL);
 	return key_def->tuple_compare(tuple_a, tuple_a_hint,
 				      tuple_b, tuple_b_hint, key_def);
 }
@@ -672,6 +679,7 @@ tuple_compare_with_key(struct tuple *tuple, hint_t tuple_hint,
 		       const char *key, uint32_t part_count,
 		       hint_t key_hint, struct key_def *key_def)
 {
+	assert(key_def->tuple_compare_with_key != NULL);
 	return key_def->tuple_compare_with_key(tuple, tuple_hint, key,
 					       part_count, key_hint, key_def);
 }
diff --git a/src/box/lua/key_def.c b/src/box/lua/key_def.c
index 1a99fab63..fb0b91665 100644
--- a/src/box/lua/key_def.c
+++ b/src/box/lua/key_def.c
@@ -127,21 +127,9 @@ luaT_key_def_set_part(struct lua_State *L, struct key_part_def *part,
 	const char *type_name = lua_tolstring(L, -1, &type_len);
 	lua_pop(L, 1);
 	part->type = field_type_by_name(type_name, type_len);
-	switch (part->type) {
-	case FIELD_TYPE_ANY:
-	case FIELD_TYPE_VARBINARY:
-	case FIELD_TYPE_ARRAY:
-	case FIELD_TYPE_MAP:
-		/* Tuple comparators don't support these types. */
-		diag_set(IllegalParams, "Unsupported field type: %s",
-			 type_name);
-		return -1;
-	case field_type_MAX:
+	if (part->type == field_type_MAX) {
 		diag_set(IllegalParams, "Unknown field type: %s", type_name);
 		return -1;
-	default:
-		/* Pass though. */
-		break;
 	}
 
 	/* Set part->is_nullable and part->nullable_action. */
@@ -316,6 +304,12 @@ lbox_key_def_compare(struct lua_State *L)
 				     "compare(tuple_a, tuple_b)");
 	}
 
+	if (key_def->tuple_compare == NULL) {
+		diag_set(IllegalParams, "Unsupported field type: %s",
+			 field_type_strs[key_def->unsupported_type]);
+		return luaT_error(L);
+	}
+
 	struct tuple *tuple_a, *tuple_b;
 	if ((tuple_a = luaT_key_def_check_tuple(L, key_def, 2)) == NULL)
 		return luaT_error(L);
@@ -349,6 +343,12 @@ lbox_key_def_compare_with_key(struct lua_State *L)
 				     "compare_with_key(tuple, key)");
 	}
 
+	if (key_def->tuple_compare_with_key == NULL) {
+		diag_set(IllegalParams, "Unsupported field type: %s",
+			 field_type_strs[key_def->unsupported_type]);
+		return luaT_error(L);
+	}
+
 	struct tuple *tuple = luaT_key_def_check_tuple(L, key_def, 2);
 	if (tuple == NULL)
 		return luaT_error(L);
diff --git a/src/box/tuple_compare.cc b/src/box/tuple_compare.cc
index d059c709f..762bc8019 100644
--- a/src/box/tuple_compare.cc
+++ b/src/box/tuple_compare.cc
@@ -2081,5 +2081,16 @@ key_def_set_compare_func(struct key_def *def)
 			key_def_set_compare_func_json<false, false>(def);
 		}
 	}
+	for (uint32_t i = 0; i < def->part_count; ++i) {
+		if (def->parts[i].type == FIELD_TYPE_ANY ||
+		    def->parts[i].type == FIELD_TYPE_ARRAY ||
+		    def->parts[i].type == FIELD_TYPE_MAP) {
+			/* Tuple comparators don't support these types. */
+			def->tuple_compare = NULL;
+			def->tuple_compare_with_key = NULL;
+			def->unsupported_type = def->parts[i].type;
+			break;
+		}
+	}
 	key_def_set_hint_func(def);
 }
diff --git a/test/box-tap/key_def.test.lua b/test/box-tap/key_def.test.lua
index 3a4aad687..43d5b0b37 100755
--- a/test/box-tap/key_def.test.lua
+++ b/test/box-tap/key_def.test.lua
@@ -215,7 +215,7 @@ end
 
 -- Case: extract_key().
 test:test('extract_key()', function(test)
-    test:plan(13)
+    test:plan(14)
 
     local key_def_a = key_def_lib.new({
         {type = 'unsigned', fieldno = 1},
@@ -245,6 +245,18 @@ test:test('extract_key()', function(test)
     }):extract_key({{a = {b = 'foo'}}}):totable()
     test:is_deeply(res, {'foo'}, 'JSON path (table argument)')
 
+    -- Composite types.
+    local tuple = {{{key = 'aa', data = 'aaaa'},
+                    {key = 'ab', data = 'aabb'},
+                    {key = 'bb', data = 'bbbb'}}, box.NULL,
+                    {a = 5, b = 6}}
+    local res = key_def_lib.new({
+        {type = 'array', fieldno = 1},
+        {type = 'map', fieldno = 2, is_nullable = true},
+        {type = 'any', fieldno = 3},
+    }):extract_key(tuple):totable()
+    test:is_deeply(res, tuple, 'Composite types')
+
     -- A key def has a **nullable** part with a field that is over
     -- a tuple size.
     --
@@ -335,7 +347,7 @@ end)
 
 -- Case: compare().
 test:test('compare()', function(test)
-    test:plan(8)
+    test:plan(9)
 
     local key_def_a = key_def_lib.new({
         {type = 'unsigned', fieldno = 1},
@@ -365,11 +377,19 @@ test:test('compare()', function(test)
             'case 3: less (table argument)')
     test:is(key_def_b:compare(tuple_a:totable(), tuple_c:totable()), 0,
             'case 4: equal (table argument)')
+
+    local cmp_err = 'Unsupported field type: array'
+    local key_def = key_def_lib.new({
+        {type = 'string', fieldno = 1},
+        {type = 'array', fieldno = 2, is_nullable = true},
+    })
+    local ok, err = pcall(key_def.compare, key_def, {'aa', {}}, {'bb', box.NULL})
+    test:is_deeply({ok, tostring(err)}, {false, cmp_err}, 'no composite comparison')
 end)
 
 -- Case: compare_with_key().
 test:test('compare_with_key()', function(test)
-    test:plan(2)
+    test:plan(3)
 
     local key_def_b = key_def_lib.new({
         {type = 'number', fieldno = 2},
@@ -382,11 +402,19 @@ test:test('compare_with_key()', function(test)
 
     local key = box.tuple.new({1, 22})
     test:is(key_def_b:compare_with_key(tuple_a, key), 0, 'tuple')
+
+    local cmp_err = 'Unsupported field type: map'
+    local key_def = key_def_lib.new({
+        {type = 'string', fieldno = 1},
+        {type = 'map', fieldno = 2},
+    })
+    local ok, err = pcall(key_def.compare_with_key, key_def, {'aa', {}}, {'bb', box.NULL})
+    test:is_deeply({ok, tostring(err)}, {false, cmp_err}, 'no composite comparison')
 end)
 
 -- Case: totable().
 test:test('totable()', function(test)
-    test:plan(2)
+    test:plan(3)
 
     local parts_a = {
         {type = 'unsigned', fieldno = 1}
@@ -395,14 +423,23 @@ test:test('totable()', function(test)
         {type = 'number', fieldno = 2},
         {type = 'number', fieldno = 3},
     }
+    local parts_composite = {
+        {type = 'number', fieldno = 2},
+        {type = 'array', fieldno = 3},
+        {type = 'map', fieldno = 4, is_nullable = true},
+    }
     local key_def_a = key_def_lib.new(parts_a)
     local key_def_b = key_def_lib.new(parts_b)
+    local key_def_composite = key_def_lib.new(parts_composite)
 
     local exp = set_key_part_defaults(parts_a)
     test:is_deeply(key_def_a:totable(), exp, 'case 1')
 
     local exp = set_key_part_defaults(parts_b)
     test:is_deeply(key_def_b:totable(), exp, 'case 2')
+
+    local exp = set_key_part_defaults(parts_composite)
+    test:is_deeply(key_def_composite:totable(), exp, 'composite case')
 end)
 
 -- Case: __serialize().
@@ -447,7 +484,7 @@ end)
 
 -- Case: merge().
 test:test('merge()', function(test)
-    test:plan(6)
+    test:plan(8)
 
     local key_def_a = key_def_lib.new({
         {type = 'unsigned', fieldno = 1},
@@ -488,6 +525,35 @@ test:test('merge()', function(test)
         'case 3: verify with :totable()')
     test:is_deeply(key_def_cb:extract_key(tuple_a):totable(),
         {1, 1, box.NULL, 22}, 'case 3: verify with :extract_key()')
+
+    local parts_unsigned = {
+        {type = 'unsigned', fieldno = 1, is_nullable = false},
+    }
+    local key_def_unsigned = key_def_lib.new(parts_unsigned)
+    local key_def_string = key_def_lib.new({
+        {type = 'string', fieldno = 1},
+    })
+    local key_def_array = key_def_lib.new({
+        {type = 'array', fieldno = 1},
+        {type = 'unsigned', fieldno = 2},
+    })
+    local key_def_map = key_def_lib.new({
+        {type = 'map', fieldno = 3, is_nullable = true},
+        {type = 'scalar', fieldno = 2},
+    })
+
+    local key_def_unsigned_string = key_def_unsigned:merge(key_def_string)
+    test:is_deeply(key_def_unsigned_string:totable(), parts_unsigned,
+        'in case of conflict we just get the field from the first key_def')
+
+    local key_def_array_map = key_def_array:merge(key_def_map)
+    local exp_parts = {
+        {type = 'array', fieldno = 1, is_nullable = false},
+        {type = 'unsigned', fieldno = 2, is_nullable = false},
+        {type = 'map', fieldno = 3, is_nullable = true},
+    }
+    test:is_deeply(key_def_array_map:totable(), exp_parts,
+        'composite case')
 end)
 
 os.exit(test:check() and 0 or 1)
-- 
2.17.1

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2020-10-01 18:35 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-26 21:53 [Tarantool-patches] [PATCH v2] key_def: support composite types extraction Ilya Kosarev
2020-10-01 11:47 ` Alexander Turenko
2020-10-01 18:35   ` Ilya Kosarev

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox