diff options
author | Sergei Golubchik <sergii@pisem.net> | 2011-07-02 22:08:51 +0200 |
---|---|---|
committer | Sergei Golubchik <sergii@pisem.net> | 2011-07-02 22:08:51 +0200 |
commit | 9809f05199aeb0b67991fac41bd86f38730768dc (patch) | |
tree | fa2792ff86d0da014b535d743759810612338042 /sql/sql_table.cc | |
parent | 0accbd0364e0333e0b119aa9ce93e34ded9df6cb (diff) | |
parent | 5a0e7394a5ae0c7b6a1ea35b7ea3a8985325987a (diff) | |
download | mariadb-git-9809f05199aeb0b67991fac41bd86f38730768dc.tar.gz |
5.5-merge
Diffstat (limited to 'sql/sql_table.cc')
-rw-r--r-- | sql/sql_table.cc | 549 |
1 files changed, 338 insertions, 211 deletions
diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 1a7162313f5..7d59598d606 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1,4 +1,4 @@ -/* Copyright 2000-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 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 @@ -22,10 +22,8 @@ #include "sql_rename.h" // do_rename #include "sql_parse.h" // test_if_data_home_dir #include "sql_cache.h" // query_cache_* -#include "sql_base.h" // open_temporary_table, lock_table_names -#include "lock.h" // wait_if_global_read_lock - // start_waiting_global_read_lock, - // mysql_unlock_tables +#include "sql_base.h" // open_table_uncached, lock_table_names +#include "lock.h" // mysql_unlock_tables #include "strfunc.h" // find_type2, find_set #include "sql_view.h" // view_checksum #include "sql_truncate.h" // regenerate_locked_table @@ -368,7 +366,11 @@ uint explain_filename(THD* thd, Table name length. */ -uint filename_to_tablename(const char *from, char *to, uint to_length) +uint filename_to_tablename(const char *from, char *to, uint to_length +#ifndef DBUG_OFF + , bool stay_quiet +#endif /* DBUG_OFF */ + ) { uint errors; size_t res; @@ -388,7 +390,13 @@ uint filename_to_tablename(const char *from, char *to, uint to_length) { res= (strxnmov(to, to_length, MYSQL50_TABLE_NAME_PREFIX, from, NullS) - to); - sql_print_error("Invalid (old?) table or database name '%s'", from); +#ifndef DBUG_OFF + if (!stay_quiet) { +#endif /* DBUG_OFF */ + sql_print_error("Invalid (old?) table or database name '%s'", from); +#ifndef DBUG_OFF + } +#endif /* DBUG_OFF */ /* TODO: add a stored procedure for fix table and database names, and mention its name in error log. @@ -1720,8 +1728,6 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) CHF_DELETE_FLAG, NULL) || deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) || (sync_ddl_log(), FALSE) || -#endif -#ifdef WITH_PARTITION_STORAGE_ENGINE mysql_file_rename(key_file_frm, shadow_frm_name, frm_name, MYF(MY_WME)) || lpt->table->file->ha_create_handler_files(path, shadow_path, @@ -1848,64 +1854,117 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, { bool error; Drop_table_error_handler err_handler; + TABLE_LIST *table; DBUG_ENTER("mysql_rm_table"); - /* mark for close and remove all cached entries */ + /* Disable drop of enabled log tables, must be done before name locking */ + for (table= tables; table; table= table->next_local) + { + if (check_if_log_table(table->db_length, table->db, + table->table_name_length, table->table_name, true)) + { + my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP"); + DBUG_RETURN(true); + } + } + + mysql_ha_rm_tables(thd, tables); if (!drop_temporary) { - if (!thd->locked_tables_mode && - thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE)) - DBUG_RETURN(TRUE); + if (!thd->locked_tables_mode) + { + if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout, + MYSQL_OPEN_SKIP_TEMPORARY)) + DBUG_RETURN(true); + for (table= tables; table; table= table->next_local) + tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name, + false); + } + else + { + for (table= tables; table; table= table->next_local) + if (table->open_type != OT_BASE_ONLY && + find_temporary_table(thd, table)) + { + /* + A temporary table. + + Don't try to find a corresponding MDL lock or assign it + to table->mdl_request.ticket. There can't be metadata + locks for temporary tables: they are local to the session. + + Later in this function we release the MDL lock only if + table->mdl_requeset.ticket is not NULL. Thus here we + ensure that we won't release the metadata lock on the base + table locked with LOCK TABLES as a side effect of temporary + table drop. + */ + DBUG_ASSERT(table->mdl_request.ticket == NULL); + } + else + { + /* + Not a temporary table. + + Since 'tables' list can't contain duplicates (this is ensured + by parser) it is safe to cache pointer to the TABLE instances + in its elements. + */ + table->table= find_table_for_mdl_upgrade(thd, table->db, + table->table_name, false); + if (!table->table) + DBUG_RETURN(true); + table->mdl_request.ticket= table->table->mdl_ticket; + } + } } + /* mark for close and remove all cached entries */ thd->push_internal_handler(&err_handler); - error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0); + error= mysql_rm_table_no_locks(thd, tables, if_exists, drop_temporary, + false, false); thd->pop_internal_handler(); - if (thd->global_read_lock.has_protection()) - thd->global_read_lock.start_waiting_global_read_lock(thd); - if (error) DBUG_RETURN(TRUE); my_ok(thd); DBUG_RETURN(FALSE); } -/* - Execute the drop of a normal or temporary table - - SYNOPSIS - mysql_rm_table_part2() - thd Thread handler - tables Tables to drop - if_exists If set, don't give an error if table doesn't exists. - In this case we give an warning of level 'NOTE' - drop_temporary Only drop temporary tables - drop_view Allow to delete VIEW .frm - dont_log_query Don't write query to log files. This will also not - generate warnings if the handler files doesn't exists - - TODO: - When logging to the binary log, we should log - tmp_tables and transactional tables as separate statements if we - are in a transaction; This is needed to get these tables into the - cached binary log that is only written on COMMIT. - - The current code only writes DROP statements that only uses temporary - tables to the cache binary log. This should be ok on most cases, but - not all. - RETURN - 0 ok - 1 Error - -1 Thread was killed +/** + Execute the drop of a normal or temporary table. + + @param thd Thread handler + @param tables Tables to drop + @param if_exists If set, don't give an error if table doesn't exists. + In this case we give an warning of level 'NOTE' + @param drop_temporary Only drop temporary tables + @param drop_view Allow to delete VIEW .frm + @param dont_log_query Don't write query to log files. This will also not + generate warnings if the handler files doesn't exists + + @retval 0 ok + @retval 1 Error + @retval -1 Thread was killed + + @note This function assumes that metadata locks have already been taken. + It is also assumed that the tables have been removed from TDC. + + @todo When logging to the binary log, we should log + tmp_tables and transactional tables as separate statements if we + are in a transaction; This is needed to get these tables into the + cached binary log that is only written on COMMIT. + The current code only writes DROP statements that only uses temporary + tables to the cache binary log. This should be ok on most cases, but + not all. */ -int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, - bool drop_temporary, bool drop_view, - bool dont_log_query) +int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, + bool drop_temporary, bool drop_view, + bool dont_log_query) { TABLE_LIST *table; char path[FN_REFLEN + 1], *alias= NULL; @@ -1919,7 +1978,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, bool non_tmp_table_deleted= 0; String built_query; String built_trans_tmp_query, built_non_trans_tmp_query; - DBUG_ENTER("mysql_rm_table_part2"); + DBUG_ENTER("mysql_rm_table_no_locks"); /* Prepares the drop statements that will be written into the binary @@ -1973,71 +2032,6 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, } } - mysql_ha_rm_tables(thd, tables); - - /* Disable drop of enabled log tables, must be done before name locking */ - for (table= tables; table; table= table->next_local) - { - if (check_if_log_table(table->db_length, table->db, - table->table_name_length, table->table_name, 1)) - { - my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP"); - DBUG_RETURN(1); - } - } - - if (!drop_temporary) - { - if (!thd->locked_tables_mode) - { - if (lock_table_names(thd, tables, NULL, thd->variables.lock_wait_timeout, - MYSQL_OPEN_SKIP_TEMPORARY)) - DBUG_RETURN(1); - for (table= tables; table; table= table->next_local) - { - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name, - FALSE); - } - } - else - { - for (table= tables; table; table= table->next_local) - if (table->open_type != OT_BASE_ONLY && - find_temporary_table(thd, table->db, table->table_name)) - { - /* - A temporary table. - - Don't try to find a corresponding MDL lock or assign it - to table->mdl_request.ticket. There can't be metadata - locks for temporary tables: they are local to the session. - - Later in this function we release the MDL lock only if - table->mdl_requeset.ticket is not NULL. Thus here we - ensure that we won't release the metadata lock on the base - table locked with LOCK TABLES as a side effect of temporary - table drop. - */ - DBUG_ASSERT(table->mdl_request.ticket == NULL); - } - else - { - /* - Not a temporary table. - - Since 'tables' list can't contain duplicates (this is ensured - by parser) it is safe to cache pointer to the TABLE instances - in its elements. - */ - table->table= find_table_for_mdl_upgrade(thd->open_tables, table->db, - table->table_name, FALSE); - if (!table->table) - DBUG_RETURN(1); - table->mdl_request.ticket= table->table->mdl_ticket; - } - } - } - for (table= tables; table; table= table->next_local) { bool is_trans; @@ -2050,6 +2044,16 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, table->table ? (long) table->table->s : (long) -1)); /* + If we are in locked tables mode and are dropping a temporary table, + the ticket should be NULL to ensure that we don't release a lock + on a base table later. + */ + DBUG_ASSERT(!(thd->locked_tables_mode && + table->open_type != OT_BASE_ONLY && + find_temporary_table(thd, table) && + table->mdl_request.ticket != NULL)); + + /* drop_temporary_table may return one of the following error codes: . 0 - a temporary table was successfully dropped. . 1 - a temporary table was not found. @@ -2124,6 +2128,10 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, table->table= 0; } + /* Check that we have an exclusive lock on the table to be dropped. */ + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db, + table->table_name, + MDL_EXCLUSIVE)); if (thd->killed) { error= -1; @@ -2166,8 +2174,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, built_query.append("`,"); } } - DEBUG_SYNC(thd, "rm_table_part2_before_delete_table"); - DBUG_EXECUTE_IF("sleep_before_part2_delete_table", + DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table"); + DBUG_EXECUTE_IF("sleep_before_no_locks_delete_table", my_sleep(100000);); error= 0; if (drop_temporary || @@ -2254,7 +2262,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, ER(ER_BAD_TABLE_ERROR), MYF(0), table->table_name);); } - DEBUG_SYNC(thd, "rm_table_part2_before_binlog"); + DEBUG_SYNC(thd, "rm_table_no_locks_before_binlog"); thd->thread_specific_used|= (trans_tmp_table_deleted || non_trans_tmp_table_deleted); error= 0; @@ -2605,7 +2613,8 @@ int prepare_create_field(Create_field *sql_field, MAX_FIELD_CHARLENGTH) { my_printf_error(ER_TOO_BIG_FIELDLENGTH, ER(ER_TOO_BIG_FIELDLENGTH), - MYF(0), sql_field->field_name, MAX_FIELD_CHARLENGTH); + MYF(0), sql_field->field_name, + static_cast<ulong>(MAX_FIELD_CHARLENGTH)); DBUG_RETURN(1); } } @@ -3648,12 +3657,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES))) { my_error(ER_TOO_LONG_INDEX_COMMENT, MYF(0), - key_info->name, (uint) INDEX_COMMENT_MAXLEN); + key_info->name, static_cast<ulong>(INDEX_COMMENT_MAXLEN)); DBUG_RETURN(-1); } char warn_buff[MYSQL_ERRMSG_SIZE]; my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_INDEX_COMMENT), - key_info->name, (uint) INDEX_COMMENT_MAXLEN); + key_info->name, static_cast<ulong>(INDEX_COMMENT_MAXLEN)); /* do not push duplicate warnings */ if (!check_duplicate_warning(thd, warn_buff, strlen(warn_buff))) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, @@ -3787,7 +3796,8 @@ static bool prepare_blob_field(THD *thd, Create_field *sql_field) MODE_STRICT_ALL_TABLES))) { my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name, - MAX_FIELD_VARCHARLENGTH / sql_field->charset->mbmaxlen); + static_cast<ulong>(MAX_FIELD_VARCHARLENGTH / + sql_field->charset->mbmaxlen)); DBUG_RETURN(1); } sql_field->sql_type= MYSQL_TYPE_BLOB; @@ -3865,6 +3875,46 @@ void sp_prepare_create_field(THD *thd, Create_field *sql_field) (void) prepare_blob_field(thd, sql_field); } + +/** + Auxiliary function which allows to check if freshly created .FRM + file for table can be opened. + + @retval FALSE - Success. + @retval TRUE - Failure. +*/ + +static bool check_if_created_table_can_be_opened(THD *thd, + const char *path, + const char *db, + const char *table_name, + HA_CREATE_INFO *create_info, + handler *file) +{ + TABLE table; + TABLE_SHARE share; + bool result; + + /* + It is impossible to open definition of partitioned table without .par file. + */ + if (file->ha_create_handler_files(path, NULL, CHF_CREATE_FLAG, create_info)) + return TRUE; + + init_tmp_table_share(thd, &share, db, 0, table_name, path); + + result= (open_table_def(thd, &share, 0) || + open_table_from_share(thd, &share, "", 0, (uint) READ_ALL, + 0, &table, TRUE)); + if (! result) + (void) closefrm(&table, 0); + + free_table_share(&share); + (void) file->ha_create_handler_files(path, NULL, CHF_DELETE_FLAG, create_info); + return result; +} + + /* Create a table @@ -4282,9 +4332,14 @@ bool mysql_create_table_no_lock(THD *thd, if (create_info->options & HA_LEX_CREATE_TMP_TABLE) { - TABLE *table= NULL; - /* Open table and put in temporary table list */ - if (!(table= open_temporary_table(thd, path, db, table_name, 1))) + /* + Open a table (skipping table cache) and add it into + THD::temporary_tables list. + */ + + TABLE *table= open_table_uncached(thd, path, db, table_name, TRUE); + + if (!table) { (void) rm_temporary_table(create_info->db_type, path); goto err; @@ -4295,6 +4350,29 @@ bool mysql_create_table_no_lock(THD *thd, thd->thread_specific_used= TRUE; } +#ifdef WITH_PARTITION_STORAGE_ENGINE + else if (part_info && create_info->frm_only) + { + /* + For partitioned tables we can't find some problems with table + until table is opened. Therefore in order to disallow creation + of corrupted tables we have to try to open table as the part + of its creation process. + In cases when both .FRM and SE part of table are created table + is implicitly open in ha_create_table() call. + In cases when we create .FRM without SE part we have to open + table explicitly. + */ + if (check_if_created_table_can_be_opened(thd, path, db, table_name, + create_info, file)) + { + char frm_name[FN_REFLEN]; + strxmov(frm_name, path, reg_ext, NullS); + (void) mysql_file_delete(key_file_frm, frm_name, MYF(0)); + goto err; + } + } +#endif error= FALSE; err: @@ -4561,6 +4639,11 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, local_create_info.options|= create_info->options & HA_LEX_CREATE_TMP_TABLE; /* Reset auto-increment counter for the new table. */ local_create_info.auto_increment_value= 0; + /* + Do not inherit values of DATA and INDEX DIRECTORY options from + the original table. This is documented behavior. + */ + local_create_info.data_file_name= local_create_info.index_file_name= NULL; if ((res= mysql_create_table_no_lock(thd, table->db, table->table_name, &local_create_info, &local_alter_info, @@ -4726,7 +4809,7 @@ err: @details Checks if any index is being modified (present as both DROP INDEX and ADD INDEX) in the current ALTER TABLE statement. Needed for disabling - online ALTER TABLE. + in-place ALTER TABLE. @param table The table being altered @param alter_info The ALTER TABLE structure @@ -4843,7 +4926,7 @@ mysql_compare_tables(TABLE *table, like to keep mysql_compare_tables() idempotent (not altering any of the arguments) we create a copy of alter_info here and pass it to mysql_prepare_create_table, then use the result - to evaluate possibility of fast ALTER TABLE, and then + to evaluate possibility of in-place ALTER TABLE, and then destroy the copy. */ Alter_info tmp_alter_info(*alter_info, thd->mem_root); @@ -4887,9 +4970,9 @@ mysql_compare_tables(TABLE *table, There was a bug prior to mysql-4.0.25. Number of null fields was calculated incorrectly. As a result frm and data files gets out of - sync after fast alter table. There is no way to determine by which + sync after in-place alter table. There is no way to determine by which mysql version (in 4.0 and 4.1 branches) table was created, thus we - disable fast alter table for all tables created by mysql versions + disable in-place alter table for all tables created by mysql versions prior to 5.0 branch. See BUG#6236. */ @@ -4920,7 +5003,7 @@ mysql_compare_tables(TABLE *table, DBUG_RETURN(1); /* - Use transformed info to evaluate possibility of fast ALTER TABLE + Use transformed info to evaluate possibility of in-place ALTER TABLE but use the preserved field to persist modifications. */ new_field_it.init(alter_info->create_list); @@ -5230,7 +5313,7 @@ blob_length_by_type(enum_field_types type) semantic checks. This function is invoked when we know that we're going to - perform ALTER TABLE via a temporary table -- i.e. fast ALTER TABLE + perform ALTER TABLE via a temporary table -- i.e. in-place ALTER TABLE is not possible, perhaps because the ALTER statement contains instructions that require change in table data, not only in table definition or indexes. @@ -5308,17 +5391,6 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, if (!(used_fields & HA_CREATE_USED_TRANSACTIONAL)) create_info->transactional= table->s->transactional; - if (!create_info->tablespace && create_info->storage_media != HA_SM_MEMORY) - { - char *tablespace= static_cast<char *>(thd->alloc(FN_LEN + 1)); - /* - Regular alter table of disk stored table (no tablespace/storage change) - Copy tablespace name - */ - if (tablespace && - (table->file->get_tablespace_name(thd, tablespace, FN_LEN))) - create_info->tablespace= tablespace; - } restore_record(table, s->default_values); // Empty record for DEFAULT create_info->option_list= merge_engine_table_options(table->s->option_list, @@ -5706,7 +5778,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, TABLE *table, *new_table= 0; MDL_ticket *mdl_ticket; MDL_request target_mdl_request; - bool has_target_mdl_lock= FALSE; int error= 0; char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1]; char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias; @@ -5717,7 +5788,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, 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 - uint fast_alter_partition= 0; + TABLE *table_for_fast_alter_partition= NULL; bool partition_changed= FALSE; #endif bool need_lock_for_indexes= TRUE; @@ -5725,6 +5796,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, uint index_drop_count= 0; uint *index_drop_buffer= NULL; uint index_add_count= 0; + handler_add_index *add= NULL; + bool pending_inplace_add_index= false; uint *index_add_buffer= NULL; uint candidate_key_count= 0; bool no_pk; @@ -5872,7 +5945,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, else { target_mdl_request.init(MDL_key::TABLE, new_db, new_name, - MDL_EXCLUSIVE); + MDL_EXCLUSIVE, MDL_TRANSACTION); /* Global intention exclusive lock must have been already acquired when table to be altered was open, so there is no need to do it here. @@ -5890,7 +5963,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, DBUG_RETURN(TRUE); } DEBUG_SYNC(thd, "locked_table_name"); - has_target_mdl_lock= TRUE; /* Table maybe does not exist, but we got an exclusive lock on the name, now we can safely try to find out for sure. @@ -6037,7 +6109,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, *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, table_name, + 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, @@ -6078,10 +6151,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, along with the implicit commit. */ if (new_name != table_name || new_db != db) - { - thd->mdl_context.release_lock(target_mdl_request.ticket); thd->mdl_context.release_all_locks_for_name(mdl_ticket); - } else mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE); } @@ -6092,7 +6162,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, #ifdef WITH_PARTITION_STORAGE_ENGINE if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type, - &partition_changed, &fast_alter_partition)) + &partition_changed, + db, table_name, path, + &table_for_fast_alter_partition)) goto err; #endif /* @@ -6154,7 +6226,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, } /* - If there are index changes only, try to do them online. "Index + 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. */ @@ -6162,8 +6234,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, { int pk_changed= 0; ulong alter_flags= 0; - ulong needed_online_flags= 0; - ulong needed_fast_flags= 0; + ulong needed_inplace_with_read_flags= 0; + ulong needed_inplace_flags= 0; KEY *key; uint *idx_p; uint *idx_end_p; @@ -6187,8 +6259,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, { DBUG_PRINT("info", ("Dropping primary key")); /* Primary key. */ - needed_online_flags|= HA_ONLINE_DROP_PK_INDEX; - needed_fast_flags|= HA_ONLINE_DROP_PK_INDEX_NO_WRITES; + 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--; } @@ -6198,8 +6270,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, bool is_candidate_key= true; /* Non-primary unique key. */ - needed_online_flags|= HA_ONLINE_DROP_UNIQUE_INDEX; - needed_fast_flags|= HA_ONLINE_DROP_UNIQUE_INDEX_NO_WRITES; + 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 @@ -6218,12 +6291,13 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, else { /* Non-unique key. */ - needed_online_flags|= HA_ONLINE_DROP_INDEX; - needed_fast_flags|= HA_ONLINE_DROP_INDEX_NO_WRITES; + 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_online_flags & HA_ONLINE_DROP_PK_INDEX)); + (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; @@ -6261,57 +6335,59 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, { DBUG_PRINT("info", ("Adding primary key")); /* Primary key. */ - needed_online_flags|= HA_ONLINE_ADD_PK_INDEX; - needed_fast_flags|= HA_ONLINE_ADD_PK_INDEX_NO_WRITES; + 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_online_flags|= HA_ONLINE_ADD_UNIQUE_INDEX; - needed_fast_flags|= HA_ONLINE_ADD_UNIQUE_INDEX_NO_WRITES; + 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_online_flags|= HA_ONLINE_ADD_INDEX; - needed_fast_flags|= HA_ONLINE_ADD_INDEX_NO_WRITES; + 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_online_flags & HA_ONLINE_DROP_PK_INDEX)) + 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_online_flags|= HA_ONLINE_ADD_PK_INDEX; - needed_fast_flags|= HA_ONLINE_ADD_PK_INDEX_NO_WRITES; + 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_online_flags: 0x%lx, needed_fast_flags: 0x%lx", - needed_online_flags, needed_fast_flags)); + DBUG_PRINT("info", + ("needed_inplace_with_read_flags: 0x%lx, needed_inplace_flags: 0x%lx", + needed_inplace_with_read_flags, needed_inplace_flags)); /* - Online or fast add/drop index is possible only if + 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_online_flags) == needed_online_flags) + if ((alter_flags & needed_inplace_with_read_flags) == + needed_inplace_with_read_flags) { - /* All required online flags are present. */ + /* 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_fast_flags) == needed_fast_flags) + else if ((alter_flags & needed_inplace_flags) == needed_inplace_flags) { - /* All required fast flags are present. */ + /* All required in-place flags are present. */ need_copy_table= ALTER_TABLE_METADATA_ONLY; } } @@ -6328,12 +6404,12 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, create_info->frm_only= 1; #ifdef WITH_PARTITION_STORAGE_ENGINE - if (fast_alter_partition) + if (table_for_fast_alter_partition) { DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info, create_info, table_list, db, table_name, - fast_alter_partition)); + table_for_fast_alter_partition)); } #endif @@ -6428,8 +6504,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, /* table is a normal table: Create temporary table in same directory */ build_table_filename(path, sizeof(path) - 1, new_db, tmp_name, "", FN_IS_TMP); - /* Open our intermediate table */ - new_table= open_temporary_table(thd, path, new_db, tmp_name, 1); + /* Open our intermediate table. */ + new_table= open_table_uncached(thd, path, new_db, tmp_name, TRUE); } if (!new_table) goto err_new_table_cleanup; @@ -6465,10 +6541,18 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, } else { + /* + Ensure that we will upgrade the metadata lock if + handler::enable/disable_indexes() will be called. + */ + if (alter_info->keys_onoff != LEAVE_AS_IS || + table->file->indexes_are_disabled()) + need_lock_for_indexes= true; if (!table->s->tmp_table && wait_while_table_is_used(thd, table, extra_func)) goto err_new_table_cleanup; thd_proc_info(thd, "manage keys"); + 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); @@ -6477,6 +6561,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, } thd->count_cuted_fields= CHECK_FIELD_IGNORE; + if (error) + goto err_new_table_cleanup; + /* If we did not need to copy, we might still need to add/drop indexes. */ if (! new_table) { @@ -6508,7 +6595,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, key_part->field= table->field[key_part->fieldnr]; } /* Add the indexes. */ - if ((error= table->file->add_index(table, key_info, index_add_count))) + if ((error= table->file->add_index(table, key_info, index_add_count, + &add))) { #ifdef MERGE_MONTY_ADDITION_THAT_BREAKS_5_5_TESTS /* Only report error if handler has not already reported an error */ @@ -6526,11 +6614,27 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, } goto err_new_table_cleanup; } + pending_inplace_add_index= true; } /*end of if (index_add_count)*/ if (index_drop_count) { + /* Currently we must finalize add index if we also drop indexes */ + if (pending_inplace_add_index) + { + /* Committing index changes needs exclusive metadata lock. */ + DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, + table_list->db, + table_list->table_name, + MDL_EXCLUSIVE)); + if ((error= table->file->final_add_index(add, true))) + { + table->file->print_error(error, MYF(0)); + goto err_new_table_cleanup; + } + pending_inplace_add_index= false; + } /* 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; @@ -6571,11 +6675,14 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, } /*end of if (! new_table) for add/drop index*/ - if (error) - goto err_new_table_cleanup; - if (table->s->tmp_table != NO_TMP_TABLE) { + /* + In-place operations are not supported for temporary tables, so + we don't have to call final_add_index() in this case. The assert + verifies that in-place add index has not been done. + */ + DBUG_ASSERT(!pending_inplace_add_index); /* Close lock if this is a transactional table */ if (thd->lock) { @@ -6642,8 +6749,30 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, my_casedn_str(files_charset_info, old_name); if (wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_RENAME)) + { + if (pending_inplace_add_index) + { + pending_inplace_add_index= false; + table->file->final_add_index(add, false); + } + // Mark this TABLE instance as stale to avoid out-of-sync index information. + table->m_needs_reopen= true; goto err_new_table_cleanup; - + } + if (pending_inplace_add_index) + { + pending_inplace_add_index= false; + DBUG_EXECUTE_IF("alter_table_rollback_new_index", { + table->file->final_add_index(add, false); + my_error(ER_UNKNOWN_ERROR, MYF(0)); + goto err_new_table_cleanup; + }); + if ((error= table->file->final_add_index(add, true))) + { + table->file->print_error(error, MYF(0)); + goto err_new_table_cleanup; + } + } close_all_tables_for_name(thd, table->s, new_name != table_name || new_db != db); @@ -6680,11 +6809,11 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, 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 || - 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, table_name, - new_db, new_alias))) + (need_copy_table != 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; @@ -6711,15 +6840,15 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, NO need to tamper with MERGE tables. The real open is done later. */ Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN); - TABLE *t_table; + TABLE_LIST temp_table_list; + TABLE_LIST *t_table_list; if (new_name != table_name || new_db != db) { - table_list->alias= new_name; - table_list->table_name= new_name; - table_list->table_name_length= strlen(new_name); - table_list->db= new_db; - table_list->db_length= strlen(new_db); - table_list->mdl_request.ticket= target_mdl_request.ticket; + temp_table_list.init_one_table(new_db, strlen(new_db), + new_name, strlen(new_name), + new_name, TL_READ_NO_INSERT); + temp_table_list.mdl_request.ticket= target_mdl_request.ticket; + t_table_list= &temp_table_list; } else { @@ -6729,20 +6858,21 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, to request the lock. */ table_list->mdl_request.ticket= mdl_ticket; + t_table_list= table_list; } - if (open_table(thd, table_list, thd->mem_root, &ot_ctx)) + if (open_table(thd, t_table_list, thd->mem_root, &ot_ctx)) { goto err_with_mdl; } - t_table= table_list->table; /* Tell the handler that a new frm file is in place. */ - error= t_table->file->ha_create_handler_files(path, NULL, CHF_INDEX_FLAG, - create_info); + error= t_table_list->table->file->ha_create_handler_files(path, NULL, + CHF_INDEX_FLAG, + create_info); - DBUG_ASSERT(thd->open_tables == t_table); + DBUG_ASSERT(thd->open_tables == t_table_list->table); close_thread_table(thd, &thd->open_tables); - table_list->table= 0; + t_table_list->table= NULL; if (error) goto err_with_mdl; @@ -6772,10 +6902,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES) { if ((new_name != table_name || new_db != db)) - { - thd->mdl_context.release_lock(target_mdl_request.ticket); thd->mdl_context.release_all_locks_for_name(mdl_ticket); - } else mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE); } @@ -6798,6 +6925,11 @@ err_new_table_cleanup: create_info->frm_only ? FN_IS_TMP | FRM_ONLY : FN_IS_TMP); err: +#ifdef WITH_PARTITION_STORAGE_ENGINE + /* If prep_alter_part_table created an intermediate table, destroy it. */ + if (table_for_fast_alter_partition) + close_temporary(table_for_fast_alter_partition, 1, 0); +#endif /* WITH_PARTITION_STORAGE_ENGINE */ /* No default value was provided for a DATE/DATETIME field, the current sql_mode doesn't allow the '0000-00-00' value and @@ -6831,8 +6963,6 @@ err: alter_info->datetime_field->field_name); thd->abort_on_warning= save_abort_on_warning; } - if (has_target_mdl_lock) - thd->mdl_context.release_lock(target_mdl_request.ticket); DBUG_RETURN(TRUE); @@ -6844,9 +6974,6 @@ err_with_mdl: tables and release the exclusive metadata lock. */ thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); - if (has_target_mdl_lock) - thd->mdl_context.release_lock(target_mdl_request.ticket); - thd->mdl_context.release_all_locks_for_name(mdl_ticket); DBUG_RETURN(TRUE); } |