char *xtrabackup_incremental_basedir; /* for --backup */ char *xtrabackup_extra_lsndir; /* for --backup with --extra-lsndir */ char *xtrabackup_incremental_dir; /* for --prepare */
/** whether log_copying_thread() is active; protected by recv_sys.mutex */ staticbool log_copying_running; /** the log parsing function for --backup */ static recv_sys_t::parser backup_log_parse_low; /** for --backup, target LSN to copy the log to; protected by recv_sys.mutex */
lsn_t metadata_to_lsn;
/* String buffer used by --print-param to accumulate server options as they are
parsed from the defaults file */ static std::ostringstream print_param_str;
/* Set of specified parameters */
std::set<std::string> param_set;
struct ddl_tracker_t { /** Tablespaces with their ID and name, as they were copied to backup.*/
space_id_to_name_t tables_in_backup; /** 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; /** Deferred tablespaces with their ID and name which was
found in redo log of DDL operations */
space_id_to_name_t deferred_tables;
/** Insert the deferred tablespace id with the name */ void insert_defer_id(space_id_t space_id, std::string name)
{ auto it= defer_space_names.find(name); if (it != defer_space_names.end())
{
deferred_tables[space_id]= name;
defer_space_names.erase(it);
}
}
/** Rename the deferred tablespace with new name */ void rename_defer(space_id_t space_id, std::string old_name,
std::string new_name)
{ if (deferred_tables.find(space_id) != deferred_tables.end())
deferred_tables[space_id] = new_name; auto defer_end= defer_space_names.end(); auto defer= defer_space_names.find(old_name); if (defer == defer_end)
defer= defer_space_names.find(new_name);
if (defer != defer_end)
{
deferred_tables[space_id]= new_name;
defer_space_names.erase(defer);
}
}
/** Store the space ids of truncated undo log tablespaces. Protected
by recv_sys.mutex */ static std::set<uint32_t> undo_trunc_ids;
/** Stores the space ids of page0 INIT_PAGE redo records. It is usedtoindicatewhetherthegivendeferredtablespacecan
be reconstructed. */ static std::set<space_id_t> first_page_init_ids;
// Convert non-null terminated filename to space name static std::string filename_to_spacename(constvoid *filename, size_t len);
ut_a(!pthread_mutex_lock(&m_mutex)); for (container_t::const_iterator space_it= m_spaces.begin();
space_it != m_spaces.end(); ++space_it)
{
uint32_t space_id = space_it->first; const std::string &space_name = space_it->second.space_name; // There is no need to close tablespaces explicitly as they will be closed // in innodb_shutdown().
xb_load_single_table_tablespace(space_name, false);
fil_space_t *space = fil_space_t::get(space_id); if (!space)
die("Can't find space object for space name %s to check corrupted page",
space_name.c_str());
mtr_t mtr{nullptr}; for (std::set<unsigned>::const_iterator page_it=
space_it->second.pages.begin();
page_it != space_it->second.pages.end(); ++page_it)
{ if (fseg_page_is_allocated(&mtr, space, *page_it))
{
space_info_t &space_info = non_free_pages[space_id];
space_info.pages.insert(*page_it); if (space_info.space_name.empty())
space_info.space_name = space_name;
msg("Error: corrupted page " UINT32PF " of tablespace %s can not be fixed",
*page_it, space_name.c_str());
} else
{
space->reacquire(); auto err= space
->io(IORequest(IORequest::PUNCH_RANGE),
*page_it * srv_page_size, srv_page_size, zero_page)
.err; if (err != DB_SUCCESS)
die("Can't zero out corrupted page " UINT32PF " of tablespace %s",
*page_it, space_name.c_str());
msg("Corrupted page " UINT32PF " of tablespace %s was successfully fixed.",
*page_it, space_name.c_str());
}
}
space->flush<true>();
space->release();
}
m_spaces.swap(non_free_pages);
ut_a(!pthread_mutex_unlock(&m_mutex));
aligned_free(zero_page);
}
while (fil_node_t *node= datafiles_iter_next(&it))
{ constauto id= node->space->id; if (constchar *name= (fil_is_user_tablespace_id(id) &&
node->space->chain.start)
? node->space->chain.start->name : nullptr) if (check_if_skip_table(filename_to_spacename(name,
strlen(name)).c_str())) continue;
mdl_lock_table(id);
}
}
// Convert non-null terminated filename to space name // Note that in 10.6 the filename may be an undo file name static std::string filename_to_spacename(constvoid *filename, size_t len)
{ char f[FN_REFLEN]; char *p= 0, *table, *db;
DBUG_ASSERT(len < FN_REFLEN);
strmake(f, (constchar*) filename, len);
#ifdef _WIN32 for (size_t i = 0; i < len; i++)
{ if (f[i] == '\\')
f[i] = '/';
} #endif
/* Remove extension, if exists */ if (!(p= strrchr(f, '.'))) goto err;
*p= 0;
/* Find table name */ if (!(table= strrchr(f, '/'))) goto err;
*table = 0;
err: /* Not a database/table. Return original (converted) name */ if (p)
*p= '.'; // Restore removed extension
std::string s(f); return s;
}
/** Report an operation to create, delete, or rename a file during backup. @param[in]space_idtablespaceidentifier @param[in]typeredologfileoperationtype @param[in]namefilename(notNUL-terminated) @param[in]lenlengthofname,inbytes @param[in]new_namenewfilename(NULLifnotrename)
@param[in] new_len length of new_name, in bytes (0 if NULL) */ staticvoid backup_file_op(uint32_t space_id, int type, const byte* name, ulint len, const byte* new_name, ulint new_len)
{
/** Check whether the spacename belongs to internal FTS table @paramspace_namespacenametobechecked
@return true if it is fts table or false otherwise */ staticbool check_if_fts_table(constchar *space_name) { /* There are two types of FTS internal table 1)FTScommontables(FTS_<space_id>_<fts_common_tables>
2) FTS INDEX auxiliary table (FTS_<space_id>_<index_id>_<aux_table> */ constchar *table_name_start = strrchr(space_name, '/'); if (table_name_start)
++table_name_start; else
table_name_start = space_name;
constchar *table_name_end = strrchr(table_name_start, '.'); if (!table_name_end)
table_name_end =
table_name_start + strlen(table_name_start) - 1; if (!starts_with(table_name_start,"FTS_")) returnfalse;
/* Compare only common tables */ for (constchar **suffix = fts_common_tables; *suffix; ++suffix) { if (!strncmp(table_name_suffix, *suffix, table_name_len)) returntrue;
}
/* Skip index_id on fts table name */
table_name_suffix = strchr(table_name_suffix, '_'); if (!table_name_suffix ||
table_name_suffix == table_name_end) { returnfalse;
}
table_name_suffix++;
table_name_len = table_name_end - table_name_suffix; for (size_t i = 0; fts_index_selector[i].suffix; ++i) if (!strncmp(table_name_suffix, fts_index_selector[i].suffix,
table_name_len)) returntrue; returnfalse;
}
Wewillabortbackupinthiscase.
*/ staticvoid backup_file_op_fail(uint32_t space_id, int type, const byte* name, ulint len, const byte* new_name, ulint new_len)
{ constchar *error= ""; bool fail = false; const std::string spacename{filename_to_spacename(name, len)}; switch (type) { case FILE_CREATE:
msg("DDL tracking : create %" PRIu32 " \"%.*s\"",
space_id, int(len), name);
fail = !check_if_skip_table(spacename.c_str()); if (fail && !opt_no_lock &&
check_if_fts_table(spacename.c_str())) { /* Ignore the FTS internal table because InnoDB does createintermediatetableandtheirassociativeFTS internaltablewhentableisbeingrebuiltduring preparephase.Also,backup_set_alter_copy_lock() downgradestheMDL_BACKUP_DDLbeforepreparephase ofalter.ThisleadstotheFTSinternaltablebeing createdinthelatephaseofbackup. mariabackup--prepareshouldbeabletohandle
this case. */
fail = false;
}
error= "create"; break; case FILE_MODIFY: break; case FILE_RENAME:
msg("DDL tracking : rename %" PRIu32 " \"%.*s\",\"%.*s\"",
space_id, int(len), name, int(new_len), new_name);
fail = !check_if_skip_table(spacename.c_str())
|| !check_if_skip_table(
filename_to_spacename(new_name, new_len).c_str());
error= "rename"; break; case FILE_DELETE:
fail = !check_if_skip_table(spacename.c_str()) /* Ignore the FTS internal table because InnoDB may dropintermediatetableandtheirassociativeFTS internaltableasapartofinplacerollbackoperation. backup_set_alter_copy_lock()downgradesthe
MDL_BACKUP_DDL before inplace phase of alter */
&& !check_if_fts_table(spacename.c_str());
msg("DDL tracking : delete %" PRIu32 " \"%.*s\"",
space_id, int(len), name);
error= "delete"; break; default:
ut_ad(0); break;
}
if (fail) { if (opt_no_lock)
die("DDL operation detected in the late phase of backup while " "executing %s on %s. " "Backup is inconsistent. Remove --no-lock option to fix.",
error, name);
die("Unexpected DDL operation detected in the late phase of backup " "while executing %s on %s. Backup is inconsistent.",
error, name);
}
}
staticvoid backup_undo_trunc(uint32_t space_id)
{ if (space_id)
undo_trunc_ids.insert(space_id);
}
/* Function to store the space id of page0 INIT_PAGE
@param space_id space id which has page0 init page */ staticvoid backup_first_page_op(uint32_t space_id)
{
first_page_init_ids.insert(space_id);
}
struct my_option xb_client_options[]= {
{"verbose", 'V', "display verbose output", (G_PTR *) &verbose,
(G_PTR *) &verbose, 0, GET_BOOL, NO_ARG, FALSE, 0, 0, 0, 0, 0},
{"version", 'v', "print version information",
(G_PTR *) &xtrabackup_version, (G_PTR *) &xtrabackup_version, 0, GET_BOOL,
NO_ARG, 0, 0, 0, 0, 0, 0},
{"target-dir", OPT_XTRA_TARGET_DIR, "destination directory",
(G_PTR *) &xtrabackup_target_dir, (G_PTR *) &xtrabackup_target_dir, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"backup", OPT_XTRA_BACKUP, "take backup to target-dir",
(G_PTR *) &xtrabackup_backup, (G_PTR *) &xtrabackup_backup, 0, GET_BOOL,
NO_ARG, 0, 0, 0, 0, 0, 0},
{"prepare", OPT_XTRA_PREPARE, "prepare a backup for starting mysql server on the backup.",
(G_PTR *) &xtrabackup_prepare, (G_PTR *) &xtrabackup_prepare, 0, GET_BOOL,
NO_ARG, 0, 0, 0, 0, 0, 0},
{"export", OPT_XTRA_EXPORT, "create files to import to another database when prepare.",
(G_PTR *) &xtrabackup_export, (G_PTR *) &xtrabackup_export, 0, GET_BOOL,
NO_ARG, 0, 0, 0, 0, 0, 0},
{"print-param", OPT_XTRA_PRINT_PARAM, "print parameter of mysqld needed for copyback.",
(G_PTR *) &xtrabackup_print_param, (G_PTR *) &xtrabackup_print_param, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"use-memory", OPT_XTRA_USE_MEMORY, "The value is used in place of innodb_buffer_pool_size. " "This option is only relevant when the --prepare option is specified.",
(G_PTR *) &xtrabackup_use_memory, (G_PTR *) &xtrabackup_use_memory, 0,
GET_ULL, REQUIRED_ARG, 96 << 20, innodb_buffer_pool_extent_size,
size_t(-ssize_t(innodb_buffer_pool_extent_size)), 0, innodb_buffer_pool_extent_size, 0},
{"throttle", OPT_XTRA_THROTTLE, "limit count of IO operations (pairs of read&write) per second to IOS " "values (for '--backup')",
(G_PTR *) &xtrabackup_throttle, (G_PTR *) &xtrabackup_throttle, 0,
GET_LONG, REQUIRED_ARG, 0, 0, LONG_MAX, 0, 1, 0},
{"log", OPT_LOG, "Ignored option for MySQL option compatibility",
(G_PTR *) &log_ignored_opt, (G_PTR *) &log_ignored_opt, 0, GET_STR,
OPT_ARG, 0, 0, 0, 0, 0, 0},
{"log-copy-interval", OPT_XTRA_LOG_COPY_INTERVAL, "time interval between checks done by log copying thread in milliseconds " "(default is 1 second).",
(G_PTR *) &xtrabackup_log_copy_interval,
(G_PTR *) &xtrabackup_log_copy_interval, 0, GET_LONG, REQUIRED_ARG, 1000, 0, LONG_MAX, 0, 1, 0},
{"extra-lsndir", OPT_XTRA_EXTRA_LSNDIR, "(for --backup): save an extra copy of the " MB_METADATA_FILENAME " file in this directory.",
(G_PTR *) &xtrabackup_extra_lsndir, (G_PTR *) &xtrabackup_extra_lsndir, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"incremental-lsn", OPT_XTRA_INCREMENTAL, "(for --backup): copy only .ibd pages newer than specified LSN " "'high:low'. ##ATTENTION##: If a wrong LSN value is specified, it is " "impossible to diagnose this, causing the backup to be unusable. Be " "careful!",
(G_PTR *) &xtrabackup_incremental, (G_PTR *) &xtrabackup_incremental, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"incremental-basedir", OPT_XTRA_INCREMENTAL_BASEDIR, "(for --backup): copy only .ibd pages newer than backup at specified " "directory.",
(G_PTR *) &xtrabackup_incremental_basedir,
(G_PTR *) &xtrabackup_incremental_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"incremental-dir", OPT_XTRA_INCREMENTAL_DIR, "(for --prepare): apply .delta files and logfile in the specified " "directory.",
(G_PTR *) &xtrabackup_incremental_dir,
(G_PTR *) &xtrabackup_incremental_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"tables", OPT_XTRA_TABLES, "filtering by regexp for table names.",
(G_PTR *) &xtrabackup_tables, (G_PTR *) &xtrabackup_tables, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"tables_file", OPT_XTRA_TABLES_FILE, "filtering by list of the exact database.table name in the file.",
(G_PTR *) &xtrabackup_tables_file, (G_PTR *) &xtrabackup_tables_file, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"databases", OPT_XTRA_DATABASES, "filtering by list of databases.",
(G_PTR *) &xtrabackup_databases, (G_PTR *) &xtrabackup_databases, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"databases_file", OPT_XTRA_DATABASES_FILE, "filtering by list of databases in the file.",
(G_PTR *) &xtrabackup_databases_file,
(G_PTR *) &xtrabackup_databases_file, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"tables-exclude", OPT_XTRA_TABLES_EXCLUDE, "filtering by regexp for table names. " "Operates the same way as --tables, but matched names are excluded from " "backup. " "Note that this option has a higher priority than --tables.",
(G_PTR *) &xtrabackup_tables_exclude,
(G_PTR *) &xtrabackup_tables_exclude, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"databases-exclude", OPT_XTRA_DATABASES_EXCLUDE, "Excluding databases based on name, " "Operates the same way as --databases, but matched names are excluded " "from backup. " "Note that this option has a higher priority than --databases.",
(G_PTR *) &xtrabackup_databases_exclude,
(G_PTR *) &xtrabackup_databases_exclude, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"stream", OPT_XTRA_STREAM, "Stream all backup files to the standard output " "in the specified format." "Supported format is 'mbstream' or 'xbstream'.",
(G_PTR *) &xtrabackup_stream_str, (G_PTR *) &xtrabackup_stream_str, 0,
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"compress", OPT_XTRA_COMPRESS, "Compress individual backup files using the " "specified compression algorithm. It uses no longer maintained QuickLZ " "library hence this option was deprecated with MariaDB 10.1.31 and 10.2.13.",
(G_PTR *) &xtrabackup_compress_alg, (G_PTR *) &xtrabackup_compress_alg, 0,
GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
{"compress-threads", OPT_XTRA_COMPRESS_THREADS, "Number of threads for parallel data compression. The default value is " "1. " "This option was deprecated as it relies on the no longer " "maintained QuickLZ library.",
(G_PTR *) &xtrabackup_compress_threads,
(G_PTR *) &xtrabackup_compress_threads, 0, GET_UINT, REQUIRED_ARG, 1, 1,
UINT_MAX, 0, 0, 0},
{"compress-chunk-size", OPT_XTRA_COMPRESS_CHUNK_SIZE, "Size of working buffer(s) for compression threads in bytes. The default " "value is 64K. " "This option was deprecated as it relies on the no longer " "maintained QuickLZ library.",
(G_PTR *) &xtrabackup_compress_chunk_size,
(G_PTR *) &xtrabackup_compress_chunk_size, 0, GET_ULL, REQUIRED_ARG,
(1 << 16), 1024, ULONGLONG_MAX, 0, 0, 0},
{"incremental-force-scan", OPT_XTRA_INCREMENTAL_FORCE_SCAN, "Perform a full-scan incremental backup even in the presence of changed " "page bitmap data",
(G_PTR *) &xtrabackup_incremental_force_scan,
(G_PTR *) &xtrabackup_incremental_force_scan, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"close_files", OPT_CLOSE_FILES, "do not keep files opened. Use at your own " "risk.",
(G_PTR *) &xb_close_files, (G_PTR *) &xb_close_files, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"copy-back", OPT_COPY_BACK, "Copy all the files in a previously made " "backup from the backup directory to their original locations.",
(uchar *) &xtrabackup_copy_back, (uchar *) &xtrabackup_copy_back, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"move-back", OPT_MOVE_BACK, "Move all the files in a previously made " "backup from the backup directory to the actual datadir location. " "Use with caution, as it removes backup files.",
(uchar *) &xtrabackup_move_back, (uchar *) &xtrabackup_move_back, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"galera-info", OPT_GALERA_INFO, "This options creates the "
MB_GALERA_INFO " file which contains the local node state at " "the time of the backup. Option should be used when performing the " "backup of MariaDB Galera Cluster. Has no effect when backup locks " "are used to create the backup.",
(uchar *) &opt_galera_info, (uchar *) &opt_galera_info, 0, GET_BOOL,
NO_ARG, 0, 0, 0, 0, 0, 0},
{"slave-info", OPT_SLAVE_INFO, "This option is useful when backing " "up a replication slave server. It prints the binary log position " "and name of the master server. It also writes this information to " "the \"" MB_SLAVE_INFO "\" file as a \"CHANGE MASTER\" command. " "A new slave for this master can be set up by starting a slave server " "on this backup and issuing a \"CHANGE MASTER\" command with the " "binary log position saved in the \"" MB_SLAVE_INFO "\" file.",
(uchar *) &opt_slave_info, (uchar *) &opt_slave_info, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"no-lock", OPT_NO_LOCK, "This option should not be used as " "mariadb-backup now is using BACKUP LOCKS, which minimizes the " "lock time. ALTER TABLE can run in parallel with BACKUP LOCKS." "Use the --no-lock option it only if ALL your " "tables are InnoDB and you DO NOT CARE about the binary log " "position of the backup. This option shouldn't be used if there " "are any DDL statements being executed or if any updates are " "happening on non-InnoDB tables (this includes the system MyISAM " "tables in the mysql database), otherwise it could lead to an " "inconsistent backup. If you are considering to use --no-lock " "because your backups are failing to acquire the lock, this could " "be because of incoming replication events preventing the lock " "from succeeding. Please try using --safe-slave-backup to " "momentarily stop the replication slave thread, this may help " "the backup to succeed and you then don't need to resort to " "using this option.",
(uchar *) &opt_no_lock, (uchar *) &opt_no_lock, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"safe-slave-backup", OPT_SAFE_SLAVE_BACKUP, "Stop slave SQL thread " "and wait to start backup until Slave_open_temp_tables in " "\"SHOW STATUS\" is zero. If there are no open temporary tables, " "the backup will take place, otherwise the SQL thread will be " "started and stopped until there are no open temporary tables. " "The backup will fail if Slave_open_temp_tables does not become " "zero after --safe-slave-backup-timeout seconds. The slave SQL " "thread will be restarted when the backup finishes.",
(uchar *) &opt_safe_slave_backup, (uchar *) &opt_safe_slave_backup, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"force-non-empty-directories", OPT_FORCE_NON_EMPTY_DIRS, "This " "option, when specified, makes --copy-back or --move-back transfer " "files to non-empty directories. Note that no existing files will be " "overwritten. If --copy-back or --move-back has to copy a file from " "the backup directory which already exists in the destination " "directory, it will still fail with an error.",
(uchar *) &opt_force_non_empty_dirs, (uchar *) &opt_force_non_empty_dirs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"no-version-check", OPT_NO_VERSION_CHECK, "This option disables the " "version check which is enabled by the --version-check option.",
(uchar *) &opt_noversioncheck, (uchar *) &opt_noversioncheck, 0, GET_BOOL,
NO_ARG, 0, 0, 0, 0, 0, 0},
{"decompress", OPT_DECOMPRESS, "Decompresses all files with the .qp " "extension in a backup previously made with the --compress option. " "This option was deprecated as it relies on the no longer " "maintained QuickLZ library.",
(uchar *) &opt_decompress, (uchar *) &opt_decompress, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"user", 'u', "This option specifies the MySQL username used " "when connecting to the server, if that's not the current user. " "The option accepts a string argument. See mysql --help for details.",
(uchar *) &opt_user, (uchar *) &opt_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"host", 'H', "This option specifies the host to use when " "connecting to the database server with TCP/IP. The option accepts " "a string argument. See mysql --help for details.",
(uchar *) &opt_host, (uchar *) &opt_host, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"port", 'P', "This option specifies the port to use when " "connecting to the database server with TCP/IP. The option accepts " "a string argument. See mysql --help for details.",
&opt_port, &opt_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"password", 'p', "This option specifies the password to use " "when connecting to the database. It accepts a string argument. " "See mysql --help for details.", 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
{"protocol", OPT_PROTOCOL, "The protocol to use for connection (tcp, socket, pipe, memory).", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"socket", 'S', "This option specifies the socket to use when " "connecting to the local database server with a UNIX domain socket. " "The option accepts a string argument. See mysql --help for details.",
(uchar *) &opt_socket, (uchar *) &opt_socket, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"incremental-history-name", OPT_INCREMENTAL_HISTORY_NAME, "This option specifies the name of the backup series stored in the "
XB_HISTORY_TABLE " history record to base an " "incremental backup on. Xtrabackup will search the history table " "looking for the most recent (highest innodb_to_lsn), successful " "backup in the series and take the to_lsn value to use as the " "starting lsn for the incremental backup. This will be mutually " "exclusive with --incremental-history-uuid, --incremental-basedir " "and --incremental-lsn. If no valid lsn can be found (no series by " "that name, no successful backups by that name), an error will be returned." " It is used with the --incremental option.",
(uchar *) &opt_incremental_history_name,
(uchar *) &opt_incremental_history_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"incremental-history-uuid", OPT_INCREMENTAL_HISTORY_UUID, "This option specifies the UUID of the specific history record " "stored in the " XB_HISTORY_TABLE " table to base an " "incremental backup on. --incremental-history-name, " "--incremental-basedir and --incremental-lsn. If no valid lsn can be " "found (no success record with that uuid), an error will be returned." " It is used with the --incremental option.",
(uchar *) &opt_incremental_history_uuid,
(uchar *) &opt_incremental_history_uuid, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"ftwrl-wait-query-type", OPT_LOCK_WAIT_QUERY_TYPE, "Old disabled option which has no effect anymore (not needed " "with BACKUP LOCKS)",
(uchar*) 0, (uchar*) 0, &query_type_typelib, GET_ENUM,
REQUIRED_ARG, QUERY_TYPE_ALL, 0, 0, 0, 0, 0},
{"kill-long-query-type", OPT_KILL_LONG_QUERY_TYPE, "This option specifies which types of queries should be killed to " "unblock the global lock. Default is \"all\".",
(uchar *) &opt_kill_long_query_type, (uchar *) &opt_kill_long_query_type,
&query_type_typelib, GET_ENUM, REQUIRED_ARG, QUERY_TYPE_SELECT, 0, 0, 0, 0, 0},
{"history", OPT_HISTORY, "This option enables the tracking of backup history in the "
XB_HISTORY_TABLE " table. An optional history " "series name may be specified that will be placed with the history " "record for the current backup being taken.",
NULL, NULL, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
{"kill-long-queries-timeout", OPT_KILL_LONG_QUERIES_TIMEOUT, "Old disabled option which has no effect anymore (not needed " "with BACKUP LOCKS)",
(uchar*) 0, (uchar*) 0, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"startup-wait-timeout", OPT_LOCK_WAIT_TIMEOUT, "This option specifies time in seconds that mariadb-backup should wait for " "BACKUP STAGE START to complete. BACKUP STAGE START has to wait until all " "currently running queries using explicit LOCK TABLES has ended. " "If there are still such queries when the timeout expires, mariadb-backup " "terminates with an error. Default is 0, in which case mariadb-backup waits " "indefinitely for BACKUP STAGE START to finish",
(uchar*) &opt_lock_wait_timeout, (uchar*) &opt_lock_wait_timeout, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"ftwrl-wait-threshold", OPT_LOCK_WAIT_THRESHOLD, "Old disabled option which has no effect anymore (not needed " "with BACKUP LOCKS)",
(uchar*) 0, (uchar*) 0, 0, GET_UINT,
REQUIRED_ARG, 60, 0, 0, 0, 0, 0},
{"safe-slave-backup-timeout", OPT_SAFE_SLAVE_BACKUP_TIMEOUT, "How many seconds --safe-slave-backup should wait for " "Slave_open_temp_tables to become zero. (default 300)",
(uchar *) &opt_safe_slave_backup_timeout,
(uchar *) &opt_safe_slave_backup_timeout, 0, GET_UINT, REQUIRED_ARG, 300, 0, 0, 0, 0, 0},
{"binlog-info", OPT_BINLOG_INFO, "This option controls how backup should retrieve server's binary log " "coordinates corresponding to the backup. Possible values are OFF, ON, " "LOCKLESS and AUTO.",
&opt_binlog_info, &opt_binlog_info, &binlog_info_typelib, GET_ENUM,
OPT_ARG, BINLOG_INFO_AUTO, 0, 0, 0, 0, 0},
{"log-innodb-page-corruption", OPT_XB_IGNORE_INNODB_PAGE_CORRUPTION, "Continue backup if innodb corrupted pages are found. The pages are " "logged in " MB_CORRUPTED_PAGES_FILE " and backup is finished with error. " "--prepare will try to fix corrupted pages. If " MB_CORRUPTED_PAGES_FILE " exists after --prepare in base backup directory, backup still contains " "corrupted pages and can not be considered as consistent.",
&opt_log_innodb_page_corruption, &opt_log_innodb_page_corruption, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
#ifndef DBUG_OFF /** Parameters to DBUG */ staticconstchar *dbug_option; #endif
static my_bool innodb_log_checkpoint_now;
struct my_option xb_server_options[] =
{
{"datadir", 'h', "Path to the database root.", (G_PTR*) &mysql_data_home,
(G_PTR*) &mysql_data_home, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"tmpdir", 't', "Path for temporary files. Several paths may be specified, separated by a " #ifdefined(_WIN32) "semicolon (;)" #else "colon (:)" #endif ", in this case they are used in a round-robin fashion.",
(G_PTR*) &opt_mysql_tmpdir,
(G_PTR*) &opt_mysql_tmpdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"parallel", OPT_XTRA_PARALLEL, "Number of threads to use for parallel datafiles transfer. " "The default value is 1.",
(G_PTR*) &xtrabackup_parallel, (G_PTR*) &xtrabackup_parallel, 0, GET_UINT,
REQUIRED_ARG, 1, 1, INT_MAX, 0, 0, 0},
{"extended_validation", OPT_XTRA_EXTENDED_VALIDATION, "Enable extended validation for Innodb data pages during backup phase. " "Will slow down backup considerably, in case encryption is used. " "May fail if tables are created during the backup.",
(G_PTR*)&opt_extended_validation,
(G_PTR*)&opt_extended_validation, 0, GET_BOOL, NO_ARG, FALSE, 0, 0, 0, 0, 0},
{"encrypted_backup", OPT_XTRA_ENCRYPTED_BACKUP, "In --backup, assume that nonzero key_version implies that the page" " is encrypted. Use --backup --skip-encrypted-backup to allow" " copying unencrypted that were originally created before MySQL 5.1.48.",
(G_PTR*)&opt_encrypted_backup,
(G_PTR*)&opt_encrypted_backup, 0, GET_BOOL, NO_ARG, TRUE, 0, 0, 0, 0, 0},
{"plugin-dir", OPT_PLUGIN_DIR, "Server plugin directory. Used to load plugins during 'prepare' phase." "Has no effect in the 'backup' phase (plugin directory during backup is the same as server's)",
&xb_plugin_dir, &xb_plugin_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
{"aria_log_dir_path", OPT_ARIA_LOG_DIR_PATH, "Path to individual files and their sizes.",
&aria_log_dir_path, &aria_log_dir_path, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"open_files_limit", 0, "the maximum number of file " "descriptors to reserve with setrlimit().",
(G_PTR*) &xb_open_files_limit, (G_PTR*) &xb_open_files_limit, 0, GET_ULONG,
REQUIRED_ARG, 0, 0, UINT_MAX, 0, 1, 0},
{"lock-ddl-per-table", OPT_LOCK_DDL_PER_TABLE, "Lock DDL for each table " "before backup starts to copy it and until the backup is completed.",
(uchar*) &opt_lock_ddl_per_table, (uchar*) &opt_lock_ddl_per_table, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"rocksdb-datadir", OPT_ROCKSDB_DATADIR, "RocksDB data directory." "This option is only used with --copy-back or --move-back option",
&xb_rocksdb_datadir, &xb_rocksdb_datadir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
{"rocksdb-backup", OPT_BACKUP_ROCKSDB, "Backup rocksdb data, if rocksdb plugin is installed." "Used only with --backup option. Can be useful for partial backups, to exclude all rocksdb data",
&xb_backup_rocksdb, &xb_backup_rocksdb, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0 },
{"check-privileges", OPT_XTRA_CHECK_PRIVILEGES, "Check database user " "privileges for the backup user",
&opt_check_privileges, &opt_check_privileges, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0 },
{"innodb_log_checkpoint_now", OPT_INNODB_CHECKPOINT, "(for --backup): Force an InnoDB checkpoint",
(G_PTR*)&innodb_log_checkpoint_now,
(G_PTR*)&innodb_log_checkpoint_now, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
{"mysqld-args", OPT_XTRA_MYSQLD_ARGS, "All arguments that follow this argument are considered as server " "options, and if some of them are not supported by mariabackup, they " "will be ignored.",
(G_PTR *) &xtrabackup_mysqld_args, (G_PTR *) &xtrabackup_mysqld_args, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"help", '?', "Display this help and exit.",
(G_PTR *) &xtrabackup_help, (G_PTR *) &xtrabackup_help, 0,
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"binlog", OPT_BINLOG, "Backup the server binary logs. Only applies to server configured with " "--binlog-storage-engine, old-style binlog is not backed up. Enabled by " "default, specify --skip-binlog to not backup the binlog files. The " "--skip-binlog option, if used, must be specified with both --backup and " "--prepare",
(G_PTR*)&opt_backup_binlog,
(G_PTR*)&opt_backup_binlog, 0, GET_BOOL, OPT_ARG, 1, 0, 0, 0, 0, 0},
{"binlog-directory", OPT_BINLOG_DIRECTORY, "The directory into which to copy any binlog files in the backup. This can " "be used to put binlog files in the correct location if the restored " "server is to be configured with a non-default --binlog-directory. Only " "used with --copy-back",
&opt_binlog_directory, &opt_binlog_directory, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
std::string name=ut_get_name(0, buf); /* Strip partition name comment from table name, if any */ if (ends_with(name.c_str(), "*/"))
{
size_t pos= name.rfind("/*"); if (pos != std::string::npos)
name.resize(pos);
}
tables_for_export.insert(name);
}
}
staticvoid usage(void)
{
puts("Open source backup tool for InnoDB and XtraDB\n\
\n\
Copyright (C) 2009-2015 Percona LLC and/or its affiliates.\n\
Portions Copyright (C) 2000, 2011, MySQL AB & Innobase Oy.\n\
Portions Copyright (C) 2017-2023 MariaDB Corporation / MariaDB Plc.\n\
\n\ This program is free software; you can redistribute it and/or\n\
modify it under the terms of the GNU General Public License\n\
as published by the Free Software Foundation version 2\n\
of the License.\n\
\n\ This program is distributed in the hope that it will be useful,\n\
but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
GNU General Public License for more details.\n\
\n\
You can download full text of the license on http://www.gnu.org/licenses/gpl-2.0.txt\n");
if (n_shift >= 12 && n_shift <= UNIV_PAGE_SIZE_SHIFT_MAX) {
srv_page_size_shift = uint32_t(n_shift);
srv_page_size = 1U << n_shift;
msg("InnoDB: The universal page size of the " "database is set to %lu.", srv_page_size);
} else {
msg("invalid value of " "innobase_page_size: %lld", innobase_page_size); goto error;
}
} else {
srv_page_size_shift = 14;
srv_page_size = 1U << 14;
}
/* Check that values don't overflow on 32-bit systems. */ if (sizeof(ulint) == 4) { if (xtrabackup_use_memory > (longlong) UINT_MAX32) {
msg("mariabackup: use-memory can't be over 4GB" " on 32-bit systems");
}
}
/* Set InnoDB initialization parameters according to the values
read from MySQL .cnf file */
if (xtrabackup_backup) {
msg("mariabackup: using the following InnoDB configuration:");
} else {
msg("mariabackup: using the following InnoDB configuration " "for recovery:");
}
/*--------------- Data files -------------------------*/
/* The default dir for data files is the datadir of MySQL */
/* Set default InnoDB data file size to 10 MB and let it be auto-extending.ThususerscanuseInnoDBin>=4.0withouthaving
to specify any startup options. */
/* Assign the default value to srv_undo_dir if it's not specified, as my_getoptdoesnotsupportdefaultvaluesforstringoptions.Wealso ignoretheoptionandoverrideinnodb_undo_directoryon--prepare, becauseseparateundotablespacesarecopiedtotherootbackup
directory. */
if (!srv_undo_dir || !xtrabackup_backup) {
srv_undo_dir = (char*) ".";
}
if (!opt_binlog_directory || !xtrabackup_backup) { if (free_opt_binlog_directory)
my_free(const_cast<char *>(opt_binlog_directory));
opt_binlog_directory = ".";
free_opt_binlog_directory= false;
}
if (srv_force_recovery >= SRV_FORCE_IGNORE_CORRUPT) { if (!xtrabackup_prepare) {
msg("mariabackup: The option \"innodb_force_recovery\"" " should only be used with \"%s\".",
(innobackupex_mode ? "--apply-log" : "--prepare")); goto error;
} else {
msg("innodb_force_recovery = %lu", srv_force_recovery);
}
}
/* Check if the data files exist or not. */
dberr_t err= srv_sys_space.check_file_spec(&create_new_db, 5U << 20);
if (create_new_db)
{
msg("mariadb-backup: InnoDB files do not exist"); returntrue;
}
if (err == DB_SUCCESS)
err= srv_start(false);
if (err != DB_SUCCESS)
{
msg("mariadb-backup: srv_start() returned %d (%s).", err, ut_strerr(err)); returntrue;
}
ut_ad(srv_force_recovery <= SRV_FORCE_IGNORE_CORRUPT);
mysql_mutex_lock(&recv_sys.mutex);
ut_ad(recv_no_log_write); if (recv_sys.recovery_on)
recv_sys.apply(true);
mysql_mutex_unlock(&recv_sys.mutex);
recv_sys.debug_free();
buf_flush_sync_batch(0, false);
ut_ad(!os_aio_pending_reads());
ut_d(mysql_mutex_lock(&buf_pool.flush_list_mutex));
ut_ad(!buf_pool.get_oldest_modification(0));
ut_d(mysql_mutex_unlock(&buf_pool.flush_list_mutex)); /* os_aio_pending_writes() may hold here if some write_io_callback() didnotreleasetheslotyet.However,thepagewriteitselfmust havecompleted,becausethebuf_pool.flush_listisempty.Indebug builds,wewaitforthistohappen,hopingtogetahungprocessif
this assumption does not hold. */
ut_d(os_aio_wait_until_no_pending_writes(false));
log_sys.close_file();
if (xtrabackup_incremental) /* Reset the ib_logfile0 in --target-dir, not --incremental-dir. */
srv_log_group_home_dir= xtrabackup_target_dir;
if (page_size == ULINT_UNDEFINED) {
msg("page_size is required in %s", filepath);
r = FALSE;
} else {
info->page_size = page_size;
info->zip_size = zip_size;
}
if (info->space_id == UINT32_MAX) {
msg("mariabackup: Warning: This backup was taken with XtraBackup 2.0.1 " "or earlier, some DDL operations between full and incremental " "backups may be handled incorrectly");
}
@return TRUE on match or both regex_list and tables_hash are empty.*/ static my_bool
check_if_table_matches_filters(constchar *name, const regex_list_t& regex_list,
hash_table_t* tables_hash)
{ if (regex_list.empty() && !tables_hash->array) { return(FALSE);
}
if (regex_list_check_match(regex_list, name)) { return(TRUE);
}
@returnTRUEifentiredatabaseshouldbeskipped, FALSEotherwise.
*/ static
skip_database_check_result
check_if_skip_database( constchar* name /*!< in: path to the database */
)
{ /* There are some filters for databases, check them */
xb_filter_entry_t* database = NULL;
if (databases_exclude_hash.array &&
find_filter_in_hashtable(name, &databases_exclude_hash,
&database) &&
(!database->has_tables || !databases_include_hash.array)) { /* Database is found and there are no tables specified,
skip entire db. */ return DATABASE_SKIP;
}
@return TRUE if the table should be skipped. */
my_bool
check_if_skip_database_by_path( constchar* path /*!< in: path to the db directory. */
)
{ if (!databases_include_hash.array && !databases_exclude_hash.array) { return(FALSE);
}
constchar* db_name = strrchr(path, '/'); #ifdef _WIN32 if (constchar* last = strrchr(path, '\\')) { if (!db_name || last > db_name) {
db_name = last;
}
} #endif
/* Check if there's a suffix in the table name. If so, truncate it. We relyonthefactthatadotcannotbeapartofatablename(itis
encoded by the server with the @NNNN syntax). */ if ((eptr = strchr(&buf[tbname - dbname], '.')) != NULL) {
*eptr = '\0';
}
/* For partitioned tables first try to match against the regexp withouttruncatingthe#P#...suffixsowecanbackupindividual
partitions with regexps like '^test[.]t#P#p5' */ if (check_if_table_matches_filters(buf, regex_exclude_list,
&tables_exclude_hash)) { return(TRUE);
} if (check_if_table_matches_filters(buf, regex_include_list,
&tables_include_hash)) { return(FALSE);
} if ((eptr = strstr(buf, "#P#")) != NULL) {
*eptr = 0;
if (check_if_table_matches_filters(buf, regex_exclude_list,
&tables_exclude_hash)) { return (TRUE);
} if (check_if_table_matches_filters(buf, regex_include_list,
&tables_include_hash)) { return(FALSE);
}
}
if (skip_database == DATABASE_DONT_SKIP_UNLESS_EXPLICITLY_EXCLUDED) { /* Database is in include-list, and qualified name wasn't
found in any of exclusion filters.*/ return (FALSE);
}
if (skip_database == DATABASE_SKIP_SOME_TABLES ||
!regex_include_list.empty() ||
tables_include_hash.array) {
/* Include lists are present, but qualified name
failed to match any.*/ return(TRUE);
}
/* close */
msg(thread_n," ...done");
xb_fil_cur_close(&cursor); if (ds_close(dstfile)) {
rc = TRUE;
} if (write_filter.deinit) {
write_filter.deinit(&write_filt_ctxt);
} return(rc);
error:
xb_fil_cur_close(&cursor); if (dstfile != NULL) {
ds_close(dstfile);
} if (write_filter.deinit) {
write_filter.deinit(&write_filt_ctxt);;
}
msg(thread_n, "mariabackup: xtrabackup_copy_datafile() failed."); return(TRUE); /*ERROR*/
skip:
if (dstfile != NULL) {
ds_close(dstfile);
} if (write_filter.deinit) {
write_filter.deinit(&write_filt_ctxt);
}
msg(thread_n,"Warning: We assume the table was dropped during xtrabackup execution and ignore the tablespace %s", node->name); return(FALSE);
}
/* Maximum LSN the copier may parse up to. Usuallyderivedfromtheserver'sdurably-flushedredoLSN. WhenafinaltargetLSNisset,thislimitisraisedtothat targetsothelastpartialblockcanbecopiedinfull. Thecopierrefusestoacceptamini-transactionwhoseend exceedsthis,soitneverparsesthevolatiletailblock theserverisstillrewriting (whichmmapcanreadtorn,leadingtoamis-computedmtr lengthandpermanentmid-mtrdrift).Apread()ofthesame still-being-writtentailblockcanlikewisereturnatornread whichcanbeobservedonext4,thoughnotonXFS).
Read/written only under recv_sys.mutex. */ static lsn_t max_parse_lsn;
/* Set by backup_log_parse() when the last parse was rejectedbecausethemtrcrossedmax_parse_lsn.Itmeans
"caught up, wait" condition, not a stall. */ staticbool reached_parse_limit;
/** Copy memory-mapped log until the end of the log is reached orthelog_copying_stopsignalisreceived
@return whether the operation failed */ staticbool xtrabackup_copy_mmap_logfile()
{
mysql_mutex_assert_owner(&recv_sys.mutex);
recv_sys.offset= size_t(log_sys.calc_lsn_offset(recv_sys.lsn));
recv_sys.len= size_t(log_sys.file_size); const size_t seq_offset{log_sys.is_encrypted() ? 8U + 5U : 5U}; constchar one{'\1'}; const byte *start= &log_sys.buf[recv_sys.offset];
ut_d(recv_sys_t::parse_mtr_result r);
if ((ut_d(r=) backup_log_parse()) == recv_sys_t::OK)
{ do
{ /* Set the sequence bit (the backed-up log will not wrap around) */
size_t seqo= recv_sys.offset - seq_offset; if (seqo < log_sys.START_OFFSET)
seqo+= static_cast<size_t>(log_sys.file_size - log_sys.START_OFFSET); const byte *seq= &log_sys.buf[seqo];
ut_ad(*seq == log_sys.get_sequence_bit(recv_sys.lsn - seq_offset)); if (!*seq)
{ if (xtrabackup_copy_mmap_snippet(dst_log_file, start, seq) ||
ds_write(dst_log_file, &one, 1)) goto write_error;
start = seq + 1;
}
} while ((ut_d(r=) backup_log_parse()) == recv_sys_t::OK);
if (xtrabackup_copy_mmap_snippet(dst_log_file, start,
&log_sys.buf[recv_sys.offset]))
{
write_error:
msg("Error: write to ib_logfile0 failed"); returntrue;
}
pthread_cond_broadcast(&scanned_lsn_cond);
}
ut_ad(r == recv_sys_t::GOT_EOF);
if (verbose)
msg(">> log scanned up to (" LSN_PF ")", recv_sys.lsn); returnfalse;
}
/** Copy redo log until the current end of the log is reached @paramearly_exitparseandcopyonlylogsfromfirstreadandreturn
@return whether the operation failed */ staticbool xtrabackup_copy_logfile(bool early_exit)
{
mysql_mutex_assert_owner(&recv_sys.mutex);
DBUG_EXECUTE_IF("log_checksum_mismatch", returnfalse;);
if (backup_log_parse() == recv_sys_t::OK)
{ do
{ /* Set the sequence bit (the backed-up log will not wrap around) */
byte *seq= &log_sys.buf[recv_sys.offset - sequence_offset];
ut_ad(*seq == log_sys.get_sequence_bit(recv_sys.lsn -
sequence_offset));
*seq= 1;
} while ((r= backup_log_parse()) == recv_sys_t::OK);
if (ds_write(dst_log_file, log_sys.buf + start_offset,
recv_sys.offset - start_offset))
{
msg("Error: write to ib_logfile0 failed"); returntrue;
}
pthread_cond_broadcast(&scanned_lsn_cond);
if (r == recv_sys_t::GOT_EOF || early_exit) break;
ut_ad(r == recv_sys_t::PREMATURE_EOF);
if (recv_sys.offset < log_sys.write_size) break;
if (xtrabackup_throttle && io_ticket-- < 0)
mysql_cond_wait(&wait_throttle, &recv_sys.mutex);
msg("Was only able to copy log from " LSN_PF " to " LSN_PF ", not " LSN_PF, checkpoint_lsn, last_lsn, lsn);
if (needed <= capacity)
msg("mariabackup: The required redo still fits within the log " "capacity, so it has not been overwritten; check whether the " "server and backup configuration are the same."); else
msg("mariabackup: Try increasing the innodb_log_file_size."); returnfalse;
}
staticvoid log_copying_thread()
{
my_thread_init();
MYSQL *limit_con= xb_mysql_connect(); if (!limit_con)
{ /* Without this connection we cannot poll the durably-flushed LSN,somax_parse_lsncouldneveradvanceandthecopier wouldeitherstallorparsethevolatiletailblock. Failthecopygracefullyandletthemainthreadreport
it via backup_wait_timeout(). */
msg("mariabackup: Error: cannot open a server connection for " "the log copying thread; the backup will fail.");
mysql_mutex_lock(&recv_sys.mutex);
log_copying_running= false;
pthread_cond_broadcast(&scanned_lsn_cond);
mysql_mutex_unlock(&recv_sys.mutex);
my_thread_end(); return;
}
mysql_mutex_lock(&recv_sys.mutex); for (;;)
{ /* metadata_last_lsn is set to 1 when error was encountered.
Abort the log copier if an error was signaled */ if (metadata_last_lsn == METADATA_LSN_ERROR) break; /* Refresh the max_parse_lsn before each copy pass */ const lsn_t final_target= metadata_last_lsn > metadata_to_lsn
? metadata_last_lsn : metadata_to_lsn; if (final_target) /* Final phase (BLOCK_DDL/BLOCK_COMMIT): fence at the exact target insteadofthepolledflushedLSN.Thisisonlysafebecausethe targetwasderivedfromget_current_lsn(),whichruns FLUSHENGINELOGSandthusmakestheredodurableuptoatleast final_target;sofinal_target<=Innodb_lsn_flushedholdshereand thebytesuptoitarestable.DonotfeedthisbranchanLSNthat hasnotbeenflushedbytheserver,orthecopierwillparsethe
volatile tail block again (MDEV-39468). */
max_parse_lsn= final_target; else
{
mysql_mutex_unlock(&recv_sys.mutex);
lsn_t flushed= get_log_flushed_lsn(limit_con);
mysql_mutex_lock(&recv_sys.mutex); if (flushed > log_sys.get_first_lsn())
max_parse_lsn= flushed; elseif (!max_parse_lsn)
{ /* We could not obtain a flushed LSN and none was seeded before, sotheparselimitisunsetandthecopierwouldfallbackto parsingthevolatiletailblock(theMDEV-39468failure).Warn
once so this is diagnosable rather than a silent stall. */ staticbool warned; if (!warned)
{
warned= true;
msg("mariabackup: Warning: could not read Innodb_lsn_flushed; " "the redo log copier has no parse limit.");
}
}
}
if (xtrabackup_copy_logfile(false)) break; if (final_target && final_target <= recv_sys.lsn) break;
Otherwise(i.e.whenstreaminginthe'tar'format)weneed2separatedatasinks forthedatastream(anddon'tallowparalleldatacopying)andformetainfo files(includingib_logfile0).Theseconddatasinkwritestotemporary
files first, and then streams them in a serialized way when closed. */ void Backup_datasinks::init()
{ /* Start building out the pipelines from the terminus back */ if (xtrabackup_stream) { /* All streaming goes to stdout */
m_data = m_meta = m_redo = ds_create(xtrabackup_target_dir,
DS_TYPE_STDOUT);
} else { /* Local filesystem */
m_data = m_meta = m_redo = ds_create(xtrabackup_target_dir,
DS_TYPE_LOCAL);
}
/* Track it for destruction */
add_datasink_to_destroy(m_data);
/* Stream formatting */ if (xtrabackup_stream) {
ds_ctxt_t *ds;
if (is_remote) {
RemoteDatafile* rf = new RemoteDatafile();
file = rf; if (!rf->open_link_file(n)) { goto cant_open;
}
} else {
file = new Datafile();
file->make_filepath(".", n, IBD);
}
if (file->open_read_only(true) != DB_SUCCESS) {
cant_open: delete file; // Ignore FTS tables, as they can be removed for intermediate tables, // this code must be executed under stronger or equal to BLOCK_DDL lock, // so there must not be errors for non-intermediate FTS tables. if (check_if_fts_table(filname)) return;
die("Can't open datafile %s", name);
}
for (int i = 0; i < 10; i++) {
file->m_defer = false;
err = file->validate_first_page(file->get_first_page());
/* Note that in Windows opening the 'directory stream' also retrieves thefirstentryinthedirectory.Sinceitis'.',thatisnoproblem,
as we will skip over the '.' and '..' entries anyway. */
/** This function returns information of the next file in the directory. We jump overthe'.'and'..'entriesinthedirectory. @param[in]dirnamedirectorynameorpath @param[in]dirdirectorystream @param[out]infobufferwheretheinfoisreturned
@return 0 if ok, -1 if error, 1 if at the end of the directory */ int
os_file_readdir_next_file( constchar* dirname,
os_file_dir_t dir,
os_file_stat_t* info)
{ #ifdef _WIN32 BOOL ret; int status;
WIN32_FIND_DATA find_data;
next_file:
ret = FindNextFile(dir, &find_data);
if (ret > 0) {
constchar* name;
name = static_cast<constchar*>(find_data.cFileName);
if (find_data.dwFileAttributes
& FILE_ATTRIBUTE_REPARSE_POINT) {
/* TODO: test Windows symlinks */ /* TODO: MySQL has apparently its own symlink implementationinWindows,dbname.symcan redirectadatabasedirectory:
REFMAN "windows-symbolic-links.html" */
if (errno == ENOENT) { /* readdir() returned a file that does not exist, itmusthavebeendeletedinthemeantime.Dowhat wouldhavehappenedifthefilewasdeletedbefore readdir()-ignoreandgotothenextentry. Ifthisisthelastentrytheninfo->namewillstill containthenameofthedeletedfilewhenthis functionreturns,butthisisnotanissuesincethe callershouldn'tbelookingatinfowhenendof
directory is returned. */
/***********************************************************************//**
A fault-tolerant function that tries to read the next file name in the
directory. We retry 100 times if os_file_readdir_next_file() returns -1. The
idea is to read as much good data as we can and jump over bad data.
@return0if ok, -1if error even after the retries, 1if at the end
of the directory */ int
fil_file_readdir_next_file( /*=======================*/
dberr_t* err, /*!< out: this is set to DB_ERROR if an error
was encountered, otherwise not changed */ constchar* dirname,/*!< in: directory name or path */
os_file_dir_t dir, /*!< in: directory stream */
os_file_stat_t* info) /*!< in/out: buffer where the
info is returned */
{ for (ulint i = 0; i < 100; i++) { int ret = os_file_readdir_next_file(dirname, dir, info);
if (ret != -1) {
return(ret);
}
ib::error() << "os_file_readdir_next_file() returned -1 in" " directory " << dirname
<< ", crash recovery may have failed" " for some .ibd files!";
*err = DB_ERROR;
}
return(-1);
}
/** Scan the database directories under the MySQL datadir, looking for .ibdfilesanddeterminingthespaceidineachofthem.
@return DB_SUCCESS or error number */
/* Scan all directories under the datadir. They are the database
directories of MySQL. */
ret = fil_file_readdir_next_file(&err, fil_path_to_mysql_datadir, dir,
&dbinfo); while (ret == 0) {
/* General tablespaces are always at the first level of the
data home dir */ if (dbinfo.type != OS_FILE_TYPE_FILE) { constbool is_isl = ends_with(dbinfo.name, ".isl"); if (is_isl || ends_with(dbinfo.name,".ibd")) {
(*callback)(nullptr, dbinfo.name, is_isl, false, 0);
}
}
if (dbinfo.type == OS_FILE_TYPE_FILE
|| dbinfo.type == OS_FILE_TYPE_UNKNOWN) {
goto next_datadir_item;
}
/* We found a symlink or a directory; try opening it to see
if a symlink is a directory */
len = strlen(fil_path_to_mysql_datadir)
+ strlen (dbinfo.name) + 2; if (len > dbpath_len) {
dbpath_len = len;
if (UNIV_UNLIKELY(dbdir != IF_WIN(INVALID_HANDLE_VALUE,NULL))){ /* We found a database directory; loop through it,
looking for possible .ibd files in it */
for (ret = fil_file_readdir_next_file(&err, dbpath,
dbdir,
&fileinfo);
ret == 0;
ret = fil_file_readdir_next_file(&err, dbpath,
dbdir,
&fileinfo)) { if (fileinfo.type == OS_FILE_TYPE_DIR) { continue;
}
/* We found a symlink or a file */ if (strlen(fileinfo.name) > 4) { bool is_isl= false; if (ends_with(fileinfo.name, ".ibd") || ((is_isl = ends_with(fileinfo.name, ".isl"))))
(*callback)(dbinfo.name, fileinfo.name, is_isl, false, 0);
}
}
if (os_file_closedir_failed(dbdir)) {
fprintf(stderr, "InnoDB: Warning: could not" " close database directory %s\n",
dbpath);
err = DB_ERROR;
}
} else {
msg("Can't open dir %s", dbpath);
err = DB_ERROR; break;
}
next_datadir_item:
ret = fil_file_readdir_next_file(&err,
fil_path_to_mysql_datadir,
dir, &dbinfo);
}
ut_free(dbpath);
if (os_file_closedir_failed(dir)) {
fprintf(stderr, "InnoDB: Error: could not close MariaDB datadir\n"); return(DB_ERROR);
}
return(err);
}
/** Close all undo tablespaces while applying incremental delta */ staticvoid xb_close_undo_tablespaces()
{ if (srv_undo_space_id_start == 0) return; for (uint32_t space_id= srv_undo_space_id_start;
space_id < srv_undo_space_id_start + srv_undo_tablespaces_open;
space_id++)
{
fil_space_t *space= fil_space_get(space_id);
ut_ad(space);
space->close();
}
}
/* create_new_db must not be true. */ if (err != DB_SUCCESS || create_new_db) {
msg("Could not find data files at the specified datadir"); return(DB_ERROR);
}
for (int i= 0; i < 10; i++) {
err = srv_sys_space.open_or_create(false, false, &sum_of_new_sizes); if (err == DB_PAGE_CORRUPTED || err == DB_CORRUPTION) {
my_sleep(1000);
} else break;
}
if (err != DB_SUCCESS) {
msg("Could not open data files.\n"); return(err);
}
/* Add separate undo tablespaces to fil_system */
err = srv_undo_tablespaces_init(false, nullptr);
if (err != DB_SUCCESS) { return(err);
}
/* It is important to call xb_load_single_table_tablespaces() after srv_undo_tablespaces_init(),becausefil_is_user_tablespace_id()*
relies on srv_undo_tablespaces_open to be properly initialized */
msg("mariabackup: Generating a list of tablespaces");
err = enumerate_ibd_files(xb_load_single_table_tablespace); if (err != DB_SUCCESS) { return(err);
}
if (srv_operation == SRV_OPERATION_RESTORE_DELTA) {
xb_close_undo_tablespaces();
}
/*********************************************************************** Validatenameoftableordatabase.Ifnameisinvalid,programwill
be finished with error code */ static void
xb_validate_name( /*=============*/ constchar* name, /*!< in: name */
size_t len) /*!< in: length of name */
{ constchar* p;
/* perform only basic validation. validate length and
path symbols */ if (len > NAME_LEN) {
die("name `%s` is too long.", name);
}
p = strpbrk(name, "/\\~"); if (p && (uint) (p - name) < NAME_LEN) {
die("name `%s` is not valid.", name);
}
}
/*********************************************************************** Registernewfilterentrywhichcanbeeitherdatabase
or table name. */ static void
xb_register_filter_entry( /*=====================*/ constchar* name, /*!< in: name */
hash_table_t* databases_hash,
hash_table_t* tables_hash
)
{
size_t namelen = strlen(name); if (constchar* p = strchr(name, '.')) { char dbname[NAME_LEN + 1];
/***********************************************************************
Register new table for the filter. */ static void
xb_register_table( /*==============*/ constchar* name) /*!< in: name of table */
{ if (strchr(name, '.') == NULL) {
die("`%s` is not fully qualified name.", name);
}
xb_register_include_filter_entry(name);
}
static void
xb_add_regex_to_list( constchar* regex, /*!< in: regex */ constchar* error_context, /*!< in: context to error message */
regex_list_t* list) /*! in: list to put new regex to */
{ char errbuf[100]; int ret;
regex_t compiled_regex;
ret = regcomp(&compiled_regex, regex, REG_EXTENDED);
/***********************************************************************
Register new regex for the include filter. */ static void
xb_register_include_regex( /*==============*/ constchar* regex) /*!< in: regex */
{
xb_add_regex_to_list(regex, "tables", ®ex_include_list);
}
/***********************************************************************
Register new regex for the exclude filter. */ static void
xb_register_exclude_regex( /*==============*/ constchar* regex) /*!< in: regex */
{
xb_add_regex_to_list(regex, "tables-exclude", ®ex_exclude_list);
}
typedefvoid (*insert_entry_func_t)(constchar*);
/* Scan string and load filter entries from it. @param[in]liststringrepresentingalist @param[in]delimitersdelimitersofentries
@param[in] ins callback to add entry */ void xb_load_list_string(char *list, constchar *delimiters,
insert_entry_func_t ins)
{ char *p; char *saveptr;
p= strtok_r(list, delimiters, &saveptr); while (p)
{
ins(p);
p= strtok_r(NULL, delimiters, &saveptr);
}
}
/***********************************************************************
Scan file and load filter entries from it. */ static void
xb_load_list_file( /*==============*/ constchar* filename, /*!< in: name of file */
insert_entry_func_t ins) /*!< in: callback to add entry */
{ char name_buf[NAME_LEN*2+2];
FILE* fp;
/* read and store the filenames */
fp = fopen(filename, "rt"); if (!fp) {
die("Can't open %s",
filename);
} while (fgets(name_buf, sizeof(name_buf), fp) != NULL) { char* p = strchr(name_buf, '\n'); if (p) {
*p = '\0';
} else {
die("`%s...` name is too long", name_buf);
}
for (uint32_t id : failed_ids) {
msg("mariabackup: Failed to read undo log " "tablespace space id " UINT32PF " and there is no undo " "tablespace truncation redo record.",
id);
}
if (!xtrabackup_stream_metadata(m_meta)) {
msg("Error: failed to stream metadata."); returnfalse;
} if (xtrabackup_extra_lsndir) { char filename[FN_REFLEN];
snprintf(filename, sizeof(filename), "%s/%s",
xtrabackup_extra_lsndir, MB_METADATA_FILENAME); if (!xtrabackup_write_metadata(filename)) {
msg("Error: failed to write metadata " "to '%s'.", filename); returnfalse;
}
snprintf(filename, sizeof(filename), "%s/%s",
xtrabackup_extra_lsndir, MB_INFO); if (!write_xtrabackup_info(m_data,
mysql_connection, filename, false, false)) {
msg("Error: failed to write info " "to '%s'.", filename); returnfalse;
}
}
void copy_job(fil_node_t *node, unsigned thread_num) {
DBUG_ASSERT(node); // TODO: this came from the old code, where it was not thread-safe // too, use separate mysql connection per thread here
DBUG_MARIABACKUP_EVENT("before_copy", node->space->name());
DBUG_EXECUTE_FOR_KEY("wait_innodb_redo_before_copy",
node->space->name(),
backup_wait_for_lsn(
get_current_lsn(mysql_connection));); /* copy the datafile */ if(xtrabackup_copy_datafile(m_backup_datasinks.m_data,
m_backup_datasinks.m_meta,
node, thread_num, NULL,
xtrabackup_incremental
? wf_incremental : wf_write_through,
m_corrupted_pages))
die("mariabackup: Error: failed to copy datafile."); // TODO: this came from the old code, where it was not thread-safe // too, use separate mysql connection per thread here
DBUG_MARIABACKUP_EVENT("after_copy", node->space->name());
m_tasks.finish_task(1);
}
bool init() { if ((m_bs_con = xb_mysql_connect()) == nullptr) returnfalse;
while(m_con_pool.size() < xtrabackup_parallel) {
MYSQL *con = xb_mysql_connect(); if (con == nullptr) returnfalse;
m_con_pool.push_back(con);
}
if (!m_thread_pool.start(xtrabackup_parallel)) returnfalse; if (!m_aria_backup.init()) returnfalse;
m_aria_backup.set_post_copy_table_hook(
std::bind(&BackupStages::store_table_version, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
m_common_backup.set_post_copy_table_hook(
std::bind(&BackupStages::store_table_version, this,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); returntrue;
}
void destroy() {
m_thread_pool.stop(); while (!m_con_pool.empty()) {
MYSQL *con = m_con_pool.back();
m_con_pool.pop_back();
mysql_close(con);
} if (m_bs_con)
mysql_close(m_bs_con);
m_bs_con = nullptr;
}
bool stage_start(Backup_datasinks &backup_datasinks,
CorruptedPages &corrupted_pages) {
msg("Starting BACKUP STAGE START"); if (!opt_no_lock) { if (opt_safe_slave_backup) { if (!wait_for_safe_slave(mysql_connection)) { return(false);
}
}
history_lock_time = time(NULL);
if (!lock_for_backup_stage_start(m_bs_con)) {
msg("Error on BACKUP STAGE START query execution"); return(false);
}
msg("Acquired locks for BACKUP STAGE START");
}
InnodbDataCopier innodb_data_copier(backup_datasinks,
corrupted_pages,
m_thread_pool); // Start InnoDB data files copy in background if (!innodb_data_copier.start()) {
msg("Error on starting InnoDB data files backup"); returnfalse;
} // Start online non-stats-log Aria tables copying in background if (!m_aria_backup.start(opt_no_lock)) {
msg("Error on starting Aria data files backup");
innodb_data_copier.wait_for_finish(); returnfalse;
}
// Wait for all innodb data files copy finish if(!innodb_data_copier.wait_for_finish()) {
msg("InnoDB data files backup process is finished with error"); returnfalse;
} // Wait for online non-stats-log Aria tables copy finish if (!m_aria_backup.wait_for_finish()) {
msg("Aria data files backup process is finished with error"); returnfalse;
}
bool stage_flush() {
msg("Starting BACKUP STAGE FLUSH"); if (!opt_no_lock) { if (!lock_for_backup_stage_flush(m_bs_con)) {
msg("Error on BACKUP STAGE FLUSH query execution"); returnfalse;
}
msg("Acquired locks for BACKUP STAGE FLUSH");
} auto tables_in_use = get_tables_in_use(mysql_connection); // Copy non-stats-log non-in-use tables of non-InnoDB-Aria-RocksDB engines // in background if (!m_common_backup.scan(tables_in_use,
&m_copied_common_tables, opt_no_lock, true)) {
msg("Error on scan data directory for common engines"); returnfalse;
} // Copy Aria offline non-stats-log non-in-use tables in background if (!m_aria_backup.copy_offline_tables(&tables_in_use, opt_no_lock, false)) {
msg("Error on start Aria tables backup"); returnfalse;
}
if (!m_aria_backup.copy_log_tail()) {
msg("Error on Aria log tail copy"); returnfalse;
};
// Wait for Aria tables copy finish if (!m_aria_backup.wait_for_finish()) {
msg("Aria data files backup process is finished with error"); returnfalse;
} // Wait for non-InnoDB-Aria-RocksDB engines copy finish if (!m_common_backup.wait_for_finish()) {
msg("Data files backup process is finished with error"); returnfalse;
}
// Copy the rest of non-stats-lognon-InnoDB-Aria-RocksDB tables // Do not execute BACKUP LOCK under BLOCK_DDL stage if (!m_common_backup.scan(m_copied_common_tables, &m_copied_common_tables, true, false)) {
msg("Error on scan data directory for common engines"); returnfalse;
} // Copy log tables tail if (!m_common_backup.copy_log_tables(false)) {
msg("Error on copy system tables"); returnfalse;
}
// Copy the rest of non-stats Aria tables in background if (!m_aria_backup.copy_offline_tables(nullptr, true, false)) {
msg("Error on start Aria tables backup"); returnfalse;
}
// Copy .frm, .trn and other files if (!backup_files(backup_datasinks.m_data,
fil_path_to_mysql_datadir)) {
msg("Backup files error"); returnfalse;
}
if (!backup_wait_for_lsn(server_lsn_after_lock)) { returnfalse;
}
corrupted_pages.backup_fix_ddl(backup_datasinks.m_data,
backup_datasinks.m_meta);
if (!m_aria_backup.copy_log_tail()) {
msg("Error on Aria log tail copy"); returnfalse;
}
// Wait for Aria tables copy finish if (!m_aria_backup.wait_for_finish()) {
msg("Aria data files backup process is finished with error"); returnfalse;
} // Wait for non-InnoDB-Aria-RocksDB engines copy finish if (!m_common_backup.wait_for_finish()) {
msg("Data files backup process is finished with error"); returnfalse;
}
bool stage_block_commit(Backup_datasinks &backup_datasinks) {
msg("Starting BACKUP STAGE BLOCK_COMMIT"); if (!opt_no_lock) { if (!lock_for_backup_stage_commit(m_bs_con)) {
msg("Error on BACKUP STAGE BLOCK_COMMIT query execution"); returnfalse;
}
msg("Acquired locks for BACKUP STAGE BLOCK_COMMIT");
}
// Copy log tables tail if (!m_common_backup.copy_log_tables(true) ||
!m_common_backup.close_log_tables()) {
msg("Error on copy log tables"); returnfalse;
}
// Copy just enough log to cover the latest commit. // Meanwhile, there could be some active transactions // that are modifying the database and writing more // log. Not copying log for them will save busy work // and avoid some rollback of the incomplete // transactions after the backup has been restored. // // We find the current InnoDB LSN by executing // SHOW ENGINE INNODB STATUS, which in the case of // general_log=1, log_output='TABLES' // would be written to the table mysql.general_log // that we just finished copying above. if (!backup_wait_for_commit_lsn()) { returnfalse;
}
// Copy stats tables if (!m_common_backup.copy_stats_tables()) {
msg("Error on copy stats tables"); returnfalse;
}
// Copy system Aria files if (!m_aria_backup.finalize()) {
msg("Error on finalize Aria tables backup"); returnfalse;
}
if (!m_common_backup.wait_for_finish()) {
msg("Error on finish common engines backup"); returnfalse;
}
if (!backup_files_from_datadir(backup_datasinks.m_data,
fil_path_to_mysql_datadir, "aws-kms-key")) {
msg("Error on root data dir files backup"); returnfalse;
}
if (!backup_files_from_datadir(backup_datasinks.m_data,
fil_path_to_mysql_datadir, "mariadb_upgrade_info")) {
msg("Error on root data dir files backup"); returnfalse;
}
if (has_rocksdb_plugin()) {
rocksdb_create_checkpoint();
}
// 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. if (opt_no_lock) { if (opt_safe_slave_backup) { if (!wait_for_safe_slave(mysql_connection)) { return(false);
}
}
}
if (opt_slave_info) { if (!write_slave_info(backup_datasinks.m_data,
mysql_connection)) { return(false);
}
}
/* The only reason why Galera/binlog info is written before wait_for_ibbackup_log_copy_finish()isthatafterthatcallthextrabackup binarywillstartstreamingatemporarycopyofREDOlogtostdoutand thus,anystreamingfrominnobackupexwouldinterfere.Theonlywayto avoidthatistohaveasingleprocess,i.e.mergeinnobackupexand
xtrabackup. */ if (opt_galera_info) { if (!write_galera_info(backup_datasinks.m_data,
m_bs_con)) { return(false);
}
}
/* create extra LSN dir if it does not exist. */ if (xtrabackup_extra_lsndir
&&!my_stat(xtrabackup_extra_lsndir,&stat_info,MYF(0))
&& (my_mkdir(xtrabackup_extra_lsndir,0777,MYF(0)) < 0)) {
msg("Error: cannot mkdir %d: %s\n",
my_errno, xtrabackup_extra_lsndir); goto fail;
}
/* create target dir if not exist */ if (!xtrabackup_stream_str && !my_stat(xtrabackup_target_dir,&stat_info,MYF(0))
&& (my_mkdir(xtrabackup_target_dir,0777,MYF(0)) < 0)){
msg("Error: cannot mkdir %d: %s\n",
my_errno, xtrabackup_target_dir); goto fail;
}
backup_datasinks.init();
/* open the log file */
memset(&stat_info, 0, sizeof(MY_STAT));
dst_log_file =
ds_open(backup_datasinks.m_redo, "ib_logfile0", &stat_info); if (dst_log_file == NULL) {
msg("Error: failed to open the target stream" " for 'ib_logfile0'."); goto fail;
}
/* try to wait for a log checkpoint, but do not fail if the
server does not support this */ if (innodb_log_checkpoint_now != false) {
mysql_read_query_result(mysql_connection);
msg("Finished waiting for checkpoint");
}
if (!select_history()) { goto fail;
}
/* label it */
recv_sys.file_checkpoint = log_sys.last_checkpoint_lsn;
log_hdr_init(); /* Write log header*/ if (ds_write(dst_log_file, log_hdr_buf, 12288)) {
msg("error: write to logfile failed"); goto fail;
}
log_copying_running = true;
/* Disable further DDL on backed up tables (only needed for --no-lock).*/
mysql_mutex_lock(&recv_sys.mutex);
log_file_op = backup_file_op_fail;
mysql_mutex_unlock(&recv_sys.mutex);
for (space_id_to_name_t::iterator iter = ddl_tracker.tables_in_backup.begin();
iter != ddl_tracker.tables_in_backup.end();
iter++) {
uint32_t id = iter->first; const std::string &name = iter->second;
if (ddl_tracker.drops.find(id) != ddl_tracker.drops.end()) {
dropped_tables.insert(name);
drop_space(id); continue;
}
if (ddl_tracker.id_to_name.find(id) == ddl_tracker.id_to_name.end()) { continue;
}
/* tablespace was affected by DDL. */ const std::string new_name = ddl_tracker.id_to_name[id]; if (new_name != name) {
renamed_tables[name] = new_name; if (opt_log_innodb_page_corruption)
rename_space(id, new_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++) {
uint32_t id = iter->first; const std::string &name = iter->second;
if (ddl_tracker.drops.find(id) == ddl_tracker.drops.end()
&& ddl_tracker.deferred_tables.find(id)
== ddl_tracker.deferred_tables.end()) {
dropped_tables.erase(name);
new_tables[id] = name; if (opt_log_innodb_page_corruption)
drop_space(id);
}
}
// 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;
DBUG_ASSERT(ds_data);
ds_data->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);
ds_data->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; 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; if (n->is_open()) {
mysql_mutex_lock(&fil_system.mutex);
n->close();
mysql_mutex_unlock(&fil_system.mutex);
}
fil_space_free(n->space->id, false);
}
/* Mariabackup detected the FILE_MODIFY or FILE_RENAME forthedeferredtablespace.Soitneedstoreadthe tablespaceagainifinnodbdoesn'thavepage0initialization
redo log for it */ for (space_id_to_name_t::iterator iter =
ddl_tracker.deferred_tables.begin();
iter != ddl_tracker.deferred_tables.end();
iter++) { if (check_if_skip_table(iter->second.c_str())) { continue;
}
if (first_page_init_ids.find(iter->first)
!= first_page_init_ids.end()) {
new_tables[iter->first] = iter->second.c_str(); continue;
}
/* Mariabackup doesn't detect any FILE_OP for the deferred tablespace.Thereisapossibilitythatpage0could've
been corrupted persistently in the disk */ for (auto space_name: defer_space_names) { if (!check_if_skip_table(space_name.c_str())) {
xb_load_single_table_tablespace(
space_name, false);
}
}
srv_operation = SRV_OPERATION_BACKUP;
for (constauto &t : new_tables) { if (!check_if_skip_table(t.second.c_str())) {
xb_load_single_table_tablespace(t.second, false,
t.first);
}
}
datafiles_iter_t it2;
while (fil_node_t *node = datafiles_iter_next(&it2)) { if (!fil_is_user_tablespace_id(node->space->id)) continue;
std::string dest_name= filename_to_spacename(
node->name, strlen(node->name));
dest_name.append(".new");
/*********************************************************************** Generatespathtothemetafilepathfromagivenpathtoanincremental.delta byreplacingtrailing".delta"with".meta",orreturnserrorif'delta_path' doesnotendwiththe".delta"charactersequence.
@return TRUE on success, FALSE on error. */ static
ibool
get_meta_path( constchar *delta_path, /* in: path to a .delta file */ char *meta_path) /* out: path to the corresponding .meta
file */
{
size_t len = strlen(delta_path);
if (len <= 6 || strcmp(delta_path + len - 6, ".delta")) { returnFALSE;
}
memcpy(meta_path, delta_path, len - 6);
strcpy(meta_path + len - 6, XB_DELTA_INFO_SUFFIX);
returnTRUE;
}
/****************************************************************//**
Create a new tablespace on disk andreturn the handle to its opened
file. Code adopted from fil_create_new_single_table_tablespace with
the main difference that only disk file is created without updating
the InnoDB in-memory dictionary data structures.
@returntrue on success, false on error. */ static bool
xb_space_create_file( /*==================*/ constchar* path, /*!<in: path to tablespace */
uint32_t space_id, /*!<in: space id */
uint32_t flags, /*!<in: tablespace flags */
pfs_os_file_t* file) /*!<out: file handle */
{ bool ret;
/* Create the database directory if it doesn't exist yet */ if (!os_file_create_directory(dest_dir, FALSE)) {
msg("mariabackup: error: cannot create dir %s", dest_dir); return file;
}
if (!*success) {
msg("mariabackup: Cannot open file %s\n", real_name);
} exit:
mysql_mutex_unlock(&recv_sys.mutex); return file;
}
const size_t len = strlen(dest_space_name); /* remember space name for further reference */
table = static_cast<xb_filter_entry_t *>
(malloc(sizeof(xb_filter_entry_t) +
len + 1));
/* Free file system buffer cache after the batch was written. */ #ifdef __linux__
os_file_flush_func(dst_file); #endif
posix_fadvise(dst_file, 0, 0, POSIX_FADV_DONTNEED);
incremental_buffers++;
}
aligned_free(incremental_buffer); if (src_file != OS_FILE_CLOSED) {
os_file_close(src_file);
os_file_delete(0,src_path);
} if (dst_file != OS_FILE_CLOSED && info.space_id)
os_file_close(dst_file); returnTRUE;
error:
aligned_free(incremental_buffer); if (src_file != OS_FILE_CLOSED)
os_file_close(src_file); if (dst_file != OS_FILE_CLOSED && info.space_id)
os_file_close(dst_file);
msg("Error: xtrabackup_apply_delta(): " "failed to apply %s to %s.\n", src_path, dst_path); returnFALSE;
}
void rename_file(constchar *from,constchar *to) {
msg("Renaming %s to %s\n", from, to); if (my_rename(from, to, MY_WME)) {
die("Can't rename %s to %s errno %d", from, to, errno);
}
}
staticvoid rename_file(const std::string& from, const std::string &to) {
rename_file(from.c_str(), to.c_str());
} /************************************************************************ Callbacktohandledatadirentry.Functionofthistypewillbecalled foreachentrywhichmatchesthemaskbyxb_process_datadir.
@return should return TRUE on success */ typedef ibool (*handle_datadir_entry_func_t)( /*=========================================*/ constchar* data_home_dir, /*!<in: path to datadir */ constchar* db_name, /*!<in: database name */ constchar* file_name, /*!<in: file name with suffix */ void* arg); /*!<in: caller-provided data */
/** Rename, and replace destination file, if exists */ void rename_force(constchar *from, constchar *to) { if (access(to, R_OK) == 0) {
msg("Removing %s", 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()andbackup_optimized_ddl_op(),to".ibd".Inthecaseof incrementalbackup,i.e.ofargargumentisset,move".new"filesto destinationdirectoryandrenamethemto".ibd",removeexisting".ibd.delta" and".idb.meta"filesinincrementaldirectorytoavoidapplyingdeltato ".ibd"file.
int argc; for (argc = 0; argv[argc]; argc++) {}
encryption_plugin_prepare_init(argc, argv);
xtrabackup_target_dir= mysql_data_home_buff;
xtrabackup_target_dir[0]=FN_CURLIB; // all paths are relative from here
xtrabackup_target_dir[1]=0; const lsn_t target_lsn = xtrabackup_incremental
? incremental_to_lsn : metadata_to_lsn;
/* readmetadataoftarget
*/ if (!mb_read_metadata_from_dir(xtrabackup_target_dir)) { return(false);
}
if (!strcmp(metadata_type, "full-backuped")) { if (xtrabackup_incremental) {
msg("error: applying incremental backup " "needs a prepared target."); return(false);
}
msg("This target seems to be not prepared yet.");
} elseif (!strcmp(metadata_type, "log-applied")) {
msg("This target seems to be already prepared.");
} else {
msg("This target does not have correct metadata."); return(false);
}
bool ok = !xtrabackup_incremental
|| metadata_to_lsn == incremental_lsn; if (!ok) {
msg("error: This incremental backup seems " "not to be proper for the target. Check 'to_lsn' of the target and " "'from_lsn' of the incremental."); return(false);
}
srv_n_purge_threads = 1;
xb_filters_init();
srv_log_group_home_dir = NULL;
if (xtrabackup_incremental) {
srv_operation = SRV_OPERATION_RESTORE_DELTA;
if (innodb_init_param()) {
error:
ok = false; goto cleanup;
}
mysql_mutex_lock(&recv_sys.mutex);
ok = fil_system.sys_space->open(false);
mysql_mutex_unlock(&recv_sys.mutex); if (ok) ok = xtrabackup_apply_deltas();
xb_data_files_close();
if (ok) { /* Cleanup datadir from tablespaces deleted
between full and incremental backups */
if (ok) {
msg("Last binlog file %s, position %lld",
trx_sys.recovered_binlog_filename,
longlong(trx_sys.recovered_binlog_offset));
/* output to xtrabackup_binlog_pos_innodb and (if backup_safe_binlog_infowasavailableontheserver)to xtrabackup_binlog_info.Inthelattercase xtrabackup_binlog_pos_innodbbecomesredundantandiscreated
only for compatibility. */
ok = store_binlog_info( "xtrabackup_binlog_pos_innodb",
trx_sys.recovered_binlog_filename,
trx_sys.recovered_binlog_offset)
&& (!recover_binlog_info || store_binlog_info(
XTRABACKUP_BINLOG_INFO,
trx_sys.recovered_binlog_filename,
trx_sys.recovered_binlog_offset));
}
/* Check whether the log is applied enough or not. */ if (recv_sys.lsn && recv_sys.lsn < target_lsn) {
msg("mariabackup: error: " "The log was only applied up to LSN " LSN_PF ", instead of " LSN_PF, recv_sys.lsn, target_lsn);
ok = false;
} #ifdef WITH_WSREP elseif (ok) xb_write_galera_info(xtrabackup_incremental); #endif
innodb_shutdown();
innodb_free_param();
/* output to metadata file */ if (ok) { char filename[FN_REFLEN];
snprintf(filename, sizeof(filename), "%s/%s", xtrabackup_target_dir, MB_METADATA_FILENAME); if (!xtrabackup_write_metadata(filename)) {
msg("mariabackup: Error: failed to write metadata " "to '%s'", filename);
ok = false;
} elseif (xtrabackup_extra_lsndir) {
snprintf(filename, sizeof(filename), "%s/%s", xtrabackup_extra_lsndir, MB_METADATA_FILENAME); if (!xtrabackup_write_metadata(filename)) {
msg("mariabackup: Error: failed to write " "metadata to '%s'", filename);
ok = false;
}
}
}
if (ok) ok = apply_log_finish();
if (ok && xtrabackup_export)
ok= (prepare_export() == 0);
if (ok) ok = aria::prepare(xtrabackup_target_dir);
cleanup:
xb_filters_free(); return ok && !ib::error::was_logged() && corrupted_pages.empty();
}
/**************************************************************************
Append group name to xb_load_default_groups list. */ static void
append_defaults_group(constchar *group, constchar *default_groups[],
size_t default_groups_size)
{
uint i; bool appended = false; for (i = 0; i < default_groups_size - 1; i++) { if (default_groups[i] == NULL) {
default_groups[i] = group;
appended = true; break;
}
}
ut_a(appended);
}
staticconstchar*
normalize_privilege_target_name(constchar* name)
{ if (strcmp(name, "*") == 0) { return"\\*";
} else { /* should have no regex special characters. */
ut_ad(strpbrk(name, ".()[]*+?") == 0);
} return name;
}
/******************************************************************//**
Check if specific privilege is granted.
Uses regexp magic to check if requested privilege is granted for given
database.table or database.* or *.* orif user has 'ALL PRIVILEGES' granted.
@returntrueif requested privilege is granted, false otherwise. */ staticbool
has_privilege(const std::list<std::string> &granted, constchar* required, constchar* db_name, constchar* table_name)
{ char buffer[1000];
regex_t priv_re;
regmatch_t tables_regmatch[1]; bool result = false;
int written = snprintf(buffer, sizeof(buffer), "GRANT .*(%s)|(ALL PRIVILEGES).* ON (\\*|`%s`)\\.(\\*|`%s`)",
required, db_name, table_name); if (written < 0 || written == sizeof(buffer)
|| regcomp(&priv_re, buffer, REG_EXTENDED)) {
die("regcomp() failed for '%s'", buffer);
}
typedef std::list<std::string>::const_iterator string_iter; for (string_iter i = granted.begin(), e = granted.end(); i != e; ++i) { int res = regexec(&priv_re, i->c_str(), 1, tables_regmatch, 0);
if (res != REG_NOMATCH) {
result = true; break;
}
}
FetchesDBuserprivileges,determinesintendedactionsbasedon command-lineargumentsandprintsmissingprivileges.
@return whether all the necessary privileges are granted */ staticbool check_all_privileges()
{ if (!mysql_connection) { /* Not connected, no queries is going to be executed. */ returntrue;
}
/* SHOW MASTER STATUS */ /* SHOW SLAVE STATUS */ if (opt_galera_info || opt_slave_info
|| opt_safe_slave_backup) {
check_result |= check_privilege(granted_privileges, "SLAVE MONITOR", "*", "*",
PRIVILEGE_WARNING);
}
if (check_result & PRIVILEGE_ERROR) {
msg("Current privileges, as reported by 'SHOW GRANTS': "); int n=1; for (std::list<std::string>::const_iterator it = granted_privileges.begin();
it != granted_privileges.end();
it++,n++) {
msg(" %d.%s", n, it->c_str());
} returnfalse;
}
if (opt_slave_info
&& opt_no_lock
&& !opt_safe_slave_backup) {
msg("Error: --slave-info is used with --no-lock but " "without --safe-slave-backup. The binlog position " "cannot be consistent with the backup data."); return(false);
}
n_mixed_options = 0;
if (opt_decompress) {
mixed_options[n_mixed_options++] = "--decompress";
}
if (xtrabackup_copy_back) {
mixed_options[n_mixed_options++] = "--copy-back";
}
if (xtrabackup_move_back) {
mixed_options[n_mixed_options++] = "--move-back";
}
if (xtrabackup_prepare) {
mixed_options[n_mixed_options++] = "--apply-log";
}
if (n_mixed_options > 1) {
msg("Error: %s and %s are mutually exclusive\n",
mixed_options[0], mixed_options[1]); return(false);
}
if (xtrabackup_backup) { if ((mysql_connection = xb_mysql_connect()) == NULL) { return(false);
}
if (!get_mysql_vars(mysql_connection)) { return(false);
}
if (opt_check_privileges && !check_all_privileges()) { return(false);
}
history_start_time = time(NULL);
}
return(true);
}
externvoid init_signals(void);
#include <sql_locale.h>
void setup_error_messages()
{
my_default_lc_messages = &my_locale_en_US; if (init_errmessage())
die("could not initialize error messages");
}
setup_error_messages();
sys_var_init();
plugin_mutex_init();
mysql_prlock_init(key_rwlock_LOCK_system_variables_hash, &LOCK_system_variables_hash);
test_flags |= TEST_SIGINT;
init_signals(); #ifndef _WIN32 /* Exit process on SIGINT. */
my_sigset(SIGINT, SIG_DFL); #endif
sf_leaking_memory = 1; /* don't report memory leaks on early exist */
int i; int ho_error;
char* target_dir = NULL; bool prepare = false;
char conf_file[FN_REFLEN];
// array_elements() will not work for load_defaults, as it is defined // as external symbol, so let's use dynamic array to have ability to // add new server default groups
std::vector<constchar *> server_default_groups;
for (constchar **default_group= load_default_groups; *default_group;
++default_group)
server_default_groups.push_back(*default_group);
int n; for (n = 0; (*argv_server)[n]; n++) {};
argc_server = n;
print_param_str << "# This MySQL options file was generated by XtraBackup.\n" "[" << defaults_group << "]\n";
/* Reset u_max_value for all options, as we don't want the
--maximum-... modifier to set the actual option values */ for (my_option *optp= xb_server_options; optp->name; optp++) {
optp->u_max_value = (G_PTR *) &global_max_value;
}
/* Throw a descriptive error if --defaults-file or --defaults-extra-file
is not the first command line argument */ for (int i = 2 ; i < argc ; i++) { char *optend = strcend((argv)[i], '=');
if (optend - argv[i] == 15 &&
!strncmp(argv[i], "--defaults-file", optend - argv[i])) {
die("--defaults-file must be specified first on the command line");
} if (optend - argv[i] == 21 &&
!strncmp(argv[i], "--defaults-extra-file",
optend - argv[i])) {
die("--defaults-extra-file must be specified first on the command line");
}
}
if (argc_server > 0
&& (ho_error=handle_options(&argc_server, argv_server,
xb_server_options, xb_get_one_option))) exit(ho_error);
/* 2) Load client groups and process client options, ignore unknown
options */
/* 4) Process --mysqld-args options, ignore unknown options */
my_getopt_skip_unknown = TRUE;
int argc_mysqld = static_cast<int>(mysqld_args.size()); if (argc_mysqld > 1)
{ char **argv_mysqld= &mysqld_args[0]; if ((ho_error= handle_options(&argc_mysqld, &argv_mysqld,
xb_server_options, xb_get_one_option))) exit(ho_error);
}
my_handle_options_init_variables = TRUE;
/* Reject command line arguments that don't look like options, i.e. are notoftheform'-X'(single-characteroptions)or'--option'(long
options) */ for (int i = 0 ; i < argc_backup ; i++) { constchar * const opt = (*argv_backup)[i];
/* get default temporary directory */ if (!opt_mysql_tmpdir || !opt_mysql_tmpdir[0]) {
opt_mysql_tmpdir = getenv("TMPDIR"); #ifdefined(_WIN32) if (!opt_mysql_tmpdir) {
opt_mysql_tmpdir = getenv("TEMP");
} if (!opt_mysql_tmpdir) {
opt_mysql_tmpdir = getenv("TMP");
} #endif if (!opt_mysql_tmpdir || !opt_mysql_tmpdir[0]) {
opt_mysql_tmpdir = const_cast<char*>(DEFAULT_TMPDIR);
}
}
/* temporary setting of enough size */
srv_page_size_shift = UNIV_PAGE_SIZE_SHIFT_MAX;
srv_page_size = UNIV_PAGE_SIZE_MAX; if (xtrabackup_backup && xtrabackup_incremental) { /* direct specification is only for --backup */ /* and the lsn is prior to the other option */
char* endchar; int error = 0;
incremental_lsn = strtoll(xtrabackup_incremental, &endchar, 10); if (*endchar != '\0')
error = 1;
if (error) {
msg("mariabackup: value '%s' may be wrong format for " "incremental option.", xtrabackup_incremental); return(EXIT_FAILURE);
}
} elseif (xtrabackup_backup && xtrabackup_incremental_basedir) { if (!mb_read_metadata_from_dir(xtrabackup_incremental_basedir)) { return(EXIT_FAILURE);
}
if (xtrabackup_stream && !xtrabackup_backup) {
msg("Warning: --stream parameter is ignored, it only works together with --backup.");
}
if (!xb_init()) { return(EXIT_FAILURE);
}
/* --print-param */ if (xtrabackup_print_param) {
printf("%s", print_param_str.str().c_str()); return(EXIT_SUCCESS);
}
print_version(); if (xtrabackup_incremental) {
msg("incremental backup from " LSN_PF " is enabled.",
incremental_lsn);
}
if (xtrabackup_export && !srv_file_per_table) {
msg("mariabackup: auto-enabling --innodb-file-per-table due to " "the --export option");
srv_file_per_table = TRUE;
}
/* cannot execute both for now */
{ int num = 0;
if (xtrabackup_backup) num++; if (xtrabackup_prepare) num++; if (xtrabackup_copy_back) num++; if (xtrabackup_move_back) num++; if (xtrabackup_decrypt_decompress) num++; if (num != 1) { /* !XOR (for now) */
usage(); return(EXIT_FAILURE);
}
}
ut_ad(!field_ref_zero); if (auto b = aligned_malloc(UNIV_PAGE_SIZE_MAX, 4096)) {
field_ref_zero = static_cast<byte*>(
memset_aligned<4096>(b, 0, UNIV_PAGE_SIZE_MAX));
} else {
msg("Can't allocate memory for field_ref_zero"); return EXIT_FAILURE;
}
auto _ = make_scope_exit([]() {
aligned_free(const_cast<byte*>(field_ref_zero));
field_ref_zero = nullptr;
});
/* --backup */ if (xtrabackup_backup && !xtrabackup_backup_func()) { return(EXIT_FAILURE);
}
/* --prepare */ if (xtrabackup_prepare
&& !xtrabackup_prepare_func(argv)) { return(EXIT_FAILURE);
}
if (xtrabackup_copy_back || xtrabackup_move_back) { if (!check_if_param_set("datadir")) {
mysql_data_home = get_default_datadir();
} if (!copy_back()) return(EXIT_FAILURE);
}
if (xtrabackup_decrypt_decompress && !decrypt_decompress()) { return(EXIT_FAILURE);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.