diff --git a/src/box/alter.cc b/src/box/alter.cc index 4bc2e6e6b300..84f95abc1d34 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 f8a184c4e814..97335e284568 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 8064a046d98d..cc5ccbd3cee6 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 1f289ad97dcc..09d2b7d7cbe9 100644 --- a/src/box/sql.h +++ b/src/box/sql.h @@ -34,6 +34,7 @@ #include #include #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 trigger, + * the 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 233f567340e5..9f9f80b70973 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 3995e15dd622..9083736aef3b 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 2a2d52de6a31..539a16560971 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 0c8a3bc75ce0..185f936b42c6 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 bb08ff8d811c..8c9a69a0060c 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 trigger, - * the 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 f30c68b09f79..269c09a3d323 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 =. 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 75bc487b8837..0e1fef8558e3 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 f50198281f2b..7af1bfa606c3 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;