[PATCH 3/7] Make tuple_extract_key support multikey indexes

Vladimir Davydov vdavydov.dev at gmail.com
Wed May 8 20:22:35 MSK 2019


Add multikey_idx argument to tuple_extract_key and forward it to
tuple_field_by_part in the method implementation. For unikey indexes
pass -1. We need this to support multikey indexes in Vinyl.

We could of course introduce a separate set of methods for multikey
indexes (something like tuple_extract_key_multikey), but that would
look cumbersome and hardly result in any performance benefits, because
passing -1 to a relatively cold function, such as key extractor, isn't
a big deal. Besides, passing multikey_idx unconditionally is consistent
with tuple_compare.
---
 src/box/index.cc             |  2 +-
 src/box/key_def.h            | 18 +++++++++++----
 src/box/lua/key_def.c        |  2 +-
 src/box/memtx_space.c        |  2 +-
 src/box/request.c            |  5 ++--
 src/box/space.c              |  2 +-
 src/box/sql.c                | 10 ++++----
 src/box/tuple_extract_key.cc | 54 ++++++++++++++++++++++++++++++--------------
 src/box/vinyl.c              | 12 +++++-----
 src/box/vy_run.c             |  6 ++---
 src/box/vy_stmt.c            | 17 +++++++-------
 src/box/vy_stmt.h            |  6 ++---
 12 files changed, 83 insertions(+), 53 deletions(-)

diff --git a/src/box/index.cc b/src/box/index.cc
index 2817d076..39b0ee46 100644
--- a/src/box/index.cc
+++ b/src/box/index.cc
@@ -156,7 +156,7 @@ box_tuple_extract_key(box_tuple_t *tuple, uint32_t space_id, uint32_t index_id,
 	struct index *index = index_find(space, index_id);
 	if (index == NULL)
 		return NULL;
-	return tuple_extract_key(tuple, index->def->key_def, key_size);
+	return tuple_extract_key(tuple, index->def->key_def, -1, key_size);
 }
 
 static inline int
diff --git a/src/box/key_def.h b/src/box/key_def.h
index 5f65f501..8b94b3b6 100644
--- a/src/box/key_def.h
+++ b/src/box/key_def.h
@@ -145,11 +145,13 @@ typedef int (*tuple_compare_t)(struct tuple *tuple_a,
 /** @copydoc tuple_extract_key() */
 typedef char *(*tuple_extract_key_t)(struct tuple *tuple,
 				     struct key_def *key_def,
+				     int multikey_idx,
 				     uint32_t *key_size);
 /** @copydoc tuple_extract_key_raw() */
 typedef char *(*tuple_extract_key_raw_t)(const char *data,
 					 const char *data_end,
 					 struct key_def *key_def,
+					 int multikey_idx,
 					 uint32_t *key_size);
 /** @copydoc tuple_hash() */
 typedef uint32_t (*tuple_hash_t)(struct tuple *tuple,
@@ -531,10 +533,12 @@ key_part_cmp(const struct key_part *parts1, uint32_t part_count1,
  * Check if a key of @a tuple contains NULL.
  * @param tuple Tuple to check.
  * @param def Key def to check by.
+ * @param multikey_idx Multikey index hint.
  * @retval Does the key contain NULL or not?
  */
 bool
-tuple_key_contains_null(struct tuple *tuple, struct key_def *def);
+tuple_key_contains_null(struct tuple *tuple, struct key_def *def,
+			int multikey_idx);
 
 /**
  * Check that tuple fields match with given key definition
@@ -554,6 +558,7 @@ tuple_validate_key_parts(struct key_def *key_def, struct tuple *tuple);
  * has O(n) complexity, where n is the number of key parts.
  * @param tuple - tuple from which need to extract key
  * @param key_def - definition of key that need to extract
+ * @param multikey_idx - multikey index hint
  * @param key_size - here will be size of extracted key
  *
  * @retval not NULL Success
@@ -561,9 +566,10 @@ tuple_validate_key_parts(struct key_def *key_def, struct tuple *tuple);
  */
 static inline char *
 tuple_extract_key(struct tuple *tuple, struct key_def *key_def,
-		  uint32_t *key_size)
+		  int multikey_idx, uint32_t *key_size)
 {
-	return key_def->tuple_extract_key(tuple, key_def, key_size);
+	return key_def->tuple_extract_key(tuple, key_def, multikey_idx,
+					  key_size);
 }
 
 /**
@@ -574,6 +580,7 @@ tuple_extract_key(struct tuple *tuple, struct key_def *key_def,
  * @param data - msgpuck data from which need to extract key
  * @param data_end - pointer at the end of data
  * @param key_def - definition of key that need to extract
+ * @param multikey_idx - multikey index hint
  * @param key_size - here will be size of extracted key
  *
  * @retval not NULL Success
@@ -581,10 +588,11 @@ tuple_extract_key(struct tuple *tuple, struct key_def *key_def,
  */
 static inline char *
 tuple_extract_key_raw(const char *data, const char *data_end,
-		      struct key_def *key_def, uint32_t *key_size)
+		      struct key_def *key_def, int multikey_idx,
+		      uint32_t *key_size)
 {
 	return key_def->tuple_extract_key_raw(data, data_end, key_def,
-					      key_size);
+					      multikey_idx, key_size);
 }
 
 /**
diff --git a/src/box/lua/key_def.c b/src/box/lua/key_def.c
index 72bb05c4..28d40478 100644
--- a/src/box/lua/key_def.c
+++ b/src/box/lua/key_def.c
@@ -265,7 +265,7 @@ lbox_key_def_extract_key(struct lua_State *L)
 	struct region *region = &fiber()->gc;
 	size_t region_svp = region_used(region);
 	uint32_t key_size;
-	char *key = tuple_extract_key(tuple, key_def, &key_size);
+	char *key = tuple_extract_key(tuple, key_def, -1, &key_size);
 	tuple_unref(tuple);
 	if (key == NULL)
 		return luaT_error(L);
diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c
index 4d5e7991..265bf923 100644
--- a/src/box/memtx_space.c
+++ b/src/box/memtx_space.c
@@ -445,7 +445,7 @@ memtx_space_execute_upsert(struct space *space, struct txn *txn,
 	/* Extract the primary key from tuple. */
 	const char *key = tuple_extract_key_raw(request->tuple,
 						request->tuple_end,
-						index->def->key_def, NULL);
+						index->def->key_def, -1, NULL);
 	if (key == NULL)
 		return -1;
 	/* Cut array header */
