From 21e2def9dec942a751d43e291727e5906f1b21b6 Mon Sep 17 00:00:00 2001
From: Vladimir Davydov <vdavydov@tarantool.org>
Date: Fri, 22 Apr 2022 23:12:07 +0300
Subject: [PATCH] box: introduce result processor

A convenient helper that is supposed to apply various transformations
to a tuple fetched from a space before returning it to the user.
Usage:

  struct result_processor res_proc;
  result_process_prepare(&res_proc, space);
  rc = index_get(index, key, part_count, result);
  result_process(&res_proc, &rc, result);

We need to split the procedure into two parts, because index_get and
similar methods may yield in case of Vinyl. The 'prepare' method is
supposed to pin all data structures needed to process the result in the
result_processor struct while the 'process' method is supposed to
release them.

Currently, the new methods do absolutely nothing. They will be used to
convert tuples during space upgrade.

NO_DOC=internal
NO_TEST=internal
NO_CHANGELOG=internal
---
 src/box/box.cc   | 20 ++++++++++++++++--
 src/box/index.cc | 36 +++++++++++++++++++++++++++-----
 src/box/result.h | 53 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 102 insertions(+), 7 deletions(-)
 create mode 100644 src/box/result.h

diff --git a/src/box/box.cc b/src/box/box.cc
index 1146bc902e..f26a0235e8 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -58,6 +58,7 @@
 #include "vinyl.h"
 #include "space.h"
 #include "index.h"
+#include "result.h"
 #include "port.h"
 #include "txn.h"
 #include "txn_limbo.h"
