Skip to content

Commit

Permalink
box: introduce trigger_action_timing enum
Browse files Browse the repository at this point in the history
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
  • Loading branch information
kshcherbatov committed Oct 14, 2019
1 parent 440986c commit 5be4e41
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 59 deletions.
15 changes: 15 additions & 0 deletions src/box/sql.c
Expand Up @@ -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();
}
}
7 changes: 7 additions & 0 deletions src/box/sql.h
Expand Up @@ -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
Expand Down
13 changes: 7 additions & 6 deletions src/box/sql/delete.c
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/box/sql/insert.c
Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
}
Expand Down
9 changes: 6 additions & 3 deletions src/box/sql/parse_def.h
Expand Up @@ -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. */
Expand Down Expand Up @@ -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;
Expand Down
35 changes: 17 additions & 18 deletions src/box/sql/sqlInt.h
Expand Up @@ -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;
/**
Expand All @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -3657,16 +3653,19 @@ 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.
*
* @retval mask value.
*/
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)

Expand Down
43 changes: 23 additions & 20 deletions src/box/sql/trigger.c
Expand Up @@ -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) ||
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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);
Expand All @@ -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 =
Expand All @@ -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);
Expand Down
20 changes: 11 additions & 9 deletions src/box/sql/update.c
Expand Up @@ -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)) {
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions src/box/trigger_def.c
Expand Up @@ -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"};
17 changes: 17 additions & 0 deletions src/box/trigger_def.h
Expand Up @@ -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) */
Expand Down

0 comments on commit 5be4e41

Please sign in to comment.