[Tarantool-patches] [PATCH v1 5/9] sql: use rlist to organize triggers in a list

Kirill Shcherbatov kshcherbatov at tarantool.org
Mon Oct 14 19:03:20 MSK 2019


Using a rlist structure member to organize structures in a list
is typical practice in the Tarantool core; and reduces costs for
supporting and extending of an existent code.

With this refactoring using an universal trigger structure in
further patches would be simpler.

Needed for #4343
---
 src/box/alter.cc            |  4 +--
 src/box/space.c             |  3 +-
 src/box/space.h             |  2 +-
 src/box/sql.h               | 47 +++++++++++++++++++++++++++++
 src/box/sql/build.c         |  6 ++--
 src/box/sql/delete.c        |  7 +++--
 src/box/sql/fk_constraint.c |  1 +
 src/box/sql/insert.c        | 31 +++++++++----------
 src/box/sql/sqlInt.h        | 59 +++++--------------------------------
 src/box/sql/trigger.c       | 54 ++++++++++++++++-----------------
 src/box/sql/update.c        | 24 +++++++--------
 src/box/sql/vdbe.c          | 16 ++++++----
 12 files changed, 130 insertions(+), 124 deletions(-)

diff --git a/src/box/alter.cc b/src/box/alter.cc
index 4bc2e6e6b..84f95abc1 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -606,9 +606,7 @@ space_swap_triggers(struct space *new_space, struct space *old_space)
 	rlist_swap(&new_space->before_replace, &old_space->before_replace);
 	rlist_swap(&new_space->on_replace, &old_space->on_replace);
 	/** Swap SQL Triggers pointer. */
-	struct sql_trigger *new_value = new_space->sql_triggers;
-	new_space->sql_triggers = old_space->sql_triggers;
-	old_space->sql_triggers = new_value;
+	rlist_swap(&new_space->trigger_list, &old_space->trigger_list);
 }
 
 /** The same as for triggers - swap lists of FK constraints. */
