summaryrefslogtreecommitdiff
path: root/extra
diff options
context:
space:
mode:
authorVladislav Vaintroub <wlad@mariadb.com>2018-08-09 15:06:52 +0100
committerVladislav Vaintroub <wlad@mariadb.com>2018-08-14 15:11:13 +0100
commit922e7badfcb3f0ab60d1b53e6ab3a771edd6ee0a (patch)
tree24592d855950d9a59b139a15aa264ed524cac686 /extra
parent9a4998a35a0c402fa2dd6d53fbef3ac9897f9e0a (diff)
downloadmariadb-git-922e7badfcb3f0ab60d1b53e6ab3a771edd6ee0a.tar.gz
MDEV-16791 mariabackup : Support DDL commands during backup
Diffstat (limited to 'extra')
-rw-r--r--extra/mariabackup/backup_copy.cc30
-rw-r--r--extra/mariabackup/backup_mysql.cc7
-rw-r--r--extra/mariabackup/datasink.c3
-rw-r--r--extra/mariabackup/fil_cur.cc38
-rw-r--r--extra/mariabackup/fil_cur.h5
-rw-r--r--extra/mariabackup/xtrabackup.cc557
6 files changed, 583 insertions, 57 deletions
diff --git a/extra/mariabackup/backup_copy.cc b/extra/mariabackup/backup_copy.cc
index 30e94153275..3a5a5bc5a2a 100644
--- a/extra/mariabackup/backup_copy.cc
+++ b/extra/mariabackup/backup_copy.cc
@@ -1388,6 +1388,30 @@ out:
return(ret);
}
+void backup_fix_ddl(void);
+
+#define LSN_PREFIX_IN_SHOW_STATUS "\nLog sequence number "
+static lsn_t get_current_lsn(MYSQL *connection) {
+ MYSQL_RES *res = xb_mysql_query(connection, "SHOW ENGINE INNODB STATUS", true, false);
+ if (!res)
+ return 0;
+ MYSQL_ROW row = mysql_fetch_row(res);
+ DBUG_ASSERT(row);
+ if (row) {
+ const char *p = strstr(row[2],LSN_PREFIX_IN_SHOW_STATUS);
+ DBUG_ASSERT(p);
+ if (p)
+ {
+ p += sizeof(LSN_PREFIX_IN_SHOW_STATUS) - 1;
+ return (lsn_t)strtoll(p, NULL, 10);
+ }
+ }
+ mysql_free_result(res);
+ return 0;
+}
+
+lsn_t server_lsn_after_lock;
+extern void backup_wait_for_lsn(lsn_t lsn);
/** Start --backup */
bool backup_start()
{
@@ -1407,6 +1431,7 @@ bool backup_start()
if (!lock_tables(mysql_connection)) {
return(false);
}
+ server_lsn_after_lock = get_current_lsn(mysql_connection);
}
if (!backup_files(fil_path_to_mysql_datadir, false)) {
@@ -1421,6 +1446,10 @@ bool backup_start()
rocksdb_create_checkpoint();
}
+ msg_ts("Waiting for log copy thread to read lsn %llu\n", (ulonglong)server_lsn_after_lock);
+ backup_wait_for_lsn(server_lsn_after_lock);
+ backup_fix_ddl();
+
// There is no need to stop slave thread before coping non-Innodb data when
// --no-lock option is used because --no-lock option requires that no DDL or
// DML to non-transaction tables can occur.
@@ -2230,6 +2259,7 @@ static void rocksdb_lock_checkpoint()
msg_ts("Could not obtain rocksdb checkpont lock\n");
exit(EXIT_FAILURE);
}
+ mysql_free_result(res);
}
static void rocksdb_unlock_checkpoint()
diff --git a/extra/mariabackup/backup_mysql.cc b/extra/mariabackup/backup_mysql.cc
index 60dfb66d2c8..efa21965965 100644
--- a/extra/mariabackup/backup_mysql.cc
+++ b/extra/mariabackup/backup_mysql.cc
@@ -1794,7 +1794,12 @@ mdl_lock_table(ulint space_id)
std::ostringstream lock_query;
lock_query << "SELECT 1 FROM " << full_table_name << " LIMIT 0";
msg_ts("Locking MDL for %s\n", full_table_name.c_str());
- xb_mysql_query(mdl_con, lock_query.str().c_str(), false, true);
+ if (mysql_query(mdl_con, lock_query.str().c_str())) {
+ msg_ts("Warning : locking MDL failed for space id %zu, name %s\n", space_id, full_table_name.c_str());
+ } else {
+ MYSQL_RES *r = mysql_store_result(mdl_con);
+ mysql_free_result(r);
+ }
}
pthread_mutex_unlock(&mdl_lock_con_mutex);
diff --git a/extra/mariabackup/datasink.c b/extra/mariabackup/datasink.c
index 1459da2fb57..cb31538726f 100644
--- a/extra/mariabackup/datasink.c
+++ b/extra/mariabackup/datasink.c
@@ -108,6 +108,9 @@ Write to a datasink file.
int
ds_write(ds_file_t *file, const void *buf, size_t len)
{
+ if (len == 0) {
+ return 0;
+ }
return file->datasink->write(file, (const uchar *)buf, len);
}
diff --git a/extra/mariabackup/fil_cur.cc b/extra/mariabackup/fil_cur.cc
index f6e6fe43085..be2e0593129 100644
--- a/extra/mariabackup/fil_cur.cc
+++ b/extra/mariabackup/fil_cur.cc
@@ -131,14 +131,15 @@ Open a source file cursor and initialize the associated read filter.
be skipped and XB_FIL_CUR_ERROR on error. */
xb_fil_cur_result_t
xb_fil_cur_open(
-/*============*/
+ /*============*/
xb_fil_cur_t* cursor, /*!< out: source file cursor */
xb_read_filt_t* read_filter, /*!< in/out: the read filter */
fil_node_t* node, /*!< in: source tablespace node */
- uint thread_n) /*!< thread number for diagnostics */
+ uint thread_n, /*!< thread number for diagnostics */
+ ulonglong max_file_size)
{
bool success;
-
+ int err;
/* Initialize these first so xb_fil_cur_close() handles them correctly
in case of error */
cursor->orig_buf = NULL;
@@ -173,7 +174,7 @@ xb_fil_cur_open(
"tablespace %s\n",
thread_n, cursor->abs_path);
- return(XB_FIL_CUR_ERROR);
+ return(XB_FIL_CUR_SKIP);
}
mutex_enter(&fil_system->mutex);
@@ -194,14 +195,31 @@ xb_fil_cur_open(
cursor->node = node;
cursor->file = node->handle;
-
- if (stat(cursor->abs_path, &cursor->statinfo)) {
- msg("[%02u] mariabackup: error: cannot stat %s\n",
+#ifdef _WIN32
+ HANDLE hDup;
+ DuplicateHandle(GetCurrentProcess(),cursor->file.m_file,
+ GetCurrentProcess(), &hDup, 0, FALSE, DUPLICATE_SAME_ACCESS);
+ int filenr = _open_osfhandle((intptr_t)hDup, 0);
+ if (filenr < 0) {
+ err = EINVAL;
+ }
+ else {
+ err = _fstat64(filenr, &cursor->statinfo);
+ close(filenr);
+ }
+#else
+ err = fstat(cursor->file.m_file, &cursor->statinfo);
+#endif
+ if (max_file_size < (ulonglong)cursor->statinfo.st_size) {
+ cursor->statinfo.st_size = (ulonglong)max_file_size;
+ }
+ if (err) {
+ msg("[%02u] mariabackup: error: cannot fstat %s\n",
thread_n, cursor->abs_path);
xb_fil_cur_close(cursor);
- return(XB_FIL_CUR_ERROR);
+ return(XB_FIL_CUR_SKIP);
}
if (srv_file_flush_method == SRV_O_DIRECT
@@ -374,7 +392,9 @@ xb_fil_cur_close(
/*=============*/
xb_fil_cur_t *cursor) /*!< in/out: source file cursor */
{
- cursor->read_filter->deinit(&cursor->read_filter_ctxt);
+ if (cursor->read_filter) {
+ cursor->read_filter->deinit(&cursor->read_filter_ctxt);
+ }
free(cursor->orig_buf);
diff --git a/extra/mariabackup/fil_cur.h b/extra/mariabackup/fil_cur.h
index e3f356a346c..4d7f7fd84d9 100644
--- a/extra/mariabackup/fil_cur.h
+++ b/extra/mariabackup/fil_cur.h
@@ -57,7 +57,7 @@ struct xb_fil_cur_t {
ulint space_size; /*!< space size in pages */
/** TODO: remove this default constructor */
- xb_fil_cur_t() : page_size(0), read_filter_ctxt() {}
+ xb_fil_cur_t() : page_size(0), read_filter(0), read_filter_ctxt() {}
/** @return whether this is not a file-per-table tablespace */
bool is_system() const
@@ -86,7 +86,8 @@ xb_fil_cur_open(
xb_fil_cur_t* cursor, /*!< out: source file cursor */
xb_read_filt_t* read_filter, /*!< in/out: the read filter */
fil_node_t* node, /*!< in: source tablespace node */
- uint thread_n); /*!< thread number for diagnostics */
+ uint thread_n, /*!< thread number for diagnostics */
+ ulonglong max_file_size = ULLONG_MAX);
/************************************************************************
Reads and verifies the next block of pages from the source
diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc
index 4545e7a6857..a7d136be820 100644
--- a/extra/mariabackup/xtrabackup.cc
+++ b/extra/mariabackup/xtrabackup.cc
@@ -343,6 +343,25 @@ const char *opt_history = NULL;
char mariabackup_exe[FN_REFLEN];
char orig_argv1[FN_REFLEN];
+pthread_mutex_t backup_mutex;
+pthread_cond_t scanned_lsn_cond;
+
+typedef std::map<space_id_t,std::string> space_id_to_name_t;
+
+struct ddl_tracker_t {
+ /** Tablspaces with their ID and name, as they were copied to backup.*/
+ space_id_to_name_t tables_in_backup;
+ /** Tablespaces for that optimized DDL without redo log was found.*/
+ std::set<space_id_t> optimized_ddl;
+ /** Drop operations found in redo log. */
+ std::set<space_id_t> drops;
+ /* For DDL operation found in redo log, */
+ space_id_to_name_t id_to_name;
+};
+const space_id_t REMOVED_SPACE_ID = ULINT_MAX;
+static ddl_tracker_t ddl_tracker;
+
+
/* Whether xtrabackup_binlog_info should be created on recovery */
static bool recover_binlog_info;
@@ -537,49 +556,79 @@ void mdl_lock_all()
mdl_lock_table(node->space->id);
}
datafiles_iter_free(it);
-
- DBUG_EXECUTE_IF("check_mdl_lock_works",
- dbug_alter_thread_done =
- dbug_start_query_thread("ALTER TABLE test.t ADD COLUMN mdl_lock_column int",
- "Waiting for table metadata lock",1, ER_QUERY_INTERRUPTED););
}
-/** Check if the space id belongs to the table which name should
-be skipped based on the --tables, --tables-file and --table-exclude
-options.
-@param[in] space_id space id to check
-@return true if the space id belongs to skip table/database list. */
-static bool backup_includes(space_id_t space_id)
-{
- datafiles_iter_t *it = datafiles_iter_new(fil_system);
- if (!it)
- return true;
- while (fil_node_t *node = datafiles_iter_next(it)){
- if (space_id == 0
- || (node->space->id == space_id
- && !check_if_skip_table(node->space->name))) {
+// Convert non-null terminated filename to space name
+std::string filename_to_spacename(const byte *filename, size_t len)
+{
+ // null- terminate filename
+ char *f = (char *)malloc(len + 1);
+ ut_a(f);
+ memcpy(f, filename, len);
+ f[len] = 0;
+ for (size_t i = 0; i < len; i++)
+ if (f[i] == '\\')
+ f[i] = '/';
+ char *p = strrchr(f, '.');
+ ut_a(p);
+ *p = 0;
+ char *table = strrchr(f, '/');
+ ut_a(table);
+ *table = 0;
+ char *db = strrchr(f, '/');
+ ut_a(db);
+ *table = '/';
+ return std::string(db+1);
+}
- msg("mariabackup: Unsupported redo log detected "
- "and it belongs to %s\n",
- space_id ? node->name: "the InnoDB system tablespace");
+/** Report an operation to create, delete, or rename a file during backup.
+@param[in] space_id tablespace identifier
+@param[in] flags tablespace flags (NULL if not create)
+@param[in] name file name (not NUL-terminated)
+@param[in] len length of name, in bytes
+@param[in] new_name new file name (NULL if not rename)
+@param[in] new_len length of new_name, in bytes (0 if NULL) */
+void backup_file_op(ulint space_id, const byte* flags,
+ const byte* name, ulint len,
+ const byte* new_name, ulint new_len)
+{
- msg("mariabackup: ALTER TABLE or OPTIMIZE TABLE "
- "was being executed during the backup.\n");
+ ut_ad(!flags || !new_name);
+ ut_ad(name);
+ ut_ad(len);
+ ut_ad(!new_name == !new_len);
+ pthread_mutex_lock(&backup_mutex);
+
+ if (flags) {
+ ddl_tracker.id_to_name[space_id] = filename_to_spacename(name, len);
+ msg("DDL tracking : create %zu \"%.*s\": %x\n",
+ space_id, int(len), name, mach_read_from_4(flags));
+ }
+ else if (new_name) {
+ ddl_tracker.id_to_name[space_id] = filename_to_spacename(new_name, new_len);
+ msg("DDL tracking : rename %zu \"%.*s\",\"%.*s\"\n",
+ space_id, int(len), name, int(new_len), new_name);
+ } else {
+ ddl_tracker.drops.insert(space_id);
+ msg("DDL tracking : delete %zu \"%.*s\"\n", space_id, int(len), name);
+ }
+ pthread_mutex_unlock(&backup_mutex);
+}
- if (!opt_lock_ddl_per_table) {
- msg("mariabackup: Use --lock-ddl-per-table "
- "parameter to lock all the table before "
- "backup operation.\n");
- }
- datafiles_iter_free(it);
- return false;
- }
- }
+/** Callback whenever MLOG_INDEX_LOAD happens.
+@param[in] space_id space id to check
+@return false */
+void backup_optimized_ddl_op(ulint space_id)
+{
+ // TODO : handle incremental
+ if (xtrabackup_incremental)
+ return;
- datafiles_iter_free(it);
- return true;
+ pthread_mutex_lock(&backup_mutex);
+ ddl_tracker.optimized_ddl.insert(space_id);
+ pthread_mutex_unlock(&backup_mutex);
}
/* ======== Date copying thread context ======== */
@@ -2336,7 +2385,7 @@ xb_get_copy_action(const char *dflt)
static
my_bool
-xtrabackup_copy_datafile(fil_node_t* node, uint thread_n)
+xtrabackup_copy_datafile(fil_node_t* node, uint thread_n, const char *dest_name=0, ulonglong max_size=ULLONG_MAX)
{
char dst_name[FN_REFLEN];
ds_file_t *dstfile = NULL;
@@ -2366,20 +2415,31 @@ xtrabackup_copy_datafile(fil_node_t* node, uint thread_n)
return(FALSE);
}
+ bool was_dropped;
+ pthread_mutex_lock(&backup_mutex);
+ was_dropped = (ddl_tracker.drops.find(node->space->id) != ddl_tracker.drops.end());
+ pthread_mutex_unlock(&backup_mutex);
+ if (was_dropped) {
+ fil_space_close(node->space->name);
+ goto skip;
+ }
+
if (!changed_page_bitmap) {
read_filter = &rf_pass_through;
}
else {
read_filter = &rf_bitmap;
}
- res = xb_fil_cur_open(&cursor, read_filter, node, thread_n);
+
+ res = xb_fil_cur_open(&cursor, read_filter, node, thread_n,max_size);
if (res == XB_FIL_CUR_SKIP) {
goto skip;
} else if (res == XB_FIL_CUR_ERROR) {
goto error;
}
- strncpy(dst_name, cursor.rel_path, sizeof(dst_name));
+ strncpy(dst_name, (dest_name)?dest_name : cursor.rel_path, sizeof(dst_name));
+
/* Setup the page write filter */
if (xtrabackup_incremental) {
@@ -2431,6 +2491,10 @@ xtrabackup_copy_datafile(fil_node_t* node, uint thread_n)
goto error;
}
+ pthread_mutex_lock(&backup_mutex);
+ ddl_tracker.tables_in_backup[node->space->id] = node_name;
+ pthread_mutex_unlock(&backup_mutex);
+
/* close */
msg_ts("[%02u] ...done\n", thread_n);
xb_fil_cur_close(&cursor);
@@ -2595,7 +2659,7 @@ static bool xtrabackup_copy_logfile(bool last = false)
if (!start_lsn) {
msg("mariabackup: Error: xtrabackup_copy_logfile()"
" failed.\n");
- return(true);
+ exit(EXIT_FAILURE);
}
} while (start_lsn == end_lsn);
@@ -2604,12 +2668,30 @@ static bool xtrabackup_copy_logfile(bool last = false)
msg_ts(">> log scanned up to (" LSN_PF ")\n", start_lsn);
/* update global variable*/
+ pthread_mutex_lock(&backup_mutex);
log_copy_scanned_lsn = start_lsn;
+ pthread_cond_broadcast(&scanned_lsn_cond);
+ pthread_mutex_unlock(&backup_mutex);
debug_sync_point("xtrabackup_copy_logfile_pause");
return(false);
}
+/**
+Wait until redo log copying thread processes given lsn
+*/
+void backup_wait_for_lsn(lsn_t lsn) {
+ bool completed = false;
+ pthread_mutex_lock(&backup_mutex);
+ do {
+ pthread_cond_wait(&scanned_lsn_cond, &backup_mutex);
+ completed = log_copy_scanned_lsn >= lsn;
+ } while (!completed);
+ pthread_mutex_unlock(&backup_mutex);
+}
+
+extern lsn_t server_lsn_after_lock;
+
static os_thread_ret_t log_copying_thread(void*)
{
/*
@@ -2666,6 +2748,42 @@ static os_thread_ret_t io_watching_thread(void*)
return(0);
}
+#ifndef DBUG_OFF
+/*
+In debug mode, execute SQL statement that was passed via environment.
+To use this facility, you need to
+
+1. Add code DBUG_EXECUTE_MARIABACKUP_EVENT("my_event_name", key););
+ to the code. key is usually a table name
+2. Set environment variable my_event_name_$key SQL statement you want to execute
+ when event occurs, in DBUG_EXECUTE_IF from above.
+ In mtr , you can set environment via 'let' statement (do not use $ as the first char
+ for the variable)
+3. start mariabackup with --dbug=+d,debug_mariabackup_events
+*/
+static void dbug_mariabackup_event(const char *event,const char *key)
+{
+ char envvar[FN_REFLEN];
+ if (key) {
+ snprintf(envvar, sizeof(envvar), "%s_%s", event, key);
+ char *slash = strchr(envvar, '/');
+ if (slash)
+ *slash = '_';
+ } else {
+ strncpy(envvar, event, sizeof(envvar));
+ }
+ char *sql = getenv(envvar);
+ if (sql) {
+ msg("dbug_mariabackup_event : executing '%s'\n", sql);
+ xb_mysql_query(mysql_connection, sql, false, true);
+ }
+
+}
+#define DBUG_MARIABACKUP_EVENT(A, B) DBUG_EXECUTE_IF("mariabackup_events", dbug_mariabackup_event(A,B););
+#else
+#define DBUG_MARIABACKUP_EVENT(A,B)
+#endif
+
/**************************************************************************
Datafiles copying thread.*/
static
@@ -2688,12 +2806,18 @@ data_copy_thread_func(
while ((node = datafiles_iter_next(ctxt->it)) != NULL) {
+ DBUG_MARIABACKUP_EVENT("before_copy", node->space->name);
+
+
/* copy the datafile */
if(xtrabackup_copy_datafile(node, num)) {
msg("[%02u] mariabackup: Error: "
"failed to copy datafile.\n", num);
exit(EXIT_FAILURE);
}
+
+ DBUG_MARIABACKUP_EVENT("after_copy", node->space->name);
+
}
pthread_mutex_lock(&ctxt->count_mutex);
@@ -2866,6 +2990,7 @@ xb_load_single_table_tablespace(
Datafile *file = xb_new_datafile(name, is_remote);
if (file->open_read_only(true) != DB_SUCCESS) {
+ msg("Can't open datafile %s\n", name);
ut_free(name);
exit(EXIT_FAILURE);
}
@@ -3182,7 +3307,7 @@ xb_load_tablespaces()
}
debug_sync_point("xtrabackup_load_tablespaces_pause");
-
+ DBUG_MARIABACKUP_EVENT("after_load_tablespaces", 0);
return(DB_SUCCESS);
}
@@ -3791,6 +3916,8 @@ xtrabackup_backup_func()
uint count;
pthread_mutex_t count_mutex;
data_thread_ctxt_t *data_threads;
+ pthread_mutex_init(&backup_mutex, NULL);
+ pthread_cond_init(&scanned_lsn_cond, NULL);
#ifdef USE_POSIX_FADVISE
msg("mariabackup: uses posix_fadvise().\n");
@@ -3817,6 +3944,7 @@ xtrabackup_backup_func()
srv_read_only_mode = TRUE;
srv_operation = SRV_OPERATION_BACKUP;
+ log_file_op = backup_file_op;
metadata_to_lsn = 0;
if (xb_close_files)
@@ -3830,6 +3958,7 @@ xtrabackup_backup_func()
fail:
metadata_to_lsn = log_copying_running;
stop_backup_threads();
+ log_file_op = NULL;
if (dst_log_file) {
ds_close(dst_log_file);
dst_log_file = NULL;
@@ -4116,6 +4245,7 @@ fail_before_log_copying_thread_start:
goto fail_before_log_copying_thread_start;
log_copying_stop = os_event_create(0);
+ log_optimized_ddl_op = backup_optimized_ddl_op;
os_thread_create(log_copying_thread, NULL, &log_copying_thread_id);
/* FLUSH CHANGED_PAGE_BITMAPS call */
@@ -4147,6 +4277,11 @@ fail_before_log_copying_thread_start:
if (opt_lock_ddl_per_table) {
mdl_lock_all();
+
+ DBUG_EXECUTE_IF("check_mdl_lock_works",
+ dbug_alter_thread_done =
+ dbug_start_query_thread("ALTER TABLE test.t ADD COLUMN mdl_lock_column int",
+ "Waiting for table metadata lock", 1, ER_QUERY_INTERRUPTED););
}
it = datafiles_iter_new(fil_system);
@@ -4184,10 +4319,6 @@ fail_before_log_copying_thread_start:
pthread_mutex_destroy(&count_mutex);
free(data_threads);
datafiles_iter_free(it);
-
- if (changed_page_bitmap) {
- xb_page_bitmap_deinit(changed_page_bitmap);
- }
}
bool ok = backup_start();
@@ -4211,6 +4342,9 @@ fail_before_log_copying_thread_start:
goto fail;
}
+ if (changed_page_bitmap) {
+ xb_page_bitmap_deinit(changed_page_bitmap);
+ }
xtrabackup_destroy_datasinks();
msg("mariabackup: Redo log (from LSN " LSN_PF " to " LSN_PF
@@ -4228,9 +4362,178 @@ fail_before_log_copying_thread_start:
}
innodb_shutdown();
+ log_file_op = NULL;
+ pthread_mutex_destroy(&backup_mutex);
+ pthread_cond_destroy(&scanned_lsn_cond);
return(true);
}
+
+/**
+This function handles DDL changes at the end of backup, under protection of
+FTWRL. This ensures consistent backup in presence of DDL.
+
+- New tables, that were created during backup, are now copied into backup.
+ Also, tablespaces with optimized (no redo loggin DDL) are re-copied into
+ backup. This tablespaces will get the extension ".new" in the backup
+
+- Tables that were renamed during backup, are marked as renamed
+ For these, file <old_name>.ren will be created.
+ The content of the file is the new tablespace name.
+
+- Tables that were deleted during backup, are marked as deleted
+ For these , an empty file <name>.del will be created
+
+ It is the responsibility of the prepare phase to deal with .new, .ren, and .del
+ files.
+*/
+void backup_fix_ddl(void)
+{
+ std::set<std::string> new_tables;
+ std::set<std::string> dropped_tables;
+ std::map<std::string, std::string> renamed_tables;
+
+ for (space_id_to_name_t::iterator iter = ddl_tracker.tables_in_backup.begin();
+ iter != ddl_tracker.tables_in_backup.end();
+ iter++) {
+
+ const std::string name = iter->second;
+ ulint id = iter->first;
+
+ if (ddl_tracker.drops.find(id) != ddl_tracker.drops.end()) {
+ dropped_tables.insert(name);
+ continue;
+ }
+
+ bool has_optimized_ddl =
+ ddl_tracker.optimized_ddl.find(id) != ddl_tracker.optimized_ddl.end();
+
+ if (ddl_tracker.id_to_name.find(id) == ddl_tracker.id_to_name.end()) {
+ if (has_optimized_ddl) {
+ new_tables.insert(name);
+ }
+ continue;
+ }
+
+ /* tablespace was affected by DDL. */
+ const std::string new_name = ddl_tracker.id_to_name[id];
+ if (new_name != name) {
+ if (has_optimized_ddl) {
+ /* table was renamed, but we need a full copy
+ of it because of optimized DDL. We emulate a drop/create.*/
+ dropped_tables.insert(name);
+ new_tables.insert(new_name);
+ } else {
+ /* Renamed, and no optimized DDL*/
+ renamed_tables[name] = new_name;
+ }
+ } else if (has_optimized_ddl) {
+ /* Table was recreated, or optimized DDL ran.
+ In both cases we need a full copy in the backup.*/
+ new_tables.insert(name);
+ }
+ }
+
+ /* Find tables that were created during backup (and not removed).*/
+ for(space_id_to_name_t::iterator iter = ddl_tracker.id_to_name.begin();
+ iter != ddl_tracker.id_to_name.end();
+ iter++) {
+
+ ulint id = iter->first;
+ std::string name = iter->second;
+
+ if (ddl_tracker.tables_in_backup.find(id) != ddl_tracker.tables_in_backup.end()) {
+ /* already processed above */
+ continue;
+ }
+
+ if (ddl_tracker.drops.find(id) == ddl_tracker.drops.end()) {
+ dropped_tables.erase(name);
+ new_tables.insert(name);
+ }
+ }
+
+ // Mark tablespaces for rename
+ for (std::map<std::string, std::string>::iterator iter = renamed_tables.begin();
+ iter != renamed_tables.end(); ++iter) {
+ const std::string old_name = iter->first;
+ std::string new_name = iter->second;
+ backup_file_printf((old_name + ".ren").c_str(), "%s", new_name.c_str());
+ }
+
+ // Mark tablespaces for drop
+ for (std::set<std::string>::iterator iter = dropped_tables.begin();
+ iter != dropped_tables.end();
+ iter++) {
+ const std::string name(*iter);
+ backup_file_printf((name + ".del").c_str(), "%s", "");
+ }
+
+ // Load and copy new tables.
+ // Close all datanodes first, reload only new tables.
+ std::vector<fil_node_t *> all_nodes;
+ datafiles_iter_t *it = datafiles_iter_new(fil_system);
+ if (!it)
+ return;
+ while (fil_node_t *node = datafiles_iter_next(it)) {
+ all_nodes.push_back(node);
+ }
+ for (size_t i = 0; i < all_nodes.size(); i++) {
+ fil_node_t *n = all_nodes[i];
+ if (n->space->id == 0)
+ continue;
+ fil_space_close(n->space->name);
+ fil_space_free(n->space->id, false);
+ }
+
+
+ for (std::set<std::string>::iterator iter = new_tables.begin();
+ iter != new_tables.end(); iter++) {
+ const char *space_name = iter->c_str();
+ if (check_if_skip_table(space_name))
+ continue;
+ std::string name(*iter);
+ bool is_remote = access((name + ".ibd").c_str(), R_OK) != 0;
+ const char *extension = is_remote ? ".isl" : ".ibd";
+ name.append(extension);
+ char buf[FN_REFLEN];
+ strncpy(buf, name.c_str(), sizeof(buf));
+ const char *dbname = buf;
+ char *p = strchr(buf, '/');
+ if (p == 0) {
+ msg("Unexpected tablespace %s filename %s\n", space_name, name.c_str());
+ ut_a(0);
+ }
+ ut_a(p);
+ *p = 0;
+ const char *tablename = p + 1;
+ xb_load_single_table_tablespace(dbname, tablename, is_remote);
+ }
+
+ it = datafiles_iter_new(fil_system);
+ if (!it)
+ return;
+
+ while (fil_node_t *node = datafiles_iter_next(it)) {
+ fil_space_t * space = node->space;
+ if (!fil_is_user_tablespace_id(space->id))
+ continue;
+ std::string dest_name(node->space->name);
+ dest_name.append(".new");
+#if 0
+ bool do_full_copy = ddl_tracker.optimized_ddl.find(n->space->id) != ddl_tracker.optimized_ddl.end();
+ if (do_full_copy) {
+ msg(
+ "Performing a full copy of the tablespace %s, because optimized (without redo logging) DDL operation"
+ "ran during backup. You can use set innodb_log_optimize_ddl=OFF to improve backup performance"
+ "in the future.\n",
+ n->space->name);
+ }
+#endif
+ xtrabackup_copy_datafile(node, 0, dest_name.c_str()/*, do_full_copy ? ULONGLONG_MAX:UNIV_PAGE_SIZE */);
+ }
+}
+
/* ================= prepare ================= */
/***********************************************************************
@@ -4739,6 +5042,27 @@ error:
return FALSE;
}
+
+std::string change_extension(std::string filename, std::string new_ext) {
+ DBUG_ASSERT(new_ext.size() == 3);
+ std::string new_name(filename);
+ new_name.resize(new_name.size() - new_ext.size());
+ new_name.append(new_ext);
+ return new_name;
+}
+
+
+static void rename_file(const char *from,const char *to) {
+ msg("Renaming %s to %s\n", from, to);
+ if (my_rename(from, to, MY_WME)) {
+ msg("Cannot rename %s to %s errno %d", from, to, errno);
+ exit(EXIT_FAILURE);
+ }
+}
+
+static void rename_file(const std::string& from, const std::string &to) {
+ rename_file(from.c_str(), to.c_str());
+}
/************************************************************************
Callback to handle datadir entry. Function of this type will be called
for each entry which matches the mask by xb_process_datadir.
@@ -4750,6 +5074,37 @@ typedef ibool (*handle_datadir_entry_func_t)(
const char* file_name, /*!<in: file name with suffix */
void* arg); /*!<in: caller-provided data */
+/** Rename, and replace destination file, if exists */
+static void rename_force(const char *from, const char *to) {
+ if (access(to, R_OK) == 0) {
+ msg("Removing %s\n", to);
+ if (my_delete(to, MYF(MY_WME))) {
+ msg("Can't remove %s, errno %d", to, errno);
+ exit(EXIT_FAILURE);
+ }
+ }
+ rename_file(from,to);
+}
+
+/* During prepare phase, rename ".new" files , that were created in backup_fix_ddl(),
+ to ".ibd".*/
+static ibool prepare_handle_new_files(
+ const char* data_home_dir, /*!<in: path to datadir */
+ const char* db_name, /*!<in: database name */
+ const char* file_name, /*!<in: file name with suffix */
+ void *)
+{
+
+ std::string src_path = std::string(data_home_dir) + '/' + std::string(db_name) + '/' + file_name;
+ std::string dest_path = src_path;
+
+ size_t index = dest_path.find(".new");
+ DBUG_ASSERT(index != std::string::npos);
+ dest_path.replace(index, 4, ".ibd");
+ rename_force(src_path.c_str(),dest_path.c_str());
+ return TRUE;
+}
+
/************************************************************************
Callback to handle datadir entry. Deletes entry if it has no matching
fil_space in fil_system directory.
@@ -4955,6 +5310,103 @@ store_binlog_info(const char* filename, const char* name, ulonglong pos)
return(true);
}
+/** Check if file exists*/
+static bool file_exists(std::string name)
+{
+ return access(name.c_str(), R_OK) == 0 ;
+}
+
+/** Read file content into STL string */
+static std::string read_file_as_string(const std::string file) {
+ char content[FN_REFLEN];
+ FILE *f = fopen(file.c_str(), "r");
+ if (!f) {
+ msg("Can not open %s\n", file.c_str());
+ }
+ size_t len = fread(content, 1, FN_REFLEN, f);
+ fclose(f);
+ return std::string(content, len);
+}
+
+/** Delete file- Provide verbose diagnostics and exit, if operation fails. */
+static void delete_file(const std::string& file, bool if_exists = false) {
+ if (if_exists && !file_exists(file))
+ return;
+ if (my_delete(file.c_str(), MYF(MY_WME))) {
+ msg("Can't remove %s, errno %d", file.c_str(), errno);
+ exit(EXIT_FAILURE);
+ }
+}
+
+/**
+Rename tablespace during prepare.
+Backup in its end phase may generate some .ren files, recording
+tablespaces that should be renamed in --prepare.
+*/
+static void rename_table_in_prepare(const std::string &datadir, const std::string& from , const std::string& to,
+ const char *extension=0) {
+ if (!extension) {
+ static const char *extensions_nonincremental[] = { ".ibd", 0 };
+ static const char *extensions_incremental[] = { ".ibd.delta", ".ibd.meta", 0 };
+ const char **extensions = xtrabackup_incremental_dir ?
+ extensions_incremental : extensions_nonincremental;
+ for (size_t i = 0; extensions[i]; i++) {
+ rename_table_in_prepare(datadir, from, to, extensions[i]);
+ }
+ return;
+ }
+ std::string src = std::string(datadir) + "/" + from + extension;
+ std::string dest = std::string(datadir) + "/" + to + extension;
+ std::string ren2, tmp;
+ if (file_exists(dest)) {
+ ren2= std::string(datadir) + "/" + to + ".ren";
+ if (!file_exists(ren2)) {
+ msg("ERROR : File %s was not found, but expected during rename processing\n", ren2.c_str());
+ ut_a(0);
+ }
+ tmp = to + "#";
+ rename_table_in_prepare(datadir, to, tmp);
+ }
+ rename_file(src, dest);
+ if (ren2.size()) {
+ // Make sure the temp. renamed file is processed.
+ std::string to2 = read_file_as_string(ren2);
+ rename_table_in_prepare(datadir, tmp, to2);
+ delete_file(ren2);
+ }
+}
+
+static ibool prepare_handle_ren_files(const char *datadir, const char *db, const char *filename, void *) {
+
+ std::string ren_file = std::string(datadir) + "/" + db + "/" + filename;
+ if (!file_exists(ren_file))
+ return TRUE;
+
+ std::string to = read_file_as_string(ren_file);
+ std::string source_space_name = std::string(db) + "/" + filename;
+ source_space_name.resize(source_space_name.size() - 4); // remove extension
+
+ rename_table_in_prepare(datadir, source_space_name.c_str(), to.c_str());
+ delete_file(ren_file);
+ return TRUE;
+}
+
+/* Remove tablespaces during backup, based on */
+static ibool prepare_handle_del_files(const char *datadir, const char *db, const char *filename, void *) {
+ std::string del_file = std::string(datadir) + "/" + db + "/" + filename;
+ std::string path(del_file);
+ path.resize(path.size() - 4); // remove extension;
+ if (xtrabackup_incremental) {
+ delete_file(path + ".ibd.delta", true);
+ delete_file(path + ".ibd.meta", true);
+ }
+ else {
+ delete_file(path + ".ibd", true);
+ }
+ delete_file(del_file);
+ return TRUE;
+}
+
/** Implement --prepare
@return whether the operation succeeded */
static bool
@@ -4972,6 +5424,21 @@ xtrabackup_prepare_func(char** argv)
}
msg("mariabackup: cd to %s\n", xtrabackup_real_target_dir);
+ fil_path_to_mysql_datadir = ".";
+
+ if (xtrabackup_incremental_dir) {
+ xb_process_datadir(xtrabackup_incremental_dir, ".new.meta", prepare_handle_new_files);
+ xb_process_datadir(xtrabackup_incremental_dir, ".new.delta", prepare_handle_new_files);
+ }
+ else {
+ xb_process_datadir(".", ".new", prepare_handle_new_files);
+ }
+ xb_process_datadir(xtrabackup_incremental_dir? xtrabackup_incremental_dir:".",
+ ".ren", prepare_handle_ren_files);
+ xb_process_datadir(xtrabackup_incremental_dir ? xtrabackup_incremental_dir : ".",
+ ".del", prepare_handle_del_files);
+
+
int argc; for (argc = 0; argv[argc]; argc++) {}
encryption_plugin_prepare_init(argc, argv);
@@ -5334,7 +5801,7 @@ handle_options(int argc, char **argv, char ***argv_client, char ***argv_server)
srv_operation = SRV_OPERATION_RESTORE;
files_charset_info = &my_charset_utf8_general_ci;
- check_if_backup_includes = backup_includes;
+
setup_error_messages();
sys_var_init();