diff options
Diffstat (limited to 'sql/sql_table.cc')
-rw-r--r-- | sql/sql_table.cc | 1080 |
1 files changed, 1009 insertions, 71 deletions
diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 633558de914..90251184120 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -241,6 +241,929 @@ static int mysql_copy_key_list(List<Key> *orig_key, DBUG_RETURN(FALSE); } +/* +-------------------------------------------------------------------------- + + MODULE: DDL log + ----------------- + + This module is used to ensure that we can recover from crashes that occur + in the middle of a meta-data operation in MySQL. E.g. DROP TABLE t1, t2; + We need to ensure that both t1 and t2 are dropped and not only t1 and + also that each table drop is entirely done and not "half-baked". + + To support this we create log entries for each meta-data statement in the + ddl log while we are executing. These entries are dropped when the + operation is completed. + + At recovery those entries that were not completed will be executed. + + There is only one ddl log in the system and it is protected by a mutex + and there is a global struct that contains information about its current + state. + + History: + First version written in 2006 by Mikael Ronstrom +-------------------------------------------------------------------------- +*/ + + +typedef struct st_global_ddl_log +{ + char file_entry_buf[4*IO_SIZE]; + char file_name_str[FN_REFLEN]; + char *file_name; + DDL_LOG_MEMORY_ENTRY *first_free; + DDL_LOG_MEMORY_ENTRY *first_used; + uint num_entries; + File file_id; + uint name_len; + uint handler_name_len; + uint io_size; + bool inited; + bool recovery_phase; +} GLOBAL_DDL_LOG; + +GLOBAL_DDL_LOG global_ddl_log; + +pthread_mutex_t LOCK_gdl; + +#define DDL_LOG_ENTRY_TYPE_POS 0 +#define DDL_LOG_ACTION_TYPE_POS 1 +#define DDL_LOG_PHASE_POS 2 +#define DDL_LOG_NEXT_ENTRY_POS 4 +#define DDL_LOG_NAME_POS 8 + +#define DDL_LOG_NUM_ENTRY_POS 0 +#define DDL_LOG_NAME_LEN_POS 4 +#define DDL_LOG_HANDLER_TYPE_POS 8 +#define DDL_LOG_IO_SIZE_POS 12 + +/* + Read one entry from ddl log file + SYNOPSIS + read_ddl_log_file_entry() + entry_no Entry number to read + RETURN VALUES + TRUE Error + FALSE Success +*/ + +static bool read_ddl_log_file_entry(uint entry_no) +{ + bool error= FALSE; + File file_id= global_ddl_log.file_id; + char *file_entry_buf= (char*)global_ddl_log.file_entry_buf; + uint io_size= global_ddl_log.io_size; + DBUG_ENTER("read_ddl_log_file_entry"); + + if (my_pread(file_id, file_entry_buf, io_size, io_size * entry_no, + MYF(MY_WME)) != io_size) + error= TRUE; + DBUG_RETURN(error); +} + + +/* + Write one entry from ddl log file + SYNOPSIS + write_ddl_log_file_entry() + entry_no Entry number to read + RETURN VALUES + TRUE Error + FALSE Success +*/ + +static bool write_ddl_log_file_entry(uint entry_no) +{ + bool error= FALSE; + File file_id= global_ddl_log.file_id; + char *file_entry_buf= (char*)global_ddl_log.file_entry_buf; + DBUG_ENTER("write_ddl_log_file_entry"); + + if (my_pwrite(file_id, file_entry_buf, + IO_SIZE, IO_SIZE * entry_no, MYF(MY_WME)) != IO_SIZE) + error= TRUE; + DBUG_RETURN(error); +} + + +/* + Write ddl log header + SYNOPSIS + write_ddl_log_header() + RETURN VALUES + TRUE Error + FALSE Success +*/ + +static bool write_ddl_log_header() +{ + uint16 const_var; + bool error= FALSE; + DBUG_ENTER("write_ddl_log_header"); + + int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NUM_ENTRY_POS], + global_ddl_log.num_entries); + const_var= FN_LEN; + int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_LEN_POS], + const_var); + const_var= DDL_LOG_HANDLER_TYPE_LEN; + int4store(&global_ddl_log.file_entry_buf[DDL_LOG_HANDLER_TYPE_POS], + const_var); + const_var= IO_SIZE; + int4store(&global_ddl_log.file_entry_buf[DDL_LOG_IO_SIZE_POS], + const_var); + if (write_ddl_log_file_entry(0UL)) + { + sql_print_error("Error writing ddl log header"); + DBUG_RETURN(TRUE); + } + VOID(sync_ddl_log()); + DBUG_RETURN(error); +} + + +/* + Create ddl log file name + SYNOPSIS + create_ddl_log_file_name() + file_name Filename setup + RETURN VALUES + NONE +*/ + +static inline void create_ddl_log_file_name(char *file_name) +{ + strxmov(file_name, mysql_data_home, "/", "ddl_log.log", NullS); +} + + +/* + Read header of ddl log file + SYNOPSIS + read_ddl_log_header() + RETURN VALUES + > 0 Last entry in ddl log + 0 No entries in ddl log + DESCRIPTION + When we read the ddl log header we get information about maximum sizes + of names in the ddl log and we also get information about the number + of entries in the ddl log. +*/ + +static uint read_ddl_log_header() +{ + char *file_entry_buf= (char*)global_ddl_log.file_entry_buf; + char file_name[FN_REFLEN]; + uint entry_no; + bool successful_open= FALSE; + DBUG_ENTER("read_ddl_log_header"); + + bzero(file_entry_buf, sizeof(global_ddl_log.file_entry_buf)); + global_ddl_log.inited= FALSE; + global_ddl_log.recovery_phase= TRUE; + create_ddl_log_file_name(file_name); + if (!(my_open(file_name, O_RDWR | O_BINARY, MYF(MY_WME)))) + { + if (read_ddl_log_file_entry(0UL)) + { + /* Write message into error log */ + sql_print_error("Failed to read ddl log file in recovery"); + } + else + successful_open= TRUE; + } + else + sql_print_error("Failed to open ddl log file in recovery"); + entry_no= uint4korr(&file_entry_buf[DDL_LOG_NUM_ENTRY_POS]); + global_ddl_log.name_len= uint4korr(&file_entry_buf[DDL_LOG_NAME_LEN_POS]); + global_ddl_log.handler_name_len= + uint4korr(&file_entry_buf[DDL_LOG_HANDLER_TYPE_POS]); + if (successful_open) + global_ddl_log.io_size= uint4korr(&file_entry_buf[DDL_LOG_IO_SIZE_POS]); + else + { + global_ddl_log.io_size= IO_SIZE; + entry_no= 0; + } + global_ddl_log.first_free= NULL; + global_ddl_log.first_used= NULL; + global_ddl_log.num_entries= 0; + VOID(pthread_mutex_init(&LOCK_gdl, MY_MUTEX_INIT_FAST)); + DBUG_RETURN(entry_no); +} + + +/* + Read a ddl log entry + SYNOPSIS + read_ddl_log_entry() + read_entry Number of entry to read + out:entry_info Information from entry + RETURN VALUES + TRUE Error + FALSE Success + DESCRIPTION + Read a specified entry in the ddl log +*/ + +bool read_ddl_log_entry(uint read_entry, DDL_LOG_ENTRY *ddl_log_entry) +{ + char *file_entry_buf= (char*)&global_ddl_log.file_entry_buf; + uint inx; + DBUG_ENTER("read_ddl_log_entry"); + + if (read_ddl_log_file_entry(read_entry)) + { + DBUG_RETURN(TRUE); + } + ddl_log_entry->entry_pos= read_entry; + ddl_log_entry->entry_type= + (enum ddl_log_entry_code)file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]; + ddl_log_entry->action_type= + (enum ddl_log_action_code)file_entry_buf[DDL_LOG_ACTION_TYPE_POS]; + ddl_log_entry->phase= file_entry_buf[DDL_LOG_PHASE_POS]; + ddl_log_entry->next_entry= uint4korr(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS]); + ddl_log_entry->name= &file_entry_buf[DDL_LOG_NAME_POS]; + inx= DDL_LOG_NAME_POS + global_ddl_log.name_len; + ddl_log_entry->from_name= &file_entry_buf[inx]; + inx+= global_ddl_log.name_len; + ddl_log_entry->handler_name= &file_entry_buf[inx]; + DBUG_RETURN(FALSE); +} + + +/* + Initialise ddl log + SYNOPSIS + init_ddl_log() + RETURN VALUES + TRUE Error + FALSE Success + DESCRIPTION + Write the header of the ddl log file and length of names. Also set + number of entries to zero. +*/ + +static bool init_ddl_log() +{ + bool error= FALSE; + char file_name[FN_REFLEN]; + DBUG_ENTER("init_ddl_log"); + + if (global_ddl_log.inited) + { + DBUG_RETURN(FALSE); + } + global_ddl_log.io_size= IO_SIZE; + if ((global_ddl_log.file_id= my_create(file_name, + CREATE_MODE, + O_RDWR | O_TRUNC | O_BINARY, + MYF(MY_WME))) < 0) + { + /* Couldn't create ddl log file, this is serious error */ + sql_print_error("Failed to open ddl log file"); + DBUG_RETURN(TRUE); + } + if (write_ddl_log_header()) + { + DBUG_RETURN(TRUE); + } + global_ddl_log.inited= TRUE; + DBUG_RETURN(FALSE); +} + + +/* + Execute one action in a ddl log entry + SYNOPSIS + execute_ddl_log_action() + ddl_log_entry Information in action entry to execute + RETURN VALUES + TRUE Error + FALSE Success +*/ + +static bool execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry) +{ + bool frm_action= FALSE; + LEX_STRING handler_name; + handler *file; + MEM_ROOT mem_root; + bool error= TRUE; + char path[FN_REFLEN]; + char from_path[FN_REFLEN]; + char *par_ext= (char*)".par"; + handlerton *hton; + DBUG_ENTER("execute_ddl_log_action"); + + if (ddl_log_entry->entry_type == DDL_IGNORE_LOG_ENTRY_CODE) + { + DBUG_RETURN(FALSE); + } + handler_name.str= (char*)ddl_log_entry->handler_name; + handler_name.length= strlen(ddl_log_entry->handler_name); + hton= ha_resolve_by_name(thd, &handler_name); + if (!hton) + { + my_error(ER_ILLEGAL_HA, MYF(0), ddl_log_entry->handler_name); + DBUG_RETURN(TRUE); + } + init_sql_alloc(&mem_root, TABLE_ALLOC_BLOCK_SIZE, 0); + if (strcmp("frm", ddl_log_entry->handler_name)) + frm_action= TRUE; + else + { + TABLE_SHARE dummy; + bzero(&dummy, sizeof(TABLE_SHARE)); + file= get_new_handler(&dummy, &mem_root, hton); + if (!file) + { + mem_alloc_error(sizeof(handler)); + goto error; + } + } + switch (ddl_log_entry->action_type) + { + case DDL_LOG_REPLACE_ACTION: + case DDL_LOG_DELETE_ACTION: + { + if (ddl_log_entry->phase == 0) + { + if (frm_action) + { + strxmov(path, ddl_log_entry->name, reg_ext, NullS); + if (my_delete(path, MYF(MY_WME))) + break; +#ifdef WITH_PARTITION_STORAGE_ENGINE + strxmov(path, ddl_log_entry->name, par_ext, NullS); + VOID(my_delete(path, MYF(MY_WME))); +#endif + } + else + { + if (file->delete_table(ddl_log_entry->name)) + break; + } + if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos))) + { + VOID(sync_ddl_log()); + error= FALSE; + } + if (ddl_log_entry->action_type == DDL_LOG_DELETE_ACTION) + break; + } + DBUG_ASSERT(ddl_log_entry->action_type == DDL_LOG_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. + */ + } + case DDL_LOG_RENAME_ACTION: + { + error= TRUE; + if (frm_action) + { + strxmov(path, ddl_log_entry->name, reg_ext, NullS); + strxmov(from_path, ddl_log_entry->from_name, reg_ext, NullS); + if (my_rename(path, from_path, MYF(MY_WME))) + break; +#ifdef WITH_PARTITION_STORAGE_ENGINE + strxmov(path, ddl_log_entry->name, par_ext, NullS); + strxmov(from_path, ddl_log_entry->from_name, par_ext, NullS); + VOID(my_rename(path, from_path, MYF(MY_WME))); +#endif + } + else + { + if (file->rename_table(ddl_log_entry->name, + ddl_log_entry->from_name)) + break; + } + if ((deactivate_ddl_log_entry(ddl_log_entry->entry_pos))) + { + VOID(sync_ddl_log()); + error= FALSE; + } + break; + } + default: + DBUG_ASSERT(0); + break; + } + delete file; +error: + free_root(&mem_root, MYF(0)); + DBUG_RETURN(error); +} + + +/* + Get a free entry in the ddl log + SYNOPSIS + get_free_ddl_log_entry() + out:active_entry A ddl log memory entry returned + RETURN VALUES + TRUE Error + FALSE Success +*/ + +static bool get_free_ddl_log_entry(DDL_LOG_MEMORY_ENTRY **active_entry, + bool *write_header) +{ + DDL_LOG_MEMORY_ENTRY *used_entry; + DDL_LOG_MEMORY_ENTRY *first_used= global_ddl_log.first_used; + DBUG_ENTER("get_free_ddl_log_entry"); + + if (global_ddl_log.first_free == NULL) + { + if (!(used_entry= (DDL_LOG_MEMORY_ENTRY*)my_malloc( + sizeof(DDL_LOG_MEMORY_ENTRY), MYF(MY_WME)))) + { + sql_print_error("Failed to allocate memory for ddl log free list"); + DBUG_RETURN(TRUE); + } + global_ddl_log.num_entries++; + used_entry->entry_pos= global_ddl_log.num_entries; + *write_header= TRUE; + } + else + { + used_entry= global_ddl_log.first_free; + global_ddl_log.first_free= used_entry->next_log_entry; + *write_header= FALSE; + } + /* + Move from free list to used list + */ + used_entry->next_log_entry= first_used; + used_entry->prev_log_entry= NULL; + global_ddl_log.first_used= used_entry; + if (first_used) + first_used->prev_log_entry= used_entry; + + *active_entry= used_entry; + DBUG_RETURN(FALSE); +} + + +/* + External interface methods for the DDL log Module + --------------------------------------------------- +*/ + +/* + SYNOPSIS + write_ddl_log_entry() + ddl_log_entry Information about log entry + out:entry_written Entry information written into + + RETURN VALUES + TRUE Error + FALSE Success + + DESCRIPTION + A careful write of the ddl log is performed to ensure that we can + handle crashes occurring during CREATE and ALTER TABLE processing. +*/ + +bool write_ddl_log_entry(DDL_LOG_ENTRY *ddl_log_entry, + DDL_LOG_MEMORY_ENTRY **active_entry) +{ + bool error, write_header; + DBUG_ENTER("write_ddl_log_entry"); + + if (init_ddl_log()) + { + DBUG_RETURN(TRUE); + } + global_ddl_log.file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_LOG_ENTRY_CODE; + global_ddl_log.file_entry_buf[DDL_LOG_ACTION_TYPE_POS]= + ddl_log_entry->action_type; + global_ddl_log.file_entry_buf[DDL_LOG_PHASE_POS]= 0; + int4store(&global_ddl_log.file_entry_buf[DDL_LOG_NEXT_ENTRY_POS], + ddl_log_entry->next_entry); + DBUG_ASSERT(strlen(ddl_log_entry->name) < FN_LEN); + strncpy(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS], + ddl_log_entry->name, FN_LEN); + if (ddl_log_entry->action_type == DDL_LOG_RENAME_ACTION || + ddl_log_entry->action_type == DDL_LOG_REPLACE_ACTION) + { + DBUG_ASSERT(strlen(ddl_log_entry->from_name) < FN_LEN); + strncpy(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN], + ddl_log_entry->from_name, FN_LEN); + } + else + global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0; + DBUG_ASSERT(strlen(ddl_log_entry->handler_name) < FN_LEN); + strncpy(&global_ddl_log.file_entry_buf[DDL_LOG_NAME_POS + (2*FN_LEN)], + ddl_log_entry->handler_name, FN_LEN); + if (get_free_ddl_log_entry(active_entry, &write_header)) + { + DBUG_RETURN(TRUE); + } + error= FALSE; + if (write_ddl_log_file_entry((*active_entry)->entry_pos)) + { + error= TRUE; + sql_print_error("Failed to write entry_no = %u", + (*active_entry)->entry_pos); + } + if (write_header && !error) + { + VOID(sync_ddl_log()); + if (write_ddl_log_header()) + error= TRUE; + VOID(sync_ddl_log()); + } + if (error) + release_ddl_log_memory_entry(*active_entry); + DBUG_RETURN(error); +} + + +/* + Write final entry in the ddl log + SYNOPSIS + write_execute_ddl_log_entry() + first_entry First entry in linked list of entries + to execute, if 0 = NULL it means that + the entry is removed and the entries + are put into the free list. + in:out:exec_entry Entry to execute, 0 = NULL if the entry + is written first time and needs to be + returned. In this case the entry written + is returned in this parameter + RETURN VALUES + TRUE Error + FALSE Success + + DESCRIPTION + This is the last write in the ddl log. The previous log entries have + already been written but not yet synched to disk. + We write a couple of log entries that describes action to perform. + This entries are set-up in a linked list, however only when a first + execute entry is put as the first entry these will be executed. + This routine writes this first +*/ + +bool write_execute_ddl_log_entry(uint first_entry, + bool complete, + DDL_LOG_MEMORY_ENTRY **active_entry) +{ + bool write_header= FALSE; + char *file_entry_buf= (char*)global_ddl_log.file_entry_buf; + DBUG_ENTER("write_execute_ddl_log_entry"); + + if (init_ddl_log()) + { + DBUG_RETURN(TRUE); + } + if (!complete) + { + /* + We haven't synched the log entries yet, we synch them now before + writing the execute entry. If complete is true we haven't written + any log entries before, we are only here to write the execute + entry to indicate it is done. + */ + VOID(sync_ddl_log()); + file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_LOG_EXECUTE_CODE; + } + else + file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_IGNORE_LOG_ENTRY_CODE; + file_entry_buf[DDL_LOG_ACTION_TYPE_POS]= 0; /* Ignored for execute entries */ + file_entry_buf[DDL_LOG_PHASE_POS]= 0; + int4store(&file_entry_buf[DDL_LOG_NEXT_ENTRY_POS], first_entry); + file_entry_buf[DDL_LOG_NAME_POS]= 0; + file_entry_buf[DDL_LOG_NAME_POS + FN_LEN]= 0; + file_entry_buf[DDL_LOG_NAME_POS + 2*FN_LEN]= 0; + if (!(*active_entry)) + { + if (get_free_ddl_log_entry(active_entry, &write_header)) + { + DBUG_RETURN(TRUE); + } + } + if (write_ddl_log_file_entry((*active_entry)->entry_pos)) + { + sql_print_error("Error writing execute entry in ddl log"); + release_ddl_log_memory_entry(*active_entry); + DBUG_RETURN(TRUE); + } + VOID(sync_ddl_log()); + if (write_header) + { + if (write_ddl_log_header()) + { + release_ddl_log_memory_entry(*active_entry); + DBUG_RETURN(TRUE); + } + VOID(sync_ddl_log()); + } + DBUG_RETURN(FALSE); +} + + +/* + For complex rename operations we need to deactivate individual entries. + SYNOPSIS + deactivate_ddl_log_entry() + entry_no Entry position of record to change + RETURN VALUES + TRUE Error + FALSE Success + DESCRIPTION + During replace operations where we start with an existing table called + t1 and a replacement table called t1#temp or something else and where + we want to delete t1 and rename t1#temp to t1 this is not possible to + do in a safe manner unless the ddl log is informed of the phases in + the change. + + Delete actions are 1-phase actions that can be ignored immediately after + being executed. + Rename actions from x to y is also a 1-phase action since there is no + interaction with any other handlers named x and y. + Replace action where drop y and x -> y happens needs to be a two-phase + action. Thus the first phase will drop y and the second phase will + rename x -> y. +*/ + +bool deactivate_ddl_log_entry(uint entry_no) +{ + char *file_entry_buf= (char*)global_ddl_log.file_entry_buf; + DBUG_ENTER("deactivate_ddl_log_entry"); + + if (!read_ddl_log_file_entry(entry_no)) + { + if (file_entry_buf[DDL_LOG_ENTRY_TYPE_POS] == DDL_LOG_ENTRY_CODE) + { + if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_DELETE_ACTION || + file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_RENAME_ACTION || + (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION && + file_entry_buf[DDL_LOG_PHASE_POS] == 1)) + file_entry_buf[DDL_LOG_ENTRY_TYPE_POS]= DDL_IGNORE_LOG_ENTRY_CODE; + else if (file_entry_buf[DDL_LOG_ACTION_TYPE_POS] == DDL_LOG_REPLACE_ACTION) + { + DBUG_ASSERT(file_entry_buf[DDL_LOG_PHASE_POS] == 0); + file_entry_buf[DDL_LOG_PHASE_POS]= 1; + } + else + { + DBUG_ASSERT(0); + } + if (write_ddl_log_file_entry(entry_no)) + { + sql_print_error("Error in deactivating log entry. Position = %u", + entry_no); + DBUG_RETURN(TRUE); + } + } + } + else + { + sql_print_error("Failed in reading entry before deactivating it"); + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); +} + + +/* + Sync ddl log file + SYNOPSIS + sync_ddl_log() + RETURN VALUES + TRUE Error + FALSE Success +*/ + +bool sync_ddl_log() +{ + bool error= FALSE; + DBUG_ENTER("sync_ddl_log"); + + if ((!global_ddl_log.recovery_phase) && + init_ddl_log()) + { + DBUG_RETURN(TRUE); + } + if (my_sync(global_ddl_log.file_id, MYF(0))) + { + /* Write to error log */ + sql_print_error("Failed to sync ddl log"); + error= TRUE; + } + DBUG_RETURN(error); +} + + +/* + Release a log memory entry + SYNOPSIS + release_ddl_log_memory_entry() + log_memory_entry Log memory entry to release + RETURN VALUES + NONE +*/ + +void release_ddl_log_memory_entry(DDL_LOG_MEMORY_ENTRY *log_entry) +{ + DDL_LOG_MEMORY_ENTRY *first_free= global_ddl_log.first_free; + DDL_LOG_MEMORY_ENTRY *next_log_entry= log_entry->next_log_entry; + DDL_LOG_MEMORY_ENTRY *prev_log_entry= log_entry->prev_log_entry; + DBUG_ENTER("release_ddl_log_memory_entry"); + + global_ddl_log.first_free= log_entry; + log_entry->next_log_entry= first_free; + + if (prev_log_entry) + prev_log_entry->next_log_entry= next_log_entry; + else + global_ddl_log.first_used= next_log_entry; + if (next_log_entry) + next_log_entry->prev_log_entry= prev_log_entry; + DBUG_VOID_RETURN; +} + + +/* + Execute one entry in the ddl log. Executing an entry means executing + a linked list of actions. + SYNOPSIS + execute_ddl_log_entry() + first_entry Reference to first action in entry + RETURN VALUES + TRUE Error + FALSE Success +*/ + +bool execute_ddl_log_entry(THD *thd, uint first_entry) +{ + DDL_LOG_ENTRY ddl_log_entry; + uint read_entry= first_entry; + DBUG_ENTER("execute_ddl_log_entry"); + + lock_global_ddl_log(); + do + { + if (read_ddl_log_entry(read_entry, &ddl_log_entry)) + { + DBUG_ASSERT(0); + /* Write to error log and continue with next log entry */ + sql_print_error("Failed to read entry = %u from ddl log", + read_entry); + break; + } + DBUG_ASSERT(ddl_log_entry.entry_type == DDL_LOG_ENTRY_CODE || + ddl_log_entry.entry_type == DDL_IGNORE_LOG_ENTRY_CODE); + + if (execute_ddl_log_action(thd, &ddl_log_entry)) + { + DBUG_ASSERT(0); + /* Write to error log and continue with next log entry */ + sql_print_error("Failed to execute action for entry = %u from ddl log", + read_entry); + break; + } + read_entry= ddl_log_entry.next_entry; + } while (read_entry); + unlock_global_ddl_log(); + DBUG_RETURN(FALSE); +} + + +/* + Execute the ddl log at recovery of MySQL Server + SYNOPSIS + execute_ddl_log_recovery() + RETURN VALUES + NONE +*/ + +void execute_ddl_log_recovery() +{ + uint num_entries, i; + DDL_LOG_ENTRY ddl_log_entry; + DBUG_ENTER("execute_ddl_log_recovery"); + + /* + To be able to run this from boot, we allocate a temporary THD + */ + if (!(thd=new THD)) + DBUG_VOID_RETURN; + thd->thread_stack= (char*) &thd; + thd->store_globals(); + + num_entries= read_ddl_log_header(); + for (i= 0; i < num_entries; i++) + { + if (read_ddl_log_entry(i, &ddl_log_entry)) + { + DBUG_ASSERT(0); + sql_print_error("Failed to read entry no = %u from ddl log", + i); + continue; + } + if (ddl_log_entry.entry_type == DDL_LOG_EXECUTE_CODE) + { + if (execute_ddl_log_entry(ddl_log_entry.next_entry)) + { + /* Real unpleasant scenario but we continue anyways. */ + DBUG_ASSERT(0); + continue; + } + } + } + create_ddl_log_file_name(file_name); + VOID(my_delete(file_name, MYF(0))); + global_ddl_log.recovery_phase= FALSE; + delete thd; + /* Remember that we don't have a THD */ + my_pthread_setspecific_ptr(THR_THD, 0); + DBUG_VOID_RETURN; +} + + +/* + Release all memory allocated to the ddl log + SYNOPSIS + release_ddl_log() + RETURN VALUES + NONE +*/ + +void release_ddl_log() +{ + DDL_LOG_MEMORY_ENTRY *free_list= global_ddl_log.first_free; + DDL_LOG_MEMORY_ENTRY *used_list= global_ddl_log.first_used; + DBUG_ENTER("release_ddl_log"); + + lock_global_ddl_log(); + while (used_list) + { + DDL_LOG_MEMORY_ENTRY *tmp= used_list->next_log_entry; + my_free((char*)used_list, MYF(0)); + used_list= tmp; + } + while (free_list) + { + DDL_LOG_MEMORY_ENTRY *tmp= free_list->next_log_entry; + my_free((char*)free_list, MYF(0)); + free_list= tmp; + } + VOID(my_close(global_ddl_log.file_id, MYF(0))); + unlock_global_ddl_log(); + VOID(pthread_mutex_destroy(&LOCK_gdl)); + DBUG_VOID_RETURN; +} + + +/* + Lock mutex for global ddl log + SYNOPSIS + lock_global_ddl_log() + RETURN VALUES + NONE +*/ + +void lock_global_ddl_log() +{ + DBUG_ENTER("lock_global_ddl_log"); + + VOID(pthread_mutex_lock(&LOCK_gdl)); + DBUG_VOID_RETURN; +} + + +/* + Unlock mutex for global ddl log + SYNOPSIS + unlock_global_ddl_log() + RETURN VALUES + NONE +*/ + +void unlock_global_ddl_log() +{ + DBUG_ENTER("unlock_global_ddl_log"); + + VOID(pthread_mutex_unlock(&LOCK_gdl)); + DBUG_VOID_RETURN; +} + + +/* +--------------------------------------------------------------------------- + + END MODULE DDL log + -------------------- + +--------------------------------------------------------------------------- +*/ + /* SYNOPSIS @@ -274,83 +1197,66 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) */ int error= 0; char path[FN_REFLEN+1]; + char shadow_path[FN_REFLEN+1]; + char shadow_frm_name[FN_REFLEN+1]; char frm_name[FN_REFLEN+1]; DBUG_ENTER("mysql_write_frm"); - if (flags & WFRM_INITIAL_WRITE) + /* + Build shadow frm file name + */ + build_table_filename(shadow_path, sizeof(shadow_path), lpt->db, + lpt->table_name, "#"); + strxmov(shadow_frm_name, shadow_path, reg_ext, NullS); + if (flags & WFRM_WRITE_SHADOW) { - error= mysql_copy_create_list(lpt->create_list, - &lpt->new_create_list); - error+= mysql_copy_key_list(lpt->key_list, - &lpt->new_key_list); - if (error) + if (mysql_copy_create_list(lpt->create_list, + &lpt->new_create_list) || + mysql_copy_key_list(lpt->key_list, + &lpt->new_key_list) || + mysql_prepare_table(lpt->thd, lpt->create_info, + &lpt->new_create_list, + &lpt->new_key_list, + /*tmp_table*/ 1, + &lpt->db_options, + lpt->table->file, + &lpt->key_info_buffer, + &lpt->key_count, + /*select_field_count*/ 0)) { DBUG_RETURN(TRUE); } - } - build_table_filename(path, sizeof(path), lpt->db, lpt->table_name, ""); - strxmov(frm_name, path, reg_ext, NullS); - if ((flags & WFRM_INITIAL_WRITE) && - (mysql_prepare_table(lpt->thd, lpt->create_info, &lpt->new_create_list, - &lpt->new_key_list,/*tmp_table*/ 1, &lpt->db_options, - lpt->table->file, &lpt->key_info_buffer, - &lpt->key_count, /*select_field_count*/ 0))) - { - DBUG_RETURN(TRUE); - } #ifdef WITH_PARTITION_STORAGE_ENGINE - { - partition_info *part_info= lpt->table->part_info; - char *part_syntax_buf; - uint syntax_len, i; - bool any_unnormal_state= FALSE; - - if (part_info) { - uint max_part_state_len= part_info->partitions.elements + - part_info->temp_partitions.elements; - if (!(part_info->part_state= (uchar*)sql_alloc(max_part_state_len))) - { - DBUG_RETURN(TRUE); - } - part_info->part_state_len= 0; - if (!(part_syntax_buf= generate_partition_syntax(part_info, - &syntax_len, - TRUE, FALSE))) - { - DBUG_RETURN(TRUE); - } - for (i= 0; i < part_info->part_state_len; i++) - { - enum partition_state part_state= - (enum partition_state)part_info->part_state[i]; - if (part_state != PART_NORMAL && part_state != PART_IS_ADDED) - any_unnormal_state= TRUE; - } - if (!any_unnormal_state) + partition_info *part_info= lpt->table->part_info; + char *part_syntax_buf; + uint syntax_len; + + if (part_info) { - part_info->part_state= NULL; - part_info->part_state_len= 0; + if (!(part_syntax_buf= generate_partition_syntax(part_info, + &syntax_len, + TRUE, FALSE))) + { + DBUG_RETURN(TRUE); + } + part_info->part_info_string= part_syntax_buf; + part_info->part_info_len= syntax_len; } - part_info->part_info_string= part_syntax_buf; - part_info->part_info_len= syntax_len; } - } #endif - /* - We write the frm file with the LOCK_open mutex since otherwise we could - overwrite the frm file as another is reading it in open_table. - */ - lpt->create_info->table_options= lpt->db_options; - VOID(pthread_mutex_lock(&LOCK_open)); - if ((mysql_create_frm(lpt->thd, frm_name, lpt->db, lpt->table_name, - lpt->create_info, lpt->new_create_list, lpt->key_count, - lpt->key_info_buffer, lpt->table->file)) || - ((flags & WFRM_CREATE_HANDLER_FILES) && - lpt->table->file->create_handler_files(path))) - { - error= 1; - goto end; + /* Write shadow frm file */ + lpt->create_info->table_options= lpt->db_options; + if ((mysql_create_frm(lpt->thd, shadow_frm_name, lpt->db, + lpt->table_name, lpt->create_info, + lpt->new_create_list, lpt->key_count, + lpt->key_info_buffer, lpt->table->file)) || + lpt->table->file->create_handler_files(shadow_path, NULL, FALSE)) + { + my_delete(shadow_frm_name, MYF(0)); + error= 1; + goto end; + } } if (flags & WFRM_PACK_FRM) { @@ -362,7 +1268,7 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) */ const void *data= 0; uint length= 0; - if (readfrm(path, &data, &length) || + if (readfrm(shadow_path, &data, &length) || packfrm(data, length, &lpt->pack_frm_data, &lpt->pack_frm_len)) { my_free((char*)data, MYF(MY_ALLOW_ZERO_PTR)); @@ -371,11 +1277,43 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) error= 1; goto end; } - error= my_delete(frm_name, MYF(MY_WME)); + error= my_delete(shadow_frm_name, MYF(MY_WME)); + } + if (flags & WFRM_INSTALL_SHADOW) + { +#ifdef WITH_PARTITION_STORAGE_ENGINE + partition_info *part_info= lpt->part_info; +#endif + /* + Build frm file name + */ + build_table_filename(path, sizeof(path), lpt->db, + lpt->table_name, ""); + strxmov(frm_name, path, reg_ext, NullS); + /* + When we are changing to use new frm file we need to ensure that we + don't collide with another thread in process to open the frm file. + */ + VOID(pthread_mutex_lock(&LOCK_open)); + if (my_delete(frm_name, MYF(MY_WME)) || +#ifdef WITH_PARTITION_STORAGE_ENGINE + deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) || + (sync_ddl_log(), FALSE) || +#endif + my_rename(shadow_frm_name, frm_name, MYF(MY_WME)) || + lpt->table->file->create_handler_files(path, shadow_path, TRUE)) + { + error= 1; + } + VOID(pthread_mutex_unlock(&LOCK_open)); +#ifdef WITH_PARTITION_STORAGE_ENGINE + deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos); + part_info->frm_log_entry= NULL; + VOID(sync_ddl_log()); +#endif } - /* Frm file have been updated to reflect the change about to happen. */ + end: - VOID(pthread_mutex_unlock(&LOCK_open)); DBUG_RETURN(error); } @@ -4788,7 +5726,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, error= (mysql_create_frm(thd, reg_path, db, table_name, create_info, prepared_create_list, key_count, key_info_buffer, table->file) || - table->file->create_handler_files(reg_path)); + table->file->create_handler_files(reg_path, NULL, FALSE)); VOID(pthread_mutex_unlock(&LOCK_open)); if (error) goto err; @@ -4834,7 +5772,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, error= (mysql_create_frm(thd, reg_path, db, table_name, create_info, prepared_create_list, key_count, key_info_buffer, table->file) || - table->file->create_handler_files(reg_path)); + table->file->create_handler_files(reg_path, NULL, FALSE)); VOID(pthread_mutex_unlock(&LOCK_open)); if (error) goto err; @@ -5061,7 +5999,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, VOID(pthread_mutex_lock(&LOCK_open)); } /* Tell the handler that a new frm file is in place. */ - if (table->file->create_handler_files(reg_path)) + if (table->file->create_handler_files(reg_path, NULL, FALSE)) { VOID(pthread_mutex_unlock(&LOCK_open)); goto err; |