Skip to content

Commit

Permalink
sql: use rlist to organize triggers in a list
Browse files Browse the repository at this point in the history
Using a rlist structure member to organize structures in a list
is typical practice in the Tarantool core; and reduces costs for
supporting and extending of an existent code.

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

Needed for #4343
  • Loading branch information
kshcherbatov committed Oct 14, 2019
1 parent 5be4e41 commit 60d9fb2
Show file tree
Hide file tree
Showing 12 changed files with 130 additions and 124 deletions.
4 changes: 1 addition & 3 deletions src/box/alter.cc
Expand Up @@ -606,9 +606,7 @@ space_swap_triggers(struct space *new_space, struct space *old_space)
rlist_swap(&new_space->before_replace, &old_space->before_replace);
rlist_swap(&new_space->on_replace, &old_space->on_replace);
/** Swap SQL Triggers pointer. */
struct sql_trigger *new_value = new_space->sql_triggers;
new_space->sql_triggers = old_space->sql_triggers;
old_space->sql_triggers = new_value;
rlist_swap(&new_space->trigger_list, &old_space->trigger_list);
}

/** The same as for triggers - swap lists of FK constraints. */
Expand Down
3 changes: 2 additions & 1 deletion src/box/space.c
Expand Up @@ -177,6 +177,7 @@ space_create(struct space *space, struct engine *engine,
rlist_create(&space->parent_fk_constraint);
rlist_create(&space->child_fk_constraint);
rlist_create(&space->ck_constraint);
rlist_create(&space->trigger_list);

/*
* Check if there are unique indexes that are contained
Expand Down Expand Up @@ -261,7 +262,7 @@ space_delete(struct space *space)
* SQL Triggers should be deleted with
* on_replace_dd_trigger on deletion from _trigger.
*/
assert(space->sql_triggers == NULL);
assert(rlist_empty(&space->trigger_list));
assert(rlist_empty(&space->parent_fk_constraint));
assert(rlist_empty(&space->child_fk_constraint));
assert(rlist_empty(&space->ck_constraint));
Expand Down
2 changes: 1 addition & 1 deletion src/box/space.h
Expand Up @@ -163,7 +163,7 @@ struct space {
/** Triggers fired after space_replace() -- see txn_commit_stmt(). */
struct rlist on_replace;
/** SQL Trigger list. */
struct sql_trigger *sql_triggers;
struct rlist trigger_list;
/**
* The number of *enabled* indexes in the space.
*
Expand Down
47 changes: 47 additions & 0 deletions src/box/sql.h
Expand Up @@ -34,6 +34,7 @@
#include <stdbool.h>
#include <stdint.h>
#include "box/trigger_def.h"
#include "small/rlist.h"

#if defined(__cplusplus)
extern "C" {
Expand Down Expand Up @@ -421,6 +422,52 @@ void
vdbe_field_ref_prepare_tuple(struct vdbe_field_ref *field_ref,
struct tuple *tuple);

/**
* Each trigger present in the database schema is stored as an
* instance of struct sql_trigger.
* Pointers to instances of struct sql_trigger are stored in a
* linked list, using the next member of struct sql_trigger. A
* pointer to the first element of the linked list is stored as
* trigger_list member of the associated space.
*
* The "step_list" member points to the first element of a linked
* list containing the SQL statements specified as the trigger
* program.
*/
struct sql_trigger {
/** The name of the trigger. */
char *zName;
/** The ID of space the trigger refers to. */
uint32_t space_id;
/**
* The trigger event. This is the type of operation
* on the associated space for which the trigger
* activates. The value is `INSERT` (a row was inserted),
* `DELETE` (a row was deleted), or `UPDATE` (a row was
* modified).
*/
enum trigger_event_manipulation event_manipulation;
/**
* Whether the trigger activates before or after the
* triggering event. The value is `BEFORE` or `AFTER`.
*/
enum trigger_action_timing action_timing;
/** The WHEN clause of the expression (may be NULL). */
struct Expr *pWhen;
/**
* If this is an UPDATE OF <column-list> trigger,
* the <column-list> is stored here
*/
struct IdList *pColumns;
/** Link list of trigger program steps. */
struct TriggerStep *step_list;
/**
* Organize sql_trigger structs into linked list
* with space::trigger_list.
*/
struct rlist link;
};

/**
* Convert a given OP_INSERT/OP_UPDATE/OP_DELETE operation
* to trigger_event_manipulation value.
Expand Down
6 changes: 2 additions & 4 deletions src/box/sql/build.c
Expand Up @@ -1546,11 +1546,9 @@ sql_code_drop_table(struct Parse *parse_context, struct space *space,
* Do not account triggers deletion - they will be
* accounted in DELETE from _space below.
*/
struct sql_trigger *trigger = space->sql_triggers;
while (trigger != NULL) {
struct sql_trigger *trigger;
rlist_foreach_entry(trigger, &space->trigger_list, link)
vdbe_code_drop_trigger(parse_context, trigger->zName, false);
trigger = trigger->next;
}
/*
* Remove any entries from the _sequence_data, _sequence
* and _space_sequence spaces associated with the table
Expand Down
7 changes: 4 additions & 3 deletions src/box/sql/delete.c
Expand Up @@ -33,6 +33,7 @@
#include "box/schema.h"
#include "sqlInt.h"
#include "tarantoolInt.h"
#include "small/rlist.h"

struct space *
sql_lookup_space(struct Parse *parse, struct SrcList_item *space_name)
Expand Down Expand Up @@ -143,14 +144,14 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
/* Figure out if we have any triggers and if the table
* being deleted from is a view.
*/
struct sql_trigger *trigger_list = NULL;
struct rlist *trigger_list = NULL;
/* True if there are triggers or FKs or subqueries in the
* WHERE clause.
*/
struct space *space = sql_lookup_space(parse, tab_list->a);
if (space == NULL)
goto delete_from_cleanup;
trigger_list = sql_triggers_exist(space->def,
trigger_list = sql_triggers_exist(space,
TRIGGER_EVENT_MANIPULATION_DELETE,
NULL, parse->sql_flags, NULL);
bool is_complex = trigger_list != NULL || fk_constraint_is_required(space, NULL);
Expand Down Expand Up @@ -432,7 +433,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,

void
sql_generate_row_delete(struct Parse *parse, struct space *space,
struct sql_trigger *trigger_list, int cursor,
struct rlist *trigger_list, int cursor,
int reg_pk, short npk, bool need_update_count,
enum on_conflict_action onconf, u8 mode,
int idx_noseek)
Expand Down
1 change: 1 addition & 0 deletions src/box/sql/fk_constraint.c
Expand Up @@ -857,6 +857,7 @@ fk_constraint_action_trigger(struct Parse *pParse, struct space_def *def,
if (trigger != NULL) {
size_t step_size = sizeof(TriggerStep) + name_len + 1;
trigger->step_list = sqlDbMallocZero(db, step_size);
rlist_create(&trigger->link);
step = trigger->step_list;
step->zTarget = (char *) &step[1];
memcpy((char *) step->zTarget, space_name, name_len);
Expand Down
31 changes: 16 additions & 15 deletions src/box/sql/insert.c
Expand Up @@ -257,7 +257,7 @@ sqlInsert(Parse * pParse, /* Parser context */
int regData; /* register holding first column to insert */
int *aRegIdx = 0; /* One register allocated to each index */
/* List of triggers on pTab, if required. */
struct sql_trigger *trigger;
struct rlist *trigger_list;
int tmask; /* Mask of trigger times */

db = pParse->db;
Expand Down Expand Up @@ -293,13 +293,13 @@ sqlInsert(Parse * pParse, /* Parser context */
* inserted into is a view
*/
struct space_def *space_def = space->def;
trigger = sql_triggers_exist(space_def,
TRIGGER_EVENT_MANIPULATION_INSERT, NULL,
pParse->sql_flags, &tmask);
trigger_list = sql_triggers_exist(space,
TRIGGER_EVENT_MANIPULATION_INSERT,
NULL, pParse->sql_flags, &tmask);

bool is_view = space_def->opts.is_view;
assert((trigger != NULL && tmask != 0) ||
(trigger == NULL && tmask == 0));
assert((trigger_list != NULL && tmask != 0) ||
(trigger_list == NULL && tmask == 0));

/* If pTab is really a view, make sure it has been initialized.
* ViewGetColumnNames() is a no-op if pTab is not a view.
Expand All @@ -320,7 +320,7 @@ sqlInsert(Parse * pParse, /* Parser context */
if (v == NULL)
goto insert_cleanup;
sqlVdbeCountChanges(v);
sql_set_multi_write(pParse, pSelect != NULL || trigger != NULL);
sql_set_multi_write(pParse, pSelect != NULL || trigger_list != NULL);

/* If the statement is of the form
*
Expand All @@ -333,7 +333,7 @@ sqlInsert(Parse * pParse, /* Parser context */
*/
if (pColumn == NULL &&
xferOptimization(pParse, space, pSelect, on_error)) {
assert(trigger == NULL);
assert(trigger_list == NULL);
assert(pList == 0);
goto insert_end;
}
Expand Down Expand Up @@ -435,7 +435,8 @@ sqlInsert(Parse * pParse, /* Parser context */
* the SELECT statement. Also use a temp table in
* the case of row triggers.
*/
if (trigger != NULL || vdbe_has_space_read(pParse, space_def))
if (trigger_list != NULL ||
vdbe_has_space_read(pParse, space_def))
useTempTable = 1;

if (useTempTable) {
Expand Down Expand Up @@ -600,7 +601,7 @@ sqlInsert(Parse * pParse, /* Parser context */
sql_emit_table_types(v, space_def, regCols + 1);

/* Fire BEFORE or INSTEAD OF triggers */
vdbe_code_row_trigger(pParse, trigger,
vdbe_code_row_trigger(pParse, trigger_list,
TRIGGER_EVENT_MANIPULATION_INSERT, 0,
TRIGGER_ACTION_TIMING_BEFORE, space,
regCols - space_def->field_count - 1, on_error,
Expand Down Expand Up @@ -753,9 +754,9 @@ sqlInsert(Parse * pParse, /* Parser context */
sqlVdbeAddOp2(v, OP_AddImm, regRowCount, 1);
}

if (trigger != NULL) {
if (trigger_list != NULL) {
/* Code AFTER triggers */
vdbe_code_row_trigger(pParse, trigger,
vdbe_code_row_trigger(pParse, trigger_list,
TRIGGER_EVENT_MANIPULATION_INSERT, 0,
TRIGGER_ACTION_TIMING_AFTER, space,
regData - 2 - space_def->field_count, on_error,
Expand Down Expand Up @@ -963,8 +964,8 @@ process_index: ;
skip_index, idx_key_reg,
part_count);
sql_set_multi_write(parse_context, true);
struct sql_trigger *trigger =
sql_triggers_exist(space->def,
struct rlist *trigger =
sql_triggers_exist(space,
TRIGGER_EVENT_MANIPULATION_DELETE, NULL,
parse_context->sql_flags, NULL);
sql_generate_row_delete(parse_context, space, trigger,
Expand Down Expand Up @@ -1069,7 +1070,7 @@ xferOptimization(Parse * pParse, /* Parser context */
return 0;
}
/* The pDest must not have triggers. */
if (dest->sql_triggers != NULL)
if (!rlist_empty(&dest->trigger_list))
return 0;
if (onError == ON_CONFLICT_ACTION_DEFAULT) {
onError = ON_CONFLICT_ACTION_ABORT;
Expand Down
59 changes: 8 additions & 51 deletions src/box/sql/sqlInt.h
Expand Up @@ -2282,49 +2282,6 @@ struct Parse {
#define OPFLAG_XFER_OPT 0x01
#endif

/*
* Each trigger present in the database schema is stored as an
* instance of struct sql_trigger.
* Pointers to instances of struct sql_trigger are stored in a
* linked list, using the next member of struct sql_trigger. A
* pointer to the first element of the linked list is stored as
* sql_triggers member of the associated space.
*
* The "step_list" member points to the first element of a linked
* list containing the SQL statements specified as the trigger
* program.
*/
struct sql_trigger {
/** The name of the trigger. */
char *zName;
/** The ID of space the trigger refers to. */
uint32_t space_id;
/**
* The trigger event. This is the type of operation
* on the associated space for which the trigger
* activates. The value is `INSERT` (a row was inserted),
* `DELETE` (a row was deleted), or `UPDATE` (a row was
* modified).
*/
enum trigger_event_manipulation event_manipulation;
/**
* Whether the trigger activates before or after the
* triggering event. The value is `BEFORE` or `AFTER`.
*/
enum trigger_action_timing action_timing;
/** The WHEN clause of the expression (may be NULL). */
Expr *pWhen;
/**
* If this is an UPDATE OF <column-list> trigger,
* the <column-list> is stored here
*/
IdList *pColumns;
/** Link list of trigger program steps. */
TriggerStep *step_list;
/** Next trigger associated with the table. */
struct sql_trigger *next;
};

/*
* An instance of struct TriggerStep is used to store a single SQL statement
* that is a part of a trigger-program.
Expand Down Expand Up @@ -3229,7 +3186,7 @@ sql_expr_needs_no_type_change(const struct Expr *expr, enum field_type type);
*/
void
sql_generate_row_delete(struct Parse *parse, struct space *space,
struct sql_trigger *trigger_list, int cursor,
struct rlist *trigger_list, int cursor,
int reg_pk, short npk, bool need_update_count,
enum on_conflict_action onconf, u8 mode,
int idx_noseek);
Expand Down Expand Up @@ -3462,8 +3419,8 @@ vdbe_code_drop_trigger(struct Parse *parser, const char *trigger_name,
* @param sql_flags SQL flags which describe how to parse request.
* @param[out] pMask Mask of TRIGGER_BEFORE|TRIGGER_AFTER
*/
struct sql_trigger *
sql_triggers_exist(struct space_def *space_def,
struct rlist *
sql_triggers_exist(struct space *space,
enum trigger_event_manipulation event_manipulation,
struct ExprList *changes_list, uint32_t sql_flags,
int *mask_ptr);
Expand Down Expand Up @@ -3512,7 +3469,7 @@ sql_triggers_exist(struct space_def *space_def,
* jump to if a trigger program raises an IGNORE exception.
*
* @param parser Parse context.
* @param trigger List of triggers on table.
* @param trigger_list List of triggers on table.
* @param event_manipulation Trigger operation.
* @param changes_list Changes list for any UPDATE OF triggers.
* @param action_timing Whether the trigger activates before or
Expand All @@ -3523,7 +3480,7 @@ sql_triggers_exist(struct space_def *space_def,
* @param ignore_jump Instruction to jump to for RAISE(IGNORE).
*/
void
vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
vdbe_code_row_trigger(struct Parse *parser, struct rlist *trigger_list,
enum trigger_event_manipulation event_manipulation,
struct ExprList *changes_list,
enum trigger_action_timing action_timing,
Expand All @@ -3537,7 +3494,7 @@ vdbe_code_row_trigger(struct Parse *parser, struct sql_trigger *trigger,
* header function for sql_code_row_trigger().
*
* @param parser Parse context.
* @param trigger Trigger to code.
* @param trigger_list Trigger to code.
* @param space The space to code triggers from.
* @param reg Reg array containing OLD.* and NEW.* values.
* @param orconf ON CONFLICT policy.
Expand Down Expand Up @@ -3650,7 +3607,7 @@ sql_trigger_delete_step(struct sql *db, struct Token *table_name,
* returned mask if the TRIGGER_AFTER bit is set in tr_tm.
*
* @param parser Parse context.
* @param trigger List of triggers on table.
* @param trigger_list List of triggers on table.
* @param changes_list Changes list for any UPDATE OF triggers.
* @param new 1 for new.* ref mask, 0 for old.* ref mask.
* @param action_timing_mask Mask of action timings referenced in
Expand All @@ -3661,7 +3618,7 @@ sql_trigger_delete_step(struct sql *db, struct Token *table_name,
* @retval mask value.
*/
uint64_t
sql_trigger_colmask(Parse *parser, struct sql_trigger *trigger,
sql_trigger_colmask(struct Parse *parser, struct rlist *trigger_list,
struct ExprList *changes_list, int new,
uint8_t action_timing_mask, struct space *space,
int orconf);
Expand Down

0 comments on commit 60d9fb2

Please sign in to comment.