[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