From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtpng3.m.smailru.net (smtpng3.m.smailru.net [94.100.177.149]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 868F7438E31 for ; Mon, 14 Oct 2019 19:03:32 +0300 (MSK) From: Kirill Shcherbatov Date: Mon, 14 Oct 2019 19:03:23 +0300 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [Tarantool-patches] [PATCH v1 8/9] sql: inherit sql_trigger from a new trigger class List-Id: Tarantool development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: tarantool-patches@freelists.org, tarantool-patches@dev.tarantool.org, kostja.osipov@gmail.com, korablev@tarantool.org 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 #include +#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 ``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 + * 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 + /** * 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