--- /mnt/epmw993.work/mysql-5.0.26/sql/sql_table.cc.old 2006-10-04 14:24:22.000000000 +0300 +++ /mnt/epmw993.work/mysql-5.0.26/sql/sql_table.cc 2006-10-24 19:13:08.000000000 +0300 @@ -3135,6 +3135,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 */ @@ -3703,6 +3716,157 @@ 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 + * - 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 steps of creating index by sort in tmp_table: + * 1) copy original MYD file to tmp_table's MYD file + * 2) repair indexes in tmp_table by sort + * + * Note: this approach doesn't used when adding unique or primary key without + * IGNORE flag, because there are no easy ways to reproduce an old behaviour + * in case when mysql found duplicate key: printing out something like + * ERROR 1062 (23000): Duplicate entry '...' for key N + */ + 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], path[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))) + { + my_message(1, "Failed to stat .MYD file for original table", MYF(0)); + VOID(quick_rm_table(new_db_type, new_db, tmp_name)); + 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))) + { + my_message(2, "Failed to stat .MYD file for temporary table", MYF(0)); + VOID(quick_rm_table(new_db_type, new_db, tmp_name)); + DBUG_RETURN(TRUE); + } + + /* close the original table and lock it */ + close_cached_table(thd, table); + table= 0; + if (lock_and_wait_for_table_name(thd, table_list)) + { + my_message(3, "Failed to get lock on table", MYF(0)); + VOID(quick_rm_table(new_db_type, new_db, tmp_name)); + DBUG_RETURN(TRUE); + } + + /* copy .MYD file of original table to tmp_name table */ + if (my_copy(from, to, MYF(MY_WME))) + { + my_message(5, "Failed to copy .MYD file", MYF(0)); + 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)); + 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 */ + my_message(7, "Failed generating indexes", MYF(0)); + 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)); + DBUG_RETURN(TRUE); + } + thd->net.vio= original_vio; /* restore network IO */ + + /* reopen original table */ + pthread_mutex_lock(&LOCK_open); + if (reopen_name_locked_table(thd, table_list)) + { + my_message(8, "Failed to reopen original table", MYF(0)); + unlock_table_name(thd, table_list); + pthread_mutex_unlock(&LOCK_open); + VOID(quick_rm_table(new_db_type, new_db, tmp_name)); + DBUG_RETURN(TRUE); + } + pthread_mutex_unlock(&LOCK_open); + + /* re-initialize table variable, which is required for the + * business logic below + */ + if (!(table= open_ltable(thd, table_list, TL_WRITE_ALLOW_READ))) + { + my_message(10, "Failed to open original table", MYF(0)); + VOID(quick_rm_table(new_db_type, new_db, tmp_name)); + DBUG_RETURN(TRUE); + } + + /* calculate copied and deleted rows count */ + build_table_path(path, sizeof(path), new_db, tmp_name, reg_ext); + if (!(new_table= open_temporary_table(thd, path, new_db, tmp_name, 0))) + { + my_message(9, "Failed to open temporary table", MYF(0)); + VOID(quick_rm_table(new_db_type, new_db, tmp_name)); + DBUG_RETURN(TRUE); + } + copied= table_list->table->file->records; + deleted= copied - new_table->file->records; + copied-= deleted; + } + else + { + if (need_copy_table) { if (table->s->tmp_table) @@ -3780,6 +3944,7 @@ } goto end_temporary; } + } if (new_table) {