diff options
Diffstat (limited to 'sql/backup.cc')
-rw-r--r-- | sql/backup.cc | 176 |
1 files changed, 171 insertions, 5 deletions
diff --git a/sql/backup.cc b/sql/backup.cc index e89f9a108a7..1b3007c5a00 100644 --- a/sql/backup.cc +++ b/sql/backup.cc @@ -34,6 +34,7 @@ #include "sql_insert.h" // kill_delayed_threads #include "sql_handler.h" // mysql_ha_cleanup_no_free #include <my_sys.h> +#include <strfunc.h> // strconvert() static const char *stage_names[]= {"START", "FLUSH", "BLOCK_DDL", "BLOCK_COMMIT", "END", 0}; @@ -42,11 +43,15 @@ TYPELIB backup_stage_names= { array_elements(stage_names)-1, "", stage_names, 0 }; static MDL_ticket *backup_flush_ticket; +static File volatile backup_log= -1; +static int backup_log_error= 0; static bool backup_start(THD *thd); static bool backup_flush(THD *thd); static bool backup_block_ddl(THD *thd); static bool backup_block_commit(THD *thd); +static bool start_ddl_logging(); +static void stop_ddl_logging(); /** Run next stage of backup @@ -55,6 +60,8 @@ static bool backup_block_commit(THD *thd); void backup_init() { backup_flush_ticket= 0; + backup_log= -1; + backup_log_error= 0; } bool run_backup_stage(THD *thd, backup_stages stage) @@ -152,7 +159,6 @@ static bool backup_start(THD *thd) thd->current_backup_stage= BACKUP_FINISHED; // For next test if (thd->has_read_only_protection()) DBUG_RETURN(1); - thd->current_backup_stage= BACKUP_START; if (thd->locked_tables_mode) { @@ -160,14 +166,31 @@ static bool backup_start(THD *thd) DBUG_RETURN(1); } - MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_START, + /* this will be reset if this stage fails */ + thd->current_backup_stage= BACKUP_START; + + /* + Wait for old backup to finish and block ddl's so that we can start the + ddl logger + */ + MDL_REQUEST_INIT(&mdl_request, MDL_key::BACKUP, "", "", MDL_BACKUP_BLOCK_DDL, MDL_EXPLICIT); if (thd->mdl_context.acquire_lock(&mdl_request, thd->variables.lock_wait_timeout)) DBUG_RETURN(1); + if (start_ddl_logging()) + { + thd->mdl_context.release_lock(mdl_request.ticket); + DBUG_RETURN(1); + } + + DBUG_ASSERT(backup_flush_ticket == 0); backup_flush_ticket= mdl_request.ticket; + /* Downgrade lock to only block other backups */ + backup_flush_ticket->downgrade_lock(MDL_BACKUP_START); + ha_prepare_for_backup(); DBUG_RETURN(0); } @@ -216,7 +239,8 @@ static bool backup_flush(THD *thd) - Kill all insert delay handlers, to ensure that all non transactional tables are closed (can be improved in the future). - - Close handlers as other threads may wait for these, which can cause deadlocks. + - Close handlers as other threads may wait for these, which can cause + deadlocks. - Wait for all statements using write locked non-transactional tables to end. @@ -271,9 +295,13 @@ static bool backup_block_ddl(THD *thd) backup_flush_ticket->downgrade_lock(MDL_BACKUP_FLUSH); DBUG_RETURN(1); } + + /* There can't be anything more that needs to be logged to ddl log */ + stop_ddl_logging(); DBUG_RETURN(0); } + /** backup_block_commit() @@ -303,6 +331,7 @@ static bool backup_block_commit(THD *thd) DBUG_RETURN(0); } + /** backup_end() @@ -316,9 +345,14 @@ bool backup_end(THD *thd) if (thd->current_backup_stage != BACKUP_FINISHED) { + DBUG_ASSERT(backup_flush_ticket); + MDL_ticket *old_ticket= backup_flush_ticket; ha_end_backup(); + // This is needed as we may call backup_end without backup_block_commit + stop_ddl_logging(); + backup_flush_ticket= 0; thd->current_backup_stage= BACKUP_FINISHED; - thd->mdl_context.release_lock(backup_flush_ticket); + thd->mdl_context.release_lock(old_ticket); } DBUG_RETURN(0); } @@ -370,7 +404,7 @@ bool backup_reset_alter_copy_lock(THD *thd) /***************************************************************************** - Backup locks + Interfaces for BACKUP LOCK These functions are used by maria_backup to ensure that there are no active ddl's on the object the backup is going to copy *****************************************************************************/ @@ -402,3 +436,135 @@ void backup_unlock(THD *thd) thd->mdl_context.release_lock(thd->mdl_backup_lock); thd->mdl_backup_lock= 0; } + + +/***************************************************************************** + Logging of ddl statements to backup log +*****************************************************************************/ + +static bool start_ddl_logging() +{ + char name[FN_REFLEN]; + DBUG_ENTER("start_ddl_logging"); + + fn_format(name, "ddl", mysql_data_home, ".log", 0); + + backup_log_error= 0; + backup_log= mysql_file_create(key_file_log_ddl, name, CREATE_MODE, + O_TRUNC | O_WRONLY | O_APPEND | O_NOFOLLOW, + MYF(MY_WME)); + DBUG_RETURN(backup_log < 0); +} + +static void stop_ddl_logging() +{ + mysql_mutex_lock(&LOCK_backup_log); + if (backup_log >= 0) + { + mysql_file_close(backup_log, MYF(MY_WME)); + backup_log= -1; + } + backup_log_error= 0; + mysql_mutex_unlock(&LOCK_backup_log); +} + + +static inline char *add_str_to_buffer(char *ptr, const LEX_CSTRING *from) +{ + if (from->length) // If length == 0, str may be 0 + memcpy(ptr, from->str, from->length); + ptr[from->length]= '\t'; + return ptr+ from->length + 1; +} + +static char *add_name_to_buffer(char *ptr, const LEX_CSTRING *from) +{ + LEX_CSTRING tmp; + char buff[NAME_LEN*4]; + uint errors; + + tmp.str= buff; + tmp.length= strconvert(system_charset_info, from->str, from->length, + &my_charset_filename, buff, sizeof(buff), &errors); + return add_str_to_buffer(ptr, &tmp); +} + + +static char *add_id_to_buffer(char *ptr, const LEX_CUSTRING *from) +{ + LEX_CSTRING tmp; + char buff[MY_UUID_STRING_LENGTH]; + + if (!from->length) + return add_str_to_buffer(ptr, (LEX_CSTRING*) from); + + tmp.str= buff; + tmp.length= MY_UUID_STRING_LENGTH; + my_uuid2str(from->str, buff); + return add_str_to_buffer(ptr, &tmp); +} + + +static char *add_bool_to_buffer(char *ptr, bool value) { + *(ptr++) = value ? '1' : '0'; + *(ptr++) = '\t'; + return ptr; +} + +/* + Write to backup log + + Sets backup_log_error in case of error. The backup thread could check this + to ensure that all logging had succeded +*/ + +void backup_log_ddl(const backup_log_info *info) +{ + if (backup_log >= 0 && backup_log_error == 0) + { + mysql_mutex_lock(&LOCK_backup_log); + if (backup_log < 0) + { + mysql_mutex_unlock(&LOCK_backup_log); + return; + } + /* Enough place for db.table *2 + query + engine_name * 2 + tabs+ uuids */ + char buff[NAME_CHAR_LEN*4+20+40*2+10+MY_UUID_STRING_LENGTH*2], *ptr= buff; + char timebuff[20]; + struct tm current_time; + LEX_CSTRING tmp_lex; + time_t tmp_time= my_time(0); + + localtime_r(&tmp_time, ¤t_time); + tmp_lex.str= timebuff; + tmp_lex.length= snprintf(timebuff, sizeof(timebuff), + "%4d-%02d-%02d %2d:%02d:%02d", + current_time.tm_year + 1900, + current_time.tm_mon+1, + current_time.tm_mday, + current_time.tm_hour, + current_time.tm_min, + current_time.tm_sec); + ptr= add_str_to_buffer(ptr, &tmp_lex); + + ptr= add_str_to_buffer(ptr, &info->query); + ptr= add_str_to_buffer(ptr, &info->org_storage_engine_name); + ptr= add_bool_to_buffer(ptr, info->org_partitioned); + ptr= add_name_to_buffer(ptr, &info->org_database); + ptr= add_name_to_buffer(ptr, &info->org_table); + ptr= add_id_to_buffer(ptr, &info->org_table_id); + + /* The following fields are only set in case of rename */ + ptr= add_str_to_buffer(ptr, &info->new_storage_engine_name); + ptr= add_bool_to_buffer(ptr, info->new_partitioned); + ptr= add_name_to_buffer(ptr, &info->new_database); + ptr= add_name_to_buffer(ptr, &info->new_table); + ptr= add_id_to_buffer(ptr, &info->new_table_id); + + ptr[-1]= '\n'; // Replace last tab with nl + if (mysql_file_write(backup_log, (uchar*) buff, (size_t) (ptr-buff), + MYF(MY_FNABP))) + backup_log_error= my_errno; + mysql_mutex_unlock(&LOCK_backup_log); + } +} |