diff --git a/src/box/request.c b/src/box/request.c
index 9c684af7..8b84260a 100644
--- a/src/box/request.c
+++ b/src/box/request.c
@@ -92,7 +92,7 @@ request_create_from_tuple(struct request *request, struct space *space,
 		uint32_t size, key_size;
 		const char *data = tuple_data_range(old_tuple, &size);
 		request->key = tuple_extract_key_raw(data, data + size,
-				space->index[0]->def->key_def, &key_size);
+				space->index[0]->def->key_def, -1, &key_size);
 		if (request->key == NULL)
 			return -1;
 		request->key_end = request->key + key_size;
@@ -125,7 +125,8 @@ request_rebind_to_primary_key(struct request *request, struct space *space,
 	struct index *pk = space_index(space, 0);
 	assert(pk != NULL);
 	uint32_t key_len;
-	char *key = tuple_extract_key(found_tuple, pk->def->key_def, &key_len);
+	char *key = tuple_extract_key(found_tuple, pk->def->key_def, -1,
+				      &key_len);
 	assert(key != NULL);
 	request->key = key;
 	request->key_end = key + key_len;
diff --git a/src/box/space.c b/src/box/space.c
index edfc5d60..9a266604 100644
--- a/src/box/space.c
+++ b/src/box/space.c
@@ -320,7 +320,7 @@ space_before_replace(struct space *space, struct txn *txn,
 			break;
 		index = pk;
 		key = tuple_extract_key_raw(request->tuple, request->tuple_end,
-					    index->def->key_def, NULL);
+					    index->def->key_def, -1, NULL);
 		if (key == NULL)
 			return -1;
 		part_count = mp_decode_array(&key);
diff --git a/src/box/sql.c b/src/box/sql.c
index 7a20d86e..c3f404ee 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -480,7 +480,7 @@ int tarantoolsqlEphemeralDelete(BtCursor *pCur)
 	char *key;
 	uint32_t key_size;
 	key = tuple_extract_key(pCur->last_tuple,
-				pCur->iter->index->def->key_def,
+				pCur->iter->index->def->key_def, -1,
 				&key_size);
 	if (key == NULL)
 		return SQL_TARANTOOL_DELETE_FAIL;
@@ -506,7 +506,7 @@ int tarantoolsqlDelete(BtCursor *pCur, u8 flags)
 	int rc;
 
 	key = tuple_extract_key(pCur->last_tuple,
-				pCur->iter->index->def->key_def,
+				pCur->iter->index->def->key_def, -1,
 				&key_size);
 	if (key == NULL)
 		return SQL_TARANTOOL_DELETE_FAIL;
@@ -561,7 +561,7 @@ int tarantoolsqlEphemeralClearTable(BtCursor *pCur)
 	uint32_t  key_size;
 
 	while (iterator_next(it, &tuple) == 0 && tuple != NULL) {
-		key = tuple_extract_key(tuple, it->index->def->key_def,
+		key = tuple_extract_key(tuple, it->index->def->key_def, -1,
 					&key_size);
 		if (space_ephemeral_delete(pCur->space, key) != 0) {
 			iterator_delete(it);
@@ -594,7 +594,7 @@ int tarantoolsqlClearTable(struct space *space, uint32_t *tuple_count)
 	if (iter == NULL)
 		return SQL_TARANTOOL_ITERATOR_FAIL;
 	while (iterator_next(iter, &tuple) == 0 && tuple != NULL) {
-		request.key = tuple_extract_key(tuple, pk->def->key_def,
+		request.key = tuple_extract_key(tuple, pk->def->key_def, -1,
 						&key_size);
 		request.key_end = request.key + key_size;
 		rc = box_process_rw(&request, space, &unused);
@@ -801,7 +801,7 @@ out:
 #ifndef NDEBUG
 	/* Sanity check. */
 	original_size = region_used(&fiber()->gc);
-	key = tuple_extract_key(tuple, key_def, &key_size);
+	key = tuple_extract_key(tuple, key_def, -1, &key_size);
 	if (key != NULL) {
 		int new_rc = sqlVdbeRecordCompareMsgpack(key, unpacked);
 		region_truncate(&fiber()->gc, original_size);
diff --git a/src/box/tuple_extract_key.cc b/src/box/tuple_extract_key.cc
index 7868378d..15e43aae 100644
--- a/src/box/tuple_extract_key.cc
+++ b/src/box/tuple_extract_key.cc
@@ -37,8 +37,10 @@ key_def_contains_sequential_parts(const struct key_def *def)
 template <bool has_optional_parts>
 static char *
 tuple_extract_key_sequential_raw(const char *data, const char *data_end,
-				 struct key_def *key_def, uint32_t *key_size)
+				 struct key_def *key_def, int multikey_idx,
+				 uint32_t *key_size)
 {
+	(void)multikey_idx;
 	assert(!has_optional_parts || key_def->is_nullable);
 	assert(key_def_is_sequential(key_def));
 	assert(has_optional_parts == key_def->has_optional_parts);
@@ -87,7 +89,7 @@ tuple_extract_key_sequential_raw(const char *data, const char *data_end,
 template <bool has_optional_parts>
 static inline char *
 tuple_extract_key_sequential(struct tuple *tuple, struct key_def *key_def,
-			     uint32_t *key_size)
+			     int multikey_idx, uint32_t *key_size)
 {
 	assert(key_def_is_sequential(key_def));
 	assert(!has_optional_parts || key_def->is_nullable);
@@ -97,6 +99,7 @@ tuple_extract_key_sequential(struct tuple *tuple, struct key_def *key_def,
 	return tuple_extract_key_sequential_raw<has_optional_parts>(data,
 								    data_end,
 								    key_def,
+								    multikey_idx,
 								    key_size);
 }
 
@@ -105,17 +108,18 @@ tuple_extract_key_sequential(struct tuple *tuple, struct key_def *key_def,
  * @copydoc tuple_extract_key()
  */
 template <bool contains_sequential_parts, bool has_optional_parts,
-	  bool has_json_paths>
+	  bool has_json_paths, bool is_multikey>
 static char *
 tuple_extract_key_slowpath(struct tuple *tuple, struct key_def *key_def,
-			   uint32_t *key_size)
+			   int multikey_idx, uint32_t *key_size)
 {
 	assert(has_json_paths == key_def->has_json_paths);
 	assert(!has_optional_parts || key_def->is_nullable);
 	assert(has_optional_parts == key_def->has_optional_parts);
 	assert(contains_sequential_parts ==
 	       key_def_contains_sequential_parts(key_def));
-	assert(!key_def_is_multikey(key_def));
+	assert(is_multikey == key_def_is_multikey(key_def));
+	assert(!key_def_is_multikey(key_def) || multikey_idx >= 0);
 	assert(mp_sizeof_nil() == 1);
 	const char *data = tuple_data(tuple);
 	uint32_t part_count = key_def->part_count;
@@ -130,9 +134,13 @@ tuple_extract_key_slowpath(struct tuple *tuple, struct key_def *key_def,
 		if (!has_json_paths) {
 			field = tuple_field_raw(format, data, field_map,
 						key_def->parts[i].fieldno);
-		} else {
+		} else if (!is_multikey) {
 			field = tuple_field_raw_by_part(format, data, field_map,
 							&key_def->parts[i], -1);
+		} else {
+			field = tuple_field_raw_by_part(format, data, field_map,
+							&key_def->parts[i],
+							multikey_idx);
 		}
 		if (has_optional_parts && field == NULL) {
 			bsize += mp_sizeof_nil();
@@ -177,9 +185,13 @@ tuple_extract_key_slowpath(struct tuple *tuple, struct key_def *key_def,
 		if (!has_json_paths) {
 			field = tuple_field_raw(format, data, field_map,
 						key_def->parts[i].fieldno);
-		} else {
+		} else if (!is_multikey) {
 			field = tuple_field_raw_by_part(format, data, field_map,
 							&key_def->parts[i], -1);
+		} else {
+			field = tuple_field_raw_by_part(format, data, field_map,
+							&key_def->parts[i],
+							multikey_idx);
 		}
 		if (has_optional_parts && field == NULL) {
 			key_buf = mp_encode_nil(key_buf);
@@ -230,12 +242,13 @@ tuple_extract_key_slowpath(struct tuple *tuple, struct key_def *key_def,
 template <bool has_optional_parts, bool has_json_paths>
 static char *
 tuple_extract_key_slowpath_raw(const char *data, const char *data_end,
-			       struct key_def *key_def, uint32_t *key_size)
+			       struct key_def *key_def, int multikey_idx,
+			       uint32_t *key_size)
 {
 	assert(has_json_paths == key_def->has_json_paths);
 	assert(!has_optional_parts || key_def->is_nullable);
 	assert(has_optional_parts == key_def->has_optional_parts);
-	assert(!key_def_is_multikey(key_def));
+	assert(!key_def_is_multikey(key_def) || multikey_idx >= 0);
 	assert(mp_sizeof_nil() == 1);
 	/* allocate buffer with maximal possible size */
 	char *key = (char *) region_alloc(&fiber()->gc, data_end - data);
@@ -311,8 +324,8 @@ tuple_extract_key_slowpath_raw(const char *data, const char *data_end,
 		const char *src = field;
 		const char *src_end = field_end;
 		if (has_json_paths && part->path != NULL) {
-			if (tuple_go_to_path(&src, part->path,
-					     part->path_len, -1) != 0) {
+			if (tuple_go_to_path(&src, part->path, part->path_len,
+					     multikey_idx) != 0) {
 				/*
 				 * The path must be correct as
 				 * it has already been validated
@@ -361,7 +374,7 @@ key_def_set_extract_func_plain(struct key_def *def)
 	} else {
 		def->tuple_extract_key = tuple_extract_key_slowpath
 					<contains_sequential_parts,
-					 has_optional_parts, false>;
+					 has_optional_parts, false, false>;
 		def->tuple_extract_key_raw = tuple_extract_key_slowpath_raw
 					<has_optional_parts, false>;
 	}
@@ -372,9 +385,15 @@ static void
 key_def_set_extract_func_json(struct key_def *def)
 {
 	assert(def->has_json_paths);
-	def->tuple_extract_key = tuple_extract_key_slowpath
+	if (key_def_is_multikey(def)) {
+		def->tuple_extract_key = tuple_extract_key_slowpath
 					<contains_sequential_parts,
-					 has_optional_parts, true>;
+					 has_optional_parts, true, true>;
+	} else {
+		def->tuple_extract_key = tuple_extract_key_slowpath
+					<contains_sequential_parts,
+					 has_optional_parts, true, false>;
+	}
 	def->tuple_extract_key_raw = tuple_extract_key_slowpath_raw
 					<has_optional_parts, true>;
 }
@@ -411,16 +430,17 @@ key_def_set_extract_func(struct key_def *key_def)
 }
 
 bool
-tuple_key_contains_null(struct tuple *tuple, struct key_def *def)
+tuple_key_contains_null(struct tuple *tuple, struct key_def *def,
+			int multikey_idx)
 {
-	assert(!key_def_is_multikey(def));
 	struct tuple_format *format = tuple_format(tuple);
 	const char *data = tuple_data(tuple);
 	const uint32_t *field_map = tuple_field_map(tuple);
 	for (struct key_part *part = def->parts, *end = part + def->part_count;
 	     part < end; ++part) {
 		const char *field = tuple_field_raw_by_part(format, data,
-							    field_map, part, -1);
+							    field_map, part,
+							    multikey_idx);
 		if (field == NULL || mp_typeof(*field) == MP_NIL)
 			return true;
 	}
diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index 6ffbecd9..eddbdbed 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -1344,7 +1344,7 @@ vy_get_by_secondary_tuple(struct vy_lsm *lsm, struct vy_tx *tx,
 	struct vy_entry key;
 	if (vy_stmt_is_key(entry.stmt)) {
 		key.stmt = vy_stmt_extract_key(entry.stmt, lsm->pk_in_cmp_def,
-					       lsm->env->key_format);
+					       lsm->env->key_format, -1);
 		if (key.stmt == NULL)
 			return -1;
 	} else {
@@ -1601,10 +1601,10 @@ vy_check_is_unique_secondary(struct vy_tx *tx, const struct vy_read_view **rv,
 	if (!lsm->check_is_unique)
 		return 0;
 	if (lsm->key_def->is_nullable &&
-	    tuple_key_contains_null(stmt, lsm->key_def))
+	    tuple_key_contains_null(stmt, lsm->key_def, -1))
 		return 0;
 	struct tuple *key = vy_stmt_extract_key(stmt, lsm->key_def,
-						lsm->env->key_format);
+						lsm->env->key_format, -1);
 	if (key == NULL)
 		return -1;
 	struct tuple *found;
@@ -2149,7 +2149,7 @@ vy_upsert(struct vy_env *env, struct vy_tx *tx, struct txn_stmt *stmt,
 	 */
 	/* Find the old tuple using the primary key. */
 	struct tuple *key = vy_stmt_extract_key_raw(tuple, tuple_end,
-					pk->key_def, pk->env->key_format);
+					pk->key_def, pk->env->key_format, -1);
 	if (key == NULL)
 		return -1;
 	int rc = vy_get(pk, tx, vy_tx_read_view(tx), key, &stmt->old_tuple);
@@ -4064,7 +4064,7 @@ vy_build_on_replace(struct trigger *trigger, void *event)
 	/* Forward the statement to the new LSM tree. */
 	if (stmt->old_tuple != NULL) {
 		struct tuple *delete = vy_stmt_extract_key(stmt->old_tuple,
-					lsm->cmp_def, lsm->env->key_format);
+					lsm->cmp_def, lsm->env->key_format, -1);
 		if (delete == NULL)
 			goto err;
 		vy_stmt_set_type(delete, IPROTO_DELETE);
@@ -4219,7 +4219,7 @@ vy_build_recover_stmt(struct vy_lsm *lsm, struct vy_lsm *pk,
 	struct tuple *old_tuple = old.stmt;
 	if (old_tuple != NULL) {
 		delete = vy_stmt_extract_key(old_tuple, lsm->cmp_def,
-					     lsm->env->key_format);
+					     lsm->env->key_format, -1);
 		if (delete == NULL)
 			return -1;
 		vy_stmt_set_type(delete, IPROTO_DELETE);
diff --git a/src/box/vy_run.c b/src/box/vy_run.c
index b15a96e9..409c3d96 100644
--- a/src/box/vy_run.c
+++ b/src/box/vy_run.c
@@ -2167,7 +2167,7 @@ vy_run_writer_start_page(struct vy_run_writer *writer,
 	const char *key = vy_stmt_is_key(first_entry.stmt) ?
 			  tuple_data(first_entry.stmt) :
 			  tuple_extract_key(first_entry.stmt,
-					    writer->cmp_def, NULL);
+					    writer->cmp_def, -1, NULL);
 	if (key == NULL)
 		return -1;
 	if (run->info.page_count == 0) {
@@ -2321,7 +2321,7 @@ vy_run_writer_commit(struct vy_run_writer *writer)
 	const char *key = vy_stmt_is_key(writer->last.stmt) ?
 		          tuple_data(writer->last.stmt) :
 			  tuple_extract_key(writer->last.stmt,
-					    writer->cmp_def, NULL);
+					    writer->cmp_def, -1, NULL);
 	if (key == NULL)
 		goto out;
 
@@ -2431,7 +2431,7 @@ vy_run_rebuild_index(struct vy_run *run, const char *dir,
 				goto close_err;
 			}
 			key = vy_stmt_is_key(tuple) ? tuple_data(tuple) :
-			      tuple_extract_key(tuple, cmp_def, NULL);
+			      tuple_extract_key(tuple, cmp_def, -1, NULL);
 			if (prev_tuple != NULL)
 				tuple_unref(prev_tuple);
 			prev_tuple = tuple;
diff --git a/src/box/vy_stmt.c b/src/box/vy_stmt.c
index c8088d34..847b6196 100644
--- a/src/box/vy_stmt.c
+++ b/src/box/vy_stmt.c
@@ -489,11 +489,12 @@ out:
 
 struct tuple *
 vy_stmt_extract_key(struct tuple *stmt, struct key_def *key_def,
-		    struct tuple_format *format)
+		    struct tuple_format *format, int multikey_idx)
 {
 	struct region *region = &fiber()->gc;
 	size_t region_svp = region_used(region);
-	const char *key_raw = tuple_extract_key(stmt, key_def, NULL);
+	const char *key_raw = tuple_extract_key(stmt, key_def,
+						multikey_idx, NULL);
 	if (key_raw == NULL)
 		return NULL;
 	uint32_t part_count = mp_decode_array(&key_raw);
@@ -506,13 +507,13 @@ vy_stmt_extract_key(struct tuple *stmt, struct key_def *key_def,
 
 struct tuple *
 vy_stmt_extract_key_raw(const char *data, const char *data_end,
-			struct key_def *key_def,
-			struct tuple_format *format)
+			struct key_def *key_def, struct tuple_format *format,
+			int multikey_idx)
 {
 	struct region *region = &fiber()->gc;
 	size_t region_svp = region_used(region);
-	const char *key_raw = tuple_extract_key_raw(data, data_end,
-						    key_def, NULL);
+	const char *key_raw = tuple_extract_key_raw(data, data_end, key_def,
+						    multikey_idx, NULL);
 	if (key_raw == NULL)
 		return NULL;
 	uint32_t part_count = mp_decode_array(&key_raw);
@@ -622,7 +623,7 @@ vy_stmt_encode_primary(struct tuple *value, struct key_def *key_def,
 	case IPROTO_DELETE:
 		extracted = vy_stmt_is_key(value) ?
 			    tuple_data_range(value, &size) :
-			    tuple_extract_key(value, key_def, &size);
+			    tuple_extract_key(value, key_def, -1, &size);
 		if (extracted == NULL)
 			return -1;
 		request.key = extracted;
@@ -666,7 +667,7 @@ vy_stmt_encode_secondary(struct tuple *value, struct key_def *cmp_def,
 	uint32_t size;
 	const char *extracted = vy_stmt_is_key(value) ?
 				tuple_data_range(value, &size) :
-				tuple_extract_key(value, cmp_def, &size);
+				tuple_extract_key(value, cmp_def, -1, &size);
 	if (extracted == NULL)
 		return -1;
 	if (type == IPROTO_REPLACE || type == IPROTO_INSERT) {
diff --git a/src/box/vy_stmt.h b/src/box/vy_stmt.h
index 49fa1602..929e537a 100644
--- a/src/box/vy_stmt.h
+++ b/src/box/vy_stmt.h
@@ -606,7 +606,7 @@ vy_key_from_msgpack(struct tuple_format *format, const char *key)
  */
 struct tuple *
 vy_stmt_extract_key(struct tuple *stmt, struct key_def *key_def,
-		    struct tuple_format *format);
+		    struct tuple_format *format, int multikey_idx);
 
 /**
  * Extract the key from msgpack by the given key definition
@@ -615,8 +615,8 @@ vy_stmt_extract_key(struct tuple *stmt, struct key_def *key_def,
  */
 struct tuple *
 vy_stmt_extract_key_raw(const char *data, const char *data_end,
-			struct key_def *key_def,
-			struct tuple_format *format);
+			struct key_def *key_def, struct tuple_format *format,
+			int multikey_idx);
 
 /**
  * Add a statement hash to a bloom filter builder.
-- 
2.11.0




More information about the Tarantool-patches mailing list