summaryrefslogtreecommitdiff
path: root/sql/backup.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/backup.cc')
-rw-r--r--sql/backup.cc176
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, &current_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);
+ }
+}