@@ -297,6 +298,8 @@ box_process_rw(struct request *request, struct space *space,
 	bool return_tuple = false;
 	struct txn *txn = in_txn();
 	bool is_autocommit = txn == NULL;
+	struct result_processor res_proc;
+	int rc;
 	if (is_autocommit && (txn = txn_begin()) == NULL)
 		return -1;
 	assert(iproto_type_is_dml(request->type));
@@ -305,14 +308,19 @@ box_process_rw(struct request *request, struct space *space,
 		goto rollback;
 	if (txn_begin_stmt(txn, space, request->type) != 0)
 		goto rollback;
-	if (space_execute_dml(space, txn, request, &tuple) != 0) {
+	result_process_prepare(&res_proc, space);
+	rc = space_execute_dml(space, txn, request, &tuple);
+	if (result == NULL)
+		tuple = NULL;
+	result_process(&res_proc, &rc, &tuple);
+	if (rc != 0) {
 		txn_rollback_stmt(txn);
 		goto rollback;
 	}
 	if (result != NULL)
 		*result = tuple;
 
-	return_tuple = result != NULL && tuple != NULL;
+	return_tuple = tuple != NULL;
 	if (return_tuple) {
 		/*
 		 * Pin the tuple locally before the commit,
@@ -2444,7 +2452,10 @@ box_select(uint32_t space_id, uint32_t index_id,
 	struct tuple *tuple;
 	port_c_create(port);
 	while (found < limit) {
+		struct result_processor res_proc;
+		result_process_prepare(&res_proc, space);
 		rc = iterator_next(it, &tuple);
+		result_process(&res_proc, &rc, &tuple);
 		if (rc != 0 || tuple == NULL)
 			break;
 		if (offset > 0) {
@@ -2455,6 +2466,11 @@ box_select(uint32_t space_id, uint32_t index_id,
 		if (rc != 0)
 			break;
 		found++;
+		/*
+		 * Refresh the pointer to the space, because the space struct
+		 * could be freed if the iterator yielded.
+		 */
+		space = iterator_space(it);
 	}
 	iterator_delete(it);
 
diff --git a/src/box/index.cc b/src/box/index.cc
index 80d982cb9b..5bd4b2fbd7 100644
--- a/src/box/index.cc
+++ b/src/box/index.cc
@@ -34,6 +34,7 @@
 #include "schema.h"
 #include "user_def.h"
 #include "space.h"
+#include "result.h"
 #include "iproto_constants.h"
 #include "txn.h"
 #include "rmean.h"
@@ -216,7 +217,11 @@ box_index_random(uint32_t space_id, uint32_t index_id, uint32_t rnd,
 	if (check_index(space_id, index_id, &space, &index) != 0)
 		return -1;
 	/* No tx management, random() is for approximation anyway. */
-	if (index_random(index, rnd, result) != 0)
+	struct result_processor res_proc;
+	result_process_prepare(&res_proc, space);
+	int rc = index_random(index, rnd, result);
+	result_process(&res_proc, &rc, result);
+	if (rc != 0)
 		return -1;
 	if (*result != NULL)
 		tuple_bless(*result);
@@ -247,7 +252,11 @@ box_index_get(uint32_t space_id, uint32_t index_id, const char *key,
 	struct txn_ro_savepoint svp;
 	if (txn_begin_ro_stmt(space, &txn, &svp) != 0)
 		return -1;
-	if (index_get(index, key, part_count, result) != 0) {
+	struct result_processor res_proc;
+	result_process_prepare(&res_proc, space);
+	int rc = index_get(index, key, part_count, result);
+	result_process(&res_proc, &rc, result);
+	if (rc != 0) {
 		txn_rollback_stmt(txn);
 		return -1;
 	}
@@ -284,7 +293,11 @@ box_index_min(uint32_t space_id, uint32_t index_id, const char *key,
 	struct txn_ro_savepoint svp;
 	if (txn_begin_ro_stmt(space, &txn, &svp) != 0)
 		return -1;
-	if (index_min(index, key, part_count, result) != 0) {
+	struct result_processor res_proc;
+	result_process_prepare(&res_proc, space);
+	int rc = index_min(index, key, part_count, result);
+	result_process(&res_proc, &rc, result);
+	if (rc != 0) {
 		txn_rollback_stmt(txn);
 		return -1;
 	}
@@ -319,7 +332,11 @@ box_index_max(uint32_t space_id, uint32_t index_id, const char *key,
 	struct txn_ro_savepoint svp;
 	if (txn_begin_ro_stmt(space, &txn, &svp) != 0)
 		return -1;
-	if (index_max(index, key, part_count, result) != 0) {
+	struct result_processor res_proc;
+	result_process_prepare(&res_proc, space);
+	int rc = index_max(index, key, part_count, result);
+	result_process(&res_proc, &rc, result);
+	if (rc != 0) {
 		txn_rollback_stmt(txn);
 		return -1;
 	}
@@ -408,7 +425,16 @@ int
 box_iterator_next(box_iterator_t *itr, box_tuple_t **result)
 {
 	assert(result != NULL);
-	if (iterator_next(itr, result) != 0)
+	struct space *space = iterator_space(itr);
+	if (space == NULL) {
+		*result = NULL;
+		return 0;
+	}
+	struct result_processor res_proc;
+	result_process_prepare(&res_proc, space);
+	int rc = iterator_next(itr, result);
+	result_process(&res_proc, &rc, result);
+	if (rc != 0)
 		return -1;
 	if (*result != NULL)
 		tuple_bless(*result);
diff --git a/src/box/result.h b/src/box/result.h
new file mode 100644
index 0000000000..032a8ac11d
--- /dev/null
+++ b/src/box/result.h
@@ -0,0 +1,53 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2010-2022, Tarantool AUTHORS, please see AUTHORS file.
+ */
+#pragma once
+
+struct space;
+struct tuple;
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+/**
+ * Helpers that apply various transformations to tuples fetched from a space.
+ * The procedure is split in two parts, because the read operation may yield,
+ * which opens a time window during which the space struct can be deleted.
+ * The 'prepare' phase is supposed to reference and store in result_processor
+ * all data structures needed to apply transforamtions.
+ *
+ * Used by box methods that read tuples from a space and return them to
+ * the user like this:
+ *
+ *   struct result_processor res_proc;
+ *   result_process_prepare(&res_proc, space);
+ *   rc = index_get(index, key, part_count, result);
+ *   result_process(&res_proc, &rc, result);
+ *
+ * Note, if result_process_prepare() was called, then result_process()
+ * must be called as well, because it may need to free some resources.
+ */
+struct result_processor {
+};
+
+static inline void
+result_process_prepare(struct result_processor *p, struct space *space)
+{
+	(void)p;
+	(void)space;
+}
+
+static inline void
+result_process(struct result_processor *p, int *rc, struct tuple **result)
+{
+	(void)p;
+	(void)rc;
+	(void)result;
+}
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
-- 
GitLab