[tarantool-patches] [PATCH v4 6/8] sql: refactor AST trigger object name

Kirill Shcherbatov kshcherbatov at tarantool.org
Tue Jun 26 19:13:31 MSK 2018


Part of #3273.
---
 src/box/alter.cc        |  14 +-
 src/box/space.h         |   2 +-
 src/box/sql.h           |  17 +-
 src/box/sql/build.c     |   6 +-
 src/box/sql/callback.c  |   3 +-
 src/box/sql/delete.c    |  24 +-
 src/box/sql/expr.c      |   2 -
 src/box/sql/fkey.c      | 162 +++++------
 src/box/sql/insert.c    |  37 ++-
 src/box/sql/parse.y     |   9 +-
 src/box/sql/pragma.c    |   2 -
 src/box/sql/pragma.h    |   6 +-
 src/box/sql/resolve.c   |   2 -
 src/box/sql/select.c    |   2 -
 src/box/sql/sqliteInt.h | 253 ++++++++++++++---
 src/box/sql/status.c    |   2 +-
 src/box/sql/tokenize.c  |   4 +-
 src/box/sql/treeview.c  |   2 -
 src/box/sql/trigger.c   | 709 +++++++++++++++++++++---------------------------
 src/box/sql/update.c    |  32 +--
 src/box/sql/vdbe.c      |  10 +-
 src/box/sql/vdbe.h      |   2 -
 22 files changed, 678 insertions(+), 624 deletions(-)

diff --git a/src/box/alter.cc b/src/box/alter.cc
index 1c0e889..449b4b1 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -554,7 +554,7 @@ space_swap_triggers(struct space *new_space, struct space *old_space)
 	rlist_swap(&new_space->on_replace, &old_space->on_replace);
 	rlist_swap(&new_space->on_stmt_begin, &old_space->on_stmt_begin);
 	/** Swap SQL Triggers pointer. */
-	struct Trigger *new_value = new_space->sql_triggers;
+	struct sql_trigger *new_value = new_space->sql_triggers;
 	new_space->sql_triggers = old_space->sql_triggers;
 	old_space->sql_triggers = new_value;
 }
@@ -3257,8 +3257,8 @@ static void
 on_replace_trigger_rollback(struct trigger *trigger, void *event)
 {
 	struct txn_stmt *stmt = txn_last_stmt((struct txn*) event);
-	struct Trigger *old_trigger = (struct Trigger *)trigger->data;
-	struct Trigger *new_trigger;
+	struct sql_trigger *old_trigger = (struct sql_trigger *)trigger->data;
+	struct sql_trigger *new_trigger;
 
 	if (stmt->old_tuple != NULL && stmt->new_tuple == NULL) {
 		/* Rollback DELETE trigger. */
@@ -3294,7 +3294,7 @@ on_replace_trigger_rollback(struct trigger *trigger, void *event)
 static void
 on_replace_trigger_commit(struct trigger *trigger, void * /* event */)
 {
-	struct Trigger *old_trigger = (struct Trigger *)trigger->data;
+	struct sql_trigger *old_trigger = (struct sql_trigger *)trigger->data;
 	sql_trigger_delete(sql_get(), old_trigger);
 }
 
@@ -3328,7 +3328,7 @@ on_replace_dd_trigger(struct trigger * /* trigger */, void *event)
 		memcpy(trigger_name, trigger_name_src, trigger_name_len);
 		trigger_name[trigger_name_len] = 0;
 
-		struct Trigger *old_trigger;
+		struct sql_trigger *old_trigger;
 		int rc = sql_trigger_replace(sql_get(), trigger_name, NULL,
 					     &old_trigger);
 		(void)rc;
@@ -3351,7 +3351,7 @@ on_replace_dd_trigger(struct trigger * /* trigger */, void *event)
 		struct space_opts opts;
 		struct region *region = &fiber()->gc;
 		space_opts_decode(&opts, space_opts, region);
-		struct Trigger *new_trigger =
+		struct sql_trigger *new_trigger =
 			sql_trigger_compile(sql_get(), opts.sql);
 		if (new_trigger == NULL)
 			diag_raise();
@@ -3377,7 +3377,7 @@ on_replace_dd_trigger(struct trigger * /* trigger */, void *event)
 				  "resolved on AST building from SQL");
 		}
 
-		struct Trigger *old_trigger;
+		struct sql_trigger *old_trigger;
 		if (sql_trigger_replace(sql_get(), trigger_name, new_trigger,
 					&old_trigger) != 0)
 			diag_raise();
diff --git a/src/box/space.h b/src/box/space.h
index 64aa8c7..7da2ee5 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -147,7 +147,7 @@ struct space {
 	/** Triggers fired before space statement */
 	struct rlist on_stmt_begin;
 	/** SQL Trigger list. */
-	struct Trigger *sql_triggers;
+	struct sql_trigger *sql_triggers;
 	/**
 	 * The number of *enabled* indexes in the space.
 	 *
diff --git a/src/box/sql.h b/src/box/sql.h
index 2572a15..f483921 100644
--- a/src/box/sql.h
+++ b/src/box/sql.h
@@ -66,7 +66,7 @@ struct Expr;
 struct Parse;
 struct Select;
 struct Table;
-struct Trigger;
+struct sql_trigger;
 
 /**
  * Perform parsing of provided expression. This is done by
@@ -100,9 +100,9 @@ sql_view_compile(struct sqlite3 *db, const char *view_stmt);
  * @param sql request to parse.
  *
  * @retval NULL on error
- * @retval not NULL Trigger AST pointer on success.
+ * @retval not NULL sql_trigger AST pointer on success.
  */
-struct Trigger *
+struct sql_trigger *
 sql_trigger_compile(struct sqlite3 *db, const char *sql);
 
 /**
@@ -111,7 +111,7 @@ sql_trigger_compile(struct sqlite3 *db, const char *sql);
  * @param trigger AST object.
  */
 void
-sql_trigger_delete(struct sqlite3 *db, struct Trigger *trigger);
+sql_trigger_delete(struct sqlite3 *db, struct sql_trigger *trigger);
 
 /**
  * Get server triggers list by space_id.
@@ -119,7 +119,7 @@ sql_trigger_delete(struct sqlite3 *db, struct Trigger *trigger);
  *
  * @retval trigger AST list.
  */
-struct Trigger *
+struct sql_trigger *
 space_trigger_list(uint32_t space_id);
 
 /**
@@ -134,7 +134,8 @@ space_trigger_list(uint32_t space_id);
  */
 int
 sql_trigger_replace(struct sqlite3 *db, const char *name,
-		    struct Trigger *trigger, struct Trigger **old_trigger);
+		    struct sql_trigger *trigger,
+		    struct sql_trigger **old_trigger);
 
 /**
  * Get trigger name by trigger AST object.
@@ -142,7 +143,7 @@ sql_trigger_replace(struct sqlite3 *db, const char *name,
  * @return trigger name string.
  */
 const char *
-sql_trigger_name(struct Trigger *trigger);
+sql_trigger_name(struct sql_trigger *trigger);
 
 /**
  * Get space_id of the space that trigger has been built for.
@@ -150,7 +151,7 @@ sql_trigger_name(struct Trigger *trigger);
  * @return space identifier.
  */
 uint32_t
-sql_trigger_space_id(struct Trigger *trigger);
+sql_trigger_space_id(struct sql_trigger *trigger);
 
 /**
  * Store duplicate of a parsed expression into @a parser.
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 2b4e6c7..b88b8fe 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -2119,10 +2119,10 @@ sql_code_drop_table(struct Parse *parse_context, struct space *space,
 	 * accounted in DELETE from _space below.
 	 */
 	parse_context->nested++;
-	struct Trigger *trigger = space->sql_triggers;
+	struct sql_trigger *trigger = space->sql_triggers;
 	while (trigger != NULL) {
-		sqlite3DropTriggerPtr(parse_context, trigger);
-		trigger = trigger->pNext;
+		vdbe_code_drop_trigger_ptr(parse_context, trigger);
+		trigger = trigger->next;
 	}
 	parse_context->nested--;
 	/*
diff --git a/src/box/sql/callback.c b/src/box/sql/callback.c
index bd8db99..c3c38cb 100644
--- a/src/box/sql/callback.c
+++ b/src/box/sql/callback.c
@@ -292,7 +292,8 @@ sqlite3SchemaClear(sqlite3 * db)
 	sqlite3HashInit(&pSchema->trigHash);
 	for (pElem = sqliteHashFirst(&temp2); pElem != NULL;
 	     pElem = sqliteHashNext(pElem))
-		sql_trigger_delete(NULL, (Trigger *) sqliteHashData(pElem));
+		sql_trigger_delete(NULL,
+				   (struct sql_trigger *)sqliteHashData(pElem));
 	sqlite3HashClear(&temp2);
 	sqlite3HashInit(&pSchema->tblHash);
 	for (pElem = sqliteHashFirst(&temp1); pElem;
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 8b13f60..818bbbd 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -93,7 +93,7 @@ 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 Trigger *trigger_list = NULL;
+	struct sql_trigger *trigger_list = NULL;
 	/* True if there are triggers or FKs or subqueries in the
 	 * WHERE clause.
 	 */
@@ -124,8 +124,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 		space_id = SQLITE_PAGENO_TO_SPACEID(table->tnum);
 		space = space_by_id(space_id);
 		assert(space != NULL);
-		trigger_list =sqlite3TriggersExist(table, TK_DELETE,
-						   NULL, NULL);
+		trigger_list = sql_triggers_exist(table, TK_DELETE, NULL, NULL);
 		is_complex = trigger_list != NULL ||
 			     sqlite3FkRequired(table, NULL);
 	}
@@ -424,8 +423,8 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 
 void
 sql_generate_row_delete(struct Parse *parse, struct Table *table,
-			struct Trigger *trigger_list, int cursor, int reg_pk,
-			short npk, bool need_update_count,
+			struct sql_trigger *trigger_list, int cursor,
+			int reg_pk, short npk, bool need_update_count,
 			enum on_conflict_action onconf, u8 mode,
 			int idx_noseek)
 {
@@ -457,9 +456,10 @@ sql_generate_row_delete(struct Parse *parse, struct Table *table,
 		/* Mask of OLD.* columns in use */
 		/* TODO: Could use temporary registers here. */
 		uint32_t mask =
-		    sqlite3TriggerColmask(parse, trigger_list, 0, 0,
-					  TRIGGER_BEFORE | TRIGGER_AFTER, table,
-					  onconf);
+			sql_trigger_colmask(parse, trigger_list, 0, 0,
+					    TRIGGER_BEFORE | TRIGGER_AFTER,
+					    table,
+					    onconf);
 		mask |= sqlite3FkOldmask(parse, table);
 		first_old_reg = parse->nMem + 1;
 		parse->nMem += (1 + (int)table->def->field_count);
@@ -483,7 +483,7 @@ sql_generate_row_delete(struct Parse *parse, struct Table *table,
 
 		/* Invoke BEFORE DELETE trigger programs. */
 		int addr_start = sqlite3VdbeCurrentAddr(v);
-		sqlite3CodeRowTrigger(parse, trigger_list, TK_DELETE, NULL,
+		vdbe_code_row_trigger(parse, trigger_list, TK_DELETE, NULL,
 				      TRIGGER_BEFORE, table, first_old_reg,
 				      onconf, label);
 
@@ -537,9 +537,9 @@ sql_generate_row_delete(struct Parse *parse, struct Table *table,
 		sqlite3FkActions(parse, table, 0, first_old_reg, 0);
 
 		/* Invoke AFTER DELETE trigger programs. */
-		sqlite3CodeRowTrigger(parse, trigger_list,
-				      TK_DELETE, 0, TRIGGER_AFTER, table,
-				      first_old_reg, onconf, label);
+		vdbe_code_row_trigger(parse, trigger_list, TK_DELETE, 0,
+				      TRIGGER_AFTER, table, first_old_reg,
+				      onconf, label);
 	}
 
 	/* Jump here if the row had already been deleted before
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index 59e7cb4..70e134f 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -4289,7 +4289,6 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 			sqlite3VdbeResolveLabel(v, endLabel);
 			break;
 		}
-#ifndef SQLITE_OMIT_TRIGGER
 	case TK_RAISE:{
 			assert(pExpr->affinity == ON_CONFLICT_ACTION_ROLLBACK
 			       || pExpr->affinity == ON_CONFLICT_ACTION_ABORT
@@ -4319,7 +4318,6 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
 
 			break;
 		}
-#endif
 	}
 	sqlite3ReleaseTempReg(pParse, regFree1);
 	sqlite3ReleaseTempReg(pParse, regFree2);
diff --git a/src/box/sql/fkey.c b/src/box/sql/fkey.c
index ce63ff0..121831b 100644
--- a/src/box/sql/fkey.c
+++ b/src/box/sql/fkey.c
@@ -40,7 +40,6 @@
 #include "tarantoolInt.h"
 
 #ifndef SQLITE_OMIT_FOREIGN_KEY
-#ifndef SQLITE_OMIT_TRIGGER
 
 /*
  * Deferred and Immediate FKs
@@ -730,25 +729,29 @@ sqlite3FkReferences(Table * pTab)
 					pTab->def->name);
 }
 
-/*
+/**
  * The second argument is a Trigger structure allocated by the
- * fkActionTrigger() routine. This function deletes the Trigger structure
- * and all of its sub-components.
+ * fkActionTrigger() routine. This function deletes the sql_trigger
+ * structure and all of its sub-components.
  *
- * The Trigger structure or any of its sub-components may be allocated from
- * the lookaside buffer belonging to database handle dbMem.
+ * The Trigger structure or any of its sub-components may be
+ * allocated from the lookaside buffer belonging to database
+ * handle dbMem.
+ *
+ * @param db Database connection.
+ * @param trigger AST object.
  */
 static void
-fkTriggerDelete(sqlite3 * dbMem, Trigger * p)
+sql_fk_trigger_delete(struct sqlite3 *db, struct sql_trigger *trigger)
 {
-	if (p) {
-		TriggerStep *pStep = p->step_list;
-		sql_expr_delete(dbMem, pStep->pWhere, false);
-		sql_expr_list_delete(dbMem, pStep->pExprList);
-		sql_select_delete(dbMem, pStep->pSelect);
-		sql_expr_delete(dbMem, p->pWhen, false);
-		sqlite3DbFree(dbMem, p);
-	}
+	if (trigger == NULL)
+		return;
+	struct TriggerStep *trigger_step = trigger->step_list;
+	sql_expr_delete(db, trigger_step->pWhere, false);
+	sql_expr_list_delete(db, trigger_step->pExprList);
+	sql_select_delete(db, trigger_step->pSelect);
+	sql_expr_delete(db, trigger->pWhen, false);
+	sqlite3DbFree(db, trigger);
 }
 
 /**
@@ -858,15 +861,13 @@ static int
 isSetNullAction(Parse * pParse, FKey * pFKey)
 {
 	Parse *pTop = sqlite3ParseToplevel(pParse);
-	if (pTop->pTriggerPrg) {
-		Trigger *p = pTop->pTriggerPrg->pTrigger;
-		if ((p == pFKey->apTrigger[0]
-		     && pFKey->aAction[0] == OE_SetNull)
-		    || (p == pFKey->apTrigger[1]
-			&& pFKey->aAction[1] == OE_SetNull)
-		    ) {
+	if (pTop->pTriggerPrg != NULL) {
+		struct sql_trigger *trigger = pTop->pTriggerPrg->trigger;
+		if ((trigger == pFKey->apTrigger[0] &&
+		     pFKey->aAction[0] == OE_SetNull) ||
+		    (trigger == pFKey->apTrigger[1]
+			&& pFKey->aAction[1] == OE_SetNull))
 			return 1;
-		}
 	}
 	return 0;
 }
@@ -1175,21 +1176,24 @@ sqlite3FkRequired(Table * pTab,	/* Table being modified */
 	return 0;
 }
 
-/*
- * This function is called when an UPDATE or DELETE operation is being
- * compiled on table pTab, which is the parent table of foreign-key pFKey.
- * If the current operation is an UPDATE, then the pChanges parameter is
- * passed a pointer to the list of columns being modified. If it is a
- * DELETE, pChanges is passed a NULL pointer.
- *
- * It returns a pointer to a Trigger structure containing a trigger
- * equivalent to the ON UPDATE or ON DELETE action specified by pFKey.
- * If the action is "NO ACTION" or "RESTRICT", then a NULL pointer is
- * returned (these actions require no special handling by the triggers
- * sub-system, code for them is created by fkScanChildren()).
- *
- * For example, if pFKey is the foreign key and pTab is table "p" in
- * the following schema:
+/**
+ * This function is called when an UPDATE or DELETE operation is
+ * being compiled on table pTab, which is the parent table of
+ * foreign-key pFKey.
+ * If the current operation is an UPDATE, then the pChanges
+ * parameter is passed a pointer to the list of columns being
+ * modified. If it is a DELETE, pChanges is passed a NULL pointer.
+ *
+ * It returns a pointer to a sql_trigger structure containing a
+ * trigger equivalent to the ON UPDATE or ON DELETE action
+ * specified by pFKey.
+ * If the action is "NO ACTION" or "RESTRICT", then a NULL pointer
+ * is returned (these actions require no special handling by the
+ * triggers sub-system, code for them is created by
+ * fkScanChildren()).
+ *
+ * For example, if pFKey is the foreign key and pTab is table "p"
+ * in the following schema:
  *
  *   CREATE TABLE p(pk PRIMARY KEY);
  *   CREATE TABLE c(ck REFERENCES p ON DELETE CASCADE);
@@ -1200,20 +1204,25 @@ sqlite3FkRequired(Table * pTab,	/* Table being modified */
  *     DELETE FROM c WHERE ck = old.pk;
  *   END;
  *
- * The returned pointer is cached as part of the foreign key object. It
- * is eventually freed along with the rest of the foreign key object by
- * sqlite3FkDelete().
+ * The returned pointer is cached as part of the foreign key
+ * object. It is eventually freed along with the rest of the
+ * foreign key object by sqlite3FkDelete().
+ *
+ * @param pParse Parse context.
+ * @param pTab Table being updated or deleted from.
+ * @param pFKey Foreign key to get action for.
+ * @param pChanges Change-list for UPDATE, NULL for DELETE.
+ *
+ * @retval not NULL on success.
+ * @retval NULL on failure.
  */
-static Trigger *
-fkActionTrigger(Parse * pParse,	/* Parse context */
-		Table * pTab,	/* Table being updated or deleted from */
-		FKey * pFKey,	/* Foreign key to get action for */
-		ExprList * pChanges	/* Change-list for UPDATE, NULL for DELETE */
-    )
+static struct sql_trigger *
+fkActionTrigger(Parse * pParse, Table * pTab, FKey * pFKey, ExprList * pChanges)
 {
 	sqlite3 *db = pParse->db;	/* Database handle */
 	int action;		/* One of OE_None, OE_Cascade etc. */
-	Trigger *pTrigger;	/* Trigger definition to return */
+	/* Trigger definition to return. */
+	struct sql_trigger *trigger;
 	int iAction = (pChanges != 0);	/* 1 for UPDATE, 0 for DELETE */
 	struct session *user_session = current_session();
 
@@ -1222,9 +1231,9 @@ fkActionTrigger(Parse * pParse,	/* Parse context */
 	    && (user_session->sql_flags & SQLITE_DeferFKs)) {
 		return 0;
 	}
-	pTrigger = pFKey->apTrigger[iAction];
+	trigger = pFKey->apTrigger[iAction];
 
-	if (action != ON_CONFLICT_ACTION_NONE && !pTrigger) {
+	if (action != ON_CONFLICT_ACTION_NONE && trigger == NULL) {
 		char const *zFrom;	/* Name of child table */
 		int nFrom;	/* Length in bytes of zFrom */
 		Index *pIdx = 0;	/* Parent key index for this FK */
@@ -1379,13 +1388,13 @@ fkActionTrigger(Parse * pParse,	/* Parse context */
 		/* Disable lookaside memory allocation */
 		db->lookaside.bDisable++;
 
-		pTrigger = (Trigger *) sqlite3DbMallocZero(db, sizeof(Trigger) +	/* struct Trigger */
-							   sizeof(TriggerStep) +	/* Single step in trigger program */
-							   nFrom + 1	/* Space for pStep->zTarget */
-		    );
-		if (pTrigger) {
-			pStep = pTrigger->step_list =
-			    (TriggerStep *) & pTrigger[1];
+		size_t trigger_size = sizeof(struct sql_trigger) +
+				      sizeof(TriggerStep) + nFrom + 1;
+		trigger =
+			(struct sql_trigger *)sqlite3DbMallocZero(db,
+								  trigger_size);
+		if (trigger != NULL) {
+			pStep = trigger->step_list = (TriggerStep *)&trigger[1];
 			pStep->zTarget = (char *)&pStep[1];
 			memcpy((char *)pStep->zTarget, zFrom, nFrom);
 
@@ -1397,7 +1406,7 @@ fkActionTrigger(Parse * pParse,	/* Parse context */
 			    sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
 			if (pWhen) {
 				pWhen = sqlite3PExpr(pParse, TK_NOT, pWhen, 0);
-				pTrigger->pWhen =
+				trigger->pWhen =
 				    sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE);
 			}
 		}
@@ -1410,7 +1419,7 @@ fkActionTrigger(Parse * pParse,	/* Parse context */
 		sql_expr_list_delete(db, pList);
 		sql_select_delete(db, pSelect);
 		if (db->mallocFailed == 1) {
-			fkTriggerDelete(db, pTrigger);
+			sql_fk_trigger_delete(db, trigger);
 			return 0;
 		}
 		assert(pStep != 0);
@@ -1428,12 +1437,12 @@ fkActionTrigger(Parse * pParse,	/* Parse context */
 		default:
 			pStep->op = TK_UPDATE;
 		}
-		pStep->pTrig = pTrigger;
-		pFKey->apTrigger[iAction] = pTrigger;
-		pTrigger->op = (pChanges ? TK_UPDATE : TK_DELETE);
+		pStep->trigger = trigger;
+		pFKey->apTrigger[iAction] = trigger;
+		trigger->op = pChanges ? TK_UPDATE : TK_DELETE;
 	}
 
-	return pTrigger;
+	return trigger;
 }
 
 /*
@@ -1460,23 +1469,20 @@ sqlite3FkActions(Parse * pParse,	/* Parse context */
 		     pFKey = pFKey->pNextTo) {
 			if (aChange == 0
 			    || fkParentIsModified(pTab, pFKey, aChange)) {
-				Trigger *pAct =
-				    fkActionTrigger(pParse, pTab, pFKey,
-						    pChanges);
-				if (pAct) {
-					sqlite3CodeRowTriggerDirect(pParse,
-								    pAct, pTab,
-								    regOld,
-								    ON_CONFLICT_ACTION_ABORT,
-								    0);
-				}
+				struct sql_trigger *pAct =
+					fkActionTrigger(pParse, pTab, pFKey,
+							pChanges);
+				if (pAct == NULL)
+					continue;
+				vdbe_code_row_trigger_direct(pParse, pAct, pTab,
+							     regOld,
+							     ON_CONFLICT_ACTION_ABORT,
+							     0);
 			}
 		}
 	}
 }
 
-#endif				/* ifndef SQLITE_OMIT_TRIGGER */
-
 /*
  * Free all memory associated with foreign key definitions attached to
  * table pTab. Remove the deleted foreign keys from the Schema.fkeyHash
@@ -1511,10 +1517,8 @@ sqlite3FkDelete(sqlite3 * db, Table * pTab)
 		assert(pFKey->isDeferred == 0 || pFKey->isDeferred == 1);
 
 		/* Delete any triggers created to implement actions for this FK. */
-#ifndef SQLITE_OMIT_TRIGGER
-		fkTriggerDelete(db, pFKey->apTrigger[0]);
-		fkTriggerDelete(db, pFKey->apTrigger[1]);
-#endif
+		sql_fk_trigger_delete(db, pFKey->apTrigger[0]);
+		sql_fk_trigger_delete(db, pFKey->apTrigger[1]);
 
 		pNext = pFKey->pNextFrom;
 		sqlite3DbFree(db, pFKey);
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index db8165a..dac6965 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -357,7 +357,8 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 	int regData;		/* register holding first column to insert */
 	int *aRegIdx = 0;	/* One register allocated to each index */
 	uint32_t space_id = 0;
-	Trigger *pTrigger;	/* List of triggers on pTab, if required */
+	/* List of triggers on pTab, if required. */
+	struct sql_trigger *trigger;
 	int tmask;		/* Mask of trigger times */
 
 	db = pParse->db;
@@ -393,9 +394,10 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 	/* Figure out if we have any triggers and if the table being
 	 * inserted into is a view
 	 */
-	pTrigger = sqlite3TriggersExist(pTab, TK_INSERT, 0, &tmask);
+	trigger = sql_triggers_exist(pTab, TK_INSERT, NULL, &tmask);
 	bool is_view = pTab->def->opts.is_view;
-	assert((pTrigger && tmask) || (pTrigger == 0 && tmask == 0));
+	assert((trigger != NULL && tmask != 0) ||
+	       (trigger == 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.
@@ -419,7 +421,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		goto insert_cleanup;
 	if (pParse->nested == 0)
 		sqlite3VdbeCountChanges(v);
-	sql_set_multi_write(pParse, pSelect || pTrigger);
+	sql_set_multi_write(pParse, pSelect != NULL || trigger != NULL);
 
 #ifndef SQLITE_OMIT_XFER_OPT
 	/* If the statement is of the form
@@ -432,7 +434,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 	 * This is the 2nd template.
 	 */
 	if (pColumn == 0 && xferOptimization(pParse, pTab, pSelect, on_error)) {
-		assert(!pTrigger);
+		assert(trigger == NULL);
 		assert(pList == 0);
 		goto insert_end;
 	}
@@ -536,9 +538,8 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		 * of the tables being read by the SELECT statement.  Also use a
 		 * temp table in the case of row triggers.
 		 */
-		if (pTrigger || readsTable(pParse, pTab)) {
+		if (trigger != NULL || readsTable(pParse, pTab))
 			useTempTable = 1;
-		}
 
 		if (useTempTable) {
 			/* Invoke the coroutine to extract information from the SELECT
@@ -731,7 +732,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		}
 
 		/* Fire BEFORE or INSTEAD OF triggers */
-		sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0,
+		vdbe_code_row_trigger(pParse, trigger, TK_INSERT, 0,
 				      TRIGGER_BEFORE, pTab,
 				      regCols - def->field_count - 1, on_error,
 				      endOfLoop);
@@ -880,9 +881,9 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
 	}
 
-	if (pTrigger) {
+	if (trigger != NULL) {
 		/* Code AFTER triggers */
-		sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0,
+		vdbe_code_row_trigger(pParse, trigger, TK_INSERT, 0,
 				      TRIGGER_AFTER, pTab,
 				      regData - 2 - def->field_count, on_error,
 				      endOfLoop);
@@ -1360,9 +1361,8 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 		bool no_delete_triggers =
 			(0 == (user_session->sql_flags &
 			       SQLITE_RecTriggers) ||
-			 0 == sqlite3TriggersExist(pTab,
-						   TK_DELETE,
-						   0, 0));
+			 sql_triggers_exist(pTab, TK_DELETE, NULL, NULL) ==
+			 NULL);
 		bool no_foreign_keys =
 			(0 == (user_session->sql_flags &
 			       SQLITE_ForeignKeys) ||
@@ -1473,15 +1473,14 @@ sqlite3GenerateConstraintChecks(Parse * pParse,		/* The parser context */
 			sqlite3VdbeGoto(v, ignoreDest);
 			break;
 		default: {
-			Trigger *pTrigger = NULL;
+			struct sql_trigger *trigger = NULL;
 			assert(on_error == ON_CONFLICT_ACTION_REPLACE);
 			sql_set_multi_write(pParse, true);
-			if (user_session->
-			    sql_flags & SQLITE_RecTriggers) {
-				pTrigger = sqlite3TriggersExist(pTab, TK_DELETE,
-								NULL, NULL);
+			if (user_session->sql_flags & SQLITE_RecTriggers) {
+				trigger = sql_triggers_exist(pTab, TK_DELETE,
+							      NULL, NULL);
 			}
-			sql_generate_row_delete(pParse, pTab, pTrigger,
+			sql_generate_row_delete(pParse, pTab, trigger,
 						iDataCur, regR, nPkField, false,
 						ON_CONFLICT_ACTION_REPLACE,
 						pIdx == pPk ? ONEPASS_SINGLE :
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index ccd9d02..91bdc6e 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -1300,20 +1300,18 @@ plus_num(A) ::= number(A).
 minus_num(A) ::= MINUS number(X).     {A = X;}
 //////////////////////////// The CREATE TRIGGER command /////////////////////
 
-%ifndef SQLITE_OMIT_TRIGGER
-
 cmd ::= createkw trigger_decl(A) BEGIN trigger_cmd_list(S) END(Z). {
   Token all;
   all.z = A.z;
   all.n = (int)(Z.z - A.z) + Z.n;
   pParse->initiateTTrans = false;
-  sqlite3FinishTrigger(pParse, S, &all);
+  sql_trigger_finish(pParse, S, &all);
 }
 
 trigger_decl(A) ::= TRIGGER ifnotexists(NOERR) nm(B)
                     trigger_time(C) trigger_event(D)
                     ON fullname(E) foreach_clause when_clause(G). {
-  sqlite3BeginTrigger(pParse, &B, C, D.a, D.b, E, G, NOERR);
+  sql_trigger_begin(pParse, &B, C, D.a, D.b, E, G, NOERR);
   A = B; /*A-overwrites-T*/
 }
 
@@ -1414,7 +1412,6 @@ expr(A) ::= RAISE(X) LP raisetype(T) COMMA STRING(Z) RP(Y).  {
     A.pExpr->affinity = (char)T;
   }
 }
-%endif  !SQLITE_OMIT_TRIGGER
 
 %type raisetype {int}
 raisetype(A) ::= ROLLBACK.  {A = ON_CONFLICT_ACTION_ROLLBACK;}
@@ -1423,11 +1420,9 @@ raisetype(A) ::= FAIL.      {A = ON_CONFLICT_ACTION_FAIL;}
 
 
 ////////////////////////  DROP TRIGGER statement //////////////////////////////
-%ifndef SQLITE_OMIT_TRIGGER
 cmd ::= DROP TRIGGER ifexists(NOERR) fullname(X). {
   sqlite3DropTrigger(pParse,X,NOERR);
 }
-%endif  !SQLITE_OMIT_TRIGGER
 
 ////////////////////////// REINDEX collation //////////////////////////////////
 /* gh-2174: Commended until REINDEX is implemented in scope of gh-3195 */
diff --git a/src/box/sql/pragma.c b/src/box/sql/pragma.c
index 5fb29c7..be6a01c 100644
--- a/src/box/sql/pragma.c
+++ b/src/box/sql/pragma.c
@@ -583,7 +583,6 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 #endif				/* !defined(SQLITE_OMIT_FOREIGN_KEY) */
 
 #ifndef SQLITE_OMIT_FOREIGN_KEY
-#ifndef SQLITE_OMIT_TRIGGER
 	case PragTyp_FOREIGN_KEY_CHECK:{
 			FKey *pFK;	/* A foreign key constraint */
 			Table *pTab;	/* Child table contain "REFERENCES"
@@ -755,7 +754,6 @@ sqlite3Pragma(Parse * pParse, Token * pId,	/* First part of [schema.]id field */
 			}
 			break;
 		}
-#endif				/* !defined(SQLITE_OMIT_TRIGGER) */
 #endif				/* !defined(SQLITE_OMIT_FOREIGN_KEY) */
 
 #ifndef NDEBUG
diff --git a/src/box/sql/pragma.h b/src/box/sql/pragma.h
index f966018..06b7eea 100644
--- a/src/box/sql/pragma.h
+++ b/src/box/sql/pragma.h
@@ -126,7 +126,7 @@ static const PragmaName aPragmaName[] = {
 	 /* iArg:      */ SQLITE_CountRows},
 #endif
 #if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
-#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+#if !defined(SQLITE_OMIT_FOREIGN_KEY)
 	{ /* zName:     */ "defer_foreign_keys",
 	 /* ePragTyp:  */ PragTyp_FLAG,
 	 /* ePragFlg:  */ PragFlg_Result0 | PragFlg_NoColumns1,
@@ -134,7 +134,7 @@ static const PragmaName aPragmaName[] = {
 	 /* iArg:      */ SQLITE_DeferFKs},
 #endif
 #endif
-#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+#if !defined(SQLITE_OMIT_FOREIGN_KEY)
 	{ /* zName:     */ "foreign_key_check",
 	 /* ePragTyp:  */ PragTyp_FOREIGN_KEY_CHECK,
 	 /* ePragFlg:  */ PragFlg_NeedSchema,
@@ -150,7 +150,7 @@ static const PragmaName aPragmaName[] = {
 	 /* iArg:      */ 0},
 #endif
 #if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
-#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+#if !defined(SQLITE_OMIT_FOREIGN_KEY)
 	{ /* zName:     */ "foreign_keys",
 	 /* ePragTyp:  */ PragTyp_FLAG,
 	 /* ePragFlg:  */ PragFlg_Result0 | PragFlg_NoColumns1,
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 23e1618..10c717f 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -309,7 +309,6 @@ lookupName(Parse * pParse,	/* The parsing context */
 			}
 		}
 		/* if( pSrcList ) */
-#ifndef SQLITE_OMIT_TRIGGER
 		/* If we have not already resolved the name, then maybe
 		 * it is a new.* or old.* trigger argument reference
 		 */
@@ -369,7 +368,6 @@ lookupName(Parse * pParse,	/* The parsing context */
 				}
 			}
 		}
-#endif				/* !defined(SQLITE_OMIT_TRIGGER) */
 
 		/*
 		 * If the input is of the form Z (not Y.Z or X.Y.Z) then the name Z
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 368bcd6..4e61ec1 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -1270,7 +1270,6 @@ selectInnerLoop(Parse * pParse,		/* The parser context */
 		}
 #endif				/* SQLITE_OMIT_CTE */
 
-#if !defined(SQLITE_OMIT_TRIGGER)
 		/* Discard the results.  This is used for SELECT statements inside
 		 * the body of a TRIGGER.  The purpose of such selects is to call
 		 * user-defined functions that have side effects.  We do not care
@@ -1280,7 +1279,6 @@ selectInnerLoop(Parse * pParse,		/* The parser context */
 			assert(eDest == SRT_Discard);
 			break;
 		}
-#endif
 	}
 
 	/* Jump to the end of the loop if the LIMIT is reached.  Except, if
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index d5e3263..aa7d48e 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -1493,7 +1493,6 @@ typedef struct StrAccum StrAccum;
 typedef struct Table Table;
 typedef struct Token Token;
 typedef struct TreeView TreeView;
-typedef struct Trigger Trigger;
 typedef struct TriggerPrg TriggerPrg;
 typedef struct TriggerStep TriggerStep;
 typedef struct UnpackedRecord UnpackedRecord;
@@ -1994,7 +1993,8 @@ struct FKey {
 	/* EV: R-30323-21917 */
 	u8 isDeferred;		/* True if constraint checking is deferred till COMMIT */
 	u8 aAction[2];		/* ON DELETE and ON UPDATE actions, respectively */
-	Trigger *apTrigger[2];	/* Triggers for aAction[] actions */
+	/** Triggers for aAction[] actions. */
+	struct sql_trigger *apTrigger[2];
 	struct sColMap {	/* Mapping of columns in pFrom to columns in zTo */
 		int iFrom;	/* Index of column in pFrom */
 		char *zCol;	/* Name of column in zTo.  If NULL use PRIMARY KEY */
@@ -2842,7 +2842,8 @@ struct SelectDest {
  * a mask of new.* columns used by the program.
  */
 struct TriggerPrg {
-	Trigger *pTrigger;	/* Trigger this program was coded from */
+	/** Trigger this program was coded from. */
+	struct sql_trigger *trigger;
 	TriggerPrg *pNext;	/* Next entry in Parse.pTriggerPrg list */
 	SubProgram *pProgram;	/* Program implementing pTrigger/orconf */
 	int orconf;		/* Default ON CONFLICT policy */
@@ -2965,7 +2966,7 @@ struct Parse {
 	union {
 		struct Expr *expr;
 		struct Select *select;
-		struct Trigger *trigger;
+		struct sql_trigger *trigger;
 	} parsed_ast;
 };
 
@@ -3018,21 +3019,23 @@ struct Parse {
 					 */
 
 /*
- * Each trigger present in the database schema is stored as an instance of
- * struct Trigger.
+ * Each trigger present in the database schema is stored as an
+ * instance of struct sql_trigger.
  *
  * Pointers to instances of struct Trigger are stored in two ways.
- * 1. In the "trigHash" hash table (part of the sqlite3* that represents the
- *    database). This allows Trigger structures to be retrieved by name.
- * 2. All triggers associated with a single table form a linked list, using the
- *    pNext member of struct Trigger. A pointer to the first element of the
- *    linked list is stored as the "pTrigger" member of the associated
- *    struct Table.
- *
- * The "step_list" member points to the first element of a linked list
- * containing the SQL statements specified as the trigger program.
- */
-struct Trigger {
+ * 1. In the "trigHash" hash table (part of the sqlite3* that
+ *    represents the database). This allows Trigger structures to
+ *    be retrieved by name.
+ * 2. All triggers associated with a single table form a linked
+ *    list, using the next member of struct sql_trigger. A pointer
+ *    to the first element of the linked list is stored as the
+ *    "pTrigger" member of the associated struct Table.
+ *
+ * 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 {
 	char *zName;		/* The name of the trigger                        */
 	/** The ID of space the trigger refers to. */
 	uint32_t space_id;
@@ -3042,7 +3045,8 @@ struct Trigger {
 	IdList *pColumns;	/* If this is an UPDATE OF <column-list> trigger,
 				   the <column-list> is stored here */
 	TriggerStep *step_list;	/* Link list of trigger program steps             */
-	Trigger *pNext;		/* Next trigger associated with the table */
+	/** Next trigger associated with the table. */
+	struct sql_trigger *next;
 };
 
 /*
@@ -3096,7 +3100,8 @@ struct Trigger {
 struct TriggerStep {
 	u8 op;			/* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */
 	u8 orconf;		/* ON_CONFLICT_ACTION_ROLLBACK etc. */
-	Trigger *pTrig;		/* The trigger that this step is a part of */
+	/** The trigger that this step is a part of */
+	struct sql_trigger *trigger;
 	Select *pSelect;	/* SELECT statement or RHS of INSERT INTO SELECT ... */
 	char *zTarget;		/* Target table for DELETE, UPDATE, INSERT */
 	Expr *pWhere;		/* The WHERE clause for DELETE or UPDATE steps */
@@ -3933,8 +3938,8 @@ int sqlite3ExprNeedsNoAffinityChange(const Expr *, char);
  */
 void
 sql_generate_row_delete(struct Parse *parse, struct Table *table,
-			struct Trigger *trigger_list, int cursor, int reg_pk,
-			short npk, bool need_update_count,
+			struct sql_trigger *trigger_list, int cursor,
+			int reg_pk, short npk, bool need_update_count,
 			enum on_conflict_action onconf, u8 mode,
 			int idx_noseek);
 
@@ -4064,17 +4069,143 @@ void
 sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
 		     int cursor);
 
-#ifndef SQLITE_OMIT_TRIGGER
-void sqlite3BeginTrigger(Parse *, Token *, int, int, IdList *, SrcList *,
-			 Expr *, int);
-void sqlite3FinishTrigger(Parse *, TriggerStep *, Token *);
+/**
+ * This is called by the parser when it sees a CREATE TRIGGER
+ * statement up to the point of the BEGIN before the trigger
+ * actions.  A sql_trigger structure is generated based on the
+ * information available and stored in parse->parsed_ast.trigger.
+ * After the trigger actions have been parsed, the
+ * sql_trigger_finish() function is called to complete the trigger
+ * construction process.
+ *
+ * @param parse The parse context of the CREATE TRIGGER statement.
+ * @param name The name of the trigger.
+ * @param tr_tm One of TK_BEFORE, TK_AFTER, TK_INSTEAD.
+ * @param op One of TK_INSERT, TK_UPDATE, TK_DELETE.
+ * @param columns column list if this is an UPDATE OF trigger.
+ * @param table The name of the table/view the trigger applies to.
+ * @param when  WHEN clause.
+ * @param no_err Suppress errors if the trigger already exists.
+ */
+void
+sql_trigger_begin(struct Parse *parse, struct Token *name, int tr_tm,
+		  int op, struct IdList *columns, struct SrcList *table,
+		  struct Expr *when, int no_err);
+
+/**
+ * This routine is called after all of the trigger actions have
+ * been parsed in order to complete the process of building the
+ * trigger.
+ *
+ * @param parse Parser context.
+ * @param step_list The triggered program.
+ * @param token Token that describes the complete CREATE TRIGGER.
+ */
+void sql_trigger_finish(Parse *, TriggerStep *, Token *);
+
 void sqlite3DropTrigger(Parse *, SrcList *, int);
-void sqlite3DropTriggerPtr(Parse *, Trigger *);
-Trigger *sqlite3TriggersExist(Table *, int, ExprList *, int *pMask);
-void sqlite3CodeRowTrigger(Parse *, Trigger *, int, ExprList *, int, Table *,
-			   int, int, int);
-void sqlite3CodeRowTriggerDirect(Parse *, Trigger *, Table *, int, int, int);
-void sqliteViewTriggers(Parse *, Table *, Expr *, int, ExprList *);
+
+/**
+ * Drop a trigger given a pointer to that trigger.
+ *
+ * @param parser Parse context.
+ * @param trigger Trigger to drop.
+ */
+void
+vdbe_code_drop_trigger_ptr(struct Parse *parser, struct sql_trigger *trigger);
+
+/**
+ * Return a list of all triggers on table pTab if there exists at
+ * least one trigger that must be fired when an operation of type
+ * 'op' is performed on the table, and, if that operation is an
+ * UPDATE, if at least one of the columns in changes_list is being
+ * modified.
+ *
+ * @param table The table the contains the triggers.
+ * @param op operation one of TK_DELETE, TK_INSERT, TK_UPDATE.
+ * @param changes_list Columns that change in an UPDATE statement.
+ * @param[out] pMask Mask of TRIGGER_BEFORE|TRIGGER_AFTER
+ */
+struct sql_trigger *
+sql_triggers_exist(struct Table *table, int op, struct ExprList *changes_list,
+		   int *mask_ptr);
+
+/**
+ * This is called to code the required FOR EACH ROW triggers for
+ * an operation on table. The operation to code triggers for
+ * (INSERT, UPDATE or DELETE) is given by the op parameter. The
+ * tr_tm parameter determines whether the BEFORE or AFTER triggers
+ * are coded. If the operation is an UPDATE, then parameter
+ * changes_list is passed the list of columns being modified.
+ *
+ * If there are no triggers that fire at the specified time for
+ * the specified operation on table, this function is a no-op.
+ *
+ * The reg argument is the address of the first in an array of
+ * registers that contain the values substituted for the new.*
+ * and old.* references in the trigger program. If N is the number
+ * of columns in table table, then registers are populated as
+ * follows:
+ *
+ *   Register       Contains
+ *   ------------------------------------------------------
+ *   reg+0          OLD.PK
+ *   reg+1          OLD.* value of left-most column of pTab
+ *   ...            ...
+ *   reg+N          OLD.* value of right-most column of pTab
+ *   reg+N+1        NEW.PK
+ *   reg+N+2        OLD.* value of left-most column of pTab
+ *   ...            ...
+ *   reg+N+N+1      NEW.* value of right-most column of pTab
+ *
+ * For ON DELETE triggers, the registers containing the NEW.*
+ * values will never be accessed by the trigger program, so they
+ * are not allocated or populated by the caller (there is no data
+ * to populate them with anyway). Similarly, for ON INSERT
+ * triggers the values stored in the OLD.* registers are never
+ * accessed, and so are not allocated by the caller. So, for an
+ * ON INSERT trigger, the value passed to this function as
+ * parameter reg is not a readable register, although registers
+ * (reg+N) through (reg+N+N+1) are.
+ *
+ * Parameter orconf is the default conflict resolution algorithm
+ * for the trigger program to use (REPLACE, IGNORE etc.).
+ * Parameter ignoreJump is the instruction that control should
+ * jump to if a trigger program raises an IGNORE exception.
+ *
+ * @param parser Parse context.
+ * @param trigger List of triggers on table.
+ * @param op operation, one of TK_UPDATE, TK_INSERT, TK_DELETE.
+ * @param changes_list Changes list for any UPDATE OF triggers.
+ * @param tr_tm One of TRIGGER_BEFORE, TRIGGER_AFTER.
+ * @param table The table to code triggers from.
+ * @param reg The first in an array of registers.
+ * @param orconf ON CONFLICT policy.
+ * @param ignore_jump Instruction to jump to for RAISE(IGNORE).
+ */
+void
+vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
+		      int op, struct ExprList *changes_list, int tr_tm,
+		      struct Table *table, int reg, int orconf, int ignore_jump);
+
+/**
+ * Generate code for the trigger program associated with trigger
+ * p on table table. The reg, orconf and ignoreJump parameters
+ * passed to this function are the same as those described in the
+ * header function for sql_code_row_trigger().
+ *
+ * @param parser Parse context.
+ * @param trigger Trigger to code.
+ * @param table The table to code triggers from.
+ * @param reg Reg array containing OLD.* and NEW.* values.
+ * @param orconf ON CONFLICT policy.
+ * @param ignore_jump Instruction to jump to for RAISE(IGNORE).
+ */
+void
+vdbe_code_row_trigger_direct(struct Parse *parser, struct sql_trigger *trigger,
+			     struct Table *table, int reg, int orconf,
+			     int ignore_jump);
+
 void sqlite3DeleteTriggerStep(sqlite3 *, TriggerStep *);
 TriggerStep *sqlite3TriggerSelectStep(sqlite3 *, Select *);
 TriggerStep *sqlite3TriggerInsertStep(sqlite3 *, Token *, IdList *,
@@ -4082,21 +4213,53 @@ TriggerStep *sqlite3TriggerInsertStep(sqlite3 *, Token *, IdList *,
 TriggerStep *sqlite3TriggerUpdateStep(sqlite3 *, Token *, ExprList *, Expr *,
 				      u8);
 TriggerStep *sqlite3TriggerDeleteStep(sqlite3 *, Token *, Expr *);
-u32 sqlite3TriggerColmask(Parse *, Trigger *, ExprList *, int, int, Table *,
-			  int);
+
+/**
+ * Triggers may access values stored in the old.* or new.*
+ * pseudo-table.
+ * This function returns a 32-bit bitmask indicating which columns
+ * of the old.* or new.* tables actually are used by triggers.
+ * This information may be used by the caller, for example, to
+ * avoid having to load the entire old.* record into memory when
+ * executing an UPDATE or DELETE command.
+ *
+ * Bit 0 of the returned mask is set if the left-most column of
+ * the table may be accessed using an [old|new].<col> reference.
+ * Bit 1 is set if the second leftmost column value is required,
+ * and so on. If there are more than 32 columns in the table, and
+ * at least one of the columns with an index greater than 32 may
+ * be accessed, 0xffffffff is returned.
+ *
+ * It is not possible to determine if the old.PK or new.PK column
+ * is accessed by triggers. The caller must always assume that it
+ * is.
+ *
+ * Parameter isNew must be either 1 or 0. If it is 0, then the
+ * mask returned applies to the old.* table. If 1, the new.* table.
+ *
+ * Parameter tr_tm must be a mask with one or both of the
+ * TRIGGER_BEFORE and TRIGGER_AFTER bits set. Values accessed by
+ * BEFORE triggers are only included in the returned mask if the
+ * TRIGGER_BEFORE bit is set in the tr_tm parameter. Similarly,
+ * values accessed by AFTER triggers are only included in the
+ * returned mask if the TRIGGER_AFTER bit is set in tr_tm.
+ *
+ * @param parser  Parse context.
+ * @param trigger 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 tr_tm Mask of TRIGGER_BEFORE|TRIGGER_AFTER.
+ * @param table The table to code triggers from.
+ * @param orconf Default ON CONFLICT policy for trigger steps.
+ *
+ * @retval mask value.
+ */
+u32
+sql_trigger_colmask(Parse *parser, struct sql_trigger *trigger,
+		    ExprList *changes_list, int new, int tr_tm,
+		    Table *table, int orconf);
 #define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p))
 #define sqlite3IsToplevel(p) ((p)->pToplevel==0)
-#else
-#define sqlite3TriggersExist(C,D,E,F) 0
-#define sql_trigger_delete(A,B)
-#define sqlite3DropTriggerPtr(A,B)
-#define sqlite3UnlinkAndDeleteTrigger(A,B,C)
-#define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I)
-#define sqlite3CodeRowTriggerDirect(A,B,C,D,E,F)
-#define sqlite3ParseToplevel(p) p
-#define sqlite3IsToplevel(p) 1
-#define sqlite3TriggerColmask(A,B,C,D,E,F,G) 0
-#endif
 
 int sqlite3JoinType(Parse *, Token *, Token *, Token *);
 void sqlite3CreateForeignKey(Parse *, ExprList *, Token *, ExprList *, int);
@@ -4465,7 +4628,7 @@ void sqlite3WithPush(Parse *, With *, u8);
  * this case foreign keys are parsed, but no other functionality is
  * provided (enforcement of FK constraints requires the triggers sub-system).
  */
-#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+#if !defined(SQLITE_OMIT_FOREIGN_KEY)
 void sqlite3FkCheck(Parse *, Table *, int, int, int *);
 void sqlite3FkDropTable(Parse *, SrcList *, Table *);
 void sqlite3FkActions(Parse *, Table *, ExprList *, int, int *);
diff --git a/src/box/sql/status.c b/src/box/sql/status.c
index dda91c5..161136c 100644
--- a/src/box/sql/status.c
+++ b/src/box/sql/status.c
@@ -257,7 +257,7 @@ sqlite3_db_status(sqlite3 * db,	/* The database connection whose status is desir
 				for (p = sqliteHashFirst(&pSchema->trigHash); p;
 				     p = sqliteHashNext(p)) {
 					sql_trigger_delete(db,
-							   (Trigger *)
+							   (struct sql_trigger *)
 							   sqliteHashData(p));
 				}
 				for (p = sqliteHashFirst(&pSchema->tblHash); p;
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index edcc45b..7c3dabe 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -597,14 +597,14 @@ sql_view_compile(struct sqlite3 *db, const char *view_stmt)
 	return select;
 }
 
-struct Trigger *
+struct sql_trigger *
 sql_trigger_compile(struct sqlite3 *db, const char *sql)
 {
 	struct Parse parser;
 	sql_parser_create(&parser, db);
 	parser.parse_only = true;
 	char *sql_error;
-	struct Trigger *trigger = NULL;
+	struct sql_trigger *trigger = NULL;
 	if (sqlite3RunParser(&parser, sql, &sql_error) != SQLITE_OK ||
 	    parser.parsed_ast_type != AST_TYPE_TRIGGER) {
 	    if (parser.rc != SQL_TARANTOOL_ERROR)
diff --git a/src/box/sql/treeview.c b/src/box/sql/treeview.c
index 84d839e..4261e73 100644
--- a/src/box/sql/treeview.c
+++ b/src/box/sql/treeview.c
@@ -565,7 +565,6 @@ sqlite3TreeViewExpr(TreeView * pView, const Expr * pExpr, u8 moreToFollow)
 			sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0);
 			break;
 		}
-#ifndef SQLITE_OMIT_TRIGGER
 	case TK_RAISE:{
 			const char *zType = "unk";
 			switch (pExpr->affinity) {
@@ -586,7 +585,6 @@ sqlite3TreeViewExpr(TreeView * pView, const Expr * pExpr, u8 moreToFollow)
 					    pExpr->u.zToken);
 			break;
 		}
-#endif
 	case TK_MATCH:{
 			sqlite3TreeViewLine(pView, "MATCH {%d:%d}%s",
 					    pExpr->iTable, pExpr->iColumn,
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 3ec77c7..b23827d 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -42,7 +42,6 @@
 /* See comment in sqliteInt.h */
 int sqlSubProgramsRemaining;
 
-#ifndef SQLITE_OMIT_TRIGGER
 /*
  * Delete a linked list of TriggerStep structures.
  */
@@ -62,46 +61,36 @@ sqlite3DeleteTriggerStep(sqlite3 * db, TriggerStep * pTriggerStep)
 	}
 }
 
-/*
- * This is called by the parser when it sees a CREATE TRIGGER statement
- * up to the point of the BEGIN before the trigger actions.  A Trigger
- * structure is generated based on the information available and stored
- * in pParse->pNewTrigger.  After the trigger actions have been parsed, the
- * sqlite3FinishTrigger() function is called to complete the trigger
- * construction process.
- */
 void
-sqlite3BeginTrigger(Parse * pParse,	/* The parse context of the CREATE TRIGGER statement */
-		    Token * pName,	/* The name of the trigger */
-		    int tr_tm,	/* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */
-		    int op,	/* One of TK_INSERT, TK_UPDATE, TK_DELETE */
-		    IdList * pColumns,	/* column list if this is an UPDATE OF trigger */
-		    SrcList * pTableName,	/* The name of the table/view the trigger applies to */
-		    Expr * pWhen,	/* WHEN clause */
-		    int noErr	/* Suppress errors if the trigger already exists */
-    )
+sql_trigger_begin(struct Parse *parse, struct Token *name, int tr_tm,
+		  int op, struct IdList *columns, struct SrcList *table,
+		  struct Expr *when, int no_err)
 {
-	Trigger *pTrigger = 0;	/* The new trigger */
-	char *zName = 0;	/* Name of the trigger */
-	sqlite3 *db = pParse->db;	/* The database connection */
-	DbFixer sFix;		/* State vector for the DB fixer */
+	/* The new trigger. */
+	struct sql_trigger *trigger = NULL;
+	/* The database connection. */
+	struct sqlite3 *db = parse->db;
+	/* State vector for the DB fixer. */
+	struct DbFixer fixdb;
+	/* The name of the Trigger. */
+	char *trigger_name = NULL;
 
 	/*
 	 * Do not account nested operations: the count of such
 	 * operations depends on Tarantool data dictionary
 	 * internals, such as data layout in system spaces.
 	 */
-	if (!pParse->nested) {
-		Vdbe *v = sqlite3GetVdbe(pParse);
+	if (!parse->nested) {
+		struct Vdbe *v = sqlite3GetVdbe(parse);
 		if (v != NULL)
 			sqlite3VdbeCountChanges(v);
 	}
 	/* pName->z might be NULL, but not pName itself. */
-	assert(pName != NULL);
+	assert(name != NULL);
 	assert(op == TK_INSERT || op == TK_UPDATE || op == TK_DELETE);
 	assert(op > 0 && op < 0xff);
 
-	if (pTableName == NULL || db->mallocFailed)
+	if (table == NULL || db->mallocFailed)
 		goto trigger_cleanup;
 
 	/*
@@ -110,31 +99,31 @@ sqlite3BeginTrigger(Parse * pParse,	/* The parse context of the CREATE TRIGGER s
 	 */
 	if (db->mallocFailed)
 		goto trigger_cleanup;
-	assert(pTableName->nSrc == 1);
-	sqlite3FixInit(&sFix, pParse, "trigger", pName);
-	if (sqlite3FixSrcList(&sFix, pTableName) != 0)
+	assert(table->nSrc == 1);
+	sqlite3FixInit(&fixdb, parse, "trigger", name);
+	if (sqlite3FixSrcList(&fixdb, table) != 0)
 		goto trigger_cleanup;
 
-	zName = sqlite3NameFromToken(db, pName);
-	if (zName == NULL)
+	trigger_name = sqlite3NameFromToken(db, name);
+	if (trigger_name == NULL)
 		goto trigger_cleanup;
 
-	if (sqlite3CheckIdentifierName(pParse, zName) != SQLITE_OK)
+	if (sqlite3CheckIdentifierName(parse, trigger_name) != SQLITE_OK)
 		goto trigger_cleanup;
 
-	if (!pParse->parse_only &&
-	    sqlite3HashFind(&db->pSchema->trigHash, zName) != NULL) {
-		if (!noErr) {
-			diag_set(ClientError, ER_TRIGGER_EXISTS, zName);
-			pParse->rc = SQL_TARANTOOL_ERROR;
-			pParse->nErr++;
+	if (!parse->parse_only &&
+	    sqlite3HashFind(&db->pSchema->trigHash, trigger_name) != NULL) {
+		if (!no_err) {
+			diag_set(ClientError, ER_TRIGGER_EXISTS, trigger_name);
+			parse->rc = SQL_TARANTOOL_ERROR;
+			parse->nErr++;
 		} else {
 			assert(!db->init.busy);
 		}
 		goto trigger_cleanup;
 	}
 
-	const char *table_name = pTableName->a[0].zName;
+	const char *table_name = table->a[0].zName;
 	uint32_t space_id;
 	if (schema_find_id(BOX_SPACE_ID, 2, table_name, strlen(table_name),
 			   &space_id) != 0)
@@ -145,159 +134,161 @@ sqlite3BeginTrigger(Parse * pParse,	/* The parse context of the CREATE TRIGGER s
 	}
 
 	/* Build the Trigger object. */
-	pTrigger = (Trigger *)sqlite3DbMallocZero(db, sizeof(Trigger));
-	if (pTrigger == NULL)
+	trigger = (struct sql_trigger *)sqlite3DbMallocZero(db,
+							    sizeof(struct
+								   sql_trigger));
+	if (trigger == NULL)
 		goto trigger_cleanup;
-	pTrigger->space_id = space_id;
-	pTrigger->zName = zName;
-	zName = NULL;
-
-	pTrigger->op = (u8) op;
-	pTrigger->tr_tm = tr_tm;
-	pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE);
-	pTrigger->pColumns = sqlite3IdListDup(db, pColumns);
-	if ((pWhen != NULL && pTrigger->pWhen == NULL) ||
-	    (pColumns != NULL && pTrigger->pColumns == NULL))
+	trigger->space_id = space_id;
+	trigger->zName = trigger_name;
+	trigger_name = NULL;
+
+	trigger->op = (u8) op;
+	trigger->tr_tm = tr_tm;
+	trigger->pWhen = sqlite3ExprDup(db, when, EXPRDUP_REDUCE);
+	trigger->pColumns = sqlite3IdListDup(db, columns);
+	if ((when != NULL && trigger->pWhen == NULL) ||
+	    (columns != NULL && trigger->pColumns == NULL))
 		goto trigger_cleanup;
-	assert(pParse->parsed_ast.trigger == NULL);
-	pParse->parsed_ast.trigger = pTrigger;
-	pParse->parsed_ast_type = AST_TYPE_TRIGGER;
+	assert(parse->parsed_ast.trigger == NULL);
+	parse->parsed_ast.trigger = trigger;
+	parse->parsed_ast_type = AST_TYPE_TRIGGER;
 
  trigger_cleanup:
-	sqlite3DbFree(db, zName);
-	sqlite3SrcListDelete(db, pTableName);
-	sqlite3IdListDelete(db, pColumns);
-	sql_expr_delete(db, pWhen, false);
-	if (pParse->parsed_ast.trigger == NULL)
-		sql_trigger_delete(db, pTrigger);
+	sqlite3DbFree(db, trigger_name);
+	sqlite3SrcListDelete(db, table);
+	sqlite3IdListDelete(db, columns);
+	sql_expr_delete(db, when, false);
+	if (parse->parsed_ast.trigger == NULL)
+		sql_trigger_delete(db, trigger);
 	else
-		assert(pParse->parsed_ast.trigger == pTrigger);
+		assert(parse->parsed_ast.trigger == trigger);
 
 	return;
 
 set_tarantool_error_and_cleanup:
-	pParse->rc = SQL_TARANTOOL_ERROR;
-	pParse->nErr++;
+	parse->rc = SQL_TARANTOOL_ERROR;
+	parse->nErr++;
 	goto trigger_cleanup;
 }
 
-/*
- * This routine is called after all of the trigger actions have been parsed
- * in order to complete the process of building the trigger.
- */
 void
-sqlite3FinishTrigger(Parse * pParse,	/* Parser context */
-		     TriggerStep * pStepList,	/* The triggered program */
-		     Token * pAll	/* Token that describes the complete CREATE TRIGGER */
-    )
+sql_trigger_finish(struct Parse *parse, struct TriggerStep *step_list,
+		   struct Token *token)
 {
 	/* Trigger being finished. */
-	Trigger *pTrig = pParse->parsed_ast.trigger;
-	char *zName;		/* Name of trigger */
-	char *zSql = 0;		/* SQL text */
-	char *zOpts = 0;	/* MsgPack containing SQL options */
-	sqlite3 *db = pParse->db;	/* The database */
-	DbFixer sFix;		/* Fixer object */
-	Token nameToken;	/* Trigger name for error reporting */
-
-	pParse->parsed_ast.trigger = NULL;
-	if (NEVER(pParse->nErr) || !pTrig)
+	struct sql_trigger *trigger = parse->parsed_ast.trigger;
+	/* Name of trigger. */
+	char *trigger_name;
+	/* SQL text. */
+	char *sql_str = NULL;
+	/* MsgPack containing SQL options. */
+	char *opts_buff = NULL;
+	/* The database. */
+	struct sqlite3 *db = parse->db;
+
+	parse->parsed_ast.trigger = NULL;
+	if (NEVER(parse->nErr) || trigger == NULL)
 		goto triggerfinish_cleanup;
-	zName = pTrig->zName;
-	pTrig->step_list = pStepList;
-	while (pStepList) {
-		pStepList->pTrig = pTrig;
-		pStepList = pStepList->pNext;
+	trigger_name = trigger->zName;
+	trigger->step_list = step_list;
+	while (step_list != NULL) {
+		step_list->trigger = trigger;
+		step_list = step_list->pNext;
 	}
-	sqlite3TokenInit(&nameToken, pTrig->zName);
-	sqlite3FixInit(&sFix, pParse, "trigger", &nameToken);
-	if (sqlite3FixTriggerStep(&sFix, pTrig->step_list)
-	    || sqlite3FixExpr(&sFix, pTrig->pWhen)
-	    ) {
+
+	/* Trigger name for error reporting. */
+	struct Token trigger_name_token;
+	/* Fixer object. */
+	struct DbFixer fixdb;
+	sqlite3TokenInit(&trigger_name_token, trigger->zName);
+	sqlite3FixInit(&fixdb, parse, "trigger", &trigger_name_token);
+	if (sqlite3FixTriggerStep(&fixdb, trigger->step_list)
+	    || sqlite3FixExpr(&fixdb, trigger->pWhen))
 		goto triggerfinish_cleanup;
-	}
 
 	/*
 	 * Generate byte code to insert a new trigger into
 	 * Tarantool for non-parsing mode or export trigger.
 	 */
-	if (!pParse->parse_only) {
-		Vdbe *v;
-		int zOptsSz;
-		Table *pSysTrigger;
-		int iFirstCol;
-		int iCursor = pParse->nTab++;
-		int iRecord;
-
-		/* Make an entry in the _trigger space.  */
-		v = sqlite3GetVdbe(pParse);
+	if (!parse->parse_only) {
+		/* Make an entry in the _trigger space. */
+		struct Vdbe *v = sqlite3GetVdbe(parse);
 		if (v == 0)
 			goto triggerfinish_cleanup;
 
-		pSysTrigger = sqlite3HashFind(&pParse->db->pSchema->tblHash,
-					      TARANTOOL_SYS_TRIGGER_NAME);
-		if (NEVER(!pSysTrigger))
+		struct Table *sys_trigger =
+			sqlite3HashFind(&parse->db->pSchema->tblHash,
+					TARANTOOL_SYS_TRIGGER_NAME);
+		if (NEVER(sys_trigger == NULL))
 			goto triggerfinish_cleanup;
 
-		zSql = sqlite3MPrintf(db, "CREATE TRIGGER %s", pAll->z);
+		sql_str = sqlite3MPrintf(db, "CREATE TRIGGER %s", token->z);
 		if (db->mallocFailed)
 			goto triggerfinish_cleanup;
 
-		sqlite3OpenTable(pParse, iCursor, pSysTrigger, OP_OpenWrite);
+		int cursor = parse->nTab++;
+		sqlite3OpenTable(parse, cursor, sys_trigger, OP_OpenWrite);
 
-		/* makerecord(cursor(iRecord), [reg(iFirstCol), reg(iFirstCol+1)])  */
-		iFirstCol = pParse->nMem + 1;
-		pParse->nMem += 3;
-		iRecord = ++pParse->nMem;
+		/*
+		 * makerecord(cursor(iRecord),
+		 * [reg(first_col), reg(first_col+1)]).
+		 */
+		int first_col = parse->nMem + 1;
+		parse->nMem += 3;
+		int record = ++parse->nMem;
 
-		zOpts = sqlite3DbMallocRaw(pParse->db,
+		opts_buff =
+			sqlite3DbMallocRaw(parse->db,
 					   tarantoolSqlite3MakeTableOpts(0,
-									 zSql,
+									 sql_str,
 									 NULL) +
 					   1);
 		if (db->mallocFailed)
 			goto triggerfinish_cleanup;
 
-		zOptsSz = tarantoolSqlite3MakeTableOpts(0, zSql, zOpts);
+		int opts_buff_sz =
+			tarantoolSqlite3MakeTableOpts(0, sql_str, opts_buff);
 
-		zName = sqlite3DbStrDup(pParse->db, zName);
+		trigger_name = sqlite3DbStrDup(parse->db, trigger_name);
 		if (db->mallocFailed)
 			goto triggerfinish_cleanup;
 
 		sqlite3VdbeAddOp4(v,
-				  OP_String8, 0, iFirstCol, 0,
-				  zName, P4_DYNAMIC);
-		sqlite3VdbeAddOp2(v, OP_Integer, pTrig->space_id, iFirstCol + 1);
-		sqlite3VdbeAddOp4(v, OP_Blob, zOptsSz, iFirstCol + 2,
-				  MSGPACK_SUBTYPE, zOpts, P4_DYNAMIC);
-		sqlite3VdbeAddOp3(v, OP_MakeRecord, iFirstCol, 3, iRecord);
-		sqlite3VdbeAddOp2(v, OP_IdxInsert, iCursor, iRecord);
-		/* Do not account nested operations: the count of such
-		 * operations depends on Tarantool data dictionary internals,
-		 * such as data layout in system spaces.
+				  OP_String8, 0, first_col, 0,
+				  trigger_name, P4_DYNAMIC);
+		sqlite3VdbeAddOp2(v, OP_Integer, trigger->space_id,
+				  first_col + 1);
+		sqlite3VdbeAddOp4(v, OP_Blob, opts_buff_sz, first_col + 2,
+				  MSGPACK_SUBTYPE, opts_buff, P4_DYNAMIC);
+		sqlite3VdbeAddOp3(v, OP_MakeRecord, first_col, 3, record);
+		sqlite3VdbeAddOp2(v, OP_IdxInsert, cursor, record);
+		/*
+		 * Do not account nested operations: the count of
+		 * such operations depends on Tarantool data
+		 * dictionary internals, such as data layout in
+		 * system spaces.
 		 */
-		if (!pParse->nested)
+		if (!parse->nested)
 			sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE);
-		sqlite3VdbeAddOp1(v, OP_Close, iCursor);
+		sqlite3VdbeAddOp1(v, OP_Close, cursor);
 
-		sql_set_multi_write(pParse, false);
-		sqlite3ChangeCookie(pParse);
+		sql_set_multi_write(parse, false);
+		sqlite3ChangeCookie(parse);
 	} else {
-		pParse->parsed_ast.trigger = pTrig;
-		pParse->parsed_ast_type = AST_TYPE_TRIGGER;
-		pTrig = NULL;
+		parse->parsed_ast.trigger = trigger;
+		parse->parsed_ast_type = AST_TYPE_TRIGGER;
+		trigger = NULL;
 	}
 
  triggerfinish_cleanup:
 	if (db->mallocFailed) {
-		sqlite3DbFree(db, zSql);
-		sqlite3DbFree(db, zOpts);
-		/* No need to free zName sinceif we reach this point
-		   alloc for it either wasn't called at all or failed.  */
+		sqlite3DbFree(db, sql_str);
+		sqlite3DbFree(db, opts_buff);
 	}
-	sql_trigger_delete(db, pTrig);
-	assert(pParse->parsed_ast.trigger == NULL || pParse->parse_only);
-	sqlite3DeleteTriggerStep(db, pStepList);
+	sql_trigger_delete(db, trigger);
+	assert(parse->parsed_ast.trigger == NULL || parse->parse_only);
+	sqlite3DeleteTriggerStep(db, step_list);
 }
 
 /*
@@ -439,7 +430,7 @@ sqlite3TriggerDeleteStep(sqlite3 * db,	/* Database connection */
 }
 
 void
-sql_trigger_delete(struct sqlite3 *db, struct Trigger *trigger)
+sql_trigger_delete(struct sqlite3 *db, struct sql_trigger *trigger)
 {
 	if (trigger == NULL)
 		return;
@@ -451,17 +442,18 @@ sql_trigger_delete(struct sqlite3 *db, struct Trigger *trigger)
 }
 
 /*
- * This function is called to drop a trigger from the database schema.
+ * This function is called to drop a trigger from the database
+ * schema.
  *
- * This may be called directly from the parser and therefore identifies
- * the trigger by name.  The sqlite3DropTriggerPtr() routine does the
- * same job as this routine except it takes a pointer to the trigger
- * instead of the trigger name.
+ * This may be called directly from the parser and therefore
+ * identifies the trigger by name.  The sql_drop_trigger_ptr()
+ * routine does the same job as this routine except it takes a
+ * pointer to the trigger instead of the trigger name.
  */
 void
 sqlite3DropTrigger(Parse * pParse, SrcList * pName, int noErr)
 {
-	Trigger *pTrigger = 0;
+	struct sql_trigger *trigger = NULL;
 	const char *zName;
 	sqlite3 *db = pParse->db;
 
@@ -483,8 +475,8 @@ sqlite3DropTrigger(Parse * pParse, SrcList * pName, int noErr)
 
 	assert(pName->nSrc == 1);
 	zName = pName->a[0].zName;
-	pTrigger = sqlite3HashFind(&(db->pSchema->trigHash), zName);
-	if (!pTrigger) {
+	trigger = sqlite3HashFind(&(db->pSchema->trigHash), zName);
+	if (trigger == NULL) {
 		if (!noErr) {
 			sqlite3ErrorMsg(pParse, "no such trigger: %S", pName,
 					0);
@@ -492,48 +484,47 @@ sqlite3DropTrigger(Parse * pParse, SrcList * pName, int noErr)
 		pParse->checkSchema = 1;
 		goto drop_trigger_cleanup;
 	}
-	sqlite3DropTriggerPtr(pParse, pTrigger);
+	vdbe_code_drop_trigger_ptr(pParse, trigger);
 
  drop_trigger_cleanup:
 	sqlite3SrcListDelete(db, pName);
 }
 
-/*
- * Drop a trigger given a pointer to that trigger.
- */
 void
-sqlite3DropTriggerPtr(Parse * pParse, Trigger * pTrigger)
+vdbe_code_drop_trigger_ptr(struct Parse *parser, struct sql_trigger *trigger)
 {
-	Vdbe *v;
-	/* Generate code to delete entry from _trigger and
+	struct Vdbe *v = sqlite3GetVdbe(parser);
+	if (v == NULL)
+		return;
+	/*
+	 * Generate code to delete entry from _trigger and
 	 * internal SQL structures.
 	 */
-	if ((v = sqlite3GetVdbe(pParse)) != 0) {
-		int trig_name_reg = ++pParse->nMem;
-		int record_to_delete = ++pParse->nMem;
-		sqlite3VdbeAddOp4(v, OP_String8, 0, trig_name_reg, 0,
-				  pTrigger->zName, P4_STATIC);
-		sqlite3VdbeAddOp3(v, OP_MakeRecord, trig_name_reg, 1,
-				  record_to_delete);
-		sqlite3VdbeAddOp2(v, OP_SDelete, BOX_TRIGGER_ID,
-				  record_to_delete);
-		if (!pParse->nested)
-			sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE);
-
-		sqlite3ChangeCookie(pParse);
-	}
+	int trig_name_reg = ++parser->nMem;
+	int record_to_delete = ++parser->nMem;
+	sqlite3VdbeAddOp4(v, OP_String8, 0, trig_name_reg, 0,
+			  trigger->zName, P4_STATIC);
+	sqlite3VdbeAddOp3(v, OP_MakeRecord, trig_name_reg, 1,
+			  record_to_delete);
+	sqlite3VdbeAddOp2(v, OP_SDelete, BOX_TRIGGER_ID,
+			  record_to_delete);
+	if (!parser->nested)
+		sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE);
+
+	sqlite3ChangeCookie(parser);
 }
 
 int
 sql_trigger_replace(struct sqlite3 *db, const char *name,
-		    struct Trigger *trigger, struct Trigger **old_trigger)
+		    struct sql_trigger *trigger,
+		    struct sql_trigger **old_trigger)
 {
 	assert(db->pSchema != NULL);
 	assert(trigger == NULL || strcmp(name, trigger->zName) == 0);
 
 	struct Hash *hash = &db->pSchema->trigHash;
 
-	struct Trigger *src_trigger =
+	struct sql_trigger *src_trigger =
 		trigger != NULL ? trigger : sqlite3HashFind(hash, name);
 	assert(src_trigger != NULL);
 	struct space *space = space_cache_find(src_trigger->space_id);
@@ -580,31 +571,31 @@ sql_trigger_replace(struct sqlite3 *db, const char *name,
 	}
 
 	if (*old_trigger != NULL) {
-		struct Trigger **pp;
+		struct sql_trigger **pp;
 		for (pp = &space->sql_triggers; *pp != *old_trigger;
-		     pp = &((*pp)->pNext));
-		*pp = (*pp)->pNext;
+		     pp = &((*pp)->next));
+		*pp = (*pp)->next;
 	}
 	if (trigger != NULL) {
-		trigger->pNext = space->sql_triggers;
+		trigger->next = space->sql_triggers;
 		space->sql_triggers = trigger;
 	}
 	return 0;
 }
 
 const char *
-sql_trigger_name(struct Trigger *trigger)
+sql_trigger_name(struct sql_trigger *trigger)
 {
 	return trigger->zName;
 }
 
 uint32_t
-sql_trigger_space_id(struct Trigger *trigger)
+sql_trigger_space_id(struct sql_trigger *trigger)
 {
 	return trigger->space_id;
 }
 
-struct Trigger *
+struct sql_trigger *
 space_trigger_list(uint32_t space_id)
 {
 	struct space *space = space_cache_find(space_id);
@@ -635,31 +626,22 @@ checkColumnOverlap(IdList * pIdList, ExprList * pEList)
 	return 0;
 }
 
-/*
- * Return a list of all triggers on table pTab if there exists at least
- * one trigger that must be fired when an operation of type 'op' is
- * performed on the table, and, if that operation is an UPDATE, if at
- * least one of the columns in pChanges is being modified.
- */
-Trigger *
-sqlite3TriggersExist(Table * pTab,	/* The table the contains the triggers */
-		     int op,	/* one of TK_DELETE, TK_INSERT, TK_UPDATE */
-		     ExprList * pChanges,	/* Columns that change in an UPDATE statement */
-		     int *pMask	/* OUT: Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
-    )
+struct sql_trigger *
+sql_triggers_exist(struct Table *table, int op, struct ExprList *changes_list,
+		   int *mask_ptr)
 {
 	int mask = 0;
-	struct Trigger *trigger_list = NULL;
+	struct sql_trigger *trigger_list = NULL;
 	struct session *user_session = current_session();
 	if ((user_session->sql_flags & SQLITE_EnableTrigger) != 0)
-		trigger_list = space_trigger_list(pTab->def->id);
-	for (struct Trigger *p = trigger_list; p != NULL; p = p->pNext) {
+		trigger_list = space_trigger_list(table->def->id);
+	for (struct sql_trigger *p = trigger_list; p != NULL; p = p->next) {
 		if (p->op == op && checkColumnOverlap(p->pColumns,
-						      pChanges) != 0)
+						      changes_list) != 0)
 			mask |= p->tr_tm;
 	}
-	if (pMask != NULL)
-		*pMask = mask;
+	if (mask_ptr != NULL)
+		*mask_ptr = mask;
 	return mask != 0 ? trigger_list : NULL;
 }
 
@@ -829,28 +811,33 @@ transferParseError(Parse * pTo, Parse * pFrom)
 	pFrom->zErrMsg = NULL;
 }
 
-/*
+/**
  * Create and populate a new TriggerPrg object with a sub-program
  * implementing trigger pTrigger with ON CONFLICT policy orconf.
+ *
+ * @param parser Current parse context.
+ * @param trigger sql_trigger to code.
+ * @param table trigger is attached to.
+ * @param orconf ON CONFLICT policy to code trigger program with.
+ *
+ * @retval not NULL on success.
+ * @retval NULL on error.
  */
 static TriggerPrg *
-codeRowTrigger(Parse * pParse,	/* Current parse context */
-	       Trigger * pTrigger,	/* Trigger to code */
-	       Table * pTab,	/* The table pTrigger is attached to */
-	       int orconf	/* ON CONFLICT policy to code trigger program with */
-    )
+sql_row_trigger_program(struct Parse *parser, struct sql_trigger *trigger,
+			struct Table *table, int orconf)
 {
-	Parse *pTop = sqlite3ParseToplevel(pParse);
-	sqlite3 *db = pParse->db;	/* Database handle */
+	Parse *pTop = sqlite3ParseToplevel(parser);
+	/* Database handle. */
+	sqlite3 *db = parser->db;
 	TriggerPrg *pPrg;	/* Value to return */
 	Expr *pWhen = 0;	/* Duplicate of trigger WHEN expression */
-	Vdbe *v;		/* Temporary VM */
 	NameContext sNC;	/* Name context for sub-vdbe */
 	SubProgram *pProgram = 0;	/* Sub-vdbe for trigger program */
 	Parse *pSubParse;	/* Parse context for sub-vdbe */
 	int iEndTrigger = 0;	/* Label to jump to if WHEN is false */
 
-	assert(pTrigger->zName == NULL || pTab->def->id == pTrigger->space_id);
+	assert(trigger->zName == NULL || table->def->id == trigger->space_id);
 	assert(pTop->pVdbe);
 
 	/* Allocate the TriggerPrg and SubProgram objects. To ensure that they
@@ -866,13 +853,14 @@ codeRowTrigger(Parse * pParse,	/* Current parse context */
 	if (!pProgram)
 		return 0;
 	sqlite3VdbeLinkSubProgram(pTop->pVdbe, pProgram);
-	pPrg->pTrigger = pTrigger;
+	pPrg->trigger = trigger;
 	pPrg->orconf = orconf;
 	pPrg->aColmask[0] = 0xffffffff;
 	pPrg->aColmask[1] = 0xffffffff;
 
-	/* Allocate and populate a new Parse context to use for coding the
-	 * trigger sub-program.
+	/*
+	 * Allocate and populate a new Parse context to use for
+	 * coding the trigger sub-program.
 	 */
 	pSubParse = sqlite3StackAllocZero(db, sizeof(Parse));
 	if (!pSubParse)
@@ -880,34 +868,37 @@ codeRowTrigger(Parse * pParse,	/* Current parse context */
 	sql_parser_create(pSubParse, db);
 	memset(&sNC, 0, sizeof(sNC));
 	sNC.pParse = pSubParse;
-	pSubParse->pTriggerTab = pTab;
+	pSubParse->pTriggerTab = table;
 	pSubParse->pToplevel = pTop;
-	pSubParse->eTriggerOp = pTrigger->op;
-	pSubParse->nQueryLoop = pParse->nQueryLoop;
+	pSubParse->eTriggerOp = trigger->op;
+	pSubParse->nQueryLoop = parser->nQueryLoop;
 
-	v = sqlite3GetVdbe(pSubParse);
-	if (v) {
+	/* Temporary VM. */
+	struct Vdbe *v = sqlite3GetVdbe(pSubParse);
+	if (v != NULL) {
 		VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)",
-			     pTrigger->zName, onErrorText(orconf),
-			     (pTrigger->tr_tm ==
+			     trigger->zName, onErrorText(orconf),
+			     (trigger->tr_tm ==
 			      TRIGGER_BEFORE ? "BEFORE" : "AFTER"),
-			     (pTrigger->op == TK_UPDATE ? "UPDATE" : ""),
-			     (pTrigger->op == TK_INSERT ? "INSERT" : ""),
-			     (pTrigger->op == TK_DELETE ? "DELETE" : ""),
-			     pTab->def->name));
+			     (trigger->op == TK_UPDATE ? "UPDATE" : ""),
+			     (trigger->op == TK_INSERT ? "INSERT" : ""),
+			     (trigger->op == TK_DELETE ? "DELETE" : ""),
+			      table->def->name));
 #ifndef SQLITE_OMIT_TRACE
 		sqlite3VdbeChangeP4(v, -1,
 				    sqlite3MPrintf(db, "-- TRIGGER %s",
-						   pTrigger->zName),
+						   trigger->zName),
 				    P4_DYNAMIC);
 #endif
 
-		/* If one was specified, code the WHEN clause. If it evaluates to false
-		 * (or NULL) the sub-vdbe is immediately halted by jumping to the
-		 * OP_Halt inserted at the end of the program.
+		/*
+		 * If one was specified, code the WHEN clause. If
+		 * it evaluates to false (or NULL) the sub-vdbe is
+		 * immediately halted by jumping to the OP_Halt
+		 * inserted at the end of the program.
 		 */
-		if (pTrigger->pWhen) {
-			pWhen = sqlite3ExprDup(db, pTrigger->pWhen, 0);
+		if (trigger->pWhen != NULL) {
+			pWhen = sqlite3ExprDup(db, trigger->pWhen, 0);
 			if (SQLITE_OK == sqlite3ResolveExprNames(&sNC, pWhen)
 			    && db->mallocFailed == 0) {
 				iEndTrigger = sqlite3VdbeMakeLabel(v);
@@ -919,17 +910,16 @@ codeRowTrigger(Parse * pParse,	/* Current parse context */
 		}
 
 		/* Code the trigger program into the sub-vdbe. */
-		codeTriggerProgram(pSubParse, pTrigger->step_list, orconf);
+		codeTriggerProgram(pSubParse, trigger->step_list, orconf);
 
 		/* Insert an OP_Halt at the end of the sub-program. */
-		if (iEndTrigger) {
+		if (iEndTrigger)
 			sqlite3VdbeResolveLabel(v, iEndTrigger);
-		}
 		sqlite3VdbeAddOp0(v, OP_Halt);
-		VdbeComment((v, "End: %s.%s", pTrigger->zName,
+		VdbeComment((v, "End: %s.%s", trigger->zName,
 			     onErrorText(orconf)));
 
-		transferParseError(pParse, pSubParse);
+		transferParseError(parser, pSubParse);
 		if (db->mallocFailed == 0) {
 			pProgram->aOp =
 			    sqlite3VdbeTakeOpArray(v, &pProgram->nOp,
@@ -937,7 +927,7 @@ codeRowTrigger(Parse * pParse,	/* Current parse context */
 		}
 		pProgram->nMem = pSubParse->nMem;
 		pProgram->nCsr = pSubParse->nTab;
-		pProgram->token = (void *)pTrigger;
+		pProgram->token = (void *)trigger;
 		pPrg->aColmask[0] = pSubParse->oldmask;
 		pPrg->aColmask[1] = pSubParse->newmask;
 		sqlite3VdbeDelete(v);
@@ -951,211 +941,130 @@ codeRowTrigger(Parse * pParse,	/* Current parse context */
 	return pPrg;
 }
 
-/*
- * Return a pointer to a TriggerPrg object containing the sub-program for
- * trigger pTrigger with default ON CONFLICT algorithm orconf. If no such
- * TriggerPrg object exists, a new object is allocated and populated before
- * being returned.
+/**
+ * Return a pointer to a TriggerPrg object containing the
+ * sub-program for trigger with default ON CONFLICT algorithm
+ * orconf. If no such TriggerPrg object exists, a new object is
+ * allocated and populated before being returned.
+ *
+ * @param parser Current parse context.
+ * @param trigger Trigger to code.
+ * @param table table trigger is attached to.
+ * @param orconf ON CONFLICT algorithm.
+ *
+ * @retval not NULL on success.
+ * @retval NULL on error.
  */
 static TriggerPrg *
-getRowTrigger(Parse * pParse,	/* Current parse context */
-	      Trigger * pTrigger,	/* Trigger to code */
-	      Table * pTab,	/* The table trigger pTrigger is attached to */
-	      int orconf	/* ON CONFLICT algorithm. */
-    )
+sql_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
+		struct Table *table, int orconf)
 {
-	Parse *pRoot = sqlite3ParseToplevel(pParse);
+	Parse *pRoot = sqlite3ParseToplevel(parser);
 	TriggerPrg *pPrg;
 
-	assert(pTrigger->zName == NULL || pTab->def->id == pTrigger->space_id);
+	assert(trigger->zName == NULL || table->def->id == trigger->space_id);
 
-	/* It may be that this trigger has already been coded (or is in the
-	 * process of being coded). If this is the case, then an entry with
-	 * a matching TriggerPrg.pTrigger field will be present somewhere
-	 * in the Parse.pTriggerPrg list. Search for such an entry.
+	/*
+	 * It may be that this trigger has already been coded (or
+	 * is in the process of being coded). If this is the case,
+	 * then an entry with a matching TriggerPrg.pTrigger
+	 * field will be present somewhere in the
+	 * Parse.pTriggerPrg list. Search for such an entry.
 	 */
 	for (pPrg = pRoot->pTriggerPrg;
-	     pPrg && (pPrg->pTrigger != pTrigger || pPrg->orconf != orconf);
+	     pPrg && (pPrg->trigger != trigger || pPrg->orconf != orconf);
 	     pPrg = pPrg->pNext) ;
 
-	/* If an existing TriggerPrg could not be located, create a new one. */
-	if (!pPrg) {
-		pPrg = codeRowTrigger(pParse, pTrigger, pTab, orconf);
-	}
+	/*
+	 * If an existing TriggerPrg could not be located, create
+	 * a new one.
+	 */
+	if (pPrg == NULL)
+		pPrg = sql_row_trigger_program(parser, trigger, table, orconf);
 
 	return pPrg;
 }
 
-/*
- * Generate code for the trigger program associated with trigger p on
- * table pTab. The reg, orconf and ignoreJump parameters passed to this
- * function are the same as those described in the header function for
- * sqlite3CodeRowTrigger()
- */
 void
-sqlite3CodeRowTriggerDirect(Parse * pParse,	/* Parse context */
-			    Trigger * p,	/* Trigger to code */
-			    Table * pTab,	/* The table to code triggers from */
-			    int reg,	/* Reg array containing OLD.* and NEW.* values */
-			    int orconf,	/* ON CONFLICT policy */
-			    int ignoreJump	/* Instruction to jump to for RAISE(IGNORE) */
-    )
+vdbe_code_row_trigger_direct(struct Parse *parser, struct sql_trigger *trigger,
+			     struct Table *table, int reg, int orconf,
+			     int ignore_jump)
 {
-	Vdbe *v = sqlite3GetVdbe(pParse);	/* Main VM */
-	TriggerPrg *pPrg;
-	struct session *user_session = current_session();
+	/* Main VM. */
+	struct Vdbe *v = sqlite3GetVdbe(parser);
 
-	pPrg = getRowTrigger(pParse, p, pTab, orconf);
-	assert(pPrg || pParse->nErr || pParse->db->mallocFailed);
+	TriggerPrg *pPrg = sql_row_trigger(parser, trigger, table, orconf);
+	assert(pPrg != NULL || parser->nErr != 0 ||
+	       parser->db->mallocFailed != 0);
 
-	/* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program
-	 * is a pointer to the sub-vdbe containing the trigger program.
+	/*
+	 * Code the OP_Program opcode in the parent VDBE. P4 of
+	 * the OP_Program is a pointer to the sub-vdbe containing
+	 * the trigger program.
 	 */
-	if (pPrg) {
-		int bRecursive = (p->zName &&
-				  0 ==
-				  (user_session->
-				   sql_flags & SQLITE_RecTriggers));
-
-		sqlite3VdbeAddOp4(v, OP_Program, reg, ignoreJump,
-				  ++pParse->nMem, (const char *)pPrg->pProgram,
-				  P4_SUBPROGRAM);
-		VdbeComment((v, "Call: %s.%s", (p->zName ? p->zName : "fkey"),
-			     onErrorText(orconf)));
+	if (pPrg == NULL)
+		return;
 
-		/* Set the P5 operand of the OP_Program instruction to non-zero if
-		 * recursive invocation of this trigger program is disallowed. Recursive
-		 * invocation is disallowed if (a) the sub-program is really a trigger,
-		 * not a foreign key action, and (b) the flag to enable recursive triggers
-		 * is clear.
-		 */
-		sqlite3VdbeChangeP5(v, (u8) bRecursive);
-	}
+	struct session *user_session = current_session();
+	bool recursive = (trigger->zName && !(user_session->sql_flags &
+					      SQLITE_RecTriggers));
+
+	sqlite3VdbeAddOp4(v, OP_Program, reg, ignore_jump,
+			  ++parser->nMem, (const char *)pPrg->pProgram,
+			  P4_SUBPROGRAM);
+	VdbeComment((v, "Call: %s.%s", (trigger->zName ? trigger->zName :
+					"fkey"),
+		     onErrorText(orconf)));
+
+	/*
+	 * Set the P5 operand of the OP_Program
+	 * instruction to non-zero if recursive invocation
+	 * of this trigger program is disallowed.
+	 * Recursive invocation is disallowed if (a) the
+	 * sub-program is really a trigger, not a foreign
+	 * key action, and (b) the flag to enable
+	 * recursive triggers is clear.
+	 */
+	sqlite3VdbeChangeP5(v, (u8)recursive);
 }
 
-/*
- * This is called to code the required FOR EACH ROW triggers for an operation
- * on table pTab. The operation to code triggers for (INSERT, UPDATE or DELETE)
- * is given by the op parameter. The tr_tm parameter determines whether the
- * BEFORE or AFTER triggers are coded. If the operation is an UPDATE, then
- * parameter pChanges is passed the list of columns being modified.
- *
- * If there are no triggers that fire at the specified time for the specified
- * operation on pTab, this function is a no-op.
- *
- * The reg argument is the address of the first in an array of registers
- * that contain the values substituted for the new.* and old.* references
- * in the trigger program. If N is the number of columns in table pTab
- * (a copy of pTab->nCol), then registers are populated as follows:
- *
- *   Register       Contains
- *   ------------------------------------------------------
- *   reg+0          OLD.PK
- *   reg+1          OLD.* value of left-most column of pTab
- *   ...            ...
- *   reg+N          OLD.* value of right-most column of pTab
- *   reg+N+1        NEW.PK
- *   reg+N+2        OLD.* value of left-most column of pTab
- *   ...            ...
- *   reg+N+N+1      NEW.* value of right-most column of pTab
- *
- * For ON DELETE triggers, the registers containing the NEW.* values will
- * never be accessed by the trigger program, so they are not allocated or
- * populated by the caller (there is no data to populate them with anyway).
- * Similarly, for ON INSERT triggers the values stored in the OLD.* registers
- * are never accessed, and so are not allocated by the caller. So, for an
- * ON INSERT trigger, the value passed to this function as parameter reg
- * is not a readable register, although registers (reg+N) through
- * (reg+N+N+1) are.
- *
- * Parameter orconf is the default conflict resolution algorithm for the
- * trigger program to use (REPLACE, IGNORE etc.). Parameter ignoreJump
- * is the instruction that control should jump to if a trigger program
- * raises an IGNORE exception.
- */
 void
-sqlite3CodeRowTrigger(Parse * pParse,	/* Parse context */
-		      Trigger * pTrigger,	/* List of triggers on table pTab */
-		      int op,	/* One of TK_UPDATE, TK_INSERT, TK_DELETE */
-		      ExprList * pChanges,	/* Changes list for any UPDATE OF triggers */
-		      int tr_tm,	/* One of TRIGGER_BEFORE, TRIGGER_AFTER */
-		      Table * pTab,	/* The table to code triggers from */
-		      int reg,	/* The first in an array of registers (see above) */
-		      int orconf,	/* ON CONFLICT policy */
-		      int ignoreJump	/* Instruction to jump to for RAISE(IGNORE) */
-    )
+vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
+		      int op, struct ExprList *changes_list, int tr_tm,
+		      struct Table *table, int reg, int orconf, int ignore_jump)
 {
-	Trigger *p;		/* Used to iterate through pTrigger list */
-
 	assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
 	assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER);
-	assert((op == TK_UPDATE) == (pChanges != 0));
-
-	for (p = pTrigger; p; p = p->pNext) {
-		/* Determine whether we should code this trigger */
-		if (p->op == op
-		    && p->tr_tm == tr_tm
-		    && checkColumnOverlap(p->pColumns, pChanges)
-		    ) {
-			sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg,
-						    orconf, ignoreJump);
+	assert((op == TK_UPDATE) == (changes_list != NULL));
+
+	for (struct sql_trigger *p = trigger; p != NULL; p = p->next) {
+		/* Determine whether we should code trigger. */
+		if (p->op == op && p->tr_tm == tr_tm &&
+		    checkColumnOverlap(p->pColumns, changes_list)) {
+			vdbe_code_row_trigger_direct(parser, p, table, reg,
+						     orconf, ignore_jump);
 		}
 	}
 }
 
-/*
- * Triggers may access values stored in the old.* or new.* pseudo-table.
- * This function returns a 32-bit bitmask indicating which columns of the
- * old.* or new.* tables actually are used by triggers. This information
- * may be used by the caller, for example, to avoid having to load the entire
- * old.* record into memory when executing an UPDATE or DELETE command.
- *
- * Bit 0 of the returned mask is set if the left-most column of the
- * table may be accessed using an [old|new].<col> reference. Bit 1 is set if
- * the second leftmost column value is required, and so on. If there
- * are more than 32 columns in the table, and at least one of the columns
- * with an index greater than 32 may be accessed, 0xffffffff is returned.
- *
- * It is not possible to determine if the old.PK or new.PK column is
- * accessed by triggers. The caller must always assume that it is.
- *
- * Parameter isNew must be either 1 or 0. If it is 0, then the mask returned
- * applies to the old.* table. If 1, the new.* table.
- *
- * Parameter tr_tm must be a mask with one or both of the TRIGGER_BEFORE
- * and TRIGGER_AFTER bits set. Values accessed by BEFORE triggers are only
- * included in the returned mask if the TRIGGER_BEFORE bit is set in the
- * tr_tm parameter. Similarly, values accessed by AFTER triggers are only
- * included in the returned mask if the TRIGGER_AFTER bit is set in tr_tm.
- */
 u32
-sqlite3TriggerColmask(Parse * pParse,	/* Parse context */
-		      Trigger * pTrigger,	/* List of triggers on table pTab */
-		      ExprList * pChanges,	/* Changes list for any UPDATE OF triggers */
-		      int isNew,	/* 1 for new.* ref mask, 0 for old.* ref mask */
-		      int tr_tm,	/* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
-		      Table * pTab,	/* The table to code triggers from */
-		      int orconf	/* Default ON CONFLICT policy for trigger steps */
-    )
+sql_trigger_colmask(Parse *parser, struct sql_trigger *trigger,
+		    ExprList *changes_list, int new, int tr_tm,
+		    Table *table, int orconf)
 {
-	const int op = pChanges ? TK_UPDATE : TK_DELETE;
+	const int op = changes_list != NULL ? TK_UPDATE : TK_DELETE;
 	u32 mask = 0;
-	Trigger *p;
 
-	assert(isNew == 1 || isNew == 0);
-	for (p = pTrigger; p; p = p->pNext) {
+	assert(new == 1 || new == 0);
+	for (struct sql_trigger *p = trigger; p != NULL; p = p->next) {
 		if (p->op == op && (tr_tm & p->tr_tm)
-		    && checkColumnOverlap(p->pColumns, pChanges)
-		    ) {
-			TriggerPrg *pPrg;
-			pPrg = getRowTrigger(pParse, p, pTab, orconf);
-			if (pPrg) {
-				mask |= pPrg->aColmask[isNew];
-			}
+		    && checkColumnOverlap(p->pColumns, changes_list)) {
+			TriggerPrg *prg =
+				sql_row_trigger(parser, p, table, orconf);
+			if (prg != NULL)
+				mask |= prg->aColmask[new];
 		}
 	}
 
 	return mask;
 }
-
-#endif				/* !defined(SQLITE_OMIT_TRIGGER) */
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 10385eb..212adbc 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -106,7 +106,8 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	struct session *user_session = current_session();
 
 	bool is_view;		/* True when updating a view (INSTEAD OF trigger) */
-	Trigger *pTrigger;	/* List of triggers on pTab, if required */
+	/* List of triggers on pTab, if required. */
+	struct sql_trigger *trigger;
 	int tmask;		/* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
 	int newmask;		/* Mask of NEW.* columns accessed by BEFORE triggers */
 	int iEph = 0;		/* Ephemeral table holding all primary key values */
@@ -136,9 +137,9 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	/* Figure out if we have any triggers and if the table being
 	 * updated is a view.
 	 */
-	pTrigger = sqlite3TriggersExist(pTab, TK_UPDATE, pChanges, &tmask);
+	trigger = sql_triggers_exist(pTab, TK_UPDATE, pChanges, &tmask);
 	is_view = pTab->def->opts.is_view;
-	assert(pTrigger || tmask == 0);
+	assert(trigger != NULL || tmask == 0);
 
 	if (is_view &&
 	    sql_view_assign_cursors(pParse,pTab->def->opts.sql) != 0) {
@@ -269,11 +270,9 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	/* Allocate required registers. */
 	regOldPk = regNewPk = ++pParse->nMem;
 
-	if (chngPk || pTrigger || hasFK) {
+	if (chngPk != 0 || trigger != NULL || hasFK != 0) {
 		regOld = pParse->nMem + 1;
 		pParse->nMem += def->field_count;
-	}
-	if (chngPk || pTrigger || hasFK) {
 		regNewPk = ++pParse->nMem;
 	}
 	regNew = pParse->nMem + 1;
@@ -424,18 +423,18 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	 * then regNewPk is the same register as regOldPk, which is
 	 * already populated.
 	 */
-	assert(chngPk || pTrigger || hasFK || regOldPk == regNewPk);
+	assert(chngPk != 0 || trigger != NULL || hasFK != 0 ||
+	       regOldPk == regNewPk);
 
 
 	/* Compute the old pre-UPDATE content of the row being changed, if that
 	 * information is needed
 	 */
-	if (chngPk || hasFK || pTrigger) {
+	if (chngPk != 0 || hasFK != 0 || trigger != NULL) {
 		u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0);
-		oldmask |= sqlite3TriggerColmask(pParse,
-						 pTrigger, pChanges, 0,
-						 TRIGGER_BEFORE | TRIGGER_AFTER,
-						 pTab, on_error);
+		oldmask |= sql_trigger_colmask(pParse, trigger, pChanges, 0,
+					       TRIGGER_BEFORE | TRIGGER_AFTER,
+					       pTab, on_error);
 		for (i = 0; i < (int)def->field_count; i++) {
 			if (oldmask == 0xffffffff
 			    || (i < 32 && (oldmask & MASKBIT32(i)) != 0)
@@ -464,9 +463,8 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	 * may have modified them). So not loading those that are not going to
 	 * be used eliminates some redundant opcodes.
 	 */
-	newmask =
-	    sqlite3TriggerColmask(pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE,
-				  pTab, on_error);
+	newmask = sql_trigger_colmask(pParse, trigger, pChanges, 1,
+				      TRIGGER_BEFORE, pTab, on_error);
 	for (i = 0; i < (int)def->field_count; i++) {
 		if (i == pTab->iPKey) {
 			sqlite3VdbeAddOp2(v, OP_Null, 0, regNew + i);
@@ -498,7 +496,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	 */
 	if (tmask & TRIGGER_BEFORE) {
 		sqlite3TableAffinity(v, pTab, regNew);
-		sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
+		vdbe_code_row_trigger(pParse, trigger, TK_UPDATE, pChanges,
 				      TRIGGER_BEFORE, pTab, regOldPk,
 				      on_error, labelContinue);
 
@@ -611,7 +609,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 		sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1);
 	}
 
-	sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
+	vdbe_code_row_trigger(pParse, trigger, TK_UPDATE, pChanges,
 			      TRIGGER_AFTER, pTab, regOldPk, on_error,
 			      labelContinue);
 
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index cac1ad3..b0ab916 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -4733,7 +4733,7 @@ case OP_RenameTable: {
 	space = space_by_id(space_id);
 	assert(space);
 	/* Rename space op doesn't change triggers. */
-	struct Trigger *triggers = space->sql_triggers;
+	struct sql_trigger *triggers = space->sql_triggers;
 	zOldTableName = space_name(space);
 	assert(zOldTableName);
 	pTab = sqlite3HashFind(&db->pSchema->tblHash, zOldTableName);
@@ -4784,9 +4784,9 @@ case OP_RenameTable: {
 	 * due to lack of transactional DDL, but just do the best
 	 * effort.
 	 */
-	for (struct Trigger *trigger = triggers; trigger != NULL; ) {
+	for (struct sql_trigger *trigger = triggers; trigger != NULL; ) {
 		/* Store pointer as trigger will be destructed. */
-		struct Trigger *next_trigger = trigger->pNext;
+		struct sql_trigger *next_trigger = trigger->next;
 		rc = tarantoolSqlite3RenameTrigger(trigger->zName,
 						   zOldTableName, zNewTableName);
 		if (rc != SQLITE_OK) {
@@ -4849,8 +4849,6 @@ case OP_DropIndex: {
 	break;
 }
 
-#ifndef SQLITE_OMIT_TRIGGER
-
 /* Opcode: Program P1 P2 P3 P4 P5
  *
  * Execute the trigger program passed as P4 (type P4_SUBPROGRAM).
@@ -5006,8 +5004,6 @@ case OP_Param: {           /* out2 */
 	break;
 }
 
-#endif /* #ifndef SQLITE_OMIT_TRIGGER */
-
 #ifndef SQLITE_OMIT_FOREIGN_KEY
 /* Opcode: FkCounter P1 P2 * * *
  * Synopsis: fkctr[P1]+=P2
diff --git a/src/box/sql/vdbe.h b/src/box/sql/vdbe.h
index 45a89d9..03ae44e 100644
--- a/src/box/sql/vdbe.h
+++ b/src/box/sql/vdbe.h
@@ -302,9 +302,7 @@ UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(struct sqlite3 *,
 					       struct key_def *);
 int sql_vdbe_mem_alloc_region(Mem *, uint32_t);
 
-#ifndef SQLITE_OMIT_TRIGGER
 void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
-#endif
 
 /* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on
  * each VDBE opcode.
-- 
2.7.4







More information about the Tarantool-patches mailing list