From 817697e87179a5374c078696ee3a4a473d6559af Mon Sep 17 00:00:00 2001
From: Ilya Verbin <iverbin@tarantool.org>
Date: Wed, 8 Nov 2023 19:32:48 +0300
Subject: [PATCH] txn: pass txn_stmt instead of txn to on_commit/on_rollback

Currently on_rollback triggers are called on rollback of the whole
transaction. To make it possible to invoke them on rollback to a
savepoint, we need to pass a statement at which the savepoint was
created.

Needed for #9340

NO_DOC=refactoring
NO_TEST=refactroring
NO_CHANGELOG=refactoring

(cherry picked from commit a1d8582769e03b7bb5885cd71de35dc808bb213f)
---
 src/box/alter.cc   |  3 ++-
 src/box/applier.cc |  5 +++--
 src/box/lua/init.c | 19 +++++++++++--------
 src/box/txn.c      |  7 ++++---
 src/box/txn.h      |  7 +++++++
 src/box/vinyl.c    |  3 ++-
 6 files changed, 29 insertions(+), 15 deletions(-)

diff --git a/src/box/alter.cc b/src/box/alter.cc
index afb02e80a1..d6522f4046 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -825,7 +825,8 @@ struct mh_i32_t *AlterSpaceLock::registry;
 static int
 alter_space_commit(struct trigger *trigger, void *event)
 {
-	struct txn *txn = (struct txn *) event;
+	(void)event;
+	struct txn *txn = in_txn();
 	struct alter_space *alter = (struct alter_space *) trigger->data;
 	/*
 	 * The engine (vinyl) expects us to pass the signature of
diff --git a/src/box/applier.cc b/src/box/applier.cc
index c721289a54..8d2875737e 100644
--- a/src/box/applier.cc
+++ b/src/box/applier.cc
@@ -1221,8 +1221,9 @@ applier_rollback_by_wal_io(int64_t signature)
 static int
 applier_txn_rollback_cb(struct trigger *trigger, void *event)
 {
-	(void) trigger;
-	struct txn *txn = (struct txn *) event;
+	(void)trigger;
+	(void)event;
+	struct txn *txn = in_txn();
 	/*
 	 * Synchronous transaction rollback due to receiving a
 	 * ROLLBACK entry is a normal event and requires no
diff --git a/src/box/lua/init.c b/src/box/lua/init.c
index 7ae524fecc..9aa25a7e74 100644
--- a/src/box/lua/init.c
+++ b/src/box/lua/init.c
@@ -485,9 +485,9 @@ lbox_txn_iterator_next(struct lua_State *L)
 }
 
 /**
- * Open an iterator over the transaction statements. This is a C
- * closure and 1 upvalue should be available - id of the
- * transaction to iterate over.
+ * Open an iterator over the transaction statements. This is a C closure and
+ * 2 upvalues should be available: first is an id of the transaction to iterate
+ * over, second is the first statement of the iteration.
  * It returns 3 values which can be used in Lua 'for': iterator
  * generator function, unused nil and the zero key.
  */
@@ -496,13 +496,14 @@ lbox_txn_pairs(struct lua_State *L)
 {
 	int64_t txn_id = luaL_toint64(L, lua_upvalueindex(1));
 	struct txn *txn = in_txn();
+	struct txn_stmt *stmt;
+	stmt = (struct txn_stmt *)lua_topointer(L, lua_upvalueindex(2));
 	if (txn == NULL || txn->id != txn_id) {
 		diag_set(ClientError, ER_CURSOR_NO_TRANSACTION);
 		return luaT_error(L);
 	}
 	luaL_pushint64(L, txn_id);
-	lua_pushlightuserdata(L, stailq_first_entry(&txn->stmts,
-						    struct txn_stmt, next));
+	lua_pushlightuserdata(L, stmt);
 	lua_pushcclosure(L, lbox_txn_iterator_next, 2);
 	lua_pushnil(L);
 	lua_pushinteger(L, 0);
@@ -510,15 +511,17 @@ lbox_txn_pairs(struct lua_State *L)
 }
 
 /**
- * Push an argument for on_commit Lua trigger. The argument is
+ * Push an argument for on_commit and on_rollback Lua triggers. The argument is
  * a function to open an iterator over the transaction statements.
  */
 static int
 lbox_push_txn(struct lua_State *L, void *event)
 {
-	struct txn *txn = (struct txn *) event;
+	struct txn *txn = in_txn();
+	struct txn_stmt *stmt = (struct txn_stmt *)event;
 	luaL_pushint64(L, txn->id);
-	lua_pushcclosure(L, lbox_txn_pairs, 1);
+	lua_pushlightuserdata(L, stmt);
+	lua_pushcclosure(L, lbox_txn_pairs, 2);
 	return 1;
 }
 
diff --git a/src/box/txn.c b/src/box/txn.c
index f409fff6b3..da7005b61c 100644
--- a/src/box/txn.c
+++ b/src/box/txn.c
@@ -355,7 +355,7 @@ txn_rollback_one_stmt(struct txn *txn, struct txn_stmt *stmt)
 {
 	if (txn->engine != NULL && stmt->space != NULL)
 		engine_rollback_statement(txn->engine, txn, stmt);
-	if (stmt->has_triggers && trigger_run(&stmt->on_rollback, txn) != 0) {
+	if (stmt->has_triggers && trigger_run(&stmt->on_rollback, stmt) != 0) {
 		diag_log();
 		panic("statement rollback trigger failed");
 	}
@@ -704,7 +704,7 @@ txn_complete_fail(struct txn *txn)
 	if (txn->engine != NULL)
 		engine_rollback(txn->engine, txn);
 	if (txn_has_flag(txn, TXN_HAS_TRIGGERS)) {
-		if (trigger_run(&txn->on_rollback, txn) != 0) {
+		if (trigger_run(&txn->on_rollback, txn_first_stmt(txn)) != 0) {
 			diag_log();
 			panic("transaction rollback trigger failed");
 		}
@@ -733,7 +733,8 @@ txn_complete_success(struct txn *txn)
 		 * so that a trigger sees the changes done by previous triggers
 		 * (this is vital for DDL).
 		 */
-		if (trigger_run_reverse(&txn->on_commit, txn) != 0) {
+		if (trigger_run_reverse(&txn->on_commit,
+					txn_first_stmt(txn)) != 0) {
 			diag_log();
 			panic("transaction commit trigger failed");
 		}
diff --git a/src/box/txn.h b/src/box/txn.h
index 03ef60a253..526453aa2e 100644
--- a/src/box/txn.h
+++ b/src/box/txn.h
@@ -891,6 +891,13 @@ txn_is_first_statement(struct txn *txn)
 	return stailq_last(&txn->stmts) == stailq_first(&txn->stmts);
 }
 
+/** The first statement of the transaction. */
+static inline struct txn_stmt *
+txn_first_stmt(struct txn *txn)
+{
+	return stailq_first_entry(&txn->stmts, struct txn_stmt, next);
+}
+
 /** The current statement of the transaction. */
 static inline struct txn_stmt *
 txn_current_stmt(struct txn *txn)
diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index f163ff660d..135b3e030d 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -4373,7 +4373,8 @@ vinyl_space_build_index(struct space *src_space, struct index *new_index,
 static int
 vy_deferred_delete_on_commit(struct trigger *trigger, void *event)
 {
-	struct txn *txn = event;
+	(void)event;
+	struct txn *txn = in_txn();
 	struct vy_mem *mem = trigger->data;
 	/*
 	 * Update dump_lsn so that we can skip dumped deferred
-- 
GitLab