From cd26cdcd974725031e30393ff165fb0dfb365c4d Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Mon, 29 Apr 2019 00:11:48 +0400 Subject: [PATCH 01/13] MDEV-19141 server_audit_excl_users accepts only values with less than 1024 chars. Since this limit is imposed by the SHOW_VAR_FUNC_BUFF_SIZE, we just launch the error message. --- .../suite/plugins/r/server_audit.result | 14 +++++ mysql-test/suite/plugins/t/server_audit.test | 8 +++ plugin/server_audit/server_audit.c | 62 +++++++++++++++++-- 3 files changed, 78 insertions(+), 6 deletions(-) diff --git a/mysql-test/suite/plugins/r/server_audit.result b/mysql-test/suite/plugins/r/server_audit.result index 01392760317..0709444e5f6 100644 --- a/mysql-test/suite/plugins/r/server_audit.result +++ b/mysql-test/suite/plugins/r/server_audit.result @@ -21,6 +21,16 @@ set global server_audit_incl_users=null; set global server_audit_file_path='server_audit.log'; set global server_audit_output_type=file; set global server_audit_logging=on; +set global server_audit_incl_users= repeat("'root',", 10000); +ERROR 42000: Variable 'server_audit_incl_users' can't be set to the value of ''root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','roo' +show variables like 'server_audit_incl_users'; +Variable_name Value +server_audit_incl_users +set global server_audit_excl_users= repeat("'root',", 10000); +ERROR 42000: Variable 'server_audit_excl_users' can't be set to the value of ''root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','root','roo' +show variables like 'server_audit_excl_users'; +Variable_name Value +server_audit_excl_users connect con1,localhost,root,,mysql; connection default; disconnect con1; @@ -251,6 +261,10 @@ uninstall plugin server_audit; Warnings: Warning 1620 Plugin is busy and will be uninstalled on shutdown TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_logging=on',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_incl_users= repeat("\'root\',", 10000)',ID +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'show variables like \'server_audit_incl_users\'',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'set global server_audit_excl_users= repeat("\'root\',", 10000)',ID +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,test,'show variables like \'server_audit_excl_users\'',0 TIME,HOSTNAME,root,localhost,ID,0,CONNECT,mysql,,0 TIME,HOSTNAME,root,localhost,ID,0,DISCONNECT,mysql,,0 TIME,HOSTNAME,no_such_user,localhost,ID,0,FAILED_CONNECT,,,ID diff --git a/mysql-test/suite/plugins/t/server_audit.test b/mysql-test/suite/plugins/t/server_audit.test index 4af1ed883e3..fa23dc5caa3 100644 --- a/mysql-test/suite/plugins/t/server_audit.test +++ b/mysql-test/suite/plugins/t/server_audit.test @@ -13,6 +13,14 @@ set global server_audit_incl_users=null; set global server_audit_file_path='server_audit.log'; set global server_audit_output_type=file; set global server_audit_logging=on; + +--error ER_WRONG_VALUE_FOR_VAR +set global server_audit_incl_users= repeat("'root',", 10000); +show variables like 'server_audit_incl_users'; +--error ER_WRONG_VALUE_FOR_VAR +set global server_audit_excl_users= repeat("'root',", 10000); +show variables like 'server_audit_excl_users'; + --sleep 2 connect (con1,localhost,root,,mysql); connection default; diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index 0995327a86d..2f9cd99fbd8 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -335,6 +335,10 @@ static void update_file_rotations(MYSQL_THD thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save); static void update_incl_users(MYSQL_THD thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save); +static int check_incl_users(MYSQL_THD thd, struct st_mysql_sys_var *var, void *save, + struct st_mysql_value *value); +static int check_excl_users(MYSQL_THD thd, struct st_mysql_sys_var *var, void *save, + struct st_mysql_value *value); static void update_excl_users(MYSQL_THD thd, struct st_mysql_sys_var *var, void *var_ptr, const void *save); static void update_output_type(MYSQL_THD thd, struct st_mysql_sys_var *var, @@ -354,10 +358,10 @@ static void rotate_log(MYSQL_THD thd, struct st_mysql_sys_var *var, static MYSQL_SYSVAR_STR(incl_users, incl_users, PLUGIN_VAR_RQCMDARG, "Comma separated list of users to monitor.", - NULL, update_incl_users, NULL); + check_incl_users, update_incl_users, NULL); static MYSQL_SYSVAR_STR(excl_users, excl_users, PLUGIN_VAR_RQCMDARG, "Comma separated list of users to exclude from auditing.", - NULL, update_excl_users, NULL); + check_excl_users, update_excl_users, NULL); /* bits in the event filter. */ #define EVENT_CONNECT 1 #define EVENT_QUERY_ALL 2 @@ -2643,16 +2647,56 @@ static void update_file_rotate_size(MYSQL_THD thd __attribute__((unused)), } +static int check_users(void *save, struct st_mysql_value *value, + size_t s, const char *name) +{ + const char *users; + int len= 0; + + users= value->val_str(value, NULL, &len); + if ((size_t) len > s) + { + error_header(); + fprintf(stderr, + "server_audit_%s_users value can't be longer than %ld characters.\n", + name, s); + return 1; + } + *((const char**)save)= users; + return 0; +} + +static int check_incl_users(MYSQL_THD thd __attribute__((unused)), + struct st_mysql_sys_var *var __attribute__((unused)), + void *save, struct st_mysql_value *value) +{ + return check_users(save, value, sizeof(incl_user_buffer), "incl"); +} + +static int check_excl_users(MYSQL_THD thd __attribute__((unused)), + struct st_mysql_sys_var *var __attribute__((unused)), + void *save, struct st_mysql_value *value) +{ + return check_users(save, value, sizeof(excl_user_buffer), "excl"); +} + + static void update_incl_users(MYSQL_THD thd, struct st_mysql_sys_var *var __attribute__((unused)), void *var_ptr __attribute__((unused)), const void *save) { char *new_users= (*(char **) save) ? *(char **) save : empty_str; + size_t new_len= strlen(new_users) + 1; if (!maria_55_started || !debug_server_started) flogger_mutex_lock(&lock_operations); mark_always_logged(thd); - strncpy(incl_user_buffer, new_users, sizeof(incl_user_buffer)-1); - incl_user_buffer[sizeof(incl_user_buffer)-1]= 0; + + if (new_len > sizeof(incl_user_buffer)) + new_len= sizeof(incl_user_buffer); + + memcpy(incl_user_buffer, new_users, new_len - 1); + incl_user_buffer[new_len - 1]= 0; + incl_users= incl_user_buffer; user_coll_fill(&incl_user_coll, incl_users, &excl_user_coll, 1); error_header(); @@ -2667,11 +2711,17 @@ static void update_excl_users(MYSQL_THD thd __attribute__((unused)), void *var_ptr __attribute__((unused)), const void *save) { char *new_users= (*(char **) save) ? *(char **) save : empty_str; + size_t new_len= strlen(new_users) + 1; if (!maria_55_started || !debug_server_started) flogger_mutex_lock(&lock_operations); mark_always_logged(thd); - strncpy(excl_user_buffer, new_users, sizeof(excl_user_buffer)-1); - excl_user_buffer[sizeof(excl_user_buffer)-1]= 0; + + if (new_len > sizeof(excl_user_buffer)) + new_len= sizeof(excl_user_buffer); + + memcpy(excl_user_buffer, new_users, new_len - 1); + excl_user_buffer[new_len - 1]= 0; + excl_users= excl_user_buffer; user_coll_fill(&excl_user_coll, excl_users, &incl_user_coll, 0); error_header(); From a529188e05da8060c95eeb4c8caef05adbd6cc6a Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Mon, 29 Apr 2019 01:25:17 +0400 Subject: [PATCH 02/13] MDEV-17456 Malicious SUPER user can possibly change audit log configuration without leaving traces. The 'SET server_audit_logging ' statements should be logged no matter what. --- mysql-test/suite/plugins/r/server_audit.result | 3 +++ mysql-test/suite/plugins/t/server_audit.test | 3 +++ plugin/server_audit/server_audit.c | 7 ++++--- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/plugins/r/server_audit.result b/mysql-test/suite/plugins/r/server_audit.result index 0709444e5f6..b8d2986feea 100644 --- a/mysql-test/suite/plugins/r/server_audit.result +++ b/mysql-test/suite/plugins/r/server_audit.result @@ -212,6 +212,8 @@ select 2; 2 2 drop table t1; +set global server_audit_logging= off; +set global server_audit_logging= on; set global server_audit_events=''; set global server_audit_query_log_limit= 15; select (1), (2), (3), (4); @@ -378,6 +380,7 @@ TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'SET PASSWORD FOR u1=',ID TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'CREATE USER u3 IDENTIFIED BY *****',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'drop user u1, u2, u3',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'insert into t1 values (1), (2)',0 +TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'set global server_audit_logging= off',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'set global server_audit_events=\'\'',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'set global serv',0 TIME,HOSTNAME,root,localhost,ID,ID,QUERY,sa_db,'select (1), (2)',0 diff --git a/mysql-test/suite/plugins/t/server_audit.test b/mysql-test/suite/plugins/t/server_audit.test index fa23dc5caa3..f19c8f53b61 100644 --- a/mysql-test/suite/plugins/t/server_audit.test +++ b/mysql-test/suite/plugins/t/server_audit.test @@ -136,6 +136,9 @@ select * from t1; select 2; drop table t1; +set global server_audit_logging= off; +set global server_audit_logging= on; + set global server_audit_events=''; set global server_audit_query_log_limit= 15; diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index 2f9cd99fbd8..f03564e21d4 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -15,7 +15,7 @@ #define PLUGIN_VERSION 0x104 -#define PLUGIN_STR_VERSION "1.4.4" +#define PLUGIN_STR_VERSION "1.4.5" #define _my_thread_var loc_thread_var @@ -1623,7 +1623,7 @@ static int log_statement_ex(const struct connection_info *cn, } if (query && !(events & EVENT_QUERY_ALL) && - (events & EVENT_QUERY)) + (events & EVENT_QUERY && !cn->log_always)) { const char *orig_query= query; @@ -2556,9 +2556,10 @@ static void log_current_query(MYSQL_THD thd) if (!ci_needs_setup(cn) && cn->query_length && FILTER(EVENT_QUERY) && do_log_user(cn->user)) { + cn->log_always= 1; log_statement_ex(cn, cn->query_time, thd_get_thread_id(thd), cn->query, cn->query_length, 0, "QUERY"); - cn->log_always= 1; + cn->log_always= 0; } } From bb4f4b3a1b6c9287fcef6a6d290fd228e8a181f1 Mon Sep 17 00:00:00 2001 From: Alexey Botchkov Date: Mon, 29 Apr 2019 02:32:13 +0400 Subject: [PATCH 03/13] Make Win compiler happy. --- plugin/server_audit/server_audit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index f03564e21d4..e98d868bcba 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -2660,7 +2660,7 @@ static int check_users(void *save, struct st_mysql_value *value, error_header(); fprintf(stderr, "server_audit_%s_users value can't be longer than %ld characters.\n", - name, s); + name, (long int) s); return 1; } *((const char**)save)= users; From cc359eae3bd38a158690067f61f31de8524730c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 29 Apr 2019 09:24:59 +0300 Subject: [PATCH 04/13] Remove a type cast, and use correct format instead --- plugin/server_audit/server_audit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/server_audit/server_audit.c b/plugin/server_audit/server_audit.c index e98d868bcba..4e8c7ad670e 100644 --- a/plugin/server_audit/server_audit.c +++ b/plugin/server_audit/server_audit.c @@ -2659,8 +2659,8 @@ static int check_users(void *save, struct st_mysql_value *value, { error_header(); fprintf(stderr, - "server_audit_%s_users value can't be longer than %ld characters.\n", - name, (long int) s); + "server_audit_%s_users value can't be longer than %zu characters.\n", + name, s); return 1; } *((const char**)save)= users; From e10b3fa97a47cad0e6dbcc1d1691bad655087f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 29 Apr 2019 10:04:54 +0300 Subject: [PATCH 05/13] MDEV-19231: Correct an assertion BtrBulk::finish(): On error, the B-tree can be corrupted. Only upon successful completion, it makes sense to validate the index. --- storage/innobase/btr/btr0bulk.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/innobase/btr/btr0bulk.cc b/storage/innobase/btr/btr0bulk.cc index 2655ce2a2ce..1296641d073 100644 --- a/storage/innobase/btr/btr0bulk.cc +++ b/storage/innobase/btr/btr0bulk.cc @@ -1050,7 +1050,7 @@ BtrBulk::finish(dberr_t err) ut_ad(!sync_check_iterate(dict_sync_check())); - ut_ad(err == DB_SUCCESS + ut_ad(err != DB_SUCCESS || btr_validate_index(m_index, NULL, false) == DB_SUCCESS); return(err); } From 1b577e4d8baf962a7eb0ff8c946697b1e3d8f412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 29 Apr 2019 11:43:22 +0300 Subject: [PATCH 06/13] MDEV-19356 Assertion 'space->free_limit == 0 || space->free_limit == free_limit' fil_node_t::read_page0(): Do not replace up-to-date metadata with a possibly old version of page 0 that is being reread from the file. A more up-to-date page 0 could still exists in the buffer pool, waiting to be written back to the file. --- storage/innobase/fil/fil0fil.cc | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index ccf1ac0ecef..d892898f555 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -579,13 +579,9 @@ bool fil_node_t::read_page0(bool first) return false; } - ut_ad(space->free_limit == 0 || space->free_limit == free_limit); - ut_ad(space->free_len == 0 || space->free_len == free_len); - space->size_in_header = size; - space->free_limit = free_limit; - space->free_len = free_len; - if (first) { + ut_ad(space->id != TRX_SYS_SPACE); + /* Truncate the size to a multiple of extent size. */ ulint mask = psize * FSP_EXTENT_SIZE - 1; @@ -598,8 +594,19 @@ bool fil_node_t::read_page0(bool first) this->size = ulint(size_bytes / psize); space->size += this->size; + } else if (space->id != TRX_SYS_SPACE || space->size_in_header) { + /* If this is not the first-time open, do nothing. + For the system tablespace, we always get invoked as + first=false, so we detect the true first-time-open based + on size_in_header and proceed to initiailze the data. */ + return true; } + ut_ad(space->free_limit == 0 || space->free_limit == free_limit); + ut_ad(space->free_len == 0 || space->free_len == free_len); + space->size_in_header = size; + space->free_limit = free_limit; + space->free_len = free_len; return true; } From e03ad4f71ae9f897b3ddd94269c7ae922ca0505c Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Wed, 24 Apr 2019 11:44:32 +0100 Subject: [PATCH 07/13] Fix a typo --- extra/mariabackup/xtrabackup.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc index 07d21029733..eae55ddb02e 100644 --- a/extra/mariabackup/xtrabackup.cc +++ b/extra/mariabackup/xtrabackup.cc @@ -4203,7 +4203,7 @@ reread_log_header: memset(&stat_info, 0, sizeof(MY_STAT)); dst_log_file = ds_open(ds_redo, "ib_logfile0", &stat_info); if (dst_log_file == NULL) { - msg("§rror: failed to open the target stream for " + msg("Error: failed to open the target stream for " "'ib_logfile0'."); goto fail; } From d6e431dfa8a5653b669615cd9c9d2d206ce0d053 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Mon, 29 Apr 2019 12:44:00 +0100 Subject: [PATCH 08/13] MDEV-18131 : Update C/C to fix IP address SAN verification in 10.2+ --- libmariadb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libmariadb b/libmariadb index 2c5aebb3bc7..b5087161176 160000 --- a/libmariadb +++ b/libmariadb @@ -1 +1 @@ -Subproject commit 2c5aebb3bc724c1663c481ba2fedde00ab494fa4 +Subproject commit b50871611764d282874ad095d6c021163d1fe354 From 61f370a3c9997d2c7067b8cf5d39c8ad67dde5aa Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Thu, 25 Apr 2019 20:24:10 +0300 Subject: [PATCH 09/13] MDEV-18429 Consistent non-locking reads do not appear in TRANSACTIONS section of SHOW ENGINE INNODB STATUS lock_print_info_all_transactions(): print transactions which are started but not in RW or RO lists. print_not_started(): Replaces PrintNotStarted. Collect the skipped transactions. --- storage/innobase/lock/lock0lock.cc | 51 ++++++++++++++++++------------ 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index 7f6171241ac..22ec2ee7f06 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -4973,28 +4973,26 @@ lock_print_info_summary( return(TRUE); } -/** Functor to print not-started transaction from the mysql_trx_list. */ +/** Prints not started transaction or puts it into set. +@param[in] trx transaction +@param[in,out] file fd where to print +@param[in,out] started put transaction here if is started */ +static void +print_not_started(const trx_t* trx, FILE* file, std::set& started) +{ + ut_ad(trx->in_mysql_trx_list); + ut_ad(mutex_own(&trx_sys->mutex)); -struct PrintNotStarted { + /* See state transitions and locking rules in trx0trx.h */ - PrintNotStarted(FILE* file) : m_file(file) { } + if (trx_state_eq(trx, TRX_STATE_NOT_STARTED)) { - void operator()(const trx_t* trx) - { - ut_ad(trx->in_mysql_trx_list); - ut_ad(mutex_own(&trx_sys->mutex)); - - /* See state transitions and locking rules in trx0trx.h */ - - if (trx_state_eq(trx, TRX_STATE_NOT_STARTED)) { - - fputs("---", m_file); - trx_print_latched(m_file, trx, 600); - } + fputs("---", file); + trx_print_latched(file, trx, 600); + } else { + started.insert(trx); } - - FILE* m_file; -}; +} /** Iterate over a transaction's locks. Keeping track of the iterator using an ordinal value. */ @@ -5287,8 +5285,11 @@ lock_print_info_all_transactions( transactions will be omitted here. The information will be available from INFORMATION_SCHEMA.INNODB_TRX. */ - PrintNotStarted print_not_started(file); - ut_list_map(trx_sys->mysql_trx_list, print_not_started); + std::set not_printed_transactions; + for (const trx_t* trx = UT_LIST_GET_FIRST(trx_sys->mysql_trx_list); + trx; trx = UT_LIST_GET_NEXT(mysql_trx_list, trx)) { + print_not_started(trx, file, not_printed_transactions); + } const trx_t* trx; TrxListIterator trx_iter; @@ -5302,6 +5303,8 @@ lock_print_info_all_transactions( check_trx_state(trx); + not_printed_transactions.erase(trx); + if (trx != prev_trx) { lock_trx_print_wait_and_mvcc_state(file, trx); prev_trx = trx; @@ -5342,6 +5345,14 @@ lock_print_info_all_transactions( trx_iter.next(); } + for (std::set::const_iterator it + = not_printed_transactions.begin(), + end = not_printed_transactions.end(); + it != end; ++it) { + fputs("---", file); + trx_print_latched(file, *it, 600); + } + lock_mutex_exit(); mutex_exit(&trx_sys->mutex); From bdd6e33f00bff927358a7b03798a285ff41b35b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 29 Apr 2019 14:05:44 +0300 Subject: [PATCH 10/13] MDEV-13626: Add a test case --- .../suite/innodb/r/page_reorganize.result | 27 +++++++++ .../suite/innodb/t/page_reorganize.test | 56 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 mysql-test/suite/innodb/r/page_reorganize.result create mode 100644 mysql-test/suite/innodb/t/page_reorganize.test diff --git a/mysql-test/suite/innodb/r/page_reorganize.result b/mysql-test/suite/innodb/r/page_reorganize.result new file mode 100644 index 00000000000..1059fc78531 --- /dev/null +++ b/mysql-test/suite/innodb/r/page_reorganize.result @@ -0,0 +1,27 @@ +# +# Bug# 20005279 ASSERT !OTHER_LOCK, LOCK_MOVE_REORGANIZE_PAGE() +# +create table t1 (f1 int auto_increment primary key, +f2 char(255)) engine=innodb; +start transaction; +commit; +start transaction; +select f1, f2 from t1 where f1 = 20 for update; +f1 f2 +20 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +connect con1,localhost,root,,; +select f1 from t1 where f1 = 20 for update; +connection default; +SET @save_dbug = @@debug_dbug; +SET DEBUG_DBUG = '+d,do_page_reorganize,do_lock_reverse_page_reorganize'; +insert into t1(f2) values (repeat('+', 100)); +SET DEBUG = @save_dbug; +Warnings: +Warning 1287 '@@debug' is deprecated and will be removed in a future release. Please use '@@debug_dbug' instead +commit; +connection con1; +f1 +20 +disconnect con1; +connection default; +drop table t1; diff --git a/mysql-test/suite/innodb/t/page_reorganize.test b/mysql-test/suite/innodb/t/page_reorganize.test new file mode 100644 index 00000000000..7408353976d --- /dev/null +++ b/mysql-test/suite/innodb/t/page_reorganize.test @@ -0,0 +1,56 @@ +--source include/have_innodb.inc +--source include/have_innodb_16k.inc +--source include/have_debug.inc + +--source include/count_sessions.inc + +--echo # +--echo # Bug# 20005279 ASSERT !OTHER_LOCK, LOCK_MOVE_REORGANIZE_PAGE() +--echo # + +create table t1 (f1 int auto_increment primary key, + f2 char(255)) engine=innodb; + +let $inc = 50; + +start transaction; +--disable_query_log + +while ($inc) +{ + insert into t1(f2) values (repeat('~', 50)); + dec $inc; +} + +--enable_query_log +commit; + +start transaction; +select f1, f2 from t1 where f1 = 20 for update; + +connect (con1,localhost,root,,); +--send +select f1 from t1 where f1 = 20 for update; + +connection default; + +let $wait_condition= + select count(*) = 1 from information_schema.processlist + where INFO = 'select f1 from t1 where f1 = 20 for update'; + +--source include/wait_condition.inc + +SET @save_dbug = @@debug_dbug; +SET DEBUG_DBUG = '+d,do_page_reorganize,do_lock_reverse_page_reorganize'; +insert into t1(f2) values (repeat('+', 100)); +SET DEBUG = @save_dbug; +commit; + +connection con1; +reap; +disconnect con1; +connection default; + +drop table t1; + +--source include/wait_until_count_sessions.inc From 5fb4c0a8a877077e2835a9b39ac6e670fa4eaaac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 29 Apr 2019 14:33:46 +0300 Subject: [PATCH 11/13] Clean up ut_list ut_list_validate(), ut_list_map(): Add variants with const Functor& so that these functions can be called with an rvalue. Remove wrapper macros, and add #ifdef UNIV_DEBUG around debug-only code. --- storage/innobase/buf/buf0buddy.cc | 11 ++- storage/innobase/buf/buf0flu.cc | 5 +- storage/innobase/fil/fil0fil.cc | 2 +- storage/innobase/include/buf0buf.h | 10 +-- storage/innobase/include/ut0lst.h | 134 +++++++++++++++++------------ storage/innobase/lock/lock0lock.cc | 4 +- 6 files changed, 92 insertions(+), 74 deletions(-) diff --git a/storage/innobase/buf/buf0buddy.cc b/storage/innobase/buf/buf0buddy.cc index 777188d045d..d4e47f136ac 100644 --- a/storage/innobase/buf/buf0buddy.cc +++ b/storage/innobase/buf/buf0buddy.cc @@ -175,13 +175,13 @@ buf_buddy_get( struct CheckZipFree { CheckZipFree(ulint i) : m_i(i) {} - void operator()(const buf_buddy_free_t* elem) const + void operator()(const buf_buddy_free_t* elem) const { - ut_a(buf_buddy_stamp_is_free(elem)); - ut_a(elem->stamp.size <= m_i); + ut_ad(buf_buddy_stamp_is_free(elem)); + ut_ad(elem->stamp.size <= m_i); } - ulint m_i; + const ulint m_i; }; /** Validate a buddy list. @@ -193,8 +193,7 @@ buf_buddy_list_validate( const buf_pool_t* buf_pool, ulint i) { - CheckZipFree check(i); - ut_list_validate(buf_pool->zip_free[i], check); + ut_list_validate(buf_pool->zip_free[i], CheckZipFree(i)); } /**********************************************************************//** diff --git a/storage/innobase/buf/buf0flu.cc b/storage/innobase/buf/buf0flu.cc index f6f566bb8c7..cffbdc1660c 100644 --- a/storage/innobase/buf/buf0flu.cc +++ b/storage/innobase/buf/buf0flu.cc @@ -3533,7 +3533,7 @@ buf_flush_request_force( /** Functor to validate the flush list. */ struct Check { - void operator()(const buf_page_t* elem) + void operator()(const buf_page_t* elem) const { ut_a(elem->in_flush_list); } @@ -3550,11 +3550,10 @@ buf_flush_validate_low( { buf_page_t* bpage; const ib_rbt_node_t* rnode = NULL; - Check check; ut_ad(buf_flush_list_mutex_own(buf_pool)); - ut_list_validate(buf_pool->flush_list, check); + ut_list_validate(buf_pool->flush_list, Check()); bpage = UT_LIST_GET_FIRST(buf_pool->flush_list); diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index d892898f555..2de94fe3e40 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -5327,7 +5327,7 @@ fil_validate(void) ut_a(fil_system->n_open == n_open); - UT_LIST_CHECK(fil_system->LRU); + ut_list_validate(fil_system->LRU); for (fil_node = UT_LIST_GET_FIRST(fil_system->LRU); fil_node != 0; diff --git a/storage/innobase/include/buf0buf.h b/storage/innobase/include/buf0buf.h index 36175b9404e..361fd623f05 100644 --- a/storage/innobase/include/buf0buf.h +++ b/storage/innobase/include/buf0buf.h @@ -2423,8 +2423,7 @@ struct CheckInLRUList { static void validate(const buf_pool_t* buf_pool) { - CheckInLRUList check; - ut_list_validate(buf_pool->LRU, check); + ut_list_validate(buf_pool->LRU, CheckInLRUList()); } }; @@ -2437,8 +2436,7 @@ struct CheckInFreeList { static void validate(const buf_pool_t* buf_pool) { - CheckInFreeList check; - ut_list_validate(buf_pool->free, check); + ut_list_validate(buf_pool->free, CheckInFreeList()); } }; @@ -2451,8 +2449,8 @@ struct CheckUnzipLRUAndLRUList { static void validate(const buf_pool_t* buf_pool) { - CheckUnzipLRUAndLRUList check; - ut_list_validate(buf_pool->unzip_LRU, check); + ut_list_validate(buf_pool->unzip_LRU, + CheckUnzipLRUAndLRUList()); } }; #endif /* UNIV_DEBUG || defined UNIV_BUF_DEBUG */ diff --git a/storage/innobase/include/ut0lst.h b/storage/innobase/include/ut0lst.h index 09733da20a0..986f73ea17c 100644 --- a/storage/innobase/include/ut0lst.h +++ b/storage/innobase/include/ut0lst.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2019, MariaDB Corporation. 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 @@ -426,24 +427,19 @@ Gets the last node in a two-way list. @return last node, or NULL if the list is empty */ #define UT_LIST_GET_LAST(BASE) (BASE).end -struct NullValidate { void operator()(const void* elem) { } }; +struct NullValidate { void operator()(const void*) const {} }; -/********************************************************************//** -Iterate over all the elements and call the functor for each element. +/** Iterate over all the elements and call the functor for each element. @param[in] list base node (not a pointer to it) @param[in,out] functor Functor that is called for each element in the list */ template -void -ut_list_map( - const List& list, - Functor& functor) +inline void ut_list_map(const List& list, Functor& functor) { - ulint count = 0; + ulint count = 0; UT_LIST_IS_INITIALISED(list); - for (typename List::elem_type* elem = list.start; - elem != 0; + for (typename List::elem_type* elem = list.start; elem; elem = (elem->*list.node).next, ++count) { functor(elem); @@ -452,32 +448,30 @@ ut_list_map( ut_a(count == list.count); } -template -void -ut_list_reverse(List& list) +/** Iterate over all the elements and call the functor for each element. +@param[in] list base node (not a pointer to it) +@param[in] functor Functor that is called for each element in the list */ +template +inline void ut_list_map(const List& list, const Functor& functor) { + ulint count = 0; + UT_LIST_IS_INITIALISED(list); - for (typename List::elem_type* elem = list.start; - elem != 0; - elem = (elem->*list.node).prev) { - (elem->*list.node).reverse(); + for (typename List::elem_type* elem = list.start; elem; + elem = (elem->*list.node).next, ++count) { + + functor(elem); } - list.reverse(); + ut_a(count == list.count); } -#define UT_LIST_REVERSE(LIST) ut_list_reverse(LIST) - -/********************************************************************//** -Checks the consistency of a two-way list. -@param[in] list base node (not a pointer to it) -@param[in,out] functor Functor that is called for each element in the list */ +/** Check the consistency of a doubly linked list. +@param[in] list base node (not a pointer to it) +@param[in,out] functor Functor that is called for each element in the list */ template -void -ut_list_validate( - const List& list, - Functor& functor) +void ut_list_validate(const List& list, Functor& functor) { ut_list_map(list, functor); @@ -493,12 +487,62 @@ ut_list_validate( ut_a(count == list.count); } -/** Check the consistency of a two-way list. -@param[in] LIST base node reference */ -#define UT_LIST_CHECK(LIST) do { \ - NullValidate nullV; \ - ut_list_validate(LIST, nullV); \ -} while (0) +/** Check the consistency of a doubly linked list. +@param[in] list base node (not a pointer to it) +@param[in] functor Functor that is called for each element in the list */ +template +inline void ut_list_validate(const List& list, const Functor& functor) +{ + ut_list_map(list, functor); + + /* Validate the list backwards. */ + ulint count = 0; + + for (typename List::elem_type* elem = list.end; + elem != 0; + elem = (elem->*list.node).prev) { + ++count; + } + + ut_a(count == list.count); +} + +template +inline void ut_list_validate(const List& list) +{ + ut_list_validate(list, NullValidate()); +} + +#ifdef UNIV_DEBUG +template +inline void ut_list_reverse(List& list) +{ + UT_LIST_IS_INITIALISED(list); + + for (typename List::elem_type* elem = list.start; + elem != 0; + elem = (elem->*list.node).prev) { + (elem->*list.node).reverse(); + } + + list.reverse(); +} + +/** Check if the given element exists in the list. +@param[in,out] list the list object +@param[in] elem the element of the list which will be checked */ +template +inline bool ut_list_exists(const List& list, typename List::elem_type* elem) +{ + for (typename List::elem_type* e1 = UT_LIST_GET_FIRST(list); e1; + e1 = (e1->*list.node).next) { + if (elem == e1) { + return true; + } + } + return false; +} +#endif /** Move the given element to the beginning of the list. @param[in,out] list the list object @@ -519,28 +563,6 @@ ut_list_move_to_front( } #ifdef UNIV_DEBUG -/** Check if the given element exists in the list. -@param[in,out] list the list object -@param[in] elem the element of the list which will be checked */ -template -bool -ut_list_exists( - List& list, - typename List::elem_type* elem) -{ - typename List::elem_type* e1; - - for (e1 = UT_LIST_GET_FIRST(list); e1 != NULL; - e1 = (e1->*list.node).next) { - if (elem == e1) { - return(true); - } - } - return(false); -} #endif -#define UT_LIST_MOVE_TO_FRONT(LIST, ELEM) \ - ut_list_move_to_front(LIST, ELEM) - #endif /* ut0lst.h */ diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index 22ec2ee7f06..67adee94d14 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -2738,7 +2738,7 @@ lock_move_granted_locks_to_front( if (!lock->is_waiting()) { lock_t* prev = UT_LIST_GET_PREV(trx_locks, lock); ut_a(prev); - UT_LIST_MOVE_TO_FRONT(lock_list, lock); + ut_list_move_to_front(lock_list, lock); lock = prev; } } @@ -2826,7 +2826,7 @@ lock_move_reorganize_page( lock_move_granted_locks_to_front(old_locks); DBUG_EXECUTE_IF("do_lock_reverse_page_reorganize", - UT_LIST_REVERSE(old_locks);); + ut_list_reverse(old_locks);); for (lock = UT_LIST_GET_FIRST(old_locks); lock; lock = UT_LIST_GET_NEXT(trx_locks, lock)) { From 092602ac9b650f921ec5380866d17d740f0eedb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 29 Apr 2019 15:05:25 +0300 Subject: [PATCH 12/13] MDEV-14130 InnoDB messages should not refer to the MySQL 5.7 manual In InnoDB error messages, replace the hyperlink URLs to point to the MariaDB knowledge base. --- extra/innochecksum.cc | 7 ++++--- .../suite/innodb_zip/r/innochecksum_2.result | 2 ++ storage/innobase/handler/ha_innodb.cc | 19 +++++++------------ storage/innobase/include/ha_prototypes.h | 3 +-- storage/innobase/include/univ.i | 4 ---- storage/innobase/log/log0recv.cc | 2 +- 6 files changed, 15 insertions(+), 22 deletions(-) diff --git a/extra/innochecksum.cc b/extra/innochecksum.cc index 52e909ebbeb..5c4ad084c3c 100644 --- a/extra/innochecksum.cc +++ b/extra/innochecksum.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. - Copyright (c) 2014, 2017, MariaDB Corporation. + Copyright (c) 2014, 2019, MariaDB Corporation. 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 @@ -1231,7 +1231,7 @@ static struct my_option innochecksum_options[] = { {"verbose", 'v', "Verbose (prints progress every 5 seconds).", &verbose, &verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #ifndef DBUG_OFF - {"debug", '#', "Output debug log. See " REFMAN "dbug-package.html", + {"debug", '#', "Output debug log. See https://mariadb.com/kb/en/library/creating-a-trace-file/", &dbug_setting, &dbug_setting, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, #endif /* !DBUG_OFF */ {"count", 'c', "Print the count of pages in the file and exits.", @@ -1298,7 +1298,8 @@ static void usage(void) "[-p ] [-i] [-v] [-a ] [-n] " "[-C ] [-w ] [-S] [-D ] " "[-l ] [-l] [-m ] \n", my_progname); - printf("See " REFMAN "innochecksum.html for usage hints.\n"); + printf("See https://mariadb.com/kb/en/library/innochecksum/" + " for usage hints.\n"); my_print_help(innochecksum_options); my_print_variables(innochecksum_options); } diff --git a/mysql-test/suite/innodb_zip/r/innochecksum_2.result b/mysql-test/suite/innodb_zip/r/innochecksum_2.result index 582bb42f0cb..bfd03c72f12 100644 --- a/mysql-test/suite/innodb_zip/r/innochecksum_2.result +++ b/mysql-test/suite/innodb_zip/r/innochecksum_2.result @@ -43,10 +43,12 @@ Copyright (c) YEAR, YEAR , Oracle, MariaDB Corporation Ab and others. InnoDB offline file checksum utility. Usage: innochecksum [-c] [-s ] [-e ] [-p ] [-i] [-v] [-a ] [-n] [-C ] [-w ] [-S] [-D ] [-l ] [-l] [-m ] +See https://mariadb.com/kb/en/library/innochecksum/ for usage hints. -?, --help Displays this help and exits. -I, --info Synonym for --help. -V, --version Displays version information and exits. -v, --verbose Verbose (prints progress every 5 seconds). + https://mariadb.com/kb/en/library/creating-a-trace-file/ -c, --count Print the count of pages in the file and exits. -s, --start-page=# Start on this page number (0 based). -e, --end-page=# End at this page number (0 based). diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 953a4d89037..64559b1dae2 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -6261,9 +6261,9 @@ no_such_table: << n_cols << " user" " defined columns in InnoDB, but " << n_fields << " columns in MariaDB. Please check" - " INFORMATION_SCHEMA.INNODB_SYS_COLUMNS and " REFMAN - "innodb-troubleshooting.html for how to resolve the" - " issue."; + " INFORMATION_SCHEMA.INNODB_SYS_COLUMNS and" + " https://mariadb.com/kb/en/innodb-data-dictionary-troubleshooting/" + " for how to resolve the issue."; /* Mark this table as corrupted, so the drop table or force recovery can still use it, but not others. */ @@ -22101,11 +22101,11 @@ ib_errf( /* Keep the first 16 characters as-is, since the url is sometimes used as an offset from this.*/ const char* TROUBLESHOOTING_MSG = - "Please refer to " REFMAN "innodb-troubleshooting.html" + "Please refer to https://mariadb.com/kb/en/innodb-troubleshooting/" " for how to resolve the issue."; const char* TROUBLESHOOT_DATADICT_MSG = - "Please refer to " REFMAN "innodb-troubleshooting-datadict.html" + "Please refer to https://mariadb.com/kb/en/innodb-data-dictionary-troubleshooting/" " for how to resolve the issue."; const char* BUG_REPORT_MSG = @@ -22116,9 +22116,6 @@ const char* FORCE_RECOVERY_MSG = "https://mariadb.com/kb/en/library/innodb-recovery-modes/" " for information about forcing recovery."; -const char* ERROR_CREATING_MSG = - "Please refer to " REFMAN "error-creating-innodb.html"; - const char* OPERATING_SYSTEM_ERROR_MSG = "Some operating system error numbers are described at" " https://mariadb.com/kb/en/library/operating-system-error-codes/"; @@ -22511,8 +22508,7 @@ ib_push_frm_error( " Have you mixed up " ".frm files from different " "installations? See " - REFMAN - "innodb-troubleshooting.html\n", + "https://mariadb.com/kb/en/innodb-troubleshooting/\n", ib_table->name.m_name); if (push_warning) { @@ -22555,8 +22551,7 @@ ib_push_frm_error( " Have you mixed up " ".frm files from different " "installations? See " - REFMAN - "innodb-troubleshooting.html\n", + "https://mariadb.com/kb/en/innodb-troubleshooting/\n", ib_table->name.m_name, n_keys, table->s->keys); diff --git a/storage/innobase/include/ha_prototypes.h b/storage/innobase/include/ha_prototypes.h index 742914e97f1..37700393da2 100644 --- a/storage/innobase/include/ha_prototypes.h +++ b/storage/innobase/include/ha_prototypes.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2006, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2018, MariaDB Corporation. +Copyright (c) 2017, 2019, MariaDB Corporation. 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 @@ -442,7 +442,6 @@ extern const char* TROUBLESHOOTING_MSG; extern const char* TROUBLESHOOT_DATADICT_MSG; extern const char* BUG_REPORT_MSG; extern const char* FORCE_RECOVERY_MSG; -extern const char* ERROR_CREATING_MSG; extern const char* OPERATING_SYSTEM_ERROR_MSG; extern const char* FOREIGN_KEY_CONSTRAINTS_MSG; extern const char* SET_TRANSACTION_MSG; diff --git a/storage/innobase/include/univ.i b/storage/innobase/include/univ.i index d21de888320..69cb096bbe2 100644 --- a/storage/innobase/include/univ.i +++ b/storage/innobase/include/univ.i @@ -57,10 +57,6 @@ component, i.e. we show M.N.P as M.N */ IB_TO_STR(INNODB_VERSION_MINOR) "." \ IB_TO_STR(INNODB_VERSION_BUGFIX) -#define REFMAN "http://dev.mysql.com/doc/refman/" \ - IB_TO_STR(INNODB_VERSION_MAJOR) "." \ - IB_TO_STR(INNODB_VERSION_MINOR) "/en/" - /** How far ahead should we tell the service manager the timeout (time in seconds) */ #define INNODB_EXTEND_TIMEOUT_INTERVAL 30 diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index e342492fcfa..251ee2435f5 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -1325,7 +1325,7 @@ recv_find_max_checkpoint(ulint* max_field) " The redo log was created" " with " << creator << ". Please follow the instructions at " - REFMAN "upgrading-downgrading.html"; + "https://mariadb.com/kb/en/library/upgrading/"; /* Do not issue a message about a possibility to cleanly shut down the newer server version and to remove the redo logs, because the From 810f014ca7a705381e110cb26649c528bc00f179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 29 Apr 2019 16:23:21 +0300 Subject: [PATCH 13/13] MDEV-18429: Simpler implementation This reverts commit 61f370a3c9997d2c7067b8cf5d39c8ad67dde5aa and implements a simpler fix that is straightforward to merge to 10.3. lock_print_info: Renamed from PrintNotStarted. Dump the entire contents of trx_sys->mysql_trx_list. lock_print_info_rw_recovered: Like lock_print_info, but dump only recovered transactions in trx_sys->rw_trx_list. lock_print_info_all_transactions(): Dump both trx_sys->mysql_trx_list and trx_sys->rw_trx_list. TrxLockIterator, TrxListIterator, lock_rec_fetch_page(): Remove. This is a partial backport of the 10.3 commit a447980ff3ba000968d89e0c0c16239addeaf438 which removed the race-condition-prone ability of the InnoDB monitor to read relevant pages into the buffer pool for some record locks. --- storage/innobase/lock/lock0lock.cc | 358 ++++------------------------- 1 file changed, 51 insertions(+), 307 deletions(-) diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index 67adee94d14..fa0dae753dc 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -4973,137 +4973,6 @@ lock_print_info_summary( return(TRUE); } -/** Prints not started transaction or puts it into set. -@param[in] trx transaction -@param[in,out] file fd where to print -@param[in,out] started put transaction here if is started */ -static void -print_not_started(const trx_t* trx, FILE* file, std::set& started) -{ - ut_ad(trx->in_mysql_trx_list); - ut_ad(mutex_own(&trx_sys->mutex)); - - /* See state transitions and locking rules in trx0trx.h */ - - if (trx_state_eq(trx, TRX_STATE_NOT_STARTED)) { - - fputs("---", file); - trx_print_latched(file, trx, 600); - } else { - started.insert(trx); - } -} - -/** Iterate over a transaction's locks. Keeping track of the -iterator using an ordinal value. */ - -class TrxLockIterator { -public: - TrxLockIterator() { rewind(); } - - /** Get the m_index(th) lock of a transaction. - @return current lock or 0 */ - const lock_t* current(const trx_t* trx) const - { - lock_t* lock; - ulint i = 0; - - for (lock = UT_LIST_GET_FIRST(trx->lock.trx_locks); - lock != NULL && i < m_index; - lock = UT_LIST_GET_NEXT(trx_locks, lock), ++i) { - - /* No op */ - } - - return(lock); - } - - /** Set the ordinal value to 0 */ - void rewind() - { - m_index = 0; - } - - /** Increment the ordinal value. - @retun the current index value */ - ulint next() - { - return(++m_index); - } - -private: - /** Current iterator position */ - ulint m_index; -}; - -/** This iterates over both the RW and RO trx_sys lists. We need to keep -track where the iterator was up to and we do that using an ordinal value. */ - -class TrxListIterator { -public: - TrxListIterator() : m_index() - { - /* We iterate over the RW trx list first. */ - - m_trx_list = &trx_sys->rw_trx_list; - } - - /** Get the current transaction whose ordinality is m_index. - @return current transaction or 0 */ - - const trx_t* current() - { - return(reposition()); - } - - /** Advance the transaction current ordinal value and reset the - transaction lock ordinal value */ - - void next() - { - ++m_index; - m_lock_iter.rewind(); - } - - TrxLockIterator& lock_iter() - { - return(m_lock_iter); - } - -private: - /** Reposition the "cursor" on the current transaction. If it - is the first time then the "cursor" will be positioned on the - first transaction. - - @return transaction instance or 0 */ - const trx_t* reposition() const - { - ulint i; - trx_t* trx; - - /* Make the transaction at the ordinal value of m_index - the current transaction. ie. reposition/restore */ - - for (i = 0, trx = UT_LIST_GET_FIRST(*m_trx_list); - trx != NULL && (i < m_index); - trx = UT_LIST_GET_NEXT(trx_list, trx), ++i) { - - check_trx_state(trx); - } - - return(trx); - } - - /** Ordinal value of the transaction in the current transaction list */ - ulint m_index; - - /** Current transaction list */ - trx_ut_list_t* m_trx_list; - - /** For iterating over a transaction's locks */ - TrxLockIterator m_lock_iter; -}; - /** Prints transaction lock wait and MVCC state. @param[in,out] file file where to print @param[in] trx transaction */ @@ -5140,118 +5009,29 @@ lock_trx_print_wait_and_mvcc_state( } /*********************************************************************//** -Prints info of locks for a transaction. This function will release the -lock mutex and the trx_sys_t::mutex if the page was read from disk. -@return true if page was read from the tablespace */ +Prints info of locks for a transaction. */ static -bool -lock_rec_fetch_page( -/*================*/ - const lock_t* lock) /*!< in: record lock */ -{ - ut_ad(lock_get_type_low(lock) == LOCK_REC); - - ulint space_id = lock->un_member.rec_lock.space; - fil_space_t* space; - bool found; - const page_size_t& page_size = fil_space_get_page_size(space_id, - &found); - ulint page_no = lock->un_member.rec_lock.page_no; - - /* Check if the .ibd file exists. */ - if (found) { - mtr_t mtr; - - lock_mutex_exit(); - - mutex_exit(&trx_sys->mutex); - - DEBUG_SYNC_C("innodb_monitor_before_lock_page_read"); - - /* Check if the space is exists or not. only - when the space is valid, try to get the page. */ - space = fil_space_acquire(space_id); - if (space) { - dberr_t err = DB_SUCCESS; - mtr_start(&mtr); - buf_page_get_gen( - page_id_t(space_id, page_no), page_size, - RW_NO_LATCH, NULL, - BUF_GET_POSSIBLY_FREED, - __FILE__, __LINE__, &mtr, &err); - mtr_commit(&mtr); - fil_space_release(space); - } - - lock_mutex_enter(); - - mutex_enter(&trx_sys->mutex); - - return(true); - } - - return(false); -} - -/*********************************************************************//** -Prints info of locks for a transaction. -@return true if all printed, false if latches were released. */ -static -bool +void lock_trx_print_locks( /*=================*/ FILE* file, /*!< in/out: File to write */ - const trx_t* trx, /*!< in: current transaction */ - TrxLockIterator&iter, /*!< in: transaction lock iterator */ - bool load_block) /*!< in: if true then read block - from disk */ + const trx_t* trx) /*!< in: current transaction */ { - const lock_t* lock; - + uint32_t i= 0; /* Iterate over the transaction's locks. */ - while ((lock = iter.current(trx)) != 0) { - + for (lock_t *lock = UT_LIST_GET_FIRST(trx->lock.trx_locks); + lock != NULL; + lock = UT_LIST_GET_NEXT(trx_locks, lock)) { if (lock_get_type_low(lock) == LOCK_REC) { - if (load_block) { - - /* Note: lock_rec_fetch_page() will - release both the lock mutex and the - trx_sys_t::mutex if it does a read - from disk. */ - - if (lock_rec_fetch_page(lock)) { - /* We need to resync the - current transaction. */ - return(false); - } - - /* It is a single table tablespace - and the .ibd file is missing: - just print the lock without - attempting to load the page in the - buffer pool. */ - - fprintf(file, - "RECORD LOCKS on non-existing" - " space %u\n", - lock->un_member.rec_lock.space); - } - - /* Print all the record locks on the page from - the record lock bitmap */ - lock_rec_print(file, lock); - - load_block = true; - } else { ut_ad(lock_get_type_low(lock) & LOCK_TABLE); lock_table_print(file, lock); } - if (iter.next() >= 10) { + if (++i == 10) { fprintf(file, "10 LOCKS PRINTED FOR THIS TRX:" @@ -5260,10 +5040,48 @@ lock_trx_print_locks( break; } } - - return(true); } +/** Functor to display all transactions (except recovered ones) */ +struct lock_print_info +{ + lock_print_info(FILE* file) : file(file) {} + + void operator()(const trx_t* trx) const + { + ut_ad(mutex_own(&trx_sys->mutex)); + ut_ad(trx->in_mysql_trx_list); + lock_trx_print_wait_and_mvcc_state(file, trx); + + if (trx->will_lock && srv_print_innodb_lock_monitor) + lock_trx_print_locks(file, trx); + } + + FILE* const file; +}; + +/** Functor to display recovered read-write transactions */ +struct lock_print_info_rw_recovered +{ + lock_print_info_rw_recovered(FILE* file) : file(file) {} + + void operator()(const trx_t* trx) const + { + ut_ad(mutex_own(&trx_sys->mutex)); + ut_ad(trx->in_rw_trx_list); + if (trx->mysql_thd) + return; + ut_ad(!trx->in_mysql_trx_list); + + lock_trx_print_wait_and_mvcc_state(file, trx); + + if (trx->will_lock && srv_print_innodb_lock_monitor) + lock_trx_print_locks(file, trx); + } + + FILE* const file; +}; + /*********************************************************************//** Prints info of locks for each transaction. This function assumes that the caller holds the lock mutex and more importantly it will release the lock @@ -5278,84 +5096,10 @@ lock_print_info_all_transactions( fprintf(file, "LIST OF TRANSACTIONS FOR EACH SESSION:\n"); mutex_enter(&trx_sys->mutex); - - /* First print info on non-active transactions */ - - /* NOTE: information of auto-commit non-locking read-only - transactions will be omitted here. The information will be - available from INFORMATION_SCHEMA.INNODB_TRX. */ - - std::set not_printed_transactions; - for (const trx_t* trx = UT_LIST_GET_FIRST(trx_sys->mysql_trx_list); - trx; trx = UT_LIST_GET_NEXT(mysql_trx_list, trx)) { - print_not_started(trx, file, not_printed_transactions); - } - - const trx_t* trx; - TrxListIterator trx_iter; - const trx_t* prev_trx = 0; - - /* Control whether a block should be fetched from the buffer pool. */ - bool load_block = true; - bool monitor = srv_print_innodb_lock_monitor; - - while ((trx = trx_iter.current()) != 0) { - - check_trx_state(trx); - - not_printed_transactions.erase(trx); - - if (trx != prev_trx) { - lock_trx_print_wait_and_mvcc_state(file, trx); - prev_trx = trx; - - /* The transaction that read in the page is no - longer the one that read the page in. We need to - force a page read. */ - load_block = true; - } - - /* If we need to print the locked record contents then we - need to fetch the containing block from the buffer pool. */ - if (monitor) { - - /* Print the locks owned by the current transaction. */ - TrxLockIterator& lock_iter = trx_iter.lock_iter(); - - if (!lock_trx_print_locks( - file, trx, lock_iter, load_block)) { - - /* Resync trx_iter, the trx_sys->mutex and - the lock mutex were released. A page was - successfully read in. We need to print its - contents on the next call to - lock_trx_print_locks(). On the next call to - lock_trx_print_locks() we should simply print - the contents of the page just read in.*/ - load_block = false; - - continue; - } - } - - load_block = true; - - /* All record lock details were printed without fetching - a page from disk, or we didn't need to print the detail. */ - trx_iter.next(); - } - - for (std::set::const_iterator it - = not_printed_transactions.begin(), - end = not_printed_transactions.end(); - it != end; ++it) { - fputs("---", file); - trx_print_latched(file, *it, 600); - } - - lock_mutex_exit(); + ut_list_map(trx_sys->mysql_trx_list, lock_print_info(file)); + ut_list_map(trx_sys->rw_trx_list, lock_print_info_rw_recovered(file)); mutex_exit(&trx_sys->mutex); - + lock_mutex_exit(); ut_ad(lock_validate()); }