[Tarantool-patches] [PATCH v1 4/9] box: introduce trigger_action_timing enum
Kirill Shcherbatov
kshcherbatov at tarantool.org
Mon Oct 14 19:03:19 MSK 2019
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
More information about the Tarantool-patches
mailing list