From 07b7062b4891151de09673aca21ba58c64fbf777 Mon Sep 17 00:00:00 2001
From: Aleksandr Lyapunov <alyapunov@tarantool.org>
Date: Tue, 13 Apr 2021 13:44:58 +0300
Subject: [PATCH] box: add successor argument in replace function

Add an argument which will receive successor of the inserted tuple.

Part of #5628
---
 src/box/index.cc       |  8 +++++---
 src/box/index.h        | 22 +++++++++++++++++-----
 src/box/memtx_bitset.c |  5 ++++-
 src/box/memtx_engine.c |  2 +-
 src/box/memtx_hash.c   |  5 ++++-
 src/box/memtx_rtree.c  |  6 +++++-
 src/box/memtx_space.c  | 17 +++++++++++------
 src/box/memtx_tree.cc  | 14 +++++++++++---
 src/box/memtx_tx.c     | 17 ++++++++++-------
 9 files changed, 68 insertions(+), 28 deletions(-)

diff --git a/src/box/index.cc b/src/box/index.cc
index e0139f699b..1c25490909 100644
--- a/src/box/index.cc
+++ b/src/box/index.cc
@@ -695,12 +695,13 @@ generic_index_get(struct index *index, const char *key,
 int
 generic_index_replace(struct index *index, struct tuple *old_tuple,
 		      struct tuple *new_tuple, enum dup_replace_mode mode,
-		      struct tuple **result)
+		      struct tuple **result, struct tuple **successor)
 {
 	(void)old_tuple;
 	(void)new_tuple;
 	(void)mode;
 	(void)result;
+	(void)successor;
 	diag_set(UnsupportedIndexFeature, index->def, "replace()");
 	return -1;
 }
@@ -764,7 +765,7 @@ generic_index_build_next(struct index *index, struct tuple *tuple)
 	 */
 	if (index_reserve(index, 0) != 0)
 		return -1;
-	return index_replace(index, NULL, tuple, DUP_INSERT, &unused);
+	return index_replace(index, NULL, tuple, DUP_INSERT, &unused, &unused);
 }
 
 void
@@ -782,11 +783,12 @@ disabled_index_build_next(struct index *index, struct tuple *tuple)
 int
 disabled_index_replace(struct index *index, struct tuple *old_tuple,
 		       struct tuple *new_tuple, enum dup_replace_mode mode,
-		       struct tuple **result)
+		       struct tuple **result, struct tuple **successor)
 {
 	(void) old_tuple; (void) new_tuple; (void) mode;
 	(void) index;
 	*result = NULL;
+	*successor = NULL;
 	return 0;
 }
 
diff --git a/src/box/index.h b/src/box/index.h
index 2395654b29..146c911dad 100644
--- a/src/box/index.h
+++ b/src/box/index.h
@@ -413,9 +413,19 @@ struct index_vtab {
 			 const char *key, uint32_t part_count);
 	int (*get)(struct index *index, const char *key,
 		   uint32_t part_count, struct tuple **result);
+	/**
+	 * Main entrance point for changing data in index. Once built and
+	 * before deletion this is the only way to insert, replace and delete
+	 * data from the index.
+	 * @param mode - @sa dup_replace_mode description
+	 * @param result - here the replaced or deleted tuple is placed.
+	 * @param successor - if the index supports ordering, then in case of
+	 *  insert (!) here the successor tuple is returned. In other words,
+	 *  here will be stored the tuple, before which new tuple is inserted.
+	 */
 	int (*replace)(struct index *index, struct tuple *old_tuple,
 		       struct tuple *new_tuple, enum dup_replace_mode mode,
-		       struct tuple **result);
+		       struct tuple **result, struct tuple **successor);
 	/** Create an index iterator. */
 	struct iterator *(*create_iterator)(struct index *index,
 			enum iterator_type type,
@@ -646,7 +656,7 @@ index_filter_tuple(struct index *index, struct tuple *tuple) {
 static inline int
 index_replace(struct index *index, struct tuple *old_tuple,
 	      struct tuple *new_tuple, enum dup_replace_mode mode,
-	      struct tuple **result)
+	      struct tuple **result, struct tuple **successor)
 {
 	old_tuple = index_filter_tuple(index, old_tuple);
 	new_tuple = index_filter_tuple(index, new_tuple);
@@ -654,7 +664,8 @@ index_replace(struct index *index, struct tuple *old_tuple,
 		*result = NULL;
 		return 0;
 	}
-	return index->vtab->replace(index, old_tuple, new_tuple, mode, result);
+	return index->vtab->replace(index, old_tuple, new_tuple, mode,
+				    result, successor);
 }
 
 static inline struct iterator *
@@ -732,7 +743,8 @@ ssize_t generic_index_count(struct index *, enum iterator_type,
 			    const char *, uint32_t);
 int generic_index_get(struct index *, const char *, uint32_t, struct tuple **);
 int generic_index_replace(struct index *, struct tuple *, struct tuple *,
-			  enum dup_replace_mode, struct tuple **);
+			  enum dup_replace_mode,
+			  struct tuple **, struct tuple **);
 struct snapshot_iterator *generic_index_create_snapshot_iterator(struct index *);
 void generic_index_stat(struct index *, struct info_handler *);
 void generic_index_compact(struct index *);