diff --git a/src/box/space.c b/src/box/space.c
index f8a184c4e..97335e284 100644
--- a/src/box/space.c
+++ b/src/box/space.c
@@ -177,6 +177,7 @@ space_create(struct space *space, struct engine *engine,
 	rlist_create(&space->parent_fk_constraint);
 	rlist_create(&space->child_fk_constraint);
 	rlist_create(&space->ck_constraint);
+	rlist_create(&space->trigger_list);
 
 	/*
 	 * Check if there are unique indexes that are contained
@@ -261,7 +262,7 @@ space_delete(struct space *space)
 	 * SQL Triggers should be deleted with
 	 * on_replace_dd_trigger on deletion from _trigger.
 	 */
-	assert(space->sql_triggers == NULL);
+	assert(rlist_empty(&space->trigger_list));
 	assert(rlist_empty(&space->parent_fk_constraint));
 	assert(rlist_empty(&space->child_fk_constraint));
 	assert(rlist_empty(&space->ck_constraint));
diff --git a/src/box/space.h b/src/box/space.h
index 8064a046d..cc5ccbd3c 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -163,7 +163,7 @@ struct space {
 	/** Triggers fired after space_replace() -- see txn_commit_stmt(). */
 	struct rlist on_replace;
 	/** SQL Trigger list. */
-	struct sql_trigger *sql_triggers;
+	struct rlist trigger_list;
 	/**
 	 * The number of *enabled* indexes in the space.
 	 *
diff --git a/src/box/sql.h b/src/box/sql.h
index 1f289ad97..09d2b7d7c 100644
--- a/src/box/sql.h
+++ b/src/box/sql.h
@@ -34,6 +34,7 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include "box/trigger_def.h"
+#include "small/rlist.h"
 
 #if defined(__cplusplus)
 extern "C" {
@@ -421,6 +422,52 @@ void
 vdbe_field_ref_prepare_tuple(struct vdbe_field_ref *field_ref,
 			     struct tuple *tuple);
 
+/**
+ * Each trigger present in the database schema is stored as an
+ * instance of struct sql_trigger.
+ * Pointers to instances of struct sql_trigger are stored in a
+ * linked list, using the next member of struct sql_trigger. A
+ * pointer to the first element of the linked list is stored as
+ * trigger_list member of the associated space.
+ *
+ * The "step_list" member points to the first element of a linked
+ * list containing the SQL statements specified as the trigger
+ * program.
+ */
+struct sql_trigger {
+	/** The name of the trigger. */
+	char *zName;
+	/** The ID of space the trigger refers to. */
+	uint32_t space_id;
+	/**
+	 * The trigger event. This is the type of operation
+	 * on the associated space for which the trigger
+	 * activates. The value is `INSERT` (a row was inserted),
+	 * `DELETE` (a row was deleted), or `UPDATE` (a row was
+	 * modified).
+	 */
+	enum trigger_event_manipulation event_manipulation;
+	/**
+	 * Whether the trigger activates before or after the
+	 * triggering event. The value is `BEFORE` or `AFTER`.
+	 */
+	enum trigger_action_timing action_timing;
+	/** The WHEN clause of the expression (may be NULL). */
+	struct Expr *pWhen;
+	/**
+	 * If this is an UPDATE OF <column-list> trigger,
+	 * the <column-list> is stored here
+	 */
+	struct IdList *pColumns;
+	/** Link list of trigger program steps. */
+	struct TriggerStep *step_list;
+	/**
+	 * Organize sql_trigger structs into linked list
+	 * with space::trigger_list.
+	 */
+	struct rlist link;
+};
+
 /**
  * Convert a given OP_INSERT/OP_UPDATE/OP_DELETE operation
  * to trigger_event_manipulation value.
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 233f56734..9f9f80b70 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -1546,11 +1546,9 @@ sql_code_drop_table(struct Parse *parse_context, struct space *space,
 	 * Do not account triggers deletion - they will be
 	 * accounted in DELETE from _space below.
 	 */
-	struct sql_trigger *trigger = space->sql_triggers;
-	while (trigger != NULL) {
+	struct sql_trigger *trigger;
+	rlist_foreach_entry(trigger, &space->trigger_list, link)
 		vdbe_code_drop_trigger(parse_context, trigger->zName, false);
-		trigger = trigger->next;
-	}
 	/*
 	 * Remove any entries from the _sequence_data, _sequence
 	 * and _space_sequence spaces associated with the table
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 3995e15dd..9083736ae 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -33,6 +33,7 @@
 #include "box/schema.h"
 #include "sqlInt.h"
 #include "tarantoolInt.h"
+#include "small/rlist.h"
 
 struct space *
 sql_lookup_space(struct Parse *parse, struct SrcList_item *space_name)
@@ -143,14 +144,14 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 	/* Figure out if we have any triggers and if the table
 	 * being deleted from is a view.
 	 */
-	struct sql_trigger *trigger_list = NULL;
+	struct rlist *trigger_list = NULL;
 	/* True if there are triggers or FKs or subqueries in the
 	 * WHERE clause.
 	 */
 	struct space *space = sql_lookup_space(parse, tab_list->a);
 	if (space == NULL)
 		goto delete_from_cleanup;
-	trigger_list = sql_triggers_exist(space->def,
+	trigger_list = sql_triggers_exist(space,
 					  TRIGGER_EVENT_MANIPULATION_DELETE,
 					  NULL, parse->sql_flags, NULL);
 	bool is_complex = trigger_list != NULL || fk_constraint_is_required(space, NULL);
@@ -432,7 +433,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 
 void
 sql_generate_row_delete(struct Parse *parse, struct space *space,
-			struct sql_trigger *trigger_list, int cursor,
+			struct rlist *trigger_list, int cursor,
 			int reg_pk, short npk, bool need_update_count,
 			enum on_conflict_action onconf, u8 mode,
 			int idx_noseek)
diff --git a/src/box/sql/fk_constraint.c b/src/box/sql/fk_constraint.c
index 2a2d52de6..539a16560 100644
--- a/src/box/sql/fk_constraint.c
+++ b/src/box/sql/fk_constraint.c
@@ -857,6 +857,7 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def,
 	if (trigger != NULL) {
 		size_t step_size = sizeof(TriggerStep) + name_len + 1;
 		trigger->step_list = sqlDbMallocZero(db, step_size);
+		rlist_create(&trigger->link);
 		step = trigger->step_list;
 		step->zTarget = (char *) &step[1];
 		memcpy((char *) step->zTarget, space_name, name_len);
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 0c8a3bc75..185f936b4 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -257,7 +257,7 @@ sqlInsert(Parse * pParse,	/* Parser context */
 	int regData;		/* register holding first column to insert */
 	int *aRegIdx = 0;	/* One register allocated to each index */
 	/* List of triggers on pTab, if required. */
-	struct sql_trigger *trigger;
+	struct rlist *trigger_list;
 	int tmask;		/* Mask of trigger times */
 
 	db = pParse->db;
@@ -293,13 +293,13 @@ sqlInsert(Parse * pParse,	/* Parser context */
 	 * inserted into is a view
 	 */
 	struct space_def *space_def = space->def;
-	trigger = sql_triggers_exist(space_def,
-				     TRIGGER_EVENT_MANIPULATION_INSERT, NULL,
-				     pParse->sql_flags, &tmask);
+	trigger_list = sql_triggers_exist(space,
+					  TRIGGER_EVENT_MANIPULATION_INSERT,
+					  NULL, pParse->sql_flags, &tmask);
 
 	bool is_view = space_def->opts.is_view;
-	assert((trigger != NULL && tmask != 0) ||
-	       (trigger == NULL && tmask == 0));
+	assert((trigger_list != NULL && tmask != 0) ||
+	       (trigger_list == NULL && tmask == 0));
 
 	/* If pTab is really a view, make sure it has been initialized.
 	 * ViewGetColumnNames() is a no-op if pTab is not a view.
@@ -320,7 +320,7 @@ sqlInsert(Parse * pParse,	/* Parser context */
 	if (v == NULL)
 		goto insert_cleanup;
 	sqlVdbeCountChanges(v);
-	sql_set_multi_write(pParse, pSelect != NULL || trigger != NULL);
+	sql_set_multi_write(pParse, pSelect != NULL || trigger_list != NULL);
 
 	/* If the statement is of the form
 	 *
@@ -333,7 +333,7 @@ sqlInsert(Parse * pParse,	/* Parser context */
 	 */
 	if (pColumn == NULL &&
 	    xferOptimization(pParse, space, pSelect, on_error)) {
-		assert(trigger == NULL);
+		assert(trigger_list == NULL);
 		assert(pList == 0);
 		goto insert_end;
 	}
@@ -435,7 +435,8 @@ sqlInsert(Parse * pParse,	/* Parser context */
 		 * the SELECT statement. Also use a temp table in
 		 * the case of row triggers.
 		 */
-		if (trigger != NULL || vdbe_has_space_read(pParse, space_def))
+		if (trigger_list != NULL ||
+		    vdbe_has_space_read(pParse, space_def))
 			useTempTable = 1;
 
 		if (useTempTable) {
@@ -600,7 +601,7 @@ sqlInsert(Parse * pParse,	/* Parser context */
 			sql_emit_table_types(v, space_def, regCols + 1);
 
 		/* Fire BEFORE or INSTEAD OF triggers */
-		vdbe_code_row_trigger(pParse, trigger,
+		vdbe_code_row_trigger(pParse, trigger_list,
 				      TRIGGER_EVENT_MANIPULATION_INSERT, 0,
 				      TRIGGER_ACTION_TIMING_BEFORE, space,
 				      regCols - space_def->field_count - 1, on_error,
@@ -753,9 +754,9 @@ sqlInsert(Parse * pParse,	/* Parser context */
 		sqlVdbeAddOp2(v, OP_AddImm, regRowCount, 1);
 	}
 
-	if (trigger != NULL) {
+	if (trigger_list != NULL) {
 		/* Code AFTER triggers */
-		vdbe_code_row_trigger(pParse, trigger,
+		vdbe_code_row_trigger(pParse, trigger_list,
 				      TRIGGER_EVENT_MANIPULATION_INSERT, 0,
 				      TRIGGER_ACTION_TIMING_AFTER, space,
 				      regData - 2 - space_def->field_count, on_error,
@@ -963,8 +964,8 @@ process_index:  ;
 					     skip_index, idx_key_reg,
 					     part_count);
 			sql_set_multi_write(parse_context, true);
-			struct sql_trigger *trigger =
-				sql_triggers_exist(space->def,
+			struct rlist *trigger =
+				sql_triggers_exist(space,
 					TRIGGER_EVENT_MANIPULATION_DELETE, NULL,
 					parse_context->sql_flags, NULL);
 			sql_generate_row_delete(parse_context, space, trigger,
@@ -1069,7 +1070,7 @@ xferOptimization(Parse * pParse,	/* Parser context */
 		return 0;
 	}
 	/* The pDest must not have triggers. */
-	if (dest->sql_triggers != NULL)
+	if (!rlist_empty(&dest->trigger_list))
 		return 0;
 	if (onError == ON_CONFLICT_ACTION_DEFAULT) {
 		onError = ON_CONFLICT_ACTION_ABORT;
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index bb08ff8d8..8c9a69a00 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -2282,49 +2282,6 @@ struct Parse {
 #define OPFLAG_XFER_OPT      0x01
 #endif
 
-/*
- * Each trigger present in the database schema is stored as an
- * instance of struct sql_trigger.
- * Pointers to instances of struct sql_trigger are stored in a
- * linked list, using the next member of struct sql_trigger. A
- * pointer to the first element of the linked list is stored as
- * sql_triggers member of the associated space.
- *
- * The "step_list" member points to the first element of a linked
- * list containing the SQL statements specified as the trigger
- * program.
- */
-struct sql_trigger {
-	/** The name of the trigger. */
-	char *zName;
-	/** The ID of space the trigger refers to. */
-	uint32_t space_id;
-	/**
-	 * The trigger event. This is the type of operation
-	 * on the associated space for which the trigger
-	 * activates. The value is `INSERT` (a row was inserted),
-	 * `DELETE` (a row was deleted), or `UPDATE` (a row was
-	 * modified).
-	 */
-	enum trigger_event_manipulation event_manipulation;
-	/**
-	 * Whether the trigger activates before or after the
-	 * triggering event. The value is `BEFORE` or `AFTER`.
-	 */
-	enum trigger_action_timing action_timing;
-	/** The WHEN clause of the expression (may be NULL). */
-	Expr *pWhen;
-	/**
-	 * If this is an UPDATE OF <column-list> trigger,
-	 * the <column-list> is stored here
-	 */
-	IdList *pColumns;
-	/** Link list of trigger program steps. */
-	TriggerStep *step_list;
-	/** Next trigger associated with the table. */
-	struct sql_trigger *next;
-};
-
 /*
  * An instance of struct TriggerStep is used to store a single SQL statement
  * that is a part of a trigger-program.
@@ -3229,7 +3186,7 @@ sql_expr_needs_no_type_change(const struct Expr *expr, enum field_type type);
  */
 void
 sql_generate_row_delete(struct Parse *parse, struct space *space,
-			struct sql_trigger *trigger_list, int cursor,
+			struct rlist *trigger_list, int cursor,
 			int reg_pk, short npk, bool need_update_count,
 			enum on_conflict_action onconf, u8 mode,
 			int idx_noseek);
@@ -3462,8 +3419,8 @@ vdbe_code_drop_trigger(struct Parse *parser, const char *trigger_name,
  * @param sql_flags SQL flags which describe how to parse request.
  * @param[out] pMask Mask of TRIGGER_BEFORE|TRIGGER_AFTER
  */
-struct sql_trigger *
-sql_triggers_exist(struct space_def *space_def,
+struct rlist *
+sql_triggers_exist(struct space *space,
 		   enum trigger_event_manipulation event_manipulation,
 		   struct ExprList *changes_list, uint32_t sql_flags,
 		   int *mask_ptr);
@@ -3512,7 +3469,7 @@ sql_triggers_exist(struct space_def *space_def,
  * jump to if a trigger program raises an IGNORE exception.
  *
  * @param parser Parse context.
- * @param trigger List of triggers on table.
+ * @param trigger_list List of triggers on table.
  * @param event_manipulation Trigger operation.
  * @param changes_list Changes list for any UPDATE OF triggers.
  * @param action_timing Whether the trigger activates before or
@@ -3523,7 +3480,7 @@ sql_triggers_exist(struct space_def *space_def,
  * @param ignore_jump Instruction to jump to for RAISE(IGNORE).
  */
 void
-vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
+vdbe_code_row_trigger(struct Parse *parser, struct rlist *trigger_list,
 		      enum trigger_event_manipulation event_manipulation,
 		      struct ExprList *changes_list,
 		      enum trigger_action_timing action_timing,
@@ -3537,7 +3494,7 @@ vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
  * header function for sql_code_row_trigger().
  *
  * @param parser Parse context.
- * @param trigger Trigger to code.
+ * @param trigger_list Trigger to code.
  * @param space The space to code triggers from.
  * @param reg Reg array containing OLD.* and NEW.* values.
  * @param orconf ON CONFLICT policy.
@@ -3650,7 +3607,7 @@ sql_trigger_delete_step(struct sql *db, struct Token *table_name,
  * returned mask if the TRIGGER_AFTER bit is set in tr_tm.
  *
  * @param parser  Parse context.
- * @param trigger List of triggers on table.
+ * @param trigger_list List of triggers on table.
  * @param changes_list Changes list for any UPDATE OF triggers.
  * @param new  1 for new.* ref mask, 0 for old.* ref mask.
  * @param action_timing_mask Mask of action timings referenced in
@@ -3661,7 +3618,7 @@ sql_trigger_delete_step(struct sql *db, struct Token *table_name,
  * @retval mask value.
  */
 uint64_t
-sql_trigger_colmask(Parse *parser, struct sql_trigger *trigger,
+sql_trigger_colmask(struct Parse *parser, struct rlist *trigger_list,
 		    struct ExprList *changes_list, int new,
 		    uint8_t action_timing_mask, struct space *space,
 		    int orconf);
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index f30c68b09..269c09a3d 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -130,6 +130,7 @@ sql_trigger_begin(struct Parse *parse)
 	trigger->action_timing = trigger_def->action_timing;
 	trigger->pWhen = sqlExprDup(db, trigger_def->when, EXPRDUP_REDUCE);
 	trigger->pColumns = sqlIdListDup(db, trigger_def->cols);
+	rlist_create(&trigger->link);
 	if ((trigger->pWhen != NULL && trigger->pWhen == NULL) ||
 	    (trigger->pColumns != NULL && trigger->pColumns == NULL))
 		goto trigger_cleanup;
@@ -472,18 +473,17 @@ sql_trigger_replace(const char *name, uint32_t space_id,
 			trigger->action_timing = TRIGGER_ACTION_TIMING_BEFORE;
 	}
 
-	struct sql_trigger **ptr = &space->sql_triggers;
-	while (*ptr != NULL && strcmp((*ptr)->zName, name) != 0)
-		ptr = &((*ptr)->next);
-	if (*ptr != NULL) {
-		*old_trigger = *ptr;
-		*ptr = (*ptr)->next;
+	struct sql_trigger *p;
+	rlist_foreach_entry(p, &space->trigger_list, link) {
+		if (strcmp(p->zName, name) != 0)
+			continue;
+		*old_trigger = p;
+		rlist_del(&p->link);
+		break;
 	}
 
-	if (trigger != NULL) {
-		trigger->next = space->sql_triggers;
-		space->sql_triggers = trigger;
-	}
+	if (trigger != NULL)
+		rlist_add(&space->trigger_list, &trigger->link);
 	return 0;
 }
 
@@ -499,15 +499,6 @@ sql_trigger_space_id(struct sql_trigger *trigger)
 	return trigger->space_id;
 }
 
-struct sql_trigger *
-space_trigger_list(uint32_t space_id)
-{
-	struct space *space = space_cache_find(space_id);
-	assert(space != NULL);
-	assert(space->def != NULL);
-	return space->sql_triggers;
-}
-
 /*
  * pEList is the SET clause of an UPDATE statement.  Each entry
  * in pEList is of the format <id>=<expr>.  If any of the entries
@@ -530,17 +521,18 @@ checkColumnOverlap(IdList * pIdList, ExprList * pEList)
 	return 0;
 }
 
-struct sql_trigger *
-sql_triggers_exist(struct space_def *space_def,
+struct rlist *
+sql_triggers_exist(struct space *space,
 		   enum trigger_event_manipulation event_manipulation,
 		   struct ExprList *changes_list, uint32_t sql_flags,
 		   int *mask_ptr)
 {
 	int mask = 0;
-	struct sql_trigger *trigger_list = NULL;
+	struct rlist *trigger_list = NULL;
 	if ((sql_flags & SQL_EnableTrigger) != 0)
-		trigger_list = space_trigger_list(space_def->id);
-	for (struct sql_trigger *p = trigger_list; p != NULL; p = p->next) {
+		trigger_list = &space->trigger_list;
+	struct sql_trigger *p;
+	rlist_foreach_entry(p, trigger_list, link) {
 		if (p->event_manipulation == event_manipulation &&
 		    checkColumnOverlap(p->pColumns, changes_list) != 0)
 			mask |= (1 << p->action_timing);
@@ -911,7 +903,7 @@ vdbe_code_row_trigger_direct(struct Parse *parser, struct sql_trigger *trigger,
 }
 
 void
-vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
+vdbe_code_row_trigger(struct Parse *parser, struct rlist *trigger_list,
 		      enum trigger_event_manipulation event_manipulation,
 		      struct ExprList *changes_list,
 		      enum trigger_action_timing action_timing,
@@ -921,8 +913,11 @@ vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
 	       action_timing == TRIGGER_ACTION_TIMING_AFTER);
 	assert((event_manipulation == TRIGGER_EVENT_MANIPULATION_UPDATE) ==
 	       (changes_list != NULL));
+	if (trigger_list == NULL)
+		return;
 
-	for (struct sql_trigger *p = trigger; p != NULL; p = p->next) {
+	struct sql_trigger *p;
+	rlist_foreach_entry(p, trigger_list, link) {
 		/* Determine whether we should code trigger. */
 		if (p->event_manipulation == event_manipulation &&
 		    p->action_timing == action_timing &&
@@ -934,7 +929,7 @@ vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
 }
 
 uint64_t
-sql_trigger_colmask(Parse *parser, struct sql_trigger *trigger,
+sql_trigger_colmask(Parse *parser, struct rlist *trigger_list,
 		    ExprList *changes_list, int new, uint8_t action_timing_mask,
 		    struct space *space, int orconf)
 {
@@ -942,9 +937,12 @@ sql_trigger_colmask(Parse *parser, struct sql_trigger *trigger,
 		changes_list != NULL ? TRIGGER_EVENT_MANIPULATION_UPDATE :
 				       TRIGGER_EVENT_MANIPULATION_DELETE;
 	uint64_t mask = 0;
+	if (trigger_list == NULL)
+		return mask;
 
 	assert(new == 1 || new == 0);
-	for (struct sql_trigger *p = trigger; p != NULL; p = p->next) {
+	struct sql_trigger *p;
+	rlist_foreach_entry(p, trigger_list, link) {
 		if (p->event_manipulation == event_manipulation &&
 		    ((action_timing_mask & (1 << p->action_timing)) != 0) &&
 		    checkColumnOverlap(p->pColumns, changes_list)) {
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 75bc487b8..0e1fef855 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -69,7 +69,7 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 
 	bool is_view;		/* True when updating a view (INSTEAD OF trigger) */
 	/* List of triggers on pTab, if required. */
-	struct sql_trigger *trigger;
+	struct rlist *trigger_list;
 	int tmask;		/* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
 	int iEph = 0;		/* Ephemeral table holding all primary key values */
 	int nKey = 0;		/* Number of elements in regKey */
@@ -100,11 +100,11 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 	/* Figure out if we have any triggers and if the table being
 	 * updated is a view.
 	 */
-	trigger = sql_triggers_exist(space->def,
-				     TRIGGER_EVENT_MANIPULATION_UPDATE,
-				     pChanges, pParse->sql_flags, &tmask);
+	trigger_list = sql_triggers_exist(space,
+					  TRIGGER_EVENT_MANIPULATION_UPDATE,
+					  pChanges, pParse->sql_flags, &tmask);
 	is_view = space->def->opts.is_view;
-	assert(trigger != NULL || tmask == 0);
+	assert(trigger_list != NULL || tmask == 0);
 
 	if (is_view &&
 	    sql_view_assign_cursors(pParse, space->def->opts.sql) != 0) {
@@ -192,7 +192,7 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 	/* Allocate required registers. */
 	regOldPk = regNewPk = ++pParse->nMem;
 
-	if (is_pk_modified || trigger != NULL || hasFK != 0) {
+	if (is_pk_modified || trigger_list != NULL || hasFK != 0) {
 		regOld = pParse->nMem + 1;
 		pParse->nMem += def->field_count;
 		regNewPk = ++pParse->nMem;
@@ -301,16 +301,16 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 	 * then regNewPk is the same register as regOldPk, which is
 	 * already populated.
 	 */
-	assert(is_pk_modified || trigger != NULL || hasFK != 0 ||
+	assert(is_pk_modified || trigger_list != NULL || hasFK != 0 ||
 	       regOldPk == regNewPk);
 
 	/* Compute the old pre-UPDATE content of the row being changed, if that
 	 * information is needed
 	 */
-	if (is_pk_modified || hasFK != 0 || trigger != NULL) {
+	if (is_pk_modified || hasFK != 0 || trigger_list != NULL) {
 		assert(space != NULL);
 		uint64_t oldmask = hasFK ? space->fk_constraint_mask : 0;
-		oldmask |= sql_trigger_colmask(pParse, trigger, pChanges, 0,
+		oldmask |= sql_trigger_colmask(pParse, trigger_list, pChanges, 0,
 					(1 << TRIGGER_ACTION_TIMING_BEFORE) |
 					(1 << TRIGGER_ACTION_TIMING_AFTER),
 					space, on_error);
@@ -339,7 +339,7 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 	 * may have modified them). So not loading those that are not going to
 	 * be used eliminates some redundant opcodes.
 	 */
-	uint64_t newmask = sql_trigger_colmask(pParse, trigger, pChanges, 1,
+	uint64_t newmask = sql_trigger_colmask(pParse, trigger_list, pChanges, 1,
 					       1 << TRIGGER_ACTION_TIMING_BEFORE,
 					       space, on_error);
 	for (i = 0; i < (int)def->field_count; i++) {
@@ -366,7 +366,7 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 	 */
 	if ((tmask & (1 << TRIGGER_ACTION_TIMING_BEFORE)) != 0) {
 		sql_emit_table_types(v, space->def, regNew);
-		vdbe_code_row_trigger(pParse, trigger,
+		vdbe_code_row_trigger(pParse, trigger_list,
 				      TRIGGER_EVENT_MANIPULATION_UPDATE,
 				      pChanges, TRIGGER_ACTION_TIMING_BEFORE,
 				      space, regOldPk, on_error, labelContinue);
@@ -481,7 +481,7 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 		sqlVdbeAddOp2(v, OP_AddImm, regRowCount, 1);
 	}
 
-	vdbe_code_row_trigger(pParse, trigger,
+	vdbe_code_row_trigger(pParse, trigger_list,
 			      TRIGGER_EVENT_MANIPULATION_UPDATE, pChanges,
 			      TRIGGER_ACTION_TIMING_AFTER, space, regOldPk,
 			      on_error, labelContinue);
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index f50198281..7af1bfa60 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -4710,23 +4710,28 @@ case OP_RenameTable: {
 	space = space_by_id(space_id);
 	assert(space);
 	/* Rename space op doesn't change triggers. */
-	struct sql_trigger *triggers = space->sql_triggers;
+	RLIST_HEAD(trigger_list);
+	rlist_swap(&trigger_list, &space->trigger_list);
 	zOldTableName = space_name(space);
 	assert(zOldTableName);
 	zNewTableName = pOp->p4.z;
 	zOldTableName = sqlDbStrNDup(db, zOldTableName,
 					 sqlStrlen30(zOldTableName));
-	if (sql_rename_table(space_id, zNewTableName) != 0)
+	if (sql_rename_table(space_id, zNewTableName) != 0) {
+		rlist_swap(&trigger_list, &space->trigger_list);
 		goto abort_due_to_error;
+	}
+	space = space_by_id(space_id);
+	assert(space != NULL);
+	rlist_swap(&trigger_list, &space->trigger_list);
 	/*
 	 * Rebuild 'CREATE TRIGGER' expressions of all triggers
 	 * created on this table. Sure, this action is not atomic
 	 * due to lack of transactional DDL, but just do the best
 	 * effort.
 	 */
-	for (struct sql_trigger *trigger = triggers; trigger != NULL; ) {
-		/* Store pointer as trigger will be destructed. */
-		struct sql_trigger *next_trigger = trigger->next;
+	struct sql_trigger *trigger, *tmp;
+	rlist_foreach_entry_safe(trigger, &space->trigger_list, link, tmp) {
 		/*
 		 * FIXME: In the case of error, part of triggers
 		 * would have invalid space name in tuple so can
@@ -4736,7 +4741,6 @@ case OP_RenameTable: {
 		if (tarantoolsqlRenameTrigger(trigger->zName, zOldTableName,
 					      zNewTableName) != 0)
 			goto abort_due_to_error;
-		trigger = next_trigger;
 	}
 	sqlDbFree(db, (void*)zOldTableName);
 	break;
-- 
2.23.0



More information about the Tarantool-patches mailing list