From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id EE91B287ED for ; Thu, 31 May 2018 07:23:00 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id erKjsqWd38sY for ; Thu, 31 May 2018 07:23:00 -0400 (EDT) Received: from smtpng2.m.smailru.net (smtpng2.m.smailru.net [94.100.179.3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id 7760D2850C for ; Thu, 31 May 2018 07:23:00 -0400 (EDT) From: Kirill Shcherbatov Subject: [tarantool-patches] [PATCH v1 4/4] sql: move Triggers to server Date: Thu, 31 May 2018 14:22:53 +0300 Message-Id: In-Reply-To: References: In-Reply-To: References: Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-help: List-unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-subscribe: List-owner: List-post: List-archive: To: tarantool-patches@freelists.org Cc: v.shpilevoy@tarantool.org, Kirill Shcherbatov Introduced sql_triggers field in space structure. Changed parser logic to do not insert builded triggers, just only to do parsing. All triggers insertions and deletions are operated via on_replace_dd_trigger now. Resolves #3273. --- src/box/alter.cc | 57 +++++++++++ src/box/space.h | 2 + src/box/sql.c | 48 ++------- src/box/sql.h | 62 ++++++++++++ src/box/sql/build.c | 8 +- src/box/sql/insert.c | 6 +- src/box/sql/sqliteInt.h | 2 - src/box/sql/tokenize.c | 43 +++++++- src/box/sql/trigger.c | 258 ++++++++++++++++++++++++++++-------------------- src/box/sql/vdbe.c | 58 +++-------- src/box/sql/vdbe.h | 1 - src/box/sql/vdbeaux.c | 9 -- 12 files changed, 343 insertions(+), 211 deletions(-) diff --git a/src/box/alter.cc b/src/box/alter.cc index b62f8ad..6e4f15d 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -53,6 +53,7 @@ #include "version.h" #include "sequence.h" #include "sql.h" +#include "box.h" /** * chap-sha1 of empty string, i.e. @@ -551,6 +552,9 @@ 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); rlist_swap(&new_space->on_stmt_begin, &old_space->on_stmt_begin); + /** Copy SQL Triggers pointer. */ + new_space->sql_triggers = old_space->sql_triggers; + old_space->sql_triggers = NULL; } /** @@ -733,6 +737,20 @@ alter_space_commit(struct trigger *trigger, void *event) trigger_run_xc(&on_alter_space, alter->new_space); + if (alter->new_space->sql_triggers != NULL && + strcmp(alter->new_space->def->name, + alter->old_space->def->name) != 0) { + /* + * The function below either changes the name of + * all triggers, or does not change any of them. + * It should be last action in alter_space_commit + * as it is difficult to guarantee its rollback. + */ + if (sql_trigger_list_alter_table_name_transactional( + alter->new_space->sql_triggers, + alter->new_space->def->name) != 0) + diag_raise(); + } alter->new_space = NULL; /* for alter_space_delete(). */ /* * Delete the old version of the space, we are not @@ -3100,6 +3118,45 @@ on_replace_dd_trigger(struct trigger * /* trigger */, void *event) { struct txn *txn = (struct txn *) event; txn_check_singlestatement_xc(txn, "Space _trigger"); + + struct txn_stmt *stmt = txn_current_stmt(txn); + if (stmt->old_tuple != NULL) { + uint32_t trigger_name_len; + const char *trigger_name_src = + tuple_field_str_xc(stmt->old_tuple, 0, + &trigger_name_len); + /* Can't use static buff as TT_STATIC_BUF_LEN + * is not enough for identifier max len test. */ + char *trigger_name = + (char *)region_alloc_xc(&fiber()->gc, trigger_name_len); + sprintf(trigger_name, "%.*s", trigger_name_len, + trigger_name_src); + if (sql_trigger_delete_by_name(sql_get(), trigger_name) != 0) + diag_raise(); + } + if (stmt->new_tuple != NULL) { + const char *space_opts = + tuple_field_with_type_xc(stmt->new_tuple, 1, MP_MAP); + struct space_opts opts; + struct region *region = &fiber()->gc; + space_opts_decode(&opts, space_opts, region); + + struct Trigger *trigger; + if (sql_trigger_compile(sql_get(), opts.sql, &trigger) != 0) + diag_raise(); + assert(trigger != NULL); + const char *table_name = + sql_trigger_get_table_name(trigger); + if (table_name == NULL) + diag_raise(); + uint32_t space_id = + space_id_by_name(BOX_SPACE_ID, table_name, + strlen(table_name)); + if (space_id == BOX_ID_NIL) + diag_raise(); + if (sql_trigger_insert(space_id, trigger) != 0) + diag_raise(); + } } struct trigger alter_space_on_replace_space = { diff --git a/src/box/space.h b/src/box/space.h index b8d29ca..0413cd0 100644 --- a/src/box/space.h +++ b/src/box/space.h @@ -146,6 +146,8 @@ struct space { struct rlist on_replace; /** Triggers fired before space statement */ struct rlist on_stmt_begin; + /** SQL Trigger list. */ + struct Trigger *sql_triggers; /** * The number of *enabled* indexes in the space. * diff --git a/src/box/sql.c b/src/box/sql.c index eacb288..0ff6e73 100644 --- a/src/box/sql.c +++ b/src/box/sql.c @@ -1219,9 +1219,6 @@ space_foreach_put_cb(struct space *space, void *udata) /* Load database schema from Tarantool. */ void tarantoolSqlite3LoadSchema(InitData *init) { - box_iterator_t *it; - box_tuple_t *tuple; - sql_schema_put( init, TARANTOOL_SYS_SCHEMA_NAME, BOX_SCHEMA_ID, 0, @@ -1290,42 +1287,6 @@ void tarantoolSqlite3LoadSchema(InitData *init) init->rc = SQL_TARANTOOL_ERROR; return; } - - /* Read _trigger */ - it = box_index_iterator(BOX_TRIGGER_ID, 0, ITER_GE, - nil_key, nil_key + sizeof(nil_key)); - - if (it == NULL) { - init->rc = SQL_TARANTOOL_ITERATOR_FAIL; - return; - } - - while (box_iterator_next(it, &tuple) == 0 && tuple != NULL) { - const char *field, *ptr; - char *name, *sql; - unsigned len; - assert(tuple_field_count(tuple) == 2); - - field = tuple_field(tuple, 0); - assert (field != NULL); - ptr = mp_decode_str(&field, &len); - name = strndup(ptr, len); - - field = tuple_field(tuple, 1); - assert (field != NULL); - mp_decode_array(&field); - ptr = mp_decode_str(&field, &len); - assert (strncmp(ptr, "sql", 3) == 0); - - ptr = mp_decode_str(&field, &len); - sql = strndup(ptr, len); - - sql_schema_put(init, name, 0, 0, sql); - - free(name); - free(sql); - } - box_iterator_free(it); } /********************************************************************* @@ -1733,6 +1694,15 @@ space_column_default_expr(uint32_t space_id, uint32_t fieldno) return space->def->fields[fieldno].default_value_expr; } +struct Trigger * +space_trigger_list(uint32_t space_id) +{ + struct space *space = space_cache_find(space_id); + assert(space != NULL); + assert(space->def != NULL); + return space->sql_triggers; +} + struct space_def * sql_ephemeral_space_def_new(Parse *parser, const char *name) { diff --git a/src/box/sql.h b/src/box/sql.h index 23021e5..f21b745 100644 --- a/src/box/sql.h +++ b/src/box/sql.h @@ -66,6 +66,7 @@ struct Expr; struct Parse; struct Select; struct Table; +struct Trigger; /** * Perform parsing of provided expression. This is done by @@ -84,6 +85,67 @@ sql_expr_compile(struct sqlite3 *db, const char *expr, int expr_len, struct Expr **result); /** + * Perform parsing of provided SQL request and construct trigger AST. + * @param db SQL context handle. + * @param sql request to parse. + * @param[out] trigger Result: AST structure. + * + * @retval Error code if any. + */ +int +sql_trigger_compile(struct sqlite3 *db, const char *sql, + struct Trigger **trigger); + +/** + * Remove a trigger from the hash tables of the sqlite* pointer. + * @param db SQL handle. + * @param trigger_name to delete. + * + * @retval Error code if any. + */ +int +sql_trigger_delete_by_name(struct sqlite3 *db, const char *trigger_name); + +/** + * Get server triggers list by space_id. + * @param space_id Space ID. + * @retval NULL on error. + * @param not NULL on success. + */ +struct Trigger * +space_trigger_list(uint32_t space_id); + +/** + * Perform insert trigger in appropriate space. + * @param space_id id of the space to append trigger. + * @param trigger object to append. + * + * @retval Error code if any. + */ +int +sql_trigger_insert(uint32_t space_id, struct Trigger *trigger); + +/** + * Get table name of specified trigger. + * @param trigger containing a table name. + * @param new_table_name with new_name (optional). + * @retval table name string. + */ +const char * +sql_trigger_get_table_name(struct Trigger *trigger); + +/** + * Rename specified trigger list. + * @param trigger containing a table name. + * @param new_table_name with new_name (optional). + * + * @retval Error code if any. + */ +int +sql_trigger_list_alter_table_name_transactional(struct Trigger *trigger, + const char *new_table_name); + +/** * Store duplicate of a parsed expression into @a parser. * @param parser Parser context. * @param select Select to extract from. diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 9cd1cde..475d8aa 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -2289,16 +2289,14 @@ sql_code_drop_table(struct Parse *parse_context, struct space *space, /* * Drop all triggers associated with the table being * dropped. Code is generated to remove entries from - * _trigger. OP_DropTrigger will remove it from internal - * SQL structures. + * _trigger. on_replace_dd_trigger will remove it from + * internal SQL structures. * * Do not account triggers deletion - they will be * accounted in DELETE from _space below. */ parse_context->nested++; - Table *table = sqlite3HashFind(&parse_context->db->pSchema->tblHash, - space->def->name); - struct Trigger *trigger = table->pTrigger; + struct Trigger *trigger = space->sql_triggers; while (trigger != NULL) { sqlite3DropTriggerPtr(parse_context, trigger); trigger = trigger->pNext; diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c index 59c61c7..023e6b0 100644 --- a/src/box/sql/insert.c +++ b/src/box/sql/insert.c @@ -1766,9 +1766,9 @@ xferOptimization(Parse * pParse, /* Parser context */ */ return 0; } - if (pDest->pTrigger) { - return 0; /* tab1 must not have triggers */ - } + /* The pDest must not have triggers. */ + if (space_trigger_list(pDest->def->id) != NULL) + return 0; if (onError == ON_CONFLICT_ACTION_DEFAULT) { if (pDest->iPKey >= 0) onError = pDest->keyConf; diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h index b23cb1a..39393c5 100644 --- a/src/box/sql/sqliteInt.h +++ b/src/box/sql/sqliteInt.h @@ -1935,7 +1935,6 @@ struct Table { #ifndef SQLITE_OMIT_ALTERTABLE int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ #endif - Trigger *pTrigger; /* List of triggers stored in pSchema */ Schema *pSchema; /* Schema that contains this table */ Table *pNextZombie; /* Next on the Parse.pZombieTab list */ /** Space definition with Tarantool metadata. */ @@ -4028,7 +4027,6 @@ TriggerStep *sqlite3TriggerUpdateStep(sqlite3 *, Token *, ExprList *, Expr *, u8); TriggerStep *sqlite3TriggerDeleteStep(sqlite3 *, Token *, Expr *); void sqlite3DeleteTrigger(sqlite3 *, Trigger *); -void sqlite3UnlinkAndDeleteTrigger(sqlite3 *, const char *); u32 sqlite3TriggerColmask(Parse *, Trigger *, ExprList *, int, int, Table *, int); #define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p)) diff --git a/src/box/sql/tokenize.c b/src/box/sql/tokenize.c index 59df75f..24db578 100644 --- a/src/box/sql/tokenize.c +++ b/src/box/sql/tokenize.c @@ -39,6 +39,7 @@ #include #include #include +#include "box/schema.h" #include "say.h" #include "sqliteInt.h" @@ -528,7 +529,10 @@ sqlite3RunParser(Parse * pParse, const char *zSql, char **pzErrMsg) if (pParse->pWithToFree) sqlite3WithDelete(db, pParse->pWithToFree); - sqlite3DeleteTrigger(db, pParse->pNewTrigger); + /* Trigger is exporting with pNewTrigger field when + * parse_only flag is set. */ + if (!pParse->parse_only) + sqlite3DeleteTrigger(db, pParse->pNewTrigger); sqlite3DbFree(db, pParse->pVList); while (pParse->pZombieTab) { Table *p = pParse->pZombieTab; @@ -564,3 +568,40 @@ sql_expr_compile(sqlite3 *db, const char *expr, int expr_len, sql_parser_destroy(&parser); return 0; } + +int +sql_trigger_compile(struct sqlite3 *db, const char *sql, + struct Trigger **trigger) +{ + struct Parse parser; + sql_parser_create(&parser, db); + parser.parse_only = true; + char *unused; + if (sqlite3RunParser(&parser, sql, &unused) != SQLITE_OK) { + diag_set(ClientError, ER_SQL_EXECUTE, sql); + return -1; + } + *trigger = parser.pNewTrigger; + sql_parser_destroy(&parser); + return 0; +} + + +int +sql_trigger_insert(uint32_t space_id, struct Trigger *trigger) +{ + struct space *space = space_cache_find(space_id); + assert(space != NULL); + struct Hash *pHash = &trigger->pSchema->trigHash; + char *zName = trigger->zName; + void *ret = sqlite3HashInsert(pHash, zName, trigger); + if (ret != NULL) { + /* Triggers couldn't present in hash. + * So this is definitely a memory error. */ + diag_set(OutOfMemory, 0, "sqlite3HashInsert", "hash"); + return -1; + } + trigger->pNext = space->sql_triggers; + space->sql_triggers = trigger; + return 0; +} diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c index ea35211..00f161e 100644 --- a/src/box/sql/trigger.c +++ b/src/box/sql/trigger.c @@ -33,6 +33,9 @@ * This file contains the implementation for TRIGGERs */ +#include "box/box.h" +#include "box/tuple.h" +#include "box/schema.h" #include "sqliteInt.h" #include "tarantoolInt.h" #include "vdbeInt.h" @@ -81,7 +84,8 @@ sqlite3BeginTrigger(Parse * pParse, /* The parse context of the CREATE TRIGGER s ) { Trigger *pTrigger = 0; /* The new trigger */ - Table *pTab; /* Table that the trigger fires off of */ + /* Table that the trigger fires off of. */ + struct Table *table = NULL; char *zName = 0; /* Name of the trigger */ sqlite3 *db = pParse->db; /* The database connection */ DbFixer sFix; /* State vector for the DB fixer */ @@ -99,60 +103,65 @@ sqlite3BeginTrigger(Parse * pParse, /* The parse context of the CREATE TRIGGER s assert(op == TK_INSERT || op == TK_UPDATE || op == TK_DELETE); assert(op > 0 && op < 0xff); - if (!pTableName || db->mallocFailed) { + if (!pTableName || db->mallocFailed) goto trigger_cleanup; - } /* Ensure the table name matches database name and that the table exists */ if (db->mallocFailed) goto trigger_cleanup; assert(pTableName->nSrc == 1); sqlite3FixInit(&sFix, pParse, "trigger", pName); - if (sqlite3FixSrcList(&sFix, pTableName)) { - goto trigger_cleanup; - } - pTab = sql_list_lookup_table(pParse, pTableName); - if (!pTab) { + if (sqlite3FixSrcList(&sFix, pTableName)) goto trigger_cleanup; - } - /* Check that the trigger name is not reserved and that no trigger of the - * specified name exists - */ zName = sqlite3NameFromToken(db, pName); - if (!zName || SQLITE_OK != sqlite3CheckIdentifierName(pParse, zName)) { + if (zName == NULL) goto trigger_cleanup; - } - if (sqlite3HashFind(&(db->pSchema->trigHash), zName)) { - if (!noErr) { - sqlite3ErrorMsg(pParse, "trigger %s already exists", - zName); - } else { - assert(!db->init.busy); + + /* FIXME: Move all checks in VDBE #3435. */ + if (!pParse->parse_only) { + if (sqlite3CheckIdentifierName(pParse, zName) != SQLITE_OK) + goto trigger_cleanup; + + table = sql_list_lookup_table(pParse, pTableName); + if (table == NULL) + goto trigger_cleanup; + + if (sqlite3HashFind(&(db->pSchema->trigHash), zName)) { + if (!noErr) { + sqlite3ErrorMsg(pParse, + "trigger %s already exists", + zName); + } else { + assert(!db->init.busy); + } + goto trigger_cleanup; } - goto trigger_cleanup; - } - /* Do not create a trigger on a system table */ - if (sqlite3StrNICmp(pTab->def->name, "sqlite_", 7) == 0) { - sqlite3ErrorMsg(pParse, - "cannot create trigger on system table"); - goto trigger_cleanup; - } + /* Do not create a trigger on a system table */ + if (sqlite3StrNICmp(table->def->name, "sqlite_", 7) == 0) { + sqlite3ErrorMsg(pParse, + "cannot create trigger on system table"); + goto trigger_cleanup; + } - /* INSTEAD of triggers are only for views and views only support INSTEAD - * of triggers. - */ - if (space_is_view(pTab) && tr_tm != TK_INSTEAD) { - sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", - (tr_tm == TK_BEFORE) ? "BEFORE" : "AFTER", - pTableName, 0); - goto trigger_cleanup; - } - if (!space_is_view(pTab) && tr_tm == TK_INSTEAD) { - sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF" - " trigger on table: %S", pTableName, 0); - goto trigger_cleanup; + /* INSTEAD of triggers are only for views and + * views only support INSTEAD of triggers. + */ + if (table->def->opts.is_view && tr_tm != TK_INSTEAD) { + sqlite3ErrorMsg(pParse, + "cannot create %s trigger on view: %S", + (tr_tm == TK_BEFORE) ? + "BEFORE" : "AFTER", + pTableName, 0); + goto trigger_cleanup; + } + if (!table->def->opts.is_view && tr_tm == TK_INSTEAD) { + sqlite3ErrorMsg(pParse, + "cannot create INSTEAD OF trigger " + "on table: %S", pTableName, 0); + goto trigger_cleanup; + } } /* INSTEAD OF triggers can only appear on views and BEFORE triggers @@ -160,9 +169,8 @@ sqlite3BeginTrigger(Parse * pParse, /* The parse context of the CREATE TRIGGER s * INSTEAD OF trigger into a BEFORE trigger. It simplifies code * elsewhere. */ - if (tr_tm == TK_INSTEAD) { + if (tr_tm == TK_INSTEAD) tr_tm = TK_BEFORE; - } /* Build the Trigger object */ pTrigger = (Trigger *) sqlite3DbMallocZero(db, sizeof(Trigger)); @@ -170,13 +178,22 @@ sqlite3BeginTrigger(Parse * pParse, /* The parse context of the CREATE TRIGGER s goto trigger_cleanup; pTrigger->zName = zName; zName = 0; - pTrigger->table = sqlite3DbStrDup(db, pTableName->a[0].zName); + pTrigger->table = strdup(pTableName->a[0].zName); + if (pTrigger->table == NULL) { + diag_set(OutOfMemory, strlen(pTableName->a[0].zName), "strdup", + "pTrigger->table"); + pParse->rc = SQL_TARANTOOL_ERROR; + goto trigger_cleanup; + } pTrigger->pSchema = db->pSchema; - pTrigger->pTabSchema = pTab->pSchema; + pTrigger->pTabSchema = db->pSchema; pTrigger->op = (u8) op; pTrigger->tr_tm = tr_tm == TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER; pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE); pTrigger->pColumns = sqlite3IdListDup(db, pColumns); + if ((pWhen != NULL && pTrigger->pWhen == NULL) || + (pColumns != NULL && pTrigger->pColumns == NULL)) + goto trigger_cleanup; assert(pParse->pNewTrigger == 0); pParse->pNewTrigger = pTrigger; @@ -210,7 +227,7 @@ sqlite3FinishTrigger(Parse * pParse, /* Parser context */ DbFixer sFix; /* Fixer object */ Token nameToken; /* Trigger name for error reporting */ - pParse->pNewTrigger = 0; + pParse->pNewTrigger = NULL; if (NEVER(pParse->nErr) || !pTrig) goto triggerfinish_cleanup; zName = pTrig->zName; @@ -227,10 +244,9 @@ sqlite3FinishTrigger(Parse * pParse, /* Parser context */ goto triggerfinish_cleanup; } - /* if we are not initializing, - * generate byte code to insert a new trigger into Tarantool. - */ - if (!db->init.busy) { + /* Generate byte code to insert a new trigger into + * Tarantool for non-parsig mode or export trigger. */ + if (!pParse->parse_only) { Vdbe *v; int zOptsSz; Table *pSysTrigger; @@ -293,32 +309,10 @@ sqlite3FinishTrigger(Parse * pParse, /* Parser context */ pParse->nMem += 2; sql_set_multi_write(pParse, false); - sqlite3VdbeAddOp4(v, - OP_String8, 0, iFirstCol, 0, - zName, P4_STATIC); - - sqlite3VdbeAddOp4(v, - OP_String8, 0, iFirstCol + 1, 0, - zSql, P4_DYNAMIC); sqlite3ChangeCookie(pParse); - sqlite3VdbeAddParseSchema3Op(v, iFirstCol); - } - - if (db->init.busy) { - Trigger *pLink = pTrig; - Hash *pHash = &db->pSchema->trigHash; - pTrig = sqlite3HashInsert(pHash, zName, pTrig); - if (pTrig) { - sqlite3OomFault(db); - } else if (pLink->pSchema == pLink->pTabSchema) { - Table *pTab; - pTab = - sqlite3HashFind(&pLink->pTabSchema->tblHash, - pLink->table); - assert(pTab != 0); - pLink->pNext = pTab->pTrigger; - pTab->pTrigger = pLink; - } + } else { + pParse->pNewTrigger = pTrig; + pTrig = NULL; } triggerfinish_cleanup: @@ -329,7 +323,7 @@ sqlite3FinishTrigger(Parse * pParse, /* Parser context */ alloc for it either wasn't called at all or failed. */ } sqlite3DeleteTrigger(db, pTrig); - assert(!pParse->pNewTrigger); + assert(!pParse->pNewTrigger || pParse->parse_only); sqlite3DeleteTriggerStep(db, pStepList); } @@ -481,7 +475,7 @@ sqlite3DeleteTrigger(sqlite3 * db, Trigger * pTrigger) return; sqlite3DeleteTriggerStep(db, pTrigger->step_list); sqlite3DbFree(db, pTrigger->zName); - sqlite3DbFree(db, pTrigger->table); + free(pTrigger->table); sql_expr_delete(db, pTrigger->pWhen, false); sqlite3IdListDelete(db, pTrigger->pColumns); sqlite3DbFree(db, pTrigger); @@ -568,34 +562,35 @@ sqlite3DropTriggerPtr(Parse * pParse, Trigger * pTrigger) sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE); sqlite3ChangeCookie(pParse); - sqlite3VdbeAddOp4(v, OP_DropTrigger, 0, 0, 0, pTrigger->zName, - 0); } } -/* - * Remove a trigger from the hash tables of the sqlite* pointer. - */ -void -sqlite3UnlinkAndDeleteTrigger(sqlite3 * db, const char *zName) +int +sql_trigger_delete_by_name(struct sqlite3 *db, const char *trigger_name) { - Trigger *pTrigger; - Hash *pHash; struct session *user_session = current_session(); - pHash = &(db->pSchema->trigHash); - pTrigger = sqlite3HashInsert(pHash, zName, 0); - if (ALWAYS(pTrigger)) { - if (pTrigger->pSchema == pTrigger->pTabSchema) { - Table *pTab = tableOfTrigger(pTrigger); - Trigger **pp; - for (pp = &pTab->pTrigger; *pp != pTrigger; - pp = &((*pp)->pNext)) ; + struct Hash *hash = &(db->pSchema->trigHash); + struct Trigger *trigger = sqlite3HashInsert(hash, trigger_name, NULL); + assert(trigger != NULL); + + if (trigger->pSchema == trigger->pTabSchema) { + uint32_t space_id = space_id_by_name(BOX_SPACE_ID, + trigger->table, strlen(trigger->table)); + struct space *space = space_by_id(space_id); + /* Space could be already deleted. */ + if (space != NULL) { + struct Trigger **pp; + for (pp = &space->sql_triggers; + *pp != trigger; + pp = &((*pp)->pNext)); *pp = (*pp)->pNext; } - sqlite3DeleteTrigger(db, pTrigger); - user_session->sql_flags |= SQLITE_InternChanges; } + + sqlite3DeleteTrigger(db, trigger); + user_session->sql_flags |= SQLITE_InternChanges; + return 0; } /* @@ -634,22 +629,18 @@ sqlite3TriggersExist(Table * pTab, /* The table the contains the triggers */ ) { int mask = 0; - Trigger *pList = 0; - Trigger *p; + struct Trigger *trigger_list = NULL; struct session *user_session = current_session(); - - if ((user_session->sql_flags & SQLITE_EnableTrigger) != 0) { - pList = pTab->pTrigger; - } - for (p = pList; p; p = p->pNext) { - if (p->op == op && checkColumnOverlap(p->pColumns, pChanges)) { + if ((user_session->sql_flags & SQLITE_EnableTrigger) != 0) + trigger_list = space_trigger_list(pTab->def->id); + struct Trigger *p; + for (p = trigger_list; p; p = p->pNext) { + if (p->op == op && checkColumnOverlap(p->pColumns, pChanges)) mask |= p->tr_tm; - } } - if (pMask) { + if (pMask) *pMask = mask; - } - return (mask ? pList : 0); + return (mask ? trigger_list : 0); } /* @@ -1155,4 +1146,55 @@ sqlite3TriggerColmask(Parse * pParse, /* Parse context */ return mask; } +const char * +sql_trigger_get_table_name(struct Trigger *trigger) +{ + return trigger->table; +} + +int +sql_trigger_list_alter_table_name_transactional(struct Trigger *trigger, + const char *new_table_name) +{ + struct names_list_t { + char *name; + struct names_list_t *next; + } *names_list = NULL; + + int rc = 0; + for (struct Trigger *t = trigger; t != NULL; t = t->pNext) { + struct names_list_t *node = + region_alloc(&fiber()->gc, sizeof(*node)); + if (rc |= (node == NULL)) { + diag_set(OutOfMemory, sizeof(*node), "region_alloc", + "node"); + break; + } + node->name = strdup(new_table_name); + if (rc |= (node->name == NULL)) { + diag_set(OutOfMemory, strlen(new_table_name), "strdup", + "node->name"); + break; + } + node->next = names_list; + names_list = node; + } + if (rc != 0) + goto error; + + for (struct Trigger *t = trigger; t != NULL; t = t->pNext) { + free(t->table); + t->table = names_list->name; + names_list = names_list->next; + } + return 0; + +error: + while (names_list != NULL) { + free(names_list->name); + names_list = names_list->next; + } + return -1; +} + #endif /* !defined(SQLITE_OMIT_TRIGGER) */ diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index 3fe5875..f60c122 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -4693,36 +4693,7 @@ case OP_ParseSchema2: { * in database P2 */ case OP_ParseSchema3: { - InitData initData; - Mem *pRec; - char zPgnoBuf[16]; - char *argv[4] = {NULL, zPgnoBuf, NULL, NULL}; - assert(db->pSchema != NULL); - - initData.db = db; - initData.pzErrMsg = &p->zErrMsg; - - assert(db->init.busy==0); - db->init.busy = 1; - initData.rc = SQLITE_OK; - assert(!db->mallocFailed); - - pRec = &aMem[pOp->p1]; - argv[0] = pRec[0].z; - argv[1] = "0"; - argv[2] = pRec[1].z; - sqlite3InitCallback(&initData, 3, argv, NULL); - - rc = initData.rc; - db->init.busy = 0; - - if (rc) { - sqlite3ResetAllSchemasOfConnection(db); - if (rc==SQLITE_NOMEM) { - goto no_mem; - } - goto abort_due_to_error; - } + unreachable(); break; } @@ -4745,7 +4716,6 @@ case OP_RenameTable: { const char *zNewTableName; Table *pTab; FKey *pFKey; - Trigger *pTrig; int iRootPage; InitData initData; char *argv[4] = {NULL, NULL, NULL, NULL}; @@ -4758,11 +4728,11 @@ case OP_RenameTable: { assert(zOldTableName); pTab = sqlite3HashFind(&db->pSchema->tblHash, zOldTableName); assert(pTab); - pTrig = pTab->pTrigger; iRootPage = pTab->tnum; zNewTableName = pOp->p4.z; zOldTableName = sqlite3DbStrNDup(db, zOldTableName, sqlite3Strlen30(zOldTableName)); + rc = tarantoolSqlite3RenameTable(pTab->tnum, zNewTableName, &zSqlStmt); if (rc) goto abort_due_to_error; @@ -4799,19 +4769,21 @@ case OP_RenameTable: { goto abort_due_to_error; } - pTab = sqlite3HashFind(&db->pSchema->tblHash, zNewTableName); - pTab->pTrigger = pTrig; + space = space_by_id(space_id); + assert(space != NULL); - /* Rename all trigger created on this table.*/ - for (; pTrig; pTrig = pTrig->pNext) { - sqlite3DbFree(db, pTrig->table); - pTrig->table = sqlite3DbStrNDup(db, zNewTableName, - sqlite3Strlen30(zNewTableName)); - pTrig->pTabSchema = pTab->pSchema; - rc = tarantoolSqlite3RenameTrigger(pTrig->zName, + /* Rename all trigger created on this table. */ + for (struct Trigger *trigger = space->sql_triggers; trigger != NULL; ) { + struct Trigger *next_trigger = trigger->pNext; + rc = tarantoolSqlite3RenameTrigger(trigger->zName, zOldTableName, zNewTableName); - if (rc) goto abort_due_to_error; + if (rc != SQLITE_OK) { + sqlite3ResetAllSchemasOfConnection(db); + goto abort_due_to_error; + } + trigger = next_trigger; } + sqlite3DbFree(db, (void*)zOldTableName); sqlite3DbFree(db, (void*)zSqlStmt); break; @@ -4866,7 +4838,7 @@ case OP_DropIndex: { * schema consistent with what is on disk. */ case OP_DropTrigger: { - sqlite3UnlinkAndDeleteTrigger(db, pOp->p4.z); + unreachable(); break; } #ifndef SQLITE_OMIT_TRIGGER diff --git a/src/box/sql/vdbe.h b/src/box/sql/vdbe.h index 68d542c..77fa41a 100644 --- a/src/box/sql/vdbe.h +++ b/src/box/sql/vdbe.h @@ -238,7 +238,6 @@ void sqlite3VdbeVerifyNoResultRow(Vdbe * p); VdbeOp *sqlite3VdbeAddOpList(Vdbe *, int nOp, VdbeOpList const *aOp, int iLineno); void sqlite3VdbeAddParseSchema2Op(Vdbe * p, int, int); -void sqlite3VdbeAddParseSchema3Op(Vdbe * p, int); void sqlite3VdbeAddRenameTableOp(Vdbe * p, int, char *); void sqlite3VdbeChangeOpcode(Vdbe *, u32 addr, u8); void sqlite3VdbeChangeP1(Vdbe *, u32 addr, int P1); diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c index 63f1978..7a5ac6f 100644 --- a/src/box/sql/vdbeaux.c +++ b/src/box/sql/vdbeaux.c @@ -410,15 +410,6 @@ sqlite3VdbeAddParseSchema2Op(Vdbe * p, int iRec, int n) sqlite3VdbeAddOp3(p, OP_ParseSchema2, iRec, n, 0); } -/* - * Add an OP_ParseSchema3 opcode which in turn will create a trigger - */ -void -sqlite3VdbeAddParseSchema3Op(Vdbe * p, int iRec) -{ - sqlite3VdbeAddOp2(p, OP_ParseSchema3, iRec, 0); -} - void sqlite3VdbeAddRenameTableOp(Vdbe * p, int iTab, char* zNewName) { -- 2.7.4