[Tarantool-patches] [PATCH v1 8/9] sql: inherit sql_trigger from a new trigger class

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


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



More information about the Tarantool-patches mailing list