Tarantool development patches archive
 help / color / mirror / Atom feed
* [Tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space
@ 2019-10-14 16:03 Kirill Shcherbatov
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 1/9] sql: remove redundant pointer in TriggerStep Kirill Shcherbatov
                   ` (9 more replies)
  0 siblings, 10 replies; 31+ messages in thread
From: Kirill Shcherbatov @ 2019-10-14 16:03 UTC (permalink / raw)
  To: tarantool-patches, tarantool-patches, kostja.osipov, korablev

This patchset reworks structures that represent SQL triggers to inherent
them from base trigger class. It is an important step to introduce a
LUA persistent trigger in Tarantool as well as to support SQL triggers
evaluation on the server side.

In scope of this patchset the _triggers space was extended with a new fields.
The _trigger space updated format is:
[<name> STRING, <space_id> UINT32, <opts> MAP, <language> STR,
 <type> STR, <event_manipulation> STR,
 <action_timing> STR, <code> STR]

Now trigger is produced mostly by tuple-based metadata definition instead of
naive SQL request parsing.

Branch: http://github.com/tarantool/tarantool/tree/kshch/gh-4343-persistent-triggers
Issue: https://github.com/tarantool/tarantool/issues/4343

Kirill Shcherbatov (9):
  sql: remove redundant pointer in TriggerStep
  box: rename struct trigger to lua_trigger
  box: introduce trigger_event_manipulation enum
  box: introduce trigger_action_timing enum
  sql: use rlist to organize triggers in a list
  sql: rework CREATE TABLE rule in parser
  sql: wrap all ASTs in sql_trigger_expr structure
  sql: inherit sql_trigger from a new trigger class
  schema: rework _trigger system space

 src/box/CMakeLists.txt                        |   2 +
 src/box/alter.cc                              | 438 +++++++++--------
 src/box/alter.h                               |  34 +-
 src/box/applier.cc                            |  28 +-
 src/box/applier.h                             |   2 +-
 src/box/bootstrap.snap                        | Bin 5934 -> 5981 bytes
 src/box/ck_constraint.c                       |   2 +-
 src/box/ck_constraint.h                       |   4 +-
 src/box/errcode.h                             |   1 +
 src/box/error.cc                              |   2 +-
 src/box/fk_constraint.c                       |   6 +-
 src/box/iproto.cc                             |   4 +-
 src/box/lua/call.c                            |   4 +-
 src/box/lua/sequence.c                        |   4 +-
 src/box/lua/space.cc                          |   4 +-
 src/box/lua/tuple.c                           |   2 +-
 src/box/lua/upgrade.lua                       |  16 +
 src/box/memtx_space.c                         |   8 +-
 src/box/recovery.cc                           |   2 +-
 src/box/relay.cc                              |   6 +-
 src/box/replication.cc                        |   8 +-
 src/box/replication.h                         |   4 +-
 src/box/schema.cc                             |   2 +-
 src/box/schema_def.h                          |   5 +
 src/box/session.cc                            |   4 +-
 src/box/session.h                             |   4 +-
 src/box/space.c                               |  11 +-
 src/box/space.h                               |   4 +-
 src/box/sql.c                                 | 105 ++--
 src/box/sql.h                                 | 125 +++--
 src/box/sql/build.c                           |  97 +++-
 src/box/sql/delete.c                          |  27 +-
 src/box/sql/fk_constraint.c                   | 109 +++--
 src/box/sql/insert.c                          |  42 +-
 src/box/sql/parse.y                           |  26 +-
 src/box/sql/parse_def.h                       |  29 +-
 src/box/sql/prepare.c                         |   7 +-
 src/box/sql/resolve.c                         |  15 +-
 src/box/sql/sqlInt.h                          | 111 ++---
 src/box/sql/tokenize.c                        |  16 +-
 src/box/sql/trigger.c                         | 448 ++++++------------
 src/box/sql/update.c                          |  45 +-
 src/box/sql/vdbe.c                            |  18 +-
 src/box/trigger.c                             |  73 +++
 src/box/trigger.h                             |  80 ++++
 src/box/trigger_def.c                         | 131 +++++
 src/box/trigger_def.h                         | 194 ++++++++
 src/box/txn.c                                 |   8 +-
 src/box/txn.h                                 |  14 +-
 src/box/vinyl.c                               |  32 +-
 src/box/vy_lsm.c                              |   2 +-
 src/box/vy_scheduler.c                        |   4 +-
 src/box/vy_tx.c                               |   2 +-
 src/lib/core/cbus.c                           |   2 +-
 src/lib/core/fiber.c                          |   6 +-
 src/lib/core/trigger.cc                       |   6 +-
 src/lib/core/trigger.h                        |  20 +-
 src/lib/swim/swim.c                           |   2 +-
 src/lua/swim.c                                |   2 +-
 src/lua/trigger.c                             |   6 +-
 src/lua/trigger.h                             |   2 +-
 src/lua/utils.h                               |   2 +-
 src/main.cc                                   |   4 +-
 test/box-py/bootstrap.result                  |   5 +-
 test/box/access_misc.result                   | 139 +++---
 test/box/misc.result                          |   1 +
 test/sql/ddl.result                           |   6 +-
 test/sql/ddl.test.lua                         |   6 +-
 .../gh2141-delete-trigger-drop-table.result   |  36 +-
 .../gh2141-delete-trigger-drop-table.test.lua |   4 +-
 test/sql/persistency.result                   |  36 +-
 test/sql/persistency.test.lua                 |   8 +-
 test/sql/triggers.result                      |  91 ++--
 test/sql/triggers.test.lua                    |  30 +-
 test/sql/upgrade.result                       |  16 +-
 test/sql/upgrade.test.lua                     |   4 +-
 test/unit/CMakeLists.txt                      |   3 +-
 test/unit/cbus.c                              |   6 +-
 test/unit/swim.c                              |  14 +-
 test/unit/swim_test_utils.c                   |   6 +-
 80 files changed, 1695 insertions(+), 1139 deletions(-)
 create mode 100644 src/box/trigger.c
 create mode 100644 src/box/trigger.h
 create mode 100644 src/box/trigger_def.c
 create mode 100644 src/box/trigger_def.h

-- 
2.23.0

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [Tarantool-patches] [PATCH v1 1/9] sql: remove redundant pointer in TriggerStep
  2019-10-14 16:03 [Tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space Kirill Shcherbatov
@ 2019-10-14 16:03 ` Kirill Shcherbatov
  2019-10-15 15:35   ` [Tarantool-patches] [tarantool-patches] " Nikita Pettik
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 2/9] box: rename struct trigger to lua_trigger Kirill Shcherbatov
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 31+ messages in thread
From: Kirill Shcherbatov @ 2019-10-14 16:03 UTC (permalink / raw)
  To: tarantool-patches, tarantool-patches, kostja.osipov, korablev

The trigger pointer in the sql_trigger structure is a dead code
now, so it could be deleted.

Needed for #4343
---
 src/box/sql/fk_constraint.c | 1 -
 src/box/sql/sqlInt.h        | 2 --
 src/box/sql/trigger.c       | 1 -
 3 files changed, 4 deletions(-)

diff --git a/src/box/sql/fk_constraint.c b/src/box/sql/fk_constraint.c
index 4717677f7..482220a95 100644
--- a/src/box/sql/fk_constraint.c
+++ b/src/box/sql/fk_constraint.c
@@ -895,7 +895,6 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def,
 		step->op = TK_UPDATE;
 	}
 
-	step->trigger = trigger;
 	if (is_update) {
 		fk->on_update_trigger = trigger;
 		trigger->op = TK_UPDATE;
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 35fc81dfb..1b6d92cb1 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -2366,8 +2366,6 @@ struct sql_trigger {
 struct TriggerStep {
 	u8 op;			/* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */
 	u8 orconf;		/* ON_CONFLICT_ACTION_ROLLBACK etc. */
-	/** 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 */
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index d746ef893..0c387bc3b 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -170,7 +170,6 @@ sql_trigger_finish(struct Parse *parse, struct TriggerStep *step_list,
 	char *trigger_name = trigger->zName;
 	trigger->step_list = step_list;
 	while (step_list != NULL) {
-		step_list->trigger = trigger;
 		step_list = step_list->pNext;
 	}
 
-- 
2.23.0

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [Tarantool-patches] [PATCH v1 2/9] box: rename struct trigger to lua_trigger
  2019-10-14 16:03 [Tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space Kirill Shcherbatov
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 1/9] sql: remove redundant pointer in TriggerStep Kirill Shcherbatov
@ 2019-10-14 16:03 ` Kirill Shcherbatov
  2019-10-17  7:33   ` Konstantin Osipov
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 3/9] box: introduce trigger_event_manipulation enum Kirill Shcherbatov
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 31+ messages in thread
From: Kirill Shcherbatov @ 2019-10-14 16:03 UTC (permalink / raw)
  To: tarantool-patches, tarantool-patches, kostja.osipov, korablev

Renamed struct trigger to struct lua_trigger because the 'trigger'
name is too general. Currently Tarantool has non-persistent
AppServer Lua triggers represented with 'trigger' structure so as
SQL-specific persistent trigger object named 'sql_trigger'.

In scope of this patchset we going to significantly rework
triggers subsystem and introduce a new base trigger class named
'trigger', so this name is be better to be released.

Needed for #4343
---
 src/box/alter.cc            | 232 ++++++++++++++++++------------------
 src/box/alter.h             |  34 +++---
 src/box/applier.cc          |  28 ++---
 src/box/applier.h           |   2 +-
 src/box/ck_constraint.c     |   2 +-
 src/box/ck_constraint.h     |   4 +-
 src/box/error.cc            |   2 +-
 src/box/iproto.cc           |   4 +-
 src/box/lua/call.c          |   4 +-
 src/box/lua/sequence.c      |   4 +-
 src/box/lua/space.cc        |   4 +-
 src/box/lua/tuple.c         |   2 +-
 src/box/memtx_space.c       |   8 +-
 src/box/recovery.cc         |   2 +-
 src/box/relay.cc            |   6 +-
 src/box/replication.cc      |   8 +-
 src/box/replication.h       |   4 +-
 src/box/schema.cc           |   2 +-
 src/box/session.cc          |   4 +-
 src/box/session.h           |   4 +-
 src/box/space.c             |   8 +-
 src/box/space.h             |   2 +-
 src/box/txn.c               |   8 +-
 src/box/txn.h               |  14 +--
 src/box/vinyl.c             |  32 ++---
 src/box/vy_lsm.c            |   2 +-
 src/box/vy_scheduler.c      |   4 +-
 src/box/vy_tx.c             |   2 +-
 src/lib/core/cbus.c         |   2 +-
 src/lib/core/fiber.c        |   6 +-
 src/lib/core/trigger.cc     |   6 +-
 src/lib/core/trigger.h      |  20 ++--
 src/lib/swim/swim.c         |   2 +-
 src/lua/swim.c              |   2 +-
 src/lua/trigger.c           |   6 +-
 src/lua/trigger.h           |   2 +-
 src/lua/utils.h             |   2 +-
 src/main.cc                 |   4 +-
 test/unit/cbus.c            |   6 +-
 test/unit/swim.c            |  14 +--
 test/unit/swim_test_utils.c |   6 +-
 41 files changed, 255 insertions(+), 255 deletions(-)

diff --git a/src/box/alter.cc b/src/box/alter.cc
index bb3686d6e..4bc2e6e6b 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -705,11 +705,11 @@ public:
  * A trigger installed on transaction commit/rollback events of
  * the transaction which initiated the alter.
  */
-static struct trigger *
+static struct lua_trigger *
 txn_alter_trigger_new(trigger_f run, void *data)
 {
-	struct trigger *trigger = (struct trigger *)
-		region_calloc_object_xc(&in_txn()->region, struct trigger);
+	struct lua_trigger *trigger = (struct lua_trigger *)
+		region_calloc_object_xc(&in_txn()->region, struct lua_trigger);
 	trigger->run = run;
 	trigger->data = data;
 	trigger->destroy = NULL;
@@ -860,7 +860,7 @@ struct mh_i32_t *AlterSpaceLock::registry;
  * Replace the old space with a new one in the space cache.
  */
 static void
-alter_space_commit(struct trigger *trigger, void *event)
+alter_space_commit(struct lua_trigger *trigger, void *event)
 {
 	struct txn *txn = (struct txn *) event;
 	struct alter_space *alter = (struct alter_space *) trigger->data;
@@ -900,7 +900,7 @@ alter_space_commit(struct trigger *trigger, void *event)
  * alter_space_commit() failure (unlikely)
  */
 static void
-alter_space_rollback(struct trigger *trigger, void * /* event */)
+alter_space_rollback(struct lua_trigger *trigger, void * /* event */)
 {
 	struct alter_space *alter = (struct alter_space *) trigger->data;
 	/* Rollback alter ops */
@@ -966,7 +966,7 @@ alter_space_do(struct txn_stmt *stmt, struct alter_space *alter)
 	 * free them in case of failure, because they are allocated on
 	 * the region.
 	 */
-	struct trigger *on_commit, *on_rollback;
+	struct lua_trigger *on_commit, *on_rollback;
 	on_commit = txn_alter_trigger_new(alter_space_commit, alter);
 	on_rollback = txn_alter_trigger_new(alter_space_rollback, alter);
 
@@ -1646,7 +1646,7 @@ MoveCkConstraints::rollback(struct alter_space *alter)
  * Delete the space. It is already removed from the space cache.
  */
 static void
-on_drop_space_commit(struct trigger *trigger, void *event)
+on_drop_space_commit(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct space *space = (struct space *)trigger->data;
@@ -1659,7 +1659,7 @@ on_drop_space_commit(struct trigger *trigger, void *event)
  * reverted by the cascading rollback.
  */
 static void
-on_drop_space_rollback(struct trigger *trigger, void *event)
+on_drop_space_rollback(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct space *space = (struct space *)trigger->data;
@@ -1674,7 +1674,7 @@ on_drop_space_rollback(struct trigger *trigger, void *event)
  * rely on cascading rollback.
  */
 static void
-on_create_space_rollback(struct trigger *trigger, void *event)
+on_create_space_rollback(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct space *space = (struct space *)trigger->data;
@@ -1792,7 +1792,7 @@ update_view_references(struct Select *select, int update_value,
  * Its purpose is to release memory of SELECT.
  */
 static void
-on_create_view_commit(struct trigger *trigger, void *event)
+on_create_view_commit(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct Select *select = (struct Select *)trigger->data;
@@ -1805,7 +1805,7 @@ on_create_view_commit(struct trigger *trigger, void *event)
  * releases memory for SELECT.
  */
 static void
-on_create_view_rollback(struct trigger *trigger, void *event)
+on_create_view_rollback(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct Select *select = (struct Select *)trigger->data;
@@ -1819,7 +1819,7 @@ on_create_view_rollback(struct trigger *trigger, void *event)
  * dependent spaces and release memory for SELECT.
  */
 static void
-on_drop_view_commit(struct trigger *trigger, void *event)
+on_drop_view_commit(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct Select *select = (struct Select *)trigger->data;
@@ -1832,7 +1832,7 @@ on_drop_view_commit(struct trigger *trigger, void *event)
  * on_replace_dd_space trigger.
  */
 static void
-on_drop_view_rollback(struct trigger *trigger, void *event)
+on_drop_view_rollback(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct Select *select = (struct Select *)trigger->data;
@@ -1891,7 +1891,7 @@ on_drop_view_rollback(struct trigger *trigger, void *event)
  * clumsy, so it is simply not done.
  */
 static void
-on_replace_dd_space(struct trigger * /* trigger */, void *event)
+on_replace_dd_space(struct lua_trigger * /* trigger */, void *event)
 {
 	struct txn *txn = (struct txn *) event;
 	struct txn_stmt *stmt = txn_current_stmt(txn);
@@ -1946,7 +1946,7 @@ on_replace_dd_space(struct trigger * /* trigger */, void *event)
 		 * so it's safe to simply drop the space on
 		 * rollback.
 		 */
-		struct trigger *on_rollback =
+		struct lua_trigger *on_rollback =
 			txn_alter_trigger_new(on_create_space_rollback, space);
 		txn_stmt_on_rollback(stmt, on_rollback);
 		if (def->opts.is_view) {
@@ -1969,11 +1969,11 @@ on_replace_dd_space(struct trigger * /* trigger */, void *event)
 				tnt_raise(ClientError, ER_NO_SUCH_SPACE,
 					  disappeared_space);
 			}
-			struct trigger *on_commit_view =
+			struct lua_trigger *on_commit_view =
 				txn_alter_trigger_new(on_create_view_commit,
 						      select);
 			txn_stmt_on_commit(stmt, on_commit_view);
-			struct trigger *on_rollback_view =
+			struct lua_trigger *on_rollback_view =
 				txn_alter_trigger_new(on_create_view_rollback,
 						      select);
 			txn_stmt_on_rollback(stmt, on_rollback_view);
@@ -2031,10 +2031,10 @@ on_replace_dd_space(struct trigger * /* trigger */, void *event)
 		 * AlterSpaceOps are registered in case of space drop.
 		 */
 		++schema_version;
-		struct trigger *on_commit =
+		struct lua_trigger *on_commit =
 			txn_alter_trigger_new(on_drop_space_commit, old_space);
 		txn_stmt_on_commit(stmt, on_commit);
-		struct trigger *on_rollback =
+		struct lua_trigger *on_rollback =
 			txn_alter_trigger_new(on_drop_space_rollback, old_space);
 		txn_stmt_on_rollback(stmt, on_rollback);
 		if (old_space->def->opts.is_view) {
@@ -2046,11 +2046,11 @@ on_replace_dd_space(struct trigger * /* trigger */, void *event)
 			auto select_guard = make_scoped_guard([=] {
 				sql_select_delete(sql_get(), select);
 			});
-			struct trigger *on_commit_view =
+			struct lua_trigger *on_commit_view =
 				txn_alter_trigger_new(on_drop_view_commit,
 						      select);
 			txn_stmt_on_commit(stmt, on_commit_view);
-			struct trigger *on_rollback_view =
+			struct lua_trigger *on_rollback_view =
 				txn_alter_trigger_new(on_drop_view_rollback,
 						      select);
 			txn_stmt_on_rollback(stmt, on_rollback_view);
@@ -2187,7 +2187,7 @@ index_is_used_by_fk_constraint(struct rlist *fk_list, uint32_t iid)
  *   are modified.
  */
 static void
-on_replace_dd_index(struct trigger * /* trigger */, void *event)
+on_replace_dd_index(struct lua_trigger * /* trigger */, void *event)
 {
 	struct txn *txn = (struct txn *) event;
 	struct txn_stmt *stmt = txn_current_stmt(txn);
@@ -2377,7 +2377,7 @@ on_replace_dd_index(struct trigger * /* trigger */, void *event)
  * rollback of all transactions following this one.
  */
 static void
-on_replace_dd_truncate(struct trigger * /* trigger */, void *event)
+on_replace_dd_truncate(struct lua_trigger * /* trigger */, void *event)
 {
 	struct txn *txn = (struct txn *) event;
 	struct txn_stmt *stmt = txn_current_stmt(txn);
@@ -2584,7 +2584,7 @@ user_def_new_from_tuple(struct tuple *tuple)
 }
 
 static void
-user_cache_remove_user(struct trigger *trigger, void * /* event */)
+user_cache_remove_user(struct lua_trigger *trigger, void * /* event */)
 {
 	struct tuple *tuple = (struct tuple *)trigger->data;
 	uint32_t uid = tuple_field_u32_xc(tuple, BOX_USER_FIELD_ID);
@@ -2592,7 +2592,7 @@ user_cache_remove_user(struct trigger *trigger, void * /* event */)
 }
 
 static void
-user_cache_alter_user(struct trigger *trigger, void * /* event */)
+user_cache_alter_user(struct lua_trigger *trigger, void * /* event */)
 {
 	struct tuple *tuple = (struct tuple *)trigger->data;
 	struct user_def *user = user_def_new_from_tuple(tuple);
@@ -2606,7 +2606,7 @@ user_cache_alter_user(struct trigger *trigger, void * /* event */)
  * A trigger invoked on replace in the user table.
  */
 static void
-on_replace_dd_user(struct trigger * /* trigger */, void *event)
+on_replace_dd_user(struct lua_trigger * /* trigger */, void *event)
 {
 	struct txn *txn = (struct txn *) event;
 	struct txn_stmt *stmt = txn_current_stmt(txn);
@@ -2623,7 +2623,7 @@ on_replace_dd_user(struct trigger * /* trigger */, void *event)
 		auto def_guard = make_scoped_guard([=] { free(user); });
 		(void) user_cache_replace(user);
 		def_guard.is_active = false;
-		struct trigger *on_rollback =
+		struct lua_trigger *on_rollback =
 			txn_alter_trigger_new(user_cache_remove_user, new_tuple);
 		txn_stmt_on_rollback(stmt, on_rollback);
 	} else if (new_tuple == NULL) { /* DELETE */
@@ -2645,7 +2645,7 @@ on_replace_dd_user(struct trigger * /* trigger */, void *event)
 				  old_user->def->name, "the user has objects");
 		}
 		user_cache_delete(uid);
-		struct trigger *on_rollback =
+		struct lua_trigger *on_rollback =
 			txn_alter_trigger_new(user_cache_alter_user, old_tuple);
 		txn_stmt_on_rollback(stmt, on_rollback);
 	} else { /* UPDATE, REPLACE */
@@ -2661,7 +2661,7 @@ on_replace_dd_user(struct trigger * /* trigger */, void *event)
 		auto def_guard = make_scoped_guard([=] { free(user); });
 		user_cache_replace(user);
 		def_guard.is_active = false;
-		struct trigger *on_rollback =
+		struct lua_trigger *on_rollback =
 			txn_alter_trigger_new(user_cache_alter_user, old_tuple);
 		txn_stmt_on_rollback(stmt, on_rollback);
 	}
@@ -2860,7 +2860,7 @@ func_def_new_from_tuple(struct tuple *tuple)
 }
 
 static void
-on_create_func_rollback(struct trigger *trigger, void * /* event */)
+on_create_func_rollback(struct lua_trigger *trigger, void * /* event */)
 {
 	/* Remove the new function from the cache and delete it. */
 	struct func *func = (struct func *)trigger->data;
@@ -2870,7 +2870,7 @@ on_create_func_rollback(struct trigger *trigger, void * /* event */)
 }
 
 static void
-on_drop_func_commit(struct trigger *trigger, void * /* event */)
+on_drop_func_commit(struct lua_trigger *trigger, void * /* event */)
 {
 	/* Delete the old function. */
 	struct func *func = (struct func *)trigger->data;
@@ -2878,7 +2878,7 @@ on_drop_func_commit(struct trigger *trigger, void * /* event */)
 }
 
 static void
-on_drop_func_rollback(struct trigger *trigger, void * /* event */)
+on_drop_func_rollback(struct lua_trigger *trigger, void * /* event */)
 {
 	/* Insert the old function back into the cache. */
 	struct func *func = (struct func *)trigger->data;
@@ -2891,7 +2891,7 @@ on_drop_func_rollback(struct trigger *trigger, void * /* event */)
  * functions on which there were defined any grants.
  */
 static void
-on_replace_dd_func(struct trigger * /* trigger */, void *event)
+on_replace_dd_func(struct lua_trigger * /* trigger */, void *event)
 {
 	struct txn *txn = (struct txn *) event;
 	struct txn_stmt *stmt = txn_current_stmt(txn);
@@ -2906,7 +2906,7 @@ on_replace_dd_func(struct trigger * /* trigger */, void *event)
 		auto def_guard = make_scoped_guard([=] { free(def); });
 		access_check_ddl(def->name, def->fid, def->uid, SC_FUNCTION,
 				 PRIV_C);
-		struct trigger *on_rollback =
+		struct lua_trigger *on_rollback =
 			txn_alter_trigger_new(on_create_func_rollback, NULL);
 		struct func *func = func_new(def);
 		if (func == NULL)
@@ -2943,9 +2943,9 @@ on_replace_dd_func(struct trigger * /* trigger */, void *event)
 				  (unsigned) old_func->def->uid,
 				  "function is SQL built-in");
 		}
-		struct trigger *on_commit =
+		struct lua_trigger *on_commit =
 			txn_alter_trigger_new(on_drop_func_commit, old_func);
-		struct trigger *on_rollback =
+		struct lua_trigger *on_rollback =
 			txn_alter_trigger_new(on_drop_func_rollback, old_func);
 		func_cache_delete(old_func->def->fid);
 		txn_stmt_on_commit(stmt, on_commit);
@@ -3058,7 +3058,7 @@ coll_id_def_new_from_tuple(struct tuple *tuple, struct coll_id_def *def)
 
 /** Delete the new collation identifier. */
 static void
-on_create_collation_rollback(struct trigger *trigger, void *event)
+on_create_collation_rollback(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct coll_id *coll_id = (struct coll_id *) trigger->data;
@@ -3069,7 +3069,7 @@ on_create_collation_rollback(struct trigger *trigger, void *event)
 
 /** Free a deleted collation identifier on commit. */
 static void
-on_drop_collation_commit(struct trigger *trigger, void *event)
+on_drop_collation_commit(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct coll_id *coll_id = (struct coll_id *) trigger->data;
@@ -3078,7 +3078,7 @@ on_drop_collation_commit(struct trigger *trigger, void *event)
 
 /** Put the collation identifier back on rollback. */
 static void
-on_drop_collation_rollback(struct trigger *trigger, void *event)
+on_drop_collation_rollback(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct coll_id *coll_id = (struct coll_id *) trigger->data;
@@ -3093,7 +3093,7 @@ on_drop_collation_rollback(struct trigger *trigger, void *event)
  * collations that a user defined.
  */
 static void
-on_replace_dd_collation(struct trigger * /* trigger */, void *event)
+on_replace_dd_collation(struct lua_trigger * /* trigger */, void *event)
 {
 	struct txn *txn = (struct txn *) event;
 	struct txn_stmt *stmt = txn_current_stmt(txn);
@@ -3101,9 +3101,9 @@ on_replace_dd_collation(struct trigger * /* trigger */, void *event)
 	struct tuple *new_tuple = stmt->new_tuple;
 	if (new_tuple == NULL && old_tuple != NULL) {
 		/* DELETE */
-		struct trigger *on_commit =
+		struct lua_trigger *on_commit =
 			txn_alter_trigger_new(on_drop_collation_commit, NULL);
-		struct trigger *on_rollback =
+		struct lua_trigger *on_rollback =
 			txn_alter_trigger_new(on_drop_collation_rollback, NULL);
 		/*
 		 * TODO: Check that no index uses the collation
@@ -3138,7 +3138,7 @@ on_replace_dd_collation(struct trigger * /* trigger */, void *event)
 		txn_stmt_on_commit(stmt, on_commit);
 	} else if (new_tuple != NULL && old_tuple == NULL) {
 		/* INSERT */
-		struct trigger *on_rollback =
+		struct lua_trigger *on_rollback =
 			txn_alter_trigger_new(on_create_collation_rollback, NULL);
 		struct coll_id_def new_def;
 		coll_id_def_new_from_tuple(new_tuple, &new_def);
@@ -3367,7 +3367,7 @@ grant_or_revoke(struct priv_def *priv)
 
 /** A trigger called on rollback of grant. */
 static void
-revoke_priv(struct trigger *trigger, void *event)
+revoke_priv(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct tuple *tuple = (struct tuple *)trigger->data;
@@ -3379,7 +3379,7 @@ revoke_priv(struct trigger *trigger, void *event)
 
 /** A trigger called on rollback of revoke or modify. */
 static void
-modify_priv(struct trigger *trigger, void *event)
+modify_priv(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct tuple *tuple = (struct tuple *)trigger->data;
@@ -3393,7 +3393,7 @@ modify_priv(struct trigger *trigger, void *event)
  * all granted privileges.
  */
 static void
-on_replace_dd_priv(struct trigger * /* trigger */, void *event)
+on_replace_dd_priv(struct lua_trigger * /* trigger */, void *event)
 {
 	struct txn *txn = (struct txn *) event;
 	struct txn_stmt *stmt = txn_current_stmt(txn);
@@ -3405,7 +3405,7 @@ on_replace_dd_priv(struct trigger * /* trigger */, void *event)
 		priv_def_create_from_tuple(&priv, new_tuple);
 		priv_def_check(&priv, PRIV_GRANT);
 		grant_or_revoke(&priv);
-		struct trigger *on_rollback =
+		struct lua_trigger *on_rollback =
 			txn_alter_trigger_new(revoke_priv, new_tuple);
 		txn_stmt_on_rollback(stmt, on_rollback);
 	} else if (new_tuple == NULL) {                /* revoke */
@@ -3414,14 +3414,14 @@ on_replace_dd_priv(struct trigger * /* trigger */, void *event)
 		priv_def_check(&priv, PRIV_REVOKE);
 		priv.access = 0;
 		grant_or_revoke(&priv);
-		struct trigger *on_rollback =
+		struct lua_trigger *on_rollback =
 			txn_alter_trigger_new(modify_priv, old_tuple);
 		txn_stmt_on_rollback(stmt, on_rollback);
 	} else {                                       /* modify */
 		priv_def_create_from_tuple(&priv, new_tuple);
 		priv_def_check(&priv, PRIV_GRANT);
 		grant_or_revoke(&priv);
-		struct trigger *on_rollback =
+		struct lua_trigger *on_rollback =
 			txn_alter_trigger_new(modify_priv, old_tuple);
 		txn_stmt_on_rollback(stmt, on_rollback);
 	}
@@ -3441,7 +3441,7 @@ on_replace_dd_priv(struct trigger * /* trigger */, void *event)
  * event, not in after-replace event.
  */
 static void
-on_replace_dd_schema(struct trigger * /* trigger */, void *event)
+on_replace_dd_schema(struct lua_trigger * /* trigger */, void *event)
 {
 	struct txn *txn = (struct txn *) event;
 	struct txn_stmt *stmt = txn_current_stmt(txn);
@@ -3465,7 +3465,7 @@ on_replace_dd_schema(struct trigger * /* trigger */, void *event)
  * with it.
  */
 static void
-register_replica(struct trigger *trigger, void * /* event */)
+register_replica(struct lua_trigger *trigger, void * /* event */)
 {
 	struct tuple *new_tuple = (struct tuple *)trigger->data;
 
@@ -3486,7 +3486,7 @@ register_replica(struct trigger *trigger, void * /* event */)
 }
 
 static void
-unregister_replica(struct trigger *trigger, void * /* event */)
+unregister_replica(struct lua_trigger *trigger, void * /* event */)
 {
 	struct tuple *old_tuple = (struct tuple *)trigger->data;
 
@@ -3517,7 +3517,7 @@ unregister_replica(struct trigger *trigger, void * /* event */)
  * set without first being reset (emptied).
  */
 static void
-on_replace_dd_cluster(struct trigger *trigger, void *event)
+on_replace_dd_cluster(struct lua_trigger *trigger, void *event)
 {
 	(void) trigger;
 	struct txn *txn = (struct txn *) event;
@@ -3550,7 +3550,7 @@ on_replace_dd_cluster(struct trigger *trigger, void *event)
 					  "updates of instance uuid");
 			}
 		} else {
-			struct trigger *on_commit;
+			struct lua_trigger *on_commit;
 			on_commit = txn_alter_trigger_new(register_replica,
 							  new_tuple);
 			txn_stmt_on_commit(stmt, on_commit);
@@ -3565,7 +3565,7 @@ on_replace_dd_cluster(struct trigger *trigger, void *event)
 			tuple_field_u32_xc(old_tuple, BOX_CLUSTER_FIELD_ID);
 		replica_check_id(replica_id);
 
-		struct trigger *on_commit;
+		struct lua_trigger *on_commit;
 		on_commit = txn_alter_trigger_new(unregister_replica,
 						  old_tuple);
 		txn_stmt_on_commit(stmt, on_commit);
@@ -3618,7 +3618,7 @@ sequence_def_new_from_tuple(struct tuple *tuple, uint32_t errcode)
 }
 
 static void
-on_create_sequence_rollback(struct trigger *trigger, void * /* event */)
+on_create_sequence_rollback(struct lua_trigger *trigger, void * /* event */)
 {
 	/* Remove the new sequence from the cache and delete it. */
 	struct sequence *seq = (struct sequence *)trigger->data;
@@ -3628,7 +3628,7 @@ on_create_sequence_rollback(struct trigger *trigger, void * /* event */)
 }
 
 static void
-on_drop_sequence_commit(struct trigger *trigger, void * /* event */)
+on_drop_sequence_commit(struct lua_trigger *trigger, void * /* event */)
 {
 	/* Delete the old sequence. */
 	struct sequence *seq = (struct sequence *)trigger->data;
@@ -3636,7 +3636,7 @@ on_drop_sequence_commit(struct trigger *trigger, void * /* event */)
 }
 
 static void
-on_drop_sequence_rollback(struct trigger *trigger, void * /* event */)
+on_drop_sequence_rollback(struct lua_trigger *trigger, void * /* event */)
 {
 	/* Insert the old sequence back into the cache. */
 	struct sequence *seq = (struct sequence *)trigger->data;
@@ -3646,7 +3646,7 @@ on_drop_sequence_rollback(struct trigger *trigger, void * /* event */)
 
 
 static void
-on_alter_sequence_commit(struct trigger *trigger, void * /* event */)
+on_alter_sequence_commit(struct lua_trigger *trigger, void * /* event */)
 {
 	/* Delete the old old sequence definition. */
 	struct sequence_def *def = (struct sequence_def *)trigger->data;
@@ -3654,7 +3654,7 @@ on_alter_sequence_commit(struct trigger *trigger, void * /* event */)
 }
 
 static void
-on_alter_sequence_rollback(struct trigger *trigger, void * /* event */)
+on_alter_sequence_rollback(struct lua_trigger *trigger, void * /* event */)
 {
 	/* Restore the old sequence definition. */
 	struct sequence_def *def = (struct sequence_def *)trigger->data;
@@ -3670,7 +3670,7 @@ on_alter_sequence_rollback(struct trigger *trigger, void * /* event */)
  * Used to alter a sequence definition.
  */
 static void
-on_replace_dd_sequence(struct trigger * /* trigger */, void *event)
+on_replace_dd_sequence(struct lua_trigger * /* trigger */, void *event)
 {
 	struct txn *txn = (struct txn *) event;
 	struct txn_stmt *stmt = txn_current_stmt(txn);
@@ -3686,7 +3686,7 @@ on_replace_dd_sequence(struct trigger * /* trigger */, void *event)
 						      ER_CREATE_SEQUENCE);
 		access_check_ddl(new_def->name, new_def->id, new_def->uid,
 				 SC_SEQUENCE, PRIV_C);
-		struct trigger *on_rollback =
+		struct lua_trigger *on_rollback =
 			txn_alter_trigger_new(on_create_sequence_rollback, NULL);
 		seq = sequence_new_xc(new_def);
 		sequence_cache_insert(seq);
@@ -3708,9 +3708,9 @@ on_replace_dd_sequence(struct trigger * /* trigger */, void *event)
 		if (schema_find_grants("sequence", seq->def->id))
 			tnt_raise(ClientError, ER_DROP_SEQUENCE,
 				  seq->def->name, "the sequence has grants");
-		struct trigger *on_commit =
+		struct lua_trigger *on_commit =
 			txn_alter_trigger_new(on_drop_sequence_commit, seq);
-		struct trigger *on_rollback =
+		struct lua_trigger *on_rollback =
 			txn_alter_trigger_new(on_drop_sequence_rollback, seq);
 		sequence_cache_delete(seq->def->id);
 		txn_stmt_on_commit(stmt, on_commit);
@@ -3722,9 +3722,9 @@ on_replace_dd_sequence(struct trigger * /* trigger */, void *event)
 		assert(seq != NULL);
 		access_check_ddl(seq->def->name, seq->def->id, seq->def->uid,
 				 SC_SEQUENCE, PRIV_A);
-		struct trigger *on_commit =
+		struct lua_trigger *on_commit =
 			txn_alter_trigger_new(on_alter_sequence_commit, seq->def);
-		struct trigger *on_rollback =
+		struct lua_trigger *on_rollback =
 			txn_alter_trigger_new(on_alter_sequence_rollback, seq->def);
 		seq->def = new_def;
 		txn_stmt_on_commit(stmt, on_commit);
@@ -3737,7 +3737,7 @@ on_replace_dd_sequence(struct trigger * /* trigger */, void *event)
 
 /** Restore the old sequence value on rollback. */
 static void
-on_drop_sequence_data_rollback(struct trigger *trigger, void * /* event */)
+on_drop_sequence_data_rollback(struct lua_trigger *trigger, void * /* event */)
 {
 	struct tuple *tuple = (struct tuple *)trigger->data;
 	uint32_t id = tuple_field_u32_xc(tuple, BOX_SEQUENCE_DATA_FIELD_ID);
@@ -3754,7 +3754,7 @@ on_drop_sequence_data_rollback(struct trigger *trigger, void * /* event */)
  * Used to update a sequence value.
  */
 static void
-on_replace_dd_sequence_data(struct trigger * /* trigger */, void *event)
+on_replace_dd_sequence_data(struct lua_trigger * /* trigger */, void *event)
 {
 	struct txn *txn = (struct txn *) event;
 	struct txn_stmt *stmt = txn_current_stmt(txn);
@@ -3779,7 +3779,7 @@ on_replace_dd_sequence_data(struct trigger * /* trigger */, void *event)
 		 * we do want to restore the original sequence value
 		 * on rollback.
 		 */
-		struct trigger *on_rollback = txn_alter_trigger_new(
+		struct lua_trigger *on_rollback = txn_alter_trigger_new(
 				on_drop_sequence_data_rollback, old_tuple);
 		txn_stmt_on_rollback(stmt, on_rollback);
 		sequence_reset(seq);
@@ -3826,7 +3826,7 @@ sequence_field_from_tuple(struct space *space, struct tuple *tuple,
 
 /** Attach a sequence to a space on rollback in _space_sequence. */
 static void
-set_space_sequence(struct trigger *trigger, void * /* event */)
+set_space_sequence(struct lua_trigger *trigger, void * /* event */)
 {
 	struct tuple *tuple = (struct tuple *)trigger->data;
 	uint32_t space_id = tuple_field_u32_xc(tuple,
@@ -3851,7 +3851,7 @@ set_space_sequence(struct trigger *trigger, void * /* event */)
 
 /** Detach a sequence from a space on rollback in _space_sequence. */
 static void
-clear_space_sequence(struct trigger *trigger, void * /* event */)
+clear_space_sequence(struct lua_trigger *trigger, void * /* event */)
 {
 	struct tuple *tuple = (struct tuple *)trigger->data;
 	uint32_t space_id = tuple_field_u32_xc(tuple,
@@ -3872,7 +3872,7 @@ clear_space_sequence(struct trigger *trigger, void * /* event */)
  * Used to update space <-> sequence mapping.
  */
 static void
-on_replace_dd_space_sequence(struct trigger * /* trigger */, void *event)
+on_replace_dd_space_sequence(struct lua_trigger * /* trigger */, void *event)
 {
 	struct txn *txn = (struct txn *) event;
 	struct txn_stmt *stmt = txn_current_stmt(txn);
@@ -3923,7 +3923,7 @@ on_replace_dd_space_sequence(struct trigger * /* trigger */, void *event)
 				  space_name(space),
 				  "can not attach generated sequence");
 		}
-		struct trigger *on_rollback;
+		struct lua_trigger *on_rollback;
 		if (stmt->old_tuple != NULL)
 			on_rollback = txn_alter_trigger_new(set_space_sequence,
 							    stmt->old_tuple);
@@ -3938,7 +3938,7 @@ on_replace_dd_space_sequence(struct trigger * /* trigger */, void *event)
 		sequence_path_guard.is_active = false;
 		txn_stmt_on_rollback(stmt, on_rollback);
 	} else {					/* DELETE */
-		struct trigger *on_rollback;
+		struct lua_trigger *on_rollback;
 		on_rollback = txn_alter_trigger_new(set_space_sequence,
 						    stmt->old_tuple);
 		assert(space->sequence == seq);
@@ -3956,7 +3956,7 @@ on_replace_dd_space_sequence(struct trigger * /* trigger */, void *event)
 
 /** Delete the new trigger on rollback of an INSERT statement. */
 static void
-on_create_trigger_rollback(struct trigger *trigger, void * /* event */)
+on_create_trigger_rollback(struct lua_trigger *trigger, void * /* event */)
 {
 	struct sql_trigger *old_trigger = (struct sql_trigger *)trigger->data;
 	struct sql_trigger *new_trigger;
@@ -3971,7 +3971,7 @@ on_create_trigger_rollback(struct trigger *trigger, void * /* event */)
 
 /** Restore the old trigger on rollback of a DELETE statement. */
 static void
-on_drop_trigger_rollback(struct trigger *trigger, void * /* event */)
+on_drop_trigger_rollback(struct lua_trigger *trigger, void * /* event */)
 {
 	struct sql_trigger *old_trigger = (struct sql_trigger *)trigger->data;
 	struct sql_trigger *new_trigger;
@@ -3989,7 +3989,7 @@ on_drop_trigger_rollback(struct trigger *trigger, void * /* event */)
  * of a REPLACE statement.
  */
 static void
-on_replace_trigger_rollback(struct trigger *trigger, void * /* event */)
+on_replace_trigger_rollback(struct lua_trigger *trigger, void * /* event */)
 {
 	struct sql_trigger *old_trigger = (struct sql_trigger *)trigger->data;
 	struct sql_trigger *new_trigger;
@@ -4005,7 +4005,7 @@ on_replace_trigger_rollback(struct trigger *trigger, void * /* event */)
  * Drop useless old sql_trigger AST object if any.
  */
 static void
-on_replace_trigger_commit(struct trigger *trigger, void * /* event */)
+on_replace_trigger_commit(struct lua_trigger *trigger, void * /* event */)
 {
 	struct sql_trigger *old_trigger = (struct sql_trigger *)trigger->data;
 	sql_trigger_delete(sql_get(), old_trigger);
@@ -4016,15 +4016,15 @@ on_replace_trigger_commit(struct trigger *trigger, void * /* event */)
  * SQL triggers.
  */
 static void
-on_replace_dd_trigger(struct trigger * /* trigger */, void *event)
+on_replace_dd_trigger(struct lua_trigger * /* trigger */, void *event)
 {
 	struct txn *txn = (struct txn *) event;
 	struct txn_stmt *stmt = txn_current_stmt(txn);
 	struct tuple *old_tuple = stmt->old_tuple;
 	struct tuple *new_tuple = stmt->new_tuple;
 
-	struct trigger *on_rollback = txn_alter_trigger_new(NULL, NULL);
-	struct trigger *on_commit =
+	struct lua_trigger *on_rollback = txn_alter_trigger_new(NULL, NULL);
+	struct lua_trigger *on_commit =
 		txn_alter_trigger_new(on_replace_trigger_commit, NULL);
 
 	if (old_tuple != NULL && new_tuple == NULL) {
@@ -4304,7 +4304,7 @@ space_reset_fk_constraint_mask(struct space *space)
  * release memory.
  */
 static void
-on_create_fk_constraint_rollback(struct trigger *trigger, void *event)
+on_create_fk_constraint_rollback(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct fk_constraint *fk = (struct fk_constraint *)trigger->data;
@@ -4317,7 +4317,7 @@ on_create_fk_constraint_rollback(struct trigger *trigger, void *event)
 
 /** Return old FK and release memory for the new one. */
 static void
-on_replace_fk_constraint_rollback(struct trigger *trigger, void *event)
+on_replace_fk_constraint_rollback(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct fk_constraint *old_fk = (struct fk_constraint *)trigger->data;
@@ -4335,7 +4335,7 @@ on_replace_fk_constraint_rollback(struct trigger *trigger, void *event)
 
 /** On rollback of drop simply return back FK to DD. */
 static void
-on_drop_fk_constraint_rollback(struct trigger *trigger, void *event)
+on_drop_fk_constraint_rollback(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct fk_constraint *old_fk = (struct fk_constraint *)trigger->data;
@@ -4355,7 +4355,7 @@ on_drop_fk_constraint_rollback(struct trigger *trigger, void *event)
  * so just release memory.
  */
 static void
-on_drop_or_replace_fk_constraint_commit(struct trigger *trigger, void *event)
+on_drop_or_replace_fk_constraint_commit(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	fk_constraint_delete((struct fk_constraint *) trigger->data);
@@ -4398,7 +4398,7 @@ error:
 
 /** A trigger invoked on replace in the _fk_constraint space. */
 static void
-on_replace_dd_fk_constraint(struct trigger * /* trigger*/, void *event)
+on_replace_dd_fk_constraint(struct lua_trigger * /* trigger*/, void *event)
 {
 	struct txn *txn = (struct txn *) event;
 	struct txn_stmt *stmt = txn_current_stmt(txn);
@@ -4510,7 +4510,7 @@ on_replace_dd_fk_constraint(struct trigger * /* trigger*/, void *event)
 					fk, in_child_space);
 			rlist_add_entry(&parent_space->parent_fk_constraint,
 					fk, in_parent_space);
-			struct trigger *on_rollback =
+			struct lua_trigger *on_rollback =
 				txn_alter_trigger_new(on_create_fk_constraint_rollback,
 						      fk);
 			txn_stmt_on_rollback(stmt, on_rollback);
@@ -4528,11 +4528,11 @@ on_replace_dd_fk_constraint(struct trigger * /* trigger*/, void *event)
 					in_child_space);
 			rlist_add_entry(&parent_space->parent_fk_constraint, fk,
 					in_parent_space);
-			struct trigger *on_rollback =
+			struct lua_trigger *on_rollback =
 				txn_alter_trigger_new(on_replace_fk_constraint_rollback,
 						      old_fk);
 			txn_stmt_on_rollback(stmt, on_rollback);
-			struct trigger *on_commit =
+			struct lua_trigger *on_commit =
 				txn_alter_trigger_new(on_drop_or_replace_fk_constraint_commit,
 						      old_fk);
 			txn_stmt_on_commit(stmt, on_commit);
@@ -4554,11 +4554,11 @@ on_replace_dd_fk_constraint(struct trigger * /* trigger*/, void *event)
 		struct fk_constraint *old_fk=
 			fk_constraint_remove(&child_space->child_fk_constraint,
 					     fk_def->name);
-		struct trigger *on_commit =
+		struct lua_trigger *on_commit =
 			txn_alter_trigger_new(on_drop_or_replace_fk_constraint_commit,
 					      old_fk);
 		txn_stmt_on_commit(stmt, on_commit);
-		struct trigger *on_rollback =
+		struct lua_trigger *on_rollback =
 			txn_alter_trigger_new(on_drop_fk_constraint_rollback,
 					      old_fk);
 		txn_stmt_on_rollback(stmt, on_rollback);
@@ -4605,7 +4605,7 @@ ck_constraint_def_new_from_tuple(struct tuple *tuple)
 
 /** Rollback INSERT check constraint. */
 static void
-on_create_ck_constraint_rollback(struct trigger *trigger, void * /* event */)
+on_create_ck_constraint_rollback(struct lua_trigger *trigger, void * /* event */)
 {
 	struct ck_constraint *ck = (struct ck_constraint *)trigger->data;
 	assert(ck != NULL);
@@ -4620,7 +4620,7 @@ on_create_ck_constraint_rollback(struct trigger *trigger, void * /* event */)
 
 /** Commit DELETE check constraint. */
 static void
-on_drop_ck_constraint_commit(struct trigger *trigger, void * /* event */)
+on_drop_ck_constraint_commit(struct lua_trigger *trigger, void * /* event */)
 {
 	struct ck_constraint *ck = (struct ck_constraint *)trigger->data;
 	assert(ck != NULL);
@@ -4629,7 +4629,7 @@ on_drop_ck_constraint_commit(struct trigger *trigger, void * /* event */)
 
 /** Rollback DELETE check constraint. */
 static void
-on_drop_ck_constraint_rollback(struct trigger *trigger, void * /* event */)
+on_drop_ck_constraint_rollback(struct lua_trigger *trigger, void * /* event */)
 {
 	struct ck_constraint *ck = (struct ck_constraint *)trigger->data;
 	assert(ck != NULL);
@@ -4644,7 +4644,7 @@ on_drop_ck_constraint_rollback(struct trigger *trigger, void * /* event */)
 
 /** Commit REPLACE check constraint. */
 static void
-on_replace_ck_constraint_commit(struct trigger *trigger, void * /* event */)
+on_replace_ck_constraint_commit(struct lua_trigger *trigger, void * /* event */)
 {
 	struct ck_constraint *ck = (struct ck_constraint *)trigger->data;
 	if (ck != NULL)
@@ -4653,7 +4653,7 @@ on_replace_ck_constraint_commit(struct trigger *trigger, void * /* event */)
 
 /** Rollback REPLACE check constraint. */
 static void
-on_replace_ck_constraint_rollback(struct trigger *trigger, void * /* event */)
+on_replace_ck_constraint_rollback(struct lua_trigger *trigger, void * /* event */)
 {
 	struct ck_constraint *ck = (struct ck_constraint *)trigger->data;
 	assert(ck != NULL);
@@ -4670,7 +4670,7 @@ on_replace_ck_constraint_rollback(struct trigger *trigger, void * /* event */)
 
 /** A trigger invoked on replace in the _ck_constraint space. */
 static void
-on_replace_dd_ck_constraint(struct trigger * /* trigger*/, void *event)
+on_replace_dd_ck_constraint(struct lua_trigger * /* trigger*/, void *event)
 {
 	struct txn *txn = (struct txn *) event;
 	struct txn_stmt *stmt = txn_current_stmt(txn);
@@ -4680,8 +4680,8 @@ on_replace_dd_ck_constraint(struct trigger * /* trigger*/, void *event)
 		tuple_field_u32_xc(old_tuple != NULL ? old_tuple : new_tuple,
 				   BOX_CK_CONSTRAINT_FIELD_SPACE_ID);
 	struct space *space = space_cache_find_xc(space_id);
-	struct trigger *on_rollback = txn_alter_trigger_new(NULL, NULL);
-	struct trigger *on_commit = txn_alter_trigger_new(NULL, NULL);
+	struct lua_trigger *on_rollback = txn_alter_trigger_new(NULL, NULL);
+	struct lua_trigger *on_commit = txn_alter_trigger_new(NULL, NULL);
 
 	if (new_tuple != NULL) {
 		bool is_deferred =
@@ -4758,7 +4758,7 @@ on_replace_dd_ck_constraint(struct trigger * /* trigger*/, void *event)
 
 /** A trigger invoked on replace in the _func_index space. */
 static void
-on_replace_dd_func_index(struct trigger *trigger, void *event)
+on_replace_dd_func_index(struct lua_trigger *trigger, void *event)
 {
 	(void) trigger;
 	struct txn *txn = (struct txn *) event;
@@ -4820,67 +4820,67 @@ on_replace_dd_func_index(struct trigger *trigger, void *event)
 	scoped_guard.is_active = false;
 }
 
-struct trigger alter_space_on_replace_space = {
+struct lua_trigger alter_space_on_replace_space = {
 	RLIST_LINK_INITIALIZER, on_replace_dd_space, NULL, NULL
 };
 
-struct trigger alter_space_on_replace_index = {
+struct lua_trigger alter_space_on_replace_index = {
 	RLIST_LINK_INITIALIZER, on_replace_dd_index, NULL, NULL
 };
 
-struct trigger on_replace_truncate = {
+struct lua_trigger on_replace_truncate = {
 	RLIST_LINK_INITIALIZER, on_replace_dd_truncate, NULL, NULL
 };
 
-struct trigger on_replace_schema = {
+struct lua_trigger on_replace_schema = {
 	RLIST_LINK_INITIALIZER, on_replace_dd_schema, NULL, NULL
 };
 
-struct trigger on_replace_user = {
+struct lua_trigger on_replace_user = {
 	RLIST_LINK_INITIALIZER, on_replace_dd_user, NULL, NULL
 };
 
-struct trigger on_replace_func = {
+struct lua_trigger on_replace_func = {
 	RLIST_LINK_INITIALIZER, on_replace_dd_func, NULL, NULL
 };
 
-struct trigger on_replace_collation = {
+struct lua_trigger on_replace_collation = {
 	RLIST_LINK_INITIALIZER, on_replace_dd_collation, NULL, NULL
 };
 
-struct trigger on_replace_priv = {
+struct lua_trigger on_replace_priv = {
 	RLIST_LINK_INITIALIZER, on_replace_dd_priv, NULL, NULL
 };
 
-struct trigger on_replace_cluster = {
+struct lua_trigger on_replace_cluster = {
 	RLIST_LINK_INITIALIZER, on_replace_dd_cluster, NULL, NULL
 };
 
-struct trigger on_replace_sequence = {
+struct lua_trigger on_replace_sequence = {
 	RLIST_LINK_INITIALIZER, on_replace_dd_sequence, NULL, NULL
 };
 
-struct trigger on_replace_sequence_data = {
+struct lua_trigger on_replace_sequence_data = {
 	RLIST_LINK_INITIALIZER, on_replace_dd_sequence_data, NULL, NULL
 };
 
-struct trigger on_replace_space_sequence = {
+struct lua_trigger on_replace_space_sequence = {
 	RLIST_LINK_INITIALIZER, on_replace_dd_space_sequence, NULL, NULL
 };
 
-struct trigger on_replace_trigger = {
+struct lua_trigger on_replace_trigger = {
 	RLIST_LINK_INITIALIZER, on_replace_dd_trigger, NULL, NULL
 };
 
-struct trigger on_replace_fk_constraint = {
+struct lua_trigger on_replace_fk_constraint = {
 	RLIST_LINK_INITIALIZER, on_replace_dd_fk_constraint, NULL, NULL
 };
 
-struct trigger on_replace_ck_constraint = {
+struct lua_trigger on_replace_ck_constraint = {
 	RLIST_LINK_INITIALIZER, on_replace_dd_ck_constraint, NULL, NULL
 };
 
-struct trigger on_replace_func_index = {
+struct lua_trigger on_replace_func_index = {
 	RLIST_LINK_INITIALIZER, on_replace_dd_func_index, NULL, NULL
 };
 
diff --git a/src/box/alter.h b/src/box/alter.h
index 1bc837359..99d72cb96 100644
--- a/src/box/alter.h
+++ b/src/box/alter.h
@@ -30,23 +30,23 @@
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#include "trigger.h"
+#include "lib/core/trigger.h"
 
-extern struct trigger alter_space_on_replace_space;
-extern struct trigger alter_space_on_replace_index;
-extern struct trigger on_replace_truncate;
-extern struct trigger on_replace_schema;
-extern struct trigger on_replace_user;
-extern struct trigger on_replace_func;
-extern struct trigger on_replace_collation;
-extern struct trigger on_replace_priv;
-extern struct trigger on_replace_cluster;
-extern struct trigger on_replace_sequence;
-extern struct trigger on_replace_sequence_data;
-extern struct trigger on_replace_space_sequence;
-extern struct trigger on_replace_trigger;
-extern struct trigger on_replace_fk_constraint;
-extern struct trigger on_replace_ck_constraint;
-extern struct trigger on_replace_func_index;
+extern struct lua_trigger alter_space_on_replace_space;
+extern struct lua_trigger alter_space_on_replace_index;
+extern struct lua_trigger on_replace_truncate;
+extern struct lua_trigger on_replace_schema;
+extern struct lua_trigger on_replace_user;
+extern struct lua_trigger on_replace_func;
+extern struct lua_trigger on_replace_collation;
+extern struct lua_trigger on_replace_priv;
+extern struct lua_trigger on_replace_cluster;
+extern struct lua_trigger on_replace_sequence;
+extern struct lua_trigger on_replace_sequence_data;
+extern struct lua_trigger on_replace_space_sequence;
+extern struct lua_trigger on_replace_trigger;
+extern struct lua_trigger on_replace_fk_constraint;
+extern struct lua_trigger on_replace_ck_constraint;
+extern struct lua_trigger on_replace_func_index;
 
 #endif /* INCLUDES_TARANTOOL_BOX_ALTER_H */
diff --git a/src/box/applier.cc b/src/box/applier.cc
index 6239fcfd3..ba0fd5605 100644
--- a/src/box/applier.cc
+++ b/src/box/applier.cc
@@ -42,7 +42,7 @@
 #include "replication.h"
 #include "iproto_constants.h"
 #include "version.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 #include "xrow_io.h"
 #include "error.h"
 #include "session.h"
@@ -612,7 +612,7 @@ applier_read_tx(struct applier *applier, struct stailq *rows)
 }
 
 static void
-applier_txn_rollback_cb(struct trigger *trigger, void *event)
+applier_txn_rollback_cb(struct lua_trigger *trigger, void *event)
 {
 	(void) trigger;
 	/* Setup shared applier diagnostic area. */
@@ -625,7 +625,7 @@ applier_txn_rollback_cb(struct trigger *trigger, void *event)
 }
 
 static void
-applier_txn_commit_cb(struct trigger *trigger, void *event)
+applier_txn_commit_cb(struct lua_trigger *trigger, void *event)
 {
 	(void) trigger;
 	/* Broadcast the commit event across all appliers. */
@@ -712,11 +712,11 @@ applier_apply_tx(struct stailq *rows)
 	}
 
 	/* We are ready to submit txn to wal. */
-	struct trigger *on_rollback, *on_commit;
-	on_rollback = (struct trigger *)region_alloc(&txn->region,
-						     sizeof(struct trigger));
-	on_commit = (struct trigger *)region_alloc(&txn->region,
-						   sizeof(struct trigger));
+	struct lua_trigger *on_rollback, *on_commit;
+	on_rollback = (struct lua_trigger *)region_alloc(&txn->region,
+						     sizeof(struct lua_trigger));
+	on_commit = (struct lua_trigger *)region_alloc(&txn->region,
+						   sizeof(struct lua_trigger));
 	if (on_rollback == NULL || on_commit == NULL)
 		goto rollback;
 
@@ -746,7 +746,7 @@ fail:
  * A trigger to update an applier state after a replication commit.
  */
 static void
-applier_on_commit(struct trigger *trigger, void *event)
+applier_on_commit(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct applier *applier = (struct applier *)trigger->data;
@@ -757,7 +757,7 @@ applier_on_commit(struct trigger *trigger, void *event)
  * A trigger to update an applier state after a replication rollback.
  */
 static void
-applier_on_rollback(struct trigger *trigger, void *event)
+applier_on_rollback(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct applier *applier = (struct applier *)trigger->data;
@@ -873,11 +873,11 @@ applier_subscribe(struct applier *applier)
 	applier->lag = TIMEOUT_INFINITY;
 
 	/* Register triggers to handle replication commits and rollbacks. */
-	struct trigger on_commit;
+	struct lua_trigger on_commit;
 	trigger_create(&on_commit, applier_on_commit, applier, NULL);
 	trigger_add(&replicaset.applier.on_commit, &on_commit);
 
-	struct trigger on_rollback;
+	struct lua_trigger on_rollback;
 	trigger_create(&on_rollback, applier_on_rollback, applier, NULL);
 	trigger_add(&replicaset.applier.on_rollback, &on_rollback);
 
@@ -1125,14 +1125,14 @@ applier_pause(struct applier *applier)
 }
 
 struct applier_on_state {
-	struct trigger base;
+	struct lua_trigger base;
 	struct applier *applier;
 	enum applier_state desired_state;
 	struct fiber_cond wakeup;
 };
 
 static void
-applier_on_state_f(struct trigger *trigger, void *event)
+applier_on_state_f(struct lua_trigger *trigger, void *event)
 {
 	(void) event;
 	struct applier_on_state *on_state =
diff --git a/src/box/applier.h b/src/box/applier.h
index b406e6aaf..ea812768d 100644
--- a/src/box/applier.h
+++ b/src/box/applier.h
@@ -38,7 +38,7 @@
 #include <small/ibuf.h>
 
 #include "fiber_cond.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 #include "trivia/util.h"
 #include "uuid/tt_uuid.h"
 #include "uri/uri.h"
diff --git a/src/box/ck_constraint.c b/src/box/ck_constraint.c
index 1cde27022..0df9301ba 100644
--- a/src/box/ck_constraint.c
+++ b/src/box/ck_constraint.c
@@ -176,7 +176,7 @@ ck_constraint_program_run(struct ck_constraint *ck_constraint,
 }
 
 void
-ck_constraint_on_replace_trigger(struct trigger *trigger, void *event)
+ck_constraint_on_replace_trigger(struct lua_trigger *trigger, void *event)
 {
 	(void) trigger;
 	struct txn *txn = (struct txn *) event;
diff --git a/src/box/ck_constraint.h b/src/box/ck_constraint.h
index f26f77a38..9050f6a31 100644
--- a/src/box/ck_constraint.h
+++ b/src/box/ck_constraint.h
@@ -42,7 +42,7 @@ struct space;
 struct space_def;
 struct sql_stmt;
 struct Expr;
-struct trigger;
+struct lua_trigger;
 
 /** Supported languages of ck constraint. */
 enum ck_constraint_language {
@@ -199,7 +199,7 @@ ck_constraint_delete(struct ck_constraint *ck_constraint);
  * The diag message is set.
  */
 void
-ck_constraint_on_replace_trigger(struct trigger *trigger, void *event);
+ck_constraint_on_replace_trigger(struct lua_trigger *trigger, void *event);
 
 /**
  * Find check constraint object in space by given name and
diff --git a/src/box/error.cc b/src/box/error.cc
index 47dce3305..92aa2a727 100644
--- a/src/box/error.cc
+++ b/src/box/error.cc
@@ -33,7 +33,7 @@
 
 #include "fiber.h"
 #include "rmean.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 #include "vclock.h"
 #include "schema.h"
 
diff --git a/src/box/iproto.cc b/src/box/iproto.cc
index 8f899fed8..00f76dbf0 100644
--- a/src/box/iproto.cc
+++ b/src/box/iproto.cc
@@ -1506,7 +1506,7 @@ error:
 }
 
 static void
-tx_process_call_on_yield(struct trigger *trigger, void *event)
+tx_process_call_on_yield(struct lua_trigger *trigger, void *event)
 {
 	(void)event;
 	struct iproto_msg *msg = (struct iproto_msg *)trigger->data;
@@ -1527,7 +1527,7 @@ tx_process_call(struct cmsg *m)
 	 * input on yield to avoid stalling other connections by
 	 * a long polling request.
 	 */
-	struct trigger fiber_on_yield;
+	struct lua_trigger fiber_on_yield;
 	trigger_create(&fiber_on_yield, tx_process_call_on_yield, msg, NULL);
 	trigger_add(&fiber()->on_yield, &fiber_on_yield);
 
diff --git a/src/box/lua/call.c b/src/box/lua/call.c
index 631003c84..0a480458c 100644
--- a/src/box/lua/call.c
+++ b/src/box/lua/call.c
@@ -953,7 +953,7 @@ lbox_func_delete(struct lua_State *L, struct func *func)
 }
 
 static void
-lbox_func_new_or_delete(struct trigger *trigger, void *event)
+lbox_func_new_or_delete(struct lua_trigger *trigger, void *event)
 {
 	struct lua_State *L = (struct lua_State *) trigger->data;
 	struct func *func = (struct func *)event;
@@ -965,7 +965,7 @@ lbox_func_new_or_delete(struct trigger *trigger, void *event)
 		lbox_func_delete(L, func);
 }
 
-static struct trigger on_alter_func_in_lua = {
+static struct lua_trigger on_alter_func_in_lua = {
 	RLIST_LINK_INITIALIZER, lbox_func_new_or_delete, NULL, NULL
 };
 
diff --git a/src/box/lua/sequence.c b/src/box/lua/sequence.c
index bd9ec7589..4e3996ca9 100644
--- a/src/box/lua/sequence.c
+++ b/src/box/lua/sequence.c
@@ -158,7 +158,7 @@ lbox_sequence_delete(struct lua_State *L, struct sequence *seq)
 }
 
 static void
-lbox_sequence_new_or_delete(struct trigger *trigger, void *event)
+lbox_sequence_new_or_delete(struct lua_trigger *trigger, void *event)
 {
 	struct lua_State *L = trigger->data;
 	struct sequence *seq = event;
@@ -180,7 +180,7 @@ box_lua_sequence_init(struct lua_State *L)
 	luaL_register(L, "box.internal.sequence", sequence_internal_lib);
 	lua_pop(L, 1);
 
-	static struct trigger on_alter_sequence_in_lua;
+	static struct lua_trigger on_alter_sequence_in_lua;
 	trigger_create(&on_alter_sequence_in_lua,
 		       lbox_sequence_new_or_delete, L, NULL);
 	trigger_add(&on_alter_sequence, &on_alter_sequence_in_lua);
diff --git a/src/box/lua/space.cc b/src/box/lua/space.cc
index d0a7e7815..060ef7feb 100644
--- a/src/box/lua/space.cc
+++ b/src/box/lua/space.cc
@@ -501,7 +501,7 @@ box_lua_space_delete(struct lua_State *L, uint32_t id)
 }
 
 static void
-box_lua_space_new_or_delete(struct trigger *trigger, void *event)
+box_lua_space_new_or_delete(struct lua_trigger *trigger, void *event)
 {
 	struct lua_State *L = (struct lua_State *) trigger->data;
 	struct space *space = (struct space *) event;
@@ -513,7 +513,7 @@ box_lua_space_new_or_delete(struct trigger *trigger, void *event)
 	}
 }
 
-static struct trigger on_alter_space_in_lua = {
+static struct lua_trigger on_alter_space_in_lua = {
 	RLIST_LINK_INITIALIZER, box_lua_space_new_or_delete, NULL, NULL
 };
 
diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
index 8b59466b9..5a37ca0f1 100644
--- a/src/box/lua/tuple.c
+++ b/src/box/lua/tuple.c
@@ -561,7 +561,7 @@ tuple_serializer_update_options(void)
 }
 
 static void
-on_msgpack_serializer_update(struct trigger *trigger, void *event)
+on_msgpack_serializer_update(struct lua_trigger *trigger, void *event)
 {
 	(void) trigger;
 	(void) event;
diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c
index 487cfdadd..f3620c172 100644
--- a/src/box/memtx_space.c
+++ b/src/box/memtx_space.c
@@ -835,7 +835,7 @@ struct memtx_ddl_state {
 };
 
 static void
-memtx_check_on_replace(struct trigger *trigger, void *event)
+memtx_check_on_replace(struct lua_trigger *trigger, void *event)
 {
 	struct txn *txn = event;
 	struct memtx_ddl_state *state = trigger->data;
@@ -890,7 +890,7 @@ memtx_space_check_format(struct space *space, struct tuple_format *format)
 	state.rc = 0;
 	diag_create(&state.diag);
 
-	struct trigger on_replace;
+	struct lua_trigger on_replace;
 	trigger_create(&on_replace, memtx_check_on_replace, &state, NULL);
 	trigger_add(&space->on_replace, &on_replace);
 
@@ -958,7 +958,7 @@ memtx_init_ephemeral_space(struct space *space)
 }
 
 static void
-memtx_build_on_replace(struct trigger *trigger, void *event)
+memtx_build_on_replace(struct lua_trigger *trigger, void *event)
 {
 	struct txn *txn = event;
 	struct memtx_ddl_state *state = trigger->data;
@@ -1043,7 +1043,7 @@ memtx_space_build_index(struct space *src_space, struct index *new_index,
 	state.rc = 0;
 	diag_create(&state.diag);
 
-	struct trigger on_replace;
+	struct lua_trigger on_replace;
 	trigger_create(&on_replace, memtx_build_on_replace, &state, NULL);
 	trigger_add(&src_space->on_replace, &on_replace);
 
diff --git a/src/box/recovery.cc b/src/box/recovery.cc
index d122d618a..61960bb26 100644
--- a/src/box/recovery.cc
+++ b/src/box/recovery.cc
@@ -32,7 +32,7 @@
 
 #include "small/rlist.h"
 #include "scoped_guard.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 #include "fiber.h"
 #include "xlog.h"
 #include "xrow.h"
diff --git a/src/box/relay.cc b/src/box/relay.cc
index 74588cba7..b94a7691c 100644
--- a/src/box/relay.cc
+++ b/src/box/relay.cc
@@ -46,7 +46,7 @@
 #include "iproto_constants.h"
 #include "recovery.h"
 #include "replication.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 #include "vclock.h"
 #include "version.h"
 #include "xrow.h"
@@ -412,7 +412,7 @@ tx_gc_advance(struct cmsg *msg)
 }
 
 static void
-relay_on_close_log_f(struct trigger *trigger, void * /* event */)
+relay_on_close_log_f(struct lua_trigger *trigger, void * /* event */)
 {
 	static const struct cmsg_hop route[] = {
 		{tx_gc_advance, NULL}
@@ -569,7 +569,7 @@ relay_subscribe_f(va_list ap)
 		  &relay->relay_pipe, NULL, NULL, cbus_process);
 
 	/* Setup garbage collection trigger. */
-	struct trigger on_close_log = {
+	struct lua_trigger on_close_log = {
 		RLIST_LINK_INITIALIZER, relay_on_close_log_f, relay, NULL
 	};
 	trigger_add(&r->on_close_log, &on_close_log);
diff --git a/src/box/replication.cc b/src/box/replication.cc
index ee102a597..1a4d5f3d3 100644
--- a/src/box/replication.cc
+++ b/src/box/replication.cc
@@ -148,7 +148,7 @@ replica_is_orphan(struct replica *replica)
 }
 
 static void
-replica_on_applier_state_f(struct trigger *trigger, void *event);
+replica_on_applier_state_f(struct lua_trigger *trigger, void *event);
 
 static struct replica *
 replica_new(void)
@@ -402,7 +402,7 @@ replica_on_applier_disconnect(struct replica *replica)
 }
 
 static void
-replica_on_applier_state_f(struct trigger *trigger, void *event)
+replica_on_applier_state_f(struct lua_trigger *trigger, void *event)
 {
 	(void)event;
 	struct replica *replica = container_of(trigger,
@@ -571,12 +571,12 @@ struct replicaset_connect_state {
 };
 
 struct applier_on_connect {
-	struct trigger base;
+	struct lua_trigger base;
 	struct replicaset_connect_state *state;
 };
 
 static void
-applier_on_connect_f(struct trigger *trigger, void *event)
+applier_on_connect_f(struct lua_trigger *trigger, void *event)
 {
 	struct applier_on_connect *on_connect = container_of(trigger,
 					struct applier_on_connect, base);
diff --git a/src/box/replication.h b/src/box/replication.h
index 19f283c7d..64104e79a 100644
--- a/src/box/replication.h
+++ b/src/box/replication.h
@@ -31,7 +31,7 @@
  * SUCH DAMAGE.
  */
 #include "uuid/tt_uuid.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 #include <stdint.h>
 #define RB_COMPACT 1
 #include <small/rb.h> /* replicaset_t */
@@ -276,7 +276,7 @@ struct replica {
 	/**
 	 * Trigger invoked when the applier changes its state.
 	 */
-	struct trigger on_applier_state;
+	struct lua_trigger on_applier_state;
 	/**
 	 * During initial connect or reconnect we require applier
 	 * to sync with the master before the replica can leave
diff --git a/src/box/schema.cc b/src/box/schema.cc
index 8d8aae448..3176555b2 100644
--- a/src/box/schema.cc
+++ b/src/box/schema.cc
@@ -264,7 +264,7 @@ static void
 sc_space_new(uint32_t id, const char *name,
 	     struct key_part_def *key_parts,
 	     uint32_t key_part_count,
-	     struct trigger *replace_trigger)
+	     struct lua_trigger *replace_trigger)
 {
 	struct key_def *key_def = key_def_new(key_parts, key_part_count, false);
 	if (key_def == NULL)
diff --git a/src/box/session.cc b/src/box/session.cc
index 59bf226dd..618cf48ad 100644
--- a/src/box/session.cc
+++ b/src/box/session.cc
@@ -32,7 +32,7 @@
 #include "fiber.h"
 #include "memory.h"
 #include "assoc.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 #include "user.h"
 #include "error.h"
 #include "tt_static.h"
@@ -81,7 +81,7 @@ sid_max()
 }
 
 static void
-session_on_stop(struct trigger *trigger, void * /* event */)
+session_on_stop(struct lua_trigger *trigger, void * /* event */)
 {
 	/*
 	 * Remove on_stop trigger from the fiber, otherwise the
diff --git a/src/box/session.h b/src/box/session.h
index 85a2d940b..2699f72ff 100644
--- a/src/box/session.h
+++ b/src/box/session.h
@@ -32,7 +32,7 @@
  */
 #include <inttypes.h>
 #include <stdbool.h>
-#include "trigger.h"
+#include "lib/core/trigger.h"
 #include "fiber.h"
 #include "user.h"
 #include "authentication.h"
@@ -104,7 +104,7 @@ struct session {
 	/** Session user id and global grants */
 	struct credentials credentials;
 	/** Trigger for fiber on_stop to cleanup created on-demand session */
-	struct trigger fiber_on_stop;
+	struct lua_trigger fiber_on_stop;
 };
 
 struct session_vtab {
diff --git a/src/box/space.c b/src/box/space.c
index 042be042c..f8a184c4e 100644
--- a/src/box/space.c
+++ b/src/box/space.c
@@ -34,7 +34,7 @@
 #include <string.h>
 #include "bit/bit.h"
 #include "tuple_format.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 #include "user.h"
 #include "session.h"
 #include "txn.h"
@@ -590,8 +590,8 @@ space_add_ck_constraint(struct space *space, struct ck_constraint *ck)
 {
 	rlist_add_entry(&space->ck_constraint, ck, link);
 	if (space->ck_constraint_trigger == NULL) {
-		struct trigger *ck_trigger =
-			(struct trigger *) malloc(sizeof(*ck_trigger));
+		struct lua_trigger *ck_trigger =
+			(struct lua_trigger *) malloc(sizeof(*ck_trigger));
 		if (ck_trigger == NULL) {
 			diag_set(OutOfMemory, sizeof(*ck_trigger), "malloc",
 				 "ck_trigger");
@@ -610,7 +610,7 @@ space_remove_ck_constraint(struct space *space, struct ck_constraint *ck)
 {
 	rlist_del_entry(ck, link);
 	if (rlist_empty(&space->ck_constraint)) {
-		struct trigger *ck_trigger = space->ck_constraint_trigger;
+		struct lua_trigger *ck_trigger = space->ck_constraint_trigger;
 		trigger_clear(ck_trigger);
 		ck_trigger->destroy(ck_trigger);
 		space->ck_constraint_trigger = NULL;
diff --git a/src/box/space.h b/src/box/space.h
index 7926aa65e..8064a046d 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -218,7 +218,7 @@ struct space {
 	 */
 	struct rlist ck_constraint;
 	/** Trigger that performs ck constraint validation. */
-	struct trigger *ck_constraint_trigger;
+	struct lua_trigger *ck_constraint_trigger;
 	/**
 	 * Lists of foreign key constraints. In SQL terms child
 	 * space is the "from" table i.e. the table that contains
diff --git a/src/box/txn.c b/src/box/txn.c
index 38b1b595f..a8b580756 100644
--- a/src/box/txn.c
+++ b/src/box/txn.c
@@ -41,10 +41,10 @@ double too_long_threshold;
 static struct stailq txn_cache = {NULL, &txn_cache.first};
 
 static void
-txn_on_stop(struct trigger *trigger, void *event);
+txn_on_stop(struct lua_trigger *trigger, void *event);
 
 static void
-txn_on_yield(struct trigger *trigger, void *event);
+txn_on_yield(struct lua_trigger *trigger, void *event);
 
 static void
 txn_run_rollback_triggers(struct txn *txn, struct rlist *triggers);
@@ -840,7 +840,7 @@ txn_savepoint_release(struct txn_savepoint *svp)
 }
 
 static void
-txn_on_stop(struct trigger *trigger, void *event)
+txn_on_stop(struct lua_trigger *trigger, void *event)
 {
 	(void) trigger;
 	(void) event;
@@ -865,7 +865,7 @@ txn_on_stop(struct trigger *trigger, void *event)
  * interactive transaction support in memtx.
  */
 static void
-txn_on_yield(struct trigger *trigger, void *event)
+txn_on_yield(struct lua_trigger *trigger, void *event)
 {
 	(void) trigger;
 	(void) event;
diff --git a/src/box/txn.h b/src/box/txn.h
index da12feebf..016d6a68b 100644
--- a/src/box/txn.h
+++ b/src/box/txn.h
@@ -33,7 +33,7 @@
 
 #include <stdbool.h>
 #include "salad/stailq.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 #include "fiber.h"
 #include "space.h"
 
@@ -205,12 +205,12 @@ struct txn {
 	 * Triggers on fiber yield to abort transaction for
 	 * for in-memory engine.
 	 */
-	struct trigger fiber_on_yield;
+	struct lua_trigger fiber_on_yield;
 	/**
 	 * Trigger on fiber stop, to rollback transaction
 	 * in case a fiber stops (all engines).
 	 */
-	struct trigger fiber_on_stop;
+	struct lua_trigger fiber_on_stop;
 	/** Commit and rollback triggers. */
 	struct rlist on_commit, on_rollback;
 	/**
@@ -309,14 +309,14 @@ txn_init_triggers(struct txn *txn)
 }
 
 static inline void
-txn_on_commit(struct txn *txn, struct trigger *trigger)
+txn_on_commit(struct txn *txn, struct lua_trigger *trigger)
 {
 	txn_init_triggers(txn);
 	trigger_add(&txn->on_commit, trigger);
 }
 
 static inline void
-txn_on_rollback(struct txn *txn, struct trigger *trigger)
+txn_on_rollback(struct txn *txn, struct lua_trigger *trigger)
 {
 	txn_init_triggers(txn);
 	trigger_add(&txn->on_rollback, trigger);
@@ -338,14 +338,14 @@ txn_stmt_init_triggers(struct txn_stmt *stmt)
 }
 
 static inline void
-txn_stmt_on_commit(struct txn_stmt *stmt, struct trigger *trigger)
+txn_stmt_on_commit(struct txn_stmt *stmt, struct lua_trigger *trigger)
 {
 	txn_stmt_init_triggers(stmt);
 	trigger_add(&stmt->on_commit, trigger);
 }
 
 static inline void
-txn_stmt_on_rollback(struct txn_stmt *stmt, struct trigger *trigger)
+txn_stmt_on_rollback(struct txn_stmt *stmt, struct lua_trigger *trigger)
 {
 	txn_stmt_init_triggers(stmt);
 	trigger_add(&stmt->on_rollback, trigger);
diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index 23910795f..f01dc77ac 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -69,7 +69,7 @@
 #include "xstream.h"
 #include "info/info.h"
 #include "column_mask.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 #include "wal.h" /* wal_mode() */
 
 /**
@@ -163,7 +163,7 @@ struct vinyl_iterator {
 	 */
 	struct vy_tx tx_autocommit;
 	/** Trigger invoked when tx ends to close the iterator. */
-	struct trigger on_tx_destroy;
+	struct lua_trigger on_tx_destroy;
 };
 
 struct vinyl_snapshot_iterator {
@@ -176,7 +176,7 @@ static const struct engine_vtab vinyl_engine_vtab;
 static const struct space_vtab vinyl_space_vtab;
 static const struct index_vtab vinyl_index_vtab;
 
-static struct trigger on_replace_vinyl_deferred_delete;
+static struct lua_trigger on_replace_vinyl_deferred_delete;
 
 /** Extract vy_env from an engine object. */
 static inline struct vy_env *
@@ -1011,7 +1011,7 @@ struct vy_check_format_ctx {
  * tuples against a new format.
  */
 static void
-vy_check_format_on_replace(struct trigger *trigger, void *event)
+vy_check_format_on_replace(struct lua_trigger *trigger, void *event)
 {
 	struct txn *txn = event;
 	struct txn_stmt *stmt = txn_current_stmt(txn);
@@ -1071,7 +1071,7 @@ vinyl_space_check_format(struct space *space, struct tuple_format *format)
 	/* See the comment in vinyl_space_build_index(). */
 	txn_can_yield(txn, true);
 
-	struct trigger on_replace;
+	struct lua_trigger on_replace;
 	struct vy_check_format_ctx ctx;
 	ctx.format = format;
 	ctx.is_failed = false;
@@ -3421,7 +3421,7 @@ fail:
 /* {{{ Cursor */
 
 static void
-vinyl_iterator_on_tx_destroy(struct trigger *trigger, void *event)
+vinyl_iterator_on_tx_destroy(struct lua_trigger *trigger, void *event)
 {
 	(void)event;
 	struct vinyl_iterator *it = container_of(trigger,
@@ -3792,7 +3792,7 @@ struct vy_build_ctx {
  * to the index that is currently being built.
  */
 static void
-vy_build_on_replace(struct trigger *trigger, void *event)
+vy_build_on_replace(struct lua_trigger *trigger, void *event)
 {
 	struct txn *txn = event;
 	struct txn_stmt *stmt = txn_current_stmt(txn);
@@ -4146,7 +4146,7 @@ vinyl_space_build_index(struct space *src_space, struct index *new_index,
 	 * may yield, we install an on_replace trigger to forward
 	 * DML requests issued during the build.
 	 */
-	struct trigger on_replace;
+	struct lua_trigger on_replace;
 	struct vy_build_ctx ctx;
 	ctx.lsm = new_lsm;
 	ctx.format = new_format;
@@ -4248,7 +4248,7 @@ out:
 /* {{{ Deferred DELETE handling */
 
 static void
-vy_deferred_delete_on_commit(struct trigger *trigger, void *event)
+vy_deferred_delete_on_commit(struct lua_trigger *trigger, void *event)
 {
 	struct txn *txn = event;
 	struct vy_mem *mem = trigger->data;
@@ -4263,7 +4263,7 @@ vy_deferred_delete_on_commit(struct trigger *trigger, void *event)
 }
 
 static void
-vy_deferred_delete_on_rollback(struct trigger *trigger, void *event)
+vy_deferred_delete_on_rollback(struct lua_trigger *trigger, void *event)
 {
 	(void)event;
 	struct vy_mem *mem = trigger->data;
@@ -4296,7 +4296,7 @@ vy_deferred_delete_on_rollback(struct trigger *trigger, void *event)
  * mistakenly skip both statements on recovery.
  */
 static void
-vy_deferred_delete_on_replace(struct trigger *trigger, void *event)
+vy_deferred_delete_on_replace(struct lua_trigger *trigger, void *event)
 {
 	(void)trigger;
 
@@ -4410,19 +4410,19 @@ vy_deferred_delete_on_replace(struct trigger *trigger, void *event)
 		 * which will propagate the WAL row LSN to
 		 * the LSM tree.
 		 */
-		struct trigger *on_commit = region_alloc(&txn->region,
+		struct lua_trigger *on_commit = region_alloc(&txn->region,
 							 sizeof(*on_commit));
 		if (on_commit == NULL) {
 			diag_set(OutOfMemory, sizeof(*on_commit),
-				 "region", "struct trigger");
+				 "region", "struct lua_trigger");
 			rc = -1;
 			break;
 		}
-		struct trigger *on_rollback = region_alloc(&txn->region,
+		struct lua_trigger *on_rollback = region_alloc(&txn->region,
 							   sizeof(*on_commit));
 		if (on_rollback == NULL) {
 			diag_set(OutOfMemory, sizeof(*on_commit),
-				 "region", "struct trigger");
+				 "region", "struct lua_trigger");
 			rc = -1;
 			break;
 		}
@@ -4443,7 +4443,7 @@ vy_deferred_delete_on_replace(struct trigger *trigger, void *event)
 		diag_raise();
 }
 
-static struct trigger on_replace_vinyl_deferred_delete = {
+static struct lua_trigger on_replace_vinyl_deferred_delete = {
 	RLIST_LINK_INITIALIZER, vy_deferred_delete_on_replace, NULL, NULL
 };
 
diff --git a/src/box/vy_lsm.c b/src/box/vy_lsm.c
index aa4bce9eb..c0f9c983c 100644
--- a/src/box/vy_lsm.c
+++ b/src/box/vy_lsm.c
@@ -45,7 +45,7 @@
 #include "say.h"
 #include "schema.h"
 #include "tuple.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 #include "vy_log.h"
 #include "vy_mem.h"
 #include "vy_range.h"
diff --git a/src/box/vy_scheduler.c b/src/box/vy_scheduler.c
index ee361c31f..581b550a4 100644
--- a/src/box/vy_scheduler.c
+++ b/src/box/vy_scheduler.c
@@ -511,7 +511,7 @@ vy_scheduler_reset_stat(struct vy_scheduler *scheduler)
 }
 
 static void
-vy_scheduler_on_delete_lsm(struct trigger *trigger, void *event)
+vy_scheduler_on_delete_lsm(struct lua_trigger *trigger, void *event)
 {
 	struct vy_lsm *lsm = event;
 	struct vy_scheduler *scheduler = trigger->data;
@@ -532,7 +532,7 @@ vy_scheduler_add_lsm(struct vy_scheduler *scheduler, struct vy_lsm *lsm)
 	 * Register a trigger that will remove this LSM tree from
 	 * the scheduler queues on destruction.
 	 */
-	struct trigger *trigger = malloc(sizeof(*trigger));
+	struct lua_trigger *trigger = malloc(sizeof(*trigger));
 	if (trigger == NULL) {
 		diag_set(OutOfMemory, sizeof(*trigger), "malloc", "trigger");
 		return -1;
diff --git a/src/box/vy_tx.c b/src/box/vy_tx.c
index d092e0cdb..a2dc0afbf 100644
--- a/src/box/vy_tx.c
+++ b/src/box/vy_tx.c
@@ -48,7 +48,7 @@
 #include "schema.h" /* space_cache_version */
 #include "session.h"
 #include "space.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 #include "trivia/util.h"
 #include "tuple.h"
 #include "vy_cache.h"
diff --git a/src/lib/core/cbus.c b/src/lib/core/cbus.c
index b3b1280e7..b65693185 100644
--- a/src/lib/core/cbus.c
+++ b/src/lib/core/cbus.c
@@ -32,7 +32,7 @@
 
 #include <limits.h>
 #include "fiber.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 
 /**
  * Cord interconnect.
diff --git a/src/lib/core/fiber.c b/src/lib/core/fiber.c
index b813c1739..2375ef42a 100644
--- a/src/lib/core/fiber.c
+++ b/src/lib/core/fiber.c
@@ -40,7 +40,7 @@
 
 #include "assoc.h"
 #include "memory.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 
 #include "third_party/valgrind/memcheck.h"
 
@@ -1299,7 +1299,7 @@ cord_cojoin(struct cord *cord)
 }
 
 void
-break_ev_loop_f(struct trigger *trigger, void *event)
+break_ev_loop_f(struct lua_trigger *trigger, void *event)
 {
 	(void) trigger;
 	(void) event;
@@ -1323,7 +1323,7 @@ cord_costart_thread_func(void *arg)
 	if (f == NULL)
 		return NULL;
 
-	struct trigger break_ev_loop = {
+	struct lua_trigger break_ev_loop = {
 		RLIST_LINK_INITIALIZER, break_ev_loop_f, NULL, NULL
 	};
 	/*
diff --git a/src/lib/core/trigger.cc b/src/lib/core/trigger.cc
index 4a43151e1..75c872569 100644
--- a/src/lib/core/trigger.cc
+++ b/src/lib/core/trigger.cc
@@ -29,14 +29,14 @@
  * SUCH DAMAGE.
  */
 
-#include "trigger.h"
+#include "lib/core/trigger.h"
 #include "exception.h"
 
 int
 trigger_run(struct rlist *list, void *event)
 {
 	try {
-		struct trigger *trigger, *tmp;
+		struct lua_trigger *trigger, *tmp;
 		rlist_foreach_entry_safe(trigger, list, link, tmp)
 			trigger->run(trigger, event);
 	} catch (Exception *e) {
@@ -49,7 +49,7 @@ int
 trigger_run_reverse(struct rlist *list, void *event)
 {
 	try {
-		struct trigger *trigger, *tmp;
+		struct lua_trigger *trigger, *tmp;
 		rlist_foreach_entry_safe_reverse(trigger, list, link, tmp)
 			trigger->run(trigger, event);
 	} catch (Exception *e) {
diff --git a/src/lib/core/trigger.h b/src/lib/core/trigger.h
index 76fa6345d..8fe19498e 100644
--- a/src/lib/core/trigger.h
+++ b/src/lib/core/trigger.h
@@ -39,11 +39,11 @@ extern "C" {
  * Type of the callback which may be invoked
  * on an event.
  */
-struct trigger;
-typedef void (*trigger_f)(struct trigger *trigger, void *event);
-typedef void (*trigger_f0)(struct trigger *trigger);
+struct lua_trigger;
+typedef void (*trigger_f)(struct lua_trigger *trigger, void *event);
+typedef void (*trigger_f0)(struct lua_trigger *trigger);
 
-struct trigger
+struct lua_trigger
 {
 	struct rlist link;
 	trigger_f run;
@@ -60,7 +60,7 @@ struct trigger
 };
 
 static inline void
-trigger_create(struct trigger *trigger, trigger_f run, void *data,
+trigger_create(struct lua_trigger *trigger, trigger_f run, void *data,
 	       trigger_f0 destroy)
 {
 	rlist_create(&trigger->link);
@@ -70,7 +70,7 @@ trigger_create(struct trigger *trigger, trigger_f run, void *data,
 }
 
 static inline void
-trigger_add(struct rlist *list, struct trigger *trigger)
+trigger_add(struct rlist *list, struct lua_trigger *trigger)
 {
 	/*
 	 * New triggers are pushed to the beginning of the list.
@@ -85,9 +85,9 @@ trigger_add(struct rlist *list, struct trigger *trigger)
 }
 
 static inline void
-trigger_add_unique(struct rlist *list, struct trigger *trigger)
+trigger_add_unique(struct rlist *list, struct lua_trigger *trigger)
 {
-	struct trigger *trg;
+	struct lua_trigger *trg;
 	rlist_foreach_entry(trg, list, link) {
 		if (trg->data == trigger->data && trg->run == trigger->run)
 			return;
@@ -96,7 +96,7 @@ trigger_add_unique(struct rlist *list, struct trigger *trigger)
 }
 
 static inline void
-trigger_clear(struct trigger *trigger)
+trigger_clear(struct lua_trigger *trigger)
 {
 	rlist_del_entry(trigger, link);
 }
@@ -105,7 +105,7 @@ trigger_clear(struct trigger *trigger)
 static inline void
 trigger_destroy(struct rlist *list)
 {
-	struct trigger *trigger, *tmp;
+	struct lua_trigger *trigger, *tmp;
 	rlist_foreach_entry_safe(trigger, list, link, tmp) {
 		trigger_clear(trigger);
 		if (trigger->destroy)
diff --git a/src/lib/swim/swim.c b/src/lib/swim/swim.c
index 9a7b4ce85..752aff60e 100644
--- a/src/lib/swim/swim.c
+++ b/src/lib/swim/swim.c
@@ -37,7 +37,7 @@
 #include "msgpuck.h"
 #include "assoc.h"
 #include "sio.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 #include "errinj.h"
 #define HEAP_FORWARD_DECLARATION
 #include "salad/heap.h"
diff --git a/src/lua/swim.c b/src/lua/swim.c
index ae916bf78..59a2d2dc3 100644
--- a/src/lua/swim.c
+++ b/src/lua/swim.c
@@ -29,8 +29,8 @@
  * SUCH DAMAGE.
  */
 #include "swim/swim.h"
-#include "trigger.h"
 #include "diag.h"
+#include "lua/trigger.h"
 #include "lua/utils.h"
 
 static uint32_t ctid_swim_member_ptr;
diff --git a/src/lua/trigger.c b/src/lua/trigger.c
index 4803e85c5..7bfbb7a71 100644
--- a/src/lua/trigger.c
+++ b/src/lua/trigger.c
@@ -35,7 +35,7 @@
 
 struct lbox_trigger
 {
-	struct trigger base;
+	struct lua_trigger base;
 	/** A reference to Lua trigger function. */
 	int ref;
 	/*
@@ -53,7 +53,7 @@ struct lbox_trigger
 };
 
 static void
-lbox_trigger_destroy(struct trigger *ptr)
+lbox_trigger_destroy(struct lua_trigger *ptr)
 {
 	if (tarantool_L) {
 		struct lbox_trigger *trigger = (struct lbox_trigger *) ptr;
@@ -63,7 +63,7 @@ lbox_trigger_destroy(struct trigger *ptr)
 }
 
 static void
-lbox_trigger_run(struct trigger *ptr, void *event)
+lbox_trigger_run(struct lua_trigger *ptr, void *event)
 {
 	struct lbox_trigger *trigger = (struct lbox_trigger *) ptr;
 	/*
diff --git a/src/lua/trigger.h b/src/lua/trigger.h
index 1d1dcce35..09f4ecf9e 100644
--- a/src/lua/trigger.h
+++ b/src/lua/trigger.h
@@ -30,7 +30,7 @@
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#include <trigger.h>
+#include "lib/core/trigger.h"
 
 #if defined(__cplusplus)
 extern "C" {
diff --git a/src/lua/utils.h b/src/lua/utils.h
index f8c34545a..c87b117ef 100644
--- a/src/lua/utils.h
+++ b/src/lua/utils.h
@@ -261,7 +261,7 @@ struct luaL_serializer {
 	 * general serializer. For example, tuple serializer
 	 * subscribes on msgpack.
 	 */
-	struct trigger update_trigger;
+	struct lua_trigger update_trigger;
 	/**
 	 * List of triggers on update of this serializer. To push
 	 * updates down to dependent serializers.
diff --git a/src/main.cc b/src/main.cc
index 6ccb2a47f..898324d97 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -95,7 +95,7 @@ static struct fiber *on_shutdown_fiber = NULL;
 /** A flag restricting repeated execution of tarantool_exit(). */
 static bool is_shutting_down = false;
 /** A trigger which will break the event loop on shutdown. */
-static struct trigger break_loop_trigger;
+static struct lua_trigger break_loop_trigger;
 static int exit_code = 0;
 
 double
@@ -684,7 +684,7 @@ print_help(const char *program)
 }
 
 static void
-break_loop(struct trigger *, void *)
+break_loop(struct lua_trigger *, void *)
 {
 	ev_break(loop(), EVBREAK_ALL);
 }
diff --git a/test/unit/cbus.c b/test/unit/cbus.c
index be930ab8f..156ff8961 100644
--- a/test/unit/cbus.c
+++ b/test/unit/cbus.c
@@ -2,7 +2,7 @@
 #include "fiber.h"
 #include "cbus.h"
 #include "unit.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 
 /**
  * Test triggers on cpipe flush. Cpipe flush send all buffered
@@ -31,7 +31,7 @@ struct cpipe pipe_to_main;
  * the direction from the main to the worker works in the same
  * way.
  */
-struct trigger on_flush_to_main;
+struct lua_trigger on_flush_to_main;
 
 /** Common callbacks. {{{ ------------------------------------- */
 
@@ -44,7 +44,7 @@ do_nothing(struct cmsg *m)
 
 /** Callback called on each flush to the main thread. */
 static void
-flush_cb(struct trigger *t, void *e)
+flush_cb(struct lua_trigger *t, void *e)
 {
 	(void) t;
 	(void) e;
diff --git a/test/unit/swim.c b/test/unit/swim.c
index 1371e7d26..40d08c58a 100644
--- a/test/unit/swim.c
+++ b/test/unit/swim.c
@@ -29,7 +29,7 @@
  * SUCH DAMAGE.
  */
 #include "swim_test_utils.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 #include <math.h>
 
 /**
@@ -828,7 +828,7 @@ struct trigger_ctx {
 };
 
 static void
-swim_on_member_event_save(struct trigger *t, void *event)
+swim_on_member_event_save(struct lua_trigger *t, void *event)
 {
 	struct trigger_ctx *c = (struct trigger_ctx *) t->data;
 	++c->counter;
@@ -839,7 +839,7 @@ swim_on_member_event_save(struct trigger *t, void *event)
 }
 
 static void
-swim_on_member_event_yield(struct trigger *t, void *event)
+swim_on_member_event_yield(struct lua_trigger *t, void *event)
 {
 	struct trigger_ctx *c = (struct trigger_ctx *) t->data;
 	++c->counter;
@@ -849,7 +849,7 @@ swim_on_member_event_yield(struct trigger *t, void *event)
 }
 
 static void
-swim_trigger_destroy_cb(struct trigger *t)
+swim_trigger_destroy_cb(struct lua_trigger *t)
 {
 	((struct trigger_ctx *) t->data)->is_deleted = true;
 }
@@ -872,7 +872,7 @@ swim_test_triggers(void)
 	struct trigger_ctx tctx, tctx2;
 	memset(&tctx, 0, sizeof(tctx));
 	memset(&tctx2, 0, sizeof(tctx2));
-	struct trigger *t1 = (struct trigger *) malloc(sizeof(*t1));
+	struct lua_trigger *t1 = (struct lua_trigger *) malloc(sizeof(*t1));
 	assert(t1 != NULL);
 	trigger_create(t1, swim_on_member_event_save, (void *) &tctx,
 		       swim_trigger_destroy_cb);
@@ -925,7 +925,7 @@ swim_test_triggers(void)
 	 * middle of an event processing. SWIM object should not
 	 * be deleted, until all the triggers are done.
 	 */
-	struct trigger *t2 = (struct trigger *) malloc(sizeof(*t2));
+	struct lua_trigger *t2 = (struct lua_trigger *) malloc(sizeof(*t2));
 	assert(t2 != NULL);
 	tctx2.need_sleep = true;
 	trigger_create(t2, swim_on_member_event_yield, (void *) &tctx2, NULL);
@@ -1138,4 +1138,4 @@ main()
 {
 	swim_run_test("swim.txt", main_f);
 	return test_result;
-}
\ No newline at end of file
+}
diff --git a/test/unit/swim_test_utils.c b/test/unit/swim_test_utils.c
index 7da82d93c..05fca6730 100644
--- a/test/unit/swim_test_utils.c
+++ b/test/unit/swim_test_utils.c
@@ -35,7 +35,7 @@
 #include "uuid/tt_uuid.h"
 #include "trivia/util.h"
 #include "msgpuck.h"
-#include "trigger.h"
+#include "lib/core/trigger.h"
 #include "memory.h"
 #include "random.h"
 #include <fcntl.h>
@@ -177,7 +177,7 @@ swim_cluster_id_to_uri(char *buffer, int id)
  * to yield.
  */
 void
-swim_test_event_cb(struct trigger *trigger, void *event)
+swim_test_event_cb(struct lua_trigger *trigger, void *event)
 {
 	(void) trigger;
 	struct swim_on_member_event_ctx *ctx =
@@ -195,7 +195,7 @@ swim_node_create(struct swim_node *n, int id)
 	n->generation = 0;
 	n->swim = swim_new(0);
 	assert(n->swim != NULL);
-	struct trigger *t = (struct trigger *) malloc(sizeof(*t));
+	struct lua_trigger *t = (struct lua_trigger *) malloc(sizeof(*t));
 	trigger_create(t, swim_test_event_cb, NULL, (trigger_f0) free);
 	trigger_add(swim_trigger_list_on_member_event(n->swim), t);
 
-- 
2.23.0

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [Tarantool-patches] [PATCH v1 3/9] box: introduce trigger_event_manipulation enum
  2019-10-14 16:03 [Tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space Kirill Shcherbatov
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 1/9] sql: remove redundant pointer in TriggerStep Kirill Shcherbatov
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 2/9] box: rename struct trigger to lua_trigger Kirill Shcherbatov
@ 2019-10-14 16:03 ` Kirill Shcherbatov
  2019-10-17  7:35   ` Konstantin Osipov
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 4/9] box: introduce trigger_action_timing enum Kirill Shcherbatov
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 31+ messages in thread
From: Kirill Shcherbatov @ 2019-10-14 16:03 UTC (permalink / raw)
  To: tarantool-patches, tarantool-patches, kostja.osipov, korablev

This patch introduces a new trigger_event_manipulation enum
that describes a trigger event. This is the type of operation on
the associated space for which the trigger activates.
The value could be `INSERT` (a row was inserted), `DELETE`
(a row was deleted), or `UPDATE` (a row was modified).

Using the enum instead of sql-specific TK_ token identified is
an important step to introduce a trigger_def structure in
further patches.

Needed for #4343
---
 src/box/CMakeLists.txt      |  1 +
 src/box/sql.c               | 15 ++++++++++
 src/box/sql.h               |  8 ++++++
 src/box/sql/delete.c        |  9 ++++--
 src/box/sql/fk_constraint.c |  6 ++--
 src/box/sql/insert.c        | 15 ++++++----
 src/box/sql/parse_def.h     |  7 +++--
 src/box/sql/resolve.c       | 15 ++++++----
 src/box/sql/sqlInt.h        | 23 ++++++++++-----
 src/box/sql/trigger.c       | 40 ++++++++++++++------------
 src/box/sql/update.c        | 13 +++++----
 src/box/trigger_def.c       | 33 +++++++++++++++++++++
 src/box/trigger_def.h       | 57 +++++++++++++++++++++++++++++++++++++
 test/unit/CMakeLists.txt    |  3 +-
 14 files changed, 194 insertions(+), 51 deletions(-)
 create mode 100644 src/box/trigger_def.c
 create mode 100644 src/box/trigger_def.h

diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index 9d2fcea4b..2ff5cf01e 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -102,6 +102,7 @@ add_library(box STATIC
     fk_constraint.c
     func.c
     func_def.c
+    trigger_def.c
     key_list.c
     alter.cc
     schema.cc
diff --git a/src/box/sql.c b/src/box/sql.c
index f1df55571..670e4fb3e 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1265,3 +1265,18 @@ vdbe_field_ref_prepare_tuple(struct vdbe_field_ref *field_ref,
 	vdbe_field_ref_create(field_ref, tuple, tuple_data(tuple),
 			      tuple->bsize);
 }
+
+enum trigger_event_manipulation
+trigger_event_manipulation_by_op(int op)
+{
+	switch (op) {
+	case TK_DELETE:
+		return TRIGGER_EVENT_MANIPULATION_DELETE;
+	case TK_UPDATE:
+		return TRIGGER_EVENT_MANIPULATION_UPDATE;
+	case TK_INSERT:
+		return TRIGGER_EVENT_MANIPULATION_INSERT;
+	default:
+		unreachable();
+	}
+}
diff --git a/src/box/sql.h b/src/box/sql.h
index 0fa52fc0b..ac6efeec9 100644
--- a/src/box/sql.h
+++ b/src/box/sql.h
@@ -33,6 +33,7 @@
 
 #include <stdbool.h>
 #include <stdint.h>
+#include "box/trigger_def.h"
 
 #if defined(__cplusplus)
 extern "C" {
@@ -420,6 +421,13 @@ void
 vdbe_field_ref_prepare_tuple(struct vdbe_field_ref *field_ref,
 			     struct tuple *tuple);
 
+/**
+ * Convert a given OP_INSERT/OP_UPDATE/OP_DELETE operation
+ * to trigger_event_manipulation value.
+ */
+enum trigger_event_manipulation
+trigger_event_manipulation_by_op(int op);
+
 #if defined(__cplusplus)
 } /* extern "C" { */
 #endif
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 2f73d80c9..94a1f7488 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -150,7 +150,8 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 	struct space *space = sql_lookup_space(parse, tab_list->a);
 	if (space == NULL)
 		goto delete_from_cleanup;
-	trigger_list = sql_triggers_exist(space->def, TK_DELETE,
+	trigger_list = sql_triggers_exist(space->def,
+					  TRIGGER_EVENT_MANIPULATION_DELETE,
 					  NULL, parse->sql_flags, NULL);
 	bool is_complex = trigger_list != NULL || fk_constraint_is_required(space, NULL);
 	bool is_view = space->def->opts.is_view;
@@ -486,7 +487,8 @@ sql_generate_row_delete(struct Parse *parse, struct space *space,
 
 		/* Invoke BEFORE DELETE trigger programs. */
 		int addr_start = sqlVdbeCurrentAddr(v);
-		vdbe_code_row_trigger(parse, trigger_list, TK_DELETE, NULL,
+		vdbe_code_row_trigger(parse, trigger_list,
+				      TRIGGER_EVENT_MANIPULATION_DELETE, NULL,
 				      TRIGGER_BEFORE, space, first_old_reg,
 				      onconf, label);
 
@@ -540,7 +542,8 @@ sql_generate_row_delete(struct Parse *parse, struct space *space,
 		fk_constraint_emit_actions(parse, space, first_old_reg, NULL);
 
 		/* Invoke AFTER DELETE trigger programs. */
-		vdbe_code_row_trigger(parse, trigger_list, TK_DELETE, 0,
+		vdbe_code_row_trigger(parse, trigger_list,
+				      TRIGGER_EVENT_MANIPULATION_DELETE, 0,
 				      TRIGGER_AFTER, space, first_old_reg,
 				      onconf, label);
 	}
diff --git a/src/box/sql/fk_constraint.c b/src/box/sql/fk_constraint.c
index 482220a95..2a2d52de6 100644
--- a/src/box/sql/fk_constraint.c
+++ b/src/box/sql/fk_constraint.c
@@ -897,10 +897,12 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def,
 
 	if (is_update) {
 		fk->on_update_trigger = trigger;
-		trigger->op = TK_UPDATE;
+		trigger->event_manipulation =
+			TRIGGER_EVENT_MANIPULATION_UPDATE;
 	} else {
 		fk->on_delete_trigger = trigger;
-		trigger->op = TK_DELETE;
+		trigger->event_manipulation =
+			TRIGGER_EVENT_MANIPULATION_DELETE;
 	}
 	return trigger;
 }
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 42b839166..9c612dc28 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -293,7 +293,8 @@ sqlInsert(Parse * pParse,	/* Parser context */
 	 * inserted into is a view
 	 */
 	struct space_def *space_def = space->def;
-	trigger = sql_triggers_exist(space_def, TK_INSERT, NULL,
+	trigger = sql_triggers_exist(space_def,
+				     TRIGGER_EVENT_MANIPULATION_INSERT, NULL,
 				     pParse->sql_flags, &tmask);
 
 	bool is_view = space_def->opts.is_view;
@@ -599,7 +600,8 @@ sqlInsert(Parse * pParse,	/* Parser context */
 			sql_emit_table_types(v, space_def, regCols + 1);
 
 		/* Fire BEFORE or INSTEAD OF triggers */
-		vdbe_code_row_trigger(pParse, trigger, TK_INSERT, 0,
+		vdbe_code_row_trigger(pParse, trigger,
+				      TRIGGER_EVENT_MANIPULATION_INSERT, 0,
 				      TRIGGER_BEFORE, space,
 				      regCols - space_def->field_count - 1, on_error,
 				      endOfLoop);
@@ -753,7 +755,8 @@ sqlInsert(Parse * pParse,	/* Parser context */
 
 	if (trigger != NULL) {
 		/* Code AFTER triggers */
-		vdbe_code_row_trigger(pParse, trigger, TK_INSERT, 0,
+		vdbe_code_row_trigger(pParse, trigger,
+				      TRIGGER_EVENT_MANIPULATION_INSERT, 0,
 				      TRIGGER_AFTER, space,
 				      regData - 2 - space_def->field_count, on_error,
 				      endOfLoop);
@@ -961,9 +964,9 @@ process_index:  ;
 					     part_count);
 			sql_set_multi_write(parse_context, true);
 			struct sql_trigger *trigger =
-				sql_triggers_exist(space->def, TK_DELETE, NULL,
-						   parse_context->sql_flags,
-						   NULL);
+				sql_triggers_exist(space->def,
+					TRIGGER_EVENT_MANIPULATION_DELETE, NULL,
+					parse_context->sql_flags, NULL);
 			sql_generate_row_delete(parse_context, space, trigger,
 						cursor, idx_key_reg, part_count,
 						true,
diff --git a/src/box/sql/parse_def.h b/src/box/sql/parse_def.h
index df1238b9e..cf233fc01 100644
--- a/src/box/sql/parse_def.h
+++ b/src/box/sql/parse_def.h
@@ -35,6 +35,7 @@
 #include "box/fk_constraint.h"
 #include "box/key_def.h"
 #include "box/sql.h"
+#include "box/trigger_def.h"
 
 /**
  * This file contains auxiliary structures and functions which
@@ -268,8 +269,8 @@ struct create_trigger_def {
 	struct create_entity_def base;
 	/** One of TK_BEFORE, TK_AFTER, TK_INSTEAD. */
 	int tr_tm;
-	/** One of TK_INSERT, TK_UPDATE, TK_DELETE. */
-	int op;
+	/** The trigger event: INSERT, UPDATE or DELETE. */
+	enum trigger_event_manipulation event_manipulation;
 	/** Column list if this is an UPDATE trigger. */
 	struct IdList *cols;
 	/** When clause. */
@@ -413,7 +414,7 @@ create_trigger_def_init(struct create_trigger_def *trigger_def,
 	create_entity_def_init(&trigger_def->base, ENTITY_TYPE_TRIGGER,
 			       table_name, name, if_not_exists);
 	trigger_def->tr_tm = tr_tm;
-	trigger_def->op = op;
+	trigger_def->event_manipulation = trigger_event_manipulation_by_op(op);
 	trigger_def->cols = cols;
 	trigger_def->when = when;
 }
diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c
index 6f625dc18..09129fdf8 100644
--- a/src/box/sql/resolve.c
+++ b/src/box/sql/resolve.c
@@ -39,6 +39,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include "box/schema.h"
+#include "box/trigger_def.h"
 
 /*
  * Walk the expression tree pExpr and increase the aggregate function
@@ -313,15 +314,17 @@ lookupName(Parse * pParse,	/* The parsing context */
 		 */
 		if (zTab != NULL && cntTab == 0 &&
 		    pParse->triggered_space != NULL) {
-			int op = pParse->eTriggerOp;
-			assert(op == TK_DELETE || op == TK_UPDATE
-			       || op == TK_INSERT);
+			enum trigger_event_manipulation event_manipulation =
+					pParse->trigger_event_manipulation;
 			struct space_def *space_def = NULL;
-			if (op != TK_DELETE && sqlStrICmp("new", zTab) == 0) {
+			if (event_manipulation !=
+			    TRIGGER_EVENT_MANIPULATION_DELETE &&
+			    sqlStrICmp("new", zTab) == 0) {
 				pExpr->iTable = 1;
 				space_def = pParse->triggered_space->def;
-			} else if (op != TK_INSERT
-				   && sqlStrICmp("old", zTab) == 0) {
+			} else if (event_manipulation !=
+				   TRIGGER_EVENT_MANIPULATION_INSERT &&
+				   sqlStrICmp("old", zTab) == 0) {
 				pExpr->iTable = 0;
 				space_def = pParse->triggered_space->def;
 			}
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 1b6d92cb1..7deae7087 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -72,6 +72,7 @@
 #include "box/field_def.h"
 #include "box/func.h"
 #include "box/func_def.h"
+#include "box/trigger_def.h"
 #include "box/sql.h"
 #include "box/txn.h"
 #include "trivia/util.h"
@@ -2140,7 +2141,7 @@ struct Parse {
 	uint64_t oldmask;
 	/* Mask of new.* columns referenced. */
 	uint64_t newmask;
-	u8 eTriggerOp;		/* TK_UPDATE, TK_INSERT or TK_DELETE */
+	enum trigger_event_manipulation trigger_event_manipulation;
 	u8 eOrconf;		/* Default ON CONFLICT policy for trigger steps */
 	/** Region to make SQL temp allocations. */
 	struct region region;
@@ -2298,8 +2299,14 @@ struct sql_trigger {
 	char *zName;
 	/** The ID of space the trigger refers to. */
 	uint32_t space_id;
-	/** One of TK_DELETE, TK_UPDATE, TK_INSERT. */
-	u8 op;
+	/**
+	 * The trigger event. This is the type of operation
+	 * on the associated space for which the trigger
+	 * activates. The value is `INSERT` (a row was inserted),
+	 * `DELETE` (a row was deleted), or `UPDATE` (a row was
+	 * modified).
+	 */
+	enum trigger_event_manipulation event_manipulation;
 	/** One of TRIGGER_BEFORE, TRIGGER_AFTER. */
 	u8 tr_tm;
 	/** The WHEN clause of the expression (may be NULL). */
@@ -3457,13 +3464,14 @@ vdbe_code_drop_trigger(struct Parse *parser, const char *trigger_name,
  *
  * @param space_def The definition of the space that contains
  *        the triggers.
- * @param op operation one of TK_DELETE, TK_INSERT, TK_UPDATE.
+ * @param event_manipulation The event of the trigger to be find.
  * @param changes_list Columns that change in an UPDATE statement.
  * @param sql_flags SQL flags which describe how to parse request.
  * @param[out] pMask Mask of TRIGGER_BEFORE|TRIGGER_AFTER
  */
 struct sql_trigger *
-sql_triggers_exist(struct space_def *space_def, int op,
+sql_triggers_exist(struct space_def *space_def,
+		   enum trigger_event_manipulation event_manipulation,
 		   struct ExprList *changes_list, uint32_t sql_flags,
 		   int *mask_ptr);
 
@@ -3512,7 +3520,7 @@ sql_triggers_exist(struct space_def *space_def, int op,
  *
  * @param parser Parse context.
  * @param trigger List of triggers on table.
- * @param op operation, one of TK_UPDATE, TK_INSERT, TK_DELETE.
+ * @param event_manipulation Trigger operation.
  * @param changes_list Changes list for any UPDATE OF triggers.
  * @param tr_tm One of TRIGGER_BEFORE, TRIGGER_AFTER.
  * @param space The space to code triggers from.
@@ -3522,7 +3530,8 @@ sql_triggers_exist(struct space_def *space_def, int op,
  */
 void
 vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
-		      int op, struct ExprList *changes_list, int tr_tm,
+		      enum trigger_event_manipulation event_manipulation,
+		      struct ExprList *changes_list, int tr_tm,
 		      struct space *space, int reg, int orconf, int ignore_jump);
 
 /**
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 0c387bc3b..cdf51c053 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -35,6 +35,7 @@
 
 #include "box/box.h"
 #include "box/schema.h"
+#include "box/trigger_def.h"
 #include "sqlInt.h"
 #include "tarantoolInt.h"
 #include "vdbeInt.h"
@@ -125,9 +126,7 @@ sql_trigger_begin(struct Parse *parse)
 	trigger->space_id = space_id;
 	trigger->zName = trigger_name;
 	trigger_name = NULL;
-	assert(trigger_def->op == TK_INSERT || trigger_def->op == TK_UPDATE ||
-	       trigger_def->op== TK_DELETE);
-	trigger->op = (u8) trigger_def->op;
+	trigger->event_manipulation = trigger_def->event_manipulation;
 	trigger->tr_tm = trigger_def->tr_tm;
 	trigger->pWhen = sqlExprDup(db, trigger_def->when, EXPRDUP_REDUCE);
 	trigger->pColumns = sqlIdListDup(db, trigger_def->cols);
@@ -530,7 +529,8 @@ checkColumnOverlap(IdList * pIdList, ExprList * pEList)
 }
 
 struct sql_trigger *
-sql_triggers_exist(struct space_def *space_def, int op,
+sql_triggers_exist(struct space_def *space_def,
+		   enum trigger_event_manipulation event_manipulation,
 		   struct ExprList *changes_list, uint32_t sql_flags,
 		   int *mask_ptr)
 {
@@ -539,8 +539,8 @@ sql_triggers_exist(struct space_def *space_def, int op,
 	if ((sql_flags & SQL_EnableTrigger) != 0)
 		trigger_list = space_trigger_list(space_def->id);
 	for (struct sql_trigger *p = trigger_list; p != NULL; p = p->next) {
-		if (p->op == op && checkColumnOverlap(p->pColumns,
-						      changes_list) != 0)
+		if (p->event_manipulation == event_manipulation &&
+		    checkColumnOverlap(p->pColumns, changes_list) != 0)
 			mask |= p->tr_tm;
 	}
 	if (mask_ptr != NULL)
@@ -755,19 +755,18 @@ sql_row_trigger_program(struct Parse *parser, struct sql_trigger *trigger,
 	sNC.pParse = pSubParse;
 	pSubParse->triggered_space = space;
 	pSubParse->pToplevel = pTop;
-	pSubParse->eTriggerOp = trigger->op;
+	pSubParse->trigger_event_manipulation = trigger->event_manipulation;
 	pSubParse->nQueryLoop = parser->nQueryLoop;
 
 	/* Temporary VM. */
 	struct Vdbe *v = sqlGetVdbe(pSubParse);
 	if (v != NULL) {
-		VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)",
+		VdbeComment((v, "Start: %s.%s (%s %s ON %s)",
 			     trigger->zName, onErrorText(orconf),
 			     (trigger->tr_tm ==
 			      TRIGGER_BEFORE ? "BEFORE" : "AFTER"),
-			     (trigger->op == TK_UPDATE ? "UPDATE" : ""),
-			     (trigger->op == TK_INSERT ? "INSERT" : ""),
-			     (trigger->op == TK_DELETE ? "DELETE" : ""),
+			      trigger_event_manipulation_strs[
+						trigger->event_manipulation],
 			      space->def->name));
 		sqlVdbeChangeP4(v, -1,
 				    sqlMPrintf(db, "-- TRIGGER %s",
@@ -912,16 +911,18 @@ vdbe_code_row_trigger_direct(struct Parse *parser, struct sql_trigger *trigger,
 
 void
 vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
-		      int op, struct ExprList *changes_list, int tr_tm,
+		      enum trigger_event_manipulation event_manipulation,
+		      struct ExprList *changes_list, int tr_tm,
 		      struct space *space, int reg, int orconf, int ignore_jump)
 {
-	assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE);
 	assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER);
-	assert((op == TK_UPDATE) == (changes_list != NULL));
+	assert((event_manipulation == TRIGGER_EVENT_MANIPULATION_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 &&
+		if (p->event_manipulation == event_manipulation &&
+		    p->tr_tm == tr_tm &&
 		    checkColumnOverlap(p->pColumns, changes_list)) {
 			vdbe_code_row_trigger_direct(parser, p, space, reg,
 						     orconf, ignore_jump);
@@ -934,13 +935,16 @@ sql_trigger_colmask(Parse *parser, struct sql_trigger *trigger,
 		    ExprList *changes_list, int new, int tr_tm,
 		    struct space *space, int orconf)
 {
-	const int op = changes_list != NULL ? TK_UPDATE : TK_DELETE;
+	enum trigger_event_manipulation event_manipulation =
+		changes_list != NULL ? TRIGGER_EVENT_MANIPULATION_UPDATE :
+				       TRIGGER_EVENT_MANIPULATION_DELETE;
 	uint64_t mask = 0;
 
 	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, changes_list)) {
+		if (p->event_manipulation == event_manipulation &&
+		    (tr_tm & p->tr_tm) &&
+		    checkColumnOverlap(p->pColumns, changes_list)) {
 			TriggerPrg *prg =
 				sql_row_trigger(parser, p, space, orconf);
 			if (prg != NULL)
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 2d7ebf8cd..400375bcf 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -100,8 +100,9 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 	/* Figure out if we have any triggers and if the table being
 	 * updated is a view.
 	 */
-	trigger = sql_triggers_exist(space->def, TK_UPDATE, pChanges,
-				     pParse->sql_flags, &tmask);
+	trigger = sql_triggers_exist(space->def,
+				     TRIGGER_EVENT_MANIPULATION_UPDATE,
+				     pChanges, pParse->sql_flags, &tmask);
 	is_view = space->def->opts.is_view;
 	assert(trigger != NULL || tmask == 0);
 
@@ -363,8 +364,9 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 	 */
 	if (tmask & TRIGGER_BEFORE) {
 		sql_emit_table_types(v, space->def, regNew);
-		vdbe_code_row_trigger(pParse, trigger, TK_UPDATE, pChanges,
-				      TRIGGER_BEFORE, space, regOldPk,
+		vdbe_code_row_trigger(pParse, trigger,
+				      TRIGGER_EVENT_MANIPULATION_UPDATE,
+				      pChanges, TRIGGER_BEFORE, space, regOldPk,
 				      on_error, labelContinue);
 
 		/* The row-trigger may have deleted the row being updated. In this
@@ -477,7 +479,8 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 		sqlVdbeAddOp2(v, OP_AddImm, regRowCount, 1);
 	}
 
-	vdbe_code_row_trigger(pParse, trigger, TK_UPDATE, pChanges,
+	vdbe_code_row_trigger(pParse, trigger,
+			      TRIGGER_EVENT_MANIPULATION_UPDATE, pChanges,
 			      TRIGGER_AFTER, space, regOldPk, on_error,
 			      labelContinue);
 
diff --git a/src/box/trigger_def.c b/src/box/trigger_def.c
new file mode 100644
index 000000000..4c34f5089
--- /dev/null
+++ b/src/box/trigger_def.c
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2010-2019, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "trigger_def.h"
+
+const char *trigger_event_manipulation_strs[] = {"DELETE", "UPDATE", "INSERT"};
diff --git a/src/box/trigger_def.h b/src/box/trigger_def.h
new file mode 100644
index 000000000..c1fed4f82
--- /dev/null
+++ b/src/box/trigger_def.h
@@ -0,0 +1,57 @@
+#ifndef INCLUDES_BOX_TRIGGER_DEF_H
+#define INCLUDES_BOX_TRIGGER_DEF_H
+/*
+ * Copyright 2010-2019, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+/**
+ * The trigger event. This is the type of operation
+ * on the associated space for which the trigger
+ * activates. The value is `DELETE` (a row was deleted), or
+ * `UPDATE` (a row was modified), `INSERT` (a row was inserted).
+ */
+enum trigger_event_manipulation {
+	TRIGGER_EVENT_MANIPULATION_DELETE,
+	TRIGGER_EVENT_MANIPULATION_UPDATE,
+	TRIGGER_EVENT_MANIPULATION_INSERT,
+	trigger_event_manipulation_MAX
+};
+
+extern const char *trigger_event_manipulation_strs[];
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
+
+#endif /* INCLUDES_BOX_TRIGGER_DEF_H */
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 4a57597e9..64ed5b2fe 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -141,7 +141,8 @@ add_executable(histogram.test histogram.c)
 target_link_libraries(histogram.test stat unit)
 add_executable(ratelimit.test ratelimit.c)
 target_link_libraries(ratelimit.test unit)
-add_executable(luaT_tuple_new.test luaT_tuple_new.c)
+add_executable(luaT_tuple_new.test luaT_tuple_new.c
+               ${PROJECT_SOURCE_DIR}/src/box/trigger_def.c)
 target_link_libraries(luaT_tuple_new.test unit box server core misc
     ${CURL_LIBRARIES} ${LIBYAML_LIBRARIES} ${READLINE_LIBRARIES}
     ${ICU_LIBRARIES} ${LUAJIT_LIBRARIES})
-- 
2.23.0

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [Tarantool-patches] [PATCH v1 4/9] box: introduce trigger_action_timing enum
  2019-10-14 16:03 [Tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space Kirill Shcherbatov
                   ` (2 preceding siblings ...)
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 3/9] box: introduce trigger_event_manipulation enum Kirill Shcherbatov
@ 2019-10-14 16:03 ` Kirill Shcherbatov
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 5/9] sql: use rlist to organize triggers in a list Kirill Shcherbatov
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 31+ messages in thread
From: Kirill Shcherbatov @ 2019-10-14 16:03 UTC (permalink / raw)
  To: tarantool-patches, tarantool-patches, kostja.osipov, korablev

This patch introduces a new trigger_action_timing enum
that describes a moment when the trigger activates: before or
after the triggering event.

Using the enum instead of sql-specific TK_ token identified is
an important step to introduce a trigger_def structure in
further patches.

Needed for #4343
---
 src/box/sql.c           | 15 ++++++++++++++
 src/box/sql.h           |  7 +++++++
 src/box/sql/delete.c    | 13 +++++++------
 src/box/sql/insert.c    |  6 +++---
 src/box/sql/parse_def.h |  9 ++++++---
 src/box/sql/sqlInt.h    | 35 ++++++++++++++++-----------------
 src/box/sql/trigger.c   | 43 ++++++++++++++++++++++-------------------
 src/box/sql/update.c    | 20 ++++++++++---------
 src/box/trigger_def.c   |  2 ++
 src/box/trigger_def.h   | 17 ++++++++++++++++
 10 files changed, 108 insertions(+), 59 deletions(-)

diff --git a/src/box/sql.c b/src/box/sql.c
index 670e4fb3e..134225dcc 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -1280,3 +1280,18 @@ trigger_event_manipulation_by_op(int op)
 		unreachable();
 	}
 }
+
+enum trigger_action_timing
+trigger_action_timing_by_op(int op)
+{
+	switch (op) {
+	case TK_BEFORE:
+		return TRIGGER_ACTION_TIMING_BEFORE;
+	case TK_AFTER:
+		return TRIGGER_ACTION_TIMING_AFTER;
+	case TK_INSTEAD:
+		return TRIGGER_ACTION_TIMING_INSTEAD;
+	default:
+		unreachable();
+	}
+}
diff --git a/src/box/sql.h b/src/box/sql.h
index ac6efeec9..1f289ad97 100644
--- a/src/box/sql.h
+++ b/src/box/sql.h
@@ -428,6 +428,13 @@ vdbe_field_ref_prepare_tuple(struct vdbe_field_ref *field_ref,
 enum trigger_event_manipulation
 trigger_event_manipulation_by_op(int op);
 
+/**
+ * Convert a given TK_BEFORE/TK_AFTER/TK_INSTEAD operation
+ * to trigger_event_manipulation value.
+ */
+enum trigger_action_timing
+trigger_action_timing_by_op(int op);
+
 #if defined(__cplusplus)
 } /* extern "C" { */
 #endif
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 94a1f7488..3995e15dd 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -466,8 +466,9 @@ sql_generate_row_delete(struct Parse *parse, struct space *space,
 		/* TODO: Could use temporary registers here. */
 		uint64_t mask =
 			sql_trigger_colmask(parse, trigger_list, 0, 0,
-					    TRIGGER_BEFORE | TRIGGER_AFTER,
-					    space, onconf);
+					(1 << TRIGGER_ACTION_TIMING_BEFORE) |
+					(1 << TRIGGER_ACTION_TIMING_AFTER),
+					space, onconf);
 		assert(space != NULL);
 		mask |= space->fk_constraint_mask;
 		first_old_reg = parse->nMem + 1;
@@ -489,8 +490,8 @@ sql_generate_row_delete(struct Parse *parse, struct space *space,
 		int addr_start = sqlVdbeCurrentAddr(v);
 		vdbe_code_row_trigger(parse, trigger_list,
 				      TRIGGER_EVENT_MANIPULATION_DELETE, NULL,
-				      TRIGGER_BEFORE, space, first_old_reg,
-				      onconf, label);
+				      TRIGGER_ACTION_TIMING_BEFORE, space,
+				      first_old_reg, onconf, label);
 
 		/* If any BEFORE triggers were coded, then seek
 		 * the cursor to the row to be deleted again. It
@@ -544,8 +545,8 @@ sql_generate_row_delete(struct Parse *parse, struct space *space,
 		/* Invoke AFTER DELETE trigger programs. */
 		vdbe_code_row_trigger(parse, trigger_list,
 				      TRIGGER_EVENT_MANIPULATION_DELETE, 0,
-				      TRIGGER_AFTER, space, first_old_reg,
-				      onconf, label);
+				      TRIGGER_ACTION_TIMING_AFTER, space,
+				      first_old_reg, onconf, label);
 	}
 
 	/* Jump here if the row had already been deleted before
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 9c612dc28..0c8a3bc75 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -552,7 +552,7 @@ sqlInsert(Parse * pParse,	/* Parser context */
 	/* Run the BEFORE and INSTEAD OF triggers, if there are any
 	 */
 	endOfLoop = sqlVdbeMakeLabel(v);
-	if (tmask & TRIGGER_BEFORE) {
+	if ((tmask & (1 << TRIGGER_ACTION_TIMING_BEFORE)) != 0) {
 		int regCols =
 			sqlGetTempRange(pParse, space_def->field_count + 1);
 
@@ -602,7 +602,7 @@ sqlInsert(Parse * pParse,	/* Parser context */
 		/* Fire BEFORE or INSTEAD OF triggers */
 		vdbe_code_row_trigger(pParse, trigger,
 				      TRIGGER_EVENT_MANIPULATION_INSERT, 0,
-				      TRIGGER_BEFORE, space,
+				      TRIGGER_ACTION_TIMING_BEFORE, space,
 				      regCols - space_def->field_count - 1, on_error,
 				      endOfLoop);
 
@@ -757,7 +757,7 @@ sqlInsert(Parse * pParse,	/* Parser context */
 		/* Code AFTER triggers */
 		vdbe_code_row_trigger(pParse, trigger,
 				      TRIGGER_EVENT_MANIPULATION_INSERT, 0,
-				      TRIGGER_AFTER, space,
+				      TRIGGER_ACTION_TIMING_AFTER, space,
 				      regData - 2 - space_def->field_count, on_error,
 				      endOfLoop);
 	}
diff --git a/src/box/sql/parse_def.h b/src/box/sql/parse_def.h
index cf233fc01..2502fc46c 100644
--- a/src/box/sql/parse_def.h
+++ b/src/box/sql/parse_def.h
@@ -267,8 +267,11 @@ struct drop_index_def {
 
 struct create_trigger_def {
 	struct create_entity_def base;
-	/** One of TK_BEFORE, TK_AFTER, TK_INSTEAD. */
-	int tr_tm;
+	/**
+	 * Whether the trigger activates before or after the
+	 * triggering event.
+	 */
+	enum trigger_action_timing action_timing;
 	/** The trigger event: INSERT, UPDATE or DELETE. */
 	enum trigger_event_manipulation event_manipulation;
 	/** Column list if this is an UPDATE trigger. */
@@ -413,7 +416,7 @@ create_trigger_def_init(struct create_trigger_def *trigger_def,
 {
 	create_entity_def_init(&trigger_def->base, ENTITY_TYPE_TRIGGER,
 			       table_name, name, if_not_exists);
-	trigger_def->tr_tm = tr_tm;
+	trigger_def->action_timing = trigger_action_timing_by_op(tr_tm);
 	trigger_def->event_manipulation = trigger_event_manipulation_by_op(op);
 	trigger_def->cols = cols;
 	trigger_def->when = when;
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 7deae7087..bb08ff8d8 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -2307,8 +2307,11 @@ struct sql_trigger {
 	 * modified).
 	 */
 	enum trigger_event_manipulation event_manipulation;
-	/** One of TRIGGER_BEFORE, TRIGGER_AFTER. */
-	u8 tr_tm;
+	/**
+	 * Whether the trigger activates before or after the
+	 * triggering event. The value is `BEFORE` or `AFTER`.
+	 */
+	enum trigger_action_timing action_timing;
 	/** The WHEN clause of the expression (may be NULL). */
 	Expr *pWhen;
 	/**
@@ -2322,16 +2325,6 @@ struct sql_trigger {
 	struct sql_trigger *next;
 };
 
-/*
- * A trigger is either a BEFORE or an AFTER trigger.  The following constants
- * determine which.
- *
- * If there are multiple triggers, you might of some BEFORE and some AFTER.
- * In that cases, the constants below can be ORed together.
- */
-#define TRIGGER_BEFORE  1
-#define TRIGGER_AFTER   2
-
 /*
  * An instance of struct TriggerStep is used to store a single SQL statement
  * that is a part of a trigger-program.
@@ -3522,7 +3515,8 @@ sql_triggers_exist(struct space_def *space_def,
  * @param trigger List of triggers on table.
  * @param event_manipulation Trigger operation.
  * @param changes_list Changes list for any UPDATE OF triggers.
- * @param tr_tm One of TRIGGER_BEFORE, TRIGGER_AFTER.
+ * @param action_timing Whether the trigger activates before or
+ *                      after the triggering event. .
  * @param space The space to code triggers from.
  * @param reg The first in an array of registers.
  * @param orconf ON CONFLICT policy.
@@ -3531,8 +3525,10 @@ sql_triggers_exist(struct space_def *space_def,
 void
 vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
 		      enum trigger_event_manipulation event_manipulation,
-		      struct ExprList *changes_list, int tr_tm,
-		      struct space *space, int reg, int orconf, int ignore_jump);
+		      struct ExprList *changes_list,
+		      enum trigger_action_timing action_timing,
+		      struct space *space, int reg, int orconf,
+		      int ignore_jump);
 
 /**
  * Generate code for the trigger program associated with trigger
@@ -3657,7 +3653,8 @@ sql_trigger_delete_step(struct sql *db, struct Token *table_name,
  * @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 action_timing_mask Mask of action timings referenced in
+ *                           the triggers list.
  * @param space The space to code triggers from.
  * @param orconf Default ON CONFLICT policy for trigger steps.
  *
@@ -3665,8 +3662,10 @@ sql_trigger_delete_step(struct sql *db, struct Token *table_name,
  */
 uint64_t
 sql_trigger_colmask(Parse *parser, struct sql_trigger *trigger,
-		    ExprList *changes_list, int new, int tr_tm,
-		    struct space *space, int orconf);
+		    struct ExprList *changes_list, int new,
+		    uint8_t action_timing_mask, struct space *space,
+		    int orconf);
+
 #define sqlParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p))
 #define sqlIsToplevel(p) ((p)->pToplevel==0)
 
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index cdf51c053..f30c68b09 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -127,7 +127,7 @@ sql_trigger_begin(struct Parse *parse)
 	trigger->zName = trigger_name;
 	trigger_name = NULL;
 	trigger->event_manipulation = trigger_def->event_manipulation;
-	trigger->tr_tm = trigger_def->tr_tm;
+	trigger->action_timing = trigger_def->action_timing;
 	trigger->pWhen = sqlExprDup(db, trigger_def->when, EXPRDUP_REDUCE);
 	trigger->pColumns = sqlIdListDup(db, trigger_def->cols);
 	if ((trigger->pWhen != NULL && trigger->pWhen == NULL) ||
@@ -449,25 +449,27 @@ sql_trigger_replace(const char *name, uint32_t space_id,
 		 * INSTEAD of triggers are only for views and
 		 * views only support INSTEAD of triggers.
 		 */
-		if (space->def->opts.is_view && trigger->tr_tm != TK_INSTEAD) {
-			diag_set(ClientError, ER_SQL_EXECUTE,
-				 tt_sprintf("cannot create %s "\
-                         "trigger on view: %s", trigger->tr_tm == TK_BEFORE ?
-						"BEFORE" : "AFTER",
-					    space->def->name));
+		if (space->def->opts.is_view &&
+		    trigger->action_timing != TRIGGER_ACTION_TIMING_INSTEAD) {
+			const char *err_msg =
+				tt_sprintf("cannot create %s trigger on "
+					   "view: %s",
+					    trigger_action_timing_strs[
+						trigger->action_timing],
+					    space->def->name);
+			diag_set(ClientError, ER_SQL_EXECUTE, err_msg);
 			return -1;
 		}
-		if (!space->def->opts.is_view && trigger->tr_tm == TK_INSTEAD) {
+		if (!space->def->opts.is_view &&
+		    trigger->action_timing == TRIGGER_ACTION_TIMING_INSTEAD) {
 			diag_set(ClientError, ER_SQL_EXECUTE,
 				 tt_sprintf("cannot create "\
                          "INSTEAD OF trigger on space: %s", space->def->name));
 			return -1;
 		}
 
-		if (trigger->tr_tm == TK_BEFORE || trigger->tr_tm == TK_INSTEAD)
-			trigger->tr_tm = TRIGGER_BEFORE;
-		else if (trigger->tr_tm == TK_AFTER)
-			trigger->tr_tm = TRIGGER_AFTER;
+		if (trigger->action_timing == TRIGGER_ACTION_TIMING_INSTEAD)
+			trigger->action_timing = TRIGGER_ACTION_TIMING_BEFORE;
 	}
 
 	struct sql_trigger **ptr = &space->sql_triggers;
@@ -541,7 +543,7 @@ sql_triggers_exist(struct space_def *space_def,
 	for (struct sql_trigger *p = trigger_list; p != NULL; p = p->next) {
 		if (p->event_manipulation == event_manipulation &&
 		    checkColumnOverlap(p->pColumns, changes_list) != 0)
-			mask |= p->tr_tm;
+			mask |= (1 << p->action_timing);
 	}
 	if (mask_ptr != NULL)
 		*mask_ptr = mask;
@@ -763,8 +765,7 @@ sql_row_trigger_program(struct Parse *parser, struct sql_trigger *trigger,
 	if (v != NULL) {
 		VdbeComment((v, "Start: %s.%s (%s %s ON %s)",
 			     trigger->zName, onErrorText(orconf),
-			     (trigger->tr_tm ==
-			      TRIGGER_BEFORE ? "BEFORE" : "AFTER"),
+			     trigger_action_timing_strs[trigger->action_timing],
 			      trigger_event_manipulation_strs[
 						trigger->event_manipulation],
 			      space->def->name));
@@ -912,17 +913,19 @@ vdbe_code_row_trigger_direct(struct Parse *parser, struct sql_trigger *trigger,
 void
 vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
 		      enum trigger_event_manipulation event_manipulation,
-		      struct ExprList *changes_list, int tr_tm,
+		      struct ExprList *changes_list,
+		      enum trigger_action_timing action_timing,
 		      struct space *space, int reg, int orconf, int ignore_jump)
 {
-	assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER);
+	assert(action_timing == TRIGGER_ACTION_TIMING_BEFORE ||
+	       action_timing == TRIGGER_ACTION_TIMING_AFTER);
 	assert((event_manipulation == TRIGGER_EVENT_MANIPULATION_UPDATE) ==
 	       (changes_list != NULL));
 
 	for (struct sql_trigger *p = trigger; p != NULL; p = p->next) {
 		/* Determine whether we should code trigger. */
 		if (p->event_manipulation == event_manipulation &&
-		    p->tr_tm == tr_tm &&
+		    p->action_timing == action_timing &&
 		    checkColumnOverlap(p->pColumns, changes_list)) {
 			vdbe_code_row_trigger_direct(parser, p, space, reg,
 						     orconf, ignore_jump);
@@ -932,7 +935,7 @@ vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
 
 uint64_t
 sql_trigger_colmask(Parse *parser, struct sql_trigger *trigger,
-		    ExprList *changes_list, int new, int tr_tm,
+		    ExprList *changes_list, int new, uint8_t action_timing_mask,
 		    struct space *space, int orconf)
 {
 	enum trigger_event_manipulation event_manipulation =
@@ -943,7 +946,7 @@ sql_trigger_colmask(Parse *parser, struct sql_trigger *trigger,
 	assert(new == 1 || new == 0);
 	for (struct sql_trigger *p = trigger; p != NULL; p = p->next) {
 		if (p->event_manipulation == event_manipulation &&
-		    (tr_tm & p->tr_tm) &&
+		    ((action_timing_mask & (1 << p->action_timing)) != 0) &&
 		    checkColumnOverlap(p->pColumns, changes_list)) {
 			TriggerPrg *prg =
 				sql_row_trigger(parser, p, space, orconf);
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 400375bcf..75bc487b8 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -311,8 +311,9 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 		assert(space != NULL);
 		uint64_t oldmask = hasFK ? space->fk_constraint_mask : 0;
 		oldmask |= sql_trigger_colmask(pParse, trigger, pChanges, 0,
-					       TRIGGER_BEFORE | TRIGGER_AFTER,
-					       space, on_error);
+					(1 << TRIGGER_ACTION_TIMING_BEFORE) |
+					(1 << TRIGGER_ACTION_TIMING_AFTER),
+					space, on_error);
 		for (i = 0; i < (int)def->field_count; i++) {
 			if (column_mask_fieldno_is_set(oldmask, i) ||
 			    sql_space_column_is_in_pk(space, i)) {
@@ -339,13 +340,14 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 	 * be used eliminates some redundant opcodes.
 	 */
 	uint64_t newmask = sql_trigger_colmask(pParse, trigger, pChanges, 1,
-					       TRIGGER_BEFORE, space, on_error);
+					       1 << TRIGGER_ACTION_TIMING_BEFORE,
+					       space, on_error);
 	for (i = 0; i < (int)def->field_count; i++) {
 		j = aXRef[i];
 		if (j >= 0) {
 			sqlExprCode(pParse, pChanges->a[j].pExpr,
 					regNew + i);
-		} else if ((tmask & TRIGGER_BEFORE) == 0 ||
+		} else if ((tmask & (1 << TRIGGER_ACTION_TIMING_BEFORE)) == 0 ||
 			   column_mask_fieldno_is_set(newmask, i) != 0) {
 			/* This branch loads the value of a column that will not be changed
 			 * into a register. This is done if there are no BEFORE triggers, or
@@ -362,12 +364,12 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 	/* Fire any BEFORE UPDATE triggers. This happens before constraints are
 	 * verified. One could argue that this is wrong.
 	 */
-	if (tmask & TRIGGER_BEFORE) {
+	if ((tmask & (1 << TRIGGER_ACTION_TIMING_BEFORE)) != 0) {
 		sql_emit_table_types(v, space->def, regNew);
 		vdbe_code_row_trigger(pParse, trigger,
 				      TRIGGER_EVENT_MANIPULATION_UPDATE,
-				      pChanges, TRIGGER_BEFORE, space, regOldPk,
-				      on_error, labelContinue);
+				      pChanges, TRIGGER_ACTION_TIMING_BEFORE,
+				      space, regOldPk, on_error, labelContinue);
 
 		/* The row-trigger may have deleted the row being updated. In this
 		 * case, jump to the next row. No updates or AFTER triggers are
@@ -481,8 +483,8 @@ sqlUpdate(Parse * pParse,		/* The parser context */
 
 	vdbe_code_row_trigger(pParse, trigger,
 			      TRIGGER_EVENT_MANIPULATION_UPDATE, pChanges,
-			      TRIGGER_AFTER, space, regOldPk, on_error,
-			      labelContinue);
+			      TRIGGER_ACTION_TIMING_AFTER, space, regOldPk,
+			      on_error, labelContinue);
 
 	/* Repeat the above with the next record to be updated, until
 	 * all record selected by the WHERE clause have been updated.
diff --git a/src/box/trigger_def.c b/src/box/trigger_def.c
index 4c34f5089..ff7938f8d 100644
--- a/src/box/trigger_def.c
+++ b/src/box/trigger_def.c
@@ -31,3 +31,5 @@
 #include "trigger_def.h"
 
 const char *trigger_event_manipulation_strs[] = {"DELETE", "UPDATE", "INSERT"};
+
+const char *trigger_action_timing_strs[] = {"BEFORE", "AFTER", "INSTEAD"};
diff --git a/src/box/trigger_def.h b/src/box/trigger_def.h
index c1fed4f82..dd34f6859 100644
--- a/src/box/trigger_def.h
+++ b/src/box/trigger_def.h
@@ -50,6 +50,23 @@ enum trigger_event_manipulation {
 
 extern const char *trigger_event_manipulation_strs[];
 
+/**
+ * Whether the trigger activates before or after the triggering
+ * event. The value is `BEFORE` or `AFTER`.
+ */
+enum trigger_action_timing {
+	TRIGGER_ACTION_TIMING_BEFORE,
+	TRIGGER_ACTION_TIMING_AFTER,
+	/*
+	 * INSTEAD of triggers are only for views and
+	 * views only support INSTEAD of triggers.
+	 */
+	TRIGGER_ACTION_TIMING_INSTEAD,
+	trigger_action_timing_MAX
+};
+
+extern const char *trigger_action_timing_strs[];
+
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
-- 
2.23.0

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [Tarantool-patches] [PATCH v1 5/9] sql: use rlist to organize triggers in a list
  2019-10-14 16:03 [Tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space Kirill Shcherbatov
                   ` (3 preceding siblings ...)
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 4/9] box: introduce trigger_action_timing enum Kirill Shcherbatov
@ 2019-10-14 16:03 ` Kirill Shcherbatov
  2019-10-17  7:36   ` Konstantin Osipov
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 6/9] sql: rework CREATE TABLE rule in parser Kirill Shcherbatov
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 31+ messages in thread
From: Kirill Shcherbatov @ 2019-10-14 16:03 UTC (permalink / raw)
  To: tarantool-patches, tarantool-patches, kostja.osipov, korablev

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

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

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

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

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [Tarantool-patches] [PATCH v1 6/9] sql: rework CREATE TABLE rule in parser
  2019-10-14 16:03 [Tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space Kirill Shcherbatov
                   ` (4 preceding siblings ...)
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 5/9] sql: use rlist to organize triggers in a list Kirill Shcherbatov
@ 2019-10-14 16:03 ` Kirill Shcherbatov
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 7/9] sql: wrap all ASTs in sql_trigger_expr structure Kirill Shcherbatov
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 31+ messages in thread
From: Kirill Shcherbatov @ 2019-10-14 16:03 UTC (permalink / raw)
  To: tarantool-patches, tarantool-patches, kostja.osipov, korablev

SQL grammar used to construct the sql_trigger object or to
generate a related VDBE instructions in a two functions with
a shared context: sql_trigger_begin and sql_trigger_end.

It used to be an AST compile or VDBE code generation in according
to parser::is_parse_only flag state - that is messy.

This patch reworks SQL rule a bit to call a function once, when
the whole trigger context is assembled to call the first function
that constructs AST or the second to emit VDBE code.

Needed for #4343.
---
 src/box/sql/build.c      |  82 ++++++++++++++++++++++++++
 src/box/sql/parse.y      |  26 ++++-----
 src/box/sql/parse_def.h  |  13 ++++-
 src/box/sql/sqlInt.h     |  18 ++----
 src/box/sql/trigger.c    | 121 +++------------------------------------
 test/sql/triggers.result |  16 +++---
 test/sql/upgrade.result  |   4 +-
 7 files changed, 128 insertions(+), 152 deletions(-)

diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 9f9f80b70..5040d04d1 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -1281,6 +1281,88 @@ sqlEndTable(struct Parse *pParse)
 	}
 }
 
+void
+sql_create_trigger(struct Parse *parse_context)
+{
+	struct create_trigger_def *trigger_def =
+					&parse_context->create_trigger_def;
+	struct create_entity_def *create_def = &trigger_def->base;
+	struct alter_entity_def *alter_def = &create_def->base;
+	assert(alter_def->entity_type == ENTITY_TYPE_TRIGGER);
+	assert(alter_def->alter_action == ALTER_ACTION_CREATE);
+	struct sql *db = parse_context->db;
+
+	char *trigger_name = sql_name_from_token(db, &create_def->name);
+	if (trigger_name == NULL ||
+	    sqlCheckIdentifierName(parse_context, trigger_name) != 0) {
+		parse_context->is_aborted = true;
+		goto end;
+	}
+
+	struct Vdbe *v = sqlGetVdbe(parse_context);
+	if (v == NULL)
+		goto end;
+	sqlVdbeCountChanges(v);
+	const char *error_msg = tt_sprintf(tnt_errcode_desc(ER_TRIGGER_EXISTS),
+					   trigger_name);
+	int name_reg = ++parse_context->nMem;
+	sqlVdbeAddOp4(v, OP_String8, 0, name_reg, 0,
+		      sqlDbStrDup(db, trigger_name), P4_DYNAMIC);
+	if (vdbe_emit_halt_with_presence_test(parse_context, BOX_TRIGGER_ID, 0,
+					      name_reg, 1, ER_TRIGGER_EXISTS,
+					      error_msg,
+					      create_def->if_not_exist,
+					      OP_NoConflict) != 0) {
+		parse_context->is_aborted = true;
+		goto end;
+	}
+
+	const char *table_name = alter_def->entity_name->a[0].zName;
+	uint32_t space_id = box_space_id_by_name(table_name,
+						 strlen(table_name));
+	if (space_id == BOX_ID_NIL) {
+		diag_set(ClientError, ER_NO_SUCH_SPACE, table_name);
+		parse_context->is_aborted = true;
+		goto end;
+	}
+
+	struct space *_trigger = space_by_id(BOX_TRIGGER_ID);
+	assert(_trigger != NULL);
+
+	int first_col = parse_context->nMem + 1;
+	parse_context->nMem += 3;
+	int record = ++parse_context->nMem;
+
+	uint32_t opts_buff_sz = mp_sizeof_map(1) +
+				mp_sizeof_str(strlen("sql")) +
+				mp_sizeof_str(trigger_def->sql_len);
+	char *opts_buff = (char *) sqlDbMallocRaw(db, opts_buff_sz);
+	if (opts_buff == NULL) {
+		parse_context->is_aborted = true;
+		goto end;
+	}
+
+	char *data = mp_encode_map(opts_buff, 1);
+	data = mp_encode_str(data, "sql", strlen("sql"));
+	data = mp_encode_str(data, trigger_def->sql, trigger_def->sql_len);
+	sqlVdbeAddOp4(v, OP_String8, 0, first_col, 0, trigger_name, P4_DYNAMIC);
+	sqlVdbeAddOp2(v, OP_Integer, space_id, first_col + 1);
+	sqlVdbeAddOp4(v, OP_Blob, opts_buff_sz, first_col + 2,
+		      SQL_SUBTYPE_MSGPACK, opts_buff, P4_DYNAMIC);
+	sqlVdbeAddOp3(v, OP_MakeRecord, first_col, 3, record);
+	sqlVdbeAddOp4(v, OP_IdxInsert, record, 0, 0, (char *)_trigger,
+		      P4_SPACEPTR);
+	sqlVdbeChangeP5(v, OPFLAG_NCHANGE);
+	trigger_name = NULL;
+
+end:
+	sqlDbFree(db, trigger_name);
+	sqlSrcListDelete(db, alter_def->entity_name);
+	sqlIdListDelete(db, trigger_def->cols);
+	sql_expr_delete(db, trigger_def->when, false);
+	sqlDeleteTriggerStep(db, trigger_def->step_list);
+}
+
 void
 sql_create_view(struct Parse *parse_context)
 {
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index ed59a875a..264b24f99 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -1574,21 +1574,19 @@ plus_num(A) ::= number(A).
 minus_num(A) ::= MINUS number(X).     {A = X;}
 //////////////////////////// The CREATE TRIGGER command /////////////////////
 
-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 = true;
-  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). {
+cmd ::= createkw(Y) TRIGGER ifnotexists(NOERR) nm(B) trigger_time(C)
+        trigger_event(D) ON fullname(E) foreach_clause when_clause(G)
+        BEGIN trigger_cmd_list(S) END(Z). {
+  int sql_len = (int)(Z.z - Y.z) + Z.n;
+  const char *sql = Y.z;
   create_trigger_def_init(&pParse->create_trigger_def, E, &B, C, D.a, D.b, G,
-                          NOERR);
-  sql_trigger_begin(pParse);
-  A = B; /*A-overwrites-T*/
+                          NOERR, sql, sql_len, S);
+  if (!pParse->parse_only) {
+    pParse->initiateTTrans = true;
+    sql_create_trigger(pParse);
+  } else {
+    sql_store_trigger(pParse);
+  }
 }
 
 %type trigger_time {int}
diff --git a/src/box/sql/parse_def.h b/src/box/sql/parse_def.h
index 2502fc46c..71fa89959 100644
--- a/src/box/sql/parse_def.h
+++ b/src/box/sql/parse_def.h
@@ -278,6 +278,12 @@ struct create_trigger_def {
 	struct IdList *cols;
 	/** When clause. */
 	struct Expr *when;
+	/** Source SQL Expression. */
+	const char *sql;
+	/** Source SQL Expression length. */
+	uint32_t sql_len;
+	/** Steps describing SQL trigger program. */
+	struct TriggerStep *step_list;
 };
 
 struct create_constraint_def {
@@ -412,7 +418,9 @@ static inline void
 create_trigger_def_init(struct create_trigger_def *trigger_def,
 			struct SrcList *table_name, struct Token *name,
 			int tr_tm, int op, struct IdList *cols,
-			struct Expr *when, bool if_not_exists)
+			struct Expr *when, bool if_not_exists,
+			const char *sql, uint32_t sql_len,
+			struct TriggerStep *step_list)
 {
 	create_entity_def_init(&trigger_def->base, ENTITY_TYPE_TRIGGER,
 			       table_name, name, if_not_exists);
@@ -420,6 +428,9 @@ create_trigger_def_init(struct create_trigger_def *trigger_def,
 	trigger_def->event_manipulation = trigger_event_manipulation_by_op(op);
 	trigger_def->cols = cols;
 	trigger_def->when = when;
+	trigger_def->sql = sql;
+	trigger_def->sql_len = sql_len;
+	trigger_def->step_list = step_list;
 }
 
 static inline void
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 8c9a69a00..180997ce4 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -3364,25 +3364,17 @@ sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
  * 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.
  */
 void
-sql_trigger_begin(struct Parse *parse);
+sql_store_trigger(struct Parse *parse);
 
 /**
- * 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.
+ * This function is called from parser to generate create trigger
+ * VDBE code.
+ * @param parser Parser context.
  */
 void
-sql_trigger_finish(struct Parse *parse, struct TriggerStep *step_list,
-		   struct Token *token);
+sql_create_trigger(struct Parse *parse);
 
 /**
  * This function is called from parser to generate drop trigger
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 269c09a3d..3a0ee6b7d 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -63,7 +63,7 @@ sqlDeleteTriggerStep(sql * db, TriggerStep * pTriggerStep)
 }
 
 void
-sql_trigger_begin(struct Parse *parse)
+sql_store_trigger(struct Parse *parse)
 {
 	/* The new trigger. */
 	struct sql_trigger *trigger = NULL;
@@ -95,28 +95,6 @@ sql_trigger_begin(struct Parse *parse)
 		goto set_tarantool_error_and_cleanup;
 	}
 
-	if (!parse->parse_only) {
-		struct Vdbe *v = sqlGetVdbe(parse);
-		if (v != NULL)
-			sqlVdbeCountChanges(v);
-		const char *error_msg =
-			tt_sprintf(tnt_errcode_desc(ER_TRIGGER_EXISTS),
-				   trigger_name);
-		char *name_copy = sqlDbStrDup(db, trigger_name);
-		if (name_copy == NULL)
-			goto trigger_cleanup;
-		int name_reg = ++parse->nMem;
-		sqlVdbeAddOp4(parse->pVdbe, OP_String8, 0, name_reg, 0,
-				  name_copy, P4_DYNAMIC);
-		bool no_err = create_def->if_not_exist;
-		if (vdbe_emit_halt_with_presence_test(parse, BOX_TRIGGER_ID, 0,
-						      name_reg, 1,
-						      ER_TRIGGER_EXISTS,
-						      error_msg, (no_err != 0),
-						      OP_NoConflict) != 0)
-			goto trigger_cleanup;
-	}
-
 	/* Build the Trigger object. */
 	trigger = (struct sql_trigger *)sqlDbMallocZero(db,
 							    sizeof(struct
@@ -131,9 +109,12 @@ sql_trigger_begin(struct Parse *parse)
 	trigger->pWhen = sqlExprDup(db, trigger_def->when, EXPRDUP_REDUCE);
 	trigger->pColumns = sqlIdListDup(db, trigger_def->cols);
 	rlist_create(&trigger->link);
-	if ((trigger->pWhen != NULL && trigger->pWhen == NULL) ||
-	    (trigger->pColumns != NULL && trigger->pColumns == NULL))
+	if ((trigger_def->when != NULL && trigger->pWhen == NULL) ||
+	    (trigger_def->cols != NULL && trigger->pColumns == NULL))
 		goto trigger_cleanup;
+	trigger->step_list = parse->create_trigger_def.step_list;
+	parse->create_trigger_def.step_list = NULL;
+
 	assert(parse->parsed_ast.trigger == NULL);
 	parse->parsed_ast.trigger = trigger;
 	parse->parsed_ast_type = AST_TYPE_TRIGGER;
@@ -143,6 +124,7 @@ sql_trigger_begin(struct Parse *parse)
 	sqlSrcListDelete(db, alter_def->entity_name);
 	sqlIdListDelete(db, trigger_def->cols);
 	sql_expr_delete(db, trigger_def->when, false);
+	sqlDeleteTriggerStep(db, parse->create_trigger_def.step_list);
 	if (parse->parsed_ast.trigger == NULL)
 		sql_trigger_delete(db, trigger);
 	else
@@ -155,95 +137,6 @@ set_tarantool_error_and_cleanup:
 	goto trigger_cleanup;
 }
 
-void
-sql_trigger_finish(struct Parse *parse, struct TriggerStep *step_list,
-		   struct Token *token)
-{
-	/* Trigger being finished. */
-	struct sql_trigger *trigger = parse->parsed_ast.trigger;
-	/* The database. */
-	struct sql *db = parse->db;
-
-	parse->parsed_ast.trigger = NULL;
-	if (NEVER(parse->is_aborted) || trigger == NULL)
-		goto cleanup;
-	char *trigger_name = trigger->zName;
-	trigger->step_list = step_list;
-	while (step_list != NULL) {
-		step_list = step_list->pNext;
-	}
-
-	/* Trigger name for error reporting. */
-	struct Token trigger_name_token;
-	sqlTokenInit(&trigger_name_token, trigger->zName);
-
-	/*
-	 * Generate byte code to insert a new trigger into
-	 * Tarantool for non-parsing mode or export trigger.
-	 */
-	if (!parse->parse_only) {
-		/* Make an entry in the _trigger space. */
-		struct Vdbe *v = sqlGetVdbe(parse);
-		if (v == 0)
-			goto cleanup;
-
-		char *sql_str =
-			sqlMPrintf(db, "CREATE TRIGGER %s", token->z);
-		if (db->mallocFailed)
-			goto cleanup;
-
-		struct space *_trigger = space_by_id(BOX_TRIGGER_ID);
-		assert(_trigger != NULL);
-
-		int first_col = parse->nMem + 1;
-		parse->nMem += 3;
-		int record = ++parse->nMem;
-		int sql_str_len = strlen(sql_str);
-		int sql_len = strlen("sql");
-
-		uint32_t opts_buff_sz = mp_sizeof_map(1) +
-					mp_sizeof_str(sql_len) +
-					mp_sizeof_str(sql_str_len);
-		char *opts_buff = (char *) sqlDbMallocRaw(db, opts_buff_sz);
-		if (opts_buff == NULL)
-			goto cleanup;
-
-		char *data = mp_encode_map(opts_buff, 1);
-		data = mp_encode_str(data, "sql", sql_len);
-		data = mp_encode_str(data, sql_str, sql_str_len);
-		sqlDbFree(db, sql_str);
-
-		trigger_name = sqlDbStrDup(db, trigger_name);
-		if (trigger_name == NULL) {
-			sqlDbFree(db, opts_buff);
-			goto cleanup;
-		}
-
-		sqlVdbeAddOp4(v, OP_String8, 0, first_col, 0,
-				  trigger_name, P4_DYNAMIC);
-		sqlVdbeAddOp2(v, OP_Integer, trigger->space_id,
-				  first_col + 1);
-		sqlVdbeAddOp4(v, OP_Blob, opts_buff_sz, first_col + 2,
-				  SQL_SUBTYPE_MSGPACK, opts_buff, P4_DYNAMIC);
-		sqlVdbeAddOp3(v, OP_MakeRecord, first_col, 3, record);
-		sqlVdbeAddOp4(v, OP_IdxInsert, record, 0, 0,
-				  (char *)_trigger, P4_SPACEPTR);
-
-		sqlVdbeChangeP5(v, OPFLAG_NCHANGE);
-
-		sql_set_multi_write(parse, false);
-	} else {
-		parse->parsed_ast.trigger = trigger;
-		parse->parsed_ast_type = AST_TYPE_TRIGGER;
-		trigger = NULL;
-	}
-
-cleanup:
-	sql_trigger_delete(db, trigger);
-	assert(parse->parsed_ast.trigger == NULL || parse->parse_only);
-	sqlDeleteTriggerStep(db, step_list);
-}
-
 struct TriggerStep *
 sql_trigger_select_step(struct sql *db, struct Select *select)
 {
diff --git a/test/sql/triggers.result b/test/sql/triggers.result
index 9dfe981a0..1a70ddf2b 100644
--- a/test/sql/triggers.result
+++ b/test/sql/triggers.result
@@ -34,7 +34,7 @@ immutable_part(box.space._trigger:select())
 ---
 - - - T1T
     - {'sql': 'CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(1); END; '}
+        t2 VALUES(1); END'}
 ...
 space_id = box.space._space.index["name"]:get('T1').id
 ---
@@ -68,7 +68,7 @@ immutable_part(box.space._trigger:select())
 ---
 - - - T1T
     - {'sql': 'CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(1); END; '}
+        t2 VALUES(1); END'}
 ...
 box.execute("DROP TABLE T1;")
 ---
@@ -90,7 +90,7 @@ immutable_part(box.space._trigger:select())
 ---
 - - - T1T
     - {'sql': 'CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(1); END; '}
+        t2 VALUES(1); END'}
 ...
 space_id = box.space._space.index["name"]:get('T1').id
 ---
@@ -129,7 +129,7 @@ immutable_part(box.space._trigger:select())
 ---
 - - - T1T
     - {'sql': 'CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(1); END; '}
+        t2 VALUES(1); END'}
   - - T2T
     - {'sql': 'CREATE TRIGGER t2t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
         t2 VALUES(2); END;'}
@@ -167,7 +167,7 @@ immutable_part(box.space._trigger:select())
 ---
 - - - T1T
     - {'sql': 'CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(1); END; '}
+        t2 VALUES(1); END'}
 ...
 box.execute("INSERT INTO t1 VALUES(3);")
 ---
@@ -198,13 +198,13 @@ immutable_part(box.space._trigger:select())
 ---
 - - - T1T
     - {'sql': 'CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(1); END; '}
+        t2 VALUES(1); END'}
   - - T2T
     - {'sql': 'CREATE TRIGGER t2t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(2); END; '}
+        t2 VALUES(2); END'}
   - - T3T
     - {'sql': 'CREATE TRIGGER t3t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(3); END; '}
+        t2 VALUES(3); END'}
 ...
 box.execute("INSERT INTO t1 VALUES(4);")
 ---
diff --git a/test/sql/upgrade.result b/test/sql/upgrade.result
index f0997e17f..b5c4ad500 100644
--- a/test/sql/upgrade.result
+++ b/test/sql/upgrade.result
@@ -86,7 +86,7 @@ t1t.name
 t1t.opts
 ---
 - {'sql': 'CREATE TRIGGER t1t AFTER INSERT ON t FOR EACH ROW BEGIN INSERT INTO t_out
-    VALUES(1); END;'}
+    VALUES(1); END'}
 ...
 t2t.name
 ---
@@ -95,7 +95,7 @@ t2t.name
 t2t.opts
 ---
 - {'sql': 'CREATE TRIGGER t2t AFTER INSERT ON t FOR EACH ROW BEGIN INSERT INTO t_out
-    VALUES(2); END;'}
+    VALUES(2); END'}
 ...
 assert(t1t.space_id == t2t.space_id)
 ---
-- 
2.23.0

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [Tarantool-patches] [PATCH v1 7/9] sql: wrap all ASTs in sql_trigger_expr structure
  2019-10-14 16:03 [Tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space Kirill Shcherbatov
                   ` (5 preceding siblings ...)
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 6/9] sql: rework CREATE TABLE rule in parser Kirill Shcherbatov
@ 2019-10-14 16:03 ` Kirill Shcherbatov
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 8/9] sql: inherit sql_trigger from a new trigger class Kirill Shcherbatov
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 31+ messages in thread
From: Kirill Shcherbatov @ 2019-10-14 16:03 UTC (permalink / raw)
  To: tarantool-patches, tarantool-patches, kostja.osipov, korablev

Introduced a new sql_trigger_expr to aggregate all
sql_triger-related ASTs. This allows to rework
sql_trigger_compile function in further patches to return
sql_trigger_expr object instead of the complete trigger
object.

It is an important step of refactoring, because we are going
to construct trigger and trigger_def objects universally by
information that will be defined in the _trigger space.

Needed for #4343
---
 src/box/sql.h               | 37 +++++++++++++++-----
 src/box/sql/fk_constraint.c | 15 +++++---
 src/box/sql/trigger.c       | 69 +++++++++++++++++++++++++++++--------
 3 files changed, 92 insertions(+), 29 deletions(-)

diff --git a/src/box/sql.h b/src/box/sql.h
index 09d2b7d7c..d0c18b6ff 100644
--- a/src/box/sql.h
+++ b/src/box/sql.h
@@ -99,6 +99,32 @@ sql_expr_compile(struct sql *db, const char *expr, int expr_len);
 struct Select *
 sql_view_compile(struct sql *db, const char *view_stmt);
 
+/** The SQL Trigger AST runtime representation. */
+struct sql_trigger_expr {
+	/** The WHEN clause of the expression (may be NULL). */
+	struct Expr *when;
+	/**
+	 * If this is an UPDATE OF <column-list> trigger,
+	 * the <column-list> is stored here
+	 */
+	struct IdList *cols;
+	/** Link list of trigger program steps. */
+	struct TriggerStep *step_list;
+};
+
+/**
+ * Allocate a new SQL Trigger AST runtime object.
+ * In case of successfull construction, the constructed object
+ * start manipulate given arguments lifecycle.
+ */
+struct sql_trigger_expr *
+sql_trigger_expr_new(struct sql *db, struct IdList *cols, struct Expr *when,
+		     struct TriggerStep *step_list);
+
+/** Free SQL Trigger AST object. */
+void
+sql_trigger_expr_delete(struct sql *db, struct sql_trigger_expr *trigger_expr);
+
 /**
  * Perform parsing of provided SQL request and construct trigger AST.
  * @param db SQL context handle.
@@ -452,15 +478,8 @@ struct sql_trigger {
 	 * triggering event. The value is `BEFORE` or `AFTER`.
 	 */
 	enum trigger_action_timing action_timing;
-	/** The WHEN clause of the expression (may be NULL). */
-	struct Expr *pWhen;
-	/**
-	 * If this is an UPDATE OF <column-list> trigger,
-	 * the <column-list> is stored here
-	 */
-	struct IdList *pColumns;
-	/** Link list of trigger program steps. */
-	struct TriggerStep *step_list;
+	/** The SQL trigger AST. */
+	struct sql_trigger_expr *expr;
 	/**
 	 * Organize sql_trigger structs into linked list
 	 * with space::trigger_list.
diff --git a/src/box/sql/fk_constraint.c b/src/box/sql/fk_constraint.c
index 539a16560..454ed311f 100644
--- a/src/box/sql/fk_constraint.c
+++ b/src/box/sql/fk_constraint.c
@@ -856,22 +856,27 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def,
 							     sizeof(*trigger));
 	if (trigger != NULL) {
 		size_t step_size = sizeof(TriggerStep) + name_len + 1;
-		trigger->step_list = sqlDbMallocZero(db, step_size);
+		step = sqlDbMallocZero(db, step_size);
 		rlist_create(&trigger->link);
-		step = trigger->step_list;
+		if (step == NULL)
+			goto end;
 		step->zTarget = (char *) &step[1];
 		memcpy((char *) step->zTarget, space_name, name_len);
-
 		step->pWhere = sqlExprDup(db, where, EXPRDUP_REDUCE);
 		step->pExprList = sql_expr_list_dup(db, list, EXPRDUP_REDUCE);
 		step->pSelect = sqlSelectDup(db, select, EXPRDUP_REDUCE);
 		if (when != NULL) {
 			when = sqlPExpr(pParse, TK_NOT, when, 0);
-			trigger->pWhen =
-				sqlExprDup(db, when, EXPRDUP_REDUCE);
+			if (when == NULL)
+				goto end;
 		}
+		trigger->expr = sql_trigger_expr_new(db, NULL, when, step);
+		if (trigger->expr == NULL)
+			goto end;
+		when = NULL;
 	}
 
+end:
 	sql_expr_delete(db, where, false);
 	sql_expr_delete(db, when, false);
 	sql_expr_list_delete(db, list);
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 3a0ee6b7d..71a78d7ed 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -106,14 +106,15 @@ sql_store_trigger(struct Parse *parse)
 	trigger_name = NULL;
 	trigger->event_manipulation = trigger_def->event_manipulation;
 	trigger->action_timing = trigger_def->action_timing;
-	trigger->pWhen = sqlExprDup(db, trigger_def->when, EXPRDUP_REDUCE);
-	trigger->pColumns = sqlIdListDup(db, trigger_def->cols);
 	rlist_create(&trigger->link);
-	if ((trigger_def->when != NULL && trigger->pWhen == NULL) ||
-	    (trigger_def->cols != NULL && trigger->pColumns == NULL))
+	trigger->expr = sql_trigger_expr_new(db, trigger_def->cols,
+					     trigger_def->when,
+					     trigger_def->step_list);
+	if (trigger->expr == NULL)
 		goto trigger_cleanup;
-	trigger->step_list = parse->create_trigger_def.step_list;
-	parse->create_trigger_def.step_list = NULL;
+	trigger_def->cols = NULL;
+	trigger_def->when = NULL;
+	trigger_def->step_list = NULL;
 
 	assert(parse->parsed_ast.trigger == NULL);
 	parse->parsed_ast.trigger = trigger;
@@ -247,15 +248,53 @@ sql_trigger_delete_step(struct sql *db, struct Token *table_name,
 	return trigger_step;
 }
 
+struct sql_trigger_expr *
+sql_trigger_expr_new(struct sql *db, struct IdList *cols, struct Expr *when,
+		     struct TriggerStep *step_list)
+{
+	struct sql_trigger_expr *trigger_expr =
+		(struct sql_trigger_expr *)malloc(sizeof(*trigger_expr));
+	if (trigger_expr == NULL) {
+		diag_set(OutOfMemory, sizeof(*trigger_expr),
+			 "malloc", "trigger_expr");
+		return NULL;
+	}
+	if (when != NULL) {
+		trigger_expr->when = sqlExprDup(db, when, EXPRDUP_REDUCE);
+		if (trigger_expr->when == NULL) {
+			diag_set(OutOfMemory,
+				 sql_expr_sizeof(when, EXPRDUP_REDUCE),
+				 "sqlExprDup", "trigger_expr->when");
+			free(trigger_expr);
+			return NULL;
+		}
+	} else {
+		trigger_expr->when = NULL;
+	}
+	/* Object refers to reduced copy of when expression. */
+	sql_expr_delete(db, when, false);
+	trigger_expr->cols = cols;
+	trigger_expr->step_list = step_list;
+	return trigger_expr;
+}
+
+void
+sql_trigger_expr_delete(struct sql *db, struct sql_trigger_expr *trigger_expr)
+{
+	sqlDeleteTriggerStep(db, trigger_expr->step_list);
+	sql_expr_delete(db, trigger_expr->when, false);
+	sqlIdListDelete(db, trigger_expr->cols);
+	free(trigger_expr);
+}
+
 void
 sql_trigger_delete(struct sql *db, struct sql_trigger *trigger)
 {
 	if (trigger == NULL)
 		return;
-	sqlDeleteTriggerStep(db, trigger->step_list);
+	if (trigger->expr != NULL)
+		sql_trigger_expr_delete(db, trigger->expr);
 	sqlDbFree(db, trigger->zName);
-	sql_expr_delete(db, trigger->pWhen, false);
-	sqlIdListDelete(db, trigger->pColumns);
 	sqlDbFree(db, trigger);
 }
 
@@ -427,7 +466,7 @@ sql_triggers_exist(struct space *space,
 	struct sql_trigger *p;
 	rlist_foreach_entry(p, trigger_list, link) {
 		if (p->event_manipulation == event_manipulation &&
-		    checkColumnOverlap(p->pColumns, changes_list) != 0)
+		    checkColumnOverlap(p->expr->cols, changes_list) != 0)
 			mask |= (1 << p->action_timing);
 	}
 	if (mask_ptr != NULL)
@@ -665,8 +704,8 @@ sql_row_trigger_program(struct Parse *parser, struct sql_trigger *trigger,
 		 * immediately halted by jumping to the OP_Halt
 		 * inserted at the end of the program.
 		 */
-		if (trigger->pWhen != NULL) {
-			pWhen = sqlExprDup(db, trigger->pWhen, 0);
+		if (trigger->expr->when != NULL) {
+			pWhen = sqlExprDup(db, trigger->expr->when, 0);
 			if (0 == sqlResolveExprNames(&sNC, pWhen)
 			    && db->mallocFailed == 0) {
 				iEndTrigger = sqlVdbeMakeLabel(v);
@@ -678,7 +717,7 @@ sql_row_trigger_program(struct Parse *parser, struct sql_trigger *trigger,
 		}
 
 		/* Code the trigger program into the sub-vdbe. */
-		codeTriggerProgram(pSubParse, trigger->step_list, orconf);
+		codeTriggerProgram(pSubParse, trigger->expr->step_list, orconf);
 
 		/* Insert an OP_Halt at the end of the sub-program. */
 		if (iEndTrigger)
@@ -814,7 +853,7 @@ vdbe_code_row_trigger(struct Parse *parser, struct rlist *trigger_list,
 		/* Determine whether we should code trigger. */
 		if (p->event_manipulation == event_manipulation &&
 		    p->action_timing == action_timing &&
-		    checkColumnOverlap(p->pColumns, changes_list)) {
+		    checkColumnOverlap(p->expr->cols, changes_list)) {
 			vdbe_code_row_trigger_direct(parser, p, space, reg,
 						     orconf, ignore_jump);
 		}
@@ -838,7 +877,7 @@ sql_trigger_colmask(Parse *parser, struct rlist *trigger_list,
 	rlist_foreach_entry(p, trigger_list, link) {
 		if (p->event_manipulation == event_manipulation &&
 		    ((action_timing_mask & (1 << p->action_timing)) != 0) &&
-		    checkColumnOverlap(p->pColumns, changes_list)) {
+		    checkColumnOverlap(p->expr->cols, changes_list)) {
 			TriggerPrg *prg =
 				sql_row_trigger(parser, p, space, orconf);
 			if (prg != NULL)
-- 
2.23.0

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [Tarantool-patches] [PATCH v1 8/9] sql: inherit sql_trigger from a new trigger class
  2019-10-14 16:03 [Tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space Kirill Shcherbatov
                   ` (6 preceding siblings ...)
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 7/9] sql: wrap all ASTs in sql_trigger_expr structure Kirill Shcherbatov
@ 2019-10-14 16:03 ` Kirill Shcherbatov
  2019-10-17  7:38   ` Konstantin Osipov
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 9/9] schema: rework _trigger system space Kirill Shcherbatov
  2019-10-15 21:34 ` [Tarantool-patches] [tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space Nikita Pettik
  9 siblings, 1 reply; 31+ messages in thread
From: Kirill Shcherbatov @ 2019-10-14 16:03 UTC (permalink / raw)
  To: tarantool-patches, tarantool-patches, kostja.osipov, korablev

This patch reworks sql_trigger class to inherit it from
a new trigger base class. This will allow us to work with
all triggers uniformly regardless of it's implementation.

Needed for #4343
---
 src/box/alter.cc            |   9 +-
 src/box/fk_constraint.c     |   4 +-
 src/box/sql.h               |  35 +++----
 src/box/sql/build.c         |   8 +-
 src/box/sql/fk_constraint.c | 111 ++++++++++++++-------
 src/box/sql/prepare.c       |   2 +-
 src/box/sql/trigger.c       | 193 ++++++++++++++++++++----------------
 src/box/sql/vdbe.c          |   4 +-
 src/box/trigger.h           |  59 +++++++++++
 src/box/trigger_def.c       |  31 ++++++
 src/box/trigger_def.h       |  94 ++++++++++++++++++
 11 files changed, 394 insertions(+), 156 deletions(-)
 create mode 100644 src/box/trigger.h

diff --git a/src/box/alter.cc b/src/box/alter.cc
index 84f95abc1..7a339c4d3 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -41,6 +41,7 @@
 #include "coll_id_cache.h"
 #include "coll_id_def.h"
 #include "txn.h"
+#include "trigger.h"
 #include "tuple.h"
 #include "fiber.h" /* for gc_pool */
 #include "scoped_guard.h"
@@ -3964,7 +3965,7 @@ on_create_trigger_rollback(struct lua_trigger *trigger, void * /* event */)
 	(void)rc;
 	assert(rc == 0);
 	assert(new_trigger == old_trigger);
-	sql_trigger_delete(sql_get(), new_trigger);
+	sql_trigger_delete(new_trigger);
 }
 
 /** Restore the old trigger on rollback of a DELETE statement. */
@@ -3995,7 +3996,7 @@ on_replace_trigger_rollback(struct lua_trigger *trigger, void * /* event */)
 				sql_trigger_space_id(old_trigger),
 				old_trigger, &new_trigger) != 0)
 		panic("Out of memory on insertion into trigger hash");
-	sql_trigger_delete(sql_get(), new_trigger);
+	sql_trigger_delete(new_trigger);
 }
 
 /**
@@ -4006,7 +4007,7 @@ static void
 on_replace_trigger_commit(struct lua_trigger *trigger, void * /* event */)
 {
 	struct sql_trigger *old_trigger = (struct sql_trigger *)trigger->data;
-	sql_trigger_delete(sql_get(), old_trigger);
+	sql_trigger_delete(old_trigger);
 }
 
 /**
@@ -4069,7 +4070,7 @@ on_replace_dd_trigger(struct lua_trigger * /* trigger */, void *event)
 			diag_raise();
 
 		auto new_trigger_guard = make_scoped_guard([=] {
-		    sql_trigger_delete(sql_get(), new_trigger);
+		    sql_trigger_delete(new_trigger);
 		});
 
 		const char *trigger_name = sql_trigger_name(new_trigger);
diff --git a/src/box/fk_constraint.c b/src/box/fk_constraint.c
index 6ed34968e..5b0060ff5 100644
--- a/src/box/fk_constraint.c
+++ b/src/box/fk_constraint.c
@@ -49,8 +49,8 @@ const char *fk_constraint_match_strs[] = {
 void
 fk_constraint_delete(struct fk_constraint *fk)
 {
-	sql_trigger_delete(sql_get(), fk->on_delete_trigger);
-	sql_trigger_delete(sql_get(), fk->on_update_trigger);
+	sql_trigger_delete(fk->on_delete_trigger);
+	sql_trigger_delete(fk->on_update_trigger);
 	free(fk->def);
 	free(fk);
 }
diff --git a/src/box/sql.h b/src/box/sql.h
index d0c18b6ff..926f4ba19 100644
--- a/src/box/sql.h
+++ b/src/box/sql.h
@@ -33,6 +33,7 @@
 
 #include <stdbool.h>
 #include <stdint.h>
+#include "box/trigger.h"
 #include "box/trigger_def.h"
 #include "small/rlist.h"
 
@@ -125,8 +126,13 @@ sql_trigger_expr_new(struct sql *db, struct IdList *cols, struct Expr *when,
 void
 sql_trigger_expr_delete(struct sql *db, struct sql_trigger_expr *trigger_expr);
 
+struct sql_trigger *
+sql_trigger_new(struct trigger_def *def, struct sql_trigger_expr *expr,
+		bool is_fk_constraint_trigger);
+
 /**
- * Perform parsing of provided SQL request and construct trigger AST.
+ * Perform parsing of provided SQL request and construct
+ * sql_trigger_expr AST.
  * @param db SQL context handle.
  * @param sql request to parse.
  *
@@ -138,11 +144,10 @@ sql_trigger_compile(struct sql *db, const char *sql);
 
 /**
  * Free AST pointed by trigger.
- * @param db SQL handle.
  * @param trigger AST object.
  */
 void
-sql_trigger_delete(struct sql *db, struct sql_trigger *trigger);
+sql_trigger_delete(struct sql_trigger *trigger);
 
 /**
  * Get server triggers list by space_id.
@@ -461,30 +466,14 @@ vdbe_field_ref_prepare_tuple(struct vdbe_field_ref *field_ref,
  * program.
  */
 struct sql_trigger {
-	/** The name of the trigger. */
-	char *zName;
-	/** The ID of space the trigger refers to. */
-	uint32_t space_id;
+	struct trigger base;
 	/**
-	 * The trigger event. This is the type of operation
-	 * on the associated space for which the trigger
-	 * activates. The value is `INSERT` (a row was inserted),
-	 * `DELETE` (a row was deleted), or `UPDATE` (a row was
-	 * modified).
+	 * Whether this trigger implements FK
+	 * constraint logic.
 	 */
-	enum trigger_event_manipulation event_manipulation;
-	/**
-	 * Whether the trigger activates before or after the
-	 * triggering event. The value is `BEFORE` or `AFTER`.
-	 */
-	enum trigger_action_timing action_timing;
+	bool is_fk_constraint_trigger;
 	/** The SQL trigger AST. */
 	struct sql_trigger_expr *expr;
-	/**
-	 * Organize sql_trigger structs into linked list
-	 * with space::trigger_list.
-	 */
-	struct rlist link;
 };
 
 /**
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 5040d04d1..8a6c8cb6c 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -1628,9 +1628,11 @@ sql_code_drop_table(struct Parse *parse_context, struct space *space,
 	 * Do not account triggers deletion - they will be
 	 * accounted in DELETE from _space below.
 	 */
-	struct sql_trigger *trigger;
-	rlist_foreach_entry(trigger, &space->trigger_list, link)
-		vdbe_code_drop_trigger(parse_context, trigger->zName, false);
+	struct trigger *trigger;
+	rlist_foreach_entry(trigger, &space->trigger_list, link) {
+		vdbe_code_drop_trigger(parse_context, trigger->def->name,
+				       false);
+	}
 	/*
 	 * Remove any entries from the _sequence_data, _sequence
 	 * and _space_sequence spaces associated with the table
diff --git a/src/box/sql/fk_constraint.c b/src/box/sql/fk_constraint.c
index 454ed311f..65c13f453 100644
--- a/src/box/sql/fk_constraint.c
+++ b/src/box/sql/fk_constraint.c
@@ -35,6 +35,7 @@
  */
 #include "coll/coll.h"
 #include "sqlInt.h"
+#include "box/trigger_def.h"
 #include "box/fk_constraint.h"
 #include "box/schema.h"
 
@@ -687,6 +688,70 @@ sql_expr_new_2part_id(struct Parse *parser, const struct Token *main,
 	return sqlPExpr(parser, TK_DOT, emain, esub);
 }
 
+/**
+ * Allocate a new trigger object for given fk constraint
+ * definition and parsed context. Function duplicates all given
+ * data to store data in reduced form.
+ *
+ * In case of success not NULL value is returned, NULL otherwise.
+*/
+static struct sql_trigger *
+sql_trigger_for_fk_constrint_new(struct Parse *parser,
+		const char *child_space_name, uint32_t child_space_name_len,
+		uint32_t parent_space_id, struct ExprList *action_list,
+		struct Select *select, struct Expr *where, struct Expr *when)
+{
+	struct sql *db = parser->db;
+	size_t step_size = sizeof(TriggerStep) + child_space_name_len + 1;
+	struct TriggerStep *step = sqlDbMallocZero(db, step_size);
+	if (step == NULL)
+		goto halt;
+	step->zTarget = (char *) &step[1];
+	memcpy((char *) step->zTarget, child_space_name, child_space_name_len);
+	step->pWhere = sqlExprDup(db, where, EXPRDUP_REDUCE);
+	step->pExprList = sql_expr_list_dup(db, action_list, EXPRDUP_REDUCE);
+	step->pSelect = sqlSelectDup(db, select, EXPRDUP_REDUCE);
+	struct Expr *cond_expr = NULL;
+	if (when != NULL) {
+		struct Expr *when_copy = sqlExprDup(db, when, 0);
+		if (when_copy != NULL)
+			cond_expr = sqlPExpr(parser, TK_NOT, when_copy, NULL);
+		if (cond_expr == NULL) {
+			sql_expr_delete(db, when_copy, false);
+			sqlDeleteTriggerStep(db, step);
+			goto halt;
+		}
+	}
+	struct sql_trigger_expr *expr =
+		sql_trigger_expr_new(db, NULL, cond_expr, step);
+	if (expr == NULL) {
+		sql_expr_delete(db, cond_expr, false);
+		sqlDeleteTriggerStep(db, step);
+		goto halt;
+	}
+
+	const char *trigger_name = tt_sprintf("fk_trigger_%p", expr);
+	struct trigger_def *def =
+		trigger_def_new(trigger_name, strlen(trigger_name),
+				parent_space_id, TRIGGER_LANGUAGE_SQL,
+				TRIGGER_EVENT_MANIPULATION_UPDATE,
+				TRIGGER_ACTION_TIMING_BEFORE);
+	if (def == NULL) {
+		sql_trigger_expr_delete(db, expr);
+		goto halt;
+	}
+	struct sql_trigger *trigger = sql_trigger_new(def, expr, true);
+	if (trigger == NULL) {
+		trigger_def_delete(def);
+		sql_trigger_expr_delete(db, expr);
+		goto halt;
+	}
+	return trigger;
+halt:
+	parser->is_aborted = true;
+	return NULL;
+}
+
 /**
  * This function is called when an UPDATE or DELETE operation is
  * being compiled on table pTab, which is the parent table of
@@ -739,7 +804,6 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def,
 						  fk->on_delete_trigger;
 	if (action == FKEY_NO_ACTION || trigger != NULL)
 		return trigger;
-	struct TriggerStep *step = NULL;
 	struct Expr *where = NULL, *when = NULL;
 	struct ExprList *list = NULL;
 	struct Select *select = NULL;
@@ -852,62 +916,41 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def,
 		where = NULL;
 	}
 
-	trigger = (struct sql_trigger *) sqlDbMallocZero(db,
-							     sizeof(*trigger));
-	if (trigger != NULL) {
-		size_t step_size = sizeof(TriggerStep) + name_len + 1;
-		step = sqlDbMallocZero(db, step_size);
-		rlist_create(&trigger->link);
-		if (step == NULL)
-			goto end;
-		step->zTarget = (char *) &step[1];
-		memcpy((char *) step->zTarget, space_name, name_len);
-		step->pWhere = sqlExprDup(db, where, EXPRDUP_REDUCE);
-		step->pExprList = sql_expr_list_dup(db, list, EXPRDUP_REDUCE);
-		step->pSelect = sqlSelectDup(db, select, EXPRDUP_REDUCE);
-		if (when != NULL) {
-			when = sqlPExpr(pParse, TK_NOT, when, 0);
-			if (when == NULL)
-				goto end;
-		}
-		trigger->expr = sql_trigger_expr_new(db, NULL, when, step);
-		if (trigger->expr == NULL)
-			goto end;
-		when = NULL;
-	}
-
-end:
+	trigger = sql_trigger_for_fk_constrint_new(pParse, space_name, name_len,
+						   fk_def->parent_id, list,
+						   select, where, when);
 	sql_expr_delete(db, where, false);
 	sql_expr_delete(db, when, false);
 	sql_expr_list_delete(db, list);
 	sql_select_delete(db, select);
-	if (db->mallocFailed) {
-		sql_trigger_delete(db, trigger);
+
+	if (db->mallocFailed || trigger == NULL) {
+		sql_trigger_delete(trigger);
 		return NULL;
 	}
-	assert(step != NULL);
 
 	switch (action) {
 	case FKEY_ACTION_RESTRICT:
-		step->op = TK_SELECT;
+		trigger->expr->step_list->op = TK_SELECT;
 		break;
 	case FKEY_ACTION_CASCADE:
 		if (! is_update) {
-			step->op = TK_DELETE;
+			trigger->expr->step_list->op = TK_DELETE;
 			break;
 		}
 		FALLTHROUGH;
 	default:
-		step->op = TK_UPDATE;
+		trigger->expr->step_list->op = TK_UPDATE;
 	}
 
 	if (is_update) {
 		fk->on_update_trigger = trigger;
-		trigger->event_manipulation =
+		trigger->base.def->event_manipulation =
 			TRIGGER_EVENT_MANIPULATION_UPDATE;
+
 	} else {
 		fk->on_delete_trigger = trigger;
-		trigger->event_manipulation =
+		trigger->base.def->event_manipulation =
 			TRIGGER_EVENT_MANIPULATION_DELETE;
 	}
 	return trigger;
diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
index e077a8b5e..84a265f96 100644
--- a/src/box/sql/prepare.c
+++ b/src/box/sql/prepare.c
@@ -269,7 +269,7 @@ sql_parser_destroy(Parse *parser)
 		sql_expr_delete(db, parser->parsed_ast.expr, false);
 		break;
 	case AST_TYPE_TRIGGER:
-		sql_trigger_delete(db, parser->parsed_ast.trigger);
+		sql_trigger_delete(parser->parsed_ast.trigger);
 		break;
 	default:
 		assert(parser->parsed_ast_type == AST_TYPE_UNDEFINED);
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 71a78d7ed..2efdff3be 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -65,8 +65,6 @@ sqlDeleteTriggerStep(sql * db, TriggerStep * pTriggerStep)
 void
 sql_store_trigger(struct Parse *parse)
 {
-	/* The new trigger. */
-	struct sql_trigger *trigger = NULL;
 	/* The database connection. */
 	struct sql *db = parse->db;
 	struct create_trigger_def *trigger_def = &parse->create_trigger_def;
@@ -75,17 +73,18 @@ sql_store_trigger(struct Parse *parse)
 	assert(alter_def->entity_type == ENTITY_TYPE_TRIGGER);
 	assert(alter_def->alter_action == ALTER_ACTION_CREATE);
 
-	char *trigger_name = NULL;
-	if (alter_def->entity_name == NULL || db->mallocFailed)
-		goto trigger_cleanup;
+	struct region *region = &parse->region;
+	size_t region_svp = region_used(region);
 	assert(alter_def->entity_name->nSrc == 1);
 	assert(create_def->name.n > 0);
-	trigger_name = sql_name_from_token(db, &create_def->name);
+	char *trigger_name =
+		sql_normalized_name_region_new(region, create_def->name.z,
+					       create_def->name.n);
 	if (trigger_name == NULL)
 		goto set_tarantool_error_and_cleanup;
-
 	if (sqlCheckIdentifierName(parse, trigger_name) != 0)
-		goto trigger_cleanup;
+		goto set_tarantool_error_and_cleanup;
+	uint32_t trigger_name_len = region_used(region) - region_svp;
 
 	const char *table_name = alter_def->entity_name->a[0].zName;
 	uint32_t space_id = box_space_id_by_name(table_name,
@@ -95,46 +94,39 @@ sql_store_trigger(struct Parse *parse)
 		goto set_tarantool_error_and_cleanup;
 	}
 
-	/* Build the Trigger object. */
-	trigger = (struct sql_trigger *)sqlDbMallocZero(db,
-							    sizeof(struct
-								   sql_trigger));
-	if (trigger == NULL)
-		goto trigger_cleanup;
-	trigger->space_id = space_id;
-	trigger->zName = trigger_name;
-	trigger_name = NULL;
-	trigger->event_manipulation = trigger_def->event_manipulation;
-	trigger->action_timing = trigger_def->action_timing;
-	rlist_create(&trigger->link);
-	trigger->expr = sql_trigger_expr_new(db, trigger_def->cols,
-					     trigger_def->when,
-					     trigger_def->step_list);
-	if (trigger->expr == NULL)
-		goto trigger_cleanup;
-	trigger_def->cols = NULL;
-	trigger_def->when = NULL;
-	trigger_def->step_list = NULL;
-
+	/* Construct a trigger object. */
+	struct trigger_def *def =
+		trigger_def_new(trigger_name, trigger_name_len,
+				space_id, TRIGGER_LANGUAGE_SQL,
+				trigger_def->event_manipulation,
+				trigger_def->action_timing);
+	if (def == NULL)
+		goto set_tarantool_error_and_cleanup;
+	struct sql_trigger_expr *expr =
+		sql_trigger_expr_new(db, trigger_def->cols, trigger_def->when,
+				     trigger_def->step_list);
+	if (expr == NULL) {
+		trigger_def_delete(def);
+		goto set_tarantool_error_and_cleanup;
+	}
 	assert(parse->parsed_ast.trigger == NULL);
-	parse->parsed_ast.trigger = trigger;
 	parse->parsed_ast_type = AST_TYPE_TRIGGER;
+	parse->parsed_ast.trigger = sql_trigger_new(def, expr, false);
+	if (parse->parsed_ast.trigger == NULL) {
+		sql_trigger_expr_delete(db, expr);
+		trigger_def_delete(def);
+		goto set_tarantool_error_and_cleanup;
+	}
 
- trigger_cleanup:
-	sqlDbFree(db, trigger_name);
+trigger_cleanup:
+	region_truncate(region, region_svp);
 	sqlSrcListDelete(db, alter_def->entity_name);
-	sqlIdListDelete(db, trigger_def->cols);
-	sql_expr_delete(db, trigger_def->when, false);
-	sqlDeleteTriggerStep(db, parse->create_trigger_def.step_list);
-	if (parse->parsed_ast.trigger == NULL)
-		sql_trigger_delete(db, trigger);
-	else
-		assert(parse->parsed_ast.trigger == trigger);
-
 	return;
-
 set_tarantool_error_and_cleanup:
 	parse->is_aborted = true;
+	sqlIdListDelete(db, trigger_def->cols);
+	sql_expr_delete(db, trigger_def->when, false);
+	sqlDeleteTriggerStep(db, trigger_def->step_list);
 	goto trigger_cleanup;
 }
 
@@ -287,15 +279,30 @@ sql_trigger_expr_delete(struct sql *db, struct sql_trigger_expr *trigger_expr)
 	free(trigger_expr);
 }
 
+struct sql_trigger *
+sql_trigger_new(struct trigger_def *def, struct sql_trigger_expr *expr,
+		bool is_fk_constraint_trigger)
+{
+	struct sql_trigger *trigger = malloc(sizeof(*trigger));
+	if (trigger == NULL) {
+		diag_set(OutOfMemory, sizeof(*trigger), "malloc", "trigger");
+		return NULL;
+	}
+	rlist_create(&trigger->base.link);
+	trigger->base.def = def;
+	trigger->expr = expr;
+	trigger->is_fk_constraint_trigger = is_fk_constraint_trigger;
+	return trigger;
+}
+
 void
-sql_trigger_delete(struct sql *db, struct sql_trigger *trigger)
+sql_trigger_delete(struct sql_trigger *trigger)
 {
 	if (trigger == NULL)
 		return;
-	if (trigger->expr != NULL)
-		sql_trigger_expr_delete(db, trigger->expr);
-	sqlDbFree(db, trigger->zName);
-	sqlDbFree(db, trigger);
+	trigger_def_delete(trigger->base.def);
+	sql_trigger_expr_delete(sql_get(), trigger->expr);
+	free(trigger);
 }
 
 void
@@ -362,10 +369,9 @@ sql_drop_trigger(struct Parse *parser)
 
 int
 sql_trigger_replace(const char *name, uint32_t space_id,
-		    struct sql_trigger *trigger,
-		    struct sql_trigger **old_trigger)
+		    struct sql_trigger *trigger, struct sql_trigger **old_trigger)
 {
-	assert(trigger == NULL || strcmp(name, trigger->zName) == 0);
+	assert(trigger == NULL || strcmp(name, trigger->base.def->name) == 0);
 
 	struct space *space = space_cache_find(space_id);
 	assert(space != NULL);
@@ -383,52 +389,58 @@ sql_trigger_replace(const char *name, uint32_t space_id,
 		 * views only support INSTEAD of triggers.
 		 */
 		if (space->def->opts.is_view &&
-		    trigger->action_timing != TRIGGER_ACTION_TIMING_INSTEAD) {
+		    trigger->base.def->action_timing !=
+		    TRIGGER_ACTION_TIMING_INSTEAD) {
 			const char *err_msg =
 				tt_sprintf("cannot create %s trigger on "
 					   "view: %s",
 					    trigger_action_timing_strs[
-						trigger->action_timing],
+						trigger->base.def->
+						action_timing],
 					    space->def->name);
 			diag_set(ClientError, ER_SQL_EXECUTE, err_msg);
 			return -1;
 		}
 		if (!space->def->opts.is_view &&
-		    trigger->action_timing == TRIGGER_ACTION_TIMING_INSTEAD) {
+		    trigger->base.def->action_timing ==
+		    TRIGGER_ACTION_TIMING_INSTEAD) {
 			diag_set(ClientError, ER_SQL_EXECUTE,
 				 tt_sprintf("cannot create "\
                          "INSTEAD OF trigger on space: %s", space->def->name));
 			return -1;
 		}
 
-		if (trigger->action_timing == TRIGGER_ACTION_TIMING_INSTEAD)
-			trigger->action_timing = TRIGGER_ACTION_TIMING_BEFORE;
+		if (trigger->base.def->action_timing ==
+		    TRIGGER_ACTION_TIMING_INSTEAD) {
+			trigger->base.def->action_timing =
+				TRIGGER_ACTION_TIMING_BEFORE;
+		}
 	}
 
-	struct sql_trigger *p;
+	struct trigger *p;
 	rlist_foreach_entry(p, &space->trigger_list, link) {
-		if (strcmp(p->zName, name) != 0)
+		if (strcmp(p->def->name, name) != 0)
 			continue;
-		*old_trigger = p;
+		*old_trigger = (struct sql_trigger *)p;
 		rlist_del(&p->link);
 		break;
 	}
 
 	if (trigger != NULL)
-		rlist_add(&space->trigger_list, &trigger->link);
+		rlist_add(&space->trigger_list, &trigger->base.link);
 	return 0;
 }
 
 const char *
 sql_trigger_name(struct sql_trigger *trigger)
 {
-	return trigger->zName;
+	return trigger->base.def->name;
 }
 
 uint32_t
 sql_trigger_space_id(struct sql_trigger *trigger)
 {
-	return trigger->space_id;
+	return trigger->base.def->space_id;
 }
 
 /*
@@ -463,11 +475,12 @@ sql_triggers_exist(struct space *space,
 	struct rlist *trigger_list = NULL;
 	if ((sql_flags & SQL_EnableTrigger) != 0)
 		trigger_list = &space->trigger_list;
-	struct sql_trigger *p;
+	struct trigger *p;
 	rlist_foreach_entry(p, trigger_list, link) {
-		if (p->event_manipulation == event_manipulation &&
-		    checkColumnOverlap(p->expr->cols, changes_list) != 0)
-			mask |= (1 << p->action_timing);
+		struct sql_trigger *trigger = (struct sql_trigger *) p;
+		if (p->def->event_manipulation == event_manipulation &&
+		    checkColumnOverlap(trigger->expr->cols, changes_list) != 0)
+			mask |= (1 << p->def->action_timing);
 	}
 	if (mask_ptr != NULL)
 		*mask_ptr = mask;
@@ -648,7 +661,6 @@ sql_row_trigger_program(struct Parse *parser, struct sql_trigger *trigger,
 	Parse *pSubParse;	/* Parse context for sub-vdbe */
 	int iEndTrigger = 0;	/* Label to jump to if WHEN is false */
 
-	assert(trigger->zName == NULL || space->def->id == trigger->space_id);
 	assert(pTop->pVdbe);
 
 	/* Allocate the TriggerPrg and SubProgram objects. To ensure that they
@@ -681,22 +693,25 @@ sql_row_trigger_program(struct Parse *parser, struct sql_trigger *trigger,
 	sNC.pParse = pSubParse;
 	pSubParse->triggered_space = space;
 	pSubParse->pToplevel = pTop;
-	pSubParse->trigger_event_manipulation = trigger->event_manipulation;
+	pSubParse->trigger_event_manipulation =
+		trigger->base.def->event_manipulation;
 	pSubParse->nQueryLoop = parser->nQueryLoop;
 
 	/* Temporary VM. */
 	struct Vdbe *v = sqlGetVdbe(pSubParse);
 	if (v != NULL) {
 		VdbeComment((v, "Start: %s.%s (%s %s ON %s)",
-			     trigger->zName, onErrorText(orconf),
-			     trigger_action_timing_strs[trigger->action_timing],
+			     trigger->base.def->name, onErrorText(orconf),
+			     trigger_action_timing_strs[trigger->base.
+							def->action_timing],
 			      trigger_event_manipulation_strs[
-						trigger->event_manipulation],
+						trigger->base.def->
+						event_manipulation],
 			      space->def->name));
 		sqlVdbeChangeP4(v, -1,
 				    sqlMPrintf(db, "-- TRIGGER %s",
-						   trigger->zName),
-				    P4_DYNAMIC);
+					       trigger->base.def->name),
+					       P4_DYNAMIC);
 
 		/*
 		 * If one was specified, code the WHEN clause. If
@@ -723,7 +738,7 @@ sql_row_trigger_program(struct Parse *parser, struct sql_trigger *trigger,
 		if (iEndTrigger)
 			sqlVdbeResolveLabel(v, iEndTrigger);
 		sqlVdbeAddOp0(v, OP_Halt);
-		VdbeComment((v, "End: %s.%s", trigger->zName,
+		VdbeComment((v, "End: %s.%s", trigger->base.def->name,
 			     onErrorText(orconf)));
 
 		if (!parser->is_aborted)
@@ -769,7 +784,8 @@ sql_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
 	Parse *pRoot = sqlParseToplevel(parser);
 	TriggerPrg *pPrg;
 
-	assert(trigger->zName == NULL || space->def->id == trigger->space_id);
+	assert(trigger->base.def == NULL ||
+	       space->def->id == trigger->base.def->space_id);
 
 	/*
 	 * It may be that this trigger has already been coded (or
@@ -813,13 +829,14 @@ vdbe_code_row_trigger_direct(struct Parse *parser, struct sql_trigger *trigger,
 		return;
 
 	bool is_recursive =
-		trigger->zName && (parser->sql_flags & SQL_RecTriggers) == 0;
+		!trigger->is_fk_constraint_trigger &&
+		(parser->sql_flags & SQL_RecTriggers) == 0;
 
 	sqlVdbeAddOp4(v, OP_Program, reg, ignore_jump,
 			  ++parser->nMem, (const char *)pPrg->pProgram,
 			  P4_SUBPROGRAM);
-	VdbeComment((v, "Call: %s.%s", (trigger->zName ? trigger->zName :
-					"fk_constraint"),
+	VdbeComment((v, "Call: %s.%s", (trigger->base.def ?
+			trigger->base.def->name : "fk_constraint"),
 		     onErrorText(orconf)));
 
 	/*
@@ -848,14 +865,15 @@ vdbe_code_row_trigger(struct Parse *parser, struct rlist *trigger_list,
 	if (trigger_list == NULL)
 		return;
 
-	struct sql_trigger *p;
+	struct trigger *p;
 	rlist_foreach_entry(p, trigger_list, link) {
 		/* Determine whether we should code trigger. */
-		if (p->event_manipulation == event_manipulation &&
-		    p->action_timing == action_timing &&
-		    checkColumnOverlap(p->expr->cols, changes_list)) {
-			vdbe_code_row_trigger_direct(parser, p, space, reg,
-						     orconf, ignore_jump);
+		struct sql_trigger *trigger = (struct sql_trigger *) p;
+		if (p->def->event_manipulation == event_manipulation &&
+		    p->def->action_timing == action_timing &&
+		    checkColumnOverlap(trigger->expr->cols, changes_list)) {
+			vdbe_code_row_trigger_direct(parser, trigger, space,
+						     reg, orconf, ignore_jump);
 		}
 	}
 }
@@ -873,13 +891,14 @@ sql_trigger_colmask(Parse *parser, struct rlist *trigger_list,
 		return mask;
 
 	assert(new == 1 || new == 0);
-	struct sql_trigger *p;
+	struct trigger *p;
 	rlist_foreach_entry(p, trigger_list, link) {
-		if (p->event_manipulation == event_manipulation &&
-		    ((action_timing_mask & (1 << p->action_timing)) != 0) &&
-		    checkColumnOverlap(p->expr->cols, changes_list)) {
+		struct sql_trigger *trigger = (struct sql_trigger *)p;
+		if (p->def->event_manipulation == event_manipulation &&
+		    ((action_timing_mask & (1 << p->def->action_timing)) != 0) &&
+		    checkColumnOverlap(trigger->expr->cols, changes_list)) {
 			TriggerPrg *prg =
-				sql_row_trigger(parser, p, space, orconf);
+				sql_row_trigger(parser, trigger, space, orconf);
 			if (prg != NULL)
 				mask |= prg->column_mask[new];
 		}
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 7af1bfa60..3cd8a57f0 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -4730,7 +4730,7 @@ case OP_RenameTable: {
 	 * due to lack of transactional DDL, but just do the best
 	 * effort.
 	 */
-	struct sql_trigger *trigger, *tmp;
+	struct trigger *trigger, *tmp;
 	rlist_foreach_entry_safe(trigger, &space->trigger_list, link, tmp) {
 		/*
 		 * FIXME: In the case of error, part of triggers
@@ -4738,7 +4738,7 @@ case OP_RenameTable: {
 		 * not been persisted. Server could be restarted.
 		 * In this case, rename table back and try again.
 		 */
-		if (tarantoolsqlRenameTrigger(trigger->zName, zOldTableName,
+		if (tarantoolsqlRenameTrigger(trigger->def->name, zOldTableName,
 					      zNewTableName) != 0)
 			goto abort_due_to_error;
 	}
diff --git a/src/box/trigger.h b/src/box/trigger.h
new file mode 100644
index 000000000..9b5853fe4
--- /dev/null
+++ b/src/box/trigger.h
@@ -0,0 +1,59 @@
+#ifndef INCLUDES_BOX_TRIGGER_H
+#define INCLUDES_BOX_TRIGGER_H
+/*
+ * Copyright 2010-2019, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+#include "small/rlist.h"
+
+struct trigger_def;
+
+/**
+ * Structure representing trigger.
+ */
+struct trigger {
+	/** The trigger definition. */
+	struct trigger_def *def;
+	/**
+	 * Organize sql_trigger structs into linked list
+	 * with space::trigger_list.
+	 */
+	struct rlist link;
+};
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
+
+#endif /* INCLUDES_BOX_TRIGGER_H */
diff --git a/src/box/trigger_def.c b/src/box/trigger_def.c
index ff7938f8d..83e929662 100644
--- a/src/box/trigger_def.c
+++ b/src/box/trigger_def.c
@@ -29,7 +29,38 @@
  * SUCH DAMAGE.
  */
 #include "trigger_def.h"
+#include "diag.h"
 
 const char *trigger_event_manipulation_strs[] = {"DELETE", "UPDATE", "INSERT"};
 
 const char *trigger_action_timing_strs[] = {"BEFORE", "AFTER", "INSTEAD"};
+
+const char *trigger_language_strs[] = {"SQL"};
+
+struct trigger_def *
+trigger_def_new(const char *name, uint32_t name_len, uint32_t space_id,
+		enum trigger_language language,
+		enum trigger_event_manipulation event_manipulation,
+		enum trigger_action_timing action_timing)
+{
+	uint32_t trigger_def_sz = trigger_def_sizeof(name_len);
+	struct trigger_def *trigger_def =
+		(struct trigger_def *) malloc(trigger_def_sz);
+	if (trigger_def == NULL) {
+		diag_set(OutOfMemory, trigger_def_sz, "malloc", "trigger_def");
+		return NULL;
+	}
+	trigger_def->space_id = space_id;
+	trigger_def->language = language;
+	trigger_def->event_manipulation = event_manipulation;
+	trigger_def->action_timing = action_timing;
+	memcpy(trigger_def->name, name, name_len);
+	trigger_def->name[name_len] = '\0';
+	return trigger_def;
+}
+
+void
+trigger_def_delete(struct trigger_def *trigger_def)
+{
+	free(trigger_def);
+}
diff --git a/src/box/trigger_def.h b/src/box/trigger_def.h
index dd34f6859..98df3a3de 100644
--- a/src/box/trigger_def.h
+++ b/src/box/trigger_def.h
@@ -35,6 +35,8 @@
 extern "C" {
 #endif /* defined(__cplusplus) */
 
+#include <inttypes.h>
+
 /**
  * The trigger event. This is the type of operation
  * on the associated space for which the trigger
@@ -67,6 +69,98 @@ enum trigger_action_timing {
 
 extern const char *trigger_action_timing_strs[];
 
+/**
+ * The supported language of the stored function.
+ */
+enum trigger_language {
+	TRIGGER_LANGUAGE_SQL,
+	trigger_language_MAX,
+};
+
+extern const char *trigger_language_strs[];
+
+/**
+ * Trigger object definition.
+ * See trigger_def_sizeof() definition for implementation
+ * details and memory layout.
+ */
+struct trigger_def {
+	/**
+	 * The trigger event. This is the type of operation
+	 * on the associated space for which the trigger
+	 * activates. The value is `INSERT` (a row was inserted),
+	 * `DELETE` (a row was deleted), or `UPDATE` (a row was
+	 * modified).
+	 */
+	enum trigger_event_manipulation event_manipulation;
+	/**
+	 * Whether the trigger activates before or after the
+	 * triggering event. The value is `BEFORE` or `AFTER`.
+	 */
+	enum trigger_action_timing action_timing;
+	/** The ID of space the trigger refers to. */
+	uint32_t space_id;
+	/** The language of the stored trigger. */
+	enum trigger_language language;
+	/**
+	 * The 0-terminated string, a name of the check
+	 * constraint. Must be unique for a given space.
+	 */
+	char name[0];
+};
+
+/**
+ * Calculate trigger definition memory size and fields
+ * offsets for given arguments.
+ *
+ * Alongside with struct trigger_def itself, we reserve
+ * memory for the name string.
+ *
+ * Memory layout:
+ * +-----------------------------+ <- Allocated memory starts here
+ * |      struct trigger_def     |
+ * |-----------------------------|
+ * |          name + \0          |
+ * +-----------------------------+
+ *
+ * @param name_len The length of the name.
+ * @return The size of the trigger definition object for
+ *         given parameters.
+ */
+static inline uint32_t
+trigger_def_sizeof(uint32_t name_len)
+{
+	return sizeof(struct trigger_def) + name_len + 1;
+}
+
+/**
+ * Create a new trigger definition object with given fields.
+ *
+ * @param name The name string of a new trigger definition.
+ * @param name_len The length of @a name string.
+ * @param space_id The identifier of the target space.
+ * @param language The language of the @a trigger object.
+ * @param event_manipulation The type of operation on the
+ *                           associated space for which the
+ *                           trigger activates.
+ * @param action_timing Whether the trigger activates before or
+ *                      after the triggering event.
+ * @retval not NULL Trigger definition object pointer on success.
+ * @retval NULL Otherwise. The diag message is set.
+*/
+struct trigger_def *
+trigger_def_new(const char *name, uint32_t name_len, uint32_t space_id,
+		enum trigger_language language,
+		enum trigger_event_manipulation event_manipulation,
+		enum trigger_action_timing action_timing);
+
+/**
+ * Destroy trigger definition memory, release acquired resources.
+ * @param trigger_def The trigger definition object to destroy.
+ */
+void
+trigger_def_delete(struct trigger_def *trigger_def);
+
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
-- 
2.23.0

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [Tarantool-patches] [PATCH v1 9/9] schema: rework _trigger system space
  2019-10-14 16:03 [Tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space Kirill Shcherbatov
                   ` (7 preceding siblings ...)
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 8/9] sql: inherit sql_trigger from a new trigger class Kirill Shcherbatov
@ 2019-10-14 16:03 ` Kirill Shcherbatov
  2019-10-17  7:44   ` Konstantin Osipov
  2019-10-15 21:34 ` [Tarantool-patches] [tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space Nikita Pettik
  9 siblings, 1 reply; 31+ messages in thread
From: Kirill Shcherbatov @ 2019-10-14 16:03 UTC (permalink / raw)
  To: tarantool-patches, tarantool-patches, kostja.osipov, korablev

This patch reworks a _trigger system space to make it
useful not only for sql triggers definitions.

The format of the updated system space is

_trigger (space id = 328)
[<name> STRING, <space_id> UINT32, <opts> MAP, <language> STR,
 <type> STR, <event_manipulation> STR,
 <action_timing> STR, <code> STR]

After insertion into this space, a new instance describing trigger
object is created using language-dependent constructor.

This volumerous refactoring is an initial step for further
introduction of persistent triggers in Lua.

Needed for #4343
---
 src/box/CMakeLists.txt                        |   1 +
 src/box/alter.cc                              | 201 +++++++++++-------
 src/box/bootstrap.snap                        | Bin 5934 -> 5981 bytes
 src/box/errcode.h                             |   1 +
 src/box/fk_constraint.c                       |   6 +-
 src/box/lua/upgrade.lua                       |  16 ++
 src/box/schema_def.h                          |   5 +
 src/box/sql.c                                 |  75 +++----
 src/box/sql.h                                 |  51 +----
 src/box/sql/build.c                           |  37 ++--
 src/box/sql/fk_constraint.c                   |   5 +-
 src/box/sql/prepare.c                         |   7 +-
 src/box/sql/sqlInt.h                          |   4 +-
 src/box/sql/tokenize.c                        |  16 +-
 src/box/sql/trigger.c                         | 149 +++----------
 src/box/trigger.c                             |  73 +++++++
 src/box/trigger.h                             |  21 ++
 src/box/trigger_def.c                         |  69 +++++-
 src/box/trigger_def.h                         |  32 ++-
 test/box-py/bootstrap.result                  |   5 +-
 test/box/access_misc.result                   | 139 ++++++------
 test/box/misc.result                          |   1 +
 test/sql/ddl.result                           |   6 +-
 test/sql/ddl.test.lua                         |   6 +-
 .../gh2141-delete-trigger-drop-table.result   |  36 ++--
 .../gh2141-delete-trigger-drop-table.test.lua |   4 +-
 test/sql/persistency.result                   |  36 ++--
 test/sql/persistency.test.lua                 |   8 +-
 test/sql/triggers.result                      |  91 +++-----
 test/sql/triggers.test.lua                    |  30 +--
 test/sql/upgrade.result                       |  16 +-
 test/sql/upgrade.test.lua                     |   4 +-
 32 files changed, 618 insertions(+), 533 deletions(-)
 create mode 100644 src/box/trigger.c

diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index 2ff5cf01e..fb6279dcd 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -102,6 +102,7 @@ add_library(box STATIC
     fk_constraint.c
     func.c
     func_def.c
+    trigger.c
     trigger_def.c
     key_list.c
     alter.cc
diff --git a/src/box/alter.cc b/src/box/alter.cc
index 7a339c4d3..e4e7f910c 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -3957,30 +3957,27 @@ on_replace_dd_space_sequence(struct lua_trigger * /* trigger */, void *event)
 static void
 on_create_trigger_rollback(struct lua_trigger *trigger, void * /* event */)
 {
-	struct sql_trigger *old_trigger = (struct sql_trigger *)trigger->data;
-	struct sql_trigger *new_trigger;
-	int rc = sql_trigger_replace(sql_trigger_name(old_trigger),
-				     sql_trigger_space_id(old_trigger),
-				     NULL, &new_trigger);
-	(void)rc;
-	assert(rc == 0);
-	assert(new_trigger == old_trigger);
-	sql_trigger_delete(new_trigger);
+	struct trigger *new_trigger = (struct trigger *) trigger->data;
+	struct space *space = space_by_id(new_trigger->def->space_id);
+	assert(space != NULL);
+	assert(space_trigger_by_name(space, new_trigger->def->name,
+				new_trigger->def->name_len) == new_trigger);
+	rlist_del(&new_trigger->link);
+	trigger_delete(new_trigger);
 }
 
 /** Restore the old trigger on rollback of a DELETE statement. */
 static void
 on_drop_trigger_rollback(struct lua_trigger *trigger, void * /* event */)
 {
-	struct sql_trigger *old_trigger = (struct sql_trigger *)trigger->data;
-	struct sql_trigger *new_trigger;
+	struct trigger *old_trigger = (struct trigger *) trigger->data;
 	if (old_trigger == NULL)
 		return;
-	if (sql_trigger_replace(sql_trigger_name(old_trigger),
-				sql_trigger_space_id(old_trigger),
-				old_trigger, &new_trigger) != 0)
-		panic("Out of memory on insertion into trigger hash");
-	assert(new_trigger == NULL);
+	struct space *space = space_by_id(old_trigger->def->space_id);
+	assert(space != NULL);
+	assert(space_trigger_by_name(space, old_trigger->def->name,
+				     old_trigger->def->name_len) == NULL);
+	rlist_add(&space->trigger_list, &old_trigger->link);
 }
 
 /**
@@ -3990,13 +3987,18 @@ on_drop_trigger_rollback(struct lua_trigger *trigger, void * /* event */)
 static void
 on_replace_trigger_rollback(struct lua_trigger *trigger, void * /* event */)
 {
-	struct sql_trigger *old_trigger = (struct sql_trigger *)trigger->data;
-	struct sql_trigger *new_trigger;
-	if (sql_trigger_replace(sql_trigger_name(old_trigger),
-				sql_trigger_space_id(old_trigger),
-				old_trigger, &new_trigger) != 0)
-		panic("Out of memory on insertion into trigger hash");
-	sql_trigger_delete(new_trigger);
+	struct trigger *old_trigger = (struct trigger *) trigger->data;
+	struct space *space = space_by_id(old_trigger->def->space_id);
+	assert(space != NULL);
+	struct trigger *new_trigger =
+		space_trigger_by_name(space, old_trigger->def->name,
+				      old_trigger->def->name_len);
+	assert(new_trigger != NULL);
+	rlist_del(&new_trigger->link);
+	rlist_add(&space->trigger_list, &old_trigger->link);
+	assert(space_trigger_by_name(space, old_trigger->def->name,
+				old_trigger->def->name_len) == old_trigger);
+	trigger_delete(new_trigger);
 }
 
 /**
@@ -4006,8 +4008,72 @@ on_replace_trigger_rollback(struct lua_trigger *trigger, void * /* event */)
 static void
 on_replace_trigger_commit(struct lua_trigger *trigger, void * /* event */)
 {
-	struct sql_trigger *old_trigger = (struct sql_trigger *)trigger->data;
-	sql_trigger_delete(old_trigger);
+	struct trigger *old_trigger = (struct trigger *) trigger->data;
+	if (old_trigger != NULL)
+		trigger_delete(old_trigger);
+}
+
+/** Create a trigger definition from tuple. */
+static struct trigger_def *
+trigger_def_new_from_tuple(struct tuple *tuple)
+{
+	uint32_t name_len;
+	const char *name = tuple_field_str_xc(tuple, BOX_TRIGGER_FIELD_NAME,
+					      &name_len);
+	uint32_t space_id =
+		tuple_field_u32_xc(tuple, BOX_TRIGGER_FIELD_SPACE_ID);
+	const char *language_str =
+		tuple_field_cstr_xc(tuple, BOX_TRIGGER_FIELD_LANGUAGE);
+	enum trigger_language language = STR2ENUM(trigger_language,
+						  language_str);
+	if (language == trigger_language_MAX) {
+		tnt_raise(ClientError, ER_CREATE_TRIGGER,
+			  tt_cstr(name, name_len), "invalid language");
+	}
+	const char *type_str = tuple_field_cstr_xc(tuple,
+						   BOX_TRIGGER_FIELD_TYPE);
+	enum trigger_type type = STR2ENUM(trigger_type, type_str);
+	if (type == trigger_type_MAX) {
+		tnt_raise(ClientError, ER_CREATE_TRIGGER,
+			  tt_cstr(name, name_len), "invalid type");
+	}
+	const char *event_manipulation_str =
+		tuple_field_cstr_xc(tuple, BOX_TRIGGER_FIELD_EVENT_MANIPULATION);
+	enum trigger_event_manipulation event_manipulation =
+		STR2ENUM(trigger_event_manipulation, event_manipulation_str);
+	if (event_manipulation == trigger_event_manipulation_MAX) {
+		tnt_raise(ClientError, ER_CREATE_TRIGGER,
+			  tt_cstr(name, name_len),
+			  "invalid event_manipulation value");
+	}
+	const char *action_timing_str =
+		tuple_field_cstr_xc(tuple, BOX_TRIGGER_FIELD_ACTION_TIMING);
+	enum trigger_action_timing action_timing =
+		STR2ENUM(trigger_action_timing, action_timing_str);
+	if (action_timing == trigger_action_timing_MAX) {
+		tnt_raise(ClientError, ER_CREATE_TRIGGER,
+			  tt_cstr(name, name_len),
+			  "invalid action_timing value");
+	}
+	uint32_t code_len;
+	const char *code = tuple_field_str_xc(tuple, BOX_TRIGGER_FIELD_CODE,
+					      &code_len);
+
+	struct trigger_def *def =
+		trigger_def_new(name, name_len, space_id, language,
+				event_manipulation, action_timing,
+				code, code_len);
+	if (def == NULL)
+		diag_raise();
+	if (trigger_def_check(def) != 0) {
+		trigger_def_delete(def);
+		diag_raise();
+	}
+	assert(def->action_timing != TRIGGER_ACTION_TIMING_INSTEAD ||
+	       def->language == TRIGGER_LANGUAGE_SQL);
+	if (def->action_timing == TRIGGER_ACTION_TIMING_INSTEAD)
+		def->action_timing = TRIGGER_ACTION_TIMING_BEFORE;
+	return def;
 }
 
 /**
@@ -4021,6 +4087,12 @@ on_replace_dd_trigger(struct lua_trigger * /* trigger */, void *event)
 	struct txn_stmt *stmt = txn_current_stmt(txn);
 	struct tuple *old_tuple = stmt->old_tuple;
 	struct tuple *new_tuple = stmt->new_tuple;
+	/* Ignore legacy tuples. */
+	if ((old_tuple != NULL &&
+	     tuple_field_count(old_tuple) <= BOX_TRIGGER_FIELD_CODE) ||
+	    (new_tuple != NULL &&
+	     tuple_field_count(new_tuple) <= BOX_TRIGGER_FIELD_CODE))
+		return;
 
 	struct lua_trigger *on_rollback = txn_alter_trigger_new(NULL, NULL);
 	struct lua_trigger *on_commit =
@@ -4028,73 +4100,42 @@ on_replace_dd_trigger(struct lua_trigger * /* trigger */, void *event)
 
 	if (old_tuple != NULL && new_tuple == NULL) {
 		/* DROP trigger. */
-		uint32_t trigger_name_len;
-		const char *trigger_name_src =
-			tuple_field_str_xc(old_tuple, BOX_TRIGGER_FIELD_NAME,
-					   &trigger_name_len);
-		uint32_t space_id =
-			tuple_field_u32_xc(old_tuple,
-					   BOX_TRIGGER_FIELD_SPACE_ID);
-		char *trigger_name =
-			(char *)region_alloc_xc(&fiber()->gc,
-						trigger_name_len + 1);
-		memcpy(trigger_name, trigger_name_src, trigger_name_len);
-		trigger_name[trigger_name_len] = 0;
-
-		struct sql_trigger *old_trigger;
-		int rc = sql_trigger_replace(trigger_name, space_id, NULL,
-					     &old_trigger);
-		(void)rc;
-		assert(rc == 0);
+		struct trigger_def *def = trigger_def_new_from_tuple(old_tuple);
+		auto trigger_def_guard = make_scoped_guard([=] {
+			trigger_def_delete(def);
+		});
+		struct space *space = space_by_id(def->space_id);
+		assert(space != NULL);
+		struct trigger *old_trigger =
+			space_trigger_by_name(space, def->name, def->name_len);
+		assert(old_trigger != NULL);
+		rlist_del(&old_trigger->link);
 
 		on_commit->data = old_trigger;
 		on_rollback->data = old_trigger;
 		on_rollback->run = on_drop_trigger_rollback;
 	} else {
 		/* INSERT, REPLACE trigger. */
-		uint32_t trigger_name_len;
-		const char *trigger_name_src =
-			tuple_field_str_xc(new_tuple, BOX_TRIGGER_FIELD_NAME,
-					   &trigger_name_len);
-
-		const char *space_opts =
-			tuple_field_with_type_xc(new_tuple,
-						 BOX_TRIGGER_FIELD_OPTS,
-						 MP_MAP);
-		struct space_opts opts;
-		struct region *region = &fiber()->gc;
-		space_opts_decode(&opts, space_opts, region);
-		struct sql_trigger *new_trigger =
-			sql_trigger_compile(sql_get(), opts.sql);
+		struct trigger_def *def = trigger_def_new_from_tuple(new_tuple);
+		auto trigger_def_guard = make_scoped_guard([=] {
+			trigger_def_delete(def);
+		});
+		struct trigger *new_trigger = trigger_new(def);
 		if (new_trigger == NULL)
 			diag_raise();
-
 		auto new_trigger_guard = make_scoped_guard([=] {
-		    sql_trigger_delete(new_trigger);
+			trigger_delete(new_trigger);
 		});
+		trigger_def_guard.is_active = false;
 
-		const char *trigger_name = sql_trigger_name(new_trigger);
-		if (strlen(trigger_name) != trigger_name_len ||
-		    memcmp(trigger_name_src, trigger_name,
-			   trigger_name_len) != 0) {
-			tnt_raise(ClientError, ER_SQL_EXECUTE,
-				  "trigger name does not match extracted "
-				  "from SQL");
-		}
-		uint32_t space_id =
-			tuple_field_u32_xc(new_tuple,
-					   BOX_TRIGGER_FIELD_SPACE_ID);
-		if (space_id != sql_trigger_space_id(new_trigger)) {
-			tnt_raise(ClientError, ER_SQL_EXECUTE,
-				  "trigger space_id does not match the value "
-				  "resolved on AST building from SQL");
-		}
+		struct space *space = space_by_id(def->space_id);
+		assert(space != NULL);
+		struct trigger *old_trigger =
+			space_trigger_by_name(space, def->name, def->name_len);
 
-		struct sql_trigger *old_trigger;
-		if (sql_trigger_replace(trigger_name,
-					sql_trigger_space_id(new_trigger),
-					new_trigger, &old_trigger) != 0)
-			diag_raise();
+		if (old_trigger != NULL)
+			rlist_del(&old_trigger->link);
+		rlist_add(&space->trigger_list, &new_trigger->link);
 
 		on_commit->data = old_trigger;
 		if (old_tuple != NULL) {
diff --git a/src/box/bootstrap.snap b/src/box/bootstrap.snap
index 4c9aea7f50f8ac86be32ca9f126fea9a3d2d182f..4c094e0e48d03110112fbd531b8523f40c6dc3fd 100644
GIT binary patch
literal 5981

diff --git a/src/box/errcode.h b/src/box/errcode.h
index d5d396d87..3d318d7e0 100644
--- a/src/box/errcode.h
+++ b/src/box/errcode.h
@@ -256,6 +256,7 @@ struct errcode_record {
 	/*201 */_(ER_NO_SUCH_FIELD_NAME,	"Field '%s' was not found in the tuple") \
 	/*202 */_(ER_FUNC_WRONG_ARG_COUNT,	"Wrong number of arguments is passed to %s(): expected %s, got %d") \
 	/*203 */_(ER_BOOTSTRAP_READONLY,	"Trying to bootstrap a local read-only instance as master") \
+	/*204 */_(ER_CREATE_TRIGGER,		"Failed to create trigger '%s': %s") \
 
 /*
  * !IMPORTANT! Please follow instructions at start of the file
diff --git a/src/box/fk_constraint.c b/src/box/fk_constraint.c
index 5b0060ff5..28236e9b0 100644
--- a/src/box/fk_constraint.c
+++ b/src/box/fk_constraint.c
@@ -49,8 +49,10 @@ const char *fk_constraint_match_strs[] = {
 void
 fk_constraint_delete(struct fk_constraint *fk)
 {
-	sql_trigger_delete(fk->on_delete_trigger);
-	sql_trigger_delete(fk->on_update_trigger);
+	if (fk->on_delete_trigger != NULL)
+		trigger_delete((struct trigger *)fk->on_delete_trigger);
+	if (fk->on_update_trigger != NULL)
+		trigger_delete((struct trigger *)fk->on_update_trigger);
 	free(fk->def);
 	free(fk);
 }
diff --git a/src/box/lua/upgrade.lua b/src/box/lua/upgrade.lua
index 2abd75dff..f4cfd617f 100644
--- a/src/box/lua/upgrade.lua
+++ b/src/box/lua/upgrade.lua
@@ -927,6 +927,22 @@ local function upgrade_to_2_3_0()
                                         datetime, datetime})
         _priv:replace{ADMIN, PUBLIC, 'function', t.id, box.priv.X}
     end
+
+    log.info("Migrate SQL Triggers")
+    local _trigger = box.space[box.schema.TRIGGER_ID]
+    for _, v in _trigger:pairs() do
+        _trigger:delete(v.name)
+        box.execute(v.opts.sql)
+    end
+    local format = {{name='name', type='string'},
+                    {name='space_id', type='unsigned'},
+                    {name='opts', type='map'},
+                    {name='language', type='string'},
+                    {name='type', type='string'},
+                    {name='event_manipulation', type='string'},
+                    {name='action_timing', type='string'},
+                    {name='code', type='string'}}
+    _trigger:format(format)
 end
 
 --------------------------------------------------------------------------------
diff --git a/src/box/schema_def.h b/src/box/schema_def.h
index 85f652d52..2786688cc 100644
--- a/src/box/schema_def.h
+++ b/src/box/schema_def.h
@@ -245,6 +245,11 @@ enum {
 	BOX_TRIGGER_FIELD_NAME = 0,
 	BOX_TRIGGER_FIELD_SPACE_ID = 1,
 	BOX_TRIGGER_FIELD_OPTS = 2,
+	BOX_TRIGGER_FIELD_LANGUAGE = 3,
+	BOX_TRIGGER_FIELD_TYPE = 4,
+	BOX_TRIGGER_FIELD_EVENT_MANIPULATION = 5,
+	BOX_TRIGGER_FIELD_ACTION_TIMING = 6,
+	BOX_TRIGGER_FIELD_CODE = 7,
 };
 
 /** _fk_constraint fields. */
diff --git a/src/box/sql.c b/src/box/sql.c
index 134225dcc..140da5e46 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -580,8 +580,11 @@ int tarantoolsqlRenameTrigger(const char *trig_name,
 	uint32_t trig_name_len = strlen(trig_name);
 	uint32_t old_table_name_len = strlen(old_table_name);
 	uint32_t new_table_name_len = strlen(new_table_name);
+
+	struct region *region = &fiber()->gc;
+	size_t region_svp = region_used(region);
 	uint32_t key_len = mp_sizeof_str(trig_name_len) + mp_sizeof_array(1);
-	char *key_begin = (char*) region_alloc(&fiber()->gc, key_len);
+	char *key_begin = (char*) region_alloc(region, key_len);
 	if (key_begin == NULL) {
 		diag_set(OutOfMemory, key_len, "region_alloc", "key_begin");
 		return -1;
@@ -591,54 +594,44 @@ int tarantoolsqlRenameTrigger(const char *trig_name,
 	if (box_index_get(BOX_TRIGGER_ID, 0, key_begin, key, &tuple) != 0)
 		return -1;
 	assert(tuple != NULL);
-	assert(tuple_field_count(tuple) == 3);
-	const char *field = tuple_field(tuple, BOX_TRIGGER_FIELD_SPACE_ID);
-	assert(mp_typeof(*field) == MP_UINT);
-	uint32_t space_id = mp_decode_uint(&field);
-	field = tuple_field(tuple, BOX_TRIGGER_FIELD_OPTS);
-	assert(mp_typeof(*field) == MP_MAP);
-	mp_decode_map(&field);
-	const char *sql_str = mp_decode_str(&field, &key_len);
-	if (sqlStrNICmp(sql_str, "sql", 3) != 0) {
-		diag_set(ClientError, ER_SQL_EXECUTE, "can't modify name of "\
-			 "space created not via SQL facilities");
-		return -1;
-	}
-	uint32_t trigger_stmt_len;
-	const char *trigger_stmt_old = mp_decode_str(&field, &trigger_stmt_len);
-	char *trigger_stmt = (char*)region_alloc(&fiber()->gc,
-						 trigger_stmt_len + 1);
-	if (trigger_stmt == NULL) {
-		diag_set(OutOfMemory, trigger_stmt_len + 1, "region_alloc",
+	assert(tuple_field_count(tuple) == 8);
+
+	uint32_t code_str_len;
+	const char *code_str = tuple_field_str(tuple, BOX_TRIGGER_FIELD_CODE,
+					       &code_str_len);
+	assert(code_str != NULL);
+	char *old_stmt = (char *) region_alloc(region, code_str_len + 1);
+	if (old_stmt == NULL) {
+		diag_set(OutOfMemory, code_str_len + 1, "region_alloc",
 			 "trigger_stmt");
 		return -1;
 	}
-	memcpy(trigger_stmt, trigger_stmt_old, trigger_stmt_len);
-	trigger_stmt[trigger_stmt_len] = '\0';
+	memcpy(old_stmt, code_str, code_str_len);
+	old_stmt[code_str_len] = '\0';
+
 	bool is_quoted = false;
-	trigger_stmt = rename_trigger(db, trigger_stmt, new_table_name, &is_quoted);
-
-	uint32_t trigger_stmt_new_len = trigger_stmt_len + new_table_name_len -
-					old_table_name_len + 2 * (!is_quoted);
-	assert(trigger_stmt_new_len > 0);
-	key_len = mp_sizeof_array(3) + mp_sizeof_str(trig_name_len) +
-		  mp_sizeof_map(1) + mp_sizeof_str(3) +
-		  mp_sizeof_str(trigger_stmt_new_len) +
-		  mp_sizeof_uint(space_id);
-	char *new_tuple = (char*)region_alloc(&fiber()->gc, key_len);
+	char *stmt = rename_trigger(db, old_stmt, new_table_name, &is_quoted);
+	uint32_t stmt_len = code_str_len + new_table_name_len -
+			    old_table_name_len + 2 * (!is_quoted);
+	uint32_t prefix_sz = code_str - tuple_data(tuple) -
+			     mp_sizeof_strl(code_str_len);
+	uint32_t new_tuple_sz = prefix_sz + mp_sizeof_str(stmt_len);
+	char *new_tuple = (char *) region_alloc(region, new_tuple_sz);
 	if (new_tuple == NULL) {
-		diag_set(OutOfMemory, key_len, "region_alloc", "new_tuple");
+		sqlDbFree(db, stmt);
+		region_truncate(region, region_svp);
+		diag_set(OutOfMemory, new_tuple_sz, "region_alloc",
+			 "new_tuple");
 		return -1;
 	}
-	char *new_tuple_end = mp_encode_array(new_tuple, 3);
-	new_tuple_end = mp_encode_str(new_tuple_end, trig_name, trig_name_len);
-	new_tuple_end = mp_encode_uint(new_tuple_end, space_id);
-	new_tuple_end = mp_encode_map(new_tuple_end, 1);
-	new_tuple_end = mp_encode_str(new_tuple_end, "sql", 3);
-	new_tuple_end = mp_encode_str(new_tuple_end, trigger_stmt,
-				      trigger_stmt_new_len);
+	memcpy(new_tuple, tuple_data(tuple), prefix_sz);
+	char *new_tuple_end = mp_encode_str(new_tuple + prefix_sz,
+					    stmt, stmt_len);
+	sqlDbFree(db, stmt);
 
-	return box_replace(BOX_TRIGGER_ID, new_tuple, new_tuple_end, NULL);
+	int rc = box_replace(BOX_TRIGGER_ID, new_tuple, new_tuple_end, NULL);
+	region_truncate(region, region_svp);
+	return rc;
 }
 
 int
diff --git a/src/box/sql.h b/src/box/sql.h
index 926f4ba19..8693c249d 100644
--- a/src/box/sql.h
+++ b/src/box/sql.h
@@ -139,55 +139,8 @@ sql_trigger_new(struct trigger_def *def, struct sql_trigger_expr *expr,
  * @retval NULL on error
  * @retval not NULL sql_trigger AST pointer on success.
  */
-struct sql_trigger *
-sql_trigger_compile(struct sql *db, const char *sql);
-
-/**
- * Free AST pointed by trigger.
- * @param trigger AST object.
- */
-void
-sql_trigger_delete(struct sql_trigger *trigger);
-
-/**
- * Get server triggers list by space_id.
- * @param space_id valid Space ID.
- *
- * @retval trigger AST list.
- */
-struct sql_trigger *
-space_trigger_list(uint32_t space_id);
-
-/**
- * Perform replace trigger in SQL internals with new AST object.
- * @param name a name of the trigger.
- * @param space_id of the space to insert trigger.
- * @param trigger AST object to insert.
- * @param[out] old_trigger Old object if exists.
- *
- * @retval 0 on success.
- * @retval -1 on error.
- */
-int
-sql_trigger_replace(const char *name, uint32_t space_id,
-		    struct sql_trigger *trigger,
-		    struct sql_trigger **old_trigger);
-
-/**
- * Get trigger name by trigger AST object.
- * @param trigger AST object.
- * @return trigger name string.
- */
-const char *
-sql_trigger_name(struct sql_trigger *trigger);
-
-/**
- * Get space_id of the space that trigger has been built for.
- * @param trigger AST object.
- * @return space identifier.
- */
-uint32_t
-sql_trigger_space_id(struct sql_trigger *trigger);
+struct sql_trigger_expr *
+sql_trigger_expr_compile(struct sql *db, const char *sql);
 
 /**
  * Store duplicate of a parsed expression into @a parser.
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 8a6c8cb6c..e27a64019 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -1330,26 +1330,33 @@ sql_create_trigger(struct Parse *parse_context)
 	assert(_trigger != NULL);
 
 	int first_col = parse_context->nMem + 1;
-	parse_context->nMem += 3;
+	parse_context->nMem += 8;
 	int record = ++parse_context->nMem;
 
-	uint32_t opts_buff_sz = mp_sizeof_map(1) +
-				mp_sizeof_str(strlen("sql")) +
-				mp_sizeof_str(trigger_def->sql_len);
-	char *opts_buff = (char *) sqlDbMallocRaw(db, opts_buff_sz);
-	if (opts_buff == NULL) {
-		parse_context->is_aborted = true;
-		goto end;
-	}
-
-	char *data = mp_encode_map(opts_buff, 1);
-	data = mp_encode_str(data, "sql", strlen("sql"));
-	data = mp_encode_str(data, trigger_def->sql, trigger_def->sql_len);
 	sqlVdbeAddOp4(v, OP_String8, 0, first_col, 0, trigger_name, P4_DYNAMIC);
 	sqlVdbeAddOp2(v, OP_Integer, space_id, first_col + 1);
-	sqlVdbeAddOp4(v, OP_Blob, opts_buff_sz, first_col + 2,
+	char *opts_buff = (char *) sqlDbMallocRaw(db, mp_sizeof_map(1));
+	if (opts_buff == NULL) {
+		parse_context->is_aborted = true;
+		goto end;
+	}
+	mp_encode_map(opts_buff, 0);
+	sqlVdbeAddOp4(v, OP_Blob, mp_sizeof_map(1), first_col + 2,
 		      SQL_SUBTYPE_MSGPACK, opts_buff, P4_DYNAMIC);
-	sqlVdbeAddOp3(v, OP_MakeRecord, first_col, 3, record);
+	sqlVdbeAddOp4(v, OP_String8, 0, first_col + 3, 0,
+		      trigger_language_strs[TRIGGER_LANGUAGE_SQL], P4_STATIC);
+	sqlVdbeAddOp4(v, OP_String8, 0, first_col + 4, 0,
+		      trigger_type_strs[TRIGGER_TYPE_REPLACE], P4_STATIC);
+	sqlVdbeAddOp4(v, OP_String8, 0, first_col + 5, 0,
+		trigger_event_manipulation_strs[trigger_def->event_manipulation],
+		P4_STATIC);
+	sqlVdbeAddOp4(v, OP_String8, 0, first_col + 6, 0,
+		trigger_action_timing_strs[trigger_def->action_timing],
+		P4_STATIC);
+	sqlVdbeAddOp4(v, OP_String8, 0, first_col + 7, 0,
+		sqlDbStrNDup(db, trigger_def->sql, trigger_def->sql_len),
+		P4_DYNAMIC);
+	sqlVdbeAddOp3(v, OP_MakeRecord, first_col, 8, record);
 	sqlVdbeAddOp4(v, OP_IdxInsert, record, 0, 0, (char *)_trigger,
 		      P4_SPACEPTR);
 	sqlVdbeChangeP5(v, OPFLAG_NCHANGE);
diff --git a/src/box/sql/fk_constraint.c b/src/box/sql/fk_constraint.c
index 65c13f453..a3db6489a 100644
--- a/src/box/sql/fk_constraint.c
+++ b/src/box/sql/fk_constraint.c
@@ -735,7 +735,7 @@ sql_trigger_for_fk_constrint_new(struct Parse *parser,
 		trigger_def_new(trigger_name, strlen(trigger_name),
 				parent_space_id, TRIGGER_LANGUAGE_SQL,
 				TRIGGER_EVENT_MANIPULATION_UPDATE,
-				TRIGGER_ACTION_TIMING_BEFORE);
+				TRIGGER_ACTION_TIMING_BEFORE, NULL, 0);
 	if (def == NULL) {
 		sql_trigger_expr_delete(db, expr);
 		goto halt;
@@ -925,7 +925,8 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def,
 	sql_select_delete(db, select);
 
 	if (db->mallocFailed || trigger == NULL) {
-		sql_trigger_delete(trigger);
+		if (trigger != NULL)
+			trigger_delete((struct trigger *)trigger);
 		return NULL;
 	}
 
diff --git a/src/box/sql/prepare.c b/src/box/sql/prepare.c
index 84a265f96..b5aa87849 100644
--- a/src/box/sql/prepare.c
+++ b/src/box/sql/prepare.c
@@ -268,8 +268,11 @@ sql_parser_destroy(Parse *parser)
 	case AST_TYPE_EXPR:
 		sql_expr_delete(db, parser->parsed_ast.expr, false);
 		break;
-	case AST_TYPE_TRIGGER:
-		sql_trigger_delete(parser->parsed_ast.trigger);
+	case AST_TYPE_TRIGGER_EXPR:
+		if (parser->parsed_ast.trigger_expr != NULL) {
+			sql_trigger_expr_delete(db,
+					parser->parsed_ast.trigger_expr);
+		}
 		break;
 	default:
 		assert(parser->parsed_ast_type == AST_TYPE_UNDEFINED);
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 180997ce4..8d1588c7f 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -2091,7 +2091,7 @@ enum ast_type {
 	AST_TYPE_UNDEFINED = 0,
 	AST_TYPE_SELECT,
 	AST_TYPE_EXPR,
-	AST_TYPE_TRIGGER,
+	AST_TYPE_TRIGGER_EXPR,
 	ast_type_MAX
 };
 
@@ -2225,7 +2225,7 @@ struct Parse {
 	union {
 		struct Expr *expr;
 		struct Select *select;
-		struct sql_trigger *trigger;
+		struct sql_trigger_expr *trigger_expr;
 	} parsed_ast;
 };
 
diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c
index 9fa069d09..089e8eeff 100644
--- a/src/box/sql/tokenize.c
+++ b/src/box/sql/tokenize.c
@@ -466,7 +466,7 @@ sqlRunParser(Parse * pParse, const char *zSql)
 		return -1;
 	}
 	assert(pParse->create_table_def.new_space == NULL);
-	assert(pParse->parsed_ast.trigger == NULL);
+	assert(pParse->parsed_ast.trigger_expr == NULL);
 	assert(pParse->nVar == 0);
 	assert(pParse->pVList == 0);
 	while (1) {
@@ -578,19 +578,19 @@ sql_view_compile(struct sql *db, const char *view_stmt)
 	return select;
 }
 
-struct sql_trigger *
-sql_trigger_compile(struct sql *db, const char *sql)
+struct sql_trigger_expr *
+sql_trigger_expr_compile(struct sql *db, const char *sql)
 {
 	struct Parse parser;
 	sql_parser_create(&parser, db, default_flags);
 	parser.parse_only = true;
-	struct sql_trigger *trigger = NULL;
+	struct sql_trigger_expr *trigger_expr = NULL;
 	if (sqlRunParser(&parser, sql) == 0 &&
-	    parser.parsed_ast_type == AST_TYPE_TRIGGER) {
-		trigger = parser.parsed_ast.trigger;
-		parser.parsed_ast.trigger = NULL;
+	    parser.parsed_ast_type == AST_TYPE_TRIGGER_EXPR) {
+		trigger_expr = parser.parsed_ast.trigger_expr;
+		parser.parsed_ast.trigger_expr = NULL;
 	}
 
 	sql_parser_destroy(&parser);
-	return trigger;
+	return trigger_expr;
 }
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index 2efdff3be..388fc64f3 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -73,61 +73,23 @@ sql_store_trigger(struct Parse *parse)
 	assert(alter_def->entity_type == ENTITY_TYPE_TRIGGER);
 	assert(alter_def->alter_action == ALTER_ACTION_CREATE);
 
-	struct region *region = &parse->region;
-	size_t region_svp = region_used(region);
 	assert(alter_def->entity_name->nSrc == 1);
 	assert(create_def->name.n > 0);
-	char *trigger_name =
-		sql_normalized_name_region_new(region, create_def->name.z,
-					       create_def->name.n);
-	if (trigger_name == NULL)
-		goto set_tarantool_error_and_cleanup;
-	if (sqlCheckIdentifierName(parse, trigger_name) != 0)
-		goto set_tarantool_error_and_cleanup;
-	uint32_t trigger_name_len = region_used(region) - region_svp;
+	sqlSrcListDelete(db, alter_def->entity_name);
 
-	const char *table_name = alter_def->entity_name->a[0].zName;
-	uint32_t space_id = box_space_id_by_name(table_name,
-						 strlen(table_name));
-	if (space_id == BOX_ID_NIL) {
-		diag_set(ClientError, ER_NO_SUCH_SPACE, table_name);
-		goto set_tarantool_error_and_cleanup;
-	}
-
-	/* Construct a trigger object. */
-	struct trigger_def *def =
-		trigger_def_new(trigger_name, trigger_name_len,
-				space_id, TRIGGER_LANGUAGE_SQL,
-				trigger_def->event_manipulation,
-				trigger_def->action_timing);
-	if (def == NULL)
-		goto set_tarantool_error_and_cleanup;
-	struct sql_trigger_expr *expr =
+	assert(parse->parsed_ast.trigger_expr == NULL);
+	parse->parsed_ast_type = AST_TYPE_TRIGGER_EXPR;
+	parse->parsed_ast.trigger_expr =
 		sql_trigger_expr_new(db, trigger_def->cols, trigger_def->when,
 				     trigger_def->step_list);
-	if (expr == NULL) {
-		trigger_def_delete(def);
+	if (parse->parsed_ast.trigger_expr == NULL)
 		goto set_tarantool_error_and_cleanup;
-	}
-	assert(parse->parsed_ast.trigger == NULL);
-	parse->parsed_ast_type = AST_TYPE_TRIGGER;
-	parse->parsed_ast.trigger = sql_trigger_new(def, expr, false);
-	if (parse->parsed_ast.trigger == NULL) {
-		sql_trigger_expr_delete(db, expr);
-		trigger_def_delete(def);
-		goto set_tarantool_error_and_cleanup;
-	}
-
-trigger_cleanup:
-	region_truncate(region, region_svp);
-	sqlSrcListDelete(db, alter_def->entity_name);
 	return;
 set_tarantool_error_and_cleanup:
 	parse->is_aborted = true;
 	sqlIdListDelete(db, trigger_def->cols);
 	sql_expr_delete(db, trigger_def->when, false);
 	sqlDeleteTriggerStep(db, trigger_def->step_list);
-	goto trigger_cleanup;
 }
 
 struct TriggerStep *
@@ -279,6 +241,8 @@ sql_trigger_expr_delete(struct sql *db, struct sql_trigger_expr *trigger_expr)
 	free(trigger_expr);
 }
 
+static struct trigger_vtab sql_trigger_vtab;
+
 struct sql_trigger *
 sql_trigger_new(struct trigger_def *def, struct sql_trigger_expr *expr,
 		bool is_fk_constraint_trigger)
@@ -288,23 +252,36 @@ sql_trigger_new(struct trigger_def *def, struct sql_trigger_expr *expr,
 		diag_set(OutOfMemory, sizeof(*trigger), "malloc", "trigger");
 		return NULL;
 	}
+	if (expr == NULL) {
+		assert(def->code != NULL);
+		expr = sql_trigger_expr_compile(sql_get(), def->code);
+		if (expr == NULL) {
+			free(trigger);
+			return NULL;
+		}
+	}
 	rlist_create(&trigger->base.link);
 	trigger->base.def = def;
+	trigger->base.vtab = &sql_trigger_vtab;
 	trigger->expr = expr;
 	trigger->is_fk_constraint_trigger = is_fk_constraint_trigger;
 	return trigger;
 }
 
-void
-sql_trigger_delete(struct sql_trigger *trigger)
+static void
+sql_trigger_delete(struct trigger *base)
 {
-	if (trigger == NULL)
-		return;
-	trigger_def_delete(trigger->base.def);
+	assert(base->vtab == &sql_trigger_vtab);
+	assert(base != NULL && base->def->language == TRIGGER_LANGUAGE_SQL);
+	struct sql_trigger *trigger = (struct sql_trigger *) base;
 	sql_trigger_expr_delete(sql_get(), trigger->expr);
 	free(trigger);
 }
 
+static struct trigger_vtab sql_trigger_vtab = {
+	.destroy = sql_trigger_delete,
+};
+
 void
 vdbe_code_drop_trigger(struct Parse *parser, const char *trigger_name,
 		       bool account_changes)
@@ -367,82 +344,6 @@ sql_drop_trigger(struct Parse *parser)
 	sqlSrcListDelete(db, name);
 }
 
-int
-sql_trigger_replace(const char *name, uint32_t space_id,
-		    struct sql_trigger *trigger, struct sql_trigger **old_trigger)
-{
-	assert(trigger == NULL || strcmp(name, trigger->base.def->name) == 0);
-
-	struct space *space = space_cache_find(space_id);
-	assert(space != NULL);
-	*old_trigger = NULL;
-
-	if (trigger != NULL) {
-		/* Do not create a trigger on a system space. */
-		if (space_is_system(space)) {
-			diag_set(ClientError, ER_SQL_EXECUTE,
-				 "cannot create trigger on system table");
-			return -1;
-		}
-		/*
-		 * INSTEAD of triggers are only for views and
-		 * views only support INSTEAD of triggers.
-		 */
-		if (space->def->opts.is_view &&
-		    trigger->base.def->action_timing !=
-		    TRIGGER_ACTION_TIMING_INSTEAD) {
-			const char *err_msg =
-				tt_sprintf("cannot create %s trigger on "
-					   "view: %s",
-					    trigger_action_timing_strs[
-						trigger->base.def->
-						action_timing],
-					    space->def->name);
-			diag_set(ClientError, ER_SQL_EXECUTE, err_msg);
-			return -1;
-		}
-		if (!space->def->opts.is_view &&
-		    trigger->base.def->action_timing ==
-		    TRIGGER_ACTION_TIMING_INSTEAD) {
-			diag_set(ClientError, ER_SQL_EXECUTE,
-				 tt_sprintf("cannot create "\
-                         "INSTEAD OF trigger on space: %s", space->def->name));
-			return -1;
-		}
-
-		if (trigger->base.def->action_timing ==
-		    TRIGGER_ACTION_TIMING_INSTEAD) {
-			trigger->base.def->action_timing =
-				TRIGGER_ACTION_TIMING_BEFORE;
-		}
-	}
-
-	struct trigger *p;
-	rlist_foreach_entry(p, &space->trigger_list, link) {
-		if (strcmp(p->def->name, name) != 0)
-			continue;
-		*old_trigger = (struct sql_trigger *)p;
-		rlist_del(&p->link);
-		break;
-	}
-
-	if (trigger != NULL)
-		rlist_add(&space->trigger_list, &trigger->base.link);
-	return 0;
-}
-
-const char *
-sql_trigger_name(struct sql_trigger *trigger)
-{
-	return trigger->base.def->name;
-}
-
-uint32_t
-sql_trigger_space_id(struct sql_trigger *trigger)
-{
-	return trigger->base.def->space_id;
-}
-
 /*
  * pEList is the SET clause of an UPDATE statement.  Each entry
  * in pEList is of the format <id>=<expr>.  If any of the entries
diff --git a/src/box/trigger.c b/src/box/trigger.c
new file mode 100644
index 000000000..15a810d5b
--- /dev/null
+++ b/src/box/trigger.c
@@ -0,0 +1,73 @@
+/**
+ * Copyright 2010-2019, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "trigger.h"
+
+#include <stdbool.h>
+#include "space.h"
+#include "sql.h"
+#include "trigger_def.h"
+#include "trivia/util.h"
+
+struct trigger *
+trigger_new(struct trigger_def *def)
+{
+	struct trigger *trigger = NULL;
+	switch (def->language) {
+	case TRIGGER_LANGUAGE_SQL: {
+		trigger = (struct trigger *) sql_trigger_new(def, NULL, false);
+		break;
+	}
+	default: {
+		unreachable();
+	}
+	}
+	return trigger;
+}
+
+void
+trigger_delete(struct trigger *base)
+{
+	struct trigger_def *def = base->def;
+	base->vtab->destroy(base);
+	trigger_def_delete(def);
+}
+
+struct trigger *
+space_trigger_by_name(struct space *space, const char *name, uint32_t name_len)
+{
+	struct trigger *trigger = NULL;
+	rlist_foreach_entry(trigger, &space->trigger_list, link) {
+		if (trigger->def->name_len == name_len &&
+		    memcmp(trigger->def->name, name, name_len) == 0)
+			return trigger;
+	}
+	return NULL;
+}
diff --git a/src/box/trigger.h b/src/box/trigger.h
index 9b5853fe4..dbfa92055 100644
--- a/src/box/trigger.h
+++ b/src/box/trigger.h
@@ -35,16 +35,27 @@
 extern "C" {
 #endif /* defined(__cplusplus) */
 
+#include <inttypes.h>
 #include "small/rlist.h"
 
+struct space;
+struct trigger;
 struct trigger_def;
 
+/** Virtual method table for trigger object. */
+struct trigger_vtab {
+	/** Release implementation-specific trigger context. */
+	void (*destroy)(struct trigger *func);
+};
+
 /**
  * Structure representing trigger.
  */
 struct trigger {
 	/** The trigger definition. */
 	struct trigger_def *def;
+	/** Virtual method table. */
+	const struct trigger_vtab *vtab;
 	/**
 	 * Organize sql_trigger structs into linked list
 	 * with space::trigger_list.
@@ -52,6 +63,16 @@ struct trigger {
 	struct rlist link;
 };
 
+struct trigger *
+trigger_new(struct trigger_def *def);
+
+void
+trigger_delete(struct trigger *trigger);
+
+/** Find trigger object in space by given name and name_len. */
+struct trigger *
+space_trigger_by_name(struct space *space, const char *name, uint32_t name_len);
+
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
diff --git a/src/box/trigger_def.c b/src/box/trigger_def.c
index 83e929662..8f3feec5f 100644
--- a/src/box/trigger_def.c
+++ b/src/box/trigger_def.c
@@ -30,6 +30,10 @@
  */
 #include "trigger_def.h"
 #include "diag.h"
+#include "errcode.h"
+#include "schema.h"
+#include "space.h"
+#include "tt_static.h"
 
 const char *trigger_event_manipulation_strs[] = {"DELETE", "UPDATE", "INSERT"};
 
@@ -37,13 +41,18 @@ const char *trigger_action_timing_strs[] = {"BEFORE", "AFTER", "INSTEAD"};
 
 const char *trigger_language_strs[] = {"SQL"};
 
+const char *trigger_type_strs[] = {"REPLACE"};
+
 struct trigger_def *
 trigger_def_new(const char *name, uint32_t name_len, uint32_t space_id,
 		enum trigger_language language,
 		enum trigger_event_manipulation event_manipulation,
-		enum trigger_action_timing action_timing)
+		enum trigger_action_timing action_timing, const char *code,
+		uint32_t code_len)
 {
-	uint32_t trigger_def_sz = trigger_def_sizeof(name_len);
+	uint32_t code_offset;
+	uint32_t trigger_def_sz = trigger_def_sizeof(name_len, code_len,
+						     &code_offset);
 	struct trigger_def *trigger_def =
 		(struct trigger_def *) malloc(trigger_def_sz);
 	if (trigger_def == NULL) {
@@ -56,6 +65,14 @@ trigger_def_new(const char *name, uint32_t name_len, uint32_t space_id,
 	trigger_def->action_timing = action_timing;
 	memcpy(trigger_def->name, name, name_len);
 	trigger_def->name[name_len] = '\0';
+	trigger_def->name_len = name_len;
+	if (code_len != 0) {
+		trigger_def->code = (char *) trigger_def + code_offset;
+		memcpy(trigger_def->code, code, code_len);
+		trigger_def->code[code_len] = '\0';
+	} else {
+		trigger_def->code = NULL;
+	}
 	return trigger_def;
 }
 
@@ -64,3 +81,51 @@ trigger_def_delete(struct trigger_def *trigger_def)
 {
 	free(trigger_def);
 }
+
+int
+trigger_def_check(struct trigger_def *def)
+{
+	struct space *space = space_by_id(def->space_id);
+	if (space == NULL) {
+		diag_set(ClientError, ER_CREATE_TRIGGER, def->name,
+			 tt_sprintf("Space '%s' does not exist",
+				    int2str(def->space_id)));
+		return -1;
+	}
+	if (space_is_system(space)) {
+		diag_set(ClientError, ER_CREATE_TRIGGER, def->name,
+			 "cannot create trigger on system space");
+		return -1;
+	}
+	switch (def->language) {
+	case TRIGGER_LANGUAGE_SQL: {
+		if (space->def->opts.is_view &&
+		    def->action_timing != TRIGGER_ACTION_TIMING_INSTEAD) {
+			const char *err_msg =
+				tt_sprintf("cannot create %s trigger on "
+					   "view: %s",
+					   trigger_action_timing_strs[
+						   def->action_timing],
+					   space->def->name);
+			diag_set(ClientError, ER_SQL_EXECUTE, err_msg);
+			return -1;
+		}
+		if (!space->def->opts.is_view &&
+		    def->action_timing == TRIGGER_ACTION_TIMING_INSTEAD) {
+			diag_set(ClientError, ER_SQL_EXECUTE,
+				tt_sprintf("cannot create INSTEAD OF trigger "
+					   "on space: %s", space->def->name));
+			return -1;
+		}
+		break;
+	}
+	default: {
+		/*
+		 * Only SQL triggers could define INSTEAD OF
+		 * ACTION TIMING.
+		 * */
+		unreachable();
+	}
+	}
+	return 0;
+}
diff --git a/src/box/trigger_def.h b/src/box/trigger_def.h
index 98df3a3de..68e2c3afd 100644
--- a/src/box/trigger_def.h
+++ b/src/box/trigger_def.h
@@ -79,6 +79,13 @@ enum trigger_language {
 
 extern const char *trigger_language_strs[];
 
+enum trigger_type {
+	TRIGGER_TYPE_REPLACE,
+	trigger_type_MAX,
+};
+
+extern const char *trigger_type_strs[];
+
 /**
  * Trigger object definition.
  * See trigger_def_sizeof() definition for implementation
@@ -102,6 +109,13 @@ struct trigger_def {
 	uint32_t space_id;
 	/** The language of the stored trigger. */
 	enum trigger_language language;
+	/**
+	 * The language-dependent code defining trigger
+	 * operations.
+	 */
+	char *code;
+	/** The length of the trigger name. */
+	uint32_t name_len;
 	/**
 	 * The 0-terminated string, a name of the check
 	 * constraint. Must be unique for a given space.
@@ -128,9 +142,14 @@ struct trigger_def {
  *         given parameters.
  */
 static inline uint32_t
-trigger_def_sizeof(uint32_t name_len)
+trigger_def_sizeof(uint32_t name_len, uint32_t code_len,
+		   uint32_t *code_offset)
 {
-	return sizeof(struct trigger_def) + name_len + 1;
+	uint32_t sz = sizeof(struct trigger_def) + name_len + 1;
+	*code_offset = sz;
+	if (code_len != 0)
+		sz += code_len + 1;
+	return sz;
 }
 
 /**
@@ -145,6 +164,8 @@ trigger_def_sizeof(uint32_t name_len)
  *                           trigger activates.
  * @param action_timing Whether the trigger activates before or
  *                      after the triggering event.
+ * @param code The trigger program function code string.
+ * @param code_len The length of the @a code string.
  * @retval not NULL Trigger definition object pointer on success.
  * @retval NULL Otherwise. The diag message is set.
 */
@@ -152,7 +173,8 @@ struct trigger_def *
 trigger_def_new(const char *name, uint32_t name_len, uint32_t space_id,
 		enum trigger_language language,
 		enum trigger_event_manipulation event_manipulation,
-		enum trigger_action_timing action_timing);
+		enum trigger_action_timing action_timing, const char *code,
+		uint32_t code_len);
 
 /**
  * Destroy trigger definition memory, release acquired resources.
@@ -161,6 +183,10 @@ trigger_def_new(const char *name, uint32_t name_len, uint32_t space_id,
 void
 trigger_def_delete(struct trigger_def *trigger_def);
 
+/** Check if a non-empty trigger body is correct. */
+int
+trigger_def_check(struct trigger_def *def);
+
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
diff --git a/test/box-py/bootstrap.result b/test/box-py/bootstrap.result
index a59979e62..cc9bdfda2 100644
--- a/test/box-py/bootstrap.result
+++ b/test/box-py/bootstrap.result
 ...
 session = nil
 ---
diff --git a/test/box/misc.result b/test/box/misc.result
index 33e5ce886..3fa9ec351 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -533,6 +533,7 @@ t;
   201: box.error.NO_SUCH_FIELD_NAME
   202: box.error.FUNC_WRONG_ARG_COUNT
   203: box.error.BOOTSTRAP_READONLY
+  204: box.error.CREATE_TRIGGER
 ...
 test_run:cmd("setopt delimiter ''");
 ---
diff --git a/test/sql/ddl.result b/test/sql/ddl.result
index 28acf37ea..5f15f9f72 100644
--- a/test/sql/ddl.result
+++ b/test/sql/ddl.result
@@ -208,7 +208,7 @@ box.execute([[CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW
 
 box.begin()
 box.execute('ALTER TABLE t1 RENAME TO t1_new;')
-sql = _trigger_index:select(box.space.T1_NEW.id)[1].opts.sql
+sql = _trigger_index:select(box.space.T1_NEW.id)[1].code
 assert(sql:find('T1_NEW'))
 box.rollback()$
  | ---
@@ -218,7 +218,7 @@ test_run:cmd("setopt delimiter ''")$
  | - true
  | ...
 
-sql = _trigger_index:select(box.space.T1.id)[1].opts.sql
+sql = _trigger_index:select(box.space.T1.id)[1].code
  | ---
  | ...
 not sql:find('T1_NEW') and sql:find('t1') ~= nil
@@ -230,7 +230,7 @@ box.execute('ALTER TABLE t1 RENAME TO t1_new;')
  | ---
  | - row_count: 0
  | ...
-sql = _trigger_index:select(box.space.T1_NEW.id)[1].opts.sql
+sql = _trigger_index:select(box.space.T1_NEW.id)[1].code
  | ---
  | ...
 sql:find('T1_NEW') ~= nil
diff --git a/test/sql/ddl.test.lua b/test/sql/ddl.test.lua
index 6067b6192..aeb733366 100644
--- a/test/sql/ddl.test.lua
+++ b/test/sql/ddl.test.lua
@@ -98,16 +98,16 @@ box.execute([[CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW
 
 box.begin()
 box.execute('ALTER TABLE t1 RENAME TO t1_new;')
-sql = _trigger_index:select(box.space.T1_NEW.id)[1].opts.sql
+sql = _trigger_index:select(box.space.T1_NEW.id)[1].code
 assert(sql:find('T1_NEW'))
 box.rollback()$
 test_run:cmd("setopt delimiter ''")$
 
-sql = _trigger_index:select(box.space.T1.id)[1].opts.sql
+sql = _trigger_index:select(box.space.T1.id)[1].code
 not sql:find('T1_NEW') and sql:find('t1') ~= nil
 
 box.execute('ALTER TABLE t1 RENAME TO t1_new;')
-sql = _trigger_index:select(box.space.T1_NEW.id)[1].opts.sql
+sql = _trigger_index:select(box.space.T1_NEW.id)[1].code
 sql:find('T1_NEW') ~= nil
 
 box.execute('DROP TABLE t1_new')
diff --git a/test/sql/gh2141-delete-trigger-drop-table.result b/test/sql/gh2141-delete-trigger-drop-table.result
index 1d373f57e..961eb7a8f 100644
--- a/test/sql/gh2141-delete-trigger-drop-table.result
+++ b/test/sql/gh2141-delete-trigger-drop-table.result
@@ -38,26 +38,26 @@ box.execute("CREATE TRIGGER tt_ad AFTER DELETE ON t FOR EACH ROW BEGIN SELECT 1;
 - row_count: 1
 ...
 -- check that these triggers exist
-box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
+box.execute("SELECT \"name\", \"code\" FROM \"_trigger\"")
 ---
 - metadata:
   - name: name
     type: string
-  - name: opts
-    type: map
+  - name: code
+    type: string
   rows:
-  - ['TT_AD', {'sql': 'CREATE TRIGGER tt_ad AFTER DELETE ON t FOR EACH ROW BEGIN SELECT
-        1; END'}]
-  - ['TT_AI', {'sql': 'CREATE TRIGGER tt_ai AFTER INSERT ON t FOR EACH ROW BEGIN SELECT
-        1; END'}]
-  - ['TT_AU', {'sql': 'CREATE TRIGGER tt_au AFTER UPDATE ON t FOR EACH ROW BEGIN SELECT
-        1; END'}]
-  - ['TT_BD', {'sql': 'CREATE TRIGGER tt_bd BEFORE DELETE ON t FOR EACH ROW BEGIN
-        SELECT 1; END'}]
-  - ['TT_BI', {'sql': 'CREATE TRIGGER tt_bi BEFORE INSERT ON t FOR EACH ROW BEGIN
-        SELECT 1; END'}]
-  - ['TT_BU', {'sql': 'CREATE TRIGGER tt_bu BEFORE UPDATE ON t FOR EACH ROW BEGIN
-        SELECT 1; END'}]
+  - ['TT_AD', 'CREATE TRIGGER tt_ad AFTER DELETE ON t FOR EACH ROW BEGIN SELECT 1;
+      END']
+  - ['TT_AI', 'CREATE TRIGGER tt_ai AFTER INSERT ON t FOR EACH ROW BEGIN SELECT 1;
+      END']
+  - ['TT_AU', 'CREATE TRIGGER tt_au AFTER UPDATE ON t FOR EACH ROW BEGIN SELECT 1;
+      END']
+  - ['TT_BD', 'CREATE TRIGGER tt_bd BEFORE DELETE ON t FOR EACH ROW BEGIN SELECT 1;
+      END']
+  - ['TT_BI', 'CREATE TRIGGER tt_bi BEFORE INSERT ON t FOR EACH ROW BEGIN SELECT 1;
+      END']
+  - ['TT_BU', 'CREATE TRIGGER tt_bu BEFORE UPDATE ON t FOR EACH ROW BEGIN SELECT 1;
+      END']
 ...
 -- drop table
 box.execute("DROP TABLE t")
@@ -65,12 +65,12 @@ box.execute("DROP TABLE t")
 - row_count: 1
 ...
 -- check that triggers were dropped with deleted table
-box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
+box.execute("SELECT \"name\", \"code\" FROM \"_trigger\"")
 ---
 - metadata:
   - name: name
     type: string
-  - name: opts
-    type: map
+  - name: code
+    type: string
   rows: []
 ...
diff --git a/test/sql/gh2141-delete-trigger-drop-table.test.lua b/test/sql/gh2141-delete-trigger-drop-table.test.lua
index 4d21fd7e0..240ec67a2 100644
--- a/test/sql/gh2141-delete-trigger-drop-table.test.lua
+++ b/test/sql/gh2141-delete-trigger-drop-table.test.lua
@@ -13,10 +13,10 @@ box.execute("CREATE TRIGGER tt_bd BEFORE DELETE ON t FOR EACH ROW BEGIN SELECT 1
 box.execute("CREATE TRIGGER tt_ad AFTER DELETE ON t FOR EACH ROW BEGIN SELECT 1; END")
 
 -- check that these triggers exist
-box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
+box.execute("SELECT \"name\", \"code\" FROM \"_trigger\"")
 
 -- drop table
 box.execute("DROP TABLE t")
 
 -- check that triggers were dropped with deleted table
-box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
+box.execute("SELECT \"name\", \"code\" FROM \"_trigger\"")
diff --git a/test/sql/persistency.result b/test/sql/persistency.result
index f8f992c39..bce171eed 100644
--- a/test/sql/persistency.result
+++ b/test/sql/persistency.result
@@ -317,16 +317,16 @@ box.execute("CREATE TRIGGER tfoobar AFTER INSERT ON foobar FOR EACH ROW BEGIN IN
 ---
 - row_count: 1
 ...
-box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"");
+box.execute("SELECT \"name\", \"code\" FROM \"_trigger\"");
 ---
 - metadata:
   - name: name
     type: string
-  - name: opts
-    type: map
+  - name: code
+    type: string
   rows:
-  - ['TFOOBAR', {'sql': 'CREATE TRIGGER tfoobar AFTER INSERT ON foobar FOR EACH ROW
-        BEGIN INSERT INTO barfoo VALUES (''trigger test'', 9999); END'}]
+  - ['TFOOBAR', 'CREATE TRIGGER tfoobar AFTER INSERT ON foobar FOR EACH ROW BEGIN
+      INSERT INTO barfoo VALUES (''trigger test'', 9999); END']
 ...
 -- Many entries
 box.execute("CREATE TABLE t1(a INT,b INT,c INT,PRIMARY KEY(b,c));")
@@ -356,16 +356,16 @@ box.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
 ...
 test_run:cmd('restart server default');
 -- prove that trigger survived
-box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"");
+box.execute("SELECT \"name\", \"code\" FROM \"_trigger\"");
 ---
 - metadata:
   - name: name
     type: string
-  - name: opts
-    type: map
+  - name: code
+    type: string
   rows:
-  - ['TFOOBAR', {'sql': 'CREATE TRIGGER tfoobar AFTER INSERT ON foobar FOR EACH ROW
-        BEGIN INSERT INTO barfoo VALUES (''trigger test'', 9999); END'}]
+  - ['TFOOBAR', 'CREATE TRIGGER tfoobar AFTER INSERT ON foobar FOR EACH ROW BEGIN
+      INSERT INTO barfoo VALUES (''trigger test'', 9999); END']
 ...
 -- ... functional
 box.execute("INSERT INTO foobar VALUES ('foobar trigger test', 8888)")
@@ -383,16 +383,16 @@ box.execute("SELECT * FROM barfoo WHERE foo = 9999");
   rows: []
 ...
 -- and still persistent
-box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
+box.execute("SELECT \"name\", \"code\" FROM \"_trigger\"")
 ---
 - metadata:
   - name: name
     type: string
-  - name: opts
-    type: map
+  - name: code
+    type: string
   rows:
-  - ['TFOOBAR', {'sql': 'CREATE TRIGGER tfoobar AFTER INSERT ON foobar FOR EACH ROW
-        BEGIN INSERT INTO barfoo VALUES (''trigger test'', 9999); END'}]
+  - ['TFOOBAR', 'CREATE TRIGGER tfoobar AFTER INSERT ON foobar FOR EACH ROW BEGIN
+      INSERT INTO barfoo VALUES (''trigger test'', 9999); END']
 ...
 -- and can be dropped just once
 box.execute("DROP TRIGGER tfoobar")
@@ -406,13 +406,13 @@ box.execute("DROP TRIGGER tfoobar")
 - Trigger 'TFOOBAR' doesn't exist
 ...
 -- Should be empty
-box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
+box.execute("SELECT \"name\", \"code\" FROM \"_trigger\"")
 ---
 - metadata:
   - name: name
     type: string
-  - name: opts
-    type: map
+  - name: code
+    type: string
   rows: []
 ...
 -- prove barfoo2 still exists
diff --git a/test/sql/persistency.test.lua b/test/sql/persistency.test.lua
index 196445349..035fa8eb9 100644
--- a/test/sql/persistency.test.lua
+++ b/test/sql/persistency.test.lua
@@ -51,7 +51,7 @@ box.execute("INSERT INTO barfoo VALUES ('foobar', 1000)")
 
 -- create a trigger
 box.execute("CREATE TRIGGER tfoobar AFTER INSERT ON foobar FOR EACH ROW BEGIN INSERT INTO barfoo VALUES ('trigger test', 9999); END")
-box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"");
+box.execute("SELECT \"name\", \"code\" FROM \"_trigger\"");
 
 -- Many entries
 box.execute("CREATE TABLE t1(a INT,b INT,c INT,PRIMARY KEY(b,c));")
@@ -61,21 +61,21 @@ box.execute("SELECT a FROM t1 ORDER BY b, a LIMIT 10 OFFSET 20;");
 test_run:cmd('restart server default');
 
 -- prove that trigger survived
-box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"");
+box.execute("SELECT \"name\", \"code\" FROM \"_trigger\"");
 
 -- ... functional
 box.execute("INSERT INTO foobar VALUES ('foobar trigger test', 8888)")
 box.execute("SELECT * FROM barfoo WHERE foo = 9999");
 
 -- and still persistent
-box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
+box.execute("SELECT \"name\", \"code\" FROM \"_trigger\"")
 
 -- and can be dropped just once
 box.execute("DROP TRIGGER tfoobar")
 -- Should error
 box.execute("DROP TRIGGER tfoobar")
 -- Should be empty
-box.execute("SELECT \"name\", \"opts\" FROM \"_trigger\"")
+box.execute("SELECT \"name\", \"code\" FROM \"_trigger\"")
 
 -- prove barfoo2 still exists
 box.execute("INSERT INTO barfoo VALUES ('xfoo', 1)")
diff --git a/test/sql/triggers.result b/test/sql/triggers.result
index 1a70ddf2b..eddc3a18a 100644
--- a/test/sql/triggers.result
+++ b/test/sql/triggers.result
@@ -11,8 +11,14 @@ box.execute('pragma sql_default_engine=\''..engine..'\'')
 ---
 - row_count: 0
 ...
+function setmap(tab) return setmetatable(tab, { __serialize = 'map' }) end
+---
+...
+MAP = setmap({})
+---
+...
 -- Get invariant part of the tuple; name and opts don't change.
- function immutable_part(data) local r = {} for i, l in pairs(data) do table.insert(r, {l.name, l.opts}) end return r end
+function immutable_part(data) local r = {} for i, l in pairs(data) do table.insert(r, {l.name, l.code}) end return r end
 ---
 ...
 --
@@ -33,43 +39,12 @@ box.execute([[CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT IN
 immutable_part(box.space._trigger:select())
 ---
 - - - T1T
-    - {'sql': 'CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(1); END'}
+    - CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(1);
+      END
 ...
 space_id = box.space._space.index["name"]:get('T1').id
 ---
 ...
--- Checks for LUA tuples.
-tuple = {"T1t", space_id, {sql = "CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(1); END;"}}
----
-...
-box.space._trigger:insert(tuple)
----
-- error: 'Failed to execute SQL statement: trigger name does not match extracted from
-    SQL'
-...
-tuple = {"T1t", space_id, {sql = "CREATE TRIGGER t12t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(1); END;"}}
----
-...
-box.space._trigger:insert(tuple)
----
-- error: 'Failed to execute SQL statement: trigger name does not match extracted from
-    SQL'
-...
-tuple = {"T2T", box.space.T1.id + 1, {sql = "CREATE TRIGGER t2t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(1); END;"}}
----
-...
-box.space._trigger:insert(tuple)
----
-- error: 'Failed to execute SQL statement: trigger space_id does not match the value
-    resolved on AST building from SQL'
-...
-immutable_part(box.space._trigger:select())
----
-- - - T1T
-    - {'sql': 'CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(1); END'}
-...
 box.execute("DROP TABLE T1;")
 ---
 - row_count: 1
@@ -89,8 +64,8 @@ box.execute([[CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT IN
 immutable_part(box.space._trigger:select())
 ---
 - - - T1T
-    - {'sql': 'CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(1); END'}
+    - CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(1);
+      END
 ...
 space_id = box.space._space.index["name"]:get('T1').id
 ---
@@ -113,13 +88,13 @@ box.execute("DELETE FROM t2;")
 - row_count: 1
 ...
 -- Test triggers.
-tuple = {"T2T", space_id, {sql = "CREATE TRIGGER t2t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(2); END;"}}
+tuple = {"T2T", space_id, MAP, 'SQL', 'REPLACE', 'INSERT', 'AFTER', "CREATE TRIGGER t2t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(2); END;"}
 ---
 ...
 _ = box.space._trigger:insert(tuple)
 ---
 ...
-tuple = {"T3T", space_id, {sql = "CREATE TRIGGER t3t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(3); END;"}}
+tuple = {"T3T", space_id, MAP, 'SQL', 'REPLACE', 'INSERT', 'AFTER', "CREATE TRIGGER t3t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(3); END;"}
 ---
 ...
 _ = box.space._trigger:insert(tuple)
@@ -128,14 +103,14 @@ _ = box.space._trigger:insert(tuple)
 immutable_part(box.space._trigger:select())
 ---
 - - - T1T
-    - {'sql': 'CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(1); END'}
+    - CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(1);
+      END
   - - T2T
-    - {'sql': 'CREATE TRIGGER t2t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(2); END;'}
+    - CREATE TRIGGER t2t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(2);
+      END;
   - - T3T
-    - {'sql': 'CREATE TRIGGER t3t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(3); END;'}
+    - CREATE TRIGGER t3t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(3);
+      END;
 ...
 box.execute("INSERT INTO t1 VALUES(2);")
 ---
@@ -166,8 +141,8 @@ _ = box.space._trigger:delete("T3T")
 immutable_part(box.space._trigger:select())
 ---
 - - - T1T
-    - {'sql': 'CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(1); END'}
+    - CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(1);
+      END
 ...
 box.execute("INSERT INTO t1 VALUES(3);")
 ---
@@ -197,14 +172,14 @@ box.execute([[CREATE TRIGGER t3t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT IN
 immutable_part(box.space._trigger:select())
 ---
 - - - T1T
-    - {'sql': 'CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(1); END'}
+    - CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(1);
+      END
   - - T2T
-    - {'sql': 'CREATE TRIGGER t2t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(2); END'}
+    - CREATE TRIGGER t2t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(2);
+      END
   - - T3T
-    - {'sql': 'CREATE TRIGGER t3t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO
-        t2 VALUES(3); END'}
+    - CREATE TRIGGER t3t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(3);
+      END
 ...
 box.execute("INSERT INTO t1 VALUES(4);")
 ---
@@ -241,7 +216,7 @@ box.execute("CREATE TABLE t1(a INT PRIMARY KEY,b INT);")
 space_id = box.space.T1.id
 ---
 ...
-tuple = {"T1T", space_id, {sql = [[create trigger t1t instead of update on t1 for each row begin delete from t1 WHERE a=old.a+2; end;]]}}
+tuple = {"T1T", space_id, MAP, 'SQL', 'REPLACE', 'UPDATE', 'INSTEAD', "CREATE TRIGGER T1T INSTEAD OF UPDATE ON T1 FOR EACH ROW BEGIN DELETE FROM T1 WHERE A=OLD.A+2; END;"}
 ---
 ...
 box.space._trigger:insert(tuple)
@@ -256,14 +231,14 @@ box.execute("CREATE VIEW V1 AS SELECT * FROM t1;")
 space_id = box.space.V1.id
 ---
 ...
-tuple = {"V1T", space_id, {sql = [[create trigger v1t before update on v1 for each row begin delete from t1 WHERE a=old.a+2; end;]]}}
+tuple = {"V1T", space_id, MAP, 'SQL', 'REPLACE', 'UPDATE', 'BEFORE', "CREATE TRIGGER V1T BEFORE UPDATE ON V1 FOR EACH ROW BEGIN DELETE FROM T1 WHERE A=OLD.A+2; END;"}
 ---
 ...
 box.space._trigger:insert(tuple)
 ---
 - error: 'Failed to execute SQL statement: cannot create BEFORE trigger on view: V1'
 ...
-tuple = {"V1T", space_id, {sql = [[create trigger v1t AFTER update on v1 for each row begin delete from t1 WHERE a=old.a+2; end;]]}}
+tuple = {"V1T", space_id, MAP, 'SQL', 'REPLACE', 'UPDATE', 'AFTER', "CREATE TRIGGER V1T AFTER UPDATE ON V1 FOR EACH ROW BEGIN DELETE FROM T1 WHERE A=OLD.A+2; END;"}
 ---
 ...
 box.space._trigger:insert(tuple)
@@ -273,12 +248,12 @@ box.space._trigger:insert(tuple)
 space_id =  box.space._fk_constraint.id
 ---
 ...
-tuple = {"T1T", space_id, {sql = [[create trigger t1t instead of update on "_fk_constraint" for each row begin delete from t1 WHERE a=old.a+2; end;]]}}
+tuple = {"T1T", space_id, MAP, 'SQL', 'REPLACE', 'UPDATE', 'BEFORE', "CREATE TRIGGER T1T INSTEAD OF UPDATE ON \"_fk_constraint\" FOR EACH ROW BEGIN DELETE FROM T1 WHERE A=OLD.A+2; END;"}
 ---
 ...
 box.space._trigger:insert(tuple)
 ---
-- error: 'Failed to execute SQL statement: cannot create trigger on system table'
+- error: 'Failed to create trigger ''T1T'': cannot create trigger on system space'
 ...
 box.execute("DROP VIEW V1;")
 ---
@@ -489,7 +464,7 @@ box.execute("CREATE TRIGGER tr1 AFTER INSERT ON t1 FOR EACH ROW WHEN new.a = ? B
 - null
 - bindings are not allowed in DDL
 ...
-tuple = {"TR1", space_id, {sql = [[CREATE TRIGGER tr1 AFTER INSERT ON t1 FOR EACH ROW WHEN new.a = ? BEGIN SELECT 1; END;]]}}
+tuple = {"TR1", space_id, MAP, 'SQL', 'REPLACE', 'UPDATE', 'AFTER', "CREATE TRIGGER tr1 AFTER INSERT ON t1 FOR EACH ROW WHEN new.a = ? BEGIN SELECT 1; END;"}
 ---
 ...
 box.space._trigger:insert(tuple)
diff --git a/test/sql/triggers.test.lua b/test/sql/triggers.test.lua
index f0397dc7d..d2a91d269 100644
--- a/test/sql/triggers.test.lua
+++ b/test/sql/triggers.test.lua
@@ -3,8 +3,11 @@ test_run = env.new()
 engine = test_run:get_cfg('engine')
 box.execute('pragma sql_default_engine=\''..engine..'\'')
 
+function setmap(tab) return setmetatable(tab, { __serialize = 'map' }) end
+MAP = setmap({})
+
 -- Get invariant part of the tuple; name and opts don't change.
- function immutable_part(data) local r = {} for i, l in pairs(data) do table.insert(r, {l.name, l.opts}) end return r end
+function immutable_part(data) local r = {} for i, l in pairs(data) do table.insert(r, {l.name, l.code}) end return r end
 
 --
 -- gh-3273: Move Triggers to server
@@ -17,17 +20,6 @@ immutable_part(box.space._trigger:select())
 
 space_id = box.space._space.index["name"]:get('T1').id
 
--- Checks for LUA tuples.
-tuple = {"T1t", space_id, {sql = "CREATE TRIGGER t1t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(1); END;"}}
-box.space._trigger:insert(tuple)
-
-tuple = {"T1t", space_id, {sql = "CREATE TRIGGER t12t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(1); END;"}}
-box.space._trigger:insert(tuple)
-
-tuple = {"T2T", box.space.T1.id + 1, {sql = "CREATE TRIGGER t2t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(1); END;"}}
-box.space._trigger:insert(tuple)
-immutable_part(box.space._trigger:select())
-
 box.execute("DROP TABLE T1;")
 immutable_part(box.space._trigger:select())
 
@@ -44,9 +36,9 @@ box.execute("DELETE FROM t2;")
 
 
 -- Test triggers.
-tuple = {"T2T", space_id, {sql = "CREATE TRIGGER t2t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(2); END;"}}
+tuple = {"T2T", space_id, MAP, 'SQL', 'REPLACE', 'INSERT', 'AFTER', "CREATE TRIGGER t2t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(2); END;"}
 _ = box.space._trigger:insert(tuple)
-tuple = {"T3T", space_id, {sql = "CREATE TRIGGER t3t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(3); END;"}}
+tuple = {"T3T", space_id, MAP, 'SQL', 'REPLACE', 'INSERT', 'AFTER', "CREATE TRIGGER t3t AFTER INSERT ON t1 FOR EACH ROW BEGIN INSERT INTO t2 VALUES(3); END;"}
 _ = box.space._trigger:insert(tuple)
 immutable_part(box.space._trigger:select())
 box.execute("INSERT INTO t1 VALUES(2);")
@@ -77,20 +69,20 @@ immutable_part(box.space._trigger:select())
 box.execute("CREATE TABLE t1(a INT PRIMARY KEY,b INT);")
 space_id = box.space.T1.id
 
-tuple = {"T1T", space_id, {sql = [[create trigger t1t instead of update on t1 for each row begin delete from t1 WHERE a=old.a+2; end;]]}}
+tuple = {"T1T", space_id, MAP, 'SQL', 'REPLACE', 'UPDATE', 'INSTEAD', "CREATE TRIGGER T1T INSTEAD OF UPDATE ON T1 FOR EACH ROW BEGIN DELETE FROM T1 WHERE A=OLD.A+2; END;"}
 box.space._trigger:insert(tuple)
 
 box.execute("CREATE VIEW V1 AS SELECT * FROM t1;")
 space_id = box.space.V1.id
 
-tuple = {"V1T", space_id, {sql = [[create trigger v1t before update on v1 for each row begin delete from t1 WHERE a=old.a+2; end;]]}}
+tuple = {"V1T", space_id, MAP, 'SQL', 'REPLACE', 'UPDATE', 'BEFORE', "CREATE TRIGGER V1T BEFORE UPDATE ON V1 FOR EACH ROW BEGIN DELETE FROM T1 WHERE A=OLD.A+2; END;"}
 box.space._trigger:insert(tuple)
 
-tuple = {"V1T", space_id, {sql = [[create trigger v1t AFTER update on v1 for each row begin delete from t1 WHERE a=old.a+2; end;]]}}
+tuple = {"V1T", space_id, MAP, 'SQL', 'REPLACE', 'UPDATE', 'AFTER', "CREATE TRIGGER V1T AFTER UPDATE ON V1 FOR EACH ROW BEGIN DELETE FROM T1 WHERE A=OLD.A+2; END;"}
 box.space._trigger:insert(tuple)
 
 space_id =  box.space._fk_constraint.id
-tuple = {"T1T", space_id, {sql = [[create trigger t1t instead of update on "_fk_constraint" for each row begin delete from t1 WHERE a=old.a+2; end;]]}}
+tuple = {"T1T", space_id, MAP, 'SQL', 'REPLACE', 'UPDATE', 'BEFORE', "CREATE TRIGGER T1T INSTEAD OF UPDATE ON \"_fk_constraint\" FOR EACH ROW BEGIN DELETE FROM T1 WHERE A=OLD.A+2; END;"}
 box.space._trigger:insert(tuple)
 
 box.execute("DROP VIEW V1;")
@@ -162,7 +154,7 @@ box.execute("DROP TABLE t;")
 box.execute("CREATE TABLE t1(a INT PRIMARY KEY, b INT);")
 space_id = box.space.T1.id
 box.execute("CREATE TRIGGER tr1 AFTER INSERT ON t1 FOR EACH ROW WHEN new.a = ? BEGIN SELECT 1; END;")
-tuple = {"TR1", space_id, {sql = [[CREATE TRIGGER tr1 AFTER INSERT ON t1 FOR EACH ROW WHEN new.a = ? BEGIN SELECT 1; END;]]}}
+tuple = {"TR1", space_id, MAP, 'SQL', 'REPLACE', 'UPDATE', 'AFTER', "CREATE TRIGGER tr1 AFTER INSERT ON t1 FOR EACH ROW WHEN new.a = ? BEGIN SELECT 1; END;"}
 box.space._trigger:insert(tuple)
 box.execute("DROP TABLE t1;")
 
diff --git a/test/sql/upgrade.result b/test/sql/upgrade.result
index b5c4ad500..7392e9c7c 100644
--- a/test/sql/upgrade.result
+++ b/test/sql/upgrade.result
@@ -27,7 +27,9 @@ test_run:switch('upgrade')
 box.space._space.index['name']:get('_trigger')
 ---
 - [328, 1, '_trigger', 'memtx', 0, {}, [{'name': 'name', 'type': 'string'}, {'name': 'space_id',
-      'type': 'unsigned'}, {'name': 'opts', 'type': 'map'}]]
+      'type': 'unsigned'}, {'name': 'opts', 'type': 'map'}, {'name': 'language', 'type': 'string'},
+    {'name': 'type', 'type': 'string'}, {'name': 'event_manipulation', 'type': 'string'},
+    {'name': 'action_timing', 'type': 'string'}, {'name': 'code', 'type': 'string'}]]
 ...
 box.space._index:get({box.space._space.index['name']:get('_trigger').id, 0})
 ---
@@ -83,19 +85,19 @@ t1t.name
 ---
 - T1T
 ...
-t1t.opts
+t1t.code
 ---
-- {'sql': 'CREATE TRIGGER t1t AFTER INSERT ON t FOR EACH ROW BEGIN INSERT INTO t_out
-    VALUES(1); END'}
+- CREATE TRIGGER t1t AFTER INSERT ON t FOR EACH ROW BEGIN INSERT INTO t_out VALUES(1);
+  END
 ...
 t2t.name
 ---
 - T2T
 ...
-t2t.opts
+t2t.code
 ---
-- {'sql': 'CREATE TRIGGER t2t AFTER INSERT ON t FOR EACH ROW BEGIN INSERT INTO t_out
-    VALUES(2); END'}
+- CREATE TRIGGER t2t AFTER INSERT ON t FOR EACH ROW BEGIN INSERT INTO t_out VALUES(2);
+  END
 ...
 assert(t1t.space_id == t2t.space_id)
 ---
diff --git a/test/sql/upgrade.test.lua b/test/sql/upgrade.test.lua
index 37425ae21..79231b34b 100644
--- a/test/sql/upgrade.test.lua
+++ b/test/sql/upgrade.test.lua
@@ -29,9 +29,9 @@ box.space._space.index['name']:get('T_OUT')
 t1t = box.space._trigger:get('T1T')
 t2t = box.space._trigger:get('T2T')
 t1t.name
-t1t.opts
+t1t.code
 t2t.name
-t2t.opts
+t2t.code
 assert(t1t.space_id == t2t.space_id)
 assert(t1t.space_id == box.space.T.id)
 
-- 
2.23.0

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [tarantool-patches] [PATCH v1 1/9] sql: remove redundant pointer in TriggerStep
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 1/9] sql: remove redundant pointer in TriggerStep Kirill Shcherbatov
@ 2019-10-15 15:35   ` Nikita Pettik
  0 siblings, 0 replies; 31+ messages in thread
From: Nikita Pettik @ 2019-10-15 15:35 UTC (permalink / raw)
  To: tarantool-patches; +Cc: tarantool-patches

On 14 Oct 19:03, Kirill Shcherbatov wrote:
> The trigger pointer in the sql_trigger structure is a dead code
> now, so it could be deleted.
> 
> Needed for #4343

Pushed as trivial.

> ---
>  src/box/sql/fk_constraint.c | 1 -
>  src/box/sql/sqlInt.h        | 2 --
>  src/box/sql/trigger.c       | 1 -
>  3 files changed, 4 deletions(-)
> 
> diff --git a/src/box/sql/fk_constraint.c b/src/box/sql/fk_constraint.c
> index 4717677f7..482220a95 100644
> --- a/src/box/sql/fk_constraint.c
> +++ b/src/box/sql/fk_constraint.c
> @@ -895,7 +895,6 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def,
>  		step->op = TK_UPDATE;
>  	}
>  
> -	step->trigger = trigger;
>  	if (is_update) {
>  		fk->on_update_trigger = trigger;
>  		trigger->op = TK_UPDATE;
> diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
> index 35fc81dfb..1b6d92cb1 100644
> --- a/src/box/sql/sqlInt.h
> +++ b/src/box/sql/sqlInt.h
> @@ -2366,8 +2366,6 @@ struct sql_trigger {
>  struct TriggerStep {
>  	u8 op;			/* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */
>  	u8 orconf;		/* ON_CONFLICT_ACTION_ROLLBACK etc. */
> -	/** 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 */
> diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
> index d746ef893..0c387bc3b 100644
> --- a/src/box/sql/trigger.c
> +++ b/src/box/sql/trigger.c
> @@ -170,7 +170,6 @@ sql_trigger_finish(struct Parse *parse, struct TriggerStep *step_list,
>  	char *trigger_name = trigger->zName;
>  	trigger->step_list = step_list;
>  	while (step_list != NULL) {
> -		step_list->trigger = trigger;
>  		step_list = step_list->pNext;
>  	}
>  
> -- 
> 2.23.0
> 
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space
  2019-10-14 16:03 [Tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space Kirill Shcherbatov
                   ` (8 preceding siblings ...)
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 9/9] schema: rework _trigger system space Kirill Shcherbatov
@ 2019-10-15 21:34 ` Nikita Pettik
  2019-10-16  5:57   ` Konstantin Osipov
  9 siblings, 1 reply; 31+ messages in thread
From: Nikita Pettik @ 2019-10-15 21:34 UTC (permalink / raw)
  To: tarantool-patches; +Cc: tarantool-patches

On 14 Oct 19:03, Kirill Shcherbatov wrote:
> This patchset reworks structures that represent SQL triggers to inherent
> them from base trigger class. It is an important step to introduce a
> LUA persistent trigger in Tarantool as well as to support SQL triggers
> evaluation on the server side.

To be honest, I don't clearly understand what plan you are following now.
I remember that you wrote RFC, what status does it have now? Is it accepted
and this preparation is done in accordance with it? If so, let's firstly
push RFC. In case RFC is not approved or is changed, it would be nice to
see more detailed rationale for these patches..

Personally I've already said (see [dev] [rfc] Persistent triggers in Tarantool
thread) that I do not support idea of storing both Lua and SQL trigger's
metadata in one space.
 
> In scope of this patchset the _triggers space was extended with a new fields.
> The _trigger space updated format is:
> [<name> STRING, <space_id> UINT32, <opts> MAP, <language> STR,
>  <type> STR, <event_manipulation> STR,
>  <action_timing> STR, <code> STR]
> Now trigger is produced mostly by tuple-based metadata definition instead of
> naive SQL request parsing.
> 
> Branch: http://github.com/tarantool/tarantool/tree/kshch/gh-4343-persistent-triggers
> Issue: https://github.com/tarantool/tarantool/issues/4343
> 
> Kirill Shcherbatov (9):
>   sql: remove redundant pointer in TriggerStep
>   box: rename struct trigger to lua_trigger
>   box: introduce trigger_event_manipulation enum
>   box: introduce trigger_action_timing enum
>   sql: use rlist to organize triggers in a list
>   sql: rework CREATE TABLE rule in parser
>   sql: wrap all ASTs in sql_trigger_expr structure
>   sql: inherit sql_trigger from a new trigger class
>   schema: rework _trigger system space
> 
>  src/box/CMakeLists.txt                        |   2 +
>  src/box/alter.cc                              | 438 +++++++++--------
>  src/box/alter.h                               |  34 +-
>  src/box/applier.cc                            |  28 +-
>  src/box/applier.h                             |   2 +-
>  src/box/bootstrap.snap                        | Bin 5934 -> 5981 bytes
>  src/box/ck_constraint.c                       |   2 +-
>  src/box/ck_constraint.h                       |   4 +-
>  src/box/errcode.h                             |   1 +
>  src/box/error.cc                              |   2 +-
>  src/box/fk_constraint.c                       |   6 +-
>  src/box/iproto.cc                             |   4 +-
>  src/box/lua/call.c                            |   4 +-
>  src/box/lua/sequence.c                        |   4 +-
>  src/box/lua/space.cc                          |   4 +-
>  src/box/lua/tuple.c                           |   2 +-
>  src/box/lua/upgrade.lua                       |  16 +
>  src/box/memtx_space.c                         |   8 +-
>  src/box/recovery.cc                           |   2 +-
>  src/box/relay.cc                              |   6 +-
>  src/box/replication.cc                        |   8 +-
>  src/box/replication.h                         |   4 +-
>  src/box/schema.cc                             |   2 +-
>  src/box/schema_def.h                          |   5 +
>  src/box/session.cc                            |   4 +-
>  src/box/session.h                             |   4 +-
>  src/box/space.c                               |  11 +-
>  src/box/space.h                               |   4 +-
>  src/box/sql.c                                 | 105 ++--
>  src/box/sql.h                                 | 125 +++--
>  src/box/sql/build.c                           |  97 +++-
>  src/box/sql/delete.c                          |  27 +-
>  src/box/sql/fk_constraint.c                   | 109 +++--
>  src/box/sql/insert.c                          |  42 +-
>  src/box/sql/parse.y                           |  26 +-
>  src/box/sql/parse_def.h                       |  29 +-
>  src/box/sql/prepare.c                         |   7 +-
>  src/box/sql/resolve.c                         |  15 +-
>  src/box/sql/sqlInt.h                          | 111 ++---
>  src/box/sql/tokenize.c                        |  16 +-
>  src/box/sql/trigger.c                         | 448 ++++++------------
>  src/box/sql/update.c                          |  45 +-
>  src/box/sql/vdbe.c                            |  18 +-
>  src/box/trigger.c                             |  73 +++
>  src/box/trigger.h                             |  80 ++++
>  src/box/trigger_def.c                         | 131 +++++
>  src/box/trigger_def.h                         | 194 ++++++++
>  src/box/txn.c                                 |   8 +-
>  src/box/txn.h                                 |  14 +-
>  src/box/vinyl.c                               |  32 +-
>  src/box/vy_lsm.c                              |   2 +-
>  src/box/vy_scheduler.c                        |   4 +-
>  src/box/vy_tx.c                               |   2 +-
>  src/lib/core/cbus.c                           |   2 +-
>  src/lib/core/fiber.c                          |   6 +-
>  src/lib/core/trigger.cc                       |   6 +-
>  src/lib/core/trigger.h                        |  20 +-
>  src/lib/swim/swim.c                           |   2 +-
>  src/lua/swim.c                                |   2 +-
>  src/lua/trigger.c                             |   6 +-
>  src/lua/trigger.h                             |   2 +-
>  src/lua/utils.h                               |   2 +-
>  src/main.cc                                   |   4 +-
>  test/box-py/bootstrap.result                  |   5 +-
>  test/box/access_misc.result                   | 139 +++---
>  test/box/misc.result                          |   1 +
>  test/sql/ddl.result                           |   6 +-
>  test/sql/ddl.test.lua                         |   6 +-
>  .../gh2141-delete-trigger-drop-table.result   |  36 +-
>  .../gh2141-delete-trigger-drop-table.test.lua |   4 +-
>  test/sql/persistency.result                   |  36 +-
>  test/sql/persistency.test.lua                 |   8 +-
>  test/sql/triggers.result                      |  91 ++--
>  test/sql/triggers.test.lua                    |  30 +-
>  test/sql/upgrade.result                       |  16 +-
>  test/sql/upgrade.test.lua                     |   4 +-
>  test/unit/CMakeLists.txt                      |   3 +-
>  test/unit/cbus.c                              |   6 +-
>  test/unit/swim.c                              |  14 +-
>  test/unit/swim_test_utils.c                   |   6 +-
>  80 files changed, 1695 insertions(+), 1139 deletions(-)
>  create mode 100644 src/box/trigger.c
>  create mode 100644 src/box/trigger.h
>  create mode 100644 src/box/trigger_def.c
>  create mode 100644 src/box/trigger_def.h
> 
> -- 
> 2.23.0
> 
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space
  2019-10-15 21:34 ` [Tarantool-patches] [tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space Nikita Pettik
@ 2019-10-16  5:57   ` Konstantin Osipov
  2019-10-16  5:58     ` Konstantin Osipov
  2019-10-16 11:07     ` Nikita Pettik
  0 siblings, 2 replies; 31+ messages in thread
From: Konstantin Osipov @ 2019-10-16  5:57 UTC (permalink / raw)
  To: Nikita Pettik; +Cc: tarantool-patches, tarantool-patches

* Nikita Pettik <korablev@tarantool.org> [19/10/16 08:45]:
> Personally I've already said (see [dev] [rfc] Persistent triggers in Tarantool
> thread) that I do not support idea of storing both Lua and SQL trigger's
> metadata in one space.

Well, we can not reach the point not only because we have
different opinions, but because the discussion is so slow.

The reason to store all persistent objects of the same type in the
same space is that Tarantool is designed as a multiple frontend
system. I.e. tomorrow there may be another front end, not just Lua
or SQL, and one doesn't want to have a separate table for each
front end.

If the trigger timing, action type, definer, setuid and other
semantics is the same, and only the language is different, then 
why duplicate the space? Whenever we add a new property common to
all triggers (e.g. persist enabled/disabled), we'll have to do
extra work.


-- 
Konstantin Osipov, Moscow, Russia

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space
  2019-10-16  5:57   ` Konstantin Osipov
@ 2019-10-16  5:58     ` Konstantin Osipov
  2019-10-16 11:07     ` Nikita Pettik
  1 sibling, 0 replies; 31+ messages in thread
From: Konstantin Osipov @ 2019-10-16  5:58 UTC (permalink / raw)
  To: Nikita Pettik, tarantool-patches, tarantool-patches, Kirill Shcherbatov

* Konstantin Osipov <kostja.osipov@gmail.com> [19/10/16 08:57]:
> * Nikita Pettik <korablev@tarantool.org> [19/10/16 08:45]:
> > Personally I've already said (see [dev] [rfc] Persistent triggers in Tarantool
> > thread) that I do not support idea of storing both Lua and SQL trigger's
> > metadata in one space.
> 
> Well, we can not reach the point not only because we have
> different opinions, but because the discussion is so slow.
> 
> The reason to store all persistent objects of the same type in the
> same space is that Tarantool is designed as a multiple frontend
> system. I.e. tomorrow there may be another front end, not just Lua
> or SQL, and one doesn't want to have a separate table for each
> front end.
> 
> If the trigger timing, action type, definer, setuid and other
> semantics is the same, and only the language is different, then 
> why duplicate the space? Whenever we add a new property common to
> all triggers (e.g. persist enabled/disabled), we'll have to do
> extra work.

Having said that, I should note that I mentioned earlier that I
don't think this feature is a high priority work.


-- 
Konstantin Osipov, Moscow, Russia

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space
  2019-10-16  5:57   ` Konstantin Osipov
  2019-10-16  5:58     ` Konstantin Osipov
@ 2019-10-16 11:07     ` Nikita Pettik
  2019-10-16 11:11       ` Konstantin Osipov
  1 sibling, 1 reply; 31+ messages in thread
From: Nikita Pettik @ 2019-10-16 11:07 UTC (permalink / raw)
  To: Konstantin Osipov, tarantool-patches, tarantool-patches,
	Kirill Shcherbatov

On 16 Oct 08:57, Konstantin Osipov wrote:
> * Nikita Pettik <korablev@tarantool.org> [19/10/16 08:45]:
> > Personally I've already said (see [dev] [rfc] Persistent triggers in Tarantool
> > thread) that I do not support idea of storing both Lua and SQL trigger's
> > metadata in one space.
> 
> Well, we can not reach the point not only because we have
> different opinions, but because the discussion is so slow.

It is not slow, it just gets stuck..

> The reason to store all persistent objects of the same type in the
> same space is that Tarantool is designed as a multiple frontend
> system. I.e. tomorrow there may be another front end, not just Lua
> or SQL, and one doesn't want to have a separate table for each
> front end.

Let's be objective: how close we are to introduce new language in
Tarantool? Is there any demand for new language at all?
 
> If the trigger timing, action type, definer, setuid and other
> semantics is the same, and only the language is different, then 
> why duplicate the space?

The thing is they are not the same. In fact, set of Lua and SQL trigger's
features are quite different. In Lua trigger timing can be one of
on_replace or before_replace, meanwhile in SQL trigger timing is one of
BEFORE/AFTER/INSTEAD replace; In Lua action event is replace, whereas in SQL
it can be INSERT/DELETE/UPDATE; FOR EACH ROW/STATEMENT action in SQL, and
only FOR EACH ROW is available in Lua.

> Whenever we add a new property common to
> all triggers (e.g. persist enabled/disabled), we'll have to do
> extra work.
> 
> 
> -- 
> Konstantin Osipov, Moscow, Russia

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space
  2019-10-16 11:07     ` Nikita Pettik
@ 2019-10-16 11:11       ` Konstantin Osipov
  2019-10-16 12:18         ` Nikita Pettik
  0 siblings, 1 reply; 31+ messages in thread
From: Konstantin Osipov @ 2019-10-16 11:11 UTC (permalink / raw)
  To: Nikita Pettik; +Cc: tarantool-patches, tarantool-patches

* Nikita Pettik <korablev@tarantool.org> [19/10/16 14:08]:
> > The reason to store all persistent objects of the same type in the
> > same space is that Tarantool is designed as a multiple frontend
> > system. I.e. tomorrow there may be another front end, not just Lua
> > or SQL, and one doesn't want to have a separate table for each
> > front end.
> 
> Let's be objective: how close we are to introduce new language in
> Tarantool? Is there any demand for new language at all?

it is one of the tenets of the design. Should it be broken now
just because it feels like it won't happen? I think it's a way
broader question than the scope of this patch.

> > If the trigger timing, action type, definer, setuid and other
> > semantics is the same, and only the language is different, then 
> > why duplicate the space?
> 
> The thing is they are not the same. In fact, set of Lua and SQL trigger's
> features are quite different. In Lua trigger timing can be one of
> on_replace or before_replace, meanwhile in SQL trigger timing is one of
> BEFORE/AFTER/INSTEAD replace; In Lua action event is replace, whereas in SQL
> it can be INSERT/DELETE/UPDATE; FOR EACH ROW/STATEMENT action in SQL, and
> only FOR EACH ROW is available in Lua.

Right, so it's different values (today) of properties, but the properties
are largely the same. 
May I want to define a statement-level trigger in Lua in future?
Why not?  

What are the benefits of having a separate table?


-- 
Konstantin Osipov, Moscow, Russia

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space
  2019-10-16 11:11       ` Konstantin Osipov
@ 2019-10-16 12:18         ` Nikita Pettik
  2019-10-16 12:32           ` Konstantin Osipov
  2019-10-16 12:53           ` [Tarantool-patches] [tarantool-patches] " Kirill Shcherbatov
  0 siblings, 2 replies; 31+ messages in thread
From: Nikita Pettik @ 2019-10-16 12:18 UTC (permalink / raw)
  To: Konstantin Osipov, tarantool-patches, tarantool-patches,
	Kirill Shcherbatov

On 16 Oct 14:11, Konstantin Osipov wrote:
> * Nikita Pettik <korablev@tarantool.org> [19/10/16 14:08]:
> > > The reason to store all persistent objects of the same type in the
> > > same space is that Tarantool is designed as a multiple frontend
> > > system. I.e. tomorrow there may be another front end, not just Lua
> > > or SQL, and one doesn't want to have a separate table for each
> > > front end.
> > 
> > Let's be objective: how close we are to introduce new language in
> > Tarantool? Is there any demand for new language at all?
> 
> it is one of the tenets of the design. Should it be broken now
> just because it feels like it won't happen? I think it's a way
> broader question than the scope of this patch.

As you once said: "We're a small shop and will bother with it when there
is a real customer affected by the problem."
 
> > > If the trigger timing, action type, definer, setuid and other
> > > semantics is the same, and only the language is different, then 
> > > why duplicate the space?
> > 
> > The thing is they are not the same. In fact, set of Lua and SQL trigger's
> > features are quite different. In Lua trigger timing can be one of
> > on_replace or before_replace, meanwhile in SQL trigger timing is one of
> > BEFORE/AFTER/INSTEAD replace; In Lua action event is replace, whereas in SQL
> > it can be INSERT/DELETE/UPDATE; FOR EACH ROW/STATEMENT action in SQL, and
> > only FOR EACH ROW is available in Lua.
> 
> Right, so it's different values (today) of properties, but the properties
> are largely the same. 

Okay, let's enumerate all possible properties of _trigger space (I've taken
version from the last RFC):

- name (both have)
- func_id (only Lua)
- space_id (both have)
- type (means one of replace/connection/auth etc; only Lua)
- event (only SQL)
- action_time (SQL and Lua have sets of timings which don't intersect)
- action (only SQL)

So there are only two features in common now: name and space id.

> May I want to define a statement-level trigger in Lua in future?
> Why not?  
> 
> What are the benefits of having a separate table?

We don't have to manage and validate options that in fact are reasonable
only for SQL or Lua triggers.
 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space
  2019-10-16 12:18         ` Nikita Pettik
@ 2019-10-16 12:32           ` Konstantin Osipov
  2019-10-16 12:47             ` Nikita Pettik
  2019-10-16 12:53           ` [Tarantool-patches] [tarantool-patches] " Kirill Shcherbatov
  1 sibling, 1 reply; 31+ messages in thread
From: Konstantin Osipov @ 2019-10-16 12:32 UTC (permalink / raw)
  To: Nikita Pettik; +Cc: tarantool-patches, tarantool-patches

* Nikita Pettik <korablev@tarantool.org> [19/10/16 15:23]:

Let's just consider name:

> Okay, let's enumerate all possible properties of _trigger space (I've taken
> version from the last RFC):
> 
> - name (both have)

If there will be persistent lua triggers, would it be possible to
drop them from SQL? With what construct? What will be their name
space (same name space as SQL triggers, or a distinct names space)?
Case sensitivity? 


-- 
Konstantin Osipov, Moscow, Russia

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space
  2019-10-16 12:32           ` Konstantin Osipov
@ 2019-10-16 12:47             ` Nikita Pettik
  2019-10-16 12:53               ` Konstantin Osipov
  0 siblings, 1 reply; 31+ messages in thread
From: Nikita Pettik @ 2019-10-16 12:47 UTC (permalink / raw)
  To: Konstantin Osipov, tarantool-patches, tarantool-patches,
	Kirill Shcherbatov

On 16 Oct 15:32, Konstantin Osipov wrote:
> * Nikita Pettik <korablev@tarantool.org> [19/10/16 15:23]:
> 
> Let's just consider name:
> 
> > Okay, let's enumerate all possible properties of _trigger space (I've taken
> > version from the last RFC):
> > 
> > - name (both have)
> 
> If there will be persistent lua triggers, would it be possible to
> drop them from SQL? With what construct? What will be their name
> space (same name space as SQL triggers, or a distinct names space)?
> Case sensitivity? 

IMHO there should be no opportunity to drop Lua NoSQL triggers from SQL;
they should share different namespaces (as it happens now). The reason
is that SQL triggers will be implemented as NoSQL on_replace triggers
under the hood.

> -- 
> Konstantin Osipov, Moscow, Russia

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [tarantool-patches] Re: [PATCH v1 0/9] schema: rework _trigger space
  2019-10-16 12:18         ` Nikita Pettik
  2019-10-16 12:32           ` Konstantin Osipov
@ 2019-10-16 12:53           ` Kirill Shcherbatov
  2019-10-16 13:31             ` Nikita Pettik
  1 sibling, 1 reply; 31+ messages in thread
From: Kirill Shcherbatov @ 2019-10-16 12:53 UTC (permalink / raw)
  To: tarantool-patches, Nikita Pettik, Konstantin Osipov, tarantool-patches

> - name (both have)
> - func_id (only Lua)
> - space_id (both have)
> - type (means one of replace/connection/auth etc; only Lua)
> - event (only SQL)
> - action_time (SQL and Lua have sets of timings which don't intersect)
> - action (only SQL)

I need to mention that I didn't followed this outdated proposal in the
refactoring that we are discussing.

<name> STRING  (both have)
<space_id> UINT32 (both have)
<opts> MAP (non of them for now, legacy, but potentially usefull)
<language> STR (both have)
<type> STR (both have; currently 'replace' type only - replace trigger;
                      perhaps we would like to support some different trigger types)
<event_manipulation> STR  {"DELETE", "UPDATE", "INSERT" --
                                               SQL only? - but it is possible to support it in Lua also}
<action_timing> STR {"BEFORE", "AFTER" - both have}
<code> STR (both have)

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space
  2019-10-16 12:47             ` Nikita Pettik
@ 2019-10-16 12:53               ` Konstantin Osipov
  2019-10-16 13:13                 ` Nikita Pettik
  0 siblings, 1 reply; 31+ messages in thread
From: Konstantin Osipov @ 2019-10-16 12:53 UTC (permalink / raw)
  To: Nikita Pettik; +Cc: tarantool-patches, tarantool-patches

* Nikita Pettik <korablev@tarantool.org> [19/10/16 15:48]:
> > > Okay, let's enumerate all possible properties of _trigger space (I've taken
> > > version from the last RFC):
> > > 
> > > - name (both have)
> > 
> > If there will be persistent lua triggers, would it be possible to
> > drop them from SQL? With what construct? What will be their name
> > space (same name space as SQL triggers, or a distinct names space)?
> > Case sensitivity? 
> 
> IMHO there should be no opportunity to drop Lua NoSQL triggers from SQL;
> they should share different namespaces (as it happens now). The reason
> is that SQL triggers will be implemented as NoSQL on_replace triggers
> under the hood.

This is true for non-persistent triggers. What if we ever decide
to persist Lua triggers?

-- 
Konstantin Osipov, Moscow, Russia

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space
  2019-10-16 12:53               ` Konstantin Osipov
@ 2019-10-16 13:13                 ` Nikita Pettik
  2019-10-16 14:18                   ` Konstantin Osipov
  0 siblings, 1 reply; 31+ messages in thread
From: Nikita Pettik @ 2019-10-16 13:13 UTC (permalink / raw)
  To: Konstantin Osipov; +Cc: tarantool-patches, tarantool-patches

On 16 Oct 15:53, Konstantin Osipov wrote:
> * Nikita Pettik <korablev@tarantool.org> [19/10/16 15:48]:
> > > > Okay, let's enumerate all possible properties of _trigger space (I've taken
> > > > version from the last RFC):
> > > > 
> > > > - name (both have)
> > > 
> > > If there will be persistent lua triggers, would it be possible to
> > > drop them from SQL? With what construct? What will be their name
> > > space (same name space as SQL triggers, or a distinct names space)?
> > > Case sensitivity? 
> > 
> > IMHO there should be no opportunity to drop Lua NoSQL triggers from SQL;
> > they should share different namespaces (as it happens now). The reason
> > is that SQL triggers will be implemented as NoSQL on_replace triggers
> > under the hood.
> 
> This is true for non-persistent triggers. What if we ever decide
> to persist Lua triggers?

Why there should be any difference between them? Persisted Lua triggers
have nothing in common with SQL triggers. They are just parsed and re-created
after restart. Then they behave as normal (in-memory) NoSQL triggers.
 
> -- 
> Konstantin Osipov, Moscow, Russia

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [tarantool-patches] Re: [PATCH v1 0/9] schema: rework _trigger space
  2019-10-16 12:53           ` [Tarantool-patches] [tarantool-patches] " Kirill Shcherbatov
@ 2019-10-16 13:31             ` Nikita Pettik
  2019-10-16 13:47               ` Kirill Shcherbatov
  0 siblings, 1 reply; 31+ messages in thread
From: Nikita Pettik @ 2019-10-16 13:31 UTC (permalink / raw)
  To: Kirill Shcherbatov; +Cc: tarantool-patches, tarantool-patches

On 16 Oct 15:53, Kirill Shcherbatov wrote:
> > - name (both have)
> > - func_id (only Lua)
> > - space_id (both have)
> > - type (means one of replace/connection/auth etc; only Lua)
> > - event (only SQL)
> > - action_time (SQL and Lua have sets of timings which don't intersect)
> > - action (only SQL)
> 
> I need to mention that I didn't followed this outdated proposal in the
> refactoring that we are discussing.
> 
> <name> STRING  (both have)
> <space_id> UINT32 (both have)
> <opts> MAP (non of them for now, legacy, but potentially usefull)
> <language> STR (both have)
> <type> STR (both have; currently 'replace' type only - replace trigger;
>                       perhaps we would like to support some different trigger types)

Strictly speaking, this relates only to NoSQL triggers.

> <event_manipulation> STR  {"DELETE", "UPDATE", "INSERT" --
>                                                SQL only? - but it is possible to support it in Lua also}

How is this supposed to be supported in Lua? For NoSQL triggers only "replace"
event is valid, which obviously is non of delete, update or insert.

> <action_timing> STR {"BEFORE", "AFTER" - both have}

AFTER is not the same as on_replace; SQL also has INSTEAD.

> <code> STR (both have)

You format missing func_id which is required for NoSQL triggers and action
(FOR EACH STMT/ROW) which is valid for SQL only.

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [tarantool-patches] Re: [PATCH v1 0/9] schema: rework _trigger space
  2019-10-16 13:31             ` Nikita Pettik
@ 2019-10-16 13:47               ` Kirill Shcherbatov
  2019-10-16 20:27                 ` Vladislav Shpilevoy
  0 siblings, 1 reply; 31+ messages in thread
From: Kirill Shcherbatov @ 2019-10-16 13:47 UTC (permalink / raw)
  To: tarantool-patches, Nikita Pettik; +Cc: tarantool-patches

> Strictly speaking, this relates only to NoSQL triggers.
> 
>> <event_manipulation> STR  {"DELETE", "UPDATE", "INSERT" --
>>                                                SQL only? - but it is possible to support it in Lua also}
>> How is this supposed to be supported in Lua? For NoSQL triggers only "replace"
> event is valid, which obviously is non of delete, update or insert.
Lua has Lua triggers, right. But they have old and new tuple.
As you know, 
INSERT = REPLACE(old=NULL, new)
DELETE = REPLACE(old, new=NULL)
UPDATE = REPLACE(old, new)

So formally we may use such terminology and support corresponding
trigger_def-based if(s) if we like it.

On the other hand some options could be banned for some engines.

> 
>> <action_timing> STR {"BEFORE", "AFTER" - both have}
> 
> AFTER is not the same as on_replace; SQL also has INSTEAD.
Yes, unfortunately INSTEAD OF is a different thing. And I test it in trigger_def_check
helper.
 
> 
>> <code> STR (both have)
> 
> You format missing func_id which is required for NoSQL triggers and action
> (FOR EACH STMT/ROW) which is valid for SQL only.
We don't have to take a reference to function object by id, it is messy.
Let's better assemble own object using body field. 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space
  2019-10-16 13:13                 ` Nikita Pettik
@ 2019-10-16 14:18                   ` Konstantin Osipov
  0 siblings, 0 replies; 31+ messages in thread
From: Konstantin Osipov @ 2019-10-16 14:18 UTC (permalink / raw)
  To: Nikita Pettik; +Cc: tarantool-patches, tarantool-patches

* Nikita Pettik <korablev@tarantool.org> [19/10/16 16:14]:
> > > > > Okay, let's enumerate all possible properties of _trigger space (I've taken
> > > > > version from the last RFC):
> > > > > 
> > > > > - name (both have)
> > > > 
> > > > If there will be persistent lua triggers, would it be possible to
> > > > drop them from SQL? With what construct? What will be their name
> > > > space (same name space as SQL triggers, or a distinct names space)?
> > > > Case sensitivity? 
> > > 
> > > IMHO there should be no opportunity to drop Lua NoSQL triggers from SQL;
> > > they should share different namespaces (as it happens now). The reason
> > > is that SQL triggers will be implemented as NoSQL on_replace triggers
> > > under the hood.
> > 
> > This is true for non-persistent triggers. What if we ever decide
> > to persist Lua triggers?
> 
> Why there should be any difference between them? Persisted Lua triggers
> have nothing in common with SQL triggers. They are just parsed and re-created
> after restart. Then they behave as normal (in-memory) NoSQL triggers.

Should they be "visible" to SQL? They certainly have effect on SQL
tables. If they are visible, should it be possible to enable,
disable, drop or modify them? If yes, what SQL syntax do you
suggest? If you suggest to use the same syntax as for SQL
triggers, the issue of trigger namespace arises.


-- 
Konstantin Osipov, Moscow, Russia

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [tarantool-patches] Re: [PATCH v1 0/9] schema: rework _trigger space
  2019-10-16 13:47               ` Kirill Shcherbatov
@ 2019-10-16 20:27                 ` Vladislav Shpilevoy
  0 siblings, 0 replies; 31+ messages in thread
From: Vladislav Shpilevoy @ 2019-10-16 20:27 UTC (permalink / raw)
  To: Kirill Shcherbatov, tarantool-patches, Nikita Pettik; +Cc: tarantool-patches

Hi!

JFY, I agree with Kirill and Kostja. Triggers are
triggers, regardless of their language. If something is
not yet supported in box triggers, it will be supported
later. We will have much more trouble with managing two
tables, than in having to validate its tuples more
rigorously. After all, tuple validation will happen just
in alter.cc and only one time. I don't see why is it such
a problem, to just validate a tuple. While having two
tables will affect the whole lifetime of a space.

On 16/10/2019 15:47, Kirill Shcherbatov wrote:
>> Strictly speaking, this relates only to NoSQL triggers.
>>
>>> <event_manipulation> STR  {"DELETE", "UPDATE", "INSERT" --
>>>                                                SQL only? - but it is possible to support it in Lua also}
>>> How is this supposed to be supported in Lua? For NoSQL triggers only "replace"
>> event is valid, which obviously is non of delete, update or insert.
> Lua has Lua triggers, right. But they have old and new tuple.
> As you know, 
> INSERT = REPLACE(old=NULL, new)
> DELETE = REPLACE(old, new=NULL)
> UPDATE = REPLACE(old, new)
> 
> So formally we may use such terminology and support corresponding
> trigger_def-based if(s) if we like it.
> 
> On the other hand some options could be banned for some engines.
> 
>>
>>> <action_timing> STR {"BEFORE", "AFTER" - both have}
>>
>> AFTER is not the same as on_replace; SQL also has INSTEAD.
> Yes, unfortunately INSTEAD OF is a different thing. And I test it in trigger_def_check
> helper.
>  
>>
>>> <code> STR (both have)
>>
>> You format missing func_id which is required for NoSQL triggers and action
>> (FOR EACH STMT/ROW) which is valid for SQL only.
> We don't have to take a reference to function object by id, it is messy.
> Let's better assemble own object using body field. 
> 

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [PATCH v1 2/9] box: rename struct trigger to lua_trigger
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 2/9] box: rename struct trigger to lua_trigger Kirill Shcherbatov
@ 2019-10-17  7:33   ` Konstantin Osipov
  0 siblings, 0 replies; 31+ messages in thread
From: Konstantin Osipov @ 2019-10-17  7:33 UTC (permalink / raw)
  To: Kirill Shcherbatov; +Cc: tarantool-patches, tarantool-patches

* Kirill Shcherbatov <kshcherbatov@tarantool.org> [19/10/14 19:06]:
> Renamed struct trigger to struct lua_trigger because the 'trigger'
> name is too general. Currently Tarantool has non-persistent
> AppServer Lua triggers represented with 'trigger' structure so as
> SQL-specific persistent trigger object named 'sql_trigger'.
> 
> In scope of this patchset we going to significantly rework
> triggers subsystem and introduce a new base trigger class named
> 'trigger', so this name is be better to be released.

this is actually quite unfortunate, the trigger name is general
because it is any kind of trigger, it is not only used in Lua.

Just look at the scope of your patch, it's tree-wide.

I think the old name was good.

Besides, renaming the structure without renaming all of the api to
match is faux pas.
> -#include "trigger.h"
> +#include "lib/core/trigger.h"

-- 
Konstantin Osipov, Moscow, Russia

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [PATCH v1 3/9] box: introduce trigger_event_manipulation enum
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 3/9] box: introduce trigger_event_manipulation enum Kirill Shcherbatov
@ 2019-10-17  7:35   ` Konstantin Osipov
  0 siblings, 0 replies; 31+ messages in thread
From: Konstantin Osipov @ 2019-10-17  7:35 UTC (permalink / raw)
  To: Kirill Shcherbatov; +Cc: tarantool-patches, tarantool-patches

* Kirill Shcherbatov <kshcherbatov@tarantool.org> [19/10/14 19:06]:
> This patch introduces a new trigger_event_manipulation enum
> that describes a trigger event. This is the type of operation on
> the associated space for which the trigger activates.
> The value could be `INSERT` (a row was inserted), `DELETE`
> (a row was deleted), or `UPDATE` (a row was modified).

This is sql_trigger_event if we talk about SQL triggers only, or
box_trigger_event if we talk about any kind of database trigger.

Similarly, if we talk about sql_triggers only, it's
sql_trigger_def. If you wish to define a general-purpose trigger
object, for SQL and Lua, it's box_trigger_def.


-- 
Konstantin Osipov, Moscow, Russia

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [PATCH v1 5/9] sql: use rlist to organize triggers in a list
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 5/9] sql: use rlist to organize triggers in a list Kirill Shcherbatov
@ 2019-10-17  7:36   ` Konstantin Osipov
  0 siblings, 0 replies; 31+ messages in thread
From: Konstantin Osipov @ 2019-10-17  7:36 UTC (permalink / raw)
  To: Kirill Shcherbatov; +Cc: tarantool-patches, tarantool-patches

* Kirill Shcherbatov <kshcherbatov@tarantool.org> [19/10/14 19:06]:
> Using a rlist structure member to organize structures in a list
> is typical practice in the Tarantool core; and reduces costs for
> supporting and extending of an existent code.
> 
> With this refactoring using an universal trigger structure in
> further patches would be simpler.

This is a fine change, but sql_trigger could simply inherit from
struct trigger, and get the swap / add/remove api from it.


-- 
Konstantin Osipov, Moscow, Russia

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [PATCH v1 8/9] sql: inherit sql_trigger from a new trigger class
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 8/9] sql: inherit sql_trigger from a new trigger class Kirill Shcherbatov
@ 2019-10-17  7:38   ` Konstantin Osipov
  0 siblings, 0 replies; 31+ messages in thread
From: Konstantin Osipov @ 2019-10-17  7:38 UTC (permalink / raw)
  To: Kirill Shcherbatov; +Cc: tarantool-patches, tarantool-patches

* Kirill Shcherbatov <kshcherbatov@tarantool.org> [19/10/14 19:07]:
>  struct sql_trigger {
> -	/** The name of the trigger. */
> -	char *zName;
> -	/** The ID of space the trigger refers to. */
> -	uint32_t space_id;
> +	struct trigger base;

I agree with this inheritance path, but it's incomplete

sql_trigger inherits from box_trigger which inherits from
core/trigger.


-- 
Konstantin Osipov, Moscow, Russia

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [Tarantool-patches] [PATCH v1 9/9] schema: rework _trigger system space
  2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 9/9] schema: rework _trigger system space Kirill Shcherbatov
@ 2019-10-17  7:44   ` Konstantin Osipov
  0 siblings, 0 replies; 31+ messages in thread
From: Konstantin Osipov @ 2019-10-17  7:44 UTC (permalink / raw)
  To: Kirill Shcherbatov; +Cc: tarantool-patches, tarantool-patches

* Kirill Shcherbatov <kshcherbatov@tarantool.org> [19/10/14 19:07]:
> This patch reworks a _trigger system space to make it
> useful not only for sql triggers definitions.
> 
> The format of the updated system space is
> 
> _trigger (space id = 328)
> [<name> STRING, <space_id> UINT32, <opts> MAP, <language> STR,
>  <type> STR, <event_manipulation> STR,
>  <action_timing> STR, <code> STR]
> 
> After insertion into this space, a new instance describing trigger
> object is created using language-dependent constructor.
> 
> This volumerous refactoring is an initial step for further
> introduction of persistent triggers in Lua.

I agree with the implementation approach, given that we want
persistent Lua triggers, which we hardly do (but we do want SQL
triggers to be fired on NoSQL events, otherwise PeterG rightfully
would claim that Tarantool SQL violates relational tenets by Codd).

I think you should extract this part into an own series and split
it up - it's the most critical piece of the change.

BTW, I thought of another reason to do triggers in Lua and SQL in
a single space: this is how we dealt with persistent functions,
and it worked well. When we talk about backends in Tarantool, we
should not forget about C backend, and whatever other language it 
allows to bridge with.

-- 
Konstantin Osipov, Moscow, Russia

^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2019-10-17  7:44 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-10-14 16:03 [Tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space Kirill Shcherbatov
2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 1/9] sql: remove redundant pointer in TriggerStep Kirill Shcherbatov
2019-10-15 15:35   ` [Tarantool-patches] [tarantool-patches] " Nikita Pettik
2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 2/9] box: rename struct trigger to lua_trigger Kirill Shcherbatov
2019-10-17  7:33   ` Konstantin Osipov
2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 3/9] box: introduce trigger_event_manipulation enum Kirill Shcherbatov
2019-10-17  7:35   ` Konstantin Osipov
2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 4/9] box: introduce trigger_action_timing enum Kirill Shcherbatov
2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 5/9] sql: use rlist to organize triggers in a list Kirill Shcherbatov
2019-10-17  7:36   ` Konstantin Osipov
2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 6/9] sql: rework CREATE TABLE rule in parser Kirill Shcherbatov
2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 7/9] sql: wrap all ASTs in sql_trigger_expr structure Kirill Shcherbatov
2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 8/9] sql: inherit sql_trigger from a new trigger class Kirill Shcherbatov
2019-10-17  7:38   ` Konstantin Osipov
2019-10-14 16:03 ` [Tarantool-patches] [PATCH v1 9/9] schema: rework _trigger system space Kirill Shcherbatov
2019-10-17  7:44   ` Konstantin Osipov
2019-10-15 21:34 ` [Tarantool-patches] [tarantool-patches] [PATCH v1 0/9] schema: rework _trigger space Nikita Pettik
2019-10-16  5:57   ` Konstantin Osipov
2019-10-16  5:58     ` Konstantin Osipov
2019-10-16 11:07     ` Nikita Pettik
2019-10-16 11:11       ` Konstantin Osipov
2019-10-16 12:18         ` Nikita Pettik
2019-10-16 12:32           ` Konstantin Osipov
2019-10-16 12:47             ` Nikita Pettik
2019-10-16 12:53               ` Konstantin Osipov
2019-10-16 13:13                 ` Nikita Pettik
2019-10-16 14:18                   ` Konstantin Osipov
2019-10-16 12:53           ` [Tarantool-patches] [tarantool-patches] " Kirill Shcherbatov
2019-10-16 13:31             ` Nikita Pettik
2019-10-16 13:47               ` Kirill Shcherbatov
2019-10-16 20:27                 ` Vladislav Shpilevoy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox