--- /mnt/epmw993.work/mysql-5.0.24/mysql-5.0.24/sql/sql_table.cc.old 2006-10-05 13:40:50.000000000 +0300 +++ /mnt/epmw993.work/mysql-5.0.24/mysql-5.0.24/sql/sql_table.cc 2006-10-13 17:27:26.000000000 +0300 @@ -3133,6 +3133,19 @@ #endif /* NOT_USED */ +static bool are_all_keys_non_unique(List &keys) +{ + List_iterator key_it(keys); + Key *key; + + while ((key=key_it++)) + { + if (key->type == Key::PRIMARY || key->type == Key::UNIQUE) + return false; + } + return true; +} + /* Alter table */ @@ -3699,6 +3712,192 @@ if (error) DBUG_RETURN(error); } + + /* Indexes can be (re-)generated by sort, much faster than key cache based + * generation of indexes in copy_data_between_tables(), in the case + * of the follwoing conditions: + * - table is not temporary (note: maybe redundant condition?) + * - table type is MyISAM + * - ALTER TABLE statement contains only the following statements: + * + * ALTER IGNORE TABLE ... any_key_list + * ALTER TABLE ... DROP any_key_list + * ALTER TABLE ... ADD non_unique_key_list + * + * Here is algorithm of creating index by sort in tmp_table: + * 1) move original MYD file to tmp_name.MYD + * 2) truncate original table in order to create an empty .MYD file. This + * file is required by the business logic below the patch. + * 3) repair indexes in tmp_name table by sort + * + * Note: it is not safe to use this approach when adding unique or + * primary key without IGNORE flag, because repair function doesn't restore + * original .MYD file when duplicate keys found. It just removes duplicate + * rows from the table. + */ + if ( + !table->s->tmp_table /* not temporary table */ + && !my_strcasecmp(system_charset_info, + table->file->table_type(), "MyISAM") /* table type is MyISAM */ + && + (( + /* ALTER IGNORE TABLE statement with any indexes */ + ignore + && !(alter_info->flags & ~(ALTER_ADD_INDEX|ALTER_DROP_INDEX)) + ) + || + ( + /* ALTER TABLE ... DROP statement with any indexes */ + alter_info->flags == ALTER_DROP_INDEX + ) + || + ( + /* ALTER TABLE ... ADD statement with non-unique indexes */ + alter_info->flags == ALTER_ADD_INDEX + && are_all_keys_non_unique(keys) + ) + ) + ) + { + const char **ext= table->file->bas_ext(); + char from[FN_REFLEN], to[FN_REFLEN]; + MY_STAT stat_info; + TABLE_LIST tbl; + HA_CHECK_OPT check_opt; + Vio *original_vio; + int lock_retcode; + + bzero((void*) &tbl, sizeof(tbl)); + tbl.db= new_db; + tbl.table_name= tbl.alias= tmp_name; + + my_snprintf(from, sizeof(from), "%s%s", table->s->path, ext[1]); + if (!my_stat(from, &stat_info, MYF(0))) + { + VOID(quick_rm_table(new_db_type, new_db, tmp_name)); + my_message(1, "Failed to stat .MYD file for original table", MYF(0)); + DBUG_RETURN(TRUE); + } + + my_snprintf(to, sizeof(to), "%s/%s/%s%s", + mysql_data_home, new_db, tmp_name, ext[1]); + fn_format(to, to, "", "", 4); + if (!my_stat(to, &stat_info, MYF(0))) + { + VOID(quick_rm_table(new_db_type, new_db, tmp_name)); + my_message(2, "Failed to stat .MYD file for temporary table", MYF(0)); + DBUG_RETURN(TRUE); + } + + /* close the original table, retrieve number of rows in it and + * lock the original table + */ + pthread_mutex_lock(&LOCK_open); + copied= table_list->table->file->records; + close_cached_table(thd, table); + table= 0; + if ((lock_retcode= lock_table_name(thd, table_list)) < 0) + { + pthread_mutex_unlock(&LOCK_open); + my_message(3, "Failed to get lock on table", MYF(0)); + DBUG_RETURN(TRUE); + } + if (lock_retcode && wait_for_locked_table_names(thd, table_list)) + { + unlock_table_name(thd, table_list); + pthread_mutex_unlock(&LOCK_open); + my_message(4, "Error when waiting for locked table names", MYF(0)); + DBUG_RETURN(TRUE); + } + pthread_mutex_unlock(&LOCK_open); + + /* move .MYD file of orignal table to tmp_name table */ + if (my_rename(from, to, MYF(MY_WME))) + { + pthread_mutex_lock(&LOCK_open); + unlock_table_name(thd, table_list); + pthread_mutex_unlock(&LOCK_open); + VOID(quick_rm_table(new_db_type, new_db, tmp_name)); + my_message(5, "Failed to rename .MYD file", MYF(0)); + DBUG_RETURN(TRUE); + } + + /* create an empty .MYD file for original table */ + if (mysql_truncate(thd, table_list, 1)) + { + VOID(my_rename(to, from, MYF(MY_WME))); + pthread_mutex_lock(&LOCK_open); + unlock_table_name(thd, table_list); + pthread_mutex_unlock(&LOCK_open); + VOID(mysql_truncate(thd, &tbl, 1)); + VOID(quick_rm_table(new_db_type, new_db, tmp_name)); + my_message(6, "Failed to generate table from .FRM file", MYF(0)); + DBUG_RETURN(TRUE); + } + + /* create indexes by sort */ + check_opt.init(); + check_opt.flags|= T_QUICK|T_SILENT|T_VERY_SILENT; + original_vio= thd->net.vio; + thd->net.vio= 0; /* disable network IO during repairing the table */ + if (mysql_admin_table(thd, &tbl, &check_opt, "alter", TL_WRITE, 1, 1, + HA_OPEN_FOR_REPAIR, 0, &handler::ha_repair, 0)) + { + thd->net.vio = original_vio; /* restore network IO */ + VOID(my_rename(to, from, MYF(MY_WME))); + pthread_mutex_lock(&LOCK_open); + unlock_table_name(thd, table_list); + pthread_mutex_unlock(&LOCK_open); + VOID(mysql_truncate(thd, &tbl, 1)); + VOID(quick_rm_table(new_db_type, new_db, tmp_name)); + my_message(7, "Failed generating indexes", MYF(0)); + DBUG_RETURN(TRUE); + } + thd->net.vio= original_vio; /* restore network IO */ + + pthread_mutex_lock(&LOCK_open); + if (reopen_name_locked_table(thd, table_list)) + { + VOID(my_rename(to, from, MYF(MY_WME))); + unlock_table_name(thd, table_list); + pthread_mutex_unlock(&LOCK_open); + VOID(mysql_truncate(thd, &tbl, 1)); + VOID(quick_rm_table(new_db_type, new_db, tmp_name)); + my_message(8, "Failed to reopen original table", MYF(0)); + DBUG_RETURN(TRUE); + } + pthread_mutex_unlock(&LOCK_open); + + /* retrieve deleted rows count and adjust copied rows count */ + build_table_path(to, sizeof(to), new_db, tmp_name, reg_ext); + if (!(new_table= open_temporary_table(thd, to, new_db, tmp_name, 0))) + { + VOID(my_rename(to, from, MYF(MY_WME))); + VOID(mysql_truncate(thd, &tbl, 1)); + VOID(quick_rm_table(new_db_type, new_db, tmp_name)); + my_message(9, "Failed to open temporary table", MYF(0)); + DBUG_RETURN(TRUE); + } + deleted= copied - new_table->file->records; + copied-= deleted; + + /* open and lock original table. + * Note: TL_WRITE lock used in order to prevent wrong SELECT results + * on truncated original table. + * 'table' variable needs for the business logic below + */ + if (!(table= open_ltable(thd, table_list, TL_WRITE))) + { + VOID(my_rename(to, from, MYF(MY_WME))); + VOID(mysql_truncate(thd, &tbl, 1)); + VOID(quick_rm_table(new_db_type, new_db, tmp_name)); + my_message(10, "Failed to open original table", MYF(0)); + DBUG_RETURN(TRUE); + } + } + else + { + if (need_copy_table) { if (table->s->tmp_table) @@ -3777,6 +3976,7 @@ } goto end_temporary; } + } if (new_table) {