mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-31 02:46:29 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			514 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			514 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "common_engine.h"
 | |
| #include "backup_copy.h"
 | |
| #include "xtrabackup.h"
 | |
| #include "common.h"
 | |
| #include "backup_debug.h"
 | |
| 
 | |
| #include <unordered_map>
 | |
| #include <atomic>
 | |
| #include <memory>
 | |
| #include <chrono>
 | |
| 
 | |
| namespace common_engine {
 | |
| 
 | |
| class Table {
 | |
| public:
 | |
| 	Table(std::string &db, std::string &table, std::string &fs_name) :
 | |
| 		m_db(std::move(db)), m_table(std::move(table)),
 | |
| 		m_fs_name(std::move(fs_name)) {}
 | |
| 	virtual ~Table() {}
 | |
| 	void add_file_name(const char *file_name) { m_fnames.push_back(file_name); }
 | |
| 	virtual bool copy(ds_ctxt_t *ds, MYSQL *con, bool no_lock,
 | |
| 		bool finalize, unsigned thread_num);
 | |
| 	std::string &get_db() { return m_db; }
 | |
| 	std::string &get_table() { return m_table; }
 | |
| 	std::string &get_version() { return m_version; }
 | |
| 
 | |
| protected:
 | |
| 	std::string m_db;
 | |
| 	std::string m_table;
 | |
| 	std::string m_fs_name;
 | |
| 	std::string m_version;
 | |
| 	std::vector<std::string> m_fnames;
 | |
| };
 | |
| 
 | |
| bool
 | |
| Table::copy(ds_ctxt_t *ds, MYSQL *con, bool no_lock, bool, unsigned thread_num) {
 | |
| 	static const size_t buf_size = 10 * 1024 * 1024;
 | |
| 	std::unique_ptr<uchar[]> buf;
 | |
| 	bool result = false;
 | |
| 	File frm_file = -1;
 | |
| 	std::vector<File> files;
 | |
| 	bool locked = false;
 | |
| 	std::string full_tname("`");
 | |
| 	full_tname.append(m_db).append("`.`").append(m_table).append("`");
 | |
| 
 | |
| 	if (!no_lock && !backup_lock(con, full_tname.c_str())) {
 | |
| 		msg(thread_num, "Error on executing BACKUP LOCK for table %s",
 | |
| 			full_tname.c_str());
 | |
| 		goto exit;
 | |
| 	}
 | |
| 	else
 | |
| 		locked = !no_lock;
 | |
| 
 | |
| 	if ((frm_file = mysql_file_open(key_file_frm, (m_fs_name + ".frm").c_str(),
 | |
| 		O_RDONLY | O_SHARE, MYF(0))) < 0 && !m_fnames.empty() &&
 | |
| 		!ends_with(m_fnames[0].c_str(), ".ARZ") &&
 | |
| 		!ends_with(m_fnames[0].c_str(), ".ARM")) {
 | |
| 		// Don't treat it as error, as the table can be dropped after it
 | |
| 		// was added to queue for copying
 | |
| 		result = true;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	for (const auto &fname : m_fnames) {
 | |
| 		File file = mysql_file_open(0, fname.c_str(),O_RDONLY | O_SHARE, MYF(0));
 | |
| 		if (file < 0) {
 | |
| 			char buf[MYSYS_STRERROR_SIZE];
 | |
| 			msg(thread_num, "Error %i on file %s open during %s table copy: %s",
 | |
| 			    errno, fname.c_str(), full_tname.c_str(),
 | |
| 			    my_strerror(buf, sizeof(buf), errno));
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		files.push_back(file);
 | |
| 	}
 | |
| 
 | |
| 	if (locked && !backup_unlock(con)) {
 | |
| 		msg(thread_num, "Error on BACKUP UNLOCK for table %s", full_tname.c_str());
 | |
| 		locked = false;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	locked = false;
 | |
| 
 | |
| 	buf.reset(new uchar[buf_size]);
 | |
| 
 | |
| 	for (size_t i = 0; i < m_fnames.size(); ++i) {
 | |
| 		ds_file_t *dst_file = nullptr;
 | |
| 		size_t bytes_read;
 | |
| 		size_t copied_size = 0;
 | |
| 		MY_STAT stat_info;
 | |
| 
 | |
| 		if (my_fstat(files[i], &stat_info, MYF(0))) {
 | |
| 			msg(thread_num, "error: failed to get stat info for file %s of "
 | |
| 					"table %s", m_fnames[i].c_str(), full_tname.c_str());
 | |
| 			goto exit;
 | |
| 		}
 | |
| 
 | |
| 		const char	*dst_path =
 | |
| 			(xtrabackup_copy_back || xtrabackup_move_back) ?
 | |
| 			m_fnames[i].c_str() : trim_dotslash(m_fnames[i].c_str());
 | |
| 
 | |
| 		dst_file = ds_open(ds, dst_path, &stat_info, false);
 | |
| 		if (!dst_file) {
 | |
| 			msg(thread_num, "error: cannot open destination stream for %s, table %s",
 | |
| 				dst_path, full_tname.c_str());
 | |
| 			goto exit;
 | |
| 		}
 | |
| 
 | |
| 		while ((bytes_read = my_read(files[i], buf.get(), buf_size, MY_WME))) {
 | |
| 			if (bytes_read == size_t(-1)) {
 | |
| 				msg(thread_num, "error: file %s read for table %s",
 | |
| 					m_fnames[i].c_str(), full_tname.c_str());
 | |
| 				ds_close(dst_file);
 | |
| 				goto exit;
 | |
| 			}
 | |
| 			xtrabackup_io_throttling();
 | |
| 			if (ds_write(dst_file, buf.get(), bytes_read)) {
 | |
| 				msg(thread_num, "error: file %s write for table %s",
 | |
| 					dst_path, full_tname.c_str());
 | |
| 				ds_close(dst_file);
 | |
| 				goto exit;
 | |
| 			}
 | |
| 			copied_size += bytes_read;
 | |
| 		}
 | |
| 		mysql_file_close(files[i], MYF(MY_WME));
 | |
| 		files[i] = -1;
 | |
| 		ds_close(dst_file);
 | |
| 		msg(thread_num, "Copied file %s for table %s, %zu bytes",
 | |
| 			m_fnames[i].c_str(), full_tname.c_str(), copied_size);
 | |
| 	}
 | |
| 
 | |
| 	result = true;
 | |
| 
 | |
| #ifndef DBUG_OFF
 | |
| 	{
 | |
| 		std::string sql_name(m_db);
 | |
| 		sql_name.append("/").append(m_table);
 | |
| 		DBUG_MARIABACKUP_EVENT_LOCK("after_ce_table_copy", fil_space_t::name_type(sql_name.data(), sql_name.size()));
 | |
| 	}
 | |
| #endif // DBUG_OFF
 | |
| exit:
 | |
| 	if (frm_file >= 0) {
 | |
| 		m_version = ::read_table_version_id(frm_file);
 | |
| 		mysql_file_close(frm_file, MYF(MY_WME));
 | |
| 	}
 | |
| 	if (locked && !backup_unlock(con)) {
 | |
| 		msg(thread_num, "Error on BACKUP UNLOCK for table %s", full_tname.c_str());
 | |
| 		result = false;
 | |
| 	}
 | |
| 	for (auto file : files)
 | |
| 		if (file >= 0)
 | |
| 			mysql_file_close(file, MYF(MY_WME));
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| // Append-only tables
 | |
| class LogTable : public Table {
 | |
| 	public:
 | |
| 		LogTable(std::string &db, std::string &table, std::string &fs_name) :
 | |
| 			Table(db, table, fs_name) {}
 | |
| 
 | |
| 		virtual ~LogTable() { (void)close(); }
 | |
| 		bool
 | |
| 			copy(ds_ctxt_t *ds, MYSQL *con, bool no_lock, bool finalize,
 | |
| 			unsigned thread_num) override;
 | |
| 		bool close();
 | |
| 	private:
 | |
| 		bool open(ds_ctxt_t *ds, unsigned thread_num);
 | |
| 		std::vector<File> m_src;
 | |
| 		std::vector<ds_file_t *> m_dst;
 | |
| };
 | |
| 
 | |
| bool
 | |
| LogTable::open(ds_ctxt_t *ds, unsigned thread_num) {
 | |
| 	DBUG_ASSERT(m_src.empty());
 | |
| 	DBUG_ASSERT(m_dst.empty());
 | |
| 
 | |
| 	std::string full_tname("`");
 | |
| 	full_tname.append(m_db).append("`.`").append(m_table).append("`");
 | |
| 
 | |
| 	for (const auto &fname : m_fnames) {
 | |
| 		File file = mysql_file_open(0, fname.c_str(),O_RDONLY | O_SHARE, MYF(0));
 | |
| 		if (file < 0) {
 | |
| 			msg(thread_num, "Error on file %s open during %s log table copy",
 | |
| 				fname.c_str(), full_tname.c_str());
 | |
| 			return false;
 | |
| 		}
 | |
| 		m_src.push_back(file);
 | |
| 
 | |
| 		MY_STAT stat_info;
 | |
| 		if (my_fstat(file, &stat_info, MYF(0))) {
 | |
| 			msg(thread_num, "error: failed to get stat info for file %s of "
 | |
| 					"log table %s", fname.c_str(), full_tname.c_str());
 | |
| 			return false;
 | |
| 		}
 | |
| 		const char	*dst_path =
 | |
| 			(xtrabackup_copy_back || xtrabackup_move_back) ?
 | |
| 			fname.c_str() : trim_dotslash(fname.c_str());
 | |
| 		ds_file_t *dst_file = ds_open(ds, dst_path, &stat_info, false);
 | |
| 		if (!dst_file) {
 | |
| 			msg(thread_num, "error: cannot open destination stream for %s, "
 | |
| 				"log table %s", dst_path, full_tname.c_str());
 | |
| 			return false;
 | |
| 		}
 | |
| 		m_dst.push_back(dst_file);
 | |
| 	}
 | |
| 
 | |
| 	File frm_file;
 | |
| 	if ((frm_file = mysql_file_open(key_file_frm, (m_fs_name + ".frm").c_str(),
 | |
| 		O_RDONLY | O_SHARE, MYF(0))) < 0 && !m_fnames.empty() &&
 | |
| 		!ends_with(m_fnames[0].c_str(), ".ARZ") &&
 | |
| 		!ends_with(m_fnames[0].c_str(), ".ARM")) {
 | |
| 		msg(thread_num, "Error on .frm file open for log table %s",
 | |
| 			full_tname.c_str());
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	m_version = ::read_table_version_id(frm_file);
 | |
| 	mysql_file_close(frm_file, MYF(MY_WME));
 | |
| 
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| bool LogTable::close() {
 | |
| 	while (!m_src.empty()) {
 | |
| 		auto f = m_src.back();
 | |
| 		m_src.pop_back();
 | |
| 		mysql_file_close(f, MYF(MY_WME));
 | |
| 	}
 | |
| 	while (!m_dst.empty()) {
 | |
| 		auto f = m_dst.back();
 | |
| 		m_dst.pop_back();
 | |
| 		ds_close(f);
 | |
| 	}
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| bool
 | |
| LogTable::copy(ds_ctxt_t *ds, MYSQL *con, bool no_lock, bool finalize,
 | |
| 	unsigned thread_num) {
 | |
| 	static const size_t buf_size = 10 * 1024 * 1024;
 | |
| 	DBUG_ASSERT(ds);
 | |
| 	DBUG_ASSERT(con);
 | |
| 	if (m_src.empty() && !open(ds, thread_num)) {
 | |
| 		close();
 | |
| 		return false;
 | |
| 	}
 | |
| 	DBUG_ASSERT(m_src.size() == m_dst.size());
 | |
| 
 | |
| 	std::unique_ptr<uchar[]> buf(new uchar[buf_size]);
 | |
| 	for (size_t i = 0; i < m_src.size(); ++i) {
 | |
| 		// .CSM can be rewritten (see write_meta_file() usage in ha_tina.cc)
 | |
| 		if (!finalize && ends_with(m_fnames[i].c_str(), ".CSM"))
 | |
| 			continue;
 | |
| 		size_t bytes_read;
 | |
| 		size_t copied_size = 0;
 | |
| 		while ((bytes_read = my_read(m_src[i], buf.get(), buf_size, MY_WME))) {
 | |
| 			if (bytes_read == size_t(-1)) {
 | |
| 				msg(thread_num, "error: file %s read for log table %s",
 | |
| 					m_fnames[i].c_str(),
 | |
| 					std::string("`").append(m_db).append("`.`").
 | |
| 					append(m_table).append("`").c_str());
 | |
| 				close();
 | |
| 				return false;
 | |
| 			}
 | |
| 			xtrabackup_io_throttling();
 | |
| 			if (ds_write(m_dst[i], buf.get(), bytes_read)) {
 | |
| 				msg(thread_num, "error: file %s write for log table %s",
 | |
| 					m_fnames[i].c_str(), std::string("`").append(m_db).append("`.`").
 | |
| 					append(m_table).append("`").c_str());
 | |
| 				close();
 | |
| 				return false;
 | |
| 			}
 | |
| 			copied_size += bytes_read;
 | |
| 		}
 | |
| 		msg(thread_num, "Copied file %s for log table %s, %zu bytes",
 | |
| 			m_fnames[i].c_str(), std::string("`").append(m_db).append("`.`").
 | |
| 					append(m_table).append("`").c_str(), copied_size);
 | |
| 	}
 | |
| 
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| class BackupImpl {
 | |
| 	public:
 | |
| 		BackupImpl(
 | |
| 				const char *datadir_path, ds_ctxt_t *datasink,
 | |
| 				std::vector<MYSQL *> &con_pool, ThreadPool &thread_pool) :
 | |
| 			m_datadir_path(datadir_path), m_ds(datasink), m_con_pool(con_pool),
 | |
| 			m_process_table_jobs(thread_pool) {}
 | |
| 		~BackupImpl() { }
 | |
| 		bool scan(
 | |
| 				const std::unordered_set<std::string> &exclude_tables,
 | |
| 				std::unordered_set<std::string> *out_processed_tables,
 | |
| 				bool no_lock, bool collect_log_and_stats);
 | |
| 		void set_post_copy_table_hook(const post_copy_table_hook_t &hook) {
 | |
| 			m_table_post_copy_hook = hook;
 | |
| 		}
 | |
| 		bool copy_log_tables(bool finalize);
 | |
| 		bool copy_stats_tables();
 | |
| 		bool wait_for_finish();
 | |
| 		bool close_log_tables();
 | |
| 	private:
 | |
| 
 | |
| 		void process_table_job(Table *table, bool no_lock, bool delete_table,
 | |
| 			bool finalize, unsigned thread_num);
 | |
| 
 | |
| 		const char *m_datadir_path;
 | |
| 		ds_ctxt_t *m_ds;
 | |
| 		std::vector<MYSQL *> &m_con_pool;
 | |
| 		TasksGroup m_process_table_jobs;
 | |
| 
 | |
| 		post_copy_table_hook_t m_table_post_copy_hook;
 | |
| 		std::unordered_map<table_key_t, std::unique_ptr<LogTable>> m_log_tables;
 | |
| 		std::unordered_map<table_key_t, std::unique_ptr<Table>> m_stats_tables;
 | |
| };
 | |
| 
 | |
| void BackupImpl::process_table_job(Table *table, bool no_lock,
 | |
| 	bool delete_table, bool finalize, unsigned thread_num) {
 | |
| 	int result = 0;
 | |
| 
 | |
| 	if (!m_process_table_jobs.get_result())
 | |
| 		goto exit;
 | |
| 
 | |
| 	if (!table->copy(m_ds, m_con_pool[thread_num], no_lock, finalize, thread_num))
 | |
| 		goto exit;
 | |
| 
 | |
| 	if (m_table_post_copy_hook)
 | |
| 		m_table_post_copy_hook(table->get_db(), table->get_table(),
 | |
| 		table->get_version());
 | |
| 
 | |
| 	result = 1;
 | |
| 
 | |
| exit:
 | |
| 	if (delete_table)
 | |
| 		delete table;
 | |
| 	m_process_table_jobs.finish_task(result);
 | |
| }
 | |
| 
 | |
| bool BackupImpl::scan(const std::unordered_set<table_key_t> &exclude_tables,
 | |
| 	std::unordered_set<table_key_t> *out_processed_tables, bool no_lock,
 | |
| 	bool collect_log_and_stats) {
 | |
| 
 | |
| 	msg("Start scanning common engine tables, need backup locks: %d, "
 | |
| 		"collect log and stat tables: %d", no_lock, collect_log_and_stats);
 | |
| 
 | |
| 	std::unordered_map<table_key_t, std::unique_ptr<Table>> found_tables;
 | |
| 
 | |
| 	foreach_file_in_db_dirs(m_datadir_path,
 | |
| 		[&](const char *file_path)->bool {
 | |
| 
 | |
| 		static const char *ext_list[] =
 | |
| 			{".MYD", ".MYI", ".MRG", ".ARM", ".ARZ", ".CSM", ".CSV", NULL};
 | |
| 
 | |
| 		bool is_aria = ends_with(file_path, ".MAD") || ends_with(file_path, ".MAI");
 | |
| 
 | |
| 		if (!collect_log_and_stats && is_aria)
 | |
| 			return true;
 | |
| 
 | |
| 		if (!is_aria && !filename_matches(file_path, ext_list))
 | |
| 			return true;
 | |
| 
 | |
| 		if (check_if_skip_table(file_path)) {
 | |
| 			msg("Skipping %s.", file_path);
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 		auto db_table_fs = convert_filepath_to_tablename(file_path);
 | |
| 		auto tk =
 | |
| 			table_key(std::get<0>(db_table_fs), std::get<1>(db_table_fs));
 | |
| 
 | |
| 		// log and stats tables are only collected in this function,
 | |
| 		// so there is no need to filter out them with exclude_tables.
 | |
| 		if (collect_log_and_stats) {
 | |
| 			if (is_log_table(std::get<0>(db_table_fs).c_str(),
 | |
| 				std::get<1>(db_table_fs).c_str())) {
 | |
| 				auto table_it = m_log_tables.find(tk);
 | |
| 				if (table_it == m_log_tables.end()) {
 | |
| 					msg("Log table found: %s", tk.c_str());
 | |
| 					table_it = m_log_tables.emplace(tk,
 | |
| 						std::unique_ptr<LogTable>(new LogTable(std::get<0>(db_table_fs),
 | |
| 						std::get<1>(db_table_fs), std::get<2>(db_table_fs)))).first;
 | |
| 				}
 | |
| 				msg("Collect log table file: %s", file_path);
 | |
| 				table_it->second->add_file_name(file_path);
 | |
| 				return true;
 | |
| 			}
 | |
| 			// Aria can handle statistics tables
 | |
| 			else if (is_stats_table(std::get<0>(db_table_fs).c_str(),
 | |
| 				std::get<1>(db_table_fs).c_str()) && !is_aria) {
 | |
| 				auto table_it = m_stats_tables.find(tk);
 | |
| 				if (table_it == m_stats_tables.end()) {
 | |
| 					msg("Stats table found: %s", tk.c_str());
 | |
| 					table_it = m_stats_tables.emplace(tk,
 | |
| 						std::unique_ptr<Table>(new Table(std::get<0>(db_table_fs),
 | |
| 						std::get<1>(db_table_fs), std::get<2>(db_table_fs)))).first;
 | |
| 				}
 | |
| 				msg("Collect stats table file: %s", file_path);
 | |
| 				table_it->second->add_file_name(file_path);
 | |
| 				return true;
 | |
| 			}
 | |
| 		} else if (is_log_table(std::get<0>(db_table_fs).c_str(),
 | |
| 			std::get<1>(db_table_fs).c_str()) ||
 | |
| 			is_stats_table(std::get<0>(db_table_fs).c_str(),
 | |
| 			std::get<1>(db_table_fs).c_str()))
 | |
| 			return true;
 | |
| 
 | |
| 		if (is_aria)
 | |
| 			return true;
 | |
| 
 | |
| 		if (exclude_tables.count(tk)) {
 | |
| 			msg("Skip table %s at it is in exclude list", tk.c_str());
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 		auto table_it = found_tables.find(tk);
 | |
| 		if (table_it == found_tables.end()) {
 | |
| 			table_it = found_tables.emplace(tk,
 | |
| 				std::unique_ptr<Table>(new Table(std::get<0>(db_table_fs),
 | |
| 				std::get<1>(db_table_fs), std::get<2>(db_table_fs)))).first;
 | |
| 		}
 | |
| 
 | |
| 		table_it->second->add_file_name(file_path);
 | |
| 
 | |
| 		return true;
 | |
| 	});
 | |
| 
 | |
| 	for (auto &table_it : found_tables) {
 | |
| 		m_process_table_jobs.push_task(
 | |
| 			std::bind(&BackupImpl::process_table_job, this, table_it.second.release(),
 | |
| 					no_lock, true, false, std::placeholders::_1));
 | |
| 		if (out_processed_tables)
 | |
| 			out_processed_tables->insert(table_it.first);
 | |
| 	}
 | |
| 
 | |
| 	msg("Stop scanning common engine tables");
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| bool BackupImpl::copy_log_tables(bool finalize) {
 | |
| 	for (auto &table_it : m_log_tables) {
 | |
| 		// Do not execute BACKUP LOCK for log tables as it's supposed
 | |
| 		// that they must be copied on BLOCK_DDL and BLOCK_COMMIT locks.
 | |
| 		m_process_table_jobs.push_task(
 | |
| 			std::bind(&BackupImpl::process_table_job, this, table_it.second.get(),
 | |
| 					true, false, finalize, std::placeholders::_1));
 | |
| 	}
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| bool BackupImpl::copy_stats_tables() {
 | |
| 	for (auto &table_it : m_stats_tables) {
 | |
| 		// Do not execute BACKUP LOCK for stats tables as it's supposed
 | |
| 		// that they must be copied on BLOCK_DDL and BLOCK_COMMIT locks.
 | |
| 		// Delete stats table object after copy (see process_table_job())
 | |
| 		m_process_table_jobs.push_task(
 | |
| 			std::bind(&BackupImpl::process_table_job, this, table_it.second.release(),
 | |
| 					true, true, false, std::placeholders::_1));
 | |
| 	}
 | |
| 	m_stats_tables.clear();
 | |
| 	return true;
 | |
| }
 | |
| 
 | |
| bool BackupImpl::wait_for_finish() {
 | |
| 	/* Wait for threads to exit */
 | |
| 	return m_process_table_jobs.wait_for_finish();
 | |
| }
 | |
| 
 | |
| bool BackupImpl::close_log_tables() {
 | |
| 	bool result = wait_for_finish();
 | |
| 	for (auto &table_it : m_log_tables)
 | |
| 		table_it.second->close();
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| Backup::Backup(const char *datadir_path, ds_ctxt_t *datasink,
 | |
| 	std::vector<MYSQL *> &con_pool, ThreadPool &thread_pool) :
 | |
| 		m_backup_impl(
 | |
| 			new BackupImpl(datadir_path, datasink, con_pool,
 | |
| 			thread_pool)) { }
 | |
| 
 | |
| Backup::~Backup() {
 | |
| 	delete m_backup_impl;
 | |
| }
 | |
| 
 | |
| bool Backup::scan(
 | |
| 	const std::unordered_set<table_key_t> &exclude_tables,
 | |
| 	std::unordered_set<table_key_t> *out_processed_tables,
 | |
| 	bool no_lock, bool collect_log_and_stats) {
 | |
| 	return m_backup_impl->scan(exclude_tables, out_processed_tables, no_lock,
 | |
| 		collect_log_and_stats);
 | |
| }
 | |
| 
 | |
| bool Backup::copy_log_tables(bool finalize) {
 | |
| 	return m_backup_impl->copy_log_tables(finalize);
 | |
| }
 | |
| 
 | |
| bool Backup::copy_stats_tables() {
 | |
| 	return m_backup_impl->copy_stats_tables();
 | |
| }
 | |
| 
 | |
| bool Backup::wait_for_finish() {
 | |
| 	return m_backup_impl->wait_for_finish();
 | |
| }
 | |
| 
 | |
| bool Backup::close_log_tables() {
 | |
| 	return m_backup_impl->close_log_tables();
 | |
| }
 | |
| 
 | |
| void Backup::set_post_copy_table_hook(const post_copy_table_hook_t &hook) {
 | |
| 	m_backup_impl->set_post_copy_table_hook(hook);
 | |
| }
 | |
| 
 | |
| } // namespace common_engine
 | 
