diff options
Diffstat (limited to 'sql/sql_table.cc')
-rw-r--r-- | sql/sql_table.cc | 470 |
1 files changed, 349 insertions, 121 deletions
diff --git a/sql/sql_table.cc b/sql/sql_table.cc index ff389a96a8d..ff2e7939fb8 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2031,33 +2031,29 @@ 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"); /* 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"); + if (check_if_log_table(table, TRUE, "DROP")) DBUG_RETURN(true); - } } - if (!in_bootstrap) + if (!drop_temporary) { - for (table= tables; table; table= table->next_local) + if (!in_bootstrap) { - LEX_STRING db_name= { table->db, table->db_length }; - LEX_STRING table_name= { table->table_name, table->table_name_length }; - if (table->open_type == OT_BASE_ONLY || !find_temporary_table(thd, table)) - (void) delete_statistics_for_table(thd, &db_name, &table_name); + for (table= tables; table; table= table->next_local) + { + LEX_STRING db_name= { table->db, table->db_length }; + LEX_STRING table_name= { table->table_name, table->table_name_length }; + if (table->open_type == OT_BASE_ONLY || + !find_temporary_table(thd, table)) + (void) delete_statistics_for_table(thd, &db_name, &table_name); + } } - } - if (!drop_temporary) - { if (!thd->locked_tables_mode) { if (lock_table_names(thd, tables, NULL, @@ -2107,7 +2103,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, /* mark for close and remove all cached entries */ thd->push_internal_handler(&err_handler); error= mysql_rm_table_no_locks(thd, tables, if_exists, drop_temporary, - false, false); + false, false, false); thd->pop_internal_handler(); if (error) @@ -2172,6 +2168,8 @@ static uint32 comment_length(THD *thd, uint32 comment_pos, @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 + @param dont_free_locks Don't do automatic UNLOCK TABLE if no more locked + tables @retval 0 ok @retval 1 Error @@ -2194,7 +2192,8 @@ static uint32 comment_length(THD *thd, uint32 comment_pos, int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool drop_temporary, bool drop_view, - bool dont_log_query) + bool dont_log_query, + bool dont_free_locks) { TABLE_LIST *table; char path[FN_REFLEN + 1], wrong_tables_buff[160], *alias= NULL; @@ -2208,6 +2207,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool trans_tmp_table_deleted= 0, non_trans_tmp_table_deleted= 0; bool non_tmp_table_deleted= 0; bool is_drop_tmp_if_exists_added= 0; + bool one_table= tables->next_local == 0; + bool was_view= 0; String built_query; String built_trans_tmp_query, built_non_trans_tmp_query; DBUG_ENTER("mysql_rm_table_no_locks"); @@ -2286,7 +2287,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, for (table= tables; table; table= table->next_local) { - bool is_trans; + bool is_trans= 0; char *db=table->db; size_t db_length= table->db_length; handlerton *table_type= 0; @@ -2311,12 +2312,16 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, . 1 - a temporary table was not found. . -1 - a temporary table is used by an outer statement. */ - if (table->open_type == OT_BASE_ONLY) + if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table)) error= 1; - else if ((error= drop_temporary_table(thd, table, &is_trans)) == -1) + else { - DBUG_ASSERT(thd->in_sub_stmt); - goto err; + if ((error= drop_temporary_table(thd, table->table, &is_trans)) == -1) + { + DBUG_ASSERT(thd->in_sub_stmt); + goto err; + } + table->table= 0; } if ((drop_temporary && if_exists) || !error) @@ -2384,7 +2389,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, This handles the case where a "DROP" was executed and a regular table "may be" dropped as drop_temporary is FALSE and error is TRUE. If the error was FALSE a temporary table was dropped and - regardless of the status of drop_tempoary a "DROP TEMPORARY" + regardless of the status of drop_temporary a "DROP TEMPORARY" must be used. */ if (!dont_log_query) @@ -2412,15 +2417,15 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, } DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table"); error= 0; - if ((drop_temporary || !ha_table_exists(thd, db, alias, &table_type) || - (!drop_view && table_type == view_pseudo_hton))) + if (drop_temporary || + (ha_table_exists(thd, db, alias, &table_type) == 0 && table_type == 0) || + (!drop_view && (was_view= (table_type == view_pseudo_hton)))) { /* One of the following cases happened: . "DROP TEMPORARY" but a temporary table was not found. - . "DROP" but table was not found on disk and table can't be - created from engine. - . ./sql/datadict.cc +32 /Alfranio - TODO: We need to test this. + . "DROP" but table was not found + . "DROP TABLE" statement, but it's a view. */ if (if_exists) { @@ -2544,7 +2549,10 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, err: if (wrong_tables.length()) { - if (!foreign_key_error) + if (one_table && was_view) + my_printf_error(ER_IT_IS_A_VIEW, ER(ER_IT_IS_A_VIEW), MYF(0), + wrong_tables.c_ptr_safe()); + else if (!foreign_key_error) my_printf_error(ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR), MYF(0), wrong_tables.c_ptr_safe()); else @@ -2610,7 +2618,8 @@ err: */ if (thd->locked_tables_mode) { - if (thd->lock && thd->lock->table_count == 0 && non_temp_tables_count > 0) + if (thd->lock && thd->lock->table_count == 0 && + non_temp_tables_count > 0 && !dont_free_locks) { thd->locked_tables_list.unlock_locked_tables(thd); goto end; @@ -3793,7 +3802,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, with length (unlike blobs, where ft code takes data length from a data prefix, ignoring column->length). */ - column->length=test(f_is_blob(sql_field->pack_flag)); + column->length= MY_TEST(f_is_blob(sql_field->pack_flag)); } else { @@ -4517,12 +4526,13 @@ err: way to ensure that concurrent operations won't intervene. mysql_create_table() is a wrapper that can be used for this. - @retval false OK - @retval true error + @retval 0 OK + @retval 1 error + @retval -1 table existed but IF EXISTS was used */ static -bool create_table_impl(THD *thd, +int create_table_impl(THD *thd, const char *db, const char *table_name, const char *path, HA_CREATE_INFO *create_info, @@ -4535,7 +4545,7 @@ bool create_table_impl(THD *thd, { const char *alias; handler *file= 0; - bool error= TRUE; + int error= 1; bool frm_only= create_table_mode == C_ALTER_TABLE_FRM_ONLY; bool internal_tmp_table= create_table_mode == C_ALTER_TABLE || frm_only; DBUG_ENTER("mysql_create_table_no_lock"); @@ -4565,22 +4575,74 @@ bool create_table_impl(THD *thd, /* Check if table exists */ if (create_info->tmp_table()) { - if (find_temporary_table(thd, db, table_name)) + TABLE *tmp_table; + if ((tmp_table= find_temporary_table(thd, db, table_name))) { - if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) + if (create_info->options & HA_LEX_CREATE_REPLACE) + { + bool is_trans; + /* + We are using CREATE OR REPLACE on an existing temporary table + Remove the old table so that we can re-create it. + */ + if (drop_temporary_table(thd, tmp_table, &is_trans)) + goto err; + } + else if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) goto warn; - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias); - goto err; + else + { + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alias); + goto err; + } } } - else + else { if (!internal_tmp_table && ha_table_exists(thd, db, table_name)) { - if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) + if (create_info->options & HA_LEX_CREATE_REPLACE) + { + TABLE_LIST table_list; + table_list.init_one_table(db, strlen(db), table_name, + strlen(table_name), table_name, + TL_WRITE_ALLOW_WRITE); + table_list.table= create_info->table; + + if (check_if_log_table(&table_list, TRUE, "CREATE OR REPLACE")) + goto err; + + /* + Rollback the empty transaction started in mysql_create_table() + call to open_and_lock_tables() when we are using LOCK TABLES. + */ + (void) trans_rollback_stmt(thd); + /* Remove normal table without logging. Keep tables locked */ + if (mysql_rm_table_no_locks(thd, &table_list, 0, 0, 0, 1, 1)) + goto err; + + /* + We have to log this query, even if it failed later to ensure the + drop is done. + */ + thd->variables.option_bits|= OPTION_KEEP_LOG; + thd->log_current_statement= 1; + + /* + The test of query_tables is to ensure we have any tables in the + select part + */ + if (thd->lex->query_tables && + restart_trans_for_tables(thd, thd->lex->query_tables->next_global)) + goto err; + } + else if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS) goto warn; - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name); - goto err; + else + { + my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name); + goto err; + } } } @@ -4702,14 +4764,14 @@ bool create_table_impl(THD *thd, } #endif - error= FALSE; + error= 0; err: THD_STAGE_INFO(thd, stage_after_create); delete file; DBUG_RETURN(error); warn: - error= FALSE; + error= -1; push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR), alias); @@ -4720,7 +4782,8 @@ warn: Simple wrapper around create_table_impl() to be used in various version of CREATE TABLE statement. */ -bool mysql_create_table_no_lock(THD *thd, + +int mysql_create_table_no_lock(THD *thd, const char *db, const char *table_name, HA_CREATE_INFO *create_info, Alter_info *alter_info, bool *is_trans, @@ -4728,6 +4791,7 @@ bool mysql_create_table_no_lock(THD *thd, { KEY *not_used_1; uint not_used_2; + int res; char path[FN_REFLEN + 1]; LEX_CUSTRING frm= {0,0}; @@ -4747,9 +4811,9 @@ bool mysql_create_table_no_lock(THD *thd, } } - bool res= create_table_impl(thd, db, table_name, path, create_info, - alter_info, create_table_mode, is_trans, - ¬_used_1, ¬_used_2, &frm); + res= create_table_impl(thd, db, table_name, path, create_info, + alter_info, create_table_mode, is_trans, + ¬_used_1, ¬_used_2, &frm); my_free(const_cast<uchar*>(frm.str)); return res; } @@ -4771,16 +4835,23 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, const char *db= create_table->db; const char *table_name= create_table->table_name; bool is_trans= FALSE; + bool result= 0; int create_table_mode; + TABLE_LIST *pos_in_locked_tables= 0; DBUG_ENTER("mysql_create_table"); + DBUG_ASSERT(create_table == thd->lex->query_tables); + /* Open or obtain an exclusive metadata lock on table being created */ if (open_and_lock_tables(thd, thd->lex->query_tables, FALSE, 0)) { /* is_error() may be 0 if table existed and we generated a warning */ DBUG_RETURN(thd->is_error()); } - + /* The following is needed only in case of lock tables */ + if ((create_info->table= thd->lex->query_tables->table)) + pos_in_locked_tables= create_info->table->pos_in_locked_tables; + /* Got lock. */ DEBUG_SYNC(thd, "locked_table_name"); @@ -4791,15 +4862,42 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, promote_first_timestamp_column(&alter_info->create_list); if (mysql_create_table_no_lock(thd, db, table_name, create_info, alter_info, - &is_trans, create_table_mode)) - DBUG_RETURN(1); + &is_trans, create_table_mode) > 0) + { + result= 1; + goto err; + } + + /* + Check if we are doing CREATE OR REPLACE TABLE under LOCK TABLES + on a non temporary table + */ + if (thd->locked_tables_mode && pos_in_locked_tables && + (create_info->options & HA_LEX_CREATE_REPLACE)) + { + /* + Add back the deleted table and re-created table as a locked table + This should always work as we have a meta lock on the table. + */ + thd->locked_tables_list.add_back_last_deleted_lock(pos_in_locked_tables); + if (thd->locked_tables_list.reopen_tables(thd)) + thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); + else + { + TABLE *table= pos_in_locked_tables->table; + table->mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); + } + } +err: /* In RBR we don't need to log CREATE TEMPORARY TABLE */ if (thd->is_current_stmt_binlog_format_row() && create_info->tmp_table()) - DBUG_RETURN(0); - - bool result; - result= write_bin_log(thd, TRUE, thd->query(), thd->query_length(), is_trans); + DBUG_RETURN(result); + /* Write log if no error or if we already deleted a table */ + if (!result || thd->log_current_statement) + if (write_bin_log(thd, result ? FALSE : TRUE, thd->query(), + thd->query_length(), is_trans)) + result= 1; DBUG_RETURN(result); } @@ -4986,15 +5084,19 @@ mysql_rename_table(handlerton *base, const char *old_db, TRUE error */ -bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, +bool mysql_create_like_table(THD* thd, TABLE_LIST* table, + TABLE_LIST* src_table, HA_CREATE_INFO *create_info) { HA_CREATE_INFO local_create_info; + TABLE_LIST *pos_in_locked_tables= 0; Alter_info local_alter_info; Alter_table_ctx local_alter_ctx; // Not used bool res= TRUE; bool is_trans= FALSE; + bool do_logging= FALSE; uint not_used; + int create_res; DBUG_ENTER("mysql_create_like_table"); #ifdef WITH_WSREP @@ -5038,7 +5140,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, String query(buf, sizeof(buf), system_charset_info); query.length(0); // Have to zero it since constructor doesn't - (void) store_create_info(thd, &tbl, &query, NULL, TRUE); + (void) store_create_info(thd, &tbl, &query, NULL, TRUE, FALSE); WSREP_DEBUG("TMP TABLE: %s", query.ptr()); thd->wsrep_TOI_pre_query= query.ptr(); @@ -5068,6 +5170,18 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, res= thd->is_error(); goto err; } + /* Ensure we don't try to create something from which we select from */ + if ((create_info->options & HA_LEX_CREATE_REPLACE) && + !create_info->tmp_table()) + { + TABLE_LIST *duplicate; + if ((duplicate= unique_table(thd, table, src_table, 0))) + { + update_non_unique_table_error(src_table, "CREATE", duplicate); + goto err; + } + } + src_table->table->use_all_columns(); DEBUG_SYNC(thd, "create_table_like_after_open"); @@ -5095,7 +5209,9 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, if (src_table->schema_table) local_create_info.max_rows= 0; /* Set IF NOT EXISTS option as in the CREATE TABLE LIKE statement. */ - local_create_info.options|= create_info->options&HA_LEX_CREATE_IF_NOT_EXISTS; + local_create_info.options|= (create_info->options & + (HA_LEX_CREATE_IF_NOT_EXISTS | + HA_LEX_CREATE_REPLACE)); /* Replace type of source table with one specified in the statement. */ local_create_info.options&= ~HA_LEX_CREATE_TMP_TABLE; local_create_info.options|= create_info->tmp_table(); @@ -5107,19 +5223,54 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, */ 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, - &is_trans, C_ORDINARY_CREATE))) + /* The following is needed only in case of lock tables */ + if ((local_create_info.table= thd->lex->query_tables->table)) + pos_in_locked_tables= local_create_info.table->pos_in_locked_tables; + + res= ((create_res= + mysql_create_table_no_lock(thd, table->db, table->table_name, + &local_create_info, &local_alter_info, + &is_trans, C_ORDINARY_CREATE)) > 0); + /* Remember to log if we deleted something */ + do_logging= thd->log_current_statement; + if (res) goto err; /* - Ensure that we have an exclusive lock on target table if we are creating - non-temporary table. + Check if we are doing CREATE OR REPLACE TABLE under LOCK TABLES + on a non temporary table */ - DBUG_ASSERT((create_info->tmp_table()) || - thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db, - table->table_name, - MDL_EXCLUSIVE)); + if (thd->locked_tables_mode && pos_in_locked_tables && + (create_info->options & HA_LEX_CREATE_REPLACE)) + { + /* + Add back the deleted table and re-created table as a locked table + This should always work as we have a meta lock on the table. + */ + thd->locked_tables_list.add_back_last_deleted_lock(pos_in_locked_tables); + if (thd->locked_tables_list.reopen_tables(thd)) + thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); + else + { + /* + Get pointer to the newly opened table. We need this to ensure we + don't reopen the table when doing statment logging below. + */ + table->table= pos_in_locked_tables->table; + table->table->mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); + } + } + else + { + /* + Ensure that we have an exclusive lock on target table if we are creating + non-temporary table. + */ + DBUG_ASSERT((create_info->tmp_table()) || + thd->mdl_context.is_lock_owner(MDL_key::TABLE, table->db, + table->table_name, + MDL_EXCLUSIVE)); + } DEBUG_SYNC(thd, "create_table_like_before_binlog"); @@ -5138,7 +5289,8 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, Case Target Source Write to binary log ==== ========= ========= ============================== 1 normal normal Original statement - 2 normal temporary Generated statement + 2 normal temporary Generated statement if the table + was created. 3 temporary normal Nothing 4 temporary temporary Nothing ==== ========= ========= ============================== @@ -5153,36 +5305,59 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN); bool new_table= FALSE; // Whether newly created table is open. + if (create_res != 0) + { + /* + Table or view with same name already existed and we where using + IF EXISTS. Continue without logging anything. + */ + goto err; + } + if (!table->table) + { + TABLE_LIST::enum_open_strategy save_open_strategy; + int open_res; + /* Force the newly created table to be opened */ + save_open_strategy= table->open_strategy; + table->open_strategy= TABLE_LIST::OPEN_NORMAL; + + /* + In order for store_create_info() to work we need to open + destination table if it is not already open (i.e. if it + has not existed before). We don't need acquire metadata + lock in order to do this as we already hold exclusive + lock on this table. The table will be closed by + close_thread_table() at the end of this branch. + */ + open_res= open_table(thd, table, thd->mem_root, &ot_ctx); + /* Restore */ + table->open_strategy= save_open_strategy; + if (open_res) + { + res= 1; + goto err; + } + new_table= TRUE; + } /* - The condition avoids a crash as described in BUG#48506. Other - binlogging problems related to CREATE TABLE IF NOT EXISTS LIKE - when the existing object is a view will be solved by BUG 47442. + We have to re-test if the table was a view as the view may not + have been opened until just above. */ if (!table->view) { - if (!table->table) - { - - /* - In order for store_create_info() to work we need to open - destination table if it is not already open (i.e. if it - has not existed before). We don't need acquire metadata - lock in order to do this as we already hold exclusive - lock on this table. The table will be closed by - close_thread_table() at the end of this branch. - */ - if (open_table(thd, table, thd->mem_root, &ot_ctx)) - goto err; - new_table= TRUE; - } - int result __attribute__((unused))= store_create_info(thd, table, &query, - create_info, FALSE /* show_database */); + create_info, FALSE /* show_database */, + MY_TEST(create_info->options & + HA_LEX_CREATE_REPLACE)); DBUG_ASSERT(result == 0); // store_create_info() always return 0 + do_logging= FALSE; if (write_bin_log(thd, TRUE, query.ptr(), query.length())) + { + res= 1; goto err; + } if (new_table) { @@ -5197,17 +5372,20 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, } } else // Case 1 - if (write_bin_log(thd, TRUE, thd->query(), thd->query_length())) - goto err; + do_logging= TRUE; } /* Case 3 and 4 does nothing under RBR */ } - else if (write_bin_log(thd, TRUE, thd->query(), thd->query_length(), is_trans)) - goto err; + else + do_logging= TRUE; err: + if (do_logging && + write_bin_log(thd, res ? FALSE : TRUE, thd->query(), + thd->query_length(), is_trans)) + res= 1; DBUG_RETURN(res); #ifdef WITH_WSREP @@ -5365,7 +5543,8 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) { alter_info->flags&= ~Alter_info::ALTER_ADD_COLUMN; if (alter_info->key_list.is_empty()) - alter_info->flags&= ~Alter_info::ALTER_ADD_INDEX; + alter_info->flags&= ~(Alter_info::ALTER_ADD_INDEX | + Alter_info::ADD_FOREIGN_KEY); } break; } @@ -5440,13 +5619,32 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) else /* Alter_drop::KEY */ { uint n_key; - for (n_key=0; n_key < table->s->keys; n_key++) + if (drop->type != Alter_drop::FOREIGN_KEY) { - if (my_strcasecmp(system_charset_info, - drop->name, table->key_info[n_key].name) == 0) + for (n_key=0; n_key < table->s->keys; n_key++) { - remove_drop= FALSE; - break; + if (my_strcasecmp(system_charset_info, + drop->name, table->key_info[n_key].name) == 0) + { + remove_drop= FALSE; + break; + } + } + } + else + { + List <FOREIGN_KEY_INFO> fk_child_key_list; + FOREIGN_KEY_INFO *f_key; + table->file->get_foreign_key_list(thd, &fk_child_key_list); + List_iterator<FOREIGN_KEY_INFO> fk_key_it(fk_child_key_list); + while ((f_key= fk_key_it++)) + { + if (my_strcasecmp(system_charset_info, f_key->foreign_id->str, + drop->name) == 0) + { + remove_drop= FALSE; + break; + } } } } @@ -5458,7 +5656,8 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) drop_it.remove(); if (alter_info->drop_list.is_empty()) alter_info->flags&= ~(Alter_info::ALTER_DROP_COLUMN | - Alter_info::ALTER_DROP_INDEX); + Alter_info::ALTER_DROP_INDEX | + Alter_info::DROP_FOREIGN_KEY); } } } @@ -5469,6 +5668,7 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) Key *key; List_iterator<Key> key_it(alter_info->key_list); uint n_key; + bool remove_key; const char *keyname; while ((key=key_it++)) { @@ -5485,24 +5685,48 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) if (keyname == NULL) continue; } - for (n_key=0; n_key < table->s->keys; n_key++) + remove_key= FALSE; + if (key->type != Key::FOREIGN_KEY) { - if (my_strcasecmp(system_charset_info, - keyname, table->key_info[n_key].name) == 0) + for (n_key=0; n_key < table->s->keys; n_key++) { - push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_DUP_KEYNAME, ER(ER_DUP_KEYNAME), keyname); - key_it.remove(); - if (key->type == Key::FOREIGN_KEY) + if (my_strcasecmp(system_charset_info, + keyname, table->key_info[n_key].name) == 0) { - /* ADD FOREIGN KEY appends two items. */ - key_it.remove(); + remove_key= TRUE; + break; } - if (alter_info->key_list.is_empty()) - alter_info->flags&= ~Alter_info::ALTER_ADD_INDEX; - break; } } + else + { + List <FOREIGN_KEY_INFO> fk_child_key_list; + FOREIGN_KEY_INFO *f_key; + table->file->get_foreign_key_list(thd, &fk_child_key_list); + List_iterator<FOREIGN_KEY_INFO> fk_key_it(fk_child_key_list); + while ((f_key= fk_key_it++)) + { + if (my_strcasecmp(system_charset_info, f_key->foreign_id->str, + key->name.str) == 0) + remove_key= TRUE; + break; + } + } + if (remove_key) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_DUP_KEYNAME, ER(ER_DUP_KEYNAME), keyname); + key_it.remove(); + if (key->type == Key::FOREIGN_KEY) + { + /* ADD FOREIGN KEY appends two items. */ + key_it.remove(); + } + if (alter_info->key_list.is_empty()) + alter_info->flags&= ~(Alter_info::ALTER_ADD_INDEX | + Alter_info::ADD_FOREIGN_KEY); + break; + } } } @@ -5732,9 +5956,6 @@ static bool fill_alter_inplace_info(THD *thd, if (new_field) { - ha_alter_info->create_info->fields_option_struct[f_ptr - table->field]= - new_field->option_struct; - /* Field is not dropped. Evaluate changes bitmap for it. */ /* @@ -5846,6 +6067,15 @@ static bool fill_alter_inplace_info(THD *thd, if (new_field->column_format() != field->column_format()) ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_COLUMN_FORMAT; + + if (engine_options_differ(field->option_struct, new_field->option_struct, + table->file->ht->field_options)) + { + ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_OPTION; + ha_alter_info->create_info->fields_option_struct[f_ptr - table->field]= + new_field->option_struct; + } + } else { @@ -7226,7 +7456,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, key= new Key(key_type, key_name, strlen(key_name), &key_create_info, - test(key_info->flags & HA_GENERATED_KEY), + MY_TEST(key_info->flags & HA_GENERATED_KEY), key_parts, key_info->option_list, FALSE); new_key_list.push_back(key); } @@ -7798,9 +8028,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, it is the case. TODO: this design is obsolete and will be removed. */ - int table_kind= check_if_log_table(table_list->db_length, table_list->db, - table_list->table_name_length, - table_list->table_name, false); + int table_kind= check_if_log_table(table_list, FALSE, NullS); if (table_kind) { @@ -8841,7 +9069,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, DBUG_ENTER("copy_data_between_tables"); /* Two or 3 stages; Sorting, copying data and update indexes */ - thd_progress_init(thd, 2 + test(order)); + thd_progress_init(thd, 2 + MY_TEST(order)); if (mysql_trans_prepare_alter_copy_data(thd)) DBUG_RETURN(-1); @@ -9342,7 +9570,7 @@ static bool check_engine(THD *thd, const char *db_name, handlerton **new_engine= &create_info->db_type; handlerton *req_engine= *new_engine; bool no_substitution= - test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION); + MY_TEST(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION); if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine), no_substitution, 1))) DBUG_RETURN(true); |