Commit 1eb2d8f6 authored by Marko Mäkelä's avatar Marko Mäkelä

Merge 10.2 into 10.3

parents 197aa0d8 05153a67
...@@ -1384,6 +1384,30 @@ backup_files(const char *from, bool prep_mode) ...@@ -1384,6 +1384,30 @@ backup_files(const char *from, bool prep_mode)
return(ret); return(ret);
} }
void backup_fix_ddl(void);
#define LSN_PREFIX_IN_SHOW_STATUS "\nLog sequence number "
static lsn_t get_current_lsn(MYSQL *connection) {
MYSQL_RES *res = xb_mysql_query(connection, "SHOW ENGINE INNODB STATUS", true, false);
if (!res)
return 0;
MYSQL_ROW row = mysql_fetch_row(res);
DBUG_ASSERT(row);
if (row) {
const char *p = strstr(row[2],LSN_PREFIX_IN_SHOW_STATUS);
DBUG_ASSERT(p);
if (p)
{
p += sizeof(LSN_PREFIX_IN_SHOW_STATUS) - 1;
return (lsn_t)strtoll(p, NULL, 10);
}
}
mysql_free_result(res);
return 0;
}
lsn_t server_lsn_after_lock;
extern void backup_wait_for_lsn(lsn_t lsn);
/** Start --backup */ /** Start --backup */
bool backup_start() bool backup_start()
{ {
...@@ -1403,6 +1427,7 @@ bool backup_start() ...@@ -1403,6 +1427,7 @@ bool backup_start()
if (!lock_tables(mysql_connection)) { if (!lock_tables(mysql_connection)) {
return(false); return(false);
} }
server_lsn_after_lock = get_current_lsn(mysql_connection);
} }
if (!backup_files(fil_path_to_mysql_datadir, false)) { if (!backup_files(fil_path_to_mysql_datadir, false)) {
...@@ -1417,6 +1442,10 @@ bool backup_start() ...@@ -1417,6 +1442,10 @@ bool backup_start()
rocksdb_create_checkpoint(); rocksdb_create_checkpoint();
} }
msg_ts("Waiting for log copy thread to read lsn %llu\n", (ulonglong)server_lsn_after_lock);
backup_wait_for_lsn(server_lsn_after_lock);
backup_fix_ddl();
// There is no need to stop slave thread before coping non-Innodb data when // 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 // --no-lock option is used because --no-lock option requires that no DDL or
// DML to non-transaction tables can occur. // DML to non-transaction tables can occur.
...@@ -2226,6 +2255,7 @@ static void rocksdb_lock_checkpoint() ...@@ -2226,6 +2255,7 @@ static void rocksdb_lock_checkpoint()
msg_ts("Could not obtain rocksdb checkpont lock\n"); msg_ts("Could not obtain rocksdb checkpont lock\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
mysql_free_result(res);
} }
static void rocksdb_unlock_checkpoint() static void rocksdb_unlock_checkpoint()
......
...@@ -1795,7 +1795,12 @@ mdl_lock_table(ulint space_id) ...@@ -1795,7 +1795,12 @@ mdl_lock_table(ulint space_id)
std::ostringstream lock_query; std::ostringstream lock_query;
lock_query << "SELECT 1 FROM " << full_table_name << " LIMIT 0"; lock_query << "SELECT 1 FROM " << full_table_name << " LIMIT 0";
msg_ts("Locking MDL for %s\n", full_table_name.c_str()); msg_ts("Locking MDL for %s\n", full_table_name.c_str());
xb_mysql_query(mdl_con, lock_query.str().c_str(), false, true); if (mysql_query(mdl_con, lock_query.str().c_str())) {
msg_ts("Warning : locking MDL failed for space id %zu, name %s\n", space_id, full_table_name.c_str());
} else {
MYSQL_RES *r = mysql_store_result(mdl_con);
mysql_free_result(r);
}
} }
pthread_mutex_unlock(&mdl_lock_con_mutex); pthread_mutex_unlock(&mdl_lock_con_mutex);
......
...@@ -109,6 +109,9 @@ Write to a datasink file. ...@@ -109,6 +109,9 @@ Write to a datasink file.
int int
ds_write(ds_file_t *file, const void *buf, size_t len) ds_write(ds_file_t *file, const void *buf, size_t len)
{ {
if (len == 0) {
return 0;
}
return file->datasink->write(file, (const uchar *)buf, len); return file->datasink->write(file, (const uchar *)buf, len);
} }
......
...@@ -130,14 +130,15 @@ Open a source file cursor and initialize the associated read filter. ...@@ -130,14 +130,15 @@ Open a source file cursor and initialize the associated read filter.
be skipped and XB_FIL_CUR_ERROR on error. */ be skipped and XB_FIL_CUR_ERROR on error. */
xb_fil_cur_result_t xb_fil_cur_result_t
xb_fil_cur_open( xb_fil_cur_open(
/*============*/ /*============*/
xb_fil_cur_t* cursor, /*!< out: source file cursor */ xb_fil_cur_t* cursor, /*!< out: source file cursor */
xb_read_filt_t* read_filter, /*!< in/out: the read filter */ xb_read_filt_t* read_filter, /*!< in/out: the read filter */
fil_node_t* node, /*!< in: source tablespace node */ fil_node_t* node, /*!< in: source tablespace node */
uint thread_n) /*!< thread number for diagnostics */ uint thread_n, /*!< thread number for diagnostics */
ulonglong max_file_size)
{ {
bool success; bool success;
int err;
/* Initialize these first so xb_fil_cur_close() handles them correctly /* Initialize these first so xb_fil_cur_close() handles them correctly
in case of error */ in case of error */
cursor->orig_buf = NULL; cursor->orig_buf = NULL;
...@@ -172,7 +173,7 @@ xb_fil_cur_open( ...@@ -172,7 +173,7 @@ xb_fil_cur_open(
"tablespace %s\n", "tablespace %s\n",
thread_n, cursor->abs_path); thread_n, cursor->abs_path);
return(XB_FIL_CUR_ERROR); return(XB_FIL_CUR_SKIP);
} }
mutex_enter(&fil_system.mutex); mutex_enter(&fil_system.mutex);
...@@ -193,14 +194,31 @@ xb_fil_cur_open( ...@@ -193,14 +194,31 @@ xb_fil_cur_open(
cursor->node = node; cursor->node = node;
cursor->file = node->handle; cursor->file = node->handle;
#ifdef _WIN32
if (stat(cursor->abs_path, &cursor->statinfo)) { HANDLE hDup;
msg("[%02u] mariabackup: error: cannot stat %s\n", DuplicateHandle(GetCurrentProcess(),cursor->file.m_file,
GetCurrentProcess(), &hDup, 0, FALSE, DUPLICATE_SAME_ACCESS);
int filenr = _open_osfhandle((intptr_t)hDup, 0);
if (filenr < 0) {
err = EINVAL;
}
else {
err = _fstat64(filenr, &cursor->statinfo);
close(filenr);
}
#else
err = fstat(cursor->file.m_file, &cursor->statinfo);
#endif
if (max_file_size < (ulonglong)cursor->statinfo.st_size) {
cursor->statinfo.st_size = (ulonglong)max_file_size;
}
if (err) {
msg("[%02u] mariabackup: error: cannot fstat %s\n",
thread_n, cursor->abs_path); thread_n, cursor->abs_path);
xb_fil_cur_close(cursor); xb_fil_cur_close(cursor);
return(XB_FIL_CUR_ERROR); return(XB_FIL_CUR_SKIP);
} }
if (srv_file_flush_method == SRV_O_DIRECT if (srv_file_flush_method == SRV_O_DIRECT
...@@ -373,7 +391,9 @@ xb_fil_cur_close( ...@@ -373,7 +391,9 @@ xb_fil_cur_close(
/*=============*/ /*=============*/
xb_fil_cur_t *cursor) /*!< in/out: source file cursor */ xb_fil_cur_t *cursor) /*!< in/out: source file cursor */
{ {
cursor->read_filter->deinit(&cursor->read_filter_ctxt); if (cursor->read_filter) {
cursor->read_filter->deinit(&cursor->read_filter_ctxt);
}
free(cursor->orig_buf); free(cursor->orig_buf);
......
...@@ -58,7 +58,7 @@ struct xb_fil_cur_t { ...@@ -58,7 +58,7 @@ struct xb_fil_cur_t {
ulint space_size; /*!< space size in pages */ ulint space_size; /*!< space size in pages */
/** TODO: remove this default constructor */ /** TODO: remove this default constructor */
xb_fil_cur_t() : page_size(0), read_filter_ctxt() {} xb_fil_cur_t() : page_size(0), read_filter(0), read_filter_ctxt() {}
/** @return whether this is not a file-per-table tablespace */ /** @return whether this is not a file-per-table tablespace */
bool is_system() const bool is_system() const
...@@ -87,7 +87,8 @@ xb_fil_cur_open( ...@@ -87,7 +87,8 @@ xb_fil_cur_open(
xb_fil_cur_t* cursor, /*!< out: source file cursor */ xb_fil_cur_t* cursor, /*!< out: source file cursor */
xb_read_filt_t* read_filter, /*!< in/out: the read filter */ xb_read_filt_t* read_filter, /*!< in/out: the read filter */
fil_node_t* node, /*!< in: source tablespace node */ fil_node_t* node, /*!< in: source tablespace node */
uint thread_n); /*!< thread number for diagnostics */ uint thread_n, /*!< thread number for diagnostics */
ulonglong max_file_size = ULLONG_MAX);
/************************************************************************ /************************************************************************
Reads and verifies the next block of pages from the source Reads and verifies the next block of pages from the source
......
...@@ -344,6 +344,25 @@ const char *opt_history = NULL; ...@@ -344,6 +344,25 @@ const char *opt_history = NULL;
char mariabackup_exe[FN_REFLEN]; char mariabackup_exe[FN_REFLEN];
char orig_argv1[FN_REFLEN]; char orig_argv1[FN_REFLEN];
pthread_mutex_t backup_mutex;
pthread_cond_t scanned_lsn_cond;
typedef std::map<space_id_t,std::string> space_id_to_name_t;
struct ddl_tracker_t {
/** Tablspaces with their ID and name, as they were copied to backup.*/
space_id_to_name_t tables_in_backup;
/** Tablespaces for that optimized DDL without redo log was found.*/
std::set<space_id_t> optimized_ddl;
/** Drop operations found in redo log. */
std::set<space_id_t> drops;
/* For DDL operation found in redo log, */
space_id_to_name_t id_to_name;
};
const space_id_t REMOVED_SPACE_ID = ULINT_MAX;
static ddl_tracker_t ddl_tracker;
/* Whether xtrabackup_binlog_info should be created on recovery */ /* Whether xtrabackup_binlog_info should be created on recovery */
static bool recover_binlog_info; static bool recover_binlog_info;
...@@ -536,49 +555,79 @@ void mdl_lock_all() ...@@ -536,49 +555,79 @@ void mdl_lock_all()
mdl_lock_table(node->space->id); mdl_lock_table(node->space->id);
} }
datafiles_iter_free(it); datafiles_iter_free(it);
DBUG_EXECUTE_IF("check_mdl_lock_works",
dbug_alter_thread_done =
dbug_start_query_thread("ALTER TABLE test.t ADD COLUMN mdl_lock_column int",
"Waiting for table metadata lock",1, ER_QUERY_INTERRUPTED););
} }
/** Check if the space id belongs to the table which name should
be skipped based on the --tables, --tables-file and --table-exclude
options.
@param[in] space_id space id to check
@return true if the space id belongs to skip table/database list. */
static bool backup_includes(space_id_t space_id)
{
datafiles_iter_t *it = datafiles_iter_new();
if (!it)
return true;
while (fil_node_t *node = datafiles_iter_next(it)){ // Convert non-null terminated filename to space name
if (space_id == 0 std::string filename_to_spacename(const byte *filename, size_t len)
|| (node->space->id == space_id {
&& !check_if_skip_table(node->space->name))) { // null- terminate filename
char *f = (char *)malloc(len + 1);
ut_a(f);
memcpy(f, filename, len);
f[len] = 0;
for (size_t i = 0; i < len; i++)
if (f[i] == '\\')
f[i] = '/';
char *p = strrchr(f, '.');
ut_a(p);
*p = 0;
char *table = strrchr(f, '/');
ut_a(table);
*table = 0;
char *db = strrchr(f, '/');
ut_a(db);
*table = '/';
return std::string(db+1);
}
msg("mariabackup: Unsupported redo log detected " /** Report an operation to create, delete, or rename a file during backup.
"and it belongs to %s\n", @param[in] space_id tablespace identifier
space_id ? node->name: "the InnoDB system tablespace"); @param[in] flags tablespace flags (NULL if not create)
@param[in] name file name (not NUL-terminated)
@param[in] len length of name, in bytes
@param[in] new_name new file name (NULL if not rename)
@param[in] new_len length of new_name, in bytes (0 if NULL) */
void backup_file_op(ulint space_id, const byte* flags,
const byte* name, ulint len,
const byte* new_name, ulint new_len)
{
msg("mariabackup: ALTER TABLE or OPTIMIZE TABLE " ut_ad(!flags || !new_name);
"was being executed during the backup.\n"); ut_ad(name);
ut_ad(len);
ut_ad(!new_name == !new_len);
pthread_mutex_lock(&backup_mutex);
if (flags) {
ddl_tracker.id_to_name[space_id] = filename_to_spacename(name, len);
msg("DDL tracking : create %zu \"%.*s\": %x\n",
space_id, int(len), name, mach_read_from_4(flags));
}
else if (new_name) {
ddl_tracker.id_to_name[space_id] = filename_to_spacename(new_name, new_len);
msg("DDL tracking : rename %zu \"%.*s\",\"%.*s\"\n",
space_id, int(len), name, int(new_len), new_name);
} else {
ddl_tracker.drops.insert(space_id);
msg("DDL tracking : delete %zu \"%.*s\"\n", space_id, int(len), name);
}
pthread_mutex_unlock(&backup_mutex);
}
if (!opt_lock_ddl_per_table) {
msg("mariabackup: Use --lock-ddl-per-table "
"parameter to lock all the table before "
"backup operation.\n");
}
datafiles_iter_free(it); /** Callback whenever MLOG_INDEX_LOAD happens.
return false; @param[in] space_id space id to check
} @return false */
} void backup_optimized_ddl_op(ulint space_id)
{
// TODO : handle incremental
if (xtrabackup_incremental)
return;
datafiles_iter_free(it); pthread_mutex_lock(&backup_mutex);
return true; ddl_tracker.optimized_ddl.insert(space_id);
pthread_mutex_unlock(&backup_mutex);
} }
/* ======== Date copying thread context ======== */ /* ======== Date copying thread context ======== */
...@@ -2326,7 +2375,7 @@ xb_get_copy_action(const char *dflt) ...@@ -2326,7 +2375,7 @@ xb_get_copy_action(const char *dflt)
static static
my_bool my_bool
xtrabackup_copy_datafile(fil_node_t* node, uint thread_n) xtrabackup_copy_datafile(fil_node_t* node, uint thread_n, const char *dest_name=0, ulonglong max_size=ULLONG_MAX)
{ {
char dst_name[FN_REFLEN]; char dst_name[FN_REFLEN];
ds_file_t *dstfile = NULL; ds_file_t *dstfile = NULL;
...@@ -2356,20 +2405,35 @@ xtrabackup_copy_datafile(fil_node_t* node, uint thread_n) ...@@ -2356,20 +2405,35 @@ xtrabackup_copy_datafile(fil_node_t* node, uint thread_n)
return(FALSE); return(FALSE);
} }
bool was_dropped;
pthread_mutex_lock(&backup_mutex);
was_dropped = (ddl_tracker.drops.find(node->space->id) != ddl_tracker.drops.end());
pthread_mutex_unlock(&backup_mutex);
if (was_dropped) {
if (node->is_open()) {
mutex_enter(&fil_system.mutex);
node->close();
mutex_exit(&fil_system.mutex);
}
goto skip;
}
if (!changed_page_bitmap) { if (!changed_page_bitmap) {
read_filter = &rf_pass_through; read_filter = &rf_pass_through;
} }
else { else {
read_filter = &rf_bitmap; read_filter = &rf_bitmap;
} }
res = xb_fil_cur_open(&cursor, read_filter, node, thread_n);
res = xb_fil_cur_open(&cursor, read_filter, node, thread_n,max_size);
if (res == XB_FIL_CUR_SKIP) { if (res == XB_FIL_CUR_SKIP) {
goto skip; goto skip;
} else if (res == XB_FIL_CUR_ERROR) { } else if (res == XB_FIL_CUR_ERROR) {
goto error; goto error;
} }
strncpy(dst_name, cursor.rel_path, sizeof(dst_name)); strncpy(dst_name, (dest_name)?dest_name : cursor.rel_path, sizeof(dst_name));
/* Setup the page write filter */ /* Setup the page write filter */
if (xtrabackup_incremental) { if (xtrabackup_incremental) {
...@@ -2421,6 +2485,10 @@ xtrabackup_copy_datafile(fil_node_t* node, uint thread_n) ...@@ -2421,6 +2485,10 @@ xtrabackup_copy_datafile(fil_node_t* node, uint thread_n)
goto error; goto error;
} }
pthread_mutex_lock(&backup_mutex);
ddl_tracker.tables_in_backup[node->space->id] = node_name;
pthread_mutex_unlock(&backup_mutex);
/* close */ /* close */
msg_ts("[%02u] ...done\n", thread_n); msg_ts("[%02u] ...done\n", thread_n);
xb_fil_cur_close(&cursor); xb_fil_cur_close(&cursor);
...@@ -2584,7 +2652,7 @@ static bool xtrabackup_copy_logfile(bool last = false) ...@@ -2584,7 +2652,7 @@ static bool xtrabackup_copy_logfile(bool last = false)
if (!start_lsn) { if (!start_lsn) {
msg("mariabackup: Error: xtrabackup_copy_logfile()" msg("mariabackup: Error: xtrabackup_copy_logfile()"
" failed.\n"); " failed.\n");
return(true); exit(EXIT_FAILURE);
} }
} while (start_lsn == end_lsn); } while (start_lsn == end_lsn);
...@@ -2593,13 +2661,31 @@ static bool xtrabackup_copy_logfile(bool last = false) ...@@ -2593,13 +2661,31 @@ static bool xtrabackup_copy_logfile(bool last = false)
msg_ts(">> log scanned up to (" LSN_PF ")\n", start_lsn); msg_ts(">> log scanned up to (" LSN_PF ")\n", start_lsn);
/* update global variable*/ /* update global variable*/
pthread_mutex_lock(&backup_mutex);
log_copy_scanned_lsn = start_lsn; log_copy_scanned_lsn = start_lsn;
pthread_cond_broadcast(&scanned_lsn_cond);
pthread_mutex_unlock(&backup_mutex);
debug_sync_point("xtrabackup_copy_logfile_pause"); debug_sync_point("xtrabackup_copy_logfile_pause");
return(false); return(false);
} }
static os_thread_ret_t DECLARE_THREAD(log_copying_thread)(void*) /**
Wait until redo log copying thread processes given lsn
*/
void backup_wait_for_lsn(lsn_t lsn) {
bool completed = false;
pthread_mutex_lock(&backup_mutex);
do {
pthread_cond_wait(&scanned_lsn_cond, &backup_mutex);
completed = log_copy_scanned_lsn >= lsn;
} while (!completed);
pthread_mutex_unlock(&backup_mutex);
}
extern lsn_t server_lsn_after_lock;
static os_thread_ret_t log_copying_thread(void*)
{ {
/* /*
Initialize mysys thread-specific memory so we can Initialize mysys thread-specific memory so we can
...@@ -2655,6 +2741,42 @@ static os_thread_ret_t DECLARE_THREAD(io_watching_thread)(void*) ...@@ -2655,6 +2741,42 @@ static os_thread_ret_t DECLARE_THREAD(io_watching_thread)(void*)
return(0); return(0);
} }
#ifndef DBUG_OFF
/*
In debug mode, execute SQL statement that was passed via environment.
To use this facility, you need to
1. Add code DBUG_EXECUTE_MARIABACKUP_EVENT("my_event_name", key););
to the code. key is usually a table name
2. Set environment variable my_event_name_$key SQL statement you want to execute
when event occurs, in DBUG_EXECUTE_IF from above.
In mtr , you can set environment via 'let' statement (do not use $ as the first char
for the variable)
3. start mariabackup with --dbug=+d,debug_mariabackup_events
*/
static void dbug_mariabackup_event(const char *event,const char *key)
{
char envvar[FN_REFLEN];
if (key) {
snprintf(envvar, sizeof(envvar), "%s_%s", event, key);
char *slash = strchr(envvar, '/');
if (slash)
*slash = '_';
} else {
strncpy(envvar, event, sizeof(envvar));
}
char *sql = getenv(envvar);
if (sql) {
msg("dbug_mariabackup_event : executing '%s'\n", sql);
xb_mysql_query(mysql_connection, sql, false, true);
}
}
#define DBUG_MARIABACKUP_EVENT(A, B) DBUG_EXECUTE_IF("mariabackup_events", dbug_mariabackup_event(A,B););
#else
#define DBUG_MARIABACKUP_EVENT(A,B)
#endif
/************************************************************************** /**************************************************************************
Datafiles copying thread.*/ Datafiles copying thread.*/
static static
...@@ -2677,12 +2799,18 @@ DECLARE_THREAD(data_copy_thread_func)( ...@@ -2677,12 +2799,18 @@ DECLARE_THREAD(data_copy_thread_func)(
while ((node = datafiles_iter_next(ctxt->it)) != NULL) { while ((node = datafiles_iter_next(ctxt->it)) != NULL) {
DBUG_MARIABACKUP_EVENT("before_copy", node->space->name);
/* copy the datafile */ /* copy the datafile */
if(xtrabackup_copy_datafile(node, num)) { if(xtrabackup_copy_datafile(node, num)) {
msg("[%02u] mariabackup: Error: " msg("[%02u] mariabackup: Error: "
"failed to copy datafile.\n", num); "failed to copy datafile.\n", num);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
DBUG_MARIABACKUP_EVENT("after_copy", node->space->name);
} }
pthread_mutex_lock(&ctxt->count_mutex); pthread_mutex_lock(&ctxt->count_mutex);
...@@ -2854,6 +2982,7 @@ xb_load_single_table_tablespace( ...@@ -2854,6 +2982,7 @@ xb_load_single_table_tablespace(
Datafile *file = xb_new_datafile(name, is_remote); Datafile *file = xb_new_datafile(name, is_remote);
if (file->open_read_only(true) != DB_SUCCESS) { if (file->open_read_only(true) != DB_SUCCESS) {
msg("Can't open datafile %s\n", name);
ut_free(name); ut_free(name);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
...@@ -3171,7 +3300,7 @@ xb_load_tablespaces() ...@@ -3171,7 +3300,7 @@ xb_load_tablespaces()
} }
debug_sync_point("xtrabackup_load_tablespaces_pause"); debug_sync_point("xtrabackup_load_tablespaces_pause");
DBUG_MARIABACKUP_EVENT("after_load_tablespaces", 0);
return(DB_SUCCESS); return(DB_SUCCESS);
} }
...@@ -3766,6 +3895,8 @@ xtrabackup_backup_func() ...@@ -3766,6 +3895,8 @@ xtrabackup_backup_func()
uint count; uint count;
pthread_mutex_t count_mutex; pthread_mutex_t count_mutex;
data_thread_ctxt_t *data_threads; data_thread_ctxt_t *data_threads;
pthread_mutex_init(&backup_mutex, NULL);
pthread_cond_init(&scanned_lsn_cond, NULL);
#ifdef USE_POSIX_FADVISE #ifdef USE_POSIX_FADVISE
msg("mariabackup: uses posix_fadvise().\n"); msg("mariabackup: uses posix_fadvise().\n");
...@@ -3779,7 +3910,7 @@ xtrabackup_backup_func() ...@@ -3779,7 +3910,7 @@ xtrabackup_backup_func()
return(false); return(false);
} }
msg("mariabackup: cd to %s\n", mysql_real_data_home); msg("mariabackup: cd to %s\n", mysql_real_data_home);
encryption_plugin_backup_init(mysql_connection);
msg("mariabackup: open files limit requested %u, set to %u\n", msg("mariabackup: open files limit requested %u, set to %u\n",
(uint) xb_open_files_limit, (uint) xb_open_files_limit,
xb_set_max_open_files(xb_open_files_limit)); xb_set_max_open_files(xb_open_files_limit));
...@@ -3792,6 +3923,7 @@ xtrabackup_backup_func() ...@@ -3792,6 +3923,7 @@ xtrabackup_backup_func()
srv_read_only_mode = TRUE; srv_read_only_mode = TRUE;
srv_operation = SRV_OPERATION_BACKUP; srv_operation = SRV_OPERATION_BACKUP;
log_file_op = backup_file_op;
metadata_to_lsn = 0; metadata_to_lsn = 0;
if (xb_close_files) if (xb_close_files)
...@@ -3805,6 +3937,7 @@ xtrabackup_backup_func() ...@@ -3805,6 +3937,7 @@ xtrabackup_backup_func()
fail: fail:
metadata_to_lsn = log_copying_running; metadata_to_lsn = log_copying_running;
stop_backup_threads(); stop_backup_threads();
log_file_op = NULL;
if (dst_log_file) { if (dst_log_file) {
ds_close(dst_log_file); ds_close(dst_log_file);
dst_log_file = NULL; dst_log_file = NULL;
...@@ -4054,6 +4187,7 @@ xtrabackup_backup_func() ...@@ -4054,6 +4187,7 @@ xtrabackup_backup_func()
goto fail_before_log_copying_thread_start; goto fail_before_log_copying_thread_start;
log_copying_stop = os_event_create(0); log_copying_stop = os_event_create(0);
log_optimized_ddl_op = backup_optimized_ddl_op;
os_thread_create(log_copying_thread, NULL, &log_copying_thread_id); os_thread_create(log_copying_thread, NULL, &log_copying_thread_id);
/* FLUSH CHANGED_PAGE_BITMAPS call */ /* FLUSH CHANGED_PAGE_BITMAPS call */
...@@ -4085,6 +4219,11 @@ xtrabackup_backup_func() ...@@ -4085,6 +4219,11 @@ xtrabackup_backup_func()
if (opt_lock_ddl_per_table) { if (opt_lock_ddl_per_table) {
mdl_lock_all(); mdl_lock_all();
DBUG_EXECUTE_IF("check_mdl_lock_works",
dbug_alter_thread_done =
dbug_start_query_thread("ALTER TABLE test.t ADD COLUMN mdl_lock_column int",
"Waiting for table metadata lock", 1, ER_QUERY_INTERRUPTED););
} }
it = datafiles_iter_new(); it = datafiles_iter_new();
...@@ -4122,10 +4261,6 @@ xtrabackup_backup_func() ...@@ -4122,10 +4261,6 @@ xtrabackup_backup_func()
pthread_mutex_destroy(&count_mutex); pthread_mutex_destroy(&count_mutex);
free(data_threads); free(data_threads);
datafiles_iter_free(it); datafiles_iter_free(it);
if (changed_page_bitmap) {
xb_page_bitmap_deinit(changed_page_bitmap);
}
} }
bool ok = backup_start(); bool ok = backup_start();
...@@ -4149,6 +4284,9 @@ xtrabackup_backup_func() ...@@ -4149,6 +4284,9 @@ xtrabackup_backup_func()
goto fail; goto fail;
} }
if (changed_page_bitmap) {
xb_page_bitmap_deinit(changed_page_bitmap);
}
xtrabackup_destroy_datasinks(); xtrabackup_destroy_datasinks();
msg("mariabackup: Redo log (from LSN " LSN_PF " to " LSN_PF msg("mariabackup: Redo log (from LSN " LSN_PF " to " LSN_PF
...@@ -4166,9 +4304,182 @@ xtrabackup_backup_func() ...@@ -4166,9 +4304,182 @@ xtrabackup_backup_func()
} }
innodb_shutdown(); innodb_shutdown();
log_file_op = NULL;
pthread_mutex_destroy(&backup_mutex);
pthread_cond_destroy(&scanned_lsn_cond);
return(true); return(true);
} }
/**
This function handles DDL changes at the end of backup, under protection of
FTWRL. This ensures consistent backup in presence of DDL.
- New tables, that were created during backup, are now copied into backup.
Also, tablespaces with optimized (no redo loggin DDL) are re-copied into
backup. This tablespaces will get the extension ".new" in the backup
- Tables that were renamed during backup, are marked as renamed
For these, file <old_name>.ren will be created.
The content of the file is the new tablespace name.
- Tables that were deleted during backup, are marked as deleted
For these , an empty file <name>.del will be created
It is the responsibility of the prepare phase to deal with .new, .ren, and .del
files.
*/
void backup_fix_ddl(void)
{
std::set<std::string> new_tables;
std::set<std::string> dropped_tables;
std::map<std::string, std::string> renamed_tables;
for (space_id_to_name_t::iterator iter = ddl_tracker.tables_in_backup.begin();
iter != ddl_tracker.tables_in_backup.end();
iter++) {
const std::string name = iter->second;
ulint id = iter->first;
if (ddl_tracker.drops.find(id) != ddl_tracker.drops.end()) {
dropped_tables.insert(name);
continue;
}
bool has_optimized_ddl =
ddl_tracker.optimized_ddl.find(id) != ddl_tracker.optimized_ddl.end();
if (ddl_tracker.id_to_name.find(id) == ddl_tracker.id_to_name.end()) {
if (has_optimized_ddl) {
new_tables.insert(name);
}
continue;
}
/* tablespace was affected by DDL. */
const std::string new_name = ddl_tracker.id_to_name[id];
if (new_name != name) {
if (has_optimized_ddl) {
/* table was renamed, but we need a full copy
of it because of optimized DDL. We emulate a drop/create.*/
dropped_tables.insert(name);
new_tables.insert(new_name);
} else {
/* Renamed, and no optimized DDL*/
renamed_tables[name] = new_name;
}
} else if (has_optimized_ddl) {
/* Table was recreated, or optimized DDL ran.
In both cases we need a full copy in the backup.*/
new_tables.insert(name);
}
}
/* Find tables that were created during backup (and not removed).*/
for(space_id_to_name_t::iterator iter = ddl_tracker.id_to_name.begin();
iter != ddl_tracker.id_to_name.end();
iter++) {
ulint id = iter->first;
std::string name = iter->second;
if (ddl_tracker.tables_in_backup.find(id) != ddl_tracker.tables_in_backup.end()) {
/* already processed above */
continue;
}
if (ddl_tracker.drops.find(id) == ddl_tracker.drops.end()) {
dropped_tables.erase(name);
new_tables.insert(name);
}
}
// Mark tablespaces for rename
for (std::map<std::string, std::string>::iterator iter = renamed_tables.begin();
iter != renamed_tables.end(); ++iter) {
const std::string old_name = iter->first;
std::string new_name = iter->second;
backup_file_printf((old_name + ".ren").c_str(), "%s", new_name.c_str());
}
// Mark tablespaces for drop
for (std::set<std::string>::iterator iter = dropped_tables.begin();
iter != dropped_tables.end();
iter++) {
const std::string name(*iter);
backup_file_printf((name + ".del").c_str(), "%s", "");
}
// Load and copy new tables.
// Close all datanodes first, reload only new tables.
std::vector<fil_node_t *> all_nodes;
datafiles_iter_t *it = datafiles_iter_new();
if (!it)
return;
while (fil_node_t *node = datafiles_iter_next(it)) {
all_nodes.push_back(node);
}
for (size_t i = 0; i < all_nodes.size(); i++) {
fil_node_t *n = all_nodes[i];
if (n->space->id == 0)
continue;
if (n->is_open()) {
mutex_enter(&fil_system.mutex);
n->close();
mutex_exit(&fil_system.mutex);
}
fil_space_free(n->space->id, false);
}
for (std::set<std::string>::iterator iter = new_tables.begin();
iter != new_tables.end(); iter++) {
const char *space_name = iter->c_str();
if (check_if_skip_table(space_name))
continue;
std::string name(*iter);
bool is_remote = access((name + ".ibd").c_str(), R_OK) != 0;
const char *extension = is_remote ? ".isl" : ".ibd";
name.append(extension);
char buf[FN_REFLEN];
strncpy(buf, name.c_str(), sizeof(buf));
const char *dbname = buf;
char *p = strchr(buf, '/');
if (p == 0) {
msg("Unexpected tablespace %s filename %s\n", space_name, name.c_str());
ut_a(0);
}
ut_a(p);
*p = 0;
const char *tablename = p + 1;
xb_load_single_table_tablespace(dbname, tablename, is_remote);
}
it = datafiles_iter_new();
if (!it)
return;
while (fil_node_t *node = datafiles_iter_next(it)) {
fil_space_t * space = node->space;
if (!fil_is_user_tablespace_id(space->id))
continue;
std::string dest_name(node->space->name);
dest_name.append(".new");
#if 0
bool do_full_copy = ddl_tracker.optimized_ddl.find(n->space->id) != ddl_tracker.optimized_ddl.end();
if (do_full_copy) {
msg(
"Performing a full copy of the tablespace %s, because optimized (without redo logging) DDL operation"
"ran during backup. You can use set innodb_log_optimize_ddl=OFF to improve backup performance"
"in the future.\n",
n->space->name);
}
#endif
xtrabackup_copy_datafile(node, 0, dest_name.c_str()/*, do_full_copy ? ULONGLONG_MAX:UNIV_PAGE_SIZE */);
}
}
/* ================= prepare ================= */ /* ================= prepare ================= */
/*********************************************************************** /***********************************************************************
...@@ -4681,6 +4992,27 @@ xtrabackup_apply_delta( ...@@ -4681,6 +4992,27 @@ xtrabackup_apply_delta(
return FALSE; return FALSE;
} }
std::string change_extension(std::string filename, std::string new_ext) {
DBUG_ASSERT(new_ext.size() == 3);
std::string new_name(filename);
new_name.resize(new_name.size() - new_ext.size());
new_name.append(new_ext);
return new_name;
}
static void rename_file(const char *from,const char *to) {
msg("Renaming %s to %s\n", from, to);
if (my_rename(from, to, MY_WME)) {
msg("Cannot rename %s to %s errno %d", from, to, errno);
exit(EXIT_FAILURE);
}
}
static void rename_file(const std::string& from, const std::string &to) {
rename_file(from.c_str(), to.c_str());
}
/************************************************************************ /************************************************************************
Callback to handle datadir entry. Function of this type will be called Callback to handle datadir entry. Function of this type will be called
for each entry which matches the mask by xb_process_datadir. for each entry which matches the mask by xb_process_datadir.
...@@ -4692,6 +5024,37 @@ typedef ibool (*handle_datadir_entry_func_t)( ...@@ -4692,6 +5024,37 @@ typedef ibool (*handle_datadir_entry_func_t)(
const char* file_name, /*!<in: file name with suffix */ const char* file_name, /*!<in: file name with suffix */
void* arg); /*!<in: caller-provided data */ void* arg); /*!<in: caller-provided data */
/** Rename, and replace destination file, if exists */
static void rename_force(const char *from, const char *to) {
if (access(to, R_OK) == 0) {
msg("Removing %s\n", to);
if (my_delete(to, MYF(MY_WME))) {
msg("Can't remove %s, errno %d", to, errno);
exit(EXIT_FAILURE);
}
}
rename_file(from,to);
}
/* During prepare phase, rename ".new" files , that were created in backup_fix_ddl(),
to ".ibd".*/
static ibool prepare_handle_new_files(
const char* data_home_dir, /*!<in: path to datadir */
const char* db_name, /*!<in: database name */
const char* file_name, /*!<in: file name with suffix */
void *)
{
std::string src_path = std::string(data_home_dir) + '/' + std::string(db_name) + '/' + file_name;
std::string dest_path = src_path;
size_t index = dest_path.find(".new");
DBUG_ASSERT(index != std::string::npos);
dest_path.replace(index, 4, ".ibd");
rename_force(src_path.c_str(),dest_path.c_str());
return TRUE;
}
/************************************************************************ /************************************************************************
Callback to handle datadir entry. Deletes entry if it has no matching Callback to handle datadir entry. Deletes entry if it has no matching
fil_space in fil_system directory. fil_space in fil_system directory.
...@@ -4897,6 +5260,103 @@ store_binlog_info(const char* filename, const char* name, ulonglong pos) ...@@ -4897,6 +5260,103 @@ store_binlog_info(const char* filename, const char* name, ulonglong pos)
return(true); return(true);
} }
/** Check if file exists*/
static bool file_exists(std::string name)
{
return access(name.c_str(), R_OK) == 0 ;
}
/** Read file content into STL string */
static std::string read_file_as_string(const std::string file) {
char content[FN_REFLEN];
FILE *f = fopen(file.c_str(), "r");
if (!f) {
msg("Can not open %s\n", file.c_str());
}
size_t len = fread(content, 1, FN_REFLEN, f);
fclose(f);
return std::string(content, len);
}
/** Delete file- Provide verbose diagnostics and exit, if operation fails. */
static void delete_file(const std::string& file, bool if_exists = false) {
if (if_exists && !file_exists(file))
return;
if (my_delete(file.c_str(), MYF(MY_WME))) {
msg("Can't remove %s, errno %d", file.c_str(), errno);
exit(EXIT_FAILURE);
}
}
/**
Rename tablespace during prepare.
Backup in its end phase may generate some .ren files, recording
tablespaces that should be renamed in --prepare.
*/
static void rename_table_in_prepare(const std::string &datadir, const std::string& from , const std::string& to,
const char *extension=0) {
if (!extension) {
static const char *extensions_nonincremental[] = { ".ibd", 0 };
static const char *extensions_incremental[] = { ".ibd.delta", ".ibd.meta", 0 };
const char **extensions = xtrabackup_incremental_dir ?
extensions_incremental : extensions_nonincremental;
for (size_t i = 0; extensions[i]; i++) {
rename_table_in_prepare(datadir, from, to, extensions[i]);
}
return;
}
std::string src = std::string(datadir) + "/" + from + extension;
std::string dest = std::string(datadir) + "/" + to + extension;
std::string ren2, tmp;
if (file_exists(dest)) {
ren2= std::string(datadir) + "/" + to + ".ren";
if (!file_exists(ren2)) {
msg("ERROR : File %s was not found, but expected during rename processing\n", ren2.c_str());
ut_a(0);
}
tmp = to + "#";
rename_table_in_prepare(datadir, to, tmp);
}
rename_file(src, dest);
if (ren2.size()) {
// Make sure the temp. renamed file is processed.
std::string to2 = read_file_as_string(ren2);
rename_table_in_prepare(datadir, tmp, to2);
delete_file(ren2);
}
}
static ibool prepare_handle_ren_files(const char *datadir, const char *db, const char *filename, void *) {
std::string ren_file = std::string(datadir) + "/" + db + "/" + filename;
if (!file_exists(ren_file))
return TRUE;
std::string to = read_file_as_string(ren_file);
std::string source_space_name = std::string(db) + "/" + filename;
source_space_name.resize(source_space_name.size() - 4); // remove extension
rename_table_in_prepare(datadir, source_space_name.c_str(), to.c_str());
delete_file(ren_file);
return TRUE;
}
/* Remove tablespaces during backup, based on */
static ibool prepare_handle_del_files(const char *datadir, const char *db, const char *filename, void *) {
std::string del_file = std::string(datadir) + "/" + db + "/" + filename;
std::string path(del_file);
path.resize(path.size() - 4); // remove extension;
if (xtrabackup_incremental) {
delete_file(path + ".ibd.delta", true);
delete_file(path + ".ibd.meta", true);
}
else {
delete_file(path + ".ibd", true);
}
delete_file(del_file);
return TRUE;
}
/** Implement --prepare /** Implement --prepare
@return whether the operation succeeded */ @return whether the operation succeeded */
static bool static bool
...@@ -4914,6 +5374,21 @@ xtrabackup_prepare_func(char** argv) ...@@ -4914,6 +5374,21 @@ xtrabackup_prepare_func(char** argv)
} }
msg("mariabackup: cd to %s\n", xtrabackup_real_target_dir); msg("mariabackup: cd to %s\n", xtrabackup_real_target_dir);
fil_path_to_mysql_datadir = ".";
if (xtrabackup_incremental_dir) {
xb_process_datadir(xtrabackup_incremental_dir, ".new.meta", prepare_handle_new_files);
xb_process_datadir(xtrabackup_incremental_dir, ".new.delta", prepare_handle_new_files);
}
else {
xb_process_datadir(".", ".new", prepare_handle_new_files);
}
xb_process_datadir(xtrabackup_incremental_dir? xtrabackup_incremental_dir:".",
".ren", prepare_handle_ren_files);
xb_process_datadir(xtrabackup_incremental_dir ? xtrabackup_incremental_dir : ".",
".del", prepare_handle_del_files);
int argc; for (argc = 0; argv[argc]; argc++) {} int argc; for (argc = 0; argv[argc]; argc++) {}
encryption_plugin_prepare_init(argc, argv); encryption_plugin_prepare_init(argc, argv);
...@@ -5204,7 +5679,6 @@ xb_init() ...@@ -5204,7 +5679,6 @@ xb_init()
return(false); return(false);
} }
encryption_plugin_backup_init(mysql_connection);
history_start_time = time(NULL); history_start_time = time(NULL);
} }
...@@ -5262,7 +5736,7 @@ handle_options(int argc, char **argv, char ***argv_client, char ***argv_server) ...@@ -5262,7 +5736,7 @@ handle_options(int argc, char **argv, char ***argv_client, char ***argv_server)
srv_operation = SRV_OPERATION_RESTORE; srv_operation = SRV_OPERATION_RESTORE;
files_charset_info = &my_charset_utf8_general_ci; files_charset_info = &my_charset_utf8_general_ci;
check_if_backup_includes = backup_includes;
setup_error_messages(); setup_error_messages();
sys_var_init(); sys_var_init();
......
# xtrabackup backup
# xtrabackup prepare
# shutdown server
# remove datadir
# xtrabackup move back
# restart server
SELECT COUNT(*) from t1;
COUNT(*)
10000
DROP TABLE t1;
--source include/have_debug.inc
let $targetdir=$MYSQLTEST_VARDIR/tmp/backup;
mkdir $targetdir;
# this will table and populate it, after backup has list of tables to be copied
--let after_load_tablespaces =CREATE TABLE test.t1 ENGINE=INNODB SELECT UUID() from test.seq_1_to_10000
echo # xtrabackup backup;
--disable_result_log
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir --dbug=+d,mariabackup_events;
--enable_result_log
--let after_load_tables=
echo # xtrabackup prepare;
--disable_result_log
exec $XTRABACKUP --prepare --target-dir=$targetdir;
-- source include/restart_and_restore.inc
--enable_result_log
# Check that new table is there after restore.
SELECT COUNT(*) from t1;
DROP TABLE t1;
rmdir $targetdir;
# xtrabackup backup
# xtrabackup prepare
DROP TABLE t;
# shutdown server
# remove datadir
# xtrabackup move back
# restart server
SELECT * FROM t;
i
DROP TABLE t;
--source include/have_debug.inc
let $table_data_dir=$MYSQLTEST_VARDIR/tmp/ddir;
let $targetdir=$MYSQLTEST_VARDIR/tmp/backup;
mkdir $table_data_dir;
--replace_result $table_data_dir table_data_dir
--let after_load_tablespaces=CREATE TABLE test.t(i int) ENGINE=INNODB DATA DIRECTORY='$table_data_dir'
echo # xtrabackup backup;
let $targetdir=$MYSQLTEST_VARDIR/tmp/backup;
--disable_result_log
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir --dbug=+d,mariabackup_events;
--enable_result_log
--source include/shutdown_mysqld.inc
echo # xtrabackup prepare;
--disable_result_log
exec $XTRABACKUP --prepare --target-dir=$targetdir;
--source include/start_mysqld.inc
DROP TABLE t;
rmdir $table_data_dir;
-- source include/restart_and_restore.inc
--enable_result_log
SELECT * FROM t;
DROP TABLE t;
rmdir $targetdir;
rmdir $table_data_dir;
unsupported_redo : MDEV-16791 allows optimized redo
\ No newline at end of file
CREATE TABLE t1 (i int) ENGINE=INNODB;
CREATE TABLE t2 (i int) ENGINE=INNODB;
CREATE TABLE t3 (i int) ENGINE=INNODB;
# xtrabackup prepare
# shutdown server
# remove datadir
# xtrabackup move back
# restart server
CREATE TABLE t1(i int);
DROP TABLE t1;
CREATE TABLE t2(i int);
DROP TABLE t2;
DROP TABLE t3;
--source include/have_debug.inc
let $targetdir=$MYSQLTEST_VARDIR/tmp/backup;
CREATE TABLE t1 (i int) ENGINE=INNODB;
CREATE TABLE t2 (i int) ENGINE=INNODB;
CREATE TABLE t3 (i int) ENGINE=INNODB;
--let before_copy_test_t1=DROP TABLE test.t1
--let after_copy_test_t2=DROP TABLE test.t2;
--disable_result_log
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir --dbug=+d,mariabackup_events;
--enable_result_log
echo # xtrabackup prepare;
--disable_result_log
exec $XTRABACKUP --prepare --target-dir=$targetdir;
-- source include/restart_and_restore.inc
--enable_result_log
#check that the table t1 does not exist in backup
CREATE TABLE t1(i int);
DROP TABLE t1;
CREATE TABLE t2(i int);
DROP TABLE t2;
DROP TABLE t3;
rmdir $targetdir;
call mtr.add_suppression("InnoDB: New log files created");
CREATE TABLE t1(i INT PRIMARY KEY) ENGINE INNODB;
CREATE TABLE t2(i INT PRIMARY KEY) ENGINE INNODB;
CREATE TABLE t3(i INT) ENGINE INNODB;
# Create full backup , modify table, then create incremental/differential backup
INSERT into t1 values(1);
# Prepare full backup, apply incremental one
# Restore and check results
# shutdown server
# remove datadir
# xtrabackup move back
# restart server
CREATE TABLE t1(i int);
DROP TABLE t1;
SELECT * from t1_renamed;
i
1
DROP TABLE t1_renamed;
CREATE TABLE t2(i INT PRIMARY KEY) ENGINE INNODB;
DROP TABLE t2;
DROP TABLE t3;
DROP TABLE t4;
--source include/have_debug.inc
call mtr.add_suppression("InnoDB: New log files created");
let $basedir=$MYSQLTEST_VARDIR/tmp/backup;
let $incremental_dir=$MYSQLTEST_VARDIR/tmp/backup_inc1;
CREATE TABLE t1(i INT PRIMARY KEY) ENGINE INNODB;
CREATE TABLE t2(i INT PRIMARY KEY) ENGINE INNODB;
CREATE TABLE t3(i INT) ENGINE INNODB;
echo # Create full backup , modify table, then create incremental/differential backup;
--disable_result_log
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$basedir;
--enable_result_log
INSERT into t1 values(1);
--let after_load_tablespaces=CREATE TABLE test.t4 ENGINE=INNODB SELECT UUID() from test.seq_1_to_10000
--let after_copy_test_t1=RENAME TABLE test.t1 TO test.t1_renamed
--let after_copy_test_t2=DROP TABLE test.t2
--let after_copy_test_t3=CREATE INDEX a_i ON test.t3(i);
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$incremental_dir --incremental-basedir=$basedir --dbug=+d,mariabackup_events;
--let after_load_tablespaces=
--disable_result_log
echo # Prepare full backup, apply incremental one;
exec $XTRABACKUP --apply-log-only --prepare --target-dir=$basedir;
exec $XTRABACKUP --prepare --target-dir=$basedir --incremental-dir=$incremental_dir ;
echo # Restore and check results;
let $targetdir=$basedir;
-- source include/restart_and_restore.inc
--enable_result_log
# Test that t1 does not exist, but t1_renamed does
CREATE TABLE t1(i int);
DROP TABLE t1;
SELECT * from t1_renamed;
DROP TABLE t1_renamed;
# Test that t2 does not exist;
CREATE TABLE t2(i INT PRIMARY KEY) ENGINE INNODB;
DROP TABLE t2;
DROP TABLE t3;
DROP TABLE t4;
# Cleanup
rmdir $basedir;
rmdir $incremental_dir;
CREATE TABLE t1(i INT PRIMARY KEY auto_increment, a int) ENGINE INNODB;
INSERT INTO t1(a) SELECT * from seq_1_to_10000;
# xtrabackup backup
t1.frm
t1.ibd
t1.new
# xtrabackup prepare
# shutdown server
# remove datadir
# xtrabackup move back
# restart server
SELECT COUNT(*) from t1;
COUNT(*)
10000
DROP TABLE t1;
--source include/have_debug.inc
CREATE TABLE t1(i INT PRIMARY KEY auto_increment, a int) ENGINE INNODB;
INSERT INTO t1(a) SELECT * from seq_1_to_10000;
let $targetdir=$MYSQLTEST_VARDIR/tmp/backup;
let after_copy_test_t1=CREATE INDEX a_ind ON test.t1(a) ALGORITHM=INPLACE;
echo # xtrabackup backup;
--disable_result_log
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir --dbug=+d,mariabackup_events;
--enable_result_log
--list_files $targetdir/test t1*
--let before_copy_test_t1=
echo # xtrabackup prepare;
--disable_result_log
exec $XTRABACKUP --prepare --target-dir=$targetdir;
-- source include/restart_and_restore.inc
--enable_result_log
# Check that new table is there after restore.
SELECT COUNT(*) from t1;
DROP TABLE t1;
rmdir $targetdir;
CREATE TABLE t1(i int) ENGINE=INNODB;
CREATE TABLE t2(i int) ENGINE=INNODB;
CREATE TABLE t3(a CHAR(36)) ENGINE INNODB;
INSERT INTO t3 SELECT UUID() FROM seq_1_to_1000;
# xtrabackup backup
# xtrabackup prepare
# shutdown server
# remove datadir
# xtrabackup move back
# restart server
SELECT COUNT(*) from t1;
COUNT(*)
100
SELECT COUNT(*) from t2;
COUNT(*)
1000
SELECT COUNT(*) from t3;
COUNT(*)
1000
DROP INDEX index_a ON t3;
DROP TABLE t1;
DROP TABLE t2;
DROP TABLE t3;
--source include/have_debug.inc
let $targetdir=$MYSQLTEST_VARDIR/tmp/backup;
mkdir $targetdir;
CREATE TABLE t1(i int) ENGINE=INNODB;
CREATE TABLE t2(i int) ENGINE=INNODB;
CREATE TABLE t3(a CHAR(36)) ENGINE INNODB;
INSERT INTO t3 SELECT UUID() FROM seq_1_to_1000;
# this will table and populate it, after backup has list of tables to be copied
--let before_copy_test_t1=BEGIN NOT ATOMIC DROP TABLE test.t1;CREATE TABLE test.t1 ENGINE=INNODB SELECT UUID() from test.seq_1_to_100; END
--let after_copy_test_t2=BEGIN NOT ATOMIC DROP TABLE test.t2;CREATE TABLE test.t2 ENGINE=INNODB SELECT UUID() from test.seq_1_to_1000; END
--let after_copy_test_t3=ALTER TABLE test.t3 ADD INDEX index_a(a),ALGORITHM=COPY
echo # xtrabackup backup;
--disable_result_log
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir --close-files --dbug=+d,mariabackup_events;
--enable_result_log
--let after_load_tables=
echo # xtrabackup prepare;
--disable_result_log
exec $XTRABACKUP --prepare --target-dir=$targetdir;
-- source include/restart_and_restore.inc
--enable_result_log
# Check that new table is there after restore.
SELECT COUNT(*) from t1;
SELECT COUNT(*) from t2;
SELECT COUNT(*) from t3;
DROP INDEX index_a ON t3;
DROP TABLE t1;
DROP TABLE t2;
DROP TABLE t3;
rmdir $targetdir;
CREATE TABLE t1(i int) ENGINE INNODB;
INSERT into t1 values(1);
CREATE TABLE t2(i int) ENGINE INNODB;
INSERT INTO t2 values(2);
CREATE TABLE t3(i int) ENGINE INNODB;
CREATE TABLE t4(i int) ENGINE INNODB;
CREATE TABLE a(a int) ENGINE INNODB;
INSERT INTO a values(1);
CREATE TABLE b(b CHAR(1)) ENGINE INNODB;
INSERT INTO b VALUES('b');
CREATE TABLE a1(a1 int) ENGINE INNODB;
INSERT INTO a1 VALUES(1);
CREATE TABLE b1(b1 CHAR(2)) ENGINE INNODB;
INSERT INTO b1 VALUES('b1');
# xtrabackup prepare
# shutdown server
# remove datadir
# xtrabackup move back
# restart server
CREATE TABLE t1(i int);
DROP TABLE t1;
SELECT * from t1_renamed;
i
1
DROP TABLE t1_renamed;
CREATE TABLE t2(i int);
DROP TABLE t2;
SELECT * from t2_renamed;
i
2
DROP TABLE t2_renamed;
SELECT * from t3;
i
3
DROP TABLE t3;
SELECT * from t4;
i
DROP TABLE t4;
CREATE TABLE tmp(i int);
DROP TABLE tmp;
SELECT * FROM a;
b
b
SELECT * FROM b;
a
1
SELECT * FROM a1;
b1
b1
SELECT * FROM b1;
a1
1
DROP TABLE a,b,a1,b1;
--source include/have_debug.inc
let $targetdir=$MYSQLTEST_VARDIR/tmp/backup;
mkdir $targetdir;
CREATE TABLE t1(i int) ENGINE INNODB;
INSERT into t1 values(1);
CREATE TABLE t2(i int) ENGINE INNODB;
INSERT INTO t2 values(2);
CREATE TABLE t3(i int) ENGINE INNODB;
CREATE TABLE t4(i int) ENGINE INNODB;
CREATE TABLE a(a int) ENGINE INNODB;
INSERT INTO a values(1);
CREATE TABLE b(b CHAR(1)) ENGINE INNODB;
INSERT INTO b VALUES('b');
CREATE TABLE a1(a1 int) ENGINE INNODB;
INSERT INTO a1 VALUES(1);
CREATE TABLE b1(b1 CHAR(2)) ENGINE INNODB;
INSERT INTO b1 VALUES('b1');
# Test renames before of after copying tablespaces
--let before_copy_test_t1=RENAME TABLE test.t1 TO test.t1_renamed
--let after_copy_test_t2=RENAME TABLE test.t2 TO test.t2_renamed
--let after_copy_test_t3=BEGIN NOT ATOMIC RENAME TABLE test.t3 TO test.t3_tmp; INSERT INTO test.t3_tmp VALUES(3); RENAME TABLE test.t3_tmp TO test.t3; END
--let before_copy_test_t4=RENAME TABLE test.t4 TO test.t4_tmp
--let after_copy_test_t4=RENAME TABLE test.t4_tmp TO test.t4
# Test circular renames
--let before_copy_test_b=RENAME TABLE test.a to test.tmp, test.b to test.a, test.tmp to test.b
--let after_copy_test_b1=RENAME TABLE test.a1 to test.tmp, test.b1 to test.a1, test.tmp to test.b1
--disable_result_log
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir --dbug=+d,mariabackup_events;
--enable_result_log
--let before_copy_test_t1=''
--let after_copy_test_t2=''
--let before_copy_test_a=''
--let after_copy_test_a1=''
echo # xtrabackup prepare;
--disable_result_log
exec $XTRABACKUP --prepare --target-dir=$targetdir;
-- source include/restart_and_restore.inc
--enable_result_log
# the table was renamed from t1 to t1_renamed
# make sure t1 does not exist, and t1_renamed does.
CREATE TABLE t1(i int);
DROP TABLE t1;
SELECT * from t1_renamed;
DROP TABLE t1_renamed;
CREATE TABLE t2(i int);
DROP TABLE t2;
SELECT * from t2_renamed;
DROP TABLE t2_renamed;
#rename to itself
SELECT * from t3;
DROP TABLE t3;
SELECT * from t4;
DROP TABLE t4;
# For circular renames , make sure intermediate tables do not exist
CREATE TABLE tmp(i int);
DROP TABLE tmp;
SELECT * FROM a;
SELECT * FROM b;
SELECT * FROM a1;
SELECT * FROM b1;
DROP TABLE a,b,a1,b1;
rmdir $targetdir;
CREATE TABLE t1(i int) ENGINE INNODB; CREATE TABLE t1(i int) ENGINE INNODB;
FOUND 1 /failed to execute query SELECT 1 FROM/ in backup.log # xtrabackup prepare
# shutdown server
# remove datadir
# xtrabackup move back
# restart server
CREATE TABLE t1(i int);
DROP TABLE t1;
SELECT * from t2;
i
DROP TABLE t2; DROP TABLE t2;
--source include/have_debug.inc --source include/have_debug.inc
let $targetdir=$MYSQLTEST_VARDIR/backup; let $targetdir=$MYSQLTEST_VARDIR/tmp/backup;
mkdir $targetdir; mkdir $targetdir;
CREATE TABLE t1(i int) ENGINE INNODB; CREATE TABLE t1(i int) ENGINE INNODB;
--error 1 exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir --lock-ddl-per-table --dbug=+d,rename_during_mdl_lock_table;
exec $XTRABACKUP --defaults-file=$MYSQLTEST_VARDIR/my.cnf --backup --target-dir=$targetdir --lock-ddl-per-table --dbug=+d,rename_during_mdl_lock_table 2>$targetdir/backup.log;
let SEARCH_FILE=$targetdir/backup.log; echo # xtrabackup prepare;
let SEARCH_PATTERN=failed to execute query SELECT 1 FROM; --disable_result_log
source include/search_pattern_in_file.inc; exec $XTRABACKUP --prepare --target-dir=$targetdir;
-- source include/restart_and_restore.inc
--enable_result_log
# the table was renamed from t1 to t2
# make sure t1 does not exist, and t2 does
CREATE TABLE t1(i int);
DROP TABLE t1;
SELECT * from t2;
DROP TABLE t2; DROP TABLE t2;
rmdir $targetdir; rmdir $targetdir;
--innodb --loose-changed_page_bitmaps --innodb-sys-tables --innodb-flush-log-at-trx-commit=2 --innodb --loose-changed_page_bitmaps --innodb-sys-tables --innodb-flush-log-at-trx-commit=2 --sequence
...@@ -11,4 +11,6 @@ INSERT INTO t VALUES('foobar1'); ...@@ -11,4 +11,6 @@ INSERT INTO t VALUES('foobar1');
SELECT * from t; SELECT * from t;
c c
foobar1 foobar1
Warnings:
Note 1105 AWS KMS plugin: loaded key 1, version 1, key length 128 bit
DROP TABLE t; DROP TABLE t;
...@@ -739,42 +739,35 @@ fil_node_open_file( ...@@ -739,42 +739,35 @@ fil_node_open_file(
return(true); return(true);
} }
/** Close a file node. /** Close the file handle. */
@param[in,out] node File node */ void fil_node_t::close()
static
void
fil_node_close_file(
fil_node_t* node)
{ {
bool ret; bool ret;
ut_ad(mutex_own(&(fil_system.mutex))); ut_ad(mutex_own(&fil_system.mutex));
ut_a(node->is_open()); ut_a(is_open());
ut_a(node->n_pending == 0); ut_a(n_pending == 0);
ut_a(node->n_pending_flushes == 0); ut_a(n_pending_flushes == 0);
ut_a(!node->being_extended); ut_a(!being_extended);
ut_a(node->modification_counter == node->flush_counter ut_a(modification_counter == flush_counter
|| node->space->purpose == FIL_TYPE_TEMPORARY || space->purpose == FIL_TYPE_TEMPORARY
|| srv_fast_shutdown == 2 || srv_fast_shutdown == 2
|| !srv_was_started); || !srv_was_started);
ret = os_file_close(node->handle); ret = os_file_close(handle);
ut_a(ret); ut_a(ret);
/* printf("Closing file %s\n", node->name); */ /* printf("Closing file %s\n", name); */
node->handle = OS_FILE_CLOSED; handle = OS_FILE_CLOSED;
ut_ad(!node->is_open()); ut_ad(!is_open());
ut_a(fil_system.n_open > 0); ut_a(fil_system.n_open > 0);
fil_system.n_open--; fil_system.n_open--;
fil_n_file_opened--; fil_n_file_opened--;
if (fil_space_belongs_in_lru(node->space)) { if (fil_space_belongs_in_lru(space)) {
ut_a(UT_LIST_GET_LEN(fil_system.LRU) > 0); ut_a(UT_LIST_GET_LEN(fil_system.LRU) > 0);
UT_LIST_REMOVE(fil_system.LRU, this);
/* The node is in the LRU list, remove it */
UT_LIST_REMOVE(fil_system.LRU, node);
} }
} }
...@@ -810,7 +803,7 @@ fil_try_to_close_file_in_LRU( ...@@ -810,7 +803,7 @@ fil_try_to_close_file_in_LRU(
&& node->n_pending_flushes == 0 && node->n_pending_flushes == 0
&& !node->being_extended) { && !node->being_extended) {
fil_node_close_file(node); node->close();
return(true); return(true);
} }
...@@ -1240,7 +1233,7 @@ fil_node_close_to_free( ...@@ -1240,7 +1233,7 @@ fil_node_close_to_free(
ut_a(!node->being_extended); ut_a(!node->being_extended);
if (node->is_open()) { if (node->is_open()) {
/* We fool the assertion in fil_node_close_file() to think /* We fool the assertion in fil_node_t::close() to think
there are no unflushed modifications in the file */ there are no unflushed modifications in the file */
node->modification_counter = node->flush_counter; node->modification_counter = node->flush_counter;
...@@ -1259,7 +1252,7 @@ fil_node_close_to_free( ...@@ -1259,7 +1252,7 @@ fil_node_close_to_free(
UT_LIST_REMOVE(fil_system.unflushed_spaces, space); UT_LIST_REMOVE(fil_system.unflushed_spaces, space);
} }
fil_node_close_file(node); node->close();
} }
} }
...@@ -1750,7 +1743,7 @@ void fil_space_t::close() ...@@ -1750,7 +1743,7 @@ void fil_space_t::close()
node != NULL; node != NULL;
node = UT_LIST_GET_NEXT(chain, node)) { node = UT_LIST_GET_NEXT(chain, node)) {
if (node->is_open()) { if (node->is_open()) {
fil_node_close_file(node); node->close();
} }
} }
...@@ -1911,7 +1904,7 @@ fil_close_all_files(void) ...@@ -1911,7 +1904,7 @@ fil_close_all_files(void)
node = UT_LIST_GET_NEXT(chain, node)) { node = UT_LIST_GET_NEXT(chain, node)) {
if (node->is_open()) { if (node->is_open()) {
fil_node_close_file(node); node->close();
} }
} }
...@@ -1958,7 +1951,7 @@ fil_close_log_files( ...@@ -1958,7 +1951,7 @@ fil_close_log_files(
node = UT_LIST_GET_NEXT(chain, node)) { node = UT_LIST_GET_NEXT(chain, node)) {
if (node->is_open()) { if (node->is_open()) {
fil_node_close_file(node); node->close();
} }
} }
......
...@@ -327,6 +327,9 @@ struct fil_node_t { ...@@ -327,6 +327,9 @@ struct fil_node_t {
{ {
return(handle != OS_FILE_CLOSED); return(handle != OS_FILE_CLOSED);
} }
/** Close the file handle. */
void close();
}; };
/** Value of fil_node_t::magic_n */ /** Value of fil_node_t::magic_n */
......
...@@ -806,69 +806,6 @@ lock_trx_has_expl_x_lock( ...@@ -806,69 +806,6 @@ lock_trx_has_expl_x_lock(
MY_ATTRIBUTE((nonnull, warn_unused_result)); MY_ATTRIBUTE((nonnull, warn_unused_result));
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
/**
Allocate cached locks for the transaction.
@param trx allocate cached record locks for this transaction */
void
lock_trx_alloc_locks(trx_t* trx);
/** Lock modes and types */
/* @{ */
#define LOCK_MODE_MASK 0xFUL /*!< mask used to extract mode from the
type_mode field in a lock */
/** Lock types */
/* @{ */
#define LOCK_TABLE 16U /*!< table lock */
#define LOCK_REC 32U /*!< record lock */
#define LOCK_TYPE_MASK 0xF0UL /*!< mask used to extract lock type from the
type_mode field in a lock */
#if LOCK_MODE_MASK & LOCK_TYPE_MASK
# error "LOCK_MODE_MASK & LOCK_TYPE_MASK"
#endif
#define LOCK_WAIT 256U /*!< Waiting lock flag; when set, it
means that the lock has not yet been
granted, it is just waiting for its
turn in the wait queue */
/* Precise modes */
#define LOCK_ORDINARY 0 /*!< this flag denotes an ordinary
next-key lock in contrast to LOCK_GAP
or LOCK_REC_NOT_GAP */
#define LOCK_GAP 512U /*!< when this bit is set, it means that the
lock holds only on the gap before the record;
for instance, an x-lock on the gap does not
give permission to modify the record on which
the bit is set; locks of this type are created
when records are removed from the index chain
of records */
#define LOCK_REC_NOT_GAP 1024U /*!< this bit means that the lock is only on
the index record and does NOT block inserts
to the gap before the index record; this is
used in the case when we retrieve a record
with a unique key, and is also used in
locking plain SELECTs (not part of UPDATE
or DELETE) when the user has set the READ
COMMITTED isolation level */
#define LOCK_INSERT_INTENTION 2048U/*!< this bit is set when we place a waiting
gap type record lock request in order to let
an insert of an index record to wait until
there are no conflicting locks by other
transactions on the gap; note that this flag
remains set when the waiting lock is granted,
or if the lock is inherited to a neighboring
record */
#define LOCK_PREDICATE 8192U /*!< Predicate lock */
#define LOCK_PRDT_PAGE 16384U /*!< Page lock */
#if (LOCK_WAIT|LOCK_GAP|LOCK_REC_NOT_GAP|LOCK_INSERT_INTENTION|LOCK_PREDICATE|LOCK_PRDT_PAGE)&LOCK_MODE_MASK
# error
#endif
#if (LOCK_WAIT|LOCK_GAP|LOCK_REC_NOT_GAP|LOCK_INSERT_INTENTION|LOCK_PREDICATE|LOCK_PRDT_PAGE)&LOCK_TYPE_MASK
# error
#endif
/* @} */
/** Lock operation struct */ /** Lock operation struct */
struct lock_op_t{ struct lock_op_t{
dict_table_t* table; /*!< table to be locked */ dict_table_t* table; /*!< table to be locked */
......
...@@ -42,19 +42,6 @@ those functions in lock/ */ ...@@ -42,19 +42,6 @@ those functions in lock/ */
#define UINT32_MAX (4294967295U) #define UINT32_MAX (4294967295U)
#endif #endif
/** A table lock */
struct lock_table_t {
dict_table_t* table; /*!< database table in dictionary
cache */
UT_LIST_NODE_T(lock_t)
locks; /*!< list of locks on the same
table */
/** Print the table lock into the given output stream
@param[in,out] out the output stream
@return the given output stream. */
std::ostream& print(std::ostream& out) const;
};
/** Print the table lock into the given output stream /** Print the table lock into the given output stream
@param[in,out] out the output stream @param[in,out] out the output stream
@return the given output stream. */ @return the given output stream. */
...@@ -77,131 +64,11 @@ operator<<(std::ostream& out, const lock_table_t& lock) ...@@ -77,131 +64,11 @@ operator<<(std::ostream& out, const lock_table_t& lock)
return(lock.print(out)); return(lock.print(out));
} }
/** Record lock for a page */
struct lock_rec_t {
ib_uint32_t space; /*!< space id */
ib_uint32_t page_no; /*!< page number */
ib_uint32_t n_bits; /*!< number of bits in the lock
bitmap; NOTE: the lock bitmap is
placed immediately after the
lock struct */
/** Print the record lock into the given output stream
@param[in,out] out the output stream
@return the given output stream. */
std::ostream& print(std::ostream& out) const;
};
/** Print the record lock into the given output stream
@param[in,out] out the output stream
@return the given output stream. */
inline
std::ostream& lock_rec_t::print(std::ostream& out) const
{
out << "[lock_rec_t: space=" << space << ", page_no=" << page_no
<< ", n_bits=" << n_bits << "]";
return(out);
}
inline
std::ostream&
operator<<(std::ostream& out, const lock_rec_t& lock)
{
return(lock.print(out));
}
/** Lock struct; protected by lock_sys.mutex */
struct lock_t {
trx_t* trx; /*!< transaction owning the
lock */
UT_LIST_NODE_T(lock_t)
trx_locks; /*!< list of the locks of the
transaction */
dict_index_t* index; /*!< index for a record lock */
lock_t* hash; /*!< hash chain node for a record
lock. The link node in a singly linked
list, used during hashing. */
/* Statistics for how long lock has been held and time
how long this lock had to be waited before it was granted */
time_t requested_time; /*!< Lock request time */
ulint wait_time; /*!< Time waited this lock or 0 */
union {
lock_table_t tab_lock;/*!< table lock */
lock_rec_t rec_lock;/*!< record lock */
} un_member; /*!< lock details */
ib_uint32_t type_mode; /*!< lock type, mode, LOCK_GAP or
LOCK_REC_NOT_GAP,
LOCK_INSERT_INTENTION,
wait flag, ORed */
/** Determine if the lock object is a record lock.
@return true if record lock, false otherwise. */
bool is_record_lock() const
{
return(type() == LOCK_REC);
}
bool is_waiting() const
{
return(type_mode & LOCK_WAIT);
}
bool is_gap() const
{
return(type_mode & LOCK_GAP);
}
bool is_record_not_gap() const
{
return(type_mode & LOCK_REC_NOT_GAP);
}
bool is_insert_intention() const
{
return(type_mode & LOCK_INSERT_INTENTION);
}
ulint type() const {
return(type_mode & LOCK_TYPE_MASK);
}
enum lock_mode mode() const
{
return(static_cast<enum lock_mode>(type_mode & LOCK_MODE_MASK));
}
/** Print the lock object into the given output stream.
@param[in,out] out the output stream
@return the given output stream. */
std::ostream& print(std::ostream& out) const;
/** Convert the member 'type_mode' into a human readable string.
@return human readable string */
std::string type_mode_string() const;
const char* type_string() const
{
switch (type_mode & LOCK_TYPE_MASK) {
case LOCK_REC:
return("LOCK_REC");
case LOCK_TABLE:
return("LOCK_TABLE");
default:
ut_error;
}
}
};
/** Convert the member 'type_mode' into a human readable string. /** Convert the member 'type_mode' into a human readable string.
@return human readable string */ @return human readable string */
inline inline
std::string std::string
lock_t::type_mode_string() const ib_lock_t::type_mode_string() const
{ {
std::ostringstream sout; std::ostringstream sout;
sout << type_string(); sout << type_string();
...@@ -227,7 +94,7 @@ lock_t::type_mode_string() const ...@@ -227,7 +94,7 @@ lock_t::type_mode_string() const
inline inline
std::ostream& std::ostream&
lock_t::print(std::ostream& out) const ib_lock_t::print(std::ostream& out) const
{ {
out << "[lock_t: type_mode=" << type_mode << "(" out << "[lock_t: type_mode=" << type_mode << "("
<< type_mode_string() << ")"; << type_mode_string() << ")";
...@@ -244,7 +111,7 @@ lock_t::print(std::ostream& out) const ...@@ -244,7 +111,7 @@ lock_t::print(std::ostream& out) const
inline inline
std::ostream& std::ostream&
operator<<(std::ostream& out, const lock_t& lock) operator<<(std::ostream& out, const ib_lock_t& lock)
{ {
return(lock.print(out)); return(lock.print(out));
} }
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 2007, 2014, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2007, 2014, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
...@@ -388,17 +389,10 @@ lock_table_has( ...@@ -388,17 +389,10 @@ lock_table_has(
const dict_table_t* table, /*!< in: table */ const dict_table_t* table, /*!< in: table */
lock_mode in_mode)/*!< in: lock mode */ lock_mode in_mode)/*!< in: lock mode */
{ {
if (trx->lock.table_locks.empty()) {
return(NULL);
}
typedef lock_pool_t::const_reverse_iterator iterator;
iterator end = trx->lock.table_locks.rend();
/* Look for stronger locks the same trx already has on the table */ /* Look for stronger locks the same trx already has on the table */
for (iterator it = trx->lock.table_locks.rbegin(); it != end; ++it) { for (lock_list::const_iterator it = trx->lock.table_locks.begin(),
end = trx->lock.table_locks.end(); it != end; ++it) {
const lock_t* lock = *it; const lock_t* lock = *it;
......
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2018, MariaDB Corporation.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software the terms of the GNU General Public License as published by the Free Software
...@@ -72,6 +73,195 @@ const char* lock_mode_string(enum lock_mode mode) ...@@ -72,6 +73,195 @@ const char* lock_mode_string(enum lock_mode mode)
} }
} }
typedef UT_LIST_BASE_NODE_T(lock_t) trx_lock_list_t; /** A table lock */
struct lock_table_t {
dict_table_t* table; /*!< database table in dictionary
cache */
UT_LIST_NODE_T(ib_lock_t)
locks; /*!< list of locks on the same
table */
/** Print the table lock into the given output stream
@param[in,out] out the output stream
@return the given output stream. */
std::ostream& print(std::ostream& out) const;
};
/** Record lock for a page */
struct lock_rec_t {
ib_uint32_t space; /*!< space id */
ib_uint32_t page_no; /*!< page number */
ib_uint32_t n_bits; /*!< number of bits in the lock
bitmap; NOTE: the lock bitmap is
placed immediately after the
lock struct */
/** Print the record lock into the given output stream
@param[in,out] out the output stream
@return the given output stream. */
std::ostream& print(std::ostream& out) const;
};
/** Print the record lock into the given output stream
@param[in,out] out the output stream
@return the given output stream. */
inline
std::ostream& lock_rec_t::print(std::ostream& out) const
{
out << "[lock_rec_t: space=" << space << ", page_no=" << page_no
<< ", n_bits=" << n_bits << "]";
return(out);
}
inline
std::ostream&
operator<<(std::ostream& out, const lock_rec_t& lock)
{
return(lock.print(out));
}
#define LOCK_MODE_MASK 0xFUL /*!< mask used to extract mode from the
type_mode field in a lock */
/** Lock types */
/* @{ */
#define LOCK_TABLE 16U /*!< table lock */
#define LOCK_REC 32U /*!< record lock */
#define LOCK_TYPE_MASK 0xF0UL /*!< mask used to extract lock type from the
type_mode field in a lock */
#if LOCK_MODE_MASK & LOCK_TYPE_MASK
# error "LOCK_MODE_MASK & LOCK_TYPE_MASK"
#endif
#define LOCK_WAIT 256U /*!< Waiting lock flag; when set, it
means that the lock has not yet been
granted, it is just waiting for its
turn in the wait queue */
/* Precise modes */
#define LOCK_ORDINARY 0 /*!< this flag denotes an ordinary
next-key lock in contrast to LOCK_GAP
or LOCK_REC_NOT_GAP */
#define LOCK_GAP 512U /*!< when this bit is set, it means that the
lock holds only on the gap before the record;
for instance, an x-lock on the gap does not
give permission to modify the record on which
the bit is set; locks of this type are created
when records are removed from the index chain
of records */
#define LOCK_REC_NOT_GAP 1024U /*!< this bit means that the lock is only on
the index record and does NOT block inserts
to the gap before the index record; this is
used in the case when we retrieve a record
with a unique key, and is also used in
locking plain SELECTs (not part of UPDATE
or DELETE) when the user has set the READ
COMMITTED isolation level */
#define LOCK_INSERT_INTENTION 2048U/*!< this bit is set when we place a waiting
gap type record lock request in order to let
an insert of an index record to wait until
there are no conflicting locks by other
transactions on the gap; note that this flag
remains set when the waiting lock is granted,
or if the lock is inherited to a neighboring
record */
#define LOCK_PREDICATE 8192U /*!< Predicate lock */
#define LOCK_PRDT_PAGE 16384U /*!< Page lock */
#if (LOCK_WAIT|LOCK_GAP|LOCK_REC_NOT_GAP|LOCK_INSERT_INTENTION|LOCK_PREDICATE|LOCK_PRDT_PAGE)&LOCK_MODE_MASK
# error
#endif
#if (LOCK_WAIT|LOCK_GAP|LOCK_REC_NOT_GAP|LOCK_INSERT_INTENTION|LOCK_PREDICATE|LOCK_PRDT_PAGE)&LOCK_TYPE_MASK
# error
#endif
/* @} */
/** Lock struct; protected by lock_sys.mutex */
struct ib_lock_t
{
trx_t* trx; /*!< transaction owning the
lock */
UT_LIST_NODE_T(ib_lock_t)
trx_locks; /*!< list of the locks of the
transaction */
dict_index_t* index; /*!< index for a record lock */
ib_lock_t* hash; /*!< hash chain node for a record
lock. The link node in a singly linked
list, used during hashing. */
/* Statistics for how long lock has been held and time
how long this lock had to be waited before it was granted */
time_t requested_time; /*!< Lock request time */
ulint wait_time; /*!< Time waited this lock or 0 */
union {
lock_table_t tab_lock;/*!< table lock */
lock_rec_t rec_lock;/*!< record lock */
} un_member; /*!< lock details */
ib_uint32_t type_mode; /*!< lock type, mode, LOCK_GAP or
LOCK_REC_NOT_GAP,
LOCK_INSERT_INTENTION,
wait flag, ORed */
/** Determine if the lock object is a record lock.
@return true if record lock, false otherwise. */
bool is_record_lock() const
{
return(type() == LOCK_REC);
}
bool is_waiting() const
{
return(type_mode & LOCK_WAIT);
}
bool is_gap() const
{
return(type_mode & LOCK_GAP);
}
bool is_record_not_gap() const
{
return(type_mode & LOCK_REC_NOT_GAP);
}
bool is_insert_intention() const
{
return(type_mode & LOCK_INSERT_INTENTION);
}
ulint type() const {
return(type_mode & LOCK_TYPE_MASK);
}
enum lock_mode mode() const
{
return(static_cast<enum lock_mode>(type_mode & LOCK_MODE_MASK));
}
/** Print the lock object into the given output stream.
@param[in,out] out the output stream
@return the given output stream. */
std::ostream& print(std::ostream& out) const;
/** Convert the member 'type_mode' into a human readable string.
@return human readable string */
std::string type_mode_string() const;
const char* type_string() const
{
switch (type_mode & LOCK_TYPE_MASK) {
case LOCK_REC:
return("LOCK_REC");
case LOCK_TABLE:
return("LOCK_TABLE");
default:
ut_error;
}
}
};
typedef UT_LIST_BASE_NODE_T(ib_lock_t) trx_lock_list_t;
#endif /* lock0types_h */ #endif /* lock0types_h */
...@@ -139,9 +139,21 @@ bool recv_parse_log_recs(lsn_t checkpoint_lsn, store_t store, bool apply); ...@@ -139,9 +139,21 @@ bool recv_parse_log_recs(lsn_t checkpoint_lsn, store_t store, bool apply);
/** Moves the parsing buffer data left to the buffer start. */ /** Moves the parsing buffer data left to the buffer start. */
void recv_sys_justify_left_parsing_buf(); void recv_sys_justify_left_parsing_buf();
/** Backup function checks whether the space id belongs to /** Report optimized DDL operation (without redo log), corresponding to MLOG_INDEX_LOAD.
the skip table list given in the mariabackup option. */ @param[in] space_id tablespace identifier
extern bool(*check_if_backup_includes)(ulint space_id); */
extern void(*log_optimized_ddl_op)(ulint space_id);
/** Report an operation to create, delete, or rename a file during backup.
@param[in] space_id tablespace identifier
@param[in] flags tablespace flags (NULL if not create)
@param[in] name file name (not NUL-terminated)
@param[in] len length of name, in bytes
@param[in] new_name new file name (NULL if not rename)
@param[in] new_len length of new_name, in bytes (0 if NULL) */
extern void (*log_file_op)(ulint space_id, const byte* flags,
const byte* name, ulint len,
const byte* new_name, ulint new_len);
/** Block of log record data */ /** Block of log record data */
struct recv_data_t{ struct recv_data_t{
......
...@@ -646,8 +646,11 @@ class rw_trx_hash_t ...@@ -646,8 +646,11 @@ class rw_trx_hash_t
{ {
mutex_enter(&element->mutex); mutex_enter(&element->mutex);
lf_hash_search_unpin(pins); lf_hash_search_unpin(pins);
if ((trx= element->trx)) trx= element->trx;
{ if (!trx);
else if (UNIV_UNLIKELY(trx_id != trx->id))
trx= NULL;
else {
if (do_ref_count) if (do_ref_count)
trx->reference(); trx->reference();
ut_d(validate_element(trx)); ut_d(validate_element(trx));
......
...@@ -476,7 +476,9 @@ Check transaction state */ ...@@ -476,7 +476,9 @@ Check transaction state */
@param t transaction handle */ @param t transaction handle */
#define assert_trx_is_free(t) do { \ #define assert_trx_is_free(t) do { \
ut_ad(trx_state_eq((t), TRX_STATE_NOT_STARTED)); \ ut_ad(trx_state_eq((t), TRX_STATE_NOT_STARTED)); \
ut_ad(!trx->has_logged()); \ ut_ad(!(t)->id); \
ut_ad(!(t)->has_logged()); \
ut_ad(!(t)->is_referenced()); \
ut_ad(!(t)->read_view.is_open()); \ ut_ad(!(t)->read_view.is_open()); \
ut_ad((t)->lock.wait_thr == NULL); \ ut_ad((t)->lock.wait_thr == NULL); \
ut_ad(UT_LIST_GET_LEN((t)->lock.trx_locks) == 0); \ ut_ad(UT_LIST_GET_LEN((t)->lock.trx_locks) == 0); \
...@@ -517,7 +519,7 @@ The transaction must have mysql_thd assigned. */ ...@@ -517,7 +519,7 @@ The transaction must have mysql_thd assigned. */
# define assert_trx_nonlocking_or_in_list(trx) ((void)0) # define assert_trx_nonlocking_or_in_list(trx) ((void)0)
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
typedef std::vector<ib_lock_t*, ut_allocator<ib_lock_t*> > lock_pool_t; typedef std::vector<ib_lock_t*, ut_allocator<ib_lock_t*> > lock_list;
/*******************************************************************//** /*******************************************************************//**
Latching protocol for trx_lock_t::que_state. trx_lock_t::que_state Latching protocol for trx_lock_t::que_state. trx_lock_t::que_state
...@@ -579,13 +581,19 @@ struct trx_lock_t { ...@@ -579,13 +581,19 @@ struct trx_lock_t {
only be modified by the thread that is only be modified by the thread that is
serving the running transaction. */ serving the running transaction. */
lock_pool_t rec_pool; /*!< Pre-allocated record locks */ /** Pre-allocated record locks */
struct {
ib_lock_t lock; byte pad[256];
} rec_pool[8];
lock_pool_t table_pool; /*!< Pre-allocated table locks */ /** Pre-allocated table locks */
ib_lock_t table_pool[8];
ulint rec_cached; /*!< Next free rec lock in pool */ /** Next available rec_pool[] entry */
unsigned rec_cached;
ulint table_cached; /*!< Next free table lock in pool */ /** Next available table_pool[] entry */
unsigned table_cached;
mem_heap_t* lock_heap; /*!< memory heap for trx_locks; mem_heap_t* lock_heap; /*!< memory heap for trx_locks;
protected by lock_sys.mutex */ protected by lock_sys.mutex */
...@@ -595,7 +603,7 @@ struct trx_lock_t { ...@@ -595,7 +603,7 @@ struct trx_lock_t {
and lock_sys.mutex; removals are and lock_sys.mutex; removals are
protected by lock_sys.mutex */ protected by lock_sys.mutex */
lock_pool_t table_locks; /*!< All table locks requested by this lock_list table_locks; /*!< All table locks requested by this
transaction, including AUTOINC locks */ transaction, including AUTOINC locks */
bool cancel; /*!< true if the transaction is being bool cancel; /*!< true if the transaction is being
......
...@@ -125,8 +125,7 @@ struct Pool { ...@@ -125,8 +125,7 @@ struct Pool {
elem = NULL; elem = NULL;
} }
m_lock_strategy.exit(); #if defined HAVE_valgrind || defined __SANITIZE_ADDRESS__
if (elem) { if (elem) {
/* Unpoison the memory for AddressSanitizer */ /* Unpoison the memory for AddressSanitizer */
MEM_UNDEFINED(&elem->m_type, sizeof elem->m_type); MEM_UNDEFINED(&elem->m_type, sizeof elem->m_type);
...@@ -135,10 +134,11 @@ struct Pool { ...@@ -135,10 +134,11 @@ struct Pool {
actually initialized; we checked that by actually initialized; we checked that by
UNIV_MEM_ASSERT_RW() in mem_free() below. */ UNIV_MEM_ASSERT_RW() in mem_free() below. */
UNIV_MEM_VALID(&elem->m_type, sizeof elem->m_type); UNIV_MEM_VALID(&elem->m_type, sizeof elem->m_type);
return &elem->m_type;
} }
#endif
return NULL; m_lock_strategy.exit();
return elem ? &elem->m_type : NULL;
} }
/** Add the object to the pool. /** Add the object to the pool.
...@@ -151,8 +151,12 @@ struct Pool { ...@@ -151,8 +151,12 @@ struct Pool {
elem = reinterpret_cast<Element*>(p - sizeof(*elem)); elem = reinterpret_cast<Element*>(p - sizeof(*elem));
UNIV_MEM_ASSERT_RW(&elem->m_type, sizeof elem->m_type); UNIV_MEM_ASSERT_RW(&elem->m_type, sizeof elem->m_type);
elem->m_pool->put(elem); elem->m_pool->m_lock_strategy.enter();
elem->m_pool->putl(elem);
MEM_NOACCESS(&elem->m_type, sizeof elem->m_type); MEM_NOACCESS(&elem->m_type, sizeof elem->m_type);
elem->m_pool->m_lock_strategy.exit();
} }
protected: protected:
...@@ -170,17 +174,13 @@ struct Pool { ...@@ -170,17 +174,13 @@ struct Pool {
/** Release the object to the free pool /** Release the object to the free pool
@param elem element to free */ @param elem element to free */
void put(Element* elem) void putl(Element* elem)
{ {
m_lock_strategy.enter();
ut_ad(elem >= m_start && elem < m_last); ut_ad(elem >= m_start && elem < m_last);
ut_ad(Factory::debug(&elem->m_type)); ut_ad(Factory::debug(&elem->m_type));
m_pqueue.push(elem); m_pqueue.push(elem);
m_lock_strategy.exit();
} }
/** Initialise the elements. /** Initialise the elements.
......
...@@ -59,18 +59,6 @@ ulong innodb_lock_schedule_algorithm; ...@@ -59,18 +59,6 @@ ulong innodb_lock_schedule_algorithm;
/** The value of innodb_deadlock_detect */ /** The value of innodb_deadlock_detect */
my_bool innobase_deadlock_detect; my_bool innobase_deadlock_detect;
/** Total number of cached record locks */
static const ulint REC_LOCK_CACHE = 8;
/** Maximum record lock size in bytes */
static const ulint REC_LOCK_SIZE = sizeof(ib_lock_t) + 256;
/** Total number of cached table locks */
static const ulint TABLE_LOCK_CACHE = 8;
/** Size in bytes, of the table lock instance */
static const ulint TABLE_LOCK_SIZE = sizeof(ib_lock_t);
/*********************************************************************//** /*********************************************************************//**
Checks if a waiting record lock request still has to wait in a queue. Checks if a waiting record lock request still has to wait in a queue.
@return lock that is causing the wait */ @return lock that is causing the wait */
...@@ -1409,13 +1397,13 @@ lock_rec_create_low( ...@@ -1409,13 +1397,13 @@ lock_rec_create_low(
} }
} }
if (trx->lock.rec_cached >= trx->lock.rec_pool.size() if (trx->lock.rec_cached >= UT_ARR_SIZE(trx->lock.rec_pool)
|| sizeof *lock + n_bytes > REC_LOCK_SIZE) { || sizeof *lock + n_bytes > sizeof *trx->lock.rec_pool) {
lock = static_cast<lock_t*>( lock = static_cast<lock_t*>(
mem_heap_alloc(trx->lock.lock_heap, mem_heap_alloc(trx->lock.lock_heap,
sizeof *lock + n_bytes)); sizeof *lock + n_bytes));
} else { } else {
lock = trx->lock.rec_pool[trx->lock.rec_cached++]; lock = &trx->lock.rec_pool[trx->lock.rec_cached++].lock;
} }
lock->trx = trx; lock->trx = trx;
...@@ -3520,8 +3508,9 @@ lock_table_create( ...@@ -3520,8 +3508,9 @@ lock_table_create(
ib_vector_push(trx->autoinc_locks, &lock); ib_vector_push(trx->autoinc_locks, &lock);
} else if (trx->lock.table_cached < trx->lock.table_pool.size()) { } else if (trx->lock.table_cached
lock = trx->lock.table_pool[trx->lock.table_cached++]; < UT_ARR_SIZE(trx->lock.table_pool)) {
lock = &trx->lock.table_pool[trx->lock.table_cached++];
} else { } else {
lock = static_cast<lock_t*>( lock = static_cast<lock_t*>(
...@@ -4373,24 +4362,15 @@ lock_trx_table_locks_remove( ...@@ -4373,24 +4362,15 @@ lock_trx_table_locks_remove(
ut_ad(trx_mutex_own(trx)); ut_ad(trx_mutex_own(trx));
} }
typedef lock_pool_t::reverse_iterator iterator; for (lock_list::iterator it = trx->lock.table_locks.begin(),
end = trx->lock.table_locks.end(); it != end; ++it) {
iterator end = trx->lock.table_locks.rend();
for (iterator it = trx->lock.table_locks.rbegin(); it != end; ++it) {
const lock_t* lock = *it; const lock_t* lock = *it;
if (lock == NULL) { ut_ad(!lock || trx == lock->trx);
continue; ut_ad(!lock || lock_get_type_low(lock) & LOCK_TABLE);
} ut_ad(!lock || lock->un_member.tab_lock.table);
ut_a(trx == lock->trx);
ut_a(lock_get_type_low(lock) & LOCK_TABLE);
ut_a(lock->un_member.tab_lock.table != NULL);
if (lock == lock_to_remove) { if (lock == lock_to_remove) {
*it = NULL; *it = NULL;
if (!trx->lock.cancel) { if (!trx->lock.cancel) {
...@@ -4807,11 +4787,8 @@ lock_trx_table_locks_find( ...@@ -4807,11 +4787,8 @@ lock_trx_table_locks_find(
trx_mutex_enter(trx); trx_mutex_enter(trx);
typedef lock_pool_t::const_reverse_iterator iterator; for (lock_list::const_iterator it = trx->lock.table_locks.begin(),
end = trx->lock.table_locks.end(); it != end; ++it) {
iterator end = trx->lock.table_locks.rend();
for (iterator it = trx->lock.table_locks.rbegin(); it != end; ++it) {
const lock_t* lock = *it; const lock_t* lock = *it;
...@@ -6337,6 +6314,9 @@ lock_trx_release_locks( ...@@ -6337,6 +6314,9 @@ lock_trx_release_locks(
/*--------------------------------------*/ /*--------------------------------------*/
trx_mutex_enter(trx); trx_mutex_enter(trx);
trx->state = TRX_STATE_COMMITTED_IN_MEMORY; trx->state = TRX_STATE_COMMITTED_IN_MEMORY;
/* Ensure that rw_trx_hash_t::find() will no longer find
this transaction. */
trx->id = 0;
trx_mutex_exit(trx); trx_mutex_exit(trx);
/*--------------------------------------*/ /*--------------------------------------*/
...@@ -6547,10 +6527,8 @@ lock_trx_has_sys_table_locks( ...@@ -6547,10 +6527,8 @@ lock_trx_has_sys_table_locks(
lock_mutex_enter(); lock_mutex_enter();
typedef lock_pool_t::const_reverse_iterator iterator; const lock_list::const_iterator end = trx->lock.table_locks.end();
lock_list::const_iterator it = trx->lock.table_locks.begin();
iterator end = trx->lock.table_locks.rend();
iterator it = trx->lock.table_locks.rbegin();
/* Find a valid mode. Note: ib_vector_size() can be 0. */ /* Find a valid mode. Note: ib_vector_size() can be 0. */
...@@ -7102,33 +7080,6 @@ DeadlockChecker::check_and_resolve(const lock_t* lock, trx_t* trx) ...@@ -7102,33 +7080,6 @@ DeadlockChecker::check_and_resolve(const lock_t* lock, trx_t* trx)
return(victim_trx); return(victim_trx);
} }
/**
Allocate cached locks for the transaction.
@param trx allocate cached record locks for this transaction */
void
lock_trx_alloc_locks(trx_t* trx)
{
ulint sz = REC_LOCK_SIZE * REC_LOCK_CACHE;
byte* ptr = reinterpret_cast<byte*>(ut_malloc_nokey(sz));
/* We allocate one big chunk and then distribute it among
the rest of the elements. The allocated chunk pointer is always
at index 0. */
for (ulint i = 0; i < REC_LOCK_CACHE; ++i, ptr += REC_LOCK_SIZE) {
trx->lock.rec_pool.push_back(
reinterpret_cast<ib_lock_t*>(ptr));
}
sz = TABLE_LOCK_SIZE * TABLE_LOCK_CACHE;
ptr = reinterpret_cast<byte*>(ut_malloc_nokey(sz));
for (ulint i = 0; i < TABLE_LOCK_CACHE; ++i, ptr += TABLE_LOCK_SIZE) {
trx->lock.table_pool.push_back(
reinterpret_cast<ib_lock_t*>(ptr));
}
}
/*************************************************************//** /*************************************************************//**
Updates the lock table when a page is split and merged to Updates the lock table when a page is split and merged to
two pages. */ two pages. */
......
...@@ -169,9 +169,21 @@ typedef std::map< ...@@ -169,9 +169,21 @@ typedef std::map<
static recv_spaces_t recv_spaces; static recv_spaces_t recv_spaces;
/** Backup function checks whether the space id belongs to /** Report optimized DDL operation (without redo log), corresponding to MLOG_INDEX_LOAD.
the skip table list given in the mariabackup option. */ @param[in] space_id tablespace identifier
bool(*check_if_backup_includes)(ulint space_id); */
void (*log_optimized_ddl_op)(ulint space_id);
/** Report an operation to create, delete, or rename a file during backup.
@param[in] space_id tablespace identifier
@param[in] flags tablespace flags (NULL if not create)
@param[in] name file name (not NUL-terminated)
@param[in] len length of name, in bytes
@param[in] new_name new file name (NULL if not rename)
@param[in] new_len length of new_name, in bytes (0 if NULL) */
void (*log_file_op)(ulint space_id, const byte* flags,
const byte* name, ulint len,
const byte* new_name, ulint new_len);
/** Process a file name from a MLOG_FILE_* record. /** Process a file name from a MLOG_FILE_* record.
@param[in,out] name file name @param[in,out] name file name
...@@ -381,9 +393,13 @@ fil_name_parse( ...@@ -381,9 +393,13 @@ fil_name_parse(
fil_name_process( fil_name_process(
reinterpret_cast<char*>(ptr), len, space_id, true); reinterpret_cast<char*>(ptr), len, space_id, true);
/* fall through */
break;
case MLOG_FILE_CREATE2: case MLOG_FILE_CREATE2:
if (log_file_op) {
log_file_op(space_id,
type == MLOG_FILE_CREATE2 ? ptr - 4 : NULL,
ptr, len, NULL, 0);
}
break; break;
case MLOG_FILE_RENAME2: case MLOG_FILE_RENAME2:
if (corrupt) { if (corrupt) {
...@@ -424,6 +440,11 @@ fil_name_parse( ...@@ -424,6 +440,11 @@ fil_name_parse(
reinterpret_cast<char*>(new_name), new_len, reinterpret_cast<char*>(new_name), new_len,
space_id, false); space_id, false);
if (log_file_op) {
log_file_op(space_id, NULL,
ptr, len, new_name, new_len);
}
if (!apply) { if (!apply) {
break; break;
} }
...@@ -716,6 +737,15 @@ bool log_t::files::read_log_seg(lsn_t* start_lsn, lsn_t end_lsn) ...@@ -716,6 +737,15 @@ bool log_t::files::read_log_seg(lsn_t* start_lsn, lsn_t end_lsn)
OS_FILE_LOG_BLOCK_SIZE, true); OS_FILE_LOG_BLOCK_SIZE, true);
} }
} }
ulint dl = log_block_get_data_len(buf);
if (dl < LOG_BLOCK_HDR_SIZE
|| (dl > OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE
&& dl != OS_FILE_LOG_BLOCK_SIZE)) {
recv_sys->found_corrupt_log = true;
end_lsn = *start_lsn;
break;
}
} }
if (recv_sys->report(ut_time())) { if (recv_sys->report(ut_time())) {
...@@ -2125,7 +2155,8 @@ recv_parse_log_rec( ...@@ -2125,7 +2155,8 @@ recv_parse_log_rec(
case MLOG_MULTI_REC_END | MLOG_SINGLE_REC_FLAG: case MLOG_MULTI_REC_END | MLOG_SINGLE_REC_FLAG:
case MLOG_DUMMY_RECORD | MLOG_SINGLE_REC_FLAG: case MLOG_DUMMY_RECORD | MLOG_SINGLE_REC_FLAG:
case MLOG_CHECKPOINT | MLOG_SINGLE_REC_FLAG: case MLOG_CHECKPOINT | MLOG_SINGLE_REC_FLAG:
ib::error() << "Incorrect log record type:" << *ptr; ib::error() << "Incorrect log record type "
<< ib::hex(unsigned(*ptr));
recv_sys->found_corrupt_log = true; recv_sys->found_corrupt_log = true;
return(0); return(0);
} }
...@@ -2144,7 +2175,6 @@ recv_parse_log_rec( ...@@ -2144,7 +2175,6 @@ recv_parse_log_rec(
*type, new_ptr, end_ptr, *space, *page_no, apply, NULL, NULL); *type, new_ptr, end_ptr, *space, *page_no, apply, NULL, NULL);
if (UNIV_UNLIKELY(new_ptr == NULL)) { if (UNIV_UNLIKELY(new_ptr == NULL)) {
return(0); return(0);
} }
...@@ -2201,30 +2231,30 @@ recv_report_corrupt_log( ...@@ -2201,30 +2231,30 @@ recv_report_corrupt_log(
ib::error() << ib::error() <<
"############### CORRUPT LOG RECORD FOUND ##################"; "############### CORRUPT LOG RECORD FOUND ##################";
const ulint ptr_offset = ulint(ptr - recv_sys->buf);
ib::info() << "Log record type " << type << ", page " << space << ":" ib::info() << "Log record type " << type << ", page " << space << ":"
<< page_no << ". Log parsing proceeded successfully up to " << page_no << ". Log parsing proceeded successfully up to "
<< recv_sys->recovered_lsn << ". Previous log record type " << recv_sys->recovered_lsn << ". Previous log record type "
<< recv_previous_parsed_rec_type << ", is multi " << recv_previous_parsed_rec_type << ", is multi "
<< recv_previous_parsed_rec_is_multi << " Recv offset " << recv_previous_parsed_rec_is_multi << " Recv offset "
<< (ptr - recv_sys->buf) << ", prev " << ptr_offset << ", prev "
<< recv_previous_parsed_rec_offset; << recv_previous_parsed_rec_offset;
ut_ad(ptr <= recv_sys->buf + recv_sys->len); ut_ad(ptr <= recv_sys->buf + recv_sys->len);
const ulint limit = 100; const ulint limit = 100;
const ulint before const ulint prev_offset = std::min(recv_previous_parsed_rec_offset,
= std::min(recv_previous_parsed_rec_offset, limit); ptr_offset);
const ulint after const ulint before = std::min(prev_offset, limit);
= std::min(recv_sys->len - ulint(ptr - recv_sys->buf), limit); const ulint after = std::min(recv_sys->len - ptr_offset, limit);
ib::info() << "Hex dump starting " << before << " bytes before and" ib::info() << "Hex dump starting " << before << " bytes before and"
" ending " << after << " bytes after the corrupted record:"; " ending " << after << " bytes after the corrupted record:";
ut_print_buf(stderr, const byte* start = recv_sys->buf + prev_offset - before;
recv_sys->buf
+ recv_previous_parsed_rec_offset - before, ut_print_buf(stderr, start, ulint(ptr - start) + after);
ulint(ptr - recv_sys->buf) + before + after
- recv_previous_parsed_rec_offset);
putc('\n', stderr); putc('\n', stderr);
if (!srv_force_recovery) { if (!srv_force_recovery) {
...@@ -2295,13 +2325,8 @@ bool recv_parse_log_recs(lsn_t checkpoint_lsn, store_t store, bool apply) ...@@ -2295,13 +2325,8 @@ bool recv_parse_log_recs(lsn_t checkpoint_lsn, store_t store, bool apply)
len = recv_parse_log_rec(&type, ptr, end_ptr, &space, len = recv_parse_log_rec(&type, ptr, end_ptr, &space,
&page_no, apply, &body); &page_no, apply, &body);
if (len == 0) {
return(false);
}
if (recv_sys->found_corrupt_log) { if (recv_sys->found_corrupt_log) {
recv_report_corrupt_log( recv_report_corrupt_log(ptr, type, space, page_no);
ptr, type, space, page_no);
return(true); return(true);
} }
...@@ -2309,6 +2334,10 @@ bool recv_parse_log_recs(lsn_t checkpoint_lsn, store_t store, bool apply) ...@@ -2309,6 +2334,10 @@ bool recv_parse_log_recs(lsn_t checkpoint_lsn, store_t store, bool apply)
return(true); return(true);
} }
if (len == 0) {
return(false);
}
new_recovered_lsn = recv_calc_lsn_on_data_add(old_lsn, len); new_recovered_lsn = recv_calc_lsn_on_data_add(old_lsn, len);
if (new_recovered_lsn > recv_sys->scanned_lsn) { if (new_recovered_lsn > recv_sys->scanned_lsn) {
...@@ -2396,11 +2425,8 @@ bool recv_parse_log_recs(lsn_t checkpoint_lsn, store_t store, bool apply) ...@@ -2396,11 +2425,8 @@ bool recv_parse_log_recs(lsn_t checkpoint_lsn, store_t store, bool apply)
/* fall through */ /* fall through */
case MLOG_INDEX_LOAD: case MLOG_INDEX_LOAD:
if (type == MLOG_INDEX_LOAD) { if (type == MLOG_INDEX_LOAD) {
if (check_if_backup_includes if (log_optimized_ddl_op) {
&& !check_if_backup_includes(space)) { log_optimized_ddl_op(space);
ut_ad(srv_operation
== SRV_OPERATION_BACKUP);
return true;
} }
} }
/* fall through */ /* fall through */
...@@ -2433,13 +2459,10 @@ bool recv_parse_log_recs(lsn_t checkpoint_lsn, store_t store, bool apply) ...@@ -2433,13 +2459,10 @@ bool recv_parse_log_recs(lsn_t checkpoint_lsn, store_t store, bool apply)
&type, ptr, end_ptr, &space, &page_no, &type, ptr, end_ptr, &space, &page_no,
false, &body); false, &body);
if (len == 0) {
return(false);
}
if (recv_sys->found_corrupt_log if (recv_sys->found_corrupt_log
|| type == MLOG_CHECKPOINT || type == MLOG_CHECKPOINT
|| (*ptr & MLOG_SINGLE_REC_FLAG)) { || (ptr != end_ptr
&& (*ptr & MLOG_SINGLE_REC_FLAG))) {
recv_sys->found_corrupt_log = true; recv_sys->found_corrupt_log = true;
recv_report_corrupt_log( recv_report_corrupt_log(
ptr, type, space, page_no); ptr, type, space, page_no);
...@@ -2450,6 +2473,10 @@ bool recv_parse_log_recs(lsn_t checkpoint_lsn, store_t store, bool apply) ...@@ -2450,6 +2473,10 @@ bool recv_parse_log_recs(lsn_t checkpoint_lsn, store_t store, bool apply)
return(true); return(true);
} }
if (len == 0) {
return(false);
}
recv_previous_parsed_rec_type = type; recv_previous_parsed_rec_type = type;
recv_previous_parsed_rec_offset recv_previous_parsed_rec_offset
= recv_sys->recovered_offset + total_len; = recv_sys->recovered_offset + total_len;
......
...@@ -98,7 +98,11 @@ mlog_parse_initial_log_record( ...@@ -98,7 +98,11 @@ mlog_parse_initial_log_record(
} }
*type = mlog_id_t(*ptr & ~MLOG_SINGLE_REC_FLAG); *type = mlog_id_t(*ptr & ~MLOG_SINGLE_REC_FLAG);
ut_ad(*type <= MLOG_BIGGEST_TYPE || EXTRA_CHECK_MLOG_NUMBER(*type)); if (UNIV_UNLIKELY(*type > MLOG_BIGGEST_TYPE
&& !EXTRA_CHECK_MLOG_NUMBER(*type))) {
recv_sys->found_corrupt_log = true;
return NULL;
}
ptr++; ptr++;
......
...@@ -2256,7 +2256,10 @@ page_cur_parse_delete_rec( ...@@ -2256,7 +2256,10 @@ page_cur_parse_delete_rec(
offset = mach_read_from_2(ptr); offset = mach_read_from_2(ptr);
ptr += 2; ptr += 2;
ut_a(offset <= srv_page_size); if (UNIV_UNLIKELY(offset >= srv_page_size)) {
recv_sys->found_corrupt_log = true;
return NULL;
}
if (block) { if (block) {
page_t* page = buf_block_get_frame(block); page_t* page = buf_block_get_frame(block);
......
...@@ -118,8 +118,6 @@ trx_init( ...@@ -118,8 +118,6 @@ trx_init(
/*=====*/ /*=====*/
trx_t* trx) trx_t* trx)
{ {
trx->id = 0;
trx->no = TRX_ID_MAX; trx->no = TRX_ID_MAX;
trx->state = TRX_STATE_NOT_STARTED; trx->state = TRX_STATE_NOT_STARTED;
...@@ -197,11 +195,7 @@ struct TrxFactory { ...@@ -197,11 +195,7 @@ struct TrxFactory {
the constructors of the trx_t members. */ the constructors of the trx_t members. */
new(&trx->mod_tables) trx_mod_tables_t(); new(&trx->mod_tables) trx_mod_tables_t();
new(&trx->lock.rec_pool) lock_pool_t(); new(&trx->lock.table_locks) lock_list();
new(&trx->lock.table_pool) lock_pool_t();
new(&trx->lock.table_locks) lock_pool_t();
new(&trx->read_view) ReadView(); new(&trx->read_view) ReadView();
...@@ -225,8 +219,6 @@ struct TrxFactory { ...@@ -225,8 +219,6 @@ struct TrxFactory {
&trx_named_savept_t::trx_savepoints); &trx_named_savept_t::trx_savepoints);
mutex_create(LATCH_ID_TRX, &trx->mutex); mutex_create(LATCH_ID_TRX, &trx->mutex);
lock_trx_alloc_locks(trx);
} }
/** Release resources held by the transaction object. /** Release resources held by the transaction object.
...@@ -256,27 +248,7 @@ struct TrxFactory { ...@@ -256,27 +248,7 @@ struct TrxFactory {
ut_ad(!trx->read_view.is_open()); ut_ad(!trx->read_view.is_open());
if (!trx->lock.rec_pool.empty()) { trx->lock.table_locks.~lock_list();
/* See lock_trx_alloc_locks() why we only free
the first element. */
ut_free(trx->lock.rec_pool[0]);
}
if (!trx->lock.table_pool.empty()) {
/* See lock_trx_alloc_locks() why we only free
the first element. */
ut_free(trx->lock.table_pool[0]);
}
trx->lock.rec_pool.~lock_pool_t();
trx->lock.table_pool.~lock_pool_t();
trx->lock.table_locks.~lock_pool_t();
trx->read_view.~ReadView(); trx->read_view.~ReadView();
} }
...@@ -412,7 +384,12 @@ trx_t *trx_create() ...@@ -412,7 +384,12 @@ trx_t *trx_create()
/* Should have been either just initialized or .clear()ed by /* Should have been either just initialized or .clear()ed by
trx_free(). */ trx_free(). */
ut_a(trx->mod_tables.size() == 0); ut_ad(trx->mod_tables.empty());
ut_ad(trx->lock.table_locks.empty());
ut_ad(UT_LIST_GET_LEN(trx->lock.trx_locks) == 0);
ut_ad(trx->lock.n_rec_locks == 0);
ut_ad(trx->lock.table_cached == 0);
ut_ad(trx->lock.rec_cached == 0);
#ifdef WITH_WSREP #ifdef WITH_WSREP
trx->wsrep_event = NULL; trx->wsrep_event = NULL;
...@@ -993,8 +970,6 @@ trx_start_low( ...@@ -993,8 +970,6 @@ trx_start_low(
trx_sys.register_rw(trx); trx_sys.register_rw(trx);
} else { } else {
trx->id = 0;
if (!trx_is_autocommit_non_locking(trx)) { if (!trx_is_autocommit_non_locking(trx)) {
/* If this is a read-only transaction that is writing /* If this is a read-only transaction that is writing
...@@ -1250,9 +1225,6 @@ trx_update_mod_tables_timestamp( ...@@ -1250,9 +1225,6 @@ trx_update_mod_tables_timestamp(
/*============================*/ /*============================*/
trx_t* trx) /*!< in: transaction */ trx_t* trx) /*!< in: transaction */
{ {
ut_ad(trx->id != 0);
/* consider using trx->start_time if calling time() is too /* consider using trx->start_time if calling time() is too
expensive here */ expensive here */
time_t now = ut_time(); time_t now = ut_time();
...@@ -1325,7 +1297,10 @@ trx_commit_in_memory( ...@@ -1325,7 +1297,10 @@ trx_commit_in_memory(
trx_sys.deregister_rw(trx); trx_sys.deregister_rw(trx);
} }
/* trx->id will be cleared in lock_trx_release_locks(trx). */
ut_ad(trx->read_only || !trx->rsegs.m_redo.rseg || trx->id);
lock_trx_release_locks(trx); lock_trx_release_locks(trx);
ut_ad(trx->id == 0);
/* Remove the transaction from the list of active /* Remove the transaction from the list of active
transactions now that it no longer holds any user locks. */ transactions now that it no longer holds any user locks. */
......
...@@ -376,9 +376,31 @@ SET(SOURCES) ...@@ -376,9 +376,31 @@ SET(SOURCES)
FOREACH(s ${ROCKSDB_SOURCES}) FOREACH(s ${ROCKSDB_SOURCES})
list(APPEND SOURCES ${ROCKSDB_SOURCE_DIR}/${s}) list(APPEND SOURCES ${ROCKSDB_SOURCE_DIR}/${s})
ENDFOREACH() ENDFOREACH()
IF(MSVC)
if(MSVC)
add_definitions(-DHAVE_SSE42 -DHAVE_PCLMUL) add_definitions(-DHAVE_SSE42 -DHAVE_PCLMUL)
ENDIF() else()
set(CMAKE_REQUIRED_FLAGS "-msse4.2 -mpclmul ${CXX11_FLAGS}")
CHECK_CXX_SOURCE_COMPILES("
#include <cstdint>
#include <nmmintrin.h>
#include <wmmintrin.h>
int main() {
volatile uint32_t x = _mm_crc32_u32(0, 0);
const auto a = _mm_set_epi64x(0, 0);
const auto b = _mm_set_epi64x(0, 0);
const auto c = _mm_clmulepi64_si128(a, b, 0x00);
auto d = _mm_cvtsi128_si64(c);
}
" HAVE_SSE42)
if(HAVE_SSE42)
set_source_files_properties(${ROCKSDB_SOURCE_DIR}/util/crc32c.cc
PROPERTIES COMPILE_FLAGS "-DHAVE_SSE42 -DHAVE_PCLMUL -msse4.2 -mpclmul")
endif()
unset(CMAKE_REQUIRED_FLAGS)
endif()
IF(CMAKE_VERSION VERSION_GREATER "2.8.10") IF(CMAKE_VERSION VERSION_GREATER "2.8.10")
STRING(TIMESTAMP GIT_DATE_TIME "%Y-%m-%d %H:%M:%S") STRING(TIMESTAMP GIT_DATE_TIME "%Y-%m-%d %H:%M:%S")
ENDIF() ENDIF()
......
--binlog-format=row --binlog-format=row --rocksdb-flush-log-at-trx-commit=1
...@@ -60,6 +60,13 @@ SELECT * FROM t1; ...@@ -60,6 +60,13 @@ SELECT * FROM t1;
--source include/wait_until_count_sessions.inc --source include/wait_until_count_sessions.inc
# Note: in MariaDB, session count will be decremented *before*
# myrocks::rocksdb_close_connection is called. This causes a race condition:
# we may grep the error log before bulk load is finalized.
# To prevent that, do a soft restart of the server (I wasnt able to find
# any other reliable way)
--source include/restart_mysqld_with_option.inc
--let SEARCH_FILE=$LOG2 --let SEARCH_FILE=$LOG2
--let SEARCH_PATTERN=RocksDB: Error [0-9]+ finalizing last SST file while disconnecting --let SEARCH_PATTERN=RocksDB: Error [0-9]+ finalizing last SST file while disconnecting
--source include/search_pattern_in_file.inc --source include/search_pattern_in_file.inc
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment