From d4b81e119556d0ee448b42142149c94a6b9d738a Mon Sep 17 00:00:00 2001 From: Ramil Kalimullin Date: Wed, 14 Jan 2009 18:50:51 +0400 Subject: Fix for bug#33094: Error in upgrading from 5.0 to 5.1 when table contains triggers and #41385: Crash when attempting to repair a #mysql50# upgraded table with triggers. Problem: 1. trigger code didn't assume a table name may have a "#mysql50#" prefix, that may lead to a failing ASSERT(). 2. "ALTER DATABASE ... UPGRADE DATA DIRECTORY NAME" failed for databases with "#mysql50#" prefix if any trigger. 3. mysqlcheck --fix-table-name didn't use UTF8 as a default character set that resulted in (parsing) errors for tables with non-latin symbols in their names and definitions of triggers. Fix: 1. properly handle table/database names with "#mysql50#" prefix. 2. handle --default-character-set mysqlcheck option; if mysqlcheck is launched with --fix-table-name or --fix-db-name set default character set to UTF8 if no --default-character-set option given. Note: if given --fix-table-name or --fix-db-name option, without --default-character-set mysqlcheck option default character set is UTF8. client/mysqlcheck.c: Fix for bug#33094: Error in upgrading from 5.0 to 5.1 when table contains triggers and #41385: Crash when attempting to repair a #mysql50# upgraded table with triggers. - check and set default charset if --default-character-set option given. - set default charset to "utf8" if there's --fix-table-name or --fix-db-name and no --default-character-set. mysql-test/r/mysqlcheck.result: Fix for bug#33094: Error in upgrading from 5.0 to 5.1 when table contains triggers and #41385: Crash when attempting to repair a #mysql50# upgraded table with triggers. - test result. mysql-test/t/mysqlcheck.test: Fix for bug#33094: Error in upgrading from 5.0 to 5.1 when table contains triggers and #41385: Crash when attempting to repair a #mysql50# upgraded table with triggers. - test case. sql/mysql_priv.h: Fix for bug#33094: Error in upgrading from 5.0 to 5.1 when table contains triggers and #41385: Crash when attempting to repair a #mysql50# upgraded table with triggers. - check_n_cut_mysql50_prefix() introduced. sql/sql_table.cc: Fix for bug#33094: Error in upgrading from 5.0 to 5.1 when table contains triggers and #41385: Crash when attempting to repair a #mysql50# upgraded table with triggers. - tablename_to_filename() code split into 2 parts - check_n_cut_mysql50_prefix() introduced to cut #mysql50# prefixes, used in the trigger code as well. sql/sql_trigger.cc: Fix for bug#33094: Error in upgrading from 5.0 to 5.1 when table contains triggers and #41385: Crash when attempting to repair a #mysql50# upgraded table with triggers. - Table_triggers_list::check_n_load() - checking triggers assume a table/database name given may have "#mysql50#" prefix in some cases. - Table_triggers_list::change_table_name_in_triggers() - create .TRG file in new database directory and delete it in old one, as they may differ in case of "ALTER DATABASE ... UPGRADE DATA DIRECTORY NAME" - Table_triggers_list::change_table_name_in_trignames() - remove stale .TRN files in #mysql50#dbname directory in case of database upgrade - Table_triggers_list::change_table_name() - allow changing trigger's database in case of its upgrading sql/sql_trigger.h: Fix for bug#33094: Error in upgrading from 5.0 to 5.1 when table contains triggers and #41385: Crash when attempting to repair a #mysql50# upgraded table with triggers. - new old_db_name parameter added in Table_triggers_list::change_table_name_in_trignames() and Table_triggers_list::change_table_name_in_triggers() --- sql/sql_trigger.cc | 88 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 23 deletions(-) (limited to 'sql/sql_trigger.cc') diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 5c8b1d96646..108d5095691 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -1368,15 +1368,27 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, if (triggers->on_table_names_list.push_back(on_table_name, &table->mem_root)) goto err_with_lex_cleanup; - +#ifndef DBUG_OFF /* Let us check that we correctly update trigger definitions when we rename tables with triggers. + + In special cases like "RENAME TABLE `#mysql50#somename` TO `somename`" + or "ALTER DATABASE `#mysql50#somename` UPGRADE DATA DIRECTORY NAME" + we might be given table or database name with "#mysql50#" prefix (and + trigger's definiton contains un-prefixed version of the same name). + To remove this prefix we use check_n_cut_mysql50_prefix(). */ - DBUG_ASSERT(!my_strcasecmp(table_alias_charset, lex.query_tables->db, db) && - !my_strcasecmp(table_alias_charset, lex.query_tables->table_name, - table_name)); + char fname[NAME_LEN + 1]; + DBUG_ASSERT((!my_strcasecmp(table_alias_charset, lex.query_tables->db, db) || + (check_n_cut_mysql50_prefix(db, fname, sizeof(fname)) && + !my_strcasecmp(table_alias_charset, lex.query_tables->db, fname))) && + (!my_strcasecmp(table_alias_charset, lex.query_tables->table_name, + table_name) || + (check_n_cut_mysql50_prefix(table_name, fname, sizeof(fname)) && + !my_strcasecmp(table_alias_charset, lex.query_tables->table_name, fname)))); +#endif if (names_only) { lex_end(&lex); @@ -1692,7 +1704,8 @@ end: (change name of table in triggers' definitions). @param thd Thread context - @param db_name Database of subject table + @param old_db_name Old database of subject table + @param new_db_name New database of subject table @param old_table_name Old subject table's name @param new_table_name New subject table's name @@ -1704,7 +1717,8 @@ end: bool Table_triggers_list::change_table_name_in_triggers(THD *thd, - const char *db_name, + const char *old_db_name, + const char *new_db_name, LEX_STRING *old_table_name, LEX_STRING *new_table_name) { @@ -1757,11 +1771,11 @@ Table_triggers_list::change_table_name_in_triggers(THD *thd, if (thd->is_fatal_error) return TRUE; /* OOM */ - if (save_trigger_file(this, db_name, new_table_name->str)) + if (save_trigger_file(this, new_db_name, new_table_name->str)) return TRUE; - if (rm_trigger_file(path_buff, db_name, old_table_name->str)) + if (rm_trigger_file(path_buff, old_db_name, old_table_name->str)) { - (void) rm_trigger_file(path_buff, db_name, new_table_name->str); + (void) rm_trigger_file(path_buff, new_db_name, new_table_name->str); return TRUE; } return FALSE; @@ -1772,7 +1786,8 @@ Table_triggers_list::change_table_name_in_triggers(THD *thd, Iterate though Table_triggers_list::names_list list and update .TRN files after renaming triggers' subject table. - @param db_name Database of subject table + @param old_db_name Old database of subject table + @param new_db_name New database of subject table @param new_table_name New subject table's name @param stopper Pointer to Table_triggers_list::names_list at which we should stop updating. @@ -1785,7 +1800,8 @@ Table_triggers_list::change_table_name_in_triggers(THD *thd, */ LEX_STRING* -Table_triggers_list::change_table_name_in_trignames(const char *db_name, +Table_triggers_list::change_table_name_in_trignames(const char *old_db_name, + const char *new_db_name, LEX_STRING *new_table_name, LEX_STRING *stopper) { @@ -1798,7 +1814,7 @@ Table_triggers_list::change_table_name_in_trignames(const char *db_name, while ((trigger= it_name++) != stopper) { trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1, - db_name, trigger->str, + new_db_name, trigger->str, TRN_EXT, 0); trigname_file.str= trigname_buff; @@ -1807,6 +1823,16 @@ Table_triggers_list::change_table_name_in_trignames(const char *db_name, if (sql_create_definition_file(NULL, &trigname_file, &trigname_file_type, (uchar*)&trigname, trigname_file_parameters)) return trigger; + + /* Remove stale .TRN file in case of database upgrade */ + if (old_db_name) + { + if (rm_trigname_file(trigname_buff, old_db_name, trigger->str)) + { + (void) rm_trigname_file(trigname_buff, new_db_name, trigger->str); + return trigger; + } + } } return 0; @@ -1840,6 +1866,7 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, { TABLE table; bool result= 0; + bool upgrading50to51= FALSE; LEX_STRING *err_trigname; DBUG_ENTER("change_table_name"); @@ -1877,14 +1904,27 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, 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, new_db)) { - my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0)); - result= 1; - goto end; + char dbname[NAME_LEN + 1]; + if (check_n_cut_mysql50_prefix(db, dbname, sizeof(dbname)) && + !my_strcasecmp(table_alias_charset, dbname, new_db)) + { + upgrading50to51= TRUE; + } + else + { + my_error(ER_TRG_IN_WRONG_SCHEMA, MYF(0)); + result= 1; + goto end; + } } - if (table.triggers->change_table_name_in_triggers(thd, db, + if (table.triggers->change_table_name_in_triggers(thd, db, new_db, &old_table_name, &new_table_name)) { @@ -1892,7 +1932,8 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, goto end; } if ((err_trigname= table.triggers->change_table_name_in_trignames( - db, &new_table_name, 0))) + upgrading50to51 ? db : NULL, + new_db, &new_table_name, 0))) { /* If we were unable to update one of .TRN files properly we will @@ -1900,16 +1941,17 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *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(db, - &old_table_name, - err_trigname); - (void) table.triggers->change_table_name_in_triggers(thd, db, - &new_table_name, - &old_table_name); + (void) table.triggers->change_table_name_in_trignames( + upgrading50to51 ? new_db : NULL, db, + &old_table_name, err_trigname); + (void) table.triggers->change_table_name_in_triggers( + thd, db, new_db, + &new_table_name, &old_table_name); result= 1; goto end; } } + end: delete table.triggers; free_root(&table.mem_root, MYF(0)); -- cgit v1.2.1