From: Jon Olav Hauglid Date: March 21 2011 4:48pm Subject: bzr commit into mysql-trunk branch (jon.hauglid:3304) WL#5534 List-Archive: http://lists.mysql.com/commits/133434 Message-Id: <201103211648.p2LGmKt0023706@acsmt356.oracle.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============4429949998506993902==" --===============4429949998506993902== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///export/home/x/mysql-trunk-alter-refactor/ based on revid:georgi.kodinov@stripped 3304 Jon Olav Hauglid 2011-03-21 WL#5534 Online ALTER in 5.6* Tentative pre-requisite patch, first round of refactoring. The following changes are made to mysql_alter_table(): - Extracted new simple_rename_or_index_change() function for ALTER_RENAME / ALTER_KEYS_ONOFF without touching .FRM. - Extracted new can_alter_index_inplace() function for determining of add/drop index can be done in-place and if so, the level of locking required. - Extracted new alter_indexes() function for in-place add/drop of indexes. - Removed need_copy_table variable, use Alter_info::change_level instead. - A couple of code blocks moved closer to related code. modified: sql/sql_table.cc === modified file 'sql/sql_table.cc' --- a/sql/sql_table.cc 2011-03-17 11:11:39 +0000 +++ b/sql/sql_table.cc 2011-03-21 16:48:06 +0000 @@ -5817,6 +5817,419 @@ err: } +/** + Rename table and/or turn indexes on/off without touching .FRM + + @param thd Thread handler + @param table_list TABLE_LIST for the table to change + @param table TABLE for the table to change + @param alter_info List of keys to be changed + @param new_db New datbase name + @param new_name New table name + @param new_alias New table alias + @param db Old database name + @param table_name Old table name + @param alias Old alias + @param old_db_type Table type for handler + @param mdl_ticket Ticket representing the MDL lock on the table + + @return Operation status + @retval 0 Success + @retval != 0 Failure +*/ + +static int simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list, + TABLE *table, Alter_info *alter_info, + char *new_db, char *new_name, + char *new_alias, char *db, + char *table_name, char *alias, + handlerton *old_db_type, + MDL_ticket *mdl_ticket) +{ + int error= 0; + switch (alter_info->keys_onoff) { + case LEAVE_AS_IS: + break; + case ENABLE: + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + return -1; + DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000);); + error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); + break; + case DISABLE: + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + return -1; + error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); + break; + default: + DBUG_ASSERT(FALSE); + error= 0; + break; + } + if (error == HA_ERR_WRONG_COMMAND) + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), + table->alias); + error= 0; + } + else if (error > 0) + { + table->file->print_error(error, MYF(0)); + error= -1; + } + + if (!error && (new_name != table_name || new_db != db)) + { + thd_proc_info(thd, "rename"); + /* + Then do a 'simple' rename of the table. First we need to close all + instances of 'source' table. + Note that if wait_while_table_is_used() returns error here (i.e. if + this thread was killed) then it must be that previous step of + simple rename did nothing and therefore we can safely return + without additional clean-up. + */ + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + return -1; + close_all_tables_for_name(thd, table->s, TRUE); + + *fn_ext(new_name)=0; + if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0)) + error= -1; + else if (Table_triggers_list::change_table_name(thd, db, + alias, table_name, + new_db, new_alias)) + { + (void) mysql_rename_table(old_db_type, new_db, new_alias, db, + table_name, 0); + error= -1; + } + } + + if (!error) + { + error= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); + if (!error) + my_ok(thd); + } + table_list->table= NULL; // For query cache + query_cache_invalidate3(thd, table_list, 0); + + if ((thd->locked_tables_mode == LTM_LOCK_TABLES || + thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)) + { + /* + Under LOCK TABLES we should adjust meta-data locks before finishing + statement. Otherwise we can rely on them being released + along with the implicit commit. + */ + if (new_name != table_name || new_db != db) + thd->mdl_context.release_all_locks_for_name(mdl_ticket); + else + mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE); + } + return error; +} + + +/** + Check if indexes can be dropped/added in-place. + + @param table[in] Table to be modified + @param alter_info[in] Alter options, fields and keys for + the new table + @param index_drop_count[in] The number of indexes to drop + @param index_drop_buffer[in] An array of offsets into table->key_info + @param index_add_count[in] The number of indexes to add + @param index_add_buffer[in] An array of offsets into key_info_buffer + @param key_info_buffer[in] An array of KEY structs for new indexes + @param need_lock_for_indexes[out] true if exclusive lock is needed + + @return Value indicating if in-place operation is supported +*/ + +static bool can_alter_index_inplace(TABLE *table, Alter_info *alter_info, + uint index_drop_count, + uint *index_drop_buffer, + uint index_add_count, + uint *index_add_buffer, + uint candidate_key_count, + KEY *key_info_buffer, + bool *need_lock_for_indexes) +{ + int pk_changed= 0; + ulong alter_flags= 0; + ulong needed_inplace_with_read_flags= 0; + ulong needed_inplace_flags= 0; + KEY *key; + uint *idx_p; + uint *idx_end_p; + bool no_pk; + bool can_alter_index_inplace= false; + + alter_flags= table->file->alter_table_flags(alter_info->flags); + DBUG_PRINT("info", ("alter_flags: %lu", alter_flags)); + /* Check dropped indexes. */ + for (idx_p= index_drop_buffer, idx_end_p= idx_p + index_drop_count; + idx_p < idx_end_p; + idx_p++) + { + key= table->key_info + *idx_p; + DBUG_PRINT("info", ("index dropped: '%s'", key->name)); + if (key->flags & HA_NOSAME) + { + /* + Unique key. Check for "PRIMARY". + or if dropping last unique key + */ + if ((uint) (key - table->key_info) == table->s->primary_key) + { + DBUG_PRINT("info", ("Dropping primary key")); + /* Primary key. */ + needed_inplace_with_read_flags|= HA_INPLACE_DROP_PK_INDEX_NO_WRITE; + needed_inplace_flags|= HA_INPLACE_DROP_PK_INDEX_NO_READ_WRITE; + pk_changed++; + candidate_key_count--; + } + else + { + KEY_PART_INFO *part_end= key->key_part + key->key_parts; + bool is_candidate_key= true; + + /* Non-primary unique key. */ + needed_inplace_with_read_flags|= + HA_INPLACE_DROP_UNIQUE_INDEX_NO_WRITE; + needed_inplace_flags|= HA_INPLACE_DROP_UNIQUE_INDEX_NO_READ_WRITE; + + /* + Check if all fields in key are declared + NOT NULL and adjust candidate_key_count + */ + for (KEY_PART_INFO *key_part= key->key_part; + key_part < part_end; + key_part++) + is_candidate_key= + (is_candidate_key && + (! table->field[key_part->fieldnr-1]->maybe_null())); + if (is_candidate_key) + candidate_key_count--; + } + } + else + { + /* Non-unique key. */ + needed_inplace_with_read_flags|= HA_INPLACE_DROP_INDEX_NO_WRITE; + needed_inplace_flags|= HA_INPLACE_DROP_INDEX_NO_READ_WRITE; + } + } + no_pk= ((table->s->primary_key == MAX_KEY) || + (needed_inplace_with_read_flags & + HA_INPLACE_DROP_PK_INDEX_NO_WRITE)); + /* Check added indexes. */ + for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count; + idx_p < idx_end_p; + idx_p++) + { + key= key_info_buffer + *idx_p; + DBUG_PRINT("info", ("index added: '%s'", key->name)); + if (key->flags & HA_NOSAME) + { + /* Unique key */ + + KEY_PART_INFO *part_end= key->key_part + key->key_parts; + bool is_candidate_key= true; + + /* + Check if all fields in key are declared + NOT NULL + */ + for (KEY_PART_INFO *key_part= key->key_part; + key_part < part_end; + key_part++) + is_candidate_key= + (is_candidate_key && + (! table->field[key_part->fieldnr]->maybe_null())); + + /* + Check for "PRIMARY" + or if adding first unique key + defined on non-nullable fields + */ + + if ((!my_strcasecmp(system_charset_info, + key->name, primary_key_name)) || + (no_pk && candidate_key_count == 0 && is_candidate_key)) + { + DBUG_PRINT("info", ("Adding primary key")); + /* Primary key. */ + needed_inplace_with_read_flags|= HA_INPLACE_ADD_PK_INDEX_NO_WRITE; + needed_inplace_flags|= HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE; + pk_changed++; + no_pk= false; + } + else + { + /* Non-primary unique key. */ + needed_inplace_with_read_flags|= HA_INPLACE_ADD_UNIQUE_INDEX_NO_WRITE; + needed_inplace_flags|= HA_INPLACE_ADD_UNIQUE_INDEX_NO_READ_WRITE; + } + } + else + { + /* Non-unique key. */ + needed_inplace_with_read_flags|= HA_INPLACE_ADD_INDEX_NO_WRITE; + needed_inplace_flags|= HA_INPLACE_ADD_INDEX_NO_READ_WRITE; + } + } + + if ((candidate_key_count > 0) && + (needed_inplace_with_read_flags & HA_INPLACE_DROP_PK_INDEX_NO_WRITE)) + { + /* + Dropped primary key when there is some other unique + not null key that should be converted to primary key + */ + needed_inplace_with_read_flags|= HA_INPLACE_ADD_PK_INDEX_NO_WRITE; + needed_inplace_flags|= HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE; + pk_changed= 2; + } + + DBUG_PRINT("info", + ("needed_inplace_with_read_flags: 0x%lx, needed_inplace_flags: 0x%lx", + needed_inplace_with_read_flags, needed_inplace_flags)); + /* + In-place add/drop index is possible only if + the primary key is not added and dropped in the same statement. + Otherwise we have to recreate the table. + need_copy_table is no-zero at this place. + */ + if ( pk_changed < 2 ) + { + if ((alter_flags & needed_inplace_with_read_flags) == + needed_inplace_with_read_flags) + { + /* All required in-place flags to allow concurrent reads are present. */ + can_alter_index_inplace= true; + *need_lock_for_indexes= false; + } + else if ((alter_flags & needed_inplace_flags) == needed_inplace_flags) + { + /* All required in-place flags are present. */ + can_alter_index_inplace= true; + } + } + DBUG_PRINT("info", ("can_alter_index_inplace: %d need_lock: %d", + can_alter_index_inplace, *need_lock_for_indexes)); + return can_alter_index_inplace; +} + + +/** + Execute in-place add and drop of indexes. + + @param thd Thread handle + @param table Table to be modified + @param key_info_buffer An array of KEY structs for new indexes + @param index_drop_count The number of indexes to drop + @param index_drop_buffer An array of offsets into table->key_info + @param index_add_count The number of indexes to add + @param index_add_buffer An array of offsets into key_info_buffer + + @return Operation status + @retval 0 Success + @retval != 0 Failure +*/ + +static int alter_indexes(THD *thd, TABLE *table, KEY *key_info_buffer, + uint index_drop_count, uint *index_drop_buffer, + uint index_add_count, uint *index_add_buffer) +{ + uint *key_numbers; + uint *keyno_p; + KEY *key_info; + KEY *key; + uint *idx_p; + uint *idx_end_p; + KEY_PART_INFO *key_part; + KEY_PART_INFO *part_end; + int error; + DBUG_PRINT("info", ("No new_table, checking add/drop index")); + + table->file->ha_prepare_for_alter(); + if (index_add_count) + { + /* The add_index() method takes an array of KEY structs. */ + key_info= (KEY*) thd->alloc(sizeof(KEY) * index_add_count); + key= key_info; + for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count; + idx_p < idx_end_p; + idx_p++, key++) + { + /* Copy the KEY struct. */ + *key= key_info_buffer[*idx_p]; + /* Fix the key parts. */ + part_end= key->key_part + key->key_parts; + for (key_part= key->key_part; key_part < part_end; key_part++) + key_part->field= table->field[key_part->fieldnr]; + } + /* Add the indexes. */ + if ((error= table->file->add_index(table, key_info, index_add_count))) + { + /* + Exchange the key_info for the error message. If we exchange + key number by key name in the message later, we need correct info. + */ + KEY *save_key_info= table->key_info; + table->key_info= key_info; + table->file->print_error(error, MYF(0)); + table->key_info= save_key_info; + return error; + } + } + /*end of if (index_add_count)*/ + + if (index_drop_count) + { + /* The prepare_drop_index() method takes an array of key numbers. */ + key_numbers= (uint*) thd->alloc(sizeof(uint) * index_drop_count); + keyno_p= key_numbers; + /* Get the number of each key. */ + for (idx_p= index_drop_buffer, idx_end_p= idx_p + index_drop_count; + idx_p < idx_end_p; + idx_p++, keyno_p++) + *keyno_p= *idx_p; + /* + Tell the handler to prepare for drop indexes. + This re-numbers the indexes to get rid of gaps. + */ + if ((error= table->file->prepare_drop_index(table, key_numbers, + index_drop_count))) + { + table->file->print_error(error, MYF(0)); + return error; + } + + /* Tell the handler to finally drop the indexes. */ + if ((error= table->file->final_drop_index(table))) + { + table->file->print_error(error, MYF(0)); + return error; + } + } + /*end of if (index_drop_count)*/ + + /* + The final .frm file is already created as a temporary file + and will be renamed to the original table name later. + */ + + /* Need to commit before a table is unlocked (NDB requirement). */ + DBUG_PRINT("info", ("Committing before unlocking table")); + return trans_commit_stmt(thd) || trans_commit_implicit(thd); +} + + /* Alter table @@ -5876,7 +6289,6 @@ bool mysql_alter_table(THD *thd,char *ne char reg_path[FN_REFLEN+1]; ha_rows copied,deleted; handlerton *old_db_type, *new_db_type, *save_old_db_type; - enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY; #ifdef WITH_PARTITION_STORAGE_ENGINE TABLE *table_for_fast_alter_partition= NULL; bool partition_changed= FALSE; @@ -5887,8 +6299,6 @@ bool mysql_alter_table(THD *thd,char *ne uint *index_drop_buffer= NULL; uint index_add_count= 0; uint *index_add_buffer= NULL; - uint candidate_key_count= 0; - bool no_pk; DBUG_ENTER("mysql_alter_table"); /* @@ -5933,20 +6343,6 @@ bool mysql_alter_table(THD *thd,char *ne } } - /* - Assign variables table_name, new_name, db, new_db, path, reg_path - to simplify further comparisions: we want to see if it's a RENAME - later just by comparing the pointers, avoiding the need for strcmp. - */ - thd_proc_info(thd, "init"); - table_name=table_list->table_name; - alias= (lower_case_table_names == 2) ? table_list->alias : table_name; - db=table_list->db; - if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db)) - new_db= db; - build_table_filename(reg_path, sizeof(reg_path) - 1, db, table_name, reg_ext, 0); - build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0); - mysql_ha_rm_tables(thd, table_list); /* DISCARD/IMPORT TABLESPACE is always alone in an ALTER TABLE */ @@ -5994,6 +6390,20 @@ bool mysql_alter_table(THD *thd,char *ne DBUG_RETURN(TRUE); } + /* + Assign variables table_name, new_name, db, new_db, path, reg_path + to simplify further comparisions: we want to see if it's a RENAME + later just by comparing the pointers, avoiding the need for strcmp. + */ + thd_proc_info(thd, "init"); + table_name=table_list->table_name; + alias= (lower_case_table_names == 2) ? table_list->alias : table_name; + db=table_list->db; + if (!new_db || !my_strcasecmp(table_alias_charset, new_db, db)) + new_db= db; + build_table_filename(reg_path, sizeof(reg_path) - 1, db, table_name, reg_ext, 0); + build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0); + /* Check that we are not trying to rename to an existing table */ if (new_name) { @@ -6134,112 +6544,10 @@ bool mysql_alter_table(THD *thd,char *ne if (!(alter_info->flags & ~(ALTER_RENAME | ALTER_KEYS_ONOFF)) && !table->s->tmp_table) // no need to touch frm { - switch (alter_info->keys_onoff) { - case LEAVE_AS_IS: - break; - case ENABLE: - if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) - goto err; - DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000);); - error= table->file->ha_enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); - break; - case DISABLE: - if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) - goto err; - error=table->file->ha_disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE); - break; - default: - DBUG_ASSERT(FALSE); - error= 0; - break; - } - if (error == HA_ERR_WRONG_COMMAND) - { - error= 0; - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), - table->alias); - } - - if (!error && (new_name != table_name || new_db != db)) - { - thd_proc_info(thd, "rename"); - /* - Then do a 'simple' rename of the table. First we need to close all - instances of 'source' table. - Note that if wait_while_table_is_used() returns error here (i.e. if - this thread was killed) then it must be that previous step of - simple rename did nothing and therefore we can safely return - without additional clean-up. - */ - if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) - goto err; - close_all_tables_for_name(thd, table->s, TRUE); - /* - Then, we want check once again that target table does not exist. - Actually the order of these two steps does not matter since - earlier we took exclusive metadata lock on the target table, so - we do them in this particular order only to be consistent with 5.0, - in which we don't take this lock and where this order really matters. - TODO: Investigate if we need this access() check at all. - */ - if (!access(new_name_buff,F_OK)) - { - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name); - error= -1; - } - else - { - *fn_ext(new_name)=0; - if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0)) - error= -1; - else if (Table_triggers_list::change_table_name(thd, db, - alias, table_name, - new_db, new_alias)) - { - (void) mysql_rename_table(old_db_type, new_db, new_alias, db, - table_name, 0); - error= -1; - } - } - } - - if (error == HA_ERR_WRONG_COMMAND) - { - error= 0; - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, - ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA), - table->alias); - } - - if (!error) - { - error= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); - if (!error) - my_ok(thd); - } - else if (error > 0) - { - table->file->print_error(error, MYF(0)); - error= -1; - } - table_list->table= NULL; // For query cache - query_cache_invalidate3(thd, table_list, 0); - - if ((thd->locked_tables_mode == LTM_LOCK_TABLES || - thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)) - { - /* - Under LOCK TABLES we should adjust meta-data locks before finishing - statement. Otherwise we can rely on them being released - along with the implicit commit. - */ - if (new_name != table_name || new_db != db) - thd->mdl_context.release_all_locks_for_name(mdl_ticket); - else - mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE); - } - DBUG_RETURN(error); + DBUG_RETURN(simple_rename_or_index_change(thd, table_list, table, + alter_info, new_db, new_name, + new_alias, db, table_name, alias, + old_db_type, mdl_ticket)); } /* We have to do full alter table. */ @@ -6264,27 +6572,23 @@ bool mysql_alter_table(THD *thd,char *ne */ new_db_type= create_info->db_type; - if (is_index_maintenance_unique (table, alter_info)) - need_copy_table= ALTER_TABLE_DATA_CHANGED; + if (is_index_maintenance_unique (table, alter_info) + || thd->variables.old_alter_table +#ifdef WITH_PARTITION_STORAGE_ENGINE + || partition_changed +#endif + || (table->s->db_type() != create_info->db_type)) + alter_info->change_level= ALTER_TABLE_DATA_CHANGED; if (mysql_prepare_alter_table(thd, table, create_info, alter_info)) goto err; - if (need_copy_table == ALTER_TABLE_METADATA_ONLY) - need_copy_table= alter_info->change_level; - set_table_default_charset(thd, create_info, db); - if (thd->variables.old_alter_table - || (table->s->db_type() != create_info->db_type) -#ifdef WITH_PARTITION_STORAGE_ENGINE - || partition_changed -#endif - ) - need_copy_table= ALTER_TABLE_DATA_CHANGED; - else + if (alter_info->change_level == ALTER_TABLE_METADATA_ONLY) { Alter_table_change_level need_copy_table_res; + uint candidate_key_count= 0; /* Check how much the tables differ. */ if (mysql_compare_tables(table, alter_info, create_info, order_num, @@ -6294,196 +6598,27 @@ bool mysql_alter_table(THD *thd,char *ne &index_add_buffer, &index_add_count, &candidate_key_count, FALSE)) goto err; - - DBUG_EXECUTE_IF("alter_table_only_metadata_change", { - if (need_copy_table_res != ALTER_TABLE_METADATA_ONLY) - goto err; }); - DBUG_EXECUTE_IF("alter_table_only_index_change", { - if (need_copy_table_res != ALTER_TABLE_INDEX_CHANGED) - goto err; }); - - if (need_copy_table == ALTER_TABLE_METADATA_ONLY) - need_copy_table= need_copy_table_res; - } - - /* - If there are index changes only, try to do them in-place. "Index - changes only" means also that the handler for the table does not - change. The table is open and locked. The handler can be accessed. - */ - if (need_copy_table == ALTER_TABLE_INDEX_CHANGED) - { - int pk_changed= 0; - ulong alter_flags= 0; - ulong needed_inplace_with_read_flags= 0; - ulong needed_inplace_flags= 0; - KEY *key; - uint *idx_p; - uint *idx_end_p; - - alter_flags= table->file->alter_table_flags(alter_info->flags); - DBUG_PRINT("info", ("alter_flags: %lu", alter_flags)); - /* Check dropped indexes. */ - for (idx_p= index_drop_buffer, idx_end_p= idx_p + index_drop_count; - idx_p < idx_end_p; - idx_p++) - { - key= table->key_info + *idx_p; - DBUG_PRINT("info", ("index dropped: '%s'", key->name)); - if (key->flags & HA_NOSAME) - { - /* - Unique key. Check for "PRIMARY". - or if dropping last unique key - */ - if ((uint) (key - table->key_info) == table->s->primary_key) - { - DBUG_PRINT("info", ("Dropping primary key")); - /* Primary key. */ - needed_inplace_with_read_flags|= HA_INPLACE_DROP_PK_INDEX_NO_WRITE; - needed_inplace_flags|= HA_INPLACE_DROP_PK_INDEX_NO_READ_WRITE; - pk_changed++; - candidate_key_count--; - } - else - { - KEY_PART_INFO *part_end= key->key_part + key->key_parts; - bool is_candidate_key= true; - /* Non-primary unique key. */ - needed_inplace_with_read_flags|= - HA_INPLACE_DROP_UNIQUE_INDEX_NO_WRITE; - needed_inplace_flags|= HA_INPLACE_DROP_UNIQUE_INDEX_NO_READ_WRITE; - - /* - Check if all fields in key are declared - NOT NULL and adjust candidate_key_count - */ - for (KEY_PART_INFO *key_part= key->key_part; - key_part < part_end; - key_part++) - is_candidate_key= - (is_candidate_key && - (! table->field[key_part->fieldnr-1]->maybe_null())); - if (is_candidate_key) - candidate_key_count--; - } - } - else - { - /* Non-unique key. */ - needed_inplace_with_read_flags|= HA_INPLACE_DROP_INDEX_NO_WRITE; - needed_inplace_flags|= HA_INPLACE_DROP_INDEX_NO_READ_WRITE; - } - } - no_pk= ((table->s->primary_key == MAX_KEY) || - (needed_inplace_with_read_flags & - HA_INPLACE_DROP_PK_INDEX_NO_WRITE)); - /* Check added indexes. */ - for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count; - idx_p < idx_end_p; - idx_p++) - { - key= key_info_buffer + *idx_p; - DBUG_PRINT("info", ("index added: '%s'", key->name)); - if (key->flags & HA_NOSAME) - { - /* Unique key */ - - KEY_PART_INFO *part_end= key->key_part + key->key_parts; - bool is_candidate_key= true; - - /* - Check if all fields in key are declared - NOT NULL - */ - for (KEY_PART_INFO *key_part= key->key_part; - key_part < part_end; - key_part++) - is_candidate_key= - (is_candidate_key && - (! table->field[key_part->fieldnr]->maybe_null())); + alter_info->change_level= need_copy_table_res; - /* - Check for "PRIMARY" - or if adding first unique key - defined on non-nullable fields - */ - - if ((!my_strcasecmp(system_charset_info, - key->name, primary_key_name)) || - (no_pk && candidate_key_count == 0 && is_candidate_key)) - { - DBUG_PRINT("info", ("Adding primary key")); - /* Primary key. */ - needed_inplace_with_read_flags|= HA_INPLACE_ADD_PK_INDEX_NO_WRITE; - needed_inplace_flags|= HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE; - pk_changed++; - no_pk= false; - } - else - { - /* Non-primary unique key. */ - needed_inplace_with_read_flags|= HA_INPLACE_ADD_UNIQUE_INDEX_NO_WRITE; - needed_inplace_flags|= HA_INPLACE_ADD_UNIQUE_INDEX_NO_READ_WRITE; - } - } - else - { - /* Non-unique key. */ - needed_inplace_with_read_flags|= HA_INPLACE_ADD_INDEX_NO_WRITE; - needed_inplace_flags|= HA_INPLACE_ADD_INDEX_NO_READ_WRITE; - } - } - - if ((candidate_key_count > 0) && - (needed_inplace_with_read_flags & HA_INPLACE_DROP_PK_INDEX_NO_WRITE)) - { - /* - Dropped primary key when there is some other unique - not null key that should be converted to primary key - */ - needed_inplace_with_read_flags|= HA_INPLACE_ADD_PK_INDEX_NO_WRITE; - needed_inplace_flags|= HA_INPLACE_ADD_PK_INDEX_NO_READ_WRITE; - pk_changed= 2; - } + DBUG_EXECUTE_IF("alter_table_only_index_change", { + if (alter_info->change_level != ALTER_TABLE_INDEX_CHANGED) + goto err; }); - DBUG_PRINT("info", - ("needed_inplace_with_read_flags: 0x%lx, needed_inplace_flags: 0x%lx", - needed_inplace_with_read_flags, needed_inplace_flags)); /* - In-place add/drop index is possible only if - the primary key is not added and dropped in the same statement. - Otherwise we have to recreate the table. - need_copy_table is no-zero at this place. + If there are index changes only, try to do them in-place. "Index + changes only" means also that the handler for the table does not + change. The table is open and locked. The handler can be accessed. */ - if ( pk_changed < 2 ) - { - if ((alter_flags & needed_inplace_with_read_flags) == - needed_inplace_with_read_flags) - { - /* All required in-place flags to allow concurrent reads are present. */ - need_copy_table= ALTER_TABLE_METADATA_ONLY; - need_lock_for_indexes= FALSE; - } - else if ((alter_flags & needed_inplace_flags) == needed_inplace_flags) - { - /* All required in-place flags are present. */ - need_copy_table= ALTER_TABLE_METADATA_ONLY; - } - } - DBUG_PRINT("info", ("need_copy_table: %u need_lock: %d", - need_copy_table, need_lock_for_indexes)); + if (alter_info->change_level == ALTER_TABLE_INDEX_CHANGED && + can_alter_index_inplace(table, alter_info, + index_drop_count, index_drop_buffer, + index_add_count, index_add_buffer, + candidate_key_count, key_info_buffer, + &need_lock_for_indexes)) + alter_info->change_level= ALTER_TABLE_METADATA_ONLY; } - /* - better have a negative test here, instead of positive, like - alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|... - so that ALTER TABLE won't break when somebody will add new flag - */ - if (need_copy_table == ALTER_TABLE_METADATA_ONLY) - create_info->frm_only= 1; - #ifdef WITH_PARTITION_STORAGE_ENGINE if (table_for_fast_alter_partition) { @@ -6546,6 +6681,14 @@ bool mysql_alter_table(THD *thd,char *ne else create_info->data_file_name=create_info->index_file_name=0; + /* + better have a negative test here, instead of positive, like + alter_info->flags & ALTER_ADD_COLUMN|ALTER_ADD_INDEX|... + so that ALTER TABLE won't break when somebody will add new flag + */ + if (alter_info->change_level == ALTER_TABLE_METADATA_ONLY) + create_info->frm_only= 1; + DEBUG_SYNC(thd, "alter_table_before_create_table_no_lock"); DBUG_EXECUTE_IF("sleep_before_create_table_no_lock", my_sleep(100000);); @@ -6564,8 +6707,9 @@ bool mysql_alter_table(THD *thd,char *ne goto err; /* Open the table if we need to copy the data. */ - DBUG_PRINT("info", ("need_copy_table: %u", need_copy_table)); - if (need_copy_table != ALTER_TABLE_METADATA_ONLY) + DBUG_PRINT("info", ("Alter_info::change_level: %u", + alter_info->change_level)); + if (alter_info->change_level != ALTER_TABLE_METADATA_ONLY) { if (table->s->tmp_table) { @@ -6604,7 +6748,8 @@ bool mysql_alter_table(THD *thd,char *ne We do not copy data for MERGE tables. Only the children have data. MERGE tables have HA_NO_COPY_ON_ALTER set. */ - if (new_table && !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER)) + if (alter_info->change_level != ALTER_TABLE_METADATA_ONLY && + !(new_table->file->ha_table_flags() & HA_NO_COPY_ON_ALTER)) { /* We don't want update TIMESTAMP fields during ALTER TABLE. */ new_table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; @@ -6614,11 +6759,12 @@ bool mysql_alter_table(THD *thd,char *ne my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0)); goto err_new_table_cleanup; }); - error= copy_data_between_tables(table, new_table, - alter_info->create_list, ignore, - order_num, order, &copied, &deleted, - alter_info->keys_onoff, - alter_info->error_if_not_empty); + if (copy_data_between_tables(table, new_table, + alter_info->create_list, ignore, + order_num, order, &copied, &deleted, + alter_info->keys_onoff, + alter_info->error_if_not_empty)) + goto err_new_table_cleanup; } else { @@ -6636,102 +6782,19 @@ bool mysql_alter_table(THD *thd,char *ne DEBUG_SYNC(thd, "alter_table_manage_keys"); alter_table_manage_keys(table, table->file->indexes_are_disabled(), alter_info->keys_onoff); - error= trans_commit_stmt(thd); - if (trans_commit_implicit(thd)) - error= 1; + if (trans_commit_stmt(thd) || trans_commit_implicit(thd)) + goto err_new_table_cleanup; } thd->count_cuted_fields= CHECK_FIELD_IGNORE; /* If we did not need to copy, we might still need to add/drop indexes. */ - if (! new_table) + if (alter_info->change_level == ALTER_TABLE_METADATA_ONLY) { - uint *key_numbers; - uint *keyno_p; - KEY *key_info; - KEY *key; - uint *idx_p; - uint *idx_end_p; - KEY_PART_INFO *key_part; - KEY_PART_INFO *part_end; - DBUG_PRINT("info", ("No new_table, checking add/drop index")); - - table->file->ha_prepare_for_alter(); - if (index_add_count) - { - /* The add_index() method takes an array of KEY structs. */ - key_info= (KEY*) thd->alloc(sizeof(KEY) * index_add_count); - key= key_info; - for (idx_p= index_add_buffer, idx_end_p= idx_p + index_add_count; - idx_p < idx_end_p; - idx_p++, key++) - { - /* Copy the KEY struct. */ - *key= key_info_buffer[*idx_p]; - /* Fix the key parts. */ - part_end= key->key_part + key->key_parts; - for (key_part= key->key_part; key_part < part_end; key_part++) - key_part->field= table->field[key_part->fieldnr]; - } - /* Add the indexes. */ - if ((error= table->file->add_index(table, key_info, index_add_count))) - { - /* - Exchange the key_info for the error message. If we exchange - key number by key name in the message later, we need correct info. - */ - KEY *save_key_info= table->key_info; - table->key_info= key_info; - table->file->print_error(error, MYF(0)); - table->key_info= save_key_info; - goto err_new_table_cleanup; - } - } - /*end of if (index_add_count)*/ - - if (index_drop_count) - { - /* The prepare_drop_index() method takes an array of key numbers. */ - key_numbers= (uint*) thd->alloc(sizeof(uint) * index_drop_count); - keyno_p= key_numbers; - /* Get the number of each key. */ - for (idx_p= index_drop_buffer, idx_end_p= idx_p + index_drop_count; - idx_p < idx_end_p; - idx_p++, keyno_p++) - *keyno_p= *idx_p; - /* - Tell the handler to prepare for drop indexes. - This re-numbers the indexes to get rid of gaps. - */ - if ((error= table->file->prepare_drop_index(table, key_numbers, - index_drop_count))) - { - table->file->print_error(error, MYF(0)); - goto err_new_table_cleanup; - } - - /* Tell the handler to finally drop the indexes. */ - if ((error= table->file->final_drop_index(table))) - { - table->file->print_error(error, MYF(0)); - goto err_new_table_cleanup; - } - } - /*end of if (index_drop_count)*/ - - /* - The final .frm file is already created as a temporary file - and will be renamed to the original table name later. - */ - - /* Need to commit before a table is unlocked (NDB requirement). */ - DBUG_PRINT("info", ("Committing before unlocking table")); - if (trans_commit_stmt(thd) || trans_commit_implicit(thd)) + if (alter_indexes(thd, table, key_info_buffer, + index_drop_count, index_drop_buffer, + index_add_count, index_add_buffer)) goto err_new_table_cleanup; } - /*end of if (! new_table) for add/drop index*/ - - if (error) - goto err_new_table_cleanup; if (table->s->tmp_table != NO_TMP_TABLE) { @@ -6824,7 +6887,7 @@ bool mysql_alter_table(THD *thd,char *ne table is renamed and the SE is also changed, then an intermediate table is created and the additional call will not take place. */ - if (need_copy_table == ALTER_TABLE_METADATA_ONLY) + if (alter_info->change_level == ALTER_TABLE_METADATA_ONLY) { DBUG_ASSERT(new_db_type == old_db_type); /* This type cannot happen in regular ALTER. */ @@ -6833,36 +6896,31 @@ bool mysql_alter_table(THD *thd,char *ne if (mysql_rename_table(old_db_type, db, table_name, db, old_name, FN_TO_IS_TMP)) { - error=1; (void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP); + /* This shouldn't happen. But let us play it safe. */ + goto err_with_mdl; } else if (mysql_rename_table(new_db_type, new_db, tmp_name, new_db, new_alias, FN_FROM_IS_TMP) || ((new_name != table_name || new_db != db) && // we also do rename - (need_copy_table != ALTER_TABLE_METADATA_ONLY || + (alter_info->change_level != ALTER_TABLE_METADATA_ONLY || mysql_rename_table(save_old_db_type, db, table_name, new_db, new_alias, NO_FRM_RENAME)) && Table_triggers_list::change_table_name(thd, db, alias, table_name, new_db, new_alias))) { /* Try to get everything back. */ - error=1; (void) quick_rm_table(new_db_type,new_db,new_alias, 0); (void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP); (void) mysql_rename_table(old_db_type, db, old_name, db, alias, FN_FROM_IS_TMP); - } - - if (! error) - (void) quick_rm_table(old_db_type, db, old_name, FN_IS_TMP); - - if (error) - { /* This shouldn't happen. But let us play it safe. */ goto err_with_mdl; } - if (need_copy_table == ALTER_TABLE_METADATA_ONLY) + (void) quick_rm_table(old_db_type, db, old_name, FN_IS_TMP); + + if (alter_info->change_level == ALTER_TABLE_METADATA_ONLY) { /* Now we have to inform handler that new .FRM file is in place. --===============4429949998506993902== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/jon.hauglid@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: jon.hauglid@stripped # target_branch: file:///export/home/x/mysql-trunk-alter-refactor/ # testament_sha1: e1083222e18bb66ad0f1abb58e9b7b83504cde38 # timestamp: 2011-03-21 17:48:11 +0100 # base_revision_id: georgi.kodinov@stripped\ # 75h6l0wlpqx4lovj # # Begin bundle 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 nChIYoo11AA= --===============4429949998506993902==--