diff --git a/sql/ddl_log.cc b/sql/ddl_log.cc index 7eae243771b..5f9a025c042 100644 --- a/sql/ddl_log.cc +++ b/sql/ddl_log.cc @@ -1160,6 +1160,7 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, case DDL_RENAME_PHASE_TRIGGER: { MDL_request mdl_request; + TRIGGER_RENAME_PARAM trigger_param; build_filename_and_delete_tmp_file(to_path, sizeof(to_path), &ddl_log_entry->db, @@ -1195,14 +1196,20 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, error= thd->mdl_context.acquire_lock(&mdl_request, 1); /* acquire_locks() should never fail during recovery */ DBUG_ASSERT(error == 0); - + (void) Table_triggers_list::prepare_for_rename(thd, + &trigger_param, + &ddl_log_entry->db, + &to_table, + &to_converted_name, + &ddl_log_entry->from_db, + &from_table); (void) Table_triggers_list::change_table_name(thd, + &trigger_param, &ddl_log_entry->db, &to_table, &to_converted_name, &ddl_log_entry->from_db, &from_table); - thd->mdl_context.release_lock(mdl_request.ticket); } if (ddl_log_increment_phase_no_lock(entry_pos)) @@ -1726,7 +1733,6 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, } break; } - } default: DBUG_ASSERT(0); break; diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 2b2f32cf126..ec782792f30 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2011, 2013, Monty Program Ab. + Copyright (c) 2011, 2021, Monty Program Ab. 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 @@ -339,6 +339,7 @@ do_rename(THD *thd, rename_param *param, DDL_LOG_STATE *ddl_log_state, int rc= 1; handlerton *hton; LEX_CSTRING *old_alias, *new_alias; + TRIGGER_RENAME_PARAM rename_param; DBUG_ENTER("do_rename"); DBUG_PRINT("enter", ("skip_error: %d", (int) skip_error)); @@ -361,6 +362,15 @@ do_rename(THD *thd, rename_param *param, DDL_LOG_STATE *ddl_log_state, if (hton->flags & HTON_TABLE_MAY_NOT_EXIST_ON_SLAVE) *force_if_exists= 1; + /* Check if we can rename triggers */ + if (Table_triggers_list::prepare_for_rename(thd, &rename_param, + &ren_table->db, + old_alias, + &ren_table->table_name, + new_db, + new_alias)) + DBUG_RETURN(!skip_error); + thd->replication_flags= 0; if (ddl_log_rename_table(thd, ddl_log_state, hton, @@ -379,7 +389,9 @@ do_rename(THD *thd, rename_param *param, DDL_LOG_STATE *ddl_log_state, debug_crash_here("ddl_log_rename_before_rename_trigger"); - if (!(rc= Table_triggers_list::change_table_name(thd, &ren_table->db, + if (!(rc= Table_triggers_list::change_table_name(thd, + &rename_param, + &ren_table->db, old_alias, &ren_table->table_name, new_db, diff --git a/sql/sql_table.cc b/sql/sql_table.cc index f00aaaee00a..7b3b0eb1136 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -7058,6 +7058,7 @@ static bool mysql_inplace_alter_table(THD *thd, TABLE *altered_table, Alter_inplace_info *ha_alter_info, MDL_request *target_mdl_request, + TRIGGER_RENAME_PARAM *trigger_param, Alter_table_ctx *alter_ctx) { Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN | MYSQL_OPEN_IGNORE_KILLED); @@ -7330,7 +7331,7 @@ static bool mysql_inplace_alter_table(THD *thd, */ DBUG_RETURN(true); } - if (Table_triggers_list::change_table_name(thd, + if (Table_triggers_list::change_table_name(thd, trigger_param, &alter_ctx->db, &alter_ctx->alias, &alter_ctx->table_name, @@ -7343,7 +7344,8 @@ static bool mysql_inplace_alter_table(THD *thd, */ (void) mysql_rename_table(db_type, &alter_ctx->new_db, &alter_ctx->new_alias, - &alter_ctx->db, &alter_ctx->alias, NO_FK_CHECKS); + &alter_ctx->db, &alter_ctx->alias, + NO_FK_CHECKS); DBUG_RETURN(true); } rename_table_in_stat_tables(thd, &alter_ctx->db, &alter_ctx->alias, @@ -8849,6 +8851,7 @@ simple_tmp_rename_or_index_change(THD *thd, TABLE_LIST *table_list, static bool simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list, Alter_info::enum_enable_or_disable keys_onoff, + TRIGGER_RENAME_PARAM *trigger_param, Alter_table_ctx *alter_ctx) { TABLE *table= table_list->table; @@ -8895,7 +8898,7 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list, if (mysql_rename_table(old_db_type, &alter_ctx->db, &alter_ctx->table_name, &alter_ctx->new_db, &alter_ctx->new_alias, 0)) error= -1; - else if (Table_triggers_list::change_table_name(thd, + else if (Table_triggers_list::change_table_name(thd, trigger_param, &alter_ctx->db, &alter_ctx->alias, &alter_ctx->table_name, @@ -9080,6 +9083,7 @@ bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, MDL_request target_mdl_request; MDL_ticket *mdl_ticket= 0; Alter_table_prelocking_strategy alter_prelocking_strategy; + TRIGGER_RENAME_PARAM trigger_param; DBUG_ENTER("mysql_alter_table"); /* @@ -9504,6 +9508,16 @@ do_continue:; create_info)) DBUG_RETURN(true); + /* Check if rename of triggers are supported */ + if (alter_ctx.is_table_renamed() && + Table_triggers_list::prepare_for_rename(thd, &trigger_param, + &alter_ctx.db, + &alter_ctx.alias, + &alter_ctx.table_name, + &alter_ctx.new_db, + &alter_ctx.new_alias)) + DBUG_RETURN(true); + /* Look if we have to do anything at all. ALTER can become NOOP after handling @@ -9549,6 +9563,7 @@ do_continue:; } res= simple_rename_or_index_change(thd, table_list, alter_info->keys_onoff, + &trigger_param, &alter_ctx); } else @@ -9805,7 +9820,7 @@ do_continue:; &altered_table)) goto err_new_table_cleanup; /* - Avoid creating frm again in ha_create_table() if inline alter will not + Avoid creating frm again in ha_create_table() if inplace alter will not be used. */ frm_is_created= 1; @@ -9879,9 +9894,12 @@ do_continue:; */ enum_check_fields org_count_cuted_fields= thd->count_cuted_fields; thd->count_cuted_fields= CHECK_FIELD_WARN; - int res= mysql_inplace_alter_table(thd, table_list, table, &altered_table, + int res= mysql_inplace_alter_table(thd, + table_list, table, &altered_table, &ha_alter_info, - &target_mdl_request, &alter_ctx); + &target_mdl_request, + &trigger_param, + &alter_ctx); thd->count_cuted_fields= org_count_cuted_fields; my_free(const_cast(frm.str)); @@ -10216,7 +10234,7 @@ do_continue:; // Check if we renamed the table and if so update trigger files. if (alter_ctx.is_table_renamed()) { - if (Table_triggers_list::change_table_name(thd, + if (Table_triggers_list::change_table_name(thd, &trigger_param, &alter_ctx.db, &alter_ctx.alias, &alter_ctx.table_name, diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index e3149799b6e..181cca39f7e 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -45,6 +45,18 @@ static bool add_table_for_trigger_internal(THD *thd, TABLE_LIST **table, char *trn_path_buff); +/* + Functions for TRIGGER_RENAME_PARAM +*/ + +void TRIGGER_RENAME_PARAM::reset() +{ + delete table.triggers; + table.triggers= 0; + free_root(&table.mem_root, MYF(0)); +} + + /** Trigger_creation_ctx -- creation context of triggers. */ @@ -2206,6 +2218,80 @@ bool Trigger::change_on_table_name(void* param_arg) } +/* + Check if we can rename triggers in change_table_name() + The idea is to ensure that it is close to impossible that + change_table_name() should fail. + + @return 0 ok + @return 1 Error: rename of triggers would fail +*/ + +bool +Table_triggers_list::prepare_for_rename(THD *thd, + TRIGGER_RENAME_PARAM *param, + const LEX_CSTRING *db, + const LEX_CSTRING *old_alias, + const LEX_CSTRING *old_table, + const LEX_CSTRING *new_db, + const LEX_CSTRING *new_table) +{ + TABLE *table= ¶m->table; + bool result= 0; + DBUG_ENTER("Table_triggers_lists::prepare_change_table_name"); + + init_sql_alloc(key_memory_Table_trigger_dispatcher, + &table->mem_root, 8192, 0, MYF(0)); + + DBUG_ASSERT(my_strcasecmp(table_alias_charset, db->str, new_db->str) || + my_strcasecmp(table_alias_charset, old_alias->str, + new_table->str)); + + if (Table_triggers_list::check_n_load(thd, db, old_table, table, TRUE)) + { + result= 1; + goto end; + } + if (table->triggers) + { + if (table->triggers->check_for_broken_triggers()) + { + result= 1; + goto end; + } + /* + Since triggers should be in the same schema as their subject tables + moving table with them between two schemas raises too many questions. + (E.g. what should happen if in new schema we already have trigger + with same name ?). + + In case of "ALTER DATABASE `#mysql50#db1` UPGRADE DATA DIRECTORY NAME" + we will be given table name with "#mysql50#" prefix + To remove this prefix we use check_n_cut_mysql50_prefix(). + */ + if (my_strcasecmp(table_alias_charset, db->str, new_db->str)) + { + char dbname[SAFE_NAME_LEN + 1]; + if (check_n_cut_mysql50_prefix(db->str, dbname, sizeof(dbname)) && + !my_strcasecmp(table_alias_charset, dbname, new_db->str)) + { + param->upgrading50to51= TRUE; + } + else + { + my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0)); + result= 1; + goto end; + } + } + } + +end: + param->got_error= result; + DBUG_RETURN(result); +} + + /** Update .TRG and .TRN files after renaming triggers' subject table. @@ -2229,22 +2315,21 @@ bool Trigger::change_on_table_name(void* param_arg) @retval TRUE Error */ -bool Table_triggers_list::change_table_name(THD *thd, const LEX_CSTRING *db, +bool Table_triggers_list::change_table_name(THD *thd, + TRIGGER_RENAME_PARAM *param, + const LEX_CSTRING *db, const LEX_CSTRING *old_alias, const LEX_CSTRING *old_table, const LEX_CSTRING *new_db, const LEX_CSTRING *new_table) { - TABLE table; + TABLE *table= ¶m->table; bool result= 0; bool upgrading50to51= FALSE; Trigger *err_trigger; - DBUG_ENTER("Triggers::change_table_name"); - - table.reset(); - init_sql_alloc(key_memory_Table_trigger_dispatcher, - &table.mem_root, 8192, 0, MYF(0)); + DBUG_ENTER("Table_triggers_list::change_table_name"); + DBUG_ASSERT(!param->got_error); /* This method interfaces the mysql server code protected by an exclusive metadata lock. @@ -2253,54 +2338,16 @@ bool Table_triggers_list::change_table_name(THD *thd, const LEX_CSTRING *db, old_table->str, MDL_EXCLUSIVE)); - DBUG_ASSERT(my_strcasecmp(table_alias_charset, db->str, new_db->str) || - my_strcasecmp(table_alias_charset, old_alias->str, new_table->str)); - - if (Table_triggers_list::check_n_load(thd, db, old_table, &table, TRUE)) + if (table->triggers) { - result= 1; - goto end; - } - if (table.triggers) - { - if (table.triggers->check_for_broken_triggers()) - { - result= 1; - goto end; - } - /* - Since triggers should be in the same schema as their subject tables - moving table with them between two schemas raises too many questions. - (E.g. what should happen if in new schema we already have trigger - with same name ?). - - In case of "ALTER DATABASE `#mysql50#db1` UPGRADE DATA DIRECTORY NAME" - we will be given table name with "#mysql50#" prefix - To remove this prefix we use check_n_cut_mysql50_prefix(). - */ - if (my_strcasecmp(table_alias_charset, db->str, new_db->str)) - { - char dbname[SAFE_NAME_LEN + 1]; - if (check_n_cut_mysql50_prefix(db->str, dbname, sizeof(dbname)) && - !my_strcasecmp(table_alias_charset, dbname, new_db->str)) - { - upgrading50to51= TRUE; - } - else - { - my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0)); - result= 1; - goto end; - } - } - if (unlikely(table.triggers->change_table_name_in_triggers(thd, db, new_db, + if (unlikely(table->triggers->change_table_name_in_triggers(thd, db, new_db, old_alias, new_table))) { result= 1; goto end; } - if ((err_trigger= table.triggers-> + if ((err_trigger= table->triggers-> change_table_name_in_trignames( upgrading50to51 ? db : NULL, new_db, new_table, 0))) { @@ -2310,10 +2357,10 @@ bool Table_triggers_list::change_table_name(THD *thd, const LEX_CSTRING *db, We assume that we will be able to undo our changes without errors (we can't do much if there will be an error anyway). */ - (void) table.triggers->change_table_name_in_trignames( + (void) table->triggers->change_table_name_in_trignames( upgrading50to51 ? new_db : NULL, db, old_alias, err_trigger); - (void) table.triggers->change_table_name_in_triggers( + (void) table->triggers->change_table_name_in_triggers( thd, db, new_db, new_table, old_alias); result= 1; @@ -2322,8 +2369,6 @@ bool Table_triggers_list::change_table_name(THD *thd, const LEX_CSTRING *db, } end: - delete table.triggers; - free_root(&table.mem_root, MYF(0)); DBUG_RETURN(result); } diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 98e527a50c0..739669c86a5 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -79,6 +79,30 @@ struct st_trg_execution_order }; +/* + Parameter to change_table_name_in_triggers() +*/ + +class TRIGGER_RENAME_PARAM +{ +public: + TABLE table; + bool upgrading50to51; + bool got_error; + + TRIGGER_RENAME_PARAM() + { + upgrading50to51= got_error= 0; + table.reset(); + } + ~TRIGGER_RENAME_PARAM() + { + reset(); + } + void reset(); +}; + + class Table_triggers_list; /** @@ -237,7 +261,14 @@ public: TABLE *table, bool names_only); static bool drop_all_triggers(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *table_name, myf MyFlags); - static bool change_table_name(THD *thd, const LEX_CSTRING *db, + static bool prepare_for_rename(THD *thd, TRIGGER_RENAME_PARAM *param, + const LEX_CSTRING *db, + const LEX_CSTRING *old_alias, + const LEX_CSTRING *old_table, + const LEX_CSTRING *new_db, + const LEX_CSTRING *new_table); + static bool change_table_name(THD *thd, TRIGGER_RENAME_PARAM *param, + const LEX_CSTRING *db, const LEX_CSTRING *old_alias, const LEX_CSTRING *old_table, const LEX_CSTRING *new_db, @@ -315,6 +346,7 @@ private: } }; + bool add_table_for_trigger(THD *thd, const sp_name *trg_name, bool continue_if_not_exist,