@@ -749,7 +761,7 @@ disabled_index_build_next(struct index *index, struct tuple *tuple);
 int
 disabled_index_replace(struct index *index, struct tuple *old_tuple,
 		       struct tuple *new_tuple, enum dup_replace_mode mode,
-		       struct tuple **result);
+		       struct tuple **result, struct tuple **successor);
 
 #if defined(__cplusplus)
 } /* extern "C" */
diff --git a/src/box/memtx_bitset.c b/src/box/memtx_bitset.c
index da79e36def..40fa456692 100644
--- a/src/box/memtx_bitset.c
+++ b/src/box/memtx_bitset.c
@@ -288,10 +288,13 @@ make_key(const char *field, uint32_t *key_len)
 static int
 memtx_bitset_index_replace(struct index *base, struct tuple *old_tuple,
 			   struct tuple *new_tuple, enum dup_replace_mode mode,
-			   struct tuple **result)
+			   struct tuple **result, struct tuple **successor)
 {
 	struct memtx_bitset_index *index = (struct memtx_bitset_index *)base;
 
+	/* BITSET index doesn't support ordering. */
+	*successor = NULL;
+
 	assert(!base->def->opts.is_unique);
 	assert(!base->def->key_def->is_multikey);
 	assert(old_tuple != NULL || new_tuple != NULL);
diff --git a/src/box/memtx_engine.c b/src/box/memtx_engine.c
index 8e0622f122..e076cd71d5 100644
--- a/src/box/memtx_engine.c
+++ b/src/box/memtx_engine.c
@@ -431,7 +431,7 @@ memtx_engine_rollback_statement(struct engine *engine, struct txn *txn,
 		struct index *index = space->index[i];
 		/* Rollback must not fail. */
 		if (index_replace(index, stmt->new_tuple, stmt->old_tuple,
-				  DUP_INSERT, &unused) != 0) {
+				  DUP_INSERT, &unused, &unused) != 0) {
 			diag_log();
 			unreachable();
 			panic("failed to rollback change");
diff --git a/src/box/memtx_hash.c b/src/box/memtx_hash.c
index f424cda03e..b531fd1801 100644
--- a/src/box/memtx_hash.c
+++ b/src/box/memtx_hash.c
@@ -339,11 +339,14 @@ memtx_hash_index_get(struct index *base, const char *key,
 static int
 memtx_hash_index_replace(struct index *base, struct tuple *old_tuple,
 			 struct tuple *new_tuple, enum dup_replace_mode mode,
-			 struct tuple **result)
+			 struct tuple **result, struct tuple **successor)
 {
 	struct memtx_hash_index *index = (struct memtx_hash_index *)base;
 	struct light_index_core *hash_table = &index->hash_table;
 
+	/* HASH index doesn't support ordering. */
+	*successor = NULL;
+
 	if (new_tuple) {
 		uint32_t h = tuple_hash(new_tuple, base->def->key_def);
 		struct tuple *dup_tuple = NULL;
diff --git a/src/box/memtx_rtree.c b/src/box/memtx_rtree.c
index 83b9d3f2db..62a8d53dc7 100644
--- a/src/box/memtx_rtree.c
+++ b/src/box/memtx_rtree.c
@@ -254,10 +254,14 @@ memtx_rtree_index_get(struct index *base, const char *key,
 static int
 memtx_rtree_index_replace(struct index *base, struct tuple *old_tuple,
 			  struct tuple *new_tuple, enum dup_replace_mode mode,
-			  struct tuple **result)
+			  struct tuple **result, struct tuple **successor)
 {
 	(void)mode;
 	struct memtx_rtree_index *index = (struct memtx_rtree_index *)base;
+
+	/* RTREE index doesn't support ordering. */
+	*successor = NULL;
+
 	struct rtree_rect rect;
 	if (new_tuple) {
 		if (extract_rectangle(&rect, new_tuple, base->def) != 0)
diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c
index be672282a9..22ba82b455 100644
--- a/src/box/memtx_space.c
+++ b/src/box/memtx_space.c
@@ -145,8 +145,9 @@ memtx_space_replace_primary_key(struct space *space, struct tuple *old_tuple,
 				enum dup_replace_mode mode,
 				struct tuple **result)
 {
+	struct tuple *successor;
 	if (index_replace(space->index[0], old_tuple,
-			  new_tuple, mode, &old_tuple) != 0)
+			  new_tuple, mode, &old_tuple, &successor) != 0)
 		return -1;
 	memtx_space_update_bsize(space, old_tuple, new_tuple);
 	if (new_tuple != NULL)
@@ -285,7 +286,9 @@ memtx_space_replace_all_keys(struct space *space, struct tuple *old_tuple,
 	 * If old_tuple is not NULL, the index has to
 	 * find and delete it, or return an error.
 	 */
-	if (index_replace(pk, old_tuple, new_tuple, mode, &old_tuple) != 0)
+	struct tuple *successor;
+	if (index_replace(pk, old_tuple, new_tuple, mode,
+			  &old_tuple, &successor) != 0)
 		return -1;
 	assert(old_tuple || new_tuple);
 
@@ -294,7 +297,7 @@ memtx_space_replace_all_keys(struct space *space, struct tuple *old_tuple,
 		struct tuple *unused;
 		struct index *index = space->index[i];
 		if (index_replace(index, old_tuple, new_tuple,
-				  DUP_INSERT, &unused) != 0)
+				  DUP_INSERT, &unused, &unused) != 0)
 			goto rollback;
 	}
 
@@ -310,7 +313,7 @@ memtx_space_replace_all_keys(struct space *space, struct tuple *old_tuple,
 		struct index *index = space->index[i - 1];
 		/* Rollback must not fail. */
 		if (index_replace(index, new_tuple, old_tuple,
-				  DUP_INSERT, &unused) != 0) {
+				  DUP_INSERT, &unused, &unused) != 0) {
 			diag_log();
 			unreachable();
 			panic("failed to rollback change");
@@ -1015,8 +1018,9 @@ memtx_build_on_replace(struct trigger *trigger, void *event)
 	enum dup_replace_mode mode =
 		state->index->def->opts.is_unique ? DUP_INSERT :
 						    DUP_REPLACE_OR_INSERT;
+	struct tuple *successor;
 	state->rc = index_replace(state->index, stmt->old_tuple,
-				  stmt->new_tuple, mode, &delete);
+				  stmt->new_tuple, mode, &delete, &successor);
 	if (state->rc != 0) {
 		diag_move(diag_get(), &state->diag);
 		return 0;
@@ -1106,8 +1110,9 @@ memtx_space_build_index(struct space *src_space, struct index *new_index,
 		 * @todo: better message if there is a duplicate.
 		 */
 		struct tuple *old_tuple;
+		struct tuple *successor;
 		rc = index_replace(new_index, NULL, tuple,
-				   DUP_INSERT, &old_tuple);
+				   DUP_INSERT, &old_tuple, &successor);
 		if (rc != 0)
 			break;
 		assert(old_tuple == NULL); /* Guaranteed by DUP_INSERT. */
diff --git a/src/box/memtx_tree.cc b/src/box/memtx_tree.cc
index 514f36808b..4e9aefbbba 100644
--- a/src/box/memtx_tree.cc
+++ b/src/box/memtx_tree.cc
@@ -762,7 +762,7 @@ template <bool USE_HINT>
 static int
 memtx_tree_index_replace(struct index *base, struct tuple *old_tuple,
 			 struct tuple *new_tuple, enum dup_replace_mode mode,
-			 struct tuple **result)
+			 struct tuple **result, struct tuple **successor)
 {
 	struct memtx_tree_index<USE_HINT> *index =
 		(struct memtx_tree_index<USE_HINT> *)base;
@@ -806,6 +806,7 @@ memtx_tree_index_replace(struct index *base, struct tuple *old_tuple,
 			}
 			return -1;
 		}
+		*successor = suc_data.tuple;
 		if (dup_data.tuple != NULL) {
 			*result = dup_data.tuple;
 			return 0;
@@ -966,10 +967,14 @@ memtx_tree_index_replace_multikey_rollback(struct memtx_tree_index<true> *index,
 static int
 memtx_tree_index_replace_multikey(struct index *base, struct tuple *old_tuple,
 			struct tuple *new_tuple, enum dup_replace_mode mode,
-			struct tuple **result)
+			struct tuple **result, struct tuple **successor)
 {
 	struct memtx_tree_index<true> *index =
 		(struct memtx_tree_index<true> *)base;
+
+	/* MUTLIKEY doesn't support successor for now. */
+	*successor = NULL;
+
 	struct key_def *cmp_def = memtx_tree_cmp_def(&index->tree);
 	*result = NULL;
 	if (new_tuple != NULL) {
@@ -1088,8 +1093,11 @@ memtx_tree_func_index_replace_rollback(struct memtx_tree_index<true> *index,
 static int
 memtx_tree_func_index_replace(struct index *base, struct tuple *old_tuple,
 			struct tuple *new_tuple, enum dup_replace_mode mode,
-			struct tuple **result)
+			struct tuple **result, struct tuple **successor)
 {
+	/* FUNC doesn't support successor for now. */
+	*successor = NULL;
+
 	struct memtx_tree_index<true> *index =
 		(struct memtx_tree_index<true> *)base;
 	struct index_def *index_def = index->base.def;
diff --git a/src/box/memtx_tx.c b/src/box/memtx_tx.c
index 75d39c729b..68e173ca64 100644
--- a/src/box/memtx_tx.c
+++ b/src/box/memtx_tx.c
@@ -490,14 +490,15 @@ memtx_tx_story_full_unlink(struct memtx_story *story)
 			 */
 			if (story->del_psn > 0 && story->space != NULL) {
 				struct index *index = story->space->index[i];
-				struct tuple *unused;
+				struct tuple *removed, *unused;
 				if (index_replace(index, story->tuple, NULL,
-						  DUP_INSERT, &unused) != 0) {
+						  DUP_INSERT,
+						  &removed, &unused) != 0) {
 					diag_log();
 					unreachable();
 					panic("failed to rollback change");
 				}
-				assert(story->tuple == unused);
+				assert(story->tuple == removed);
 				/*
 				 * All tuples in pk are referenced.
 				 * Once removed it must be unreferenced.
@@ -988,9 +989,10 @@ memtx_tx_history_add_stmt(struct txn_stmt *stmt, struct tuple *old_tuple,
 		for (uint32_t i = 0; i < space->index_count; i++) {
 			struct index *index = space->index[i];
 			struct tuple **replaced = &directly_replaced[i];
+			struct tuple *successor;
 			if (index_replace(index, NULL, new_tuple,
 					  DUP_REPLACE_OR_INSERT,
-					  replaced) != 0)
+					  replaced, &successor) != 0)
 			{
 				directly_replaced_count = i;
 				goto fail;
@@ -1101,7 +1103,7 @@ memtx_tx_history_add_stmt(struct txn_stmt *stmt, struct tuple *old_tuple,
 		struct index *index = space->index[i];
 		struct tuple *unused;
 		if (index_replace(index, new_tuple, directly_replaced[i],
-				  DUP_INSERT, &unused) != 0) {
+				  DUP_INSERT, &unused, &unused) != 0) {
 			diag_log();
 			unreachable();
 			panic("failed to rollback change");
@@ -1131,7 +1133,8 @@ memtx_tx_history_rollback_stmt(struct txn_stmt *stmt)
 				struct tuple *was = link->older_story == NULL ?
 					NULL : link->older_story->tuple;
 				if (index_replace(index, story->tuple, was,
-						  DUP_INSERT, &unused) != 0) {
+						  DUP_INSERT,
+						  &unused, &unused) != 0) {
 					diag_log();
 					unreachable();
 					panic("failed to rollback change");
@@ -1243,7 +1246,7 @@ memtx_tx_history_prepare_stmt(struct txn_stmt *stmt)
 			struct tuple *unused;
 			struct index *index = stmt->space->index[i];
 			if (index_replace(index, story->tuple, old_story->tuple,
-					  DUP_INSERT, &unused) != 0) {
+					  DUP_INSERT, &unused, &unused) != 0) {
 				diag_log();
 				panic("failed to rollback change");
 			}
-- 
GitLab