diff options
Diffstat (limited to 'sql/sql_trigger.cc')
-rw-r--r-- | sql/sql_trigger.cc | 150 |
1 files changed, 100 insertions, 50 deletions
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 5802d2c811e..250ff859222 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2004, 2012, Oracle and/or its affiliates. - Copyright (c) 2010, 2018, MariaDB + Copyright (c) 2010, 2021, MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -30,11 +30,11 @@ #include "sql_table.h" // build_table_filename, // check_n_cut_mysql50_prefix #include "sql_db.h" // get_default_db_collation -#include "sql_acl.h" // *_ACL #include "sql_handler.h" // mysql_ha_rm_tables #include "sp_cache.h" // sp_invalidate_cache #include <mysys_err.h> #include "debug_sync.h" +#include "mysql/psi/mysql_sp.h" /*************************************************************************/ @@ -393,10 +393,12 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) This is a good candidate for a minor refactoring. */ TABLE *table; - bool result= TRUE; + bool result= TRUE, refresh_metadata= FALSE; String stmt_query; bool lock_upgrade_done= FALSE; + bool backup_of_table_list_done= 0;; MDL_ticket *mdl_ticket= NULL; + MDL_request mdl_request_for_trn; Query_tables_list backup; DBUG_ENTER("mysql_create_or_drop_trigger"); @@ -440,12 +442,21 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) */ if (!trust_function_creators && (WSREP_EMULATE_BINLOG(thd) || mysql_bin_log.is_open()) && - !(thd->security_ctx->master_access & SUPER_ACL)) + !(thd->security_ctx->master_access & PRIV_LOG_BIN_TRUSTED_SP_CREATOR)) { my_error(ER_BINLOG_CREATE_ROUTINE_NEED_SUPER, MYF(0)); DBUG_RETURN(TRUE); } + /* Protect against concurrent create/drop */ + MDL_REQUEST_INIT(&mdl_request_for_trn, MDL_key::TABLE, + create ? tables->db.str : thd->lex->spname->m_db.str, + thd->lex->spname->m_name.str, + MDL_EXCLUSIVE, MDL_EXPLICIT); + if (thd->mdl_context.acquire_lock(&mdl_request_for_trn, + thd->variables.lock_wait_timeout)) + goto end; + if (!create) { bool if_exists= thd->lex->if_exists(); @@ -454,6 +465,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) Protect the query table list from the temporary and potentially destructive changes necessary to open the trigger's table. */ + backup_of_table_list_done= 1; thd->lex->reset_n_backup_query_tables_list(&backup); /* Restore Query_tables_list::sql_command, which was @@ -463,7 +475,8 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) */ thd->lex->sql_command= backup.sql_command; - if (opt_readonly && !(thd->security_ctx->master_access & SUPER_ACL) && + if (opt_readonly && + !(thd->security_ctx->master_access & PRIV_IGNORE_READ_ONLY) && !thd->slave_thread) { my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); @@ -507,9 +520,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) goto end; } -#ifdef WITH_WSREP WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, tables); -#endif /* We should have only one table in table list. */ DBUG_ASSERT(tables->next_global == 0); @@ -550,10 +561,20 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) } table= tables->table; +#ifdef WITH_WSREP + if (WSREP(thd) && + !wsrep_should_replicate_ddl(thd, table->s->db_type()->db_type)) + goto end; +#endif + /* Later on we will need it to downgrade the lock */ mdl_ticket= table->mdl_ticket; - if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + /* + RENAME ensures that table is flushed properly and locked tables will + be removed from the active transaction + */ + if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME)) goto end; lock_upgrade_done= TRUE; @@ -585,28 +606,34 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) table->triggers->create_trigger(thd, tables, &stmt_query): table->triggers->drop_trigger(thd, tables, &stmt_query)); - if (result) - goto end; - - close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL); - /* - Reopen the table if we were under LOCK TABLES. - Ignore the return value for now. It's better to - keep master/slave in consistent state. - */ - if (thd->locked_tables_list.reopen_tables(thd, false)) - thd->clear_error(); - - /* - Invalidate SP-cache. That's needed because triggers may change list of - pre-locking tables. - */ - sp_cache_invalidate(); + refresh_metadata= TRUE; end: if (!result) result= write_bin_log(thd, TRUE, stmt_query.ptr(), stmt_query.length()); + if (mdl_request_for_trn.ticket) + thd->mdl_context.release_lock(mdl_request_for_trn.ticket); + + if (refresh_metadata) + { + close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL); + + /* + Reopen the table if we were under LOCK TABLES. + Ignore the return value for now. It's better to + keep master/slave in consistent state. + */ + if (thd->locked_tables_list.reopen_tables(thd, false)) + thd->clear_error(); + + /* + Invalidate SP-cache. That's needed because triggers may change list of + pre-locking tables. + */ + sp_cache_invalidate(); + } + /* If we are under LOCK TABLES we should restore original state of meta-data locks. Otherwise all locks will be released along @@ -616,16 +643,24 @@ end: mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); /* Restore the query table list. Used only for drop trigger. */ - if (!create) + if (backup_of_table_list_done) thd->lex->restore_backup_query_tables_list(&backup); if (!result) + { my_ok(thd); + /* Drop statistics for this stored program from performance schema. */ + MYSQL_DROP_SP(SP_TYPE_TRIGGER, + thd->lex->spname->m_db.str, static_cast<uint>(thd->lex->spname->m_db.length), + thd->lex->spname->m_name.str, static_cast<uint>(thd->lex->spname->m_name.length)); + } DBUG_RETURN(result); + #ifdef WITH_WSREP wsrep_error_label: - DBUG_RETURN(true); + DBUG_ASSERT(result == 1); + goto end; #endif } @@ -1020,10 +1055,10 @@ bool Trigger::add_to_file_list(void* param_arg) */ static bool rm_trigger_file(char *path, const LEX_CSTRING *db, - const LEX_CSTRING *table_name) + const LEX_CSTRING *table_name, myf MyFlags) { build_table_filename(path, FN_REFLEN-1, db->str, table_name->str, TRG_EXT, 0); - return mysql_file_delete(key_file_trg, path, MYF(MY_WME)); + return mysql_file_delete(key_file_trg, path, MyFlags); } @@ -1042,10 +1077,11 @@ static bool rm_trigger_file(char *path, const LEX_CSTRING *db, */ static bool rm_trigname_file(char *path, const LEX_CSTRING *db, - const LEX_CSTRING *trigger_name) + const LEX_CSTRING *trigger_name, myf MyFlags) { - build_table_filename(path, FN_REFLEN - 1, db->str, trigger_name->str, TRN_EXT, 0); - return mysql_file_delete(key_file_trn, path, MYF(MY_WME)); + build_table_filename(path, FN_REFLEN - 1, db->str, trigger_name->str, + TRN_EXT, 0); + return mysql_file_delete(key_file_trn, path, MyFlags); } @@ -1163,7 +1199,7 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables, parse_file.cc functionality (because we will need it elsewhere). */ - if (rm_trigger_file(path, &tables->db, &tables->table_name)) + if (rm_trigger_file(path, &tables->db, &tables->table_name, MYF(MY_WME))) return 1; } else @@ -1172,7 +1208,7 @@ bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables, return 1; } - if (rm_trigname_file(path, &tables->db, sp_name)) + if (rm_trigname_file(path, &tables->db, sp_name, MYF(MY_WME))) return 1; delete trigger; @@ -1313,13 +1349,14 @@ bool Table_triggers_list::prepare_record_accessors(TABLE *table) This could be avoided if there is no triggers for UPDATE and DELETE. @retval - False success + False no triggers or triggers where correctly loaded @retval - True error + True error (wrong trigger file) */ bool Table_triggers_list::check_n_load(THD *thd, const LEX_CSTRING *db, - const LEX_CSTRING *table_name, TABLE *table, + const LEX_CSTRING *table_name, + TABLE *table, bool names_only) { char path_buff[FN_REFLEN]; @@ -1550,6 +1587,10 @@ bool Table_triggers_list::check_n_load(THD *thd, const LEX_CSTRING *db, trigger->definer= *trg_definer; } + sp->m_sp_share= MYSQL_GET_SP_SHARE(SP_TYPE_TRIGGER, + sp->m_db.str, static_cast<uint>(sp->m_db.length), + sp->m_name.str, static_cast<uint>(sp->m_name.length)); + #ifndef DBUG_OFF /* Let us check that we correctly update trigger definitions when we @@ -1624,7 +1665,7 @@ err_with_lex_cleanup: } error: - if (unlikely(!thd->is_error())) + if (unlikely(!thd->is_error())) { /* We don't care about this error message much because .TRG files will @@ -1801,20 +1842,23 @@ bool add_table_for_trigger(THD *thd, */ bool Table_triggers_list::drop_all_triggers(THD *thd, const LEX_CSTRING *db, - const LEX_CSTRING *name) + const LEX_CSTRING *name, + myf MyFlags) { TABLE table; char path[FN_REFLEN]; bool result= 0; - DBUG_ENTER("Triggers::drop_all_triggers"); + DBUG_ENTER("Table_triggers_list::drop_all_triggers"); table.reset(); - init_sql_alloc(&table.mem_root, "Triggers::drop_all_triggers", 8192, 0, - MYF(0)); + init_sql_alloc(key_memory_Table_trigger_dispatcher, + &table.mem_root, 8192, 0, MYF(MY_WME)); if (Table_triggers_list::check_n_load(thd, db, name, &table, 1)) { result= 1; + /* We couldn't parse trigger file, best to just remove it */ + rm_trigger_file(path, db, name, MyFlags); goto end; } if (table.triggers) @@ -1834,7 +1878,7 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, const LEX_CSTRING *db, Such triggers have zero-length name and are skipped here. */ if (trigger->name.length && - rm_trigname_file(path, db, &trigger->name)) + rm_trigname_file(path, db, &trigger->name, MyFlags)) { /* Instead of immediately bailing out with error if we were unable @@ -1842,10 +1886,13 @@ bool Table_triggers_list::drop_all_triggers(THD *thd, const LEX_CSTRING *db, */ result= 1; } + /* Drop statistics for this stored program from performance schema. */ + MYSQL_DROP_SP(SP_TYPE_TRIGGER, db->str, static_cast<uint>(db->length), + trigger->name.str, static_cast<uint>(trigger->name.length)); } } } - if (rm_trigger_file(path, db, name)) + if (rm_trigger_file(path, db, name, MyFlags)) result= 1; delete table.triggers; } @@ -1906,9 +1953,10 @@ change_table_name_in_triggers(THD *thd, if (save_trigger_file(thd, new_db_name, new_table_name)) return TRUE; - if (rm_trigger_file(path_buff, old_db_name, old_table_name)) + if (rm_trigger_file(path_buff, old_db_name, old_table_name, MYF(MY_WME))) { - (void) rm_trigger_file(path_buff, new_db_name, new_table_name); + (void) rm_trigger_file(path_buff, new_db_name, new_table_name, + MYF(MY_WME)); return TRUE; } return FALSE; @@ -2017,9 +2065,11 @@ bool Trigger::change_on_table_name(void* param_arg) /* Remove stale .TRN file in case of database upgrade */ if (param->old_db_name) { - if (rm_trigname_file(trigname_buff, param->old_db_name, &name)) + if (rm_trigname_file(trigname_buff, param->old_db_name, &name, + MYF(MY_WME))) { - (void) rm_trigname_file(trigname_buff, param->new_db_name, &name); + (void) rm_trigname_file(trigname_buff, param->new_db_name, &name, + MYF(MY_WME)); return 1; } } @@ -2061,8 +2111,8 @@ bool Table_triggers_list::change_table_name(THD *thd, const LEX_CSTRING *db, DBUG_ENTER("Triggers::change_table_name"); table.reset(); - init_sql_alloc(&table.mem_root, "Triggers::change_table_name", 8192, 0, - MYF(0)); + init_sql_alloc(key_memory_Table_trigger_dispatcher, + &table.mem_root, 8192, 0, MYF(0)); /* This method interfaces the mysql server code protected by |