[tarantool-patches] [PATCH v1 4/4] sql: move Triggers to server

Kirill Shcherbatov kshcherbatov at tarantool.org
Thu May 31 14:22:53 MSK 2018


Introduced sql_triggers field in space structure.
Changed parser logic to do not insert builded triggers, just only
to do parsing. All triggers insertions and deletions are operated
via on_replace_dd_trigger now.

Resolves #3273.
---
 src/box/alter.cc        |  57 +++++++++++
 src/box/space.h         |   2 +
 src/box/sql.c           |  48 ++-------
 src/box/sql.h           |  62 ++++++++++++
 src/box/sql/build.c     |   8 +-
 src/box/sql/insert.c    |   6 +-
 src/box/sql/sqliteInt.h |   2 -
 src/box/sql/tokenize.c  |  43 +++++++-
 src/box/sql/trigger.c   | 258 ++++++++++++++++++++++++++++--------------------
 src/box/sql/vdbe.c      |  58 +++--------
 src/box/sql/vdbe.h      |   1 -
 src/box/sql/vdbeaux.c   |   9 --
 12 files changed, 343 insertions(+), 211 deletions(-)

diff --git a/src/box/alter.cc b/src/box/alter.cc
index b62f8ad..6e4f15d 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -53,6 +53,7 @@
 #include "version.h"
 #include "sequence.h"
 #include "sql.h"
+#include "box.h"
 
 /**
  * chap-sha1 of empty string, i.e.
@@ -551,6 +552,9 @@ 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);
 	rlist_swap(&new_space->on_stmt_begin, &old_space->on_stmt_begin);
+	/** Copy SQL Triggers pointer. */
+	new_space->sql_triggers = old_space->sql_triggers;
+	old_space->sql_triggers = NULL;
 }
 
 /**
@@ -733,6 +737,20 @@ alter_space_commit(struct trigger *trigger, void *event)
 
 	trigger_run_xc(&on_alter_space, alter->new_space);
 
+	if (alter->new_space->sql_triggers != NULL &&
+	    strcmp(alter->new_space->def->name,
+		   alter->old_space->def->name) != 0) {
+		/*
+		 * The function below either changes the name of
+		 * all triggers, or does not change any of them.
+		 * It should be last action in alter_space_commit
+		 * as it is difficult to guarantee its rollback.
+		 */
+		if (sql_trigger_list_alter_table_name_transactional(
+			alter->new_space->sql_triggers,
+			alter->new_space->def->name) != 0)
+			diag_raise();
+	}
 	alter->new_space = NULL; /* for alter_space_delete(). */
 	/*
 	 * Delete the old version of the space, we are not
@@ -3100,6 +3118,45 @@ on_replace_dd_trigger(struct trigger * /* trigger */, void *event)
 {
 	struct txn *txn = (struct txn *) event;
 	txn_check_singlestatement_xc(txn, "Space _trigger");
+
+	struct txn_stmt *stmt = txn_current_stmt(txn);
+	if (stmt->old_tuple != NULL) {
+		uint32_t trigger_name_len;
+		const char *trigger_name_src =
+			tuple_field_str_xc(stmt->old_tuple, 0,
+					   &trigger_name_len);
+		/* Can't use static buff as TT_STATIC_BUF_LEN
+		 * is not enough for identifier max len test. */
+		char *trigger_name =
+			(char *)region_alloc_xc(&fiber()->gc, trigger_name_len);
+		sprintf(trigger_name, "%.*s", trigger_name_len,
+			trigger_name_src);
+		if (sql_trigger_delete_by_name(sql_get(), trigger_name) != 0)
+			diag_raise();
+	}
+	if (stmt->new_tuple != NULL) {
+		const char *space_opts =
+			tuple_field_with_type_xc(stmt->new_tuple, 1, MP_MAP);
+		struct space_opts opts;
+		struct region *region = &fiber()->gc;
+		space_opts_decode(&opts, space_opts, region);
+
+		struct Trigger *trigger;
+		if (sql_trigger_compile(sql_get(), opts.sql, &trigger) != 0)
+			diag_raise();
+		assert(trigger != NULL);
+		const char *table_name =
+			sql_trigger_get_table_name(trigger);
+		if (table_name == NULL)
+			diag_raise();
+		uint32_t space_id =
+			space_id_by_name(BOX_SPACE_ID, table_name,
+					 strlen(table_name));
+		if (space_id == BOX_ID_NIL)
+			diag_raise();
+		if (sql_trigger_insert(space_id, trigger) != 0)
+			diag_raise();
+	}
 }
 
 struct trigger alter_space_on_replace_space = {
diff --git a/src/box/space.h b/src/box/space.h
index b8d29ca..0413cd0 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -146,6 +146,8 @@ struct space {
 	struct rlist on_replace;
 	/** Triggers fired before space statement */
 	struct rlist on_stmt_begin;
+	/** SQL Trigger list. */
+	struct Trigger *sql_triggers;
 	/**
 	 * The number of *enabled* indexes in the space.
 	 *
diff --git a/src/box/sql.c b/src/box/sql.c
index eacb288..0ff6e73 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1219,9 +1219,6 @@ space_foreach_put_cb(struct space *space, void *udata)
 /* Load database schema from Tarantool. */
 void tarantoolSqlite3LoadSchema(InitData *init)
 {
-	box_iterator_t *it;
-	box_tuple_t *tuple;
-
 	sql_schema_put(
 		init, TARANTOOL_SYS_SCHEMA_NAME,
 		BOX_SCHEMA_ID, 0,
@@ -1290,42 +1287,6 @@ void tarantoolSqlite3LoadSchema(InitData *init)
 		init->rc = SQL_TARANTOOL_ERROR;
 		return;
 	}
-
-	/* Read _trigger */
-	it = box_index_iterator(BOX_TRIGGER_ID, 0, ITER_GE,
-				nil_key, nil_key + sizeof(nil_key));
-
-	if (it == NULL) {
-		init->rc = SQL_TARANTOOL_ITERATOR_FAIL;
-		return;
-	}
-
-	while (box_iterator_next(it, &tuple) == 0 && tuple != NULL) {
-		const char *field, *ptr;
-		char *name, *sql;
-		unsigned len;
-		assert(tuple_field_count(tuple) == 2);
-
-		field = tuple_field(tuple, 0);
-		assert (field != NULL);
-		ptr = mp_decode_str(&field, &len);
-		name = strndup(ptr, len);
-
-		field = tuple_field(tuple, 1);
-		assert (field != NULL);
-		mp_decode_array(&field);
-		ptr = mp_decode_str(&field, &len);
-		assert (strncmp(ptr, "sql", 3) == 0);
-
-		ptr = mp_decode_str(&field, &len);
-		sql = strndup(ptr, len);
-
-		sql_schema_put(init, name, 0, 0, sql);
-
-		free(name);
-		free(sql);
-	}
-	box_iterator_free(it);
 }
 
 /*********************************************************************
@@ -1733,6 +1694,15 @@ space_column_default_expr(uint32_t space_id, uint32_t fieldno)
 	return space->def->fields[fieldno].default_value_expr;
 }
 
+struct 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;
+}
+
 struct space_def *
 sql_ephemeral_space_def_new(Parse *parser, const char *name)
 {
diff --git a/src/box/sql.h b/src/box/sql.h
index 23021e5..f21b745 100644
--- a/src/box/sql.h
+++ b/src/box/sql.h
@@ -66,6 +66,7 @@ struct Expr;
 struct Parse;
 struct Select;
 struct Table;
+struct Trigger;
 
 /**
  * Perform parsing of provided expression. This is done by
@@ -84,6 +85,67 @@ sql_expr_compile(struct sqlite3 *db, const char *expr, int expr_len,
 		 struct Expr **result);
 
 /**
+ * Perform parsing of provided SQL request and construct trigger AST.
+ * @param db SQL context handle.
+ * @param sql request to parse.
+ * @param[out] trigger Result: AST structure.
+ *
+ * @retval Error code if any.
+ */
+int
+sql_trigger_compile(struct sqlite3 *db, const char *sql,
+		    struct Trigger **trigger);
+
+/**
+ * Remove a trigger from the hash tables of the sqlite* pointer.
+ * @param db SQL handle.
+ * @param trigger_name to delete.
+ *
+ * @retval Error code if any.
+ */
+int
+sql_trigger_delete_by_name(struct sqlite3 *db, const char *trigger_name);
+
+/**
+ * Get server triggers list by space_id.
+ * @param space_id Space ID.
+ * @retval NULL on error.
+ * @param not NULL on success.
+ */
+struct Trigger *
+space_trigger_list(uint32_t space_id);
+
+/**
+ * Perform insert trigger in appropriate space.
+ * @param space_id id of the space to append trigger.
+ * @param trigger object to append.
+ *
+ * @retval Error code if any.
+ */
+int
+sql_trigger_insert(uint32_t space_id, struct Trigger *trigger);
+
+/**
+ * Get table name of specified trigger.
+ * @param trigger containing a table name.
+ * @param new_table_name with new_name (optional).
+ * @retval table name string.
+ */
+const char *
+sql_trigger_get_table_name(struct Trigger *trigger);
+
+/**
+ * Rename specified trigger list.
+ * @param trigger containing a table name.
+ * @param new_table_name with new_name (optional).
+ *
+ * @retval Error code if any.
+ */
+int
+sql_trigger_list_alter_table_name_transactional(struct Trigger *trigger,
+						const char *new_table_name);
+
+/**
  * Store duplicate of a parsed expression into @a parser.
  * @param parser Parser context.
  * @param select Select to extract from.
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 9cd1cde..475d8aa 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -2289,16 +2289,14 @@ sql_code_drop_table(struct Parse *parse_context, struct space *space,
 	/*
 	 * Drop all triggers associated with the table being
 	 * dropped. Code is generated to remove entries from
-	 * _trigger. OP_DropTrigger will remove it from internal
-	 * SQL structures.
+	 * _trigger. on_replace_dd_trigger will remove it from
+	 * internal SQL structures.
 	 *
 	 * Do not account triggers deletion - they will be
 	 * accounted in DELETE from _space below.
 	 */
 	parse_context->nested++;
-	Table *table = sqlite3HashFind(&parse_context->db->pSchema->tblHash,
-				       space->def->name);
-	struct Trigger *trigger = table->pTrigger;
+	struct Trigger *trigger = space->sql_triggers;
 	while (trigger != NULL) {
 		sqlite3DropTriggerPtr(parse_context, trigger);
 		trigger = trigger->pNext;
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 59c61c7..023e6b0 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -1766,9 +1766,9 @@ xferOptimization(Parse * pParse,	/* Parser context */
 		 */
 		return 0;
 	}
-	if (pDest->pTrigger) {
-		return 0;	/* tab1 must not have triggers */
-	}
+	/* The pDest must not have triggers. */
+	if (space_trigger_list(pDest->def->id) != NULL)
+		return 0;
 	if (onError == ON_CONFLICT_ACTION_DEFAULT) {
 		if (pDest->iPKey >= 0)
 			onError = pDest->keyConf;
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index b23cb1a..39393c5 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -1935,7 +1935,6 @@ struct Table {
 #ifndef SQLITE_OMIT_ALTERTABLE
 	int addColOffset;	/* Offset in CREATE TABLE stmt to add a new column */
 #endif
-	Trigger *pTrigger;	/* List of triggers stored in pSchema */
 	Schema *pSchema;	/* Schema that contains this table */
 	Table *pNextZombie;	/* Next on the Parse.pZombieTab list */
 	/** Space definition with Tarantool metadata. */
@@ -4028,7 +4027,6 @@ TriggerStep *sqlite3TriggerUpdateStep(sqlite3 *, Token *, ExprList *, Expr *,
 				      u8);
 TriggerStep *sqlite3TriggerDeleteStep(sqlite3 *, Token *, Expr *);
 void sqlite3DeleteTrigger(sqlite3 *, Trigger *);
-void sqlite3UnlinkAndDeleteTrigger(sqlite3 *, const char *);
 u32 sqlite3TriggerColmask(Parse *, Trigger *, ExprList *, int, int, Table *,
 			  int);
 #define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p))
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index 59df75f..24db578 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -39,6 +39,7 @@
 #include <stdlib.h>
 #include <unicode/utf8.h>
 #include <unicode/uchar.h>
+#include "box/schema.h"
 
 #include "say.h"
 #include "sqliteInt.h"
@@ -528,7 +529,10 @@ sqlite3RunParser(Parse * pParse, const char *zSql, char **pzErrMsg)
 
 	if (pParse->pWithToFree)
 		sqlite3WithDelete(db, pParse->pWithToFree);
-	sqlite3DeleteTrigger(db, pParse->pNewTrigger);
+	/* Trigger is exporting with pNewTrigger field when
+	 * parse_only flag is set. */
+	if (!pParse->parse_only)
+		sqlite3DeleteTrigger(db, pParse->pNewTrigger);
 	sqlite3DbFree(db, pParse->pVList);
 	while (pParse->pZombieTab) {
 		Table *p = pParse->pZombieTab;
@@ -564,3 +568,40 @@ sql_expr_compile(sqlite3 *db, const char *expr, int expr_len,
 	sql_parser_destroy(&parser);
 	return 0;
 }
+
+int
+sql_trigger_compile(struct sqlite3 *db, const char *sql,
+		    struct Trigger **trigger)
+{
+	struct Parse parser;
+	sql_parser_create(&parser, db);
+	parser.parse_only = true;
+	char *unused;
+	if (sqlite3RunParser(&parser, sql, &unused) != SQLITE_OK) {
+		diag_set(ClientError, ER_SQL_EXECUTE, sql);
+		return -1;
+	}
+	*trigger = parser.pNewTrigger;
+	sql_parser_destroy(&parser);
+	return 0;
+}
+
+
+int
+sql_trigger_insert(uint32_t space_id, struct Trigger *trigger)
+{
+	struct space *space = space_cache_find(space_id);
+	assert(space != NULL);
+	struct Hash *pHash = &trigger->pSchema->trigHash;
+	char *zName = trigger->zName;
+	void *ret = sqlite3HashInsert(pHash, zName, trigger);
+	if (ret != NULL) {
+		/* Triggers couldn't present in hash.
+		 * So this is definitely a memory error. */
+		diag_set(OutOfMemory, 0, "sqlite3HashInsert", "hash");
+		return -1;
+	}
+	trigger->pNext = space->sql_triggers;
+	space->sql_triggers = trigger;
+	return 0;
+}
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index ea35211..00f161e 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -33,6 +33,9 @@
  * This file contains the implementation for TRIGGERs
  */
 
+#include "box/box.h"
+#include "box/tuple.h"
+#include "box/schema.h"
 #include "sqliteInt.h"
 #include "tarantoolInt.h"
 #include "vdbeInt.h"
@@ -81,7 +84,8 @@ sqlite3BeginTrigger(Parse * pParse,	/* The parse context of the CREATE TRIGGER s
     )
 {
 	Trigger *pTrigger = 0;	/* The new trigger */
-	Table *pTab;		/* Table that the trigger fires off of */
+	/* Table that the trigger fires off of. */
+	struct Table *table = NULL;
 	char *zName = 0;	/* Name of the trigger */
 	sqlite3 *db = pParse->db;	/* The database connection */
 	DbFixer sFix;		/* State vector for the DB fixer */
@@ -99,60 +103,65 @@ sqlite3BeginTrigger(Parse * pParse,	/* The parse context of the CREATE TRIGGER s
 	assert(op == TK_INSERT || op == TK_UPDATE || op == TK_DELETE);
 	assert(op > 0 && op < 0xff);
 
-	if (!pTableName || db->mallocFailed) {
+	if (!pTableName || db->mallocFailed)
 		goto trigger_cleanup;
-	}
 
 	/* Ensure the table name matches database name and that the table exists */
 	if (db->mallocFailed)
 		goto trigger_cleanup;
 	assert(pTableName->nSrc == 1);
 	sqlite3FixInit(&sFix, pParse, "trigger", pName);
-	if (sqlite3FixSrcList(&sFix, pTableName)) {
-		goto trigger_cleanup;
-	}
-	pTab = sql_list_lookup_table(pParse, pTableName);
-	if (!pTab) {
+	if (sqlite3FixSrcList(&sFix, pTableName))
 		goto trigger_cleanup;
-	}
 
-	/* Check that the trigger name is not reserved and that no trigger of the
-	 * specified name exists
-	 */
 	zName = sqlite3NameFromToken(db, pName);
-	if (!zName || SQLITE_OK != sqlite3CheckIdentifierName(pParse, zName)) {
+	if (zName == NULL)
 		goto trigger_cleanup;
-	}
-	if (sqlite3HashFind(&(db->pSchema->trigHash), zName)) {
-		if (!noErr) {
-			sqlite3ErrorMsg(pParse, "trigger %s already exists",
-					zName);
-		} else {
-			assert(!db->init.busy);
+
+	/* FIXME: Move all checks in VDBE #3435. */
+	if (!pParse->parse_only) {
+		if (sqlite3CheckIdentifierName(pParse, zName) != SQLITE_OK)
+			goto trigger_cleanup;
+
+		table = sql_list_lookup_table(pParse, pTableName);
+		if (table == NULL)
+			goto trigger_cleanup;
+
+		if (sqlite3HashFind(&(db->pSchema->trigHash), zName)) {
+			if (!noErr) {
+				sqlite3ErrorMsg(pParse,
+						"trigger %s already exists",
+						zName);
+			} else {
+				assert(!db->init.busy);
+			}
+			goto trigger_cleanup;
 		}
-		goto trigger_cleanup;
-	}
 
-	/* Do not create a trigger on a system table */
-	if (sqlite3StrNICmp(pTab->def->name, "sqlite_", 7) == 0) {
-		sqlite3ErrorMsg(pParse,
-				"cannot create trigger on system table");
-		goto trigger_cleanup;
-	}
+		/* Do not create a trigger on a system table */
+		if (sqlite3StrNICmp(table->def->name, "sqlite_", 7) == 0) {
+			sqlite3ErrorMsg(pParse,
+					"cannot create trigger on system table");
+			goto trigger_cleanup;
+		}
 
-	/* INSTEAD of triggers are only for views and views only support INSTEAD
-	 * of triggers.
-	 */
-	if (space_is_view(pTab) && tr_tm != TK_INSTEAD) {
-		sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S",
-				(tr_tm == TK_BEFORE) ? "BEFORE" : "AFTER",
-				pTableName, 0);
-		goto trigger_cleanup;
-	}
-	if (!space_is_view(pTab) && tr_tm == TK_INSTEAD) {
-		sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF"
-				" trigger on table: %S", pTableName, 0);
-		goto trigger_cleanup;
+		/* INSTEAD of triggers are only for views and
+		 * views only support INSTEAD of triggers.
+		 */
+		if (table->def->opts.is_view && tr_tm != TK_INSTEAD) {
+			sqlite3ErrorMsg(pParse,
+					"cannot create %s trigger on view: %S",
+					(tr_tm == TK_BEFORE) ?
+					"BEFORE" : "AFTER",
+					pTableName, 0);
+			goto trigger_cleanup;
+		}
+		if (!table->def->opts.is_view && tr_tm == TK_INSTEAD) {
+			sqlite3ErrorMsg(pParse,
+					"cannot create INSTEAD OF trigger "
+					"on table: %S", pTableName, 0);
+			goto trigger_cleanup;
+		}
 	}
 
 	/* INSTEAD OF triggers can only appear on views and BEFORE triggers
@@ -160,9 +169,8 @@ sqlite3BeginTrigger(Parse * pParse,	/* The parse context of the CREATE TRIGGER s
 	 * INSTEAD OF trigger into a BEFORE trigger.  It simplifies code
 	 * elsewhere.
 	 */
-	if (tr_tm == TK_INSTEAD) {
+	if (tr_tm == TK_INSTEAD)
 		tr_tm = TK_BEFORE;
-	}
 
 	/* Build the Trigger object */
 	pTrigger = (Trigger *) sqlite3DbMallocZero(db, sizeof(Trigger));
@@ -170,13 +178,22 @@ sqlite3BeginTrigger(Parse * pParse,	/* The parse context of the CREATE TRIGGER s
 		goto trigger_cleanup;
 	pTrigger->zName = zName;
 	zName = 0;
-	pTrigger->table = sqlite3DbStrDup(db, pTableName->a[0].zName);
+	pTrigger->table = strdup(pTableName->a[0].zName);
+	if (pTrigger->table == NULL) {
+		diag_set(OutOfMemory, strlen(pTableName->a[0].zName), "strdup",
+			 "pTrigger->table");
+		pParse->rc = SQL_TARANTOOL_ERROR;
+		goto trigger_cleanup;
+	}
 	pTrigger->pSchema = db->pSchema;
-	pTrigger->pTabSchema = pTab->pSchema;
+	pTrigger->pTabSchema = db->pSchema;
 	pTrigger->op = (u8) op;
 	pTrigger->tr_tm = tr_tm == TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER;
 	pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE);
 	pTrigger->pColumns = sqlite3IdListDup(db, pColumns);
+	if ((pWhen != NULL && pTrigger->pWhen == NULL) ||
+		(pColumns != NULL && pTrigger->pColumns == NULL))
+		goto trigger_cleanup;
 	assert(pParse->pNewTrigger == 0);
 	pParse->pNewTrigger = pTrigger;
 
@@ -210,7 +227,7 @@ sqlite3FinishTrigger(Parse * pParse,	/* Parser context */
 	DbFixer sFix;		/* Fixer object */
 	Token nameToken;	/* Trigger name for error reporting */
 
-	pParse->pNewTrigger = 0;
+	pParse->pNewTrigger = NULL;
 	if (NEVER(pParse->nErr) || !pTrig)
 		goto triggerfinish_cleanup;
 	zName = pTrig->zName;
@@ -227,10 +244,9 @@ sqlite3FinishTrigger(Parse * pParse,	/* Parser context */
 		goto triggerfinish_cleanup;
 	}
 
-	/* if we are not initializing,
-	 * generate byte code to insert a new trigger into Tarantool.
-	 */
-	if (!db->init.busy) {
+	/* Generate byte code to insert a new trigger into
+	 * Tarantool for non-parsig mode or export trigger. */
+	if (!pParse->parse_only) {
 		Vdbe *v;
 		int zOptsSz;
 		Table *pSysTrigger;
@@ -293,32 +309,10 @@ sqlite3FinishTrigger(Parse * pParse,	/* Parser context */
 		pParse->nMem += 2;
 
 		sql_set_multi_write(pParse, false);
-		sqlite3VdbeAddOp4(v,
-				  OP_String8, 0, iFirstCol, 0,
-				  zName, P4_STATIC);
-
-		sqlite3VdbeAddOp4(v,
-				  OP_String8, 0, iFirstCol + 1, 0,
-				  zSql, P4_DYNAMIC);
 		sqlite3ChangeCookie(pParse);
-		sqlite3VdbeAddParseSchema3Op(v, iFirstCol);
-	}
-
-	if (db->init.busy) {
-		Trigger *pLink = pTrig;
-		Hash *pHash = &db->pSchema->trigHash;
-		pTrig = sqlite3HashInsert(pHash, zName, pTrig);
-		if (pTrig) {
-			sqlite3OomFault(db);
-		} else if (pLink->pSchema == pLink->pTabSchema) {
-			Table *pTab;
-			pTab =
-			    sqlite3HashFind(&pLink->pTabSchema->tblHash,
-					    pLink->table);
-			assert(pTab != 0);
-			pLink->pNext = pTab->pTrigger;
-			pTab->pTrigger = pLink;
-		}
+	} else {
+		pParse->pNewTrigger = pTrig;
+		pTrig = NULL;
 	}
 
  triggerfinish_cleanup:
@@ -329,7 +323,7 @@ sqlite3FinishTrigger(Parse * pParse,	/* Parser context */
 		   alloc for it either wasn't called at all or failed.  */
 	}
 	sqlite3DeleteTrigger(db, pTrig);
-	assert(!pParse->pNewTrigger);
+	assert(!pParse->pNewTrigger || pParse->parse_only);
 	sqlite3DeleteTriggerStep(db, pStepList);
 }
 
@@ -481,7 +475,7 @@ sqlite3DeleteTrigger(sqlite3 * db, Trigger * pTrigger)
 		return;
 	sqlite3DeleteTriggerStep(db, pTrigger->step_list);
 	sqlite3DbFree(db, pTrigger->zName);
-	sqlite3DbFree(db, pTrigger->table);
+	free(pTrigger->table);
 	sql_expr_delete(db, pTrigger->pWhen, false);
 	sqlite3IdListDelete(db, pTrigger->pColumns);
 	sqlite3DbFree(db, pTrigger);
@@ -568,34 +562,35 @@ sqlite3DropTriggerPtr(Parse * pParse, Trigger * pTrigger)
 			sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE);
 
 		sqlite3ChangeCookie(pParse);
-		sqlite3VdbeAddOp4(v, OP_DropTrigger, 0, 0, 0, pTrigger->zName,
-				  0);
 	}
 }
 
-/*
- * Remove a trigger from the hash tables of the sqlite* pointer.
- */
-void
-sqlite3UnlinkAndDeleteTrigger(sqlite3 * db, const char *zName)
+int
+sql_trigger_delete_by_name(struct sqlite3 *db, const char *trigger_name)
 {
-	Trigger *pTrigger;
-	Hash *pHash;
 	struct session *user_session = current_session();
 
-	pHash = &(db->pSchema->trigHash);
-	pTrigger = sqlite3HashInsert(pHash, zName, 0);
-	if (ALWAYS(pTrigger)) {
-		if (pTrigger->pSchema == pTrigger->pTabSchema) {
-			Table *pTab = tableOfTrigger(pTrigger);
-			Trigger **pp;
-			for (pp = &pTab->pTrigger; *pp != pTrigger;
-			     pp = &((*pp)->pNext)) ;
+	struct Hash *hash = &(db->pSchema->trigHash);
+	struct Trigger *trigger = sqlite3HashInsert(hash, trigger_name, NULL);
+	assert(trigger != NULL);
+
+	if (trigger->pSchema == trigger->pTabSchema) {
+		uint32_t space_id = space_id_by_name(BOX_SPACE_ID,
+			trigger->table, strlen(trigger->table));
+		struct space *space = space_by_id(space_id);
+		/* Space could be already deleted. */
+		if (space != NULL) {
+			struct Trigger **pp;
+			for (pp = &space->sql_triggers;
+			     *pp != trigger;
+			     pp = &((*pp)->pNext));
 			*pp = (*pp)->pNext;
 		}
-		sqlite3DeleteTrigger(db, pTrigger);
-		user_session->sql_flags |= SQLITE_InternChanges;
 	}
+
+	sqlite3DeleteTrigger(db, trigger);
+	user_session->sql_flags |= SQLITE_InternChanges;
+	return 0;
 }
 
 /*
@@ -634,22 +629,18 @@ sqlite3TriggersExist(Table * pTab,	/* The table the contains the triggers */
     )
 {
 	int mask = 0;
-	Trigger *pList = 0;
-	Trigger *p;
+	struct Trigger *trigger_list = NULL;
 	struct session *user_session = current_session();
-
-	if ((user_session->sql_flags & SQLITE_EnableTrigger) != 0) {
-		pList = pTab->pTrigger;
-	}
-	for (p = pList; p; p = p->pNext) {
-		if (p->op == op && checkColumnOverlap(p->pColumns, pChanges)) {
+	if ((user_session->sql_flags & SQLITE_EnableTrigger) != 0)
+		trigger_list = space_trigger_list(pTab->def->id);
+	struct Trigger *p;
+	for (p = trigger_list; p; p = p->pNext) {
+		if (p->op == op && checkColumnOverlap(p->pColumns, pChanges))
 			mask |= p->tr_tm;
-		}
 	}
-	if (pMask) {
+	if (pMask)
 		*pMask = mask;
-	}
-	return (mask ? pList : 0);
+	return (mask ? trigger_list : 0);
 }
 
 /*
@@ -1155,4 +1146,55 @@ sqlite3TriggerColmask(Parse * pParse,	/* Parse context */
 	return mask;
 }
 
+const char *
+sql_trigger_get_table_name(struct Trigger *trigger)
+{
+	return trigger->table;
+}
+
+int
+sql_trigger_list_alter_table_name_transactional(struct Trigger *trigger,
+						const char *new_table_name)
+{
+	struct names_list_t {
+	    char *name;
+	    struct names_list_t *next;
+	} *names_list = NULL;
+
+	int rc = 0;
+	for (struct Trigger *t = trigger; t != NULL; t = t->pNext) {
+		struct names_list_t *node =
+			region_alloc(&fiber()->gc, sizeof(*node));
+		if (rc |= (node == NULL)) {
+			diag_set(OutOfMemory, sizeof(*node), "region_alloc",
+				 "node");
+			break;
+		}
+		node->name = strdup(new_table_name);
+		if (rc |= (node->name == NULL)) {
+			diag_set(OutOfMemory, strlen(new_table_name), "strdup",
+				 "node->name");
+			break;
+		}
+		node->next = names_list;
+		names_list = node;
+	}
+	if (rc != 0)
+		goto error;
+
+	for (struct Trigger *t = trigger; t != NULL; t = t->pNext) {
+		free(t->table);
+		t->table = names_list->name;
+		names_list = names_list->next;
+	}
+	return 0;
+
+error:
+	while (names_list != NULL) {
+		free(names_list->name);
+		names_list = names_list->next;
+	}
+	return -1;
+}
+
 #endif				/* !defined(SQLITE_OMIT_TRIGGER) */
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 3fe5875..f60c122 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -4693,36 +4693,7 @@ case OP_ParseSchema2: {
  * in database P2
  */
 case OP_ParseSchema3: {
-	InitData initData;
-	Mem *pRec;
-	char zPgnoBuf[16];
-	char *argv[4] = {NULL, zPgnoBuf, NULL, NULL};
-	assert(db->pSchema != NULL);
-
-	initData.db = db;
-	initData.pzErrMsg = &p->zErrMsg;
-
-	assert(db->init.busy==0);
-	db->init.busy = 1;
-	initData.rc = SQLITE_OK;
-	assert(!db->mallocFailed);
-
-	pRec = &aMem[pOp->p1];
-	argv[0] = pRec[0].z;
-	argv[1] = "0";
-	argv[2] = pRec[1].z;
-	sqlite3InitCallback(&initData, 3, argv, NULL);
-
-	rc = initData.rc;
-	db->init.busy = 0;
-
-	if (rc) {
-		sqlite3ResetAllSchemasOfConnection(db);
-		if (rc==SQLITE_NOMEM) {
-			goto no_mem;
-		}
-		goto abort_due_to_error;
-	}
+	unreachable();
 	break;
 }
 
@@ -4745,7 +4716,6 @@ case OP_RenameTable: {
 	const char *zNewTableName;
 	Table *pTab;
 	FKey *pFKey;
-	Trigger *pTrig;
 	int iRootPage;
 	InitData initData;
 	char *argv[4] = {NULL, NULL, NULL, NULL};
@@ -4758,11 +4728,11 @@ case OP_RenameTable: {
 	assert(zOldTableName);
 	pTab = sqlite3HashFind(&db->pSchema->tblHash, zOldTableName);
 	assert(pTab);
-	pTrig = pTab->pTrigger;
 	iRootPage = pTab->tnum;
 	zNewTableName = pOp->p4.z;
 	zOldTableName = sqlite3DbStrNDup(db, zOldTableName,
 					 sqlite3Strlen30(zOldTableName));
+
 	rc = tarantoolSqlite3RenameTable(pTab->tnum, zNewTableName,
 					 &zSqlStmt);
 	if (rc) goto abort_due_to_error;
@@ -4799,19 +4769,21 @@ case OP_RenameTable: {
 		goto abort_due_to_error;
 	}
 
-	pTab = sqlite3HashFind(&db->pSchema->tblHash, zNewTableName);
-	pTab->pTrigger = pTrig;
+	space = space_by_id(space_id);
+	assert(space != NULL);
 
-	/* Rename all trigger created on this table.*/
-	for (; pTrig; pTrig = pTrig->pNext) {
-		sqlite3DbFree(db, pTrig->table);
-		pTrig->table = sqlite3DbStrNDup(db, zNewTableName,
-						sqlite3Strlen30(zNewTableName));
-		pTrig->pTabSchema = pTab->pSchema;
-		rc = tarantoolSqlite3RenameTrigger(pTrig->zName,
+	/* Rename all trigger created on this table. */
+	for (struct Trigger *trigger = space->sql_triggers; trigger != NULL; ) {
+		struct Trigger *next_trigger = trigger->pNext;
+		rc = tarantoolSqlite3RenameTrigger(trigger->zName,
 						   zOldTableName, zNewTableName);
-		if (rc) goto abort_due_to_error;
+		if (rc != SQLITE_OK) {
+			sqlite3ResetAllSchemasOfConnection(db);
+			goto abort_due_to_error;
+		}
+		trigger = next_trigger;
 	}
+
 	sqlite3DbFree(db, (void*)zOldTableName);
 	sqlite3DbFree(db, (void*)zSqlStmt);
 	break;
@@ -4866,7 +4838,7 @@ case OP_DropIndex: {
  * schema consistent with what is on disk.
  */
 case OP_DropTrigger: {
-	sqlite3UnlinkAndDeleteTrigger(db, pOp->p4.z);
+	unreachable();
 	break;
 }
 #ifndef SQLITE_OMIT_TRIGGER
diff --git a/src/box/sql/vdbe.h b/src/box/sql/vdbe.h
index 68d542c..77fa41a 100644
--- a/src/box/sql/vdbe.h
+++ b/src/box/sql/vdbe.h
@@ -238,7 +238,6 @@ void sqlite3VdbeVerifyNoResultRow(Vdbe * p);
 VdbeOp *sqlite3VdbeAddOpList(Vdbe *, int nOp, VdbeOpList const *aOp,
 			     int iLineno);
 void sqlite3VdbeAddParseSchema2Op(Vdbe * p, int, int);
-void sqlite3VdbeAddParseSchema3Op(Vdbe * p, int);
 void sqlite3VdbeAddRenameTableOp(Vdbe * p, int, char *);
 void sqlite3VdbeChangeOpcode(Vdbe *, u32 addr, u8);
 void sqlite3VdbeChangeP1(Vdbe *, u32 addr, int P1);
diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
index 63f1978..7a5ac6f 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -410,15 +410,6 @@ sqlite3VdbeAddParseSchema2Op(Vdbe * p, int iRec, int n)
 	sqlite3VdbeAddOp3(p, OP_ParseSchema2, iRec, n, 0);
 }
 
-/*
- * Add an OP_ParseSchema3 opcode which in turn will create a trigger
- */
-void
-sqlite3VdbeAddParseSchema3Op(Vdbe * p, int iRec)
-{
-	sqlite3VdbeAddOp2(p, OP_ParseSchema3, iRec, 0);
-}
-
 void
 sqlite3VdbeAddRenameTableOp(Vdbe * p, int iTab, char* zNewName)
 {
-- 
2.7.4





More information about the Tarantool-patches mailing list