diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/ddl_log.cc | 201 | ||||
-rw-r--r-- | sql/ddl_log.h | 21 | ||||
-rw-r--r-- | sql/ha_partition.cc | 526 | ||||
-rw-r--r-- | sql/ha_partition.h | 20 | ||||
-rw-r--r-- | sql/handler.cc | 62 | ||||
-rw-r--r-- | sql/handler.h | 21 | ||||
-rw-r--r-- | sql/lex_string.h | 4 | ||||
-rw-r--r-- | sql/partition_element.h | 25 | ||||
-rw-r--r-- | sql/partition_info.h | 4 | ||||
-rw-r--r-- | sql/sql_partition.cc | 1940 | ||||
-rw-r--r-- | sql/sql_partition.h | 11 | ||||
-rw-r--r-- | sql/sql_partition_admin.cc | 61 | ||||
-rw-r--r-- | sql/sql_rename.cc | 2 | ||||
-rw-r--r-- | sql/sql_table.cc | 84 | ||||
-rw-r--r-- | sql/sql_table.h | 34 | ||||
-rw-r--r-- | sql/sql_trigger.cc | 4 |
16 files changed, 900 insertions, 2120 deletions
diff --git a/sql/ddl_log.cc b/sql/ddl_log.cc index 199b1df9623..e2c3271261b 100644 --- a/sql/ddl_log.cc +++ b/sql/ddl_log.cc @@ -85,8 +85,8 @@ uchar ddl_log_file_magic[]= const char *ddl_log_action_name[DDL_LOG_LAST_ACTION]= { - "Unknown", "partitioning delete", "partitioning rename", - "partitioning replace", "partitioning exchange", + "Unknown", "file delete", "file rename", + "file replace", "partitioning exchange", "rename table", "rename view", "initialize drop table", "drop table", "drop view", "drop trigger", "drop db", "create table", "create view", @@ -1323,7 +1323,8 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, uint entry_pos= ddl_log_entry->entry_pos; int error= 0; uint fn_flags= 0; - bool frm_action= FALSE; + PSI_file_key key= key_file_misc; + const bool alter_partition= ddl_log_entry->flags & DDL_LOG_FLAG_ALTER_PARTITION; DBUG_ENTER("ddl_log_execute_action"); mysql_mutex_assert_owner(&LOCK_gdl); @@ -1356,12 +1357,17 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, if (!report_error) thd->push_internal_handler(&no_such_table_handler); - if (!strcmp(ddl_log_entry->handler_name.str, reg_ext)) - frm_action= TRUE; - else if (ddl_log_entry->handler_name.length) + if (ddl_log_entry->action_type <= DDL_LOG_FILE_REPLACE_ACTION && + ddl_log_entry->unique_id) + key= (PSI_file_key) ddl_log_entry->unique_id; + + if (ddl_log_entry->handler_name.length) { if (!(file= create_handler(thd, mem_root, &handler_name))) + { + error= 1; goto end; + } hton= file->ht; } @@ -1371,65 +1377,34 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, fn_flags|= FN_TO_IS_TMP; switch (ddl_log_entry->action_type) { - case DDL_LOG_REPLACE_ACTION: - case DDL_LOG_DELETE_ACTION: + case DDL_LOG_FILE_REPLACE_ACTION: + case DDL_LOG_FILE_DELETE_ACTION: { + /* DDL_LOG_FILE_REPLACE_ACTION is 2 phases: delete and rename */ if (ddl_log_entry->phase == 0) { - if (frm_action) - { - strxmov(to_path, ddl_log_entry->name.str, reg_ext, NullS); - if (unlikely((error= mysql_file_delete(key_file_frm, to_path, - MYF(MY_WME | - MY_IGNORE_ENOENT))))) - break; -#ifdef WITH_PARTITION_STORAGE_ENGINE - strxmov(to_path, ddl_log_entry->name.str, PAR_EXT, NullS); - (void) mysql_file_delete(key_file_partition_ddl_log, to_path, - MYF(0)); -#endif - } - else - { - if (unlikely((error= hton->drop_table(hton, ddl_log_entry->name.str)))) - { - if (!non_existing_table_error(error)) - break; - } - } + if (unlikely((error= mysql_file_delete(key, + ddl_log_entry->name.str, + MYF(MY_WME | MY_IGNORE_ENOENT))))) + break; if (increment_phase(entry_pos)) break; error= 0; - if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION) + if (ddl_log_entry->action_type == DDL_LOG_FILE_DELETE_ACTION) break; } } - DBUG_ASSERT(ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION); + DBUG_ASSERT(ddl_log_entry->action_type == DDL_LOG_FILE_REPLACE_ACTION); /* Fall through and perform the rename action of the replace action. We have already indicated the success of the delete action in the log entry by stepping up the phase. */ /* fall through */ - case DDL_LOG_RENAME_ACTION: + case DDL_LOG_FILE_RENAME_ACTION: { - if (frm_action) - { - strxmov(to_path, ddl_log_entry->name.str, reg_ext, NullS); - strxmov(from_path, ddl_log_entry->from_name.str, reg_ext, NullS); - error= mysql_file_rename(key_file_frm, from_path, to_path, MYF(MY_WME)); -#ifdef WITH_PARTITION_STORAGE_ENGINE - strxmov(to_path, ddl_log_entry->name.str, PAR_EXT, NullS); - strxmov(from_path, ddl_log_entry->from_name.str, PAR_EXT, NullS); - int err2= mysql_file_rename(key_file_partition_ddl_log, from_path, - to_path, MYF(MY_WME)); - if (!error) - error= err2; -#endif - } - else - error= file->ha_rename_table(ddl_log_entry->from_name.str, - ddl_log_entry->name.str); + error= mysql_file_rename(key, ddl_log_entry->from_name.str, + ddl_log_entry->name.str, MYF(MY_WME)); if (increment_phase(entry_pos)) { error= -1; @@ -1442,7 +1417,6 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, /* We hold LOCK_gdl, so we can alter global_ddl_log.file_entry_buf */ uchar *file_entry_buf= global_ddl_log.file_entry_buf; /* not yet implemented for frm */ - DBUG_ASSERT(!frm_action); /* Using a case-switch here to revert all currently done phases, since it will fall through until the first phase is undone. @@ -1488,11 +1462,13 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, */ switch (ddl_log_entry->phase) { case DDL_RENAME_PHASE_TRIGGER: + DBUG_ASSERT(!alter_partition); rename_triggers(thd, ddl_log_entry, 0, fn_flags); if (increment_phase(entry_pos)) break; /* fall through */ case DDL_RENAME_PHASE_STAT: + DBUG_ASSERT(!alter_partition); if (fn_flags & FN_TO_IS_TMP) { /* @@ -1516,13 +1492,21 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, } /* fall through */ case DDL_RENAME_PHASE_TABLE: - /* Restore frm and table to original names */ - error= execute_rename_table(ddl_log_entry, file, - &ddl_log_entry->db, &ddl_log_entry->name, - &ddl_log_entry->from_db, - &ddl_log_entry->from_name, - fn_flags, from_path, to_path); + if (alter_partition) + { + error= file->ha_rename_table(ddl_log_entry->from_name.str, + ddl_log_entry->name.str); + } + else + { + /* Restore frm and table to original names */ + error= execute_rename_table(ddl_log_entry, file, + &ddl_log_entry->db, &ddl_log_entry->name, + &ddl_log_entry->from_db, + &ddl_log_entry->from_name, + fn_flags, from_path, to_path); + } if (ddl_log_entry->flags & DDL_LOG_FLAG_UPDATE_STAT) { /* Update stat tables last */ @@ -1615,7 +1599,10 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, } } else + { + DBUG_ASSERT(!alter_partition); error= ha_delete_table_force(thd, path.str, &db, &table); + } if (error <= 0) { /* Not found or already deleted. Delete .frm if it exists */ @@ -1627,14 +1614,14 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, break; /* Fall through */ case DDL_DROP_PHASE_TRIGGER: - Table_triggers_list::drop_all_triggers(thd, &db, &table, - fn_flags, - MYF(MY_WME | MY_IGNORE_ENOENT)); + if (!alter_partition) + Table_triggers_list::drop_all_triggers(thd, &db, &table, fn_flags, + MYF(MY_WME | MY_IGNORE_ENOENT)); if (increment_phase(entry_pos)) break; /* Fall through */ case DDL_DROP_PHASE_BINLOG: - if (fn_flags & FN_IS_TMP) + if (alter_partition || (fn_flags & FN_IS_TMP)) { /* If new code is added here please finish this block like this: @@ -2031,7 +2018,10 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, if (!(org_file= create_handler(thd, mem_root, &ddl_log_entry->from_handler_name))) + { + error= 1; goto end; + } /* Handlerton of the final table and any temporary tables */ org_hton= org_file->ht; /* @@ -2041,7 +2031,7 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, */ partition_hton= hton; - if (ddl_log_entry->flags & DDL_LOG_FLAG_ALTER_PARTITION) + if (alter_partition) { /* The from and to tables where both using the partition engine. @@ -2343,7 +2333,10 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root, Query length is stored in unique_id */ if (recovery_state.query.alloc((size_t) (ddl_log_entry->unique_id+1))) + { + error= 1; goto end; + } recovery_state.query.length(0); recovery_state.db.copy(ddl_log_entry->db.str, ddl_log_entry->db.length, system_charset_info); @@ -2375,7 +2368,7 @@ end: /* If error code is HA_ERR_ code we already have file object. */ DBUG_ASSERT(file); TABLE_SHARE share; - bzero(&share, sizeof(share)); + share.reset(); share.db= ddl_log_entry->db; share.table_name= ddl_log_entry->name; share.normalized_path= ddl_log_entry->tmp_name; @@ -3041,7 +3034,7 @@ void ddl_log_complete(DDL_LOG_STATE *state) This is called for failed rename table, create trigger or drop trigger. */ -bool ddl_log_revert(THD *thd, DDL_LOG_STATE *state) +bool ddl_log_revert(THD *thd, DDL_LOG_STATE *state, bool report_error) { bool res= 0; DBUG_ENTER("ddl_log_revert"); @@ -3054,7 +3047,7 @@ bool ddl_log_revert(THD *thd, DDL_LOG_STATE *state) { res= ddl_log_execute_entry_no_lock(thd, state->list->entry_pos, state->execute_entry->entry_pos, - true); + report_error); ddl_log_disable_execute_entry(&state->execute_entry); } ddl_log_release_entries(state); @@ -3159,8 +3152,8 @@ bool ddl_log_update_xid(DDL_LOG_STATE *state, ulonglong xid) Will update DDL_LOG_STATE->flags */ -static bool ddl_log_write(DDL_LOG_STATE *ddl_state, - DDL_LOG_ENTRY *ddl_log_entry) +bool ddl_log_write(DDL_LOG_STATE *ddl_state, + DDL_LOG_ENTRY *ddl_log_entry) { int error; DDL_LOG_MEMORY_ENTRY *log_entry; @@ -3715,27 +3708,36 @@ err: } -/* - Log an delete frm file -*/ +/** + Log an delete frm file and par file -/* - TODO: Partitioning atomic DDL refactoring: this should be replaced with - ddl_log_create_table(). + @param to_path Location of frm file without extension */ + bool ddl_log_delete_frm(DDL_LOG_STATE *ddl_state, const char *to_path) { + char to_file[FN_REFLEN + 1]; DDL_LOG_ENTRY ddl_log_entry; DDL_LOG_MEMORY_ENTRY *log_entry; DBUG_ENTER("ddl_log_delete_frm"); bzero(&ddl_log_entry, sizeof(ddl_log_entry)); - ddl_log_entry.action_type= DDL_LOG_DELETE_ACTION; + ddl_log_entry.action_type= DDL_LOG_FILE_DELETE_ACTION; ddl_log_entry.next_entry= ddl_state->list ? ddl_state->list->entry_pos : 0; - lex_string_set(&ddl_log_entry.handler_name, reg_ext); - lex_string_set(&ddl_log_entry.name, to_path); - mysql_mutex_assert_owner(&LOCK_gdl); +#ifdef WITH_PARTITION_STORAGE_ENGINE + strxmov(to_file, to_path, PAR_EXT, NullS); + lex_string_set(&ddl_log_entry.name, to_file); + ddl_log_entry.unique_id= (ulonglong) key_file_partition_ddl_log; + if (ddl_log_write_entry(&ddl_log_entry, &log_entry)) + DBUG_RETURN(1); + + ddl_log_add_entry(ddl_state, log_entry); + ddl_log_entry.next_entry= ddl_state->list->entry_pos; +#endif + strxmov(to_file, to_path, reg_ext, NullS); + lex_string_set(&ddl_log_entry.name, to_file); + ddl_log_entry.unique_id= (ulonglong) key_file_frm; if (ddl_log_write_entry(&ddl_log_entry, &log_entry)) DBUG_RETURN(1); @@ -3743,6 +3745,53 @@ bool ddl_log_delete_frm(DDL_LOG_STATE *ddl_state, const char *to_path) DBUG_RETURN(0); } + +/** + Log an rename frm file and par file + + @param from_path Location of frm file without extension + @param to_path Location of new frm file without extension +*/ + +bool ddl_log_rename_frm(DDL_LOG_STATE *ddl_state, + const char *from_path, const char *to_path) +{ + char to_file[FN_REFLEN + 1], from_file[FN_REFLEN + 1]; + DDL_LOG_ENTRY ddl_log_entry; + DDL_LOG_MEMORY_ENTRY *log_entry; + DBUG_ENTER("ddl_log_rename_frm"); + + bzero(&ddl_log_entry, sizeof(ddl_log_entry)); + ddl_log_entry.action_type= DDL_LOG_FILE_RENAME_ACTION; + ddl_log_entry.next_entry= ddl_state->list ? ddl_state->list->entry_pos : 0; + + mysql_mutex_assert_owner(&LOCK_gdl); +#ifdef WITH_PARTITION_STORAGE_ENGINE + strxmov(to_file, to_path, PAR_EXT, NullS); + strxmov(from_file, from_path, PAR_EXT, NullS); + lex_string_set(&ddl_log_entry.name, to_file); + lex_string_set(&ddl_log_entry.from_name, from_file); + ddl_log_entry.unique_id= (ulonglong) key_file_partition_ddl_log; + if (ddl_log_write_entry(&ddl_log_entry, &log_entry)) + DBUG_RETURN(1); + + ddl_log_add_entry(ddl_state, log_entry); + ddl_log_entry.next_entry= ddl_state->list->entry_pos; +#endif + strxmov(to_file, to_path, reg_ext, NullS); + strxmov(from_file, from_path, reg_ext, NullS); + lex_string_set(&ddl_log_entry.name, to_file); + lex_string_set(&ddl_log_entry.from_name, from_file); + ddl_log_entry.unique_id= (ulonglong) key_file_frm; + + if (ddl_log_write_entry(&ddl_log_entry, &log_entry)) + DBUG_RETURN(true); + + ddl_log_add_entry(ddl_state, log_entry); + DBUG_RETURN(false); +} + + /* Link the ddl_log_state to another (master) chain. If the master chain is active during DDL recovery, this event will not be executed. diff --git a/sql/ddl_log.h b/sql/ddl_log.h index 301f6a1cfa8..ab5eadc5eee 100644 --- a/sql/ddl_log.h +++ b/sql/ddl_log.h @@ -59,16 +59,16 @@ enum ddl_log_action_code DDL_LOG_UNKNOWN_ACTION= 0, /* Delete a .frm file or a table in the partition engine */ - DDL_LOG_DELETE_ACTION= 1, + DDL_LOG_FILE_DELETE_ACTION= 1, /* Rename a .frm fire a table in the partition engine */ - DDL_LOG_RENAME_ACTION= 2, + DDL_LOG_FILE_RENAME_ACTION= 2, /* Rename an entity after removing the previous entry with the new name, that is replace this entry. */ - DDL_LOG_REPLACE_ACTION= 3, + DDL_LOG_FILE_REPLACE_ACTION= 3, /* Exchange two entities by renaming them a -> tmp, b -> a, tmp -> b */ DDL_LOG_EXCHANGE_ACTION= 4, @@ -263,6 +263,12 @@ bool ddl_log_close_binlogged_events(HASH *xids); int ddl_log_execute_recovery(); /* functions for updating the ddl log */ +bool ddl_log_write(DDL_LOG_STATE *ddl_state, + DDL_LOG_ENTRY *ddl_log_entry); +/* + TODO: MDEV-28844 don't use in exchange_name_with_ddl_log(), remove global + declaration +*/ bool ddl_log_write_entry(DDL_LOG_ENTRY *ddl_log_entry, DDL_LOG_MEMORY_ENTRY **active_entry); @@ -271,7 +277,8 @@ bool ddl_log_write_execute_entry(uint first_entry, uint cond_entry, bool ddl_log_disable_execute_entry(DDL_LOG_MEMORY_ENTRY **active_entry); void ddl_log_complete(DDL_LOG_STATE *ddl_log_state); -bool ddl_log_revert(THD *thd, DDL_LOG_STATE *ddl_log_state); +bool ddl_log_revert(THD *thd, DDL_LOG_STATE *ddl_log_state, + bool report_error); bool ddl_log_update_phase(DDL_LOG_STATE *entry, uchar phase); bool ddl_log_add_flag(DDL_LOG_STATE *entry, uint16 flag); @@ -282,6 +289,10 @@ bool ddl_log_disable_entry(DDL_LOG_STATE *state); bool ddl_log_increment_phase(uint entry_pos); void ddl_log_release_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry); bool ddl_log_sync(); +/* + TODO: MDEV-28844 don't use in exchange_name_with_ddl_log(), remove global + declaration +*/ bool ddl_log_execute_entry(THD *thd, uint first_entry); void ddl_log_add_entry(DDL_LOG_STATE *state, DDL_LOG_MEMORY_ENTRY *log_entry); @@ -356,6 +367,8 @@ bool ddl_log_alter_table(DDL_LOG_STATE *ddl_state, bool ddl_log_store_query(THD *thd, DDL_LOG_STATE *ddl_log_state, const char *query, size_t length); bool ddl_log_delete_frm(DDL_LOG_STATE *ddl_state, const char *to_path); +bool ddl_log_rename_frm(DDL_LOG_STATE *ddl_state, + const char *from_path, const char *to_path); void ddl_log_link_chains(DDL_LOG_STATE *state, DDL_LOG_STATE *master_state); extern mysql_mutex_t LOCK_gdl; #endif /* DDL_LOG_INCLUDED */ diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 0b59a5a335f..fe94d44f482 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -877,332 +877,6 @@ create_error: } -/* - Drop partitions as part of ALTER TABLE of partitions - - SYNOPSIS - drop_partitions() - path Complete path of db and table name - - RETURN VALUE - >0 Failure - 0 Success - - DESCRIPTION - Use part_info object on handler object to deduce which partitions to - drop (each partition has a state attached to it) -*/ - -int ha_partition::drop_partitions(const char *path) -{ - List_iterator<partition_element> part_it(m_part_info->partitions); - char part_name_buff[FN_REFLEN + 1]; - uint num_parts= m_part_info->partitions.elements; - uint num_subparts= m_part_info->num_subparts; - uint i= 0; - uint name_variant; - int ret_error; - int error= 0; - DBUG_ENTER("ha_partition::drop_partitions"); - - /* - Assert that it works without HA_FILE_BASED and lower_case_table_name = 2. - We use m_file[0] as long as all partitions have the same storage engine. - */ - DBUG_ASSERT(!strcmp(path, get_canonical_filename(m_file[0], path, - part_name_buff))); - do - { - partition_element *part_elem= part_it++; - if (part_elem->part_state == PART_TO_BE_DROPPED) - { - handler *file; - /* - This part is to be dropped, meaning the part or all its subparts. - */ - name_variant= NORMAL_PART_NAME; - if (m_is_sub_partitioned) - { - List_iterator<partition_element> sub_it(part_elem->subpartitions); - uint j= 0, part; - do - { - partition_element *sub_elem= sub_it++; - part= i * num_subparts + j; - if (unlikely((ret_error= - create_subpartition_name(part_name_buff, - sizeof(part_name_buff), path, - part_elem->partition_name, - sub_elem->partition_name, - name_variant)))) - error= ret_error; - file= m_file[part]; - DBUG_PRINT("info", ("Drop subpartition %s", part_name_buff)); - if (unlikely((ret_error= file->delete_table(part_name_buff)))) - error= ret_error; - if (unlikely(ddl_log_increment_phase(sub_elem->log_entry-> - entry_pos))) - error= 1; - } while (++j < num_subparts); - } - else - { - if ((ret_error= create_partition_name(part_name_buff, - sizeof(part_name_buff), path, - part_elem->partition_name, name_variant, TRUE))) - error= ret_error; - else - { - file= m_file[i]; - DBUG_PRINT("info", ("Drop partition %s", part_name_buff)); - if (unlikely((ret_error= file->delete_table(part_name_buff)))) - error= ret_error; - if (unlikely(ddl_log_increment_phase(part_elem->log_entry-> - entry_pos))) - error= 1; - } - } - if (part_elem->part_state == PART_IS_CHANGED) - part_elem->part_state= PART_NORMAL; - else - part_elem->part_state= PART_IS_DROPPED; - } - } while (++i < num_parts); - (void) ddl_log_sync(); - DBUG_RETURN(error); -} - - -/* - Rename partitions as part of ALTER TABLE of partitions - - SYNOPSIS - rename_partitions() - path Complete path of db and table name - - RETURN VALUE - TRUE Failure - FALSE Success - - DESCRIPTION - When reorganising partitions, adding hash partitions and coalescing - partitions it can be necessary to rename partitions while holding - an exclusive lock on the table. - Which partitions to rename is given by state of partitions found by the - partition info struct referenced from the handler object -*/ - -int ha_partition::rename_partitions(const char *path) -{ - List_iterator<partition_element> part_it(m_part_info->partitions); - List_iterator<partition_element> temp_it(m_part_info->temp_partitions); - char part_name_buff[FN_REFLEN + 1]; - char norm_name_buff[FN_REFLEN + 1]; - uint num_parts= m_part_info->partitions.elements; - uint part_count= 0; - uint num_subparts= m_part_info->num_subparts; - uint i= 0; - uint j= 0; - int error= 0; - int ret_error; - uint temp_partitions= m_part_info->temp_partitions.elements; - handler *file; - partition_element *part_elem, *sub_elem; - DBUG_ENTER("ha_partition::rename_partitions"); - - /* - Assert that it works without HA_FILE_BASED and lower_case_table_name = 2. - We use m_file[0] as long as all partitions have the same storage engine. - */ - DBUG_ASSERT(!strcmp(path, get_canonical_filename(m_file[0], path, - norm_name_buff))); - - DEBUG_SYNC(ha_thd(), "before_rename_partitions"); - if (temp_partitions) - { - /* - These are the reorganised partitions that have already been copied. - We delete the partitions and log the delete by inactivating the - delete log entry in the table log. We only need to synchronise - these writes before moving to the next loop since there is no - interaction among reorganised partitions, they cannot have the - same name. - */ - do - { - part_elem= temp_it++; - if (m_is_sub_partitioned) - { - List_iterator<partition_element> sub_it(part_elem->subpartitions); - j= 0; - do - { - sub_elem= sub_it++; - file= m_reorged_file[part_count++]; - if (unlikely((ret_error= - create_subpartition_name(norm_name_buff, - sizeof(norm_name_buff), path, - part_elem->partition_name, - sub_elem->partition_name, - NORMAL_PART_NAME)))) - error= ret_error; - DBUG_PRINT("info", ("Delete subpartition %s", norm_name_buff)); - if (unlikely((ret_error= file->delete_table(norm_name_buff)))) - error= ret_error; - else if (unlikely(ddl_log_increment_phase(sub_elem->log_entry-> - entry_pos))) - error= 1; - else - sub_elem->log_entry= NULL; /* Indicate success */ - } while (++j < num_subparts); - } - else - { - file= m_reorged_file[part_count++]; - if (unlikely((ret_error= - create_partition_name(norm_name_buff, - sizeof(norm_name_buff), path, - part_elem->partition_name, - NORMAL_PART_NAME, TRUE)))) - error= ret_error; - else - { - DBUG_PRINT("info", ("Delete partition %s", norm_name_buff)); - if (unlikely((ret_error= file->delete_table(norm_name_buff)))) - error= ret_error; - else if (unlikely(ddl_log_increment_phase(part_elem->log_entry-> - entry_pos))) - error= 1; - else - part_elem->log_entry= NULL; /* Indicate success */ - } - } - } while (++i < temp_partitions); - (void) ddl_log_sync(); - } - i= 0; - do - { - /* - When state is PART_IS_CHANGED it means that we have created a new - TEMP partition that is to be renamed to normal partition name and - we are to delete the old partition with currently the normal name. - - We perform this operation by - 1) Delete old partition with normal partition name - 2) Signal this in table log entry - 3) Synch table log to ensure we have consistency in crashes - 4) Rename temporary partition name to normal partition name - 5) Signal this to table log entry - It is not necessary to synch the last state since a new rename - should not corrupt things if there was no temporary partition. - - The only other parts we need to cater for are new parts that - replace reorganised parts. The reorganised parts were deleted - by the code above that goes through the temp_partitions list. - Thus the synch above makes it safe to simply perform step 4 and 5 - for those entries. - */ - part_elem= part_it++; - if (part_elem->part_state == PART_IS_CHANGED || - part_elem->part_state == PART_TO_BE_DROPPED || - (part_elem->part_state == PART_IS_ADDED && temp_partitions)) - { - if (m_is_sub_partitioned) - { - List_iterator<partition_element> sub_it(part_elem->subpartitions); - uint part; - - j= 0; - do - { - sub_elem= sub_it++; - part= i * num_subparts + j; - if (unlikely((ret_error= - create_subpartition_name(norm_name_buff, - sizeof(norm_name_buff), path, - part_elem->partition_name, - sub_elem->partition_name, - NORMAL_PART_NAME)))) - error= ret_error; - if (part_elem->part_state == PART_IS_CHANGED) - { - file= m_reorged_file[part_count++]; - DBUG_PRINT("info", ("Delete subpartition %s", norm_name_buff)); - if (unlikely((ret_error= file->delete_table(norm_name_buff)))) - error= ret_error; - else if (unlikely(ddl_log_increment_phase(sub_elem->log_entry-> - entry_pos))) - error= 1; - (void) ddl_log_sync(); - } - file= m_new_file[part]; - if (unlikely((ret_error= - create_subpartition_name(part_name_buff, - sizeof(part_name_buff), path, - part_elem->partition_name, - sub_elem->partition_name, - TEMP_PART_NAME)))) - error= ret_error; - DBUG_PRINT("info", ("Rename subpartition from %s to %s", - part_name_buff, norm_name_buff)); - if (unlikely((ret_error= file->ha_rename_table(part_name_buff, - norm_name_buff)))) - error= ret_error; - else if (unlikely(ddl_log_increment_phase(sub_elem->log_entry-> - entry_pos))) - error= 1; - else - sub_elem->log_entry= NULL; - } while (++j < num_subparts); - } - else - { - if (unlikely((ret_error= - create_partition_name(norm_name_buff, - sizeof(norm_name_buff), path, - part_elem->partition_name, - NORMAL_PART_NAME, TRUE)) || - (ret_error= create_partition_name(part_name_buff, - sizeof(part_name_buff), - path, - part_elem-> - partition_name, - TEMP_PART_NAME, TRUE)))) - error= ret_error; - else - { - if (part_elem->part_state == PART_IS_CHANGED) - { - file= m_reorged_file[part_count++]; - DBUG_PRINT("info", ("Delete partition %s", norm_name_buff)); - if (unlikely((ret_error= file->delete_table(norm_name_buff)))) - error= ret_error; - else if (unlikely(ddl_log_increment_phase(part_elem->log_entry-> - entry_pos))) - error= 1; - (void) ddl_log_sync(); - } - file= m_new_file[i]; - DBUG_PRINT("info", ("Rename partition from %s to %s", - part_name_buff, norm_name_buff)); - if (unlikely((ret_error= file->ha_rename_table(part_name_buff, - norm_name_buff)))) - error= ret_error; - else if (unlikely(ddl_log_increment_phase(part_elem->log_entry-> - entry_pos))) - error= 1; - else - part_elem->log_entry= NULL; - } - } - } - } while (++i < num_parts); - (void) ddl_log_sync(); - DBUG_RETURN(error); -} - - #define OPTIMIZE_PARTS 1 #define ANALYZE_PARTS 2 #define CHECK_PARTS 3 @@ -1990,10 +1664,8 @@ int ha_partition::allocate_partitions() do { handler **new_file= &new_file_array[part_count++]; - if (!(*new_file= - get_new_handler(table->s, - thd->mem_root, - part_elem->engine_type))) + if (!(*new_file= ::get_new_handler(table->s, thd->mem_root, + part_elem->engine_type))) { DBUG_RETURN(HA_ERR_OUT_OF_MEM); } @@ -2013,188 +1685,8 @@ int ha_partition::allocate_partitions() } while (++i < num_parts); m_new_file= new_file_array; - DBUG_RETURN(0); -} - - -/* - Implement the partition changes defined by ALTER TABLE of partitions - - SYNOPSIS - change_partitions() - create_info HA_CREATE_INFO object describing all - fields and indexes in table - path Complete path of db and table name - out: copied Output parameter where number of copied - records are added - out: deleted Output parameter where number of deleted - records are added - pack_frm_data Reference to packed frm file - pack_frm_len Length of packed frm file - - RETURN VALUE - >0 Failure - 0 Success - - DESCRIPTION - Add and copy if needed a number of partitions, during this operation - no other operation is ongoing in the server. This is used by - ADD PARTITION all types as well as by REORGANIZE PARTITION. For - one-phased implementations it is used also by DROP and COALESCE - PARTITIONs. - One-phased implementation needs the new frm file, other handlers will - get zero length and a NULL reference here. -*/ - -int ha_partition::change_partitions(HA_CREATE_INFO *create_info, - const char *path, - ulonglong * const copied, - ulonglong * const deleted, - const uchar *pack_frm_data - __attribute__((unused)), - size_t pack_frm_len - __attribute__((unused))) -{ - List_iterator<partition_element> part_it(m_part_info->partitions); - List_iterator <partition_element> t_it(m_part_info->temp_partitions); - char part_name_buff[FN_REFLEN + 1]; - uint num_parts= m_part_info->partitions.elements; - uint num_subparts= m_part_info->num_subparts; - uint i= 0; - int error; - uint temp_partitions= m_part_info->temp_partitions.elements; - DBUG_ENTER("ha_partition::change_partitions"); - - /* - Assert that it works without HA_FILE_BASED and lower_case_table_name = 2. - We use m_file[0] as long as all partitions have the same storage engine. - */ - DBUG_ASSERT(!strcmp(path, get_canonical_filename(m_file[0], path, - part_name_buff))); - - if ((error= allocate_partitions())) - DBUG_RETURN(error); - DBUG_ASSERT(m_new_file); - - /* - Step 5: - Create the new partitions and also open, lock and call external_lock - on them to prepare them for copy phase and also for later close - calls - */ - - /* - Before creating new partitions check whether indexes are disabled - in the partitions. - */ - - uint disable_non_uniq_indexes= indexes_are_disabled(); - - part_it.rewind(); - do - { - partition_element *part_elem= part_it++; - DBUG_ASSERT(i == part_elem->id); - if (part_elem->part_state == PART_TO_BE_ADDED || - part_elem->part_state == PART_CHANGED) - { - /* - A new partition needs to be created PART_TO_BE_ADDED means an - entirely new partition and PART_CHANGED means a changed partition - that will still exist with either more or less data in it. - */ - uint name_variant= NORMAL_PART_NAME; - if (part_elem->part_state == PART_CHANGED || - (part_elem->part_state == PART_TO_BE_ADDED && temp_partitions)) - name_variant= TEMP_PART_NAME; - if (m_part_info->is_sub_partitioned()) - { - List_iterator<partition_element> sub_it(part_elem->subpartitions); - uint j= 0; - do - { - partition_element *sub_elem= sub_it++; - DBUG_ASSERT(part_elem == sub_elem->parent_part); - DBUG_ASSERT(j == sub_elem->id); - if (unlikely((error= - create_subpartition_name(part_name_buff, - sizeof(part_name_buff), path, - part_elem->partition_name, - sub_elem->partition_name, - name_variant)))) - { - cleanup_new_partition(); - DBUG_RETURN(error); - } - DBUG_PRINT("info", ("Add subpartition %s", part_name_buff)); - if (unlikely((error= - create_partition(table, create_info, - (const char *)part_name_buff, - sub_elem, - disable_non_uniq_indexes)))) - { - cleanup_new_partition(); - DBUG_RETURN(error); - } - } while (++j < num_subparts); - } - else - { - if (unlikely((error= - create_partition_name(part_name_buff, - sizeof(part_name_buff), path, - part_elem->partition_name, - name_variant, TRUE)))) - { - cleanup_new_partition(); - DBUG_RETURN(error); - } - - DBUG_PRINT("info", ("Add partition %s", part_name_buff)); - if (unlikely((error= - create_partition(table, create_info, - (const char *)part_name_buff, - part_elem, - disable_non_uniq_indexes)))) - { - cleanup_new_partition(); - DBUG_RETURN(error); - } - } - } - } while (++i < num_parts); - - /* - Step 6: - State update to prepare for next write of the frm file. - */ - i= 0; - part_it.rewind(); - do - { - partition_element *part_elem= part_it++; - if (part_elem->part_state == PART_TO_BE_ADDED) - part_elem->part_state= PART_IS_ADDED; - else if (part_elem->part_state == PART_CHANGED) - part_elem->part_state= PART_IS_CHANGED; - else if (part_elem->part_state == PART_REORGED_DROPPED) - part_elem->part_state= PART_TO_BE_DROPPED; - } while (++i < num_parts); - for (i= 0; i < temp_partitions; i++) - { - partition_element *part_elem= t_it++; - DBUG_ASSERT(part_elem->part_state == PART_TO_BE_REORGED); - part_elem->part_state= PART_TO_BE_DROPPED; - } - if (unlikely((error= copy_partitions(copied, deleted)))) - { - /* - Close and unlock the new temporary partitions. - They will later be deleted through the ddl-log. - */ - cleanup_new_partition(); - } - DBUG_RETURN(error); + DBUG_RETURN(ERROR_INJECT("alter_partition_alloc_parts") ? + HA_ERR_OUT_OF_MEM : 0); } @@ -3057,7 +2549,7 @@ bool ha_partition::create_handlers(MEM_ROOT *mem_root) for (i= 0; i < m_tot_parts; i++) { handlerton *hton= plugin_data(m_engine_array[i], handlerton*); - if (!(m_file[i]= get_new_handler(table_share, mem_root, hton))) + if (!(m_file[i]= ::get_new_handler(table_share, mem_root, hton))) DBUG_RETURN(TRUE); DBUG_PRINT("info", ("engine_type: %u", hton->db_type)); } @@ -3118,8 +2610,8 @@ bool ha_partition::new_handlers_from_part_info(MEM_ROOT *mem_root) { for (j= 0; j < m_part_info->num_subparts; j++) { - if (!(m_file[part_count++]= get_new_handler(table_share, mem_root, - part_elem->engine_type))) + if (!(m_file[part_count++]= ::get_new_handler(table_share, mem_root, + part_elem->engine_type))) goto error; DBUG_PRINT("info", ("engine_type: %u", (uint) ha_legacy_type(part_elem->engine_type))); @@ -3127,8 +2619,8 @@ bool ha_partition::new_handlers_from_part_info(MEM_ROOT *mem_root) } else { - if (!(m_file[part_count++]= get_new_handler(table_share, mem_root, - part_elem->engine_type))) + if (!(m_file[part_count++]= ::get_new_handler(table_share, mem_root, + part_elem->engine_type))) goto error; DBUG_PRINT("info", ("engine_type: %u", (uint) ha_legacy_type(part_elem->engine_type))); diff --git a/sql/ha_partition.h b/sql/ha_partition.h index 778080132b8..a4e33058c10 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -467,6 +467,20 @@ public: { return m_file; } + handler **get_new_handlers() + { + return m_new_file; + } + handler *get_child_handler(partition_element *part_elem, + partition_element *sub_elem) + { + return m_file[part_elem->serial_id(sub_elem, m_part_info->num_subparts)]; + } + handler *get_new_handler(partition_element *part_elem, + partition_element *sub_elem) + { + return m_new_file[part_elem->serial_id(sub_elem, m_part_info->num_subparts)]; + } ha_partition *get_clone_source() { return m_is_clone_of; @@ -554,13 +568,7 @@ public: override; bool check_if_updates_are_ignored(const char *op) const override; void update_create_info(HA_CREATE_INFO *create_info) override; - int change_partitions(HA_CREATE_INFO *create_info, const char *path, - ulonglong * const copied, ulonglong * const deleted, - const uchar *pack_frm_data, size_t pack_frm_len) - override; int allocate_partitions(); - int drop_partitions(const char *path) override; - int rename_partitions(const char *path) override; bool get_no_parts(const char *, uint *num_parts) override { DBUG_ENTER("ha_partition::get_no_parts"); diff --git a/sql/handler.cc b/sql/handler.cc index 42c5584f6e1..aa35d0c7b55 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -5475,68 +5475,6 @@ handler::ha_create_partitioning_metadata(const char *name, /** - Change partitions: public interface. - - @sa handler::change_partitions() -*/ - -int -handler::ha_change_partitions(HA_CREATE_INFO *create_info, - const char *path, - ulonglong * const copied, - ulonglong * const deleted, - const uchar *pack_frm_data, - size_t pack_frm_len) -{ - /* - Must have at least RDLCK or be a TMP table. Read lock is needed to read - from current partitions and write lock will be taken on new partitions. - */ - DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || - m_lock_type != F_UNLCK); - - mark_trx_read_write(); - - return change_partitions(create_info, path, copied, deleted, - pack_frm_data, pack_frm_len); -} - - -/** - Drop partitions: public interface. - - @sa handler::drop_partitions() -*/ - -int -handler::ha_drop_partitions(const char *path) -{ - DBUG_ASSERT(!table->db_stat); - - mark_trx_read_write(); - - return drop_partitions(path); -} - - -/** - Rename partitions: public interface. - - @sa handler::rename_partitions() -*/ - -int -handler::ha_rename_partitions(const char *path) -{ - DBUG_ASSERT(!table->db_stat); - - mark_trx_read_write(); - - return rename_partitions(path); -} - - -/** Tell the storage engine that it is allowed to "disable transaction" in the handler. It is a hint that ACID is not required - it was used in NDB for ALTER TABLE, for example, when data are copied to temporary table. diff --git a/sql/handler.h b/sql/handler.h index 3f0b97034e1..5258dc10e80 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -448,7 +448,6 @@ enum chf_create_flags { */ #define HA_PARTITION_FUNCTION_SUPPORTED (1UL << 12) #define HA_FAST_CHANGE_PARTITION (1UL << 13) -#define HA_PARTITION_ONE_PHASE (1UL << 14) /* operations for disable/enable indexes */ #define HA_KEY_SWITCH_NONUNIQ 0 @@ -3559,15 +3558,6 @@ public: int ha_create_partitioning_metadata(const char *name, const char *old_name, chf_create_flags action_flag); - int ha_change_partitions(HA_CREATE_INFO *create_info, - const char *path, - ulonglong * const copied, - ulonglong * const deleted, - const uchar *pack_frm_data, - size_t pack_frm_len); - int ha_drop_partitions(const char *path); - int ha_rename_partitions(const char *path); - void adjust_next_insert_id_after_explicit_value(ulonglong nr); int update_auto_increment(); virtual void print_error(int error, myf errflag); @@ -5072,22 +5062,11 @@ public: chf_create_flags action_flag) { return FALSE; } - virtual int change_partitions(HA_CREATE_INFO *create_info, - const char *path, - ulonglong * const copied, - ulonglong * const deleted, - const uchar *pack_frm_data, - size_t pack_frm_len) - { return HA_ERR_WRONG_COMMAND; } /* @return true if it's necessary to switch current statement log format from STATEMENT to ROW if binary log format is MIXED and autoincrement values are changed in the statement */ virtual bool autoinc_lock_mode_stmt_unsafe() const { return false; } - virtual int drop_partitions(const char *path) - { return HA_ERR_WRONG_COMMAND; } - virtual int rename_partitions(const char *path) - { return HA_ERR_WRONG_COMMAND; } virtual bool set_ha_share_ref(Handler_share **arg_ha_share) { DBUG_ASSERT(!ha_share); diff --git a/sql/lex_string.h b/sql/lex_string.h index e7a732346c4..c2c9701a70c 100644 --- a/sql/lex_string.h +++ b/sql/lex_string.h @@ -108,4 +108,8 @@ static inline bool lex_string_eq(const LEX_CSTRING *a, const char *b, size_t b_l return strcasecmp(a->str, b) == 0; } +inline LEX_CSTRING strdup_root(MEM_ROOT *root, const LEX_CSTRING str) +{ + return safe_lexcstrdup_root(root, str); +} #endif /* LEX_STRING_INCLUDED */ diff --git a/sql/partition_element.h b/sql/partition_element.h index 2427f42534c..8e0c32b7405 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -32,15 +32,13 @@ enum partition_type { enum partition_state { PART_NORMAL= 0, - PART_IS_DROPPED= 1, - PART_TO_BE_DROPPED= 2, - PART_TO_BE_ADDED= 3, - PART_TO_BE_REORGED= 4, - PART_REORGED_DROPPED= 5, - PART_CHANGED= 6, - PART_IS_CHANGED= 7, - PART_IS_ADDED= 8, - PART_ADMIN= 9 + PART_IS_DROPPED= 2, + PART_TO_BE_DROPPED= 4, + PART_TO_BE_ADDED= 8, + PART_TO_BE_REORGED= 16, + PART_REORGED_DROPPED= 32, + PART_CHANGED= 64, + PART_ADMIN= 128 }; /* @@ -111,7 +109,6 @@ public: ha_rows part_min_rows; longlong range_value; const char *partition_name; - struct st_ddl_log_memory_entry *log_entry; const char* part_comment; const char* data_file_name; const char* index_file_name; @@ -130,7 +127,7 @@ public: partition_element() : part_max_rows(0), part_min_rows(0), range_value(0), partition_name(NULL), - log_entry(NULL), part_comment(NULL), + part_comment(NULL), data_file_name(NULL), index_file_name(NULL), engine_type(NULL), connect_string(null_clex_str), part_state(PART_NORMAL), nodegroup_id(UNDEF_NODEGROUP), has_null_value(FALSE), @@ -143,7 +140,6 @@ public: : part_max_rows(part_elem->part_max_rows), part_min_rows(part_elem->part_min_rows), range_value(0), partition_name(NULL), - log_entry(NULL), part_comment(part_elem->part_comment), data_file_name(part_elem->data_file_name), index_file_name(part_elem->index_file_name), @@ -174,6 +170,11 @@ public: DBUG_ASSERT(!num_subparts || parent_part); return num_subparts ? parent_part->id * num_subparts + id : id; } + + uint32 serial_id(partition_element *sub_elem, uint num_subparts) const + { + return sub_elem ? sub_elem->serial_id(num_subparts) : serial_id(num_subparts); + } }; #endif /* PARTITION_ELEMENT_INCLUDED */ diff --git a/sql/partition_info.h b/sql/partition_info.h index 46e17044b85..635f1df2736 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -32,8 +32,6 @@ typedef int (*get_part_id_func)(partition_info *part_info, uint32 *part_id, typedef int (*get_subpart_id_func)(partition_info *part_info, uint32 *part_id); typedef bool (*check_constants_func)(THD *thd, partition_info *part_info); -struct st_ddl_log_memory_entry; - #define MAX_PART_NAME_SIZE 8 @@ -79,7 +77,7 @@ struct Vers_part_info : public Sql_alloc partition_element *hist_part; }; -class partition_info : public DDL_LOG_STATE, public Sql_alloc +class partition_info : public Sql_alloc { public: /* diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index a71b0fd0bb3..a330f4a9849 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -4938,7 +4938,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, without any changes at all. */ flags= table->file->alter_table_flags(alter_info->flags); - if (flags & (HA_FAST_CHANGE_PARTITION | HA_PARTITION_ONE_PHASE)) + if (flags & HA_FAST_CHANGE_PARTITION) { *fast_alter_table= true; /* Force table re-open for consistency with the main case. */ @@ -4981,7 +4981,7 @@ uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, my_error(ER_PARTITION_FUNCTION_FAILURE, MYF(0)); goto err; } - if ((flags & (HA_FAST_CHANGE_PARTITION | HA_PARTITION_ONE_PHASE)) != 0) + if (flags & HA_FAST_CHANGE_PARTITION) { /* "Fast" change of partitioning is supported in this case. @@ -6018,534 +6018,554 @@ err: } -/* - Change partitions, used to implement ALTER TABLE ADD/REORGANIZE/COALESCE - partitions. This method is used to implement both single-phase and multi- - phase implementations of ADD/REORGANIZE/COALESCE partitions. - - SYNOPSIS - mysql_change_partitions() - lpt Struct containing parameters - - RETURN VALUES - TRUE Failure - FALSE Success +/** + DDL logger and partiton renamer - DESCRIPTION - Request handler to add partitions as set in states of the partition - - Elements of the lpt parameters used: - create_info Create information used to create partitions - db Database name - table_name Table name - copied Output parameter where number of copied - records are added - deleted Output parameter where number of deleted - records are added + Processes DROP PARTITION action and serves to other ALTER commands as an + utility basis. */ -static bool mysql_change_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) +class Alter_partition_logger { - char path[FN_REFLEN+1]; - int error; - handler *file= lpt->table->file; - THD *thd= lpt->thd; - DBUG_ENTER("mysql_change_partitions"); + char path_buf[FN_REFLEN + 1]; - build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); +protected: + const char *path; + uint from_name_type; /* NORMAL_PART_NAME, TEMP_PART_NAME, RENAMED_PART_NAME */ + uint to_name_type; /* same + SKIP_PART_NAME */ - if(mysql_trans_prepare_alter_copy_data(thd)) - DBUG_RETURN(TRUE); + DDL_LOG_ENTRY ddl_log_entry; - /* TODO: test if bulk_insert would increase the performance */ + char from_name[FN_REFLEN + 1]; + char to_name[FN_REFLEN + 1]; + ALTER_PARTITION_PARAM_TYPE *lpt; + TABLE *table; + partition_info *part_info; + DDL_LOG_STATE *rollback_chain; + DDL_LOG_STATE *cleanup_chain; + ha_partition *hp; + +public: + enum Phase + { + RENAME_TO_BACKUPS= 0, + LOG_DROP_BACKUPS= 2, + ADD_PARTITIONS= 4, + RENAME_ADDED_PARTS= 8, + CONVERT_OUT= 16, + CONVERT_IN= 32, + NO_PHASE= 255 + } phase; + + Alter_partition_logger(ALTER_PARTITION_PARAM_TYPE *lpt) : + path(NULL), from_name_type(SKIP_PART_NAME), + to_name_type(SKIP_PART_NAME), + lpt(lpt), table(lpt->table), part_info(lpt->part_info), + rollback_chain(&lpt->rollback_chain), + cleanup_chain(&lpt->cleanup_chain), + hp((ha_partition *) table->file) + { + DBUG_ASSERT(table->file->ht->db_type == DB_TYPE_PARTITION_DB); + build_table_filename(path_buf, sizeof(path_buf) - 1, lpt->db.str, + lpt->table_name.str, "", 0); + path= path_buf; + } + + virtual ~Alter_partition_logger() {} + bool iterate(Phase phase, uint from_name_arg, uint to_name_arg, + List<partition_element> *parts); + + /** + Make from_name, to_name according to from_name_type, to_name_type. + Set common ddl_log_entry parameters like flags, handler_name. + */ - if (unlikely((error= file->ha_change_partitions(lpt->create_info, path, - &lpt->copied, - &lpt->deleted, - lpt->pack_frm_data, - lpt->pack_frm_len)))) - { - file->print_error(error, MYF(error != ER_OUTOFMEMORY ? 0 : ME_FATAL)); + bool build_names(partition_element *part_elem, partition_element *sub_elem) + { + bzero(&ddl_log_entry, sizeof(ddl_log_entry)); + ddl_log_entry.flags= DDL_LOG_FLAG_ALTER_PARTITION; + + DBUG_ASSERT(lpt->thd->mdl_context.is_lock_owner(MDL_key::TABLE, + table->s->db.str, + table->s->table_name.str, + MDL_EXCLUSIVE)); + + handlerton *ht= part_elem->engine_type ? + part_elem->engine_type : part_info->default_engine_type; + DBUG_ASSERT(ht); + lex_string_set(&ddl_log_entry.handler_name, + ha_resolve_storage_engine_name(ht)); + if (!sub_elem) + { + if (from_name_type != SKIP_PART_NAME && + create_partition_name(from_name, sizeof(from_name), path, + part_elem->partition_name, from_name_type, + true /* translate */)) + return true; + if (to_name_type != SKIP_PART_NAME && + create_partition_name(to_name, sizeof(to_name), path, + part_elem->partition_name, to_name_type, + true /* translate */)) + return true; + } + else + { + if (from_name_type != SKIP_PART_NAME && + create_subpartition_name(from_name, sizeof(from_name), path, + part_elem->partition_name, + sub_elem->partition_name, from_name_type)) + return true; + if (to_name_type != SKIP_PART_NAME && + create_subpartition_name(to_name, sizeof(to_name), path, + part_elem->partition_name, + sub_elem->partition_name, to_name_type)) + return true; + } + + return false; } - if (mysql_trans_commit_alter_copy_data(thd)) - error= 1; /* The error has been reported */ + /** + check_state() selects partitions to be processed by process_partition() + */ - DBUG_RETURN(MY_TEST(error)); -} + virtual + bool check_state(partition_element *part_elem) + { + return part_elem->part_state == PART_TO_BE_DROPPED; + } + /** + DROP PARTITION processing -/* - Rename partitions in an ALTER TABLE of partitions + Iterate phases: rename partitions marked for drop to backup partitions and + write rollback_chain so it reverts these operations. Write cleanup_chain so + it drops backup partitions, but only when rollback_chain is inactive. + */ - SYNOPSIS - mysql_rename_partitions() - lpt Struct containing parameters + bool rename_parts() + { + if (iterate(RENAME_TO_BACKUPS, NORMAL_PART_NAME, RENAMED_PART_NAME, + &part_info->partitions)) + return true; + if (iterate(LOG_DROP_BACKUPS, RENAMED_PART_NAME, SKIP_PART_NAME, + &part_info->partitions)) + return true; + return false; + } - RETURN VALUES - TRUE Failure - FALSE Success + bool debug_crash_or_fail() + { +#ifndef DBUG_OFF + switch (phase) + { + case RENAME_TO_BACKUPS: + if (ERROR_INJECT("alter_partition_rename_to_backup")) + return true; + break; + case RENAME_ADDED_PARTS: + if (ERROR_INJECT("alter_partition_rename_added_part")) + return true; + break; + case LOG_DROP_BACKUPS: + if (ERROR_INJECT("alter_partition_rename_drop_backup")) + return true; + break; + default: + break; + } +#endif + return false; + } - DESCRIPTION - Request handler to rename partitions as set in states of the partition + void debug_assert_states(partition_element *part_elem) + { +#ifndef DBUG_OFF + switch (phase) + { + case RENAME_TO_BACKUPS: + DBUG_ASSERT(from_name_type == NORMAL_PART_NAME); + DBUG_ASSERT(to_name_type == RENAMED_PART_NAME); + DBUG_ASSERT(part_elem->part_state & ( + PART_TO_BE_DROPPED | + PART_TO_BE_REORGED | + PART_REORGED_DROPPED | + PART_CHANGED)); + break; + case RENAME_ADDED_PARTS: + DBUG_ASSERT(from_name_type == TEMP_PART_NAME); + DBUG_ASSERT(to_name_type == NORMAL_PART_NAME); + break; + case CONVERT_OUT: + DBUG_ASSERT(from_name_type == NORMAL_PART_NAME); + DBUG_ASSERT(to_name_type == SKIP_PART_NAME); + DBUG_ASSERT(to_name[0]); /* to_name is external table name */ + DBUG_ASSERT(part_elem->part_state & PART_TO_BE_DROPPED); + break; + case CONVERT_IN: + DBUG_ASSERT(from_name_type == SKIP_PART_NAME); + DBUG_ASSERT(to_name_type == NORMAL_PART_NAME); + DBUG_ASSERT(to_name[0]); /* to_name is external table name */ + DBUG_ASSERT(part_elem->part_state & PART_TO_BE_ADDED); + break; + case ADD_PARTITIONS: + DBUG_ASSERT(to_name_type == SKIP_PART_NAME); + break; + case LOG_DROP_BACKUPS: + DBUG_ASSERT(from_name_type == RENAMED_PART_NAME); + DBUG_ASSERT(to_name_type == SKIP_PART_NAME); + DBUG_ASSERT(part_elem->part_state & ( + PART_TO_BE_DROPPED | + PART_TO_BE_REORGED | + PART_REORGED_DROPPED | + PART_CHANGED)); + break; + default: + DBUG_ASSERT(0); + break; + } +#endif + } - Parameters used: - db Database name - table_name Table name -*/ + /** + The body of iteration: each partition selected by check_state() is + processed here. -static bool mysql_rename_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) -{ - char path[FN_REFLEN+1]; - int error; - DBUG_ENTER("mysql_rename_partitions"); + Do DDL logging and convey the rename. It doesn't do drop because that is + done by replaying cleanup_chain. Add is done by Alter_partition_add. + */ - build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); - if (unlikely((error= lpt->table->file->ha_rename_partitions(path)))) + virtual + bool process_partition(partition_element *part_elem, + partition_element *sub_elem) { - if (error != 1) - lpt->table->file->print_error(error, MYF(0)); - DBUG_RETURN(TRUE); - } - DBUG_RETURN(FALSE); -} + debug_assert_states(part_elem); + DDL_LOG_STATE *output_chain= rollback_chain; + switch (phase) + { + case RENAME_TO_BACKUPS: + case RENAME_ADDED_PARTS: + case CONVERT_OUT: + case CONVERT_IN: + ddl_log_entry.action_type= DDL_LOG_RENAME_TABLE_ACTION; + ddl_log_entry.phase= DDL_RENAME_PHASE_TABLE; + ddl_log_entry.name= { from_name, strlen(from_name) }; + ddl_log_entry.from_name= { to_name, strlen(to_name) }; + break; + case ADD_PARTITIONS: + ddl_log_entry.action_type= DDL_LOG_DROP_TABLE_ACTION; + ddl_log_entry.tmp_name= { from_name, strlen(from_name) }; + break; + case LOG_DROP_BACKUPS: + ddl_log_entry.action_type= DDL_LOG_DROP_TABLE_ACTION; + ddl_log_entry.tmp_name= { from_name, strlen(from_name) }; + output_chain= cleanup_chain; + /* cleanup_chain cannot be executed before rollback_chain */ + ddl_log_link_chains(cleanup_chain, rollback_chain); + break; + default: + DBUG_ASSERT(0); + return true; + } -/* - Drop partitions in an ALTER TABLE of partitions + ddl_log_entry.next_entry= output_chain->list ? + output_chain->list->entry_pos : 0; + if (ddl_log_write(output_chain, &ddl_log_entry)) + { + my_error(ER_DDL_LOG_ERROR, MYF(0)); + return true; + } - SYNOPSIS - mysql_drop_partitions() - lpt Struct containing parameters + if (ddl_log_entry.action_type == DDL_LOG_RENAME_TABLE_ACTION) + { + int ha_err; + DBUG_ASSERT(table->file->ht->db_type == DB_TYPE_PARTITION_DB); + handler *file= (phase & (RENAME_ADDED_PARTS|CONVERT_IN) ? + hp->get_new_handler(part_elem, sub_elem) : + hp->get_child_handler(part_elem, sub_elem)); + ha_err= file->ha_rename_table(from_name, to_name); + if (ha_err || + ERROR_INJECT("alter_partition_rename_table")) + { + file->print_error(ha_err, MYF(0)); + return true; + } + } + + return debug_crash_or_fail(); + } +}; - RETURN VALUES - TRUE Failure - FALSE Success - DESCRIPTION - Drop the partitions marked with PART_TO_BE_DROPPED state and remove - those partitions from the list. - Parameters used: - table Table object - db Database name - table_name Table name +/** + ADD PARTITION action */ -static bool mysql_drop_partitions(ALTER_PARTITION_PARAM_TYPE *lpt) +class Alter_partition_add : public Alter_partition_logger { - char path[FN_REFLEN+1]; - partition_info *part_info= lpt->table->part_info; - List_iterator<partition_element> part_it(part_info->partitions); - int error; - DBUG_ENTER("mysql_drop_partitions"); +protected: + uint disable_non_uniq_indexes; + int ha_err; - DBUG_ASSERT(lpt->thd->mdl_context.is_lock_owner(MDL_key::TABLE, - lpt->table->s->db.str, - lpt->table->s->table_name.str, - MDL_EXCLUSIVE)); +public: + Alter_partition_add(ALTER_PARTITION_PARAM_TYPE *lpt) : + Alter_partition_logger(lpt) + { + disable_non_uniq_indexes= hp->indexes_are_disabled(); + } - build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); - if ((error= lpt->table->file->ha_drop_partitions(path))) + bool check_state(partition_element *part_elem) { - lpt->table->file->print_error(error, MYF(0)); - DBUG_RETURN(TRUE); + return part_elem->part_state == PART_TO_BE_ADDED; } - DBUG_RETURN(FALSE); -} + /** + ADD PARTITION processing + */ -/* - Convert partition to a table in an ALTER TABLE of partitions + bool add_parts() + { + ha_err= hp->allocate_partitions(); + if (ha_err) + { + hp->print_error(ha_err, MYF(0)); + return true; + } + if (iterate(ADD_PARTITIONS, NORMAL_PART_NAME, SKIP_PART_NAME, + &part_info->partitions)) + return true; + return false; + } - SYNOPSIS - alter_partition_convert_out() - lpt Struct containing parameters + bool process_partition(partition_element *part_elem, + partition_element *sub_elem) + { + DBUG_ASSERT(phase == ADD_PARTITIONS); + DBUG_ASSERT(!ha_err); - RETURN VALUES - TRUE Failure - FALSE Success + if (Alter_partition_logger::process_partition(part_elem, sub_elem)) + return true; - DESCRIPTION - Rename partition table marked with PART_TO_BE_DROPPED into a separate table - under the name lpt->alter_ctx->(new_db, new_name). + ha_err= hp->create_partition(table, lpt->create_info, from_name, + sub_elem ? sub_elem : part_elem, + disable_non_uniq_indexes); + if (ha_err || + ERROR_INJECT("alter_partition_add")) + { + hp->print_error(ha_err, MYF(0)); + hp->cleanup_new_partition(); + return true; + } - This is ddl-logged by write_log_convert_out_partition(). + return false; + } +}; + + +/** + Change partition action + + ADD HASH / COALESCE / REBUILD / REORGANIZE / CONVERT IN / CONVERT OUT */ -static bool alter_partition_convert_out(ALTER_PARTITION_PARAM_TYPE *lpt) +class Alter_partition_change : public Alter_partition_add { - partition_info *part_info= lpt->table->part_info; - THD *thd= lpt->thd; - int error; - handler *file= get_new_handler(NULL, thd->mem_root, part_info->default_engine_type); + uint processed_state; - DBUG_ASSERT(lpt->thd->mdl_context.is_lock_owner(MDL_key::TABLE, - lpt->table->s->db.str, - lpt->table->s->table_name.str, - MDL_EXCLUSIVE)); +public: + using Alter_partition_add::Alter_partition_add; - char from_name[FN_REFLEN + 1], to_name[FN_REFLEN + 1]; - const char *path= lpt->table->s->path.str; + bool check_state(partition_element *part_elem) + { + return part_elem->part_state & processed_state; + } - build_table_filename(to_name, sizeof(to_name) - 1, lpt->alter_ctx->new_db.str, - lpt->alter_ctx->new_name.str, "", 0); + /** + REORGANIZE processing part 1 + */ - for (const partition_element &e: part_info->partitions) + bool add_parts_and_copy_data(THD *thd) { - if (e.part_state != PART_TO_BE_DROPPED) - continue; - - if (unlikely((error= create_partition_name(from_name, sizeof(from_name), - path, e.partition_name, - NORMAL_PART_NAME, FALSE)))) + ha_err= hp->allocate_partitions(); + if (ha_err) { - DBUG_ASSERT(thd->is_error()); + hp->print_error(ha_err, MYF(0)); return true; } - if (DBUG_IF("error_convert_partition_00") || - unlikely(error= file->ha_rename_table(from_name, to_name))) + + /* + ha_enable_transaction() must be done before ha_create(): + Aria stores born_transactional and uses it for copy data. + */ + if ((ha_err= mysql_trans_prepare_alter_copy_data(thd))) { - my_error(ER_ERROR_ON_RENAME, MYF(0), from_name, to_name, my_errno); - lpt->table->file->print_error(error, MYF(0)); + hp->print_error(ha_err, MYF(0)); return true; } - break; - } - return false; -} + processed_state= (PART_TO_BE_ADDED|PART_CHANGED); + if (iterate(ADD_PARTITIONS, TEMP_PART_NAME, SKIP_PART_NAME, + &part_info->partitions)) + { + (void) mysql_trans_commit_alter_copy_data(thd, true); + return true; + } + if (ERROR_INJECT("change_partition_add_parts_1") || + (ha_err= hp->copy_partitions(&lpt->copied, &lpt->deleted)) || + ERROR_INJECT("change_partition_add_parts_2") || + (ha_err= mysql_trans_commit_alter_copy_data(thd, false)) || + ERROR_INJECT("change_partition_add_parts_3")) + { + (void) mysql_trans_commit_alter_copy_data(thd, true); + hp->print_error(ha_err, MYF(0)); + return true; + } -/* - Release all log entries for this partition info struct - SYNOPSIS - release_part_info_log_entries() - first_log_entry First log entry in list to release - RETURN VALUES - NONE -*/ + return false; + } -static void release_part_info_log_entries(DDL_LOG_MEMORY_ENTRY *log_entry) -{ - DBUG_ENTER("release_part_info_log_entries"); + /** + REORGANIZE processing part 2 + */ - while (log_entry) + bool rename_parts() { - DDL_LOG_MEMORY_ENTRY *next= log_entry->next_active_log_entry; - ddl_log_release_memory_entry(log_entry); - log_entry= next; + DEBUG_SYNC(lpt->thd, "before_rename_partitions"); + if (part_info->temp_partitions.elements) + { + processed_state= PART_TO_BE_REORGED; + if (iterate(RENAME_TO_BACKUPS, NORMAL_PART_NAME, RENAMED_PART_NAME, + &part_info->temp_partitions)) + return true; + } + processed_state= PART_CHANGED|PART_REORGED_DROPPED; + if (iterate(RENAME_TO_BACKUPS, NORMAL_PART_NAME, RENAMED_PART_NAME, + &part_info->partitions) || + ERROR_INJECT("change_partition_rename_parts_1")) + return true; + processed_state= PART_TO_BE_ADDED|PART_CHANGED; + if (iterate(RENAME_ADDED_PARTS, TEMP_PART_NAME, NORMAL_PART_NAME, + &part_info->partitions) || + ERROR_INJECT("change_partition_rename_parts_2")) + return true; + if (part_info->temp_partitions.elements) + { + processed_state= PART_TO_BE_REORGED; + if (iterate(LOG_DROP_BACKUPS, RENAMED_PART_NAME, SKIP_PART_NAME, + &part_info->temp_partitions)) + return true; + } + processed_state= PART_CHANGED|PART_REORGED_DROPPED; + if (iterate(LOG_DROP_BACKUPS, RENAMED_PART_NAME, SKIP_PART_NAME, + &part_info->partitions)) + return true; + return false; } - DBUG_VOID_RETURN; -} + /** + CONVERT OUT processing + */ -/* - Log an rename frm file - SYNOPSIS - write_log_replace_frm() - lpt Struct for parameters - next_entry Next reference to use in log record - from_path Name to rename from - to_path Name to rename to - RETURN VALUES - TRUE Error - FALSE Success - DESCRIPTION - Support routine that writes a replace of an frm file into the - ddl log. It also inserts an entry that keeps track of used space into - the partition info object -*/ + bool convert_out() + { + build_table_filename(to_name, sizeof(to_name) - 1, + lpt->alter_ctx->new_db.str, + lpt->alter_ctx->new_name.str, "", 0); -bool write_log_replace_frm(ALTER_PARTITION_PARAM_TYPE *lpt, - uint next_entry, - const char *from_path, - const char *to_path) -{ - DDL_LOG_ENTRY ddl_log_entry; - DDL_LOG_MEMORY_ENTRY *log_entry; - DBUG_ENTER("write_log_replace_frm"); + processed_state= PART_TO_BE_DROPPED; + if (iterate(CONVERT_OUT, NORMAL_PART_NAME, SKIP_PART_NAME, + &part_info->partitions)) + return true; + return false; + } - bzero(&ddl_log_entry, sizeof(ddl_log_entry)); - ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION; - ddl_log_entry.next_entry= next_entry; - lex_string_set(&ddl_log_entry.handler_name, reg_ext); - lex_string_set(&ddl_log_entry.name, to_path); - lex_string_set(&ddl_log_entry.from_name, from_path); + /** + CONVERT IN processing + */ - if (ddl_log_write_entry(&ddl_log_entry, &log_entry)) + bool convert_in() { - DBUG_RETURN(true); - } - ddl_log_add_entry(lpt->part_info, log_entry); - DBUG_RETURN(false); -} + TABLE_LIST* table_from= lpt->table_list->next_local; + ha_err= hp->allocate_partitions(); + if (ha_err) + { + hp->print_error(ha_err, MYF(0)); + return true; + } -/* - Log final partition changes in change partition - SYNOPSIS - write_log_changed_partitions() - lpt Struct containing parameters - RETURN VALUES - TRUE Error - FALSE Success - DESCRIPTION - This code is used to perform safe ADD PARTITION for HASH partitions - and COALESCE for HASH partitions and REORGANIZE for any type of - partitions. - We prepare entries for all partitions except the reorganised partitions - in REORGANIZE partition, those are handled by - write_log_dropped_partitions. For those partitions that are replaced - special care is needed to ensure that this is performed correctly and - this requires a two-phased approach with this log as a helper for this. - - This code is closely intertwined with the code in rename_partitions in - the partition handler. -*/ + build_table_filename(from_name, sizeof(from_name) - 1, + table_from->db.str, table_from->table_name.str, + "", 0); -static bool write_log_changed_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, - uint *next_entry, const char *path) -{ - DDL_LOG_ENTRY ddl_log_entry; - partition_info *part_info= lpt->part_info; - DDL_LOG_MEMORY_ENTRY *log_entry; - char tmp_path[FN_REFLEN + 1]; - char normal_path[FN_REFLEN + 1]; - List_iterator<partition_element> part_it(part_info->partitions); - uint temp_partitions= part_info->temp_partitions.elements; - uint num_elements= part_info->partitions.elements; - uint i= 0; - DBUG_ENTER("write_log_changed_partitions"); + processed_state= PART_TO_BE_ADDED; + if (iterate(CONVERT_IN, SKIP_PART_NAME, NORMAL_PART_NAME, + &part_info->partitions)) + return true; + return false; + } - do + bool process_partition(partition_element *part_elem, + partition_element *sub_elem) { - partition_element *part_elem= part_it++; - if (part_elem->part_state == PART_IS_CHANGED || - (part_elem->part_state == PART_IS_ADDED && temp_partitions)) + switch (phase) { - bzero(&ddl_log_entry, sizeof(ddl_log_entry)); - if (part_info->is_sub_partitioned()) - { - List_iterator<partition_element> sub_it(part_elem->subpartitions); - uint num_subparts= part_info->num_subparts; - uint j= 0; - do - { - partition_element *sub_elem= sub_it++; - ddl_log_entry.next_entry= *next_entry; - lex_string_set(&ddl_log_entry.handler_name, - ha_resolve_storage_engine_name(sub_elem-> - engine_type)); - if (create_subpartition_name(tmp_path, sizeof(tmp_path), path, - part_elem->partition_name, - sub_elem->partition_name, - TEMP_PART_NAME) || - create_subpartition_name(normal_path, sizeof(normal_path), path, - part_elem->partition_name, - sub_elem->partition_name, - NORMAL_PART_NAME)) - DBUG_RETURN(TRUE); - lex_string_set(&ddl_log_entry.name, normal_path); - lex_string_set(&ddl_log_entry.from_name, tmp_path); - if (part_elem->part_state == PART_IS_CHANGED) - ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION; - else - ddl_log_entry.action_type= DDL_LOG_RENAME_ACTION; - if (ddl_log_write_entry(&ddl_log_entry, &log_entry)) - DBUG_RETURN(TRUE); - - *next_entry= log_entry->entry_pos; - sub_elem->log_entry= log_entry; - ddl_log_add_entry(part_info, log_entry); - } while (++j < num_subparts); - } - else - { - ddl_log_entry.next_entry= *next_entry; - lex_string_set(&ddl_log_entry.handler_name, - ha_resolve_storage_engine_name(part_elem->engine_type)); - if (create_partition_name(tmp_path, sizeof(tmp_path), path, - part_elem->partition_name, TEMP_PART_NAME, - TRUE) || - create_partition_name(normal_path, sizeof(normal_path), path, - part_elem->partition_name, NORMAL_PART_NAME, - TRUE)) - DBUG_RETURN(TRUE); - lex_string_set(&ddl_log_entry.name, normal_path); - lex_string_set(&ddl_log_entry.from_name, tmp_path); - if (part_elem->part_state == PART_IS_CHANGED) - ddl_log_entry.action_type= DDL_LOG_REPLACE_ACTION; - else - ddl_log_entry.action_type= DDL_LOG_RENAME_ACTION; - if (ddl_log_write_entry(&ddl_log_entry, &log_entry)) - { - DBUG_RETURN(TRUE); - } - *next_entry= log_entry->entry_pos; - part_elem->log_entry= log_entry; - ddl_log_add_entry(part_info, log_entry); - } + case ADD_PARTITIONS: + return Alter_partition_add::process_partition(part_elem, sub_elem); + case RENAME_TO_BACKUPS: + case RENAME_ADDED_PARTS: + case LOG_DROP_BACKUPS: + case CONVERT_OUT: + case CONVERT_IN: + return Alter_partition_logger::process_partition(part_elem, sub_elem); + default: + DBUG_ASSERT(0); + return true; } - } while (++i < num_elements); - DBUG_RETURN(FALSE); -} - - -/* - Log dropped or converted partitions - SYNOPSIS - log_drop_or_convert_action() - lpt Struct containing parameters - RETURN VALUES - TRUE Error - FALSE Success -*/ - -enum log_action_enum -{ - ACT_DROP = 0, - ACT_CONVERT_IN, - ACT_CONVERT_OUT + return false; + } }; -static bool log_drop_or_convert_action(ALTER_PARTITION_PARAM_TYPE *lpt, - uint *next_entry, const char *path, - const char *from_name, bool temp_list, - const log_action_enum convert_action) + +bool Alter_partition_logger::iterate(Phase phase_arg, + uint from_name_arg, uint to_name_arg, + List<partition_element> *parts) { - DDL_LOG_ENTRY ddl_log_entry; - DBUG_ASSERT(convert_action == ACT_DROP || (from_name != NULL)); - partition_info *part_info= lpt->part_info; - DDL_LOG_MEMORY_ENTRY *log_entry; - char tmp_path[FN_REFLEN + 1]; - List_iterator<partition_element> part_it(part_info->partitions); - List_iterator<partition_element> temp_it(part_info->temp_partitions); - uint num_temp_partitions= part_info->temp_partitions.elements; - uint num_elements= part_info->partitions.elements; - DBUG_ENTER("log_drop_or_convert_action"); - - bzero(&ddl_log_entry, sizeof(ddl_log_entry)); - - ddl_log_entry.action_type= convert_action ? - DDL_LOG_RENAME_ACTION : - DDL_LOG_DELETE_ACTION; - if (temp_list) - num_elements= num_temp_partitions; - while (num_elements--) - { - partition_element *part_elem; - if (temp_list) - part_elem= temp_it++; - else - part_elem= part_it++; - if (part_elem->part_state == PART_TO_BE_DROPPED || - part_elem->part_state == PART_TO_BE_ADDED || - part_elem->part_state == PART_CHANGED) - { - uint name_variant; - if (part_elem->part_state == PART_CHANGED || - (part_elem->part_state == PART_TO_BE_ADDED && - num_temp_partitions)) - name_variant= TEMP_PART_NAME; - else - name_variant= NORMAL_PART_NAME; - DBUG_ASSERT(convert_action != ACT_CONVERT_IN || - part_elem->part_state == PART_TO_BE_ADDED); - DBUG_ASSERT(convert_action != ACT_CONVERT_OUT || - part_elem->part_state == PART_TO_BE_DROPPED); + phase= phase_arg; + from_name_type= from_name_arg; + to_name_type= to_name_arg; + List_iterator<partition_element> part_it(*parts); + partition_element *part_elem; + while ((part_elem= part_it++)) + { + if (check_state(part_elem)) + { if (part_info->is_sub_partitioned()) { - DBUG_ASSERT(!convert_action); List_iterator<partition_element> sub_it(part_elem->subpartitions); uint num_subparts= part_info->num_subparts; uint j= 0; do { partition_element *sub_elem= sub_it++; - ddl_log_entry.next_entry= *next_entry; - lex_string_set(&ddl_log_entry.handler_name, - ha_resolve_storage_engine_name(sub_elem-> - engine_type)); - if (create_subpartition_name(tmp_path, sizeof(tmp_path), path, - part_elem->partition_name, - sub_elem->partition_name, name_variant)) - DBUG_RETURN(TRUE); - lex_string_set(&ddl_log_entry.name, tmp_path); - if (ddl_log_write_entry(&ddl_log_entry, &log_entry)) - { - DBUG_RETURN(TRUE); - } - *next_entry= log_entry->entry_pos; - sub_elem->log_entry= log_entry; - ddl_log_add_entry(part_info, log_entry); + if (build_names(part_elem, sub_elem)) + return true; + if (process_partition(part_elem, sub_elem)) + return true; } while (++j < num_subparts); } else { - ddl_log_entry.next_entry= *next_entry; - lex_string_set(&ddl_log_entry.handler_name, - ha_resolve_storage_engine_name(part_elem->engine_type)); - if (create_partition_name(tmp_path, sizeof(tmp_path), path, - part_elem->partition_name, name_variant, - TRUE)) - DBUG_RETURN(TRUE); - switch (convert_action) - { - case ACT_CONVERT_OUT: - ddl_log_entry.from_name= { from_name, strlen(from_name) }; - /* fall through */ - case ACT_DROP: - ddl_log_entry.name= { tmp_path, strlen(tmp_path) }; - break; - case ACT_CONVERT_IN: - ddl_log_entry.name= { from_name, strlen(from_name) }; - ddl_log_entry.from_name= { tmp_path, strlen(tmp_path) }; - } - if (ddl_log_write_entry(&ddl_log_entry, &log_entry)) - { - DBUG_RETURN(TRUE); - } - *next_entry= log_entry->entry_pos; - part_elem->log_entry= log_entry; - ddl_log_add_entry(part_info, log_entry); + if (build_names(part_elem, NULL)) + return true; + if (process_partition(part_elem, NULL)) + return true; } } } - DBUG_RETURN(FALSE); -} - - -inline -static bool write_log_dropped_partitions(ALTER_PARTITION_PARAM_TYPE *lpt, - uint *next_entry, const char *path, - bool temp_list) -{ - return log_drop_or_convert_action(lpt, next_entry, path, NULL, temp_list, - ACT_DROP); -} - -inline -static bool write_log_convert_partition(ALTER_PARTITION_PARAM_TYPE *lpt, - uint *next_entry, const char *path) -{ - char other_table[FN_REFLEN + 1]; - const ulong f= lpt->alter_info->partition_flags; - DBUG_ASSERT((f & ALTER_PARTITION_CONVERT_IN) || (f & ALTER_PARTITION_CONVERT_OUT)); - const log_action_enum convert_action= (f & ALTER_PARTITION_CONVERT_IN) - ? ACT_CONVERT_IN : ACT_CONVERT_OUT; - build_table_filename(other_table, sizeof(other_table) - 1, lpt->alter_ctx->new_db.str, - lpt->alter_ctx->new_name.str, "", 0); - DDL_LOG_MEMORY_ENTRY *main_entry= lpt->part_info->main_entry; - bool res= log_drop_or_convert_action(lpt, next_entry, path, other_table, - false, convert_action); - /* - NOTE: main_entry is "drop shadow frm", we have to keep it like this - because partitioning crash-safety disables it at install shadow FRM phase. - This is needed to avoid spurious drop action when the shadow frm is replaced - by the backup frm and there is nothing to drop. - */ - lpt->part_info->main_entry= main_entry; - return res; + return false; } @@ -6565,12 +6585,11 @@ static bool write_log_convert_partition(ALTER_PARTITION_PARAM_TYPE *lpt, */ static bool write_log_drop_frm(ALTER_PARTITION_PARAM_TYPE *lpt, - DDL_LOG_STATE *drop_chain) + DDL_LOG_STATE *drop_chain, + bool drop_backup) { char path[FN_REFLEN + 1]; DBUG_ENTER("write_log_drop_frm"); - const DDL_LOG_STATE *main_chain= lpt->part_info; - const bool drop_backup= (drop_chain != main_chain); build_table_shadow_filename(path, sizeof(path) - 1, lpt, drop_backup); mysql_mutex_lock(&LOCK_gdl); @@ -6588,15 +6607,15 @@ static bool write_log_drop_frm(ALTER_PARTITION_PARAM_TYPE *lpt, } if (ddl_log_write_execute_entry(drop_chain->list->entry_pos, - drop_backup ? - main_chain->execute_entry->entry_pos : 0, + (drop_backup ? + lpt->rollback_chain.execute_entry->entry_pos : + 0), &drop_chain->execute_entry)) goto error; mysql_mutex_unlock(&LOCK_gdl); DBUG_RETURN(FALSE); error: - release_part_info_log_entries(drop_chain->list); mysql_mutex_unlock(&LOCK_gdl); drop_chain->list= NULL; my_error(ER_DDL_LOG_ERROR, MYF(0)); @@ -6607,361 +6626,67 @@ error: static inline bool write_log_drop_shadow_frm(ALTER_PARTITION_PARAM_TYPE *lpt) { - return write_log_drop_frm(lpt, lpt->part_info); -} - - -/* - Log renaming of shadow frm to real frm name and dropping of old frm - SYNOPSIS - write_log_rename_frm() - lpt Struct containing parameters - RETURN VALUES - TRUE Error - FALSE Success - DESCRIPTION - Prepare an entry to ensure that we complete the renaming of the frm - file if failure occurs in the middle of the rename process. -*/ - -static bool write_log_rename_frm(ALTER_PARTITION_PARAM_TYPE *lpt) -{ - partition_info *part_info= lpt->part_info; - DDL_LOG_MEMORY_ENTRY *log_entry; - DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->execute_entry; - char path[FN_REFLEN + 1]; - char shadow_path[FN_REFLEN + 1]; - DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->list; - DBUG_ENTER("write_log_rename_frm"); - - part_info->list= NULL; - build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); - build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt); - mysql_mutex_lock(&LOCK_gdl); - if (write_log_replace_frm(lpt, 0UL, shadow_path, path)) - goto error; - log_entry= part_info->list; - part_info->main_entry= log_entry; - if (ddl_log_write_execute_entry(log_entry->entry_pos, 0, - &exec_log_entry)) - goto error; - release_part_info_log_entries(old_first_log_entry); - mysql_mutex_unlock(&LOCK_gdl); - DBUG_RETURN(FALSE); - -error: - release_part_info_log_entries(part_info->list); - mysql_mutex_unlock(&LOCK_gdl); - part_info->list= old_first_log_entry; - part_info->main_entry= NULL; - my_error(ER_DDL_LOG_ERROR, MYF(0)); - DBUG_RETURN(TRUE); -} - - -/* - Write the log entries to ensure that the drop partition command is completed - even in the presence of a crash. - - SYNOPSIS - write_log_drop_partition() - lpt Struct containing parameters - RETURN VALUES - TRUE Error - FALSE Success - DESCRIPTION - Prepare entries to the ddl log indicating all partitions to drop and to - install the shadow frm file and remove the old frm file. -*/ - -static bool write_log_drop_partition(ALTER_PARTITION_PARAM_TYPE *lpt) -{ - partition_info *part_info= lpt->part_info; - DDL_LOG_MEMORY_ENTRY *log_entry; - DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->execute_entry; - char tmp_path[FN_REFLEN + 1]; - char path[FN_REFLEN + 1]; - uint next_entry= 0; - DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->list; - DBUG_ENTER("write_log_drop_partition"); - - part_info->list= NULL; - build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); - build_table_shadow_filename(tmp_path, sizeof(tmp_path) - 1, lpt); - mysql_mutex_lock(&LOCK_gdl); - if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path, - FALSE)) - goto error; - if (write_log_replace_frm(lpt, next_entry, (const char*)tmp_path, - (const char*)path)) - goto error; - log_entry= part_info->list; - part_info->main_entry= log_entry; - if (ddl_log_write_execute_entry(log_entry->entry_pos, 0, - &exec_log_entry)) - goto error; - release_part_info_log_entries(old_first_log_entry); - mysql_mutex_unlock(&LOCK_gdl); - DBUG_RETURN(FALSE); - -error: - release_part_info_log_entries(part_info->list); - mysql_mutex_unlock(&LOCK_gdl); - part_info->list= old_first_log_entry; - part_info->main_entry= NULL; - my_error(ER_DDL_LOG_ERROR, MYF(0)); - DBUG_RETURN(TRUE); -} - - -static bool write_log_convert_partition(ALTER_PARTITION_PARAM_TYPE *lpt) -{ - partition_info *part_info= lpt->part_info; - char tmp_path[FN_REFLEN + 1]; - char path[FN_REFLEN + 1]; - uint next_entry= part_info->list ? part_info->list->entry_pos : 0; - - build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); - build_table_shadow_filename(tmp_path, sizeof(tmp_path) - 1, lpt); - - mysql_mutex_lock(&LOCK_gdl); - - if (write_log_convert_partition(lpt, &next_entry, (const char*)path)) - goto error; - DBUG_ASSERT(next_entry == part_info->list->entry_pos); - if (ddl_log_write_execute_entry(part_info->list->entry_pos, 0, - &part_info->execute_entry)) - goto error; - mysql_mutex_unlock(&LOCK_gdl); - return false; - -error: - mysql_mutex_unlock(&LOCK_gdl); - part_info->main_entry= NULL; - my_error(ER_DDL_LOG_ERROR, MYF(0)); - return true; -} - - -/* - Write the log entries to ensure that the add partition command is not - executed at all if a crash before it has completed - - SYNOPSIS - write_log_add_change_partition() - lpt Struct containing parameters - RETURN VALUES - TRUE Error - FALSE Success - DESCRIPTION - Prepare entries to the ddl log indicating all partitions to drop and to - remove the shadow frm file. - We always inject entries backwards in the list in the ddl log since we - don't know the entry position until we have written it. -*/ - -static bool write_log_add_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt) -{ - partition_info *part_info= lpt->part_info; - DDL_LOG_MEMORY_ENTRY *log_entry; - char tmp_path[FN_REFLEN + 1]; - char path[FN_REFLEN + 1]; - uint next_entry= 0; - DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->list; - /* write_log_drop_shadow_frm(lpt) must have been run first */ - DBUG_ASSERT(old_first_log_entry); - DBUG_ENTER("write_log_add_change_partition"); - - build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); - build_table_shadow_filename(tmp_path, sizeof(tmp_path) - 1, lpt); - mysql_mutex_lock(&LOCK_gdl); - - /* Relink the previous drop shadow frm entry */ - if (old_first_log_entry) - next_entry= old_first_log_entry->entry_pos; - if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path, - FALSE)) - goto error; - log_entry= part_info->list; - - if (ddl_log_write_execute_entry(log_entry->entry_pos, 0, - &part_info->execute_entry)) - goto error; - mysql_mutex_unlock(&LOCK_gdl); - DBUG_RETURN(FALSE); - -error: - release_part_info_log_entries(part_info->list); - mysql_mutex_unlock(&LOCK_gdl); - part_info->list= old_first_log_entry; - my_error(ER_DDL_LOG_ERROR, MYF(0)); - DBUG_RETURN(TRUE); -} - - -/* - Write description of how to complete the operation after first phase of - change partitions. - - SYNOPSIS - write_log_final_change_partition() - lpt Struct containing parameters - RETURN VALUES - TRUE Error - FALSE Success - DESCRIPTION - We will write log entries that specify to - 1) Install the shadow frm file. - 2) Remove all partitions reorganized. (To be able to reorganize a partition - to the same name. Like in REORGANIZE p0 INTO (p0, p1), - so that the later rename from the new p0-temporary name to p0 don't - fail because the partition already exists. - 3) Rename others to reflect the new naming scheme. - - Note that it is written in the ddl log in reverse. -*/ - -static bool write_log_final_change_partition(ALTER_PARTITION_PARAM_TYPE *lpt) -{ - partition_info *part_info= lpt->part_info; - DDL_LOG_MEMORY_ENTRY *log_entry; - DDL_LOG_MEMORY_ENTRY *exec_log_entry= part_info->execute_entry; - char path[FN_REFLEN + 1]; - char shadow_path[FN_REFLEN + 1]; - DDL_LOG_MEMORY_ENTRY *old_first_log_entry= part_info->list; - uint next_entry= 0; - DBUG_ENTER("write_log_final_change_partition"); - - /* - Do not link any previous log entry. - Replace the revert operations with forced retry operations. - */ - part_info->list= NULL; - build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); - build_table_shadow_filename(shadow_path, sizeof(shadow_path) - 1, lpt); - mysql_mutex_lock(&LOCK_gdl); - if (write_log_changed_partitions(lpt, &next_entry, (const char*)path)) - goto error; - if (write_log_dropped_partitions(lpt, &next_entry, (const char*)path, - lpt->alter_info->partition_flags & - ALTER_PARTITION_REORGANIZE)) - goto error; - if (write_log_replace_frm(lpt, next_entry, shadow_path, path)) - goto error; - log_entry= part_info->list; - part_info->main_entry= log_entry; - /* Overwrite the revert execute log entry with this retry execute entry */ - if (ddl_log_write_execute_entry(log_entry->entry_pos, 0, - &exec_log_entry)) - goto error; - release_part_info_log_entries(old_first_log_entry); - mysql_mutex_unlock(&LOCK_gdl); - DBUG_RETURN(FALSE); - -error: - release_part_info_log_entries(part_info->list); - mysql_mutex_unlock(&LOCK_gdl); - part_info->list= old_first_log_entry; - part_info->main_entry= NULL; - my_error(ER_DDL_LOG_ERROR, MYF(0)); - DBUG_RETURN(TRUE); -} - - -/* - Remove entry from ddl log and release resources for others to use - - SYNOPSIS - write_log_completed() - lpt Struct containing parameters - RETURN VALUES - TRUE Error - FALSE Success -*/ - -/* - TODO: Partitioning atomic DDL refactoring: this should be replaced with - ddl_log_complete(). -*/ -static void write_log_completed(ALTER_PARTITION_PARAM_TYPE *lpt, - bool dont_crash) -{ - partition_info *part_info= lpt->part_info; - DDL_LOG_MEMORY_ENTRY *log_entry= part_info->execute_entry; - DBUG_ENTER("write_log_completed"); - - DBUG_ASSERT(log_entry); - mysql_mutex_lock(&LOCK_gdl); - if (ddl_log_disable_execute_entry(&log_entry)) - { - /* - Failed to write, Bad... - We have completed the operation but have log records to REMOVE - stuff that shouldn't be removed. What clever things could one do - here? An error output was written to the error output by the - above method so we don't do anything here. - */ - ; - } - release_part_info_log_entries(part_info->list); - release_part_info_log_entries(part_info->execute_entry); - mysql_mutex_unlock(&LOCK_gdl); - part_info->execute_entry= NULL; - part_info->list= NULL; - DBUG_VOID_RETURN; + bool res= write_log_drop_frm(lpt, &lpt->rollback_chain, false); + if (!res) + lpt->drop_shadow_frm= lpt->rollback_chain.main_entry; + return res; } - -/* - Release all log entries - SYNOPSIS - release_log_entries() - part_info Partition info struct - RETURN VALUES - NONE -*/ - -/* - TODO: Partitioning atomic DDL refactoring: this should be replaced with - ddl_log_release_entries(). -*/ -static void release_log_entries(partition_info *part_info) +static inline +bool write_log_drop_backup_frm(ALTER_PARTITION_PARAM_TYPE *lpt) { - mysql_mutex_lock(&LOCK_gdl); - release_part_info_log_entries(part_info->list); - release_part_info_log_entries(part_info->execute_entry); - mysql_mutex_unlock(&LOCK_gdl); - part_info->list= NULL; - part_info->execute_entry= NULL; + return write_log_drop_frm(lpt, &lpt->cleanup_chain, true); } /* - Final part of partition changes to handle things when under - LOCK TABLES. + Close tables, prepare for reopening under LOCK TABLES. SYNPOSIS alter_partition_lock_handling() lpt Struct carrying parameters - RETURN VALUES - true on error */ -static bool alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt) +static void alter_partition_lock_handling(ALTER_PARTITION_PARAM_TYPE *lpt) { THD *thd= lpt->thd; if (lpt->table) { - /* - Remove all instances of the table and its locks and other resources. - */ - close_all_tables_for_name(thd, lpt->table->s, HA_EXTRA_NOT_USED, NULL); + TABLE *table= lpt->table; + if (!thd->mdl_context.is_lock_owner(MDL_key::TABLE, lpt->db.str, + lpt->table_name.str, + MDL_EXCLUSIVE) && + wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) + { + /* + Did not succeed in getting exclusive access to the table. + + Since we have altered a cached table object (and its part_info) we need + at least to remove this instance so it will not be reused. + + Temporarily remove it from the locked table list, so that it will get + reopened. + + Note: tested by partition_special_myisam partition_special_innodb + */ + thd->locked_tables_list.unlink_from_list(thd, + table->pos_in_locked_tables, + false); + /* + Make sure that the table is unlocked, closed and removed from + the table cache. + */ + mysql_lock_remove(thd, thd->lock, table); + close_thread_table(thd, &thd->open_tables); + lpt->table_list->table= NULL; + } + else + { + /* Ensure the share is destroyed and reopened. */ + close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL); + } } lpt->table= 0; lpt->table_list->table= 0; - if (thd->locked_tables_mode) - return thd->locked_tables_list.reopen_tables(thd, false); - - return false; } @@ -6989,197 +6714,6 @@ static int alter_close_table(ALTER_PARTITION_PARAM_TYPE *lpt) /** - Handle errors for ALTER TABLE for partitioning. - - @param lpt Struct carrying parameters - @param action_completed The action must be completed, NOT reverted - @param drop_partition Partitions has not been dropped yet - @param frm_install The shadow frm-file has not yet been installed - @param close_table Table is still open, close it before reverting -*/ - -/* - TODO: Partitioning atomic DDL refactoring: this should be replaced with - correct combination of ddl_log_revert() / ddl_log_complete() -*/ -static void handle_alter_part_error(ALTER_PARTITION_PARAM_TYPE *lpt, - bool action_completed, - bool drop_partition, - bool frm_install, - bool reopen) -{ - THD *thd= lpt->thd; - partition_info *part_info= lpt->part_info->get_clone(thd); - /* TABLE is going to be released, we should not access old part_info anymore */ - lpt->part_info= part_info; - TABLE *table= lpt->table; - DBUG_ENTER("handle_alter_part_error"); - DBUG_ASSERT(table->needs_reopen()); - - /* - All instances of this table needs to be closed. - Better to do that here, than leave the cleaning up to others. - Acquire EXCLUSIVE mdl lock if not already acquired. - */ - if (!thd->mdl_context.is_lock_owner(MDL_key::TABLE, lpt->db.str, - lpt->table_name.str, - MDL_EXCLUSIVE) && - wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) - { - /* - Did not succeed in getting exclusive access to the table. - - Since we have altered a cached table object (and its part_info) we need - at least to remove this instance so it will not be reused. - - Temporarily remove it from the locked table list, so that it will get - reopened. - */ - thd->locked_tables_list.unlink_from_list(thd, - table->pos_in_locked_tables, - false); - /* - Make sure that the table is unlocked, closed and removed from - the table cache. - */ - mysql_lock_remove(thd, thd->lock, table); - close_thread_table(thd, &thd->open_tables); - lpt->table_list->table= NULL; - } - else - { - /* Ensure the share is destroyed and reopened. */ - close_all_tables_for_name(thd, table->s, HA_EXTRA_NOT_USED, NULL); - } - - if (!reopen) - DBUG_VOID_RETURN; - - if (part_info->list && - ddl_log_execute_entry(thd, part_info->list->entry_pos)) - { - /* - We couldn't recover from error, most likely manual interaction - is required. - */ - write_log_completed(lpt, FALSE); - release_log_entries(part_info); - if (!action_completed) - { - if (drop_partition) - { - /* Table is still ok, but we left a shadow frm file behind. */ - push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 1, - "Operation was unsuccessful, table is still " - "intact, but it is possible that a shadow frm " - "file was left behind"); - } - else - { - push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 1, - "Operation was unsuccessful, table is still " - "intact, but it is possible that a shadow frm " - "file was left behind. " - "It is also possible that temporary partitions " - "are left behind, these could be empty or more " - "or less filled with records"); - } - } - else - { - if (frm_install) - { - /* - Failed during install of shadow frm file, table isn't intact - and dropped partitions are still there - */ - push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 1, - "Failed during alter of partitions, table is no " - "longer intact. " - "The frm file is in an unknown state, and a " - "backup is required."); - } - else if (drop_partition) - { - /* - Table is ok, we have switched to new table but left dropped - partitions still in their places. We remove the log records and - ask the user to perform the action manually. We remove the log - records and ask the user to perform the action manually. - */ - push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 1, - "Failed during drop of partitions, table is " - "intact. " - "Manual drop of remaining partitions is required"); - } - else - { - /* - We failed during renaming of partitions. The table is most - certainly in a very bad state so we give user warning and disable - the table by writing an ancient frm version into it. - */ - push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 1, - "Failed during renaming of partitions. We are now " - "in a position where table is not reusable " - "Table is disabled by writing ancient frm file " - "version into it"); - } - } - } - else - { - release_log_entries(part_info); - if (!action_completed) - { - /* - We hit an error before things were completed but managed - to recover from the error. An error occurred and we have - restored things to original so no need for further action. - */ - ; - } - else - { - /* - We hit an error after we had completed most of the operation - and were successful in a second attempt so the operation - actually is successful now. We need to issue a warning that - even though we reported an error the operation was successfully - completed. - */ - push_warning(thd, Sql_condition::WARN_LEVEL_WARN, 1, - "Operation was successfully completed by failure " - "handling, after failure of normal operation"); - } - } - - if (thd->locked_tables_mode) - { - Diagnostics_area *stmt_da= NULL; - Diagnostics_area tmp_stmt_da(true); - - if (unlikely(thd->is_error())) - { - /* reopen might fail if we have a previous error, use a temporary da. */ - stmt_da= thd->get_stmt_da(); - thd->set_stmt_da(&tmp_stmt_da); - } - - /* NB: error status is not needed here, the statement fails with - the original error. */ - if (unlikely(thd->locked_tables_list.reopen_tables(thd, false))) - sql_print_warning("We failed to reacquire LOCKs in ALTER TABLE"); - - if (stmt_da) - thd->set_stmt_da(stmt_da); - } - - DBUG_VOID_RETURN; -} - - -/** Downgrade an exclusive MDL lock if under LOCK TABLE. If we don't downgrade the lock, it will not be downgraded or released @@ -7277,17 +6811,33 @@ static bool check_table_data(ALTER_PARTITION_PARAM_TYPE *lpt) } +bool alter_partition_binlog(ALTER_PARTITION_PARAM_TYPE *lpt) +{ + bool res; + THD *thd= lpt->thd; + if (thd->lex->no_write_to_binlog) + return false; + + thd->binlog_xid= thd->query_id; + res= ERROR_INJECT("alter_partition_binlog_1") || + ddl_log_update_xid(&lpt->rollback_chain, thd->binlog_xid); + if (!res) + res= ERROR_INJECT("alter_partition_binlog_2") || + write_bin_log(thd, false, thd->query(), thd->query_length()); + thd->binlog_xid= 0; + return res; +} + + /** Actually perform the change requested by ALTER TABLE of partitions previously prepared. - @param thd Thread object @param table Original table object with new part_info @param alter_info ALTER TABLE info + @param alter_ctx ALTER TABLE context @param create_info Create info for CREATE TABLE @param table_list List of the table involved - @param db Database name of new table - @param table_name Table name of new table @return Operation status @retval TRUE Error @@ -7304,25 +6854,10 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, HA_CREATE_INFO *create_info, TABLE_LIST *table_list) { - /* - TODO: Partitioning atomic DDL refactoring. - - DDL log chain state is stored in partition_info: - - struct st_ddl_log_memory_entry *first_log_entry; - struct st_ddl_log_memory_entry *exec_log_entry; - struct st_ddl_log_memory_entry *frm_log_entry; - - Make it stored and used in DDL_LOG_STATE like it was done in MDEV-17567. - This requires mysql_write_frm() refactoring (see comment there). - */ - /* Set-up struct used to write frm files */ partition_info *part_info; ALTER_PARTITION_PARAM_TYPE lpt_obj; ALTER_PARTITION_PARAM_TYPE *lpt= &lpt_obj; - bool action_completed= FALSE; - bool frm_install= FALSE; MDL_ticket *mdl_ticket= table->mdl_ticket; /* option_bits is used to mark if we should log the query with IF EXISTS */ ulonglong save_option_bits= thd->variables.option_bits; @@ -7338,163 +6873,51 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, lpt->create_info= create_info; lpt->db_options= create_info->table_options_with_row_type(); lpt->table= table; - lpt->key_info_buffer= 0; - lpt->key_count= 0; lpt->db= alter_ctx->db; lpt->table_name= alter_ctx->table_name; lpt->org_tabledef_version= table->s->tabledef_version; - lpt->copied= 0; - lpt->deleted= 0; - lpt->pack_frm_data= NULL; - lpt->pack_frm_len= 0; + + DDL_LOG_STATE *rollback_chain= &lpt->rollback_chain; + DDL_LOG_STATE *cleanup_chain= &lpt->cleanup_chain; /* Add IF EXISTS to binlog if shared table */ if (table->file->partition_ht()->flags & HTON_TABLE_MAY_NOT_EXIST_ON_SLAVE) thd->variables.option_bits|= OPTION_IF_EXISTS; - if (table->file->alter_table_flags(alter_info->flags) & - HA_PARTITION_ONE_PHASE) - { - /* - In the case where the engine supports one phase online partition - changes it is not necessary to have any exclusive locks. The - correctness is upheld instead by transactions being aborted if they - access the table after its partition definition has changed (if they - are still using the old partition definition). - - The handler is in this case responsible to ensure that all users - start using the new frm file after it has changed. To implement - one phase it is necessary for the handler to have the master copy - of the frm file and use discovery mechanisms to renew it. Thus - write frm will write the frm, pack the new frm and finally - the frm is deleted and the discovery mechanisms will either restore - back to the old or installing the new after the change is activated. - - Thus all open tables will be discovered that they are old, if not - earlier as soon as they try an operation using the old table. One - should ensure that this is checked already when opening a table, - even if it is found in the cache of open tables. - - change_partitions will perform all operations and it is the duty of - the handler to ensure that the frm files in the system gets updated - in synch with the changes made and if an error occurs that a proper - error handling is done. - - If the MySQL Server crashes at this moment but the handler succeeds - in performing the change then the binlog is not written for the - change. There is no way to solve this as long as the binlog is not - transactional and even then it is hard to solve it completely. - - The first approach here was to downgrade locks. Now a different approach - is decided upon. The idea is that the handler will have access to the - Alter_info when store_lock arrives with TL_WRITE_ALLOW_READ. So if the - handler knows that this functionality can be handled with a lower lock - level it will set the lock level to TL_WRITE_ALLOW_WRITE immediately. - Thus the need to downgrade the lock disappears. - 1) Write the new frm, pack it and then delete it - 2) Perform the change within the handler - */ - if (mysql_write_frm(lpt, WFRM_WRITE_SHADOW) || - mysql_change_partitions(lpt)) - { - goto err; - } - } - else if (alter_info->partition_flags & ALTER_PARTITION_DROP) + if (alter_info->partition_flags & ALTER_PARTITION_DROP) { + Alter_partition_logger action_drop(lpt); + /* - Now after all checks and setting state on dropped partitions we can - start the actual dropping of the partitions. - - Drop partition is actually two things happening. The first is that - a lot of records are deleted. The second is that the behaviour of - subsequent updates and writes and deletes will change. The delete - part can be handled without any particular high lock level by - transactional engines whereas non-transactional engines need to - ensure that this change is done with an exclusive lock on the table. - The second part, the change of partitioning does however require - an exclusive lock to install the new partitioning as one atomic - operation. If this is not the case, it is possible for two - transactions to see the change in a different order than their - serialisation order. Thus we need an exclusive lock for both - transactional and non-transactional engines. - - For LIST partitions it could be possible to avoid the exclusive lock - (and for RANGE partitions if they didn't rearrange range definitions - after a DROP PARTITION) if one ensured that failed accesses to the - dropped partitions was aborted for sure (thus only possible for - transactional engines). - - 0) Write an entry that removes the shadow frm file if crash occurs - 1) Write the new frm file as a shadow frm - 2) Get an exclusive metadata lock on the table (waits for all active - transactions using this table). This ensures that we - can release all other locks on the table and since no one can open - the table, there can be no new threads accessing the table. They - will be hanging on this exclusive lock. - 3) Write the ddl log to ensure that the operation is completed - even in the presence of a MySQL Server crash (the log is executed - before any other threads are started, so there are no locking issues). - 4) Close the table that have already been opened but didn't stumble on - the abort locked previously. This is done as part of the - alter_close_table call. - 5) Old place for binary logging - 6) Install the previously written shadow frm file - 7) Prepare handlers for drop of partitions - 8) Drop the partitions - 9) Remove entries from ddl log - 10) Reopen table if under lock tables - 11) Write the bin log - Unfortunately the writing of the binlog is not synchronised with - other logging activities. So no matter in which order the binlog - is written compared to other activities there will always be cases - where crashes make strange things occur. In this placement it can - happen that the ALTER TABLE DROP PARTITION gets performed in the - master but not in the slaves if we have a crash, after writing the - ddl log but before writing the binlog. A solution to this would - require writing the statement first in the ddl log and then - when recovering from the crash read the binlog and insert it into - the binlog if not written already. - 12) Complete query - - We insert Error injections at all places where it could be interesting - to test if recovery is properly done. + part_info chain contains roll forward actions, + cleanup_chain drops shadow frm. + + If cleanup_chain is active part_info chain is not executed. */ + if (write_log_drop_shadow_frm(lpt) || ERROR_INJECT("drop_partition_1") || mysql_write_frm(lpt, WFRM_WRITE_SHADOW) || ERROR_INJECT("drop_partition_2") || wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) || ERROR_INJECT("drop_partition_3") || - write_log_drop_partition(lpt) || - (action_completed= TRUE, FALSE) || - ERROR_INJECT("drop_partition_4") || alter_close_table(lpt) || + ERROR_INJECT("drop_partition_4") || + action_drop.rename_parts() || ERROR_INJECT("drop_partition_5") || + write_log_drop_backup_frm(lpt) || ERROR_INJECT("drop_partition_6") || - (frm_install= TRUE, FALSE) || - mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || - alter_partition_log_backup(lpt) || - (frm_install= FALSE, FALSE) || + mysql_write_frm(lpt, WFRM_BACKUP_ORIGINAL) || ERROR_INJECT("drop_partition_7") || - mysql_drop_partitions(lpt) || + mysql_write_frm(lpt, WFRM_INSTALL_SHADOW|WFRM_BACKUP_ORIGINAL) || ERROR_INJECT("drop_partition_8") || - (write_log_completed(lpt, FALSE), FALSE) || - ((!thd->lex->no_write_to_binlog) && - (write_bin_log(thd, FALSE, - thd->query(), thd->query_length()), FALSE)) || - ERROR_INJECT("drop_partition_9")) - { - handle_alter_part_error(lpt, action_completed, TRUE, frm_install, true); - goto err; - } - if (alter_partition_lock_handling(lpt)) - goto err; - } + alter_partition_log_backup(lpt) || + alter_partition_binlog(lpt)) + goto fail; + } /* DROP */ else if (alter_info->partition_flags & ALTER_PARTITION_CONVERT_OUT) { - DDL_LOG_STATE chain_drop_backup; - bzero(&chain_drop_backup, sizeof(chain_drop_backup)); + Alter_partition_change action_conv_out(lpt); if (mysql_write_frm(lpt, WFRM_WRITE_CONVERTED_TO) || ERROR_INJECT("convert_partition_1") || @@ -7504,43 +6927,25 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, ERROR_INJECT("convert_partition_3") || wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) || ERROR_INJECT("convert_partition_4") || - write_log_convert_partition(lpt) || - ERROR_INJECT("convert_partition_5") || alter_close_table(lpt) || + ERROR_INJECT("convert_partition_5") || + action_conv_out.convert_out() || ERROR_INJECT("convert_partition_6") || - alter_partition_convert_out(lpt) || + write_log_drop_backup_frm(lpt) || ERROR_INJECT("convert_partition_7") || - write_log_drop_frm(lpt, &chain_drop_backup) || + mysql_write_frm(lpt, WFRM_BACKUP_ORIGINAL) || + ERROR_INJECT("convert_partition_8") || mysql_write_frm(lpt, WFRM_INSTALL_SHADOW|WFRM_BACKUP_ORIGINAL) || + ERROR_INJECT("convert_partition_9") || alter_partition_log_backup(lpt) || - ERROR_INJECT("convert_partition_8") || - ((!thd->lex->no_write_to_binlog) && - ((thd->binlog_xid= thd->query_id), - ddl_log_update_xid(lpt->part_info, thd->binlog_xid), - write_bin_log(thd, false, thd->query(), thd->query_length()), - (thd->binlog_xid= 0))) || - ERROR_INJECT("convert_partition_9")) - { - DDL_LOG_STATE main_state= *lpt->part_info; - handle_alter_part_error(lpt, true, true, false, false); - ddl_log_complete(&chain_drop_backup); - (void) ddl_log_revert(thd, &main_state); - if (thd->locked_tables_mode) - thd->locked_tables_list.reopen_tables(thd, false); - goto err; - } - ddl_log_complete(lpt->part_info); - ERROR_INJECT("convert_partition_10"); - (void) ddl_log_revert(thd, &chain_drop_backup); - if (alter_partition_lock_handling(lpt) || - ERROR_INJECT("convert_partition_11")) - goto err; - } + alter_partition_binlog(lpt)) + goto fail; + } /* CONVERT OUT */ else if ((alter_info->partition_flags & ALTER_PARTITION_CONVERT_IN)) { - DDL_LOG_STATE chain_drop_backup; - bzero(&chain_drop_backup, sizeof(chain_drop_backup)); TABLE *table_from= table_list->next_local->table; + TABLE_LIST *tl_from= table_from->pos_in_locked_tables; + Alter_partition_change action_conv_in(lpt); if (wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) || wait_while_table_is_used(thd, table_from, HA_EXTRA_PREPARE_FOR_RENAME) || @@ -7550,212 +6955,113 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, check_table_data(lpt)) goto err; + close_all_tables_for_name(lpt->thd, table_from->s, + HA_EXTRA_PREPARE_FOR_RENAME, NULL); + if (tl_from) + tl_from->table= NULL; + if (write_log_drop_shadow_frm(lpt) || ERROR_INJECT("convert_partition_3") || mysql_write_frm(lpt, WFRM_WRITE_SHADOW) || ERROR_INJECT("convert_partition_4") || alter_close_table(lpt) || ERROR_INJECT("convert_partition_5") || - write_log_convert_partition(lpt) || + action_conv_in.convert_in() || ERROR_INJECT("convert_partition_6") || - alter_partition_convert_in(lpt) || + write_log_drop_backup_frm(lpt) || ERROR_INJECT("convert_partition_7") || - (frm_install= true, false) || - write_log_drop_frm(lpt, &chain_drop_backup) || - mysql_write_frm(lpt, WFRM_INSTALL_SHADOW|WFRM_BACKUP_ORIGINAL) || - log_partition_alter_to_ddl_log(lpt) || - (frm_install= false, false) || + mysql_write_frm(lpt, WFRM_BACKUP_ORIGINAL) || ERROR_INJECT("convert_partition_8") || - ((!thd->lex->no_write_to_binlog) && - ((thd->binlog_xid= thd->query_id), - ddl_log_update_xid(lpt->part_info, thd->binlog_xid), - write_bin_log(thd, false, thd->query(), thd->query_length()), - (thd->binlog_xid= 0))) || - ERROR_INJECT("convert_partition_9")) - { - DDL_LOG_STATE main_state= *lpt->part_info; - handle_alter_part_error(lpt, true, true, false, false); - ddl_log_complete(&chain_drop_backup); - (void) ddl_log_revert(thd, &main_state); - if (thd->locked_tables_mode) - thd->locked_tables_list.reopen_tables(thd, false); - goto err; + mysql_write_frm(lpt, WFRM_INSTALL_SHADOW|WFRM_BACKUP_ORIGINAL) || + ERROR_INJECT("convert_partition_9") || + alter_partition_log_backup(lpt) || + alter_partition_binlog(lpt)) + { + if (tl_from) + thd->locked_tables_list.add_back_last_deleted_lock(tl_from); + goto fail; } - ddl_log_complete(lpt->part_info); - ERROR_INJECT("convert_partition_10"); - (void) ddl_log_revert(thd, &chain_drop_backup); - if (alter_partition_lock_handling(lpt) || - ERROR_INJECT("convert_partition_11")) - goto err; - } + } /* CONVERT IN */ else if ((alter_info->partition_flags & ALTER_PARTITION_ADD) && (part_info->part_type == RANGE_PARTITION || part_info->part_type == LIST_PARTITION)) { DBUG_ASSERT(!(alter_info->partition_flags & ALTER_PARTITION_CONVERT_IN)); - /* - ADD RANGE/LIST PARTITIONS - In this case there are no tuples removed and no tuples are added. - Thus the operation is merely adding a new partition. Thus it is - necessary to perform the change as an atomic operation. Otherwise - someone reading without seeing the new partition could potentially - miss updates made by a transaction serialised before it that are - inserted into the new partition. - - 0) Write an entry that removes the shadow frm file if crash occurs - 1) Write the new frm file as a shadow frm file - 2) Get an exclusive metadata lock on the table (waits for all active - transactions using this table). This ensures that we - can release all other locks on the table and since no one can open - the table, there can be no new threads accessing the table. They - will be hanging on this exclusive lock. - 3) Write an entry to remove the new parttions if crash occurs - 4) Add the new partitions. - 5) Close all instances of the table and remove them from the table cache. - 6) Old place for write binlog - 7) Now the change is completed except for the installation of the - new frm file. We thus write an action in the log to change to - the shadow frm file - 8) Install the new frm file of the table where the partitions are - added to the table. - 9) Remove entries from ddl log - 10)Reopen tables if under lock tables - 11)Write to binlog - 12)Complete query - */ + Alter_partition_add action_add(lpt); + if (write_log_drop_shadow_frm(lpt) || ERROR_INJECT("add_partition_1") || mysql_write_frm(lpt, WFRM_WRITE_SHADOW) || ERROR_INJECT("add_partition_2") || wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) || ERROR_INJECT("add_partition_3") || - write_log_add_change_partition(lpt) || + action_add.add_parts() || ERROR_INJECT("add_partition_4") || - mysql_change_partitions(lpt) || - ERROR_INJECT("add_partition_5") || alter_close_table(lpt) || + ERROR_INJECT("add_partition_5") || + write_log_drop_backup_frm(lpt) || ERROR_INJECT("add_partition_6") || + mysql_write_frm(lpt, WFRM_BACKUP_ORIGINAL) || ERROR_INJECT("add_partition_7") || - write_log_rename_frm(lpt) || - (action_completed= TRUE, FALSE) || + mysql_write_frm(lpt, WFRM_INSTALL_SHADOW|WFRM_BACKUP_ORIGINAL) || ERROR_INJECT("add_partition_8") || - (frm_install= TRUE, FALSE) || - mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || alter_partition_log_backup(lpt) || - (frm_install= FALSE, FALSE) || - ERROR_INJECT("add_partition_9") || - (write_log_completed(lpt, FALSE), FALSE) || - ((!thd->lex->no_write_to_binlog) && - (write_bin_log(thd, FALSE, - thd->query(), thd->query_length()), FALSE)) || - ERROR_INJECT("add_partition_10")) - { - handle_alter_part_error(lpt, action_completed, FALSE, frm_install, true); - goto err; - } - if (alter_partition_lock_handling(lpt)) - goto err; - } + alter_partition_binlog(lpt)) + goto fail; + } /* ADD */ else { - /* - ADD HASH PARTITION/ - COALESCE PARTITION/ - REBUILD PARTITION/ - REORGANIZE PARTITION - - In this case all records are still around after the change although - possibly organised into new partitions, thus by ensuring that all - updates go to both the old and the new partitioning scheme we can - actually perform this operation lock-free. The only exception to - this is when REORGANIZE PARTITION adds/drops ranges. In this case - there needs to be an exclusive lock during the time when the range - changes occur. - This is only possible if the handler can ensure double-write for a - period. The double write will ensure that it doesn't matter where the - data is read from since both places are updated for writes. If such - double writing is not performed then it is necessary to perform the - change with the usual exclusive lock. With double writes it is even - possible to perform writes in parallel with the reorganisation of - partitions. - - Without double write procedure we get the following procedure. - The only difference with using double write is that we can downgrade - the lock to TL_WRITE_ALLOW_WRITE. Double write in this case only - double writes from old to new. If we had double writing in both - directions we could perform the change completely without exclusive - lock for HASH partitions. - Handlers that perform double writing during the copy phase can actually - use a lower lock level. This can be handled inside store_lock in the - respective handler. - - 0) Write an entry that removes the shadow frm file if crash occurs. - 1) Write the shadow frm file of new partitioning. - 2) Log such that temporary partitions added in change phase are - removed in a crash situation. - 3) Add the new partitions. - Copy from the reorganised partitions to the new partitions. - 4) Get an exclusive metadata lock on the table (waits for all active - transactions using this table). This ensures that we - can release all other locks on the table and since no one can open - the table, there can be no new threads accessing the table. They - will be hanging on this exclusive lock. - 5) Close the table. - 6) Log that operation is completed and log all complete actions - needed to complete operation from here. - 7) Old place for write bin log. - 8) Prepare handlers for rename and delete of partitions. - 9) Rename and drop the reorged partitions such that they are no - longer used and rename those added to their real new names. - 10) Install the shadow frm file. - 11) Reopen the table if under lock tables. - 12) Write to binlog - 13) Complete query. - */ + /* ADD HASH / COALESCE / REBUILD / REORGANIZE */ + Alter_partition_change action_change(lpt); + if (write_log_drop_shadow_frm(lpt) || ERROR_INJECT("change_partition_1") || mysql_write_frm(lpt, WFRM_WRITE_SHADOW) || ERROR_INJECT("change_partition_2") || - write_log_add_change_partition(lpt) || + wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) || ERROR_INJECT("change_partition_3") || - mysql_change_partitions(lpt) || + action_change.add_parts_and_copy_data(thd) || ERROR_INJECT("change_partition_4") || - wait_while_table_is_used(thd, table, HA_EXTRA_NOT_USED) || - ERROR_INJECT("change_partition_5") || alter_close_table(lpt) || + ERROR_INJECT("change_partition_5") || + action_change.rename_parts() || ERROR_INJECT("change_partition_6") || - write_log_final_change_partition(lpt) || - (action_completed= TRUE, FALSE) || + write_log_drop_backup_frm(lpt) || ERROR_INJECT("change_partition_7") || + mysql_write_frm(lpt, WFRM_BACKUP_ORIGINAL) || ERROR_INJECT("change_partition_8") || - ((frm_install= TRUE), FALSE) || - mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) || - alter_partition_log_backup(lpt) || - (frm_install= FALSE, FALSE) || + mysql_write_frm(lpt, WFRM_INSTALL_SHADOW|WFRM_BACKUP_ORIGINAL) || ERROR_INJECT("change_partition_9") || - mysql_drop_partitions(lpt) || - ERROR_INJECT("change_partition_10") || - mysql_rename_partitions(lpt) || - ERROR_INJECT("change_partition_11") || - (write_log_completed(lpt, FALSE), FALSE) || - ((!thd->lex->no_write_to_binlog) && - (write_bin_log(thd, FALSE, - thd->query(), thd->query_length()), FALSE)) || - ERROR_INJECT("change_partition_12")) - { - handle_alter_part_error(lpt, action_completed, FALSE, frm_install, true); - goto err; - } - if (alter_partition_lock_handling(lpt)) - goto err; - } + alter_partition_log_backup(lpt) || + alter_partition_binlog(lpt)) + goto fail; + } /* ADD HASH / COALESCE / REBUILD / REORGANIZE */ + + CRASH_INJECT("done_partition_1"); + ddl_log_complete(rollback_chain); + CRASH_INJECT("done_partition_2"); + alter_partition_lock_handling(lpt); + (void) ddl_log_revert(thd, cleanup_chain, true); + + if (thd->locked_tables_mode) + (void) thd->locked_tables_list.reopen_tables(thd, false); + thd->variables.option_bits= save_option_bits; downgrade_mdl_if_lock_tables_mode(thd, mdl_ticket, MDL_SHARED_NO_READ_WRITE); - /* - A final step is to write the query to the binlog and send ok to the - user - */ + DBUG_RETURN(fast_end_partition(thd, lpt->copied, lpt->deleted, table_list)); + +fail: + CRASH_INJECT("fail_partition_1"); + ddl_log_complete(cleanup_chain); + CRASH_INJECT("fail_partition_2"); + /* We may fail to drop partitions due to existing locking, so must unlock first */ + alter_partition_lock_handling(lpt); + (void) ddl_log_revert(thd, rollback_chain, true); + + if (thd->locked_tables_mode) + (void) thd->locked_tables_list.reopen_tables(thd, false); + err: thd->variables.option_bits= save_option_bits; downgrade_mdl_if_lock_tables_mode(thd, mdl_ticket, MDL_SHARED_NO_READ_WRITE); @@ -8975,6 +8281,8 @@ static const char *longest_str(const char *s1, const char *s2, DESCRIPTION This method is used to calculate the partition name, service routine to the del_ren_cre_table method. + + TODO: MDEV-28844 Merge create_partition_name() and create_subpartition_name() */ int create_partition_name(char *out, size_t outlen, const char *in1, diff --git a/sql/sql_partition.h b/sql/sql_partition.h index a90eaae0bae..711d7d1c196 100644 --- a/sql/sql_partition.h +++ b/sql/sql_partition.h @@ -23,6 +23,7 @@ #include "sql_list.h" /* List */ #include "table.h" /* TABLE_LIST */ +#include "ddl_log.h" class Alter_info; class Alter_table_ctx; @@ -46,6 +47,7 @@ typedef struct st_key_range key_range; #define NORMAL_PART_NAME 0 #define TEMP_PART_NAME 1 #define RENAMED_PART_NAME 2 +#define SKIP_PART_NAME 255 typedef struct st_lock_param_type { @@ -65,8 +67,16 @@ typedef struct st_lock_param_type uint key_count; uint db_options; size_t pack_frm_len; + DDL_LOG_MEMORY_ENTRY *drop_shadow_frm; // TODO: remove duplicate data: part_info can be accessed via table->part_info partition_info *part_info; + DDL_LOG_STATE rollback_chain; + DDL_LOG_STATE cleanup_chain; + + st_lock_param_type() + { + bzero(this, sizeof(*this)); + } } ALTER_PARTITION_PARAM_TYPE; typedef struct { @@ -285,7 +295,6 @@ bool compare_table_with_partition(THD *thd, TABLE *table, uint part_id); bool partition_key_modified(TABLE *table, const MY_BITMAP *fields); bool write_log_replace_frm(ALTER_PARTITION_PARAM_TYPE *lpt, - uint next_entry, const char *from_path, const char *to_path); diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc index 134a01e4e0e..09dd0c5689b 100644 --- a/sql/sql_partition_admin.cc +++ b/sql/sql_partition_admin.cc @@ -396,7 +396,7 @@ static bool exchange_name_with_ddl_log(THD *thd, */ /* call rename table from table to tmp-name */ DBUG_EXECUTE_IF("exchange_partition_fail_3", - my_error(ER_ERROR_ON_RENAME, MYF(0), name, tmp_name, 0); + my_error(ER_ERROR_ON_RENAME, MYF(0), name, "#TMP", 0); error_set= TRUE; goto err_rename;); DBUG_EXECUTE_IF("exchange_partition_abort_3", DBUG_SUICIDE();); @@ -430,7 +430,7 @@ static bool exchange_name_with_ddl_log(THD *thd, /* call rename table from tmp-nam to partition */ DBUG_EXECUTE_IF("exchange_partition_fail_7", - my_error(ER_ERROR_ON_RENAME, MYF(0), tmp_name, from_name, 0); + my_error(ER_ERROR_ON_RENAME, MYF(0), "#TMP", from_name, 0); error_set= TRUE; goto err_rename;); DBUG_EXECUTE_IF("exchange_partition_abort_7", DBUG_SUICIDE();); @@ -719,13 +719,6 @@ bool Sql_cmd_alter_table_exchange_partition:: temp_file_name, table_hton))) goto err; - /* - Reopen tables under LOCK TABLES. Ignore the return value for now. It's - better to keep master/slave in consistent state. Alternative would be to - try to revert the exchange operation and issue error. - */ - (void) thd->locked_tables_list.reopen_tables(thd, false); - if (force_if_exists) thd->variables.option_bits|= OPTION_IF_EXISTS; @@ -753,6 +746,7 @@ bool Sql_cmd_alter_table_exchange_partition:: err: if (thd->locked_tables_mode) { + (void) thd->locked_tables_list.reopen_tables(thd, false); if (swap_table_mdl_ticket) swap_table_mdl_ticket->downgrade_lock(MDL_SHARED_NO_READ_WRITE); if (part_table_mdl_ticket) @@ -999,53 +993,4 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd) DBUG_RETURN(error); } - -/** - Move a table specified in the CONVERT TABLE <table_name> TO PARTITION ... - to the new partition. - - @param lpt A structure containing parameters regarding to the statement - ALTER TABLE ... TO PARTITION ... - @param part_file_name a file name of the partition being added - - @return false on success, true on error -*/ - -bool alter_partition_convert_in(ALTER_PARTITION_PARAM_TYPE *lpt) -{ - char part_file_name[2*FN_REFLEN+1]; - THD *thd= lpt->thd; - const char *path= lpt->table_list->table->s->path.str; - TABLE_LIST *table_from= lpt->table_list->next_local; - - const char *partition_name= - thd->lex->part_info->curr_part_elem->partition_name; - - if (create_partition_name(part_file_name, sizeof(part_file_name), path, - partition_name, NORMAL_PART_NAME, false)) - return true; - - char from_file_name[FN_REFLEN+1]; - - build_table_filename(from_file_name, sizeof(from_file_name), - table_from->db.str, table_from->table_name.str, "", 0); - - handler *file= get_new_handler(nullptr, thd->mem_root, - table_from->table->file->ht); - if (unlikely(!file)) - return true; - - close_all_tables_for_name(thd, table_from->table->s, - HA_EXTRA_PREPARE_FOR_RENAME, nullptr); - - bool res= file->ha_rename_table(from_file_name, part_file_name); - - if (res) - my_error(ER_ERROR_ON_RENAME, MYF(0), from_file_name, - part_file_name, my_errno); - - delete file; - return res; -} - #endif /* WITH_PARTITION_STORAGE_ENGINE */ diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 658afd57f9b..8acdade6f9a 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -199,7 +199,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent, else { /* Revert the renames of normal tables with the help of the ddl log */ - error|= ddl_log_revert(thd, &ddl_log_state); + error|= ddl_log_revert(thd, &ddl_log_state, false); } err: diff --git a/sql/sql_table.cc b/sql/sql_table.cc index b72f64bc691..f800564c538 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -730,6 +730,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) uint syntax_len; partition_info *part_info= lpt->part_info; #endif + DDL_LOG_STATE *rollback_chain= &lpt->rollback_chain; DBUG_ENTER("mysql_write_frm"); /* @@ -808,7 +809,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) create_info->db_type= work_part_info->default_engine_type; /* NOTE: partitioned temporary tables are not supported. */ DBUG_ASSERT(!create_info->tmp_table()); - if (ddl_log_create_table(part_info, create_info->db_type, &new_path, + if (ddl_log_create_table(rollback_chain, create_info->db_type, &new_path, &alter_ctx->new_db, &alter_ctx->new_name, true) || ERROR_INJECT("create_before_create_frm")) DBUG_RETURN(TRUE); @@ -828,15 +829,20 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) if (unlikely(!frm.str)) DBUG_RETURN(TRUE); + if (ERROR_INJECT("alter_partition_after_create_frm")) + { + my_free((void *) frm.str); + DBUG_RETURN(true); + } + thd->work_part_info= work_part_info; create_info->db_type= db_type; - ERROR_INJECT("alter_partition_after_create_frm"); - error= writefile(frm_name, alter_ctx->new_db.str, alter_ctx->new_name.str, create_info->tmp_table(), frm.str, frm.length); my_free((void *) frm.str); - if (unlikely(error) || ERROR_INJECT("alter_partition_after_write_frm")) + if (unlikely(error) || + ERROR_INJECT("alter_partition_after_write_frm")) { mysql_file_delete(key_file_frm, frm_name, MYF(0)); DBUG_RETURN(TRUE); @@ -844,7 +850,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) DBUG_RETURN(false); } - if (flags & WFRM_BACKUP_ORIGINAL) + if (flags == WFRM_BACKUP_ORIGINAL) { build_table_filename(path, sizeof(path) - 1, lpt->db.str, lpt->table_name.str, "", 0); @@ -853,19 +859,18 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) build_table_shadow_filename(bak_path, sizeof(bak_path) - 1, lpt, true); strxmov(bak_frm_name, bak_path, reg_ext, NullS); - DDL_LOG_MEMORY_ENTRY *main_entry= part_info->main_entry; + DDL_LOG_MEMORY_ENTRY *main_entry= rollback_chain->main_entry; mysql_mutex_lock(&LOCK_gdl); - if (write_log_replace_frm(lpt, part_info->list->entry_pos, - (const char*) bak_path, - (const char*) path) || - ddl_log_write_execute_entry(part_info->list->entry_pos, 0, - &part_info->execute_entry)) + if (ddl_log_rename_frm(&lpt->rollback_chain, + (const char*) bak_path, (const char*) path) || + ddl_log_write_execute_entry(rollback_chain->list->entry_pos, 0, + &rollback_chain->execute_entry)) { mysql_mutex_unlock(&LOCK_gdl); DBUG_RETURN(TRUE); } mysql_mutex_unlock(&LOCK_gdl); - part_info->main_entry= main_entry; + rollback_chain->main_entry= main_entry; if (mysql_file_rename(key_file_frm, frm_name, bak_frm_name, MYF(MY_WME))) DBUG_RETURN(TRUE); if (lpt->table->file->ha_create_partitioning_metadata(bak_path, path, @@ -877,9 +882,6 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) #endif /* !WITH_PARTITION_STORAGE_ENGINE */ if (flags & WFRM_INSTALL_SHADOW) { -#ifdef WITH_PARTITION_STORAGE_ENGINE - partition_info *part_info= lpt->part_info; -#endif /* Build frm file name */ @@ -901,7 +903,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) #ifdef WITH_PARTITION_STORAGE_ENGINE || lpt->table->file->ha_create_partitioning_metadata(path, shadow_path, CHF_DELETE_FLAG) || - ddl_log_increment_phase(part_info->main_entry->entry_pos) || + ddl_log_increment_phase(rollback_chain->main_entry->entry_pos) || (ddl_log_sync(), FALSE) #endif )) @@ -953,8 +955,8 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) err: #ifdef WITH_PARTITION_STORAGE_ENGINE - ddl_log_increment_phase(part_info->main_entry->entry_pos); - part_info->main_entry= NULL; + ddl_log_increment_phase(lpt->drop_shadow_frm->entry_pos); + lpt->drop_shadow_frm= NULL; (void) ddl_log_sync(); #endif ; @@ -4465,7 +4467,7 @@ void HA_CREATE_INFO::finalize_ddl(THD *thd, bool roll_back) debug_crash_here("ddl_log_create_fk_fail"); ddl_log_complete(ddl_log_state_rm); debug_crash_here("ddl_log_create_fk_fail2"); - (void) ddl_log_revert(thd, ddl_log_state_create); + (void) ddl_log_revert(thd, ddl_log_state_create, true); debug_crash_here("ddl_log_create_fk_fail3"); } else @@ -4479,7 +4481,7 @@ void HA_CREATE_INFO::finalize_ddl(THD *thd, bool roll_back) ddl_log_complete(ddl_log_state_create); debug_crash_here("ddl_log_create_log_complete2"); if (is_atomic_replace()) - (void) ddl_log_revert(thd, ddl_log_state_rm); + (void) ddl_log_revert(thd, ddl_log_state_rm, true); else ddl_log_complete(ddl_log_state_rm); debug_crash_here("ddl_log_create_log_complete3"); @@ -11389,7 +11391,7 @@ err_with_mdl: Prepare the transaction for the alter table's copy phase. */ -bool mysql_trans_prepare_alter_copy_data(THD *thd) +int mysql_trans_prepare_alter_copy_data(THD *thd) { DBUG_ENTER("mysql_trans_prepare_alter_copy_data"); /* @@ -11406,9 +11408,9 @@ bool mysql_trans_prepare_alter_copy_data(THD *thd) Commit the copy phase of the alter table. */ -bool mysql_trans_commit_alter_copy_data(THD *thd) +int mysql_trans_commit_alter_copy_data(THD *thd, bool rollback) { - bool error= FALSE; + int error= 0; uint save_unsafe_rollback_flags; DBUG_ENTER("mysql_trans_commit_alter_copy_data"); @@ -11417,19 +11419,29 @@ bool mysql_trans_commit_alter_copy_data(THD *thd) DEBUG_SYNC(thd, "alter_table_copy_trans_commit"); - if (ha_enable_transaction(thd, TRUE)) - DBUG_RETURN(TRUE); + if ((error= ha_enable_transaction(thd, TRUE))) + DBUG_RETURN(error); - /* - Ensure that the new table is saved properly to disk before installing - the new .frm. - And that InnoDB's internal latches are released, to avoid deadlock - when waiting on other instances of the table before rename (Bug#54747). - */ - if (trans_commit_stmt(thd)) - error= TRUE; - if (trans_commit_implicit(thd)) - error= TRUE; + if (!rollback) + { + /* + Ensure that the new table is saved properly to disk before installing + the new .frm. + And that InnoDB's internal latches are released, to avoid deadlock + when waiting on other instances of the table before rename (Bug#54747). + */ + if (trans_commit_stmt(thd)) + error= HA_ERR_COMMIT_ERROR; + if (trans_commit_implicit(thd)) + error= HA_ERR_COMMIT_ERROR; + } + else + { + if (trans_rollback_stmt(thd)) + error= HA_ERR_COMMIT_ERROR; + if (trans_rollback_implicit(thd)) + error= HA_ERR_COMMIT_ERROR; + } thd->transaction->stmt.m_unsafe_rollback_flags= save_unsafe_rollback_flags; DBUG_RETURN(error); @@ -11784,7 +11796,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, if (backup_reset_alter_copy_lock(thd)) error= 1; - if (unlikely(mysql_trans_commit_alter_copy_data(thd))) + if (unlikely(mysql_trans_commit_alter_copy_data(thd, false))) error= 1; err: diff --git a/sql/sql_table.h b/sql/sql_table.h index 56c69b955c3..0e721c99b92 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -18,11 +18,35 @@ #define SQL_TABLE_INCLUDED #include <my_sys.h> // pthread_mutex_t +#include "mysqld_error.h" #include "m_string.h" // LEX_CUSTRING -#define ERROR_INJECT(code) \ - ((DBUG_IF("crash_" code) && (DBUG_SUICIDE(), 0)) || \ - (DBUG_IF("fail_" code) && (my_error(ER_UNKNOWN_ERROR, MYF(0)), 1))) +#ifndef DBUG_OFF +void sql_print_error(const char *format, ...); + +inline bool _error_inject(const char *crash_kw, const char *fail_kw) +{ + if (DBUG_IF(crash_kw)) + { + sql_print_error("Crashing at %s", crash_kw); + DBUG_SUICIDE(); + return false; + } + if (DBUG_IF(fail_kw)) + { + sql_print_error("Failing at %s", fail_kw); + my_error(ER_UNKNOWN_ERROR, MYF(0)); + return true; + } + DBUG_PRINT("ddl_log", ("Passing %s", fail_kw)); + return false; +} +#define ERROR_INJECT(code) (_error_inject("crash_" code, "fail_" code)) +#define CRASH_INJECT(code) (_error_inject("crash_" code, "crash_" code)) +#else +#define ERROR_INJECT(code) false +#define CRASH_INJECT(code) do { } while(0) +#endif class Alter_info; class Alter_table_ctx; @@ -126,8 +150,8 @@ bool mysql_prepare_alter_table(THD *thd, TABLE *table, HA_CREATE_INFO *create_info, Alter_info *alter_info, Alter_table_ctx *alter_ctx); -bool mysql_trans_prepare_alter_copy_data(THD *thd); -bool mysql_trans_commit_alter_copy_data(THD *thd); +int mysql_trans_prepare_alter_copy_data(THD *thd); +int mysql_trans_commit_alter_copy_data(THD *thd, bool rollback); bool mysql_alter_table(THD *thd, const LEX_CSTRING *new_db, const LEX_CSTRING *new_name, HA_CREATE_INFO *create_info, diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 6fe0d5384a9..1b0c9f1b8c7 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -704,7 +704,7 @@ end: ddl_log_complete(&ddl_log_state); debug_crash_here("ddl_log_drop_before_delete_tmp"); /* delete any created log files */ - result|= ddl_log_revert(thd, &ddl_log_state_tmp_file); + result|= ddl_log_revert(thd, &ddl_log_state_tmp_file, true); if (mdl_request_for_trn.ticket) thd->mdl_context.release_lock(mdl_request_for_trn.ticket); @@ -1118,7 +1118,7 @@ err: } /* Recover the old .TRN and .TRG files & delete backup files */ - ddl_log_revert(thd, ddl_log_state); + ddl_log_revert(thd, ddl_log_state, true); /* All backup files are now deleted */ ddl_log_complete(ddl_log_state_tmp_file); DBUG_RETURN(true); |