From b1ab2ba5833ad466ea9c08b89d89bb51e1d445ed Mon Sep 17 00:00:00 2001 From: Varun Gupta Date: Thu, 7 Nov 2019 15:24:21 +0530 Subject: [PATCH 1/9] MDEV-20519: Query plan regression with optimizer_use_condition_selectivity > 1 The issue here is the wrong estimate of the cardinality of a partial join, the cardinality is too high because the function table_cond_selectivity() returns an absurd number 100 while selectivity cannot be greater than 1. When accessing table t by outer reference t1.a via index we do not perform any range analysis for t. Yet we see TABLE::quick_key_parts[key] and TABLE->quick_rows[key] contain a non-zero value though these should have been remained untouched and equal to 0. Thus real cause of the problem is that TABLE::init does not clean the arrays TABLE::quick_key_parts[] and TABLE::>quick_rows[]. It should have done it because the TABLE structure created for any instance of a table can be reused for many queries. --- mysql-test/r/selectivity.result | 56 ++++++++++++++++++++++++++ mysql-test/r/selectivity_innodb.result | 56 ++++++++++++++++++++++++++ mysql-test/t/selectivity.test | 32 +++++++++++++++ sql/sql_select.cc | 4 -- sql/table.cc | 21 ++++++++++ sql/table.h | 1 + 6 files changed, 166 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/selectivity.result b/mysql-test/r/selectivity.result index 5fe6986e9ff..dc7e9494946 100644 --- a/mysql-test/r/selectivity.result +++ b/mysql-test/r/selectivity.result @@ -1810,4 +1810,60 @@ b a a b 9 9 10 10 set optimizer_use_condition_selectivity= @@optimizer_use_condition_selectivity; drop table t1,t2,t3; +# +# MDEV-20519: Query plan regression with optimizer_use_condition_selectivity=4 +# +create table t1 (id int, a int, PRIMARY KEY(id), key(a)); +insert into t1 select seq,seq from seq_1_to_100; +create table t2 (id int, a int, b int, PRIMARY KEY(id), key(a), key(b)); +insert into t2 select seq,seq,seq from seq_1_to_100; +set optimizer_switch='exists_to_in=off'; +set optimizer_use_condition_selectivity=2; +SELECT * FROM t1 +WHERE +EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id +WHERE A.a=t1.a AND t2.b < 20); +id a +1 1 +2 2 +3 3 +4 4 +5 5 +6 6 +7 7 +8 8 +9 9 +10 10 +11 11 +12 12 +13 13 +14 14 +15 15 +16 16 +17 17 +18 18 +19 19 +explain SELECT * FROM t1 +WHERE +EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id +WHERE A.a=t1.a AND t2.b < 20); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 100 Using where +2 DEPENDENT SUBQUERY A ref PRIMARY,a a 5 test.t1.a 1 +2 DEPENDENT SUBQUERY t2 ref a,b a 5 test.A.id 1 Using where +EXPLAIN SELECT * FROM t1 A, t1 B WHERE A.a = B.a and A.id = 65; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE A const PRIMARY,a PRIMARY 4 const 1 +1 SIMPLE B ref a a 5 const 1 +explain SELECT * FROM t1 +WHERE +EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id +WHERE A.a=t1.a AND t2.b < 20); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 100 Using where +2 DEPENDENT SUBQUERY A ref PRIMARY,a a 5 test.t1.a 1 +2 DEPENDENT SUBQUERY t2 ref a,b a 5 test.A.id 1 Using where +set optimizer_switch= @save_optimizer_switch; +set optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; +drop table t1,t2; # End of 10.1 tests diff --git a/mysql-test/r/selectivity_innodb.result b/mysql-test/r/selectivity_innodb.result index 70ddd5bf1a9..f0cbf1662aa 100644 --- a/mysql-test/r/selectivity_innodb.result +++ b/mysql-test/r/selectivity_innodb.result @@ -1820,6 +1820,62 @@ b a a b 9 9 10 10 set optimizer_use_condition_selectivity= @@optimizer_use_condition_selectivity; drop table t1,t2,t3; +# +# MDEV-20519: Query plan regression with optimizer_use_condition_selectivity=4 +# +create table t1 (id int, a int, PRIMARY KEY(id), key(a)); +insert into t1 select seq,seq from seq_1_to_100; +create table t2 (id int, a int, b int, PRIMARY KEY(id), key(a), key(b)); +insert into t2 select seq,seq,seq from seq_1_to_100; +set optimizer_switch='exists_to_in=off'; +set optimizer_use_condition_selectivity=2; +SELECT * FROM t1 +WHERE +EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id +WHERE A.a=t1.a AND t2.b < 20); +id a +1 1 +2 2 +3 3 +4 4 +5 5 +6 6 +7 7 +8 8 +9 9 +10 10 +11 11 +12 12 +13 13 +14 14 +15 15 +16 16 +17 17 +18 18 +19 19 +explain SELECT * FROM t1 +WHERE +EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id +WHERE A.a=t1.a AND t2.b < 20); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 index NULL a 5 NULL 100 Using where; Using index +2 DEPENDENT SUBQUERY A ref PRIMARY,a a 5 test.t1.a 1 Using index +2 DEPENDENT SUBQUERY t2 ref a,b a 5 test.A.id 1 Using where +EXPLAIN SELECT * FROM t1 A, t1 B WHERE A.a = B.a and A.id = 65; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE A const PRIMARY,a PRIMARY 4 const 1 +1 SIMPLE B ref a a 5 const 1 Using index +explain SELECT * FROM t1 +WHERE +EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id +WHERE A.a=t1.a AND t2.b < 20); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 index NULL a 5 NULL 100 Using where; Using index +2 DEPENDENT SUBQUERY A ref PRIMARY,a a 5 test.t1.a 1 Using index +2 DEPENDENT SUBQUERY t2 ref a,b a 5 test.A.id 1 Using where +set optimizer_switch= @save_optimizer_switch; +set optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; +drop table t1,t2; # End of 10.1 tests set optimizer_switch=@save_optimizer_switch_for_selectivity_test; set @tmp_ust= @@use_stat_tables; diff --git a/mysql-test/t/selectivity.test b/mysql-test/t/selectivity.test index a93ad5efb07..7df326edb44 100644 --- a/mysql-test/t/selectivity.test +++ b/mysql-test/t/selectivity.test @@ -1234,5 +1234,37 @@ set optimizer_use_condition_selectivity= @@optimizer_use_condition_selectivity; drop table t1,t2,t3; + +--echo # +--echo # MDEV-20519: Query plan regression with optimizer_use_condition_selectivity=4 +--echo # + + + +create table t1 (id int, a int, PRIMARY KEY(id), key(a)); +insert into t1 select seq,seq from seq_1_to_100; + +create table t2 (id int, a int, b int, PRIMARY KEY(id), key(a), key(b)); +insert into t2 select seq,seq,seq from seq_1_to_100; + +set optimizer_switch='exists_to_in=off'; +set optimizer_use_condition_selectivity=2; + +let $query= SELECT * FROM t1 + WHERE + EXISTS (SELECT * FROM t1 A INNER JOIN t2 ON t2.a = A.id + WHERE A.a=t1.a AND t2.b < 20); + +eval $query; +eval explain $query; + +EXPLAIN SELECT * FROM t1 A, t1 B WHERE A.a = B.a and A.id = 65; + +eval explain $query; + +set optimizer_switch= @save_optimizer_switch; +set optimizer_use_condition_selectivity= @save_optimizer_use_condition_selectivity; +drop table t1,t2; + --echo # End of 10.1 tests diff --git a/sql/sql_select.cc b/sql/sql_select.cc index dfc9f729118..55d371bdbfd 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7654,7 +7654,6 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s, something went wrong. */ sel /= (double)table->quick_rows[key] / (double) table->stat_records(); - DBUG_ASSERT(0 < sel && sel <= 2.0); set_if_smaller(sel, 1.0); used_range_selectivity= true; } @@ -7703,7 +7702,6 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s, if (table->field[fldno]->cond_selectivity > 0) { sel /= table->field[fldno]->cond_selectivity; - DBUG_ASSERT(0 < sel && sel <= 2.0); set_if_smaller(sel, 1.0); } /* @@ -7761,7 +7759,6 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s, if (field->cond_selectivity > 0) { sel/= field->cond_selectivity; - DBUG_ASSERT(0 < sel && sel <= 2.0); set_if_smaller(sel, 1.0); } break; @@ -7773,7 +7770,6 @@ double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s, sel*= table_multi_eq_cond_selectivity(join, idx, s, rem_tables, keyparts, ref_keyuse_steps); - DBUG_ASSERT(0.0 < sel && sel <= 1.0); return sel; } diff --git a/sql/table.cc b/sql/table.cc index 94cd174ffd7..52e63ea7389 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -4162,6 +4162,8 @@ void TABLE::init(THD *thd, TABLE_LIST *tl) created= TRUE; cond_selectivity= 1.0; cond_selectivity_sampling_explain= NULL; + quick_condition_rows=0; + initialize_quick_structures(); #ifdef HAVE_REPLICATION /* used in RBR Triggers */ master_had_triggers= 0; @@ -7546,3 +7548,22 @@ bool fk_modifies_child(enum_fk_option opt) static bool can_write[]= { false, false, true, true, false, true }; return can_write[opt]; } + + +/* + @brief + Initialize all the quick structures that are used to stored the + estimates when the range optimizer is run. + @details + This is specifically needed when we read the TABLE structure from the + table cache. There can be some garbage data from previous queries + that need to be reset here. +*/ + +void TABLE::initialize_quick_structures() +{ + bzero(quick_rows, sizeof(quick_rows)); + bzero(quick_key_parts, sizeof(quick_key_parts)); + bzero(quick_costs, sizeof(quick_costs)); + bzero(quick_n_ranges, sizeof(quick_n_ranges)); +} diff --git a/sql/table.h b/sql/table.h index 98ec9f005ea..44803b5aacd 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1450,6 +1450,7 @@ public: } bool update_const_key_parts(COND *conds); + void initialize_quick_structures(); my_ptrdiff_t default_values_offset() const { return (my_ptrdiff_t) (s->default_values - record[0]); } From c4a844ca5143080426f92ef9de3f0a069a764d53 Mon Sep 17 00:00:00 2001 From: Hartmut Holzgraefe Date: Fri, 8 Nov 2019 08:03:49 +0100 Subject: [PATCH 2/9] MDEV-20981 wsrep_sst_mariabackup fails silently when mariabackup is not installed (#1406) Make sure failure to find mariabackup binary does not terminate the script silently, terminate with a clear error message instead --- scripts/wsrep_sst_mariabackup.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/wsrep_sst_mariabackup.sh b/scripts/wsrep_sst_mariabackup.sh index cb4cb3bd1d4..59d2e12817b 100644 --- a/scripts/wsrep_sst_mariabackup.sh +++ b/scripts/wsrep_sst_mariabackup.sh @@ -83,7 +83,14 @@ fi pcmd="pv $pvopts" declare -a RC +set +e INNOBACKUPEX_BIN=$(which mariabackup) +if test -z $INNOBACKUPEX_BIN +then + wsrep_log_error 'mariabackup binary not found in $PATH' + exit 42 +fi +set -e XBSTREAM_BIN=mbstream XBCRYPT_BIN=xbcrypt # Not available in MariaBackup From dfd2d3d861fa7fad6747489dbbd173b7f680f44d Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Fri, 8 Nov 2019 09:50:14 -0500 Subject: [PATCH 3/9] bump the VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 8a84b79a423..c4402b8a39e 100644 --- a/VERSION +++ b/VERSION @@ -1,3 +1,3 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=1 -MYSQL_VERSION_PATCH=43 +MYSQL_VERSION_PATCH=44 From 13db50fc03e7312e6c01b06c7e4af69f69ba5382 Mon Sep 17 00:00:00 2001 From: Andrei Elkin Date: Wed, 6 Nov 2019 17:05:58 +0200 Subject: [PATCH 4/9] MDEV-19376 Repl_semi_sync_master::commit_trx assertion failure: ... || !m_active_tranxs->is_tranx_end_pos(trx_wait_binlog_name, trx_wait_binlog_pos) The assert indicates that the current transaction got caught uncleaned from the semisync master's cache when it is signaled to proceed upon its ack receive. The reason of missed cleanup turns out to be a flaw in the gtid connect mode. A submitted by connecting slave value of its last received event's binlog file *name* was adopted into {{Repl_semi_sync_master::m_reply_file_name}} as a part of semisync initialization. Notice that the initialization still refines the position part of the submitted last received event's binlog coordinates. The master side binlog filename:pos refinement is specific to the gtid connect mode for purpose of computing the latest binlog file to resume slave feeding from. Effectively in the gtid connect mode the computed resumption filename:pos may appear smaller in which case a new post-connect time committing transaction may be logged with its filename:pos also less than the submitted coordinates and that triggers the assert. Fixed with making the semisync initialization to use the refined filename:pos. It is guaranteed to be less than any new generated transaction's binlog:pos. --- .../rpl/r/rpl_semi_sync_gtid_reconnect.result | 28 +++++++ .../rpl/t/rpl_semi_sync_gtid_reconnect.test | 75 +++++++++++++++++++ sql/sql_repl.cc | 6 +- 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 mysql-test/suite/rpl/r/rpl_semi_sync_gtid_reconnect.result create mode 100644 mysql-test/suite/rpl/t/rpl_semi_sync_gtid_reconnect.test diff --git a/mysql-test/suite/rpl/r/rpl_semi_sync_gtid_reconnect.result b/mysql-test/suite/rpl/r/rpl_semi_sync_gtid_reconnect.result new file mode 100644 index 00000000000..3c720b94fde --- /dev/null +++ b/mysql-test/suite/rpl/r/rpl_semi_sync_gtid_reconnect.result @@ -0,0 +1,28 @@ +include/master-slave.inc +[connection master] +RESET MASTER; +SET @@GLOBAL.rpl_semi_sync_master_enabled = 1; +include/stop_slave.inc +SET @@GLOBAL. rpl_semi_sync_slave_enabled = 1; +include/start_slave.inc +CREATE TABLE t1 (a INT); +INSERT INTO t1 SET a = 1; +include/save_master_gtid.inc +FLUSH LOGS; +INSERT INTO t1 SET a = 2; +include/stop_slave_sql.inc +INSERT INTO t1 SET a = 3; +include/sync_slave_io_with_master.inc +include/stop_slave_io.inc +RESET MASTER; +SET @@global.gtid_binlog_state = '0-1-2'; +CHANGE MASTER TO MASTER_USE_GTID = slave_pos; +SET @@global.gtid_slave_pos = '0-1-2'; +include/start_slave.inc +INSERT INTO t1 SET a = 4; +DROP TABLE t1; +SET @@GLOBAL. rpl_semi_sync_master_enabled = 0; +include/stop_slave.inc +SET @@GLOBAL. rpl_semi_sync_slave_enabled = 0; +include/start_slave.inc +include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_semi_sync_gtid_reconnect.test b/mysql-test/suite/rpl/t/rpl_semi_sync_gtid_reconnect.test new file mode 100644 index 00000000000..c524fcff3a4 --- /dev/null +++ b/mysql-test/suite/rpl/t/rpl_semi_sync_gtid_reconnect.test @@ -0,0 +1,75 @@ +source include/not_embedded.inc; +source include/have_binlog_format_mixed.inc; +source include/have_semisync.inc; +source include/master-slave.inc; + +# +# Semisync initialization +# +--connection master +RESET MASTER; +--let $sav_enabled_master=`SELECT @@GLOBAL.rpl_semi_sync_master_enabled` +SET @@GLOBAL.rpl_semi_sync_master_enabled = 1; + +--connection slave +source include/stop_slave.inc; +--let $sav_enabled_slave=`SELECT @@GLOBAL.rpl_semi_sync_slave_enabled` +SET @@GLOBAL. rpl_semi_sync_slave_enabled = 1; +source include/start_slave.inc; + +# Prove fixes to +# MDEV-19376 Assert (!m_active_tranxs->is_tranx_end_pos(trx_wait_binlog_name...) +# +# +# Run few queries to replicate/execute on slave. +# Stop the slave applier. +# Replicate/not-executed few more. +# Restart the slave. +# +--connection master +CREATE TABLE t1 (a INT); +INSERT INTO t1 SET a = 1; +--source include/save_master_gtid.inc +--let $resume_gtid = $master_pos +FLUSH LOGS; +INSERT INTO t1 SET a = 2; + +--sync_slave_with_master +--connection slave +--source include/stop_slave_sql.inc + +--connection master +INSERT INTO t1 SET a = 3; + +# the sync connection is 'slave' by default +--source include/sync_slave_io_with_master.inc +--connection slave +--source include/stop_slave_io.inc + +--connection master +RESET MASTER; +--eval SET @@global.gtid_binlog_state = '$resume_gtid' + +# The resume gtid is set up to point to the very first binlog file +--connection slave +CHANGE MASTER TO MASTER_USE_GTID = slave_pos; +--eval SET @@global.gtid_slave_pos = '$resume_gtid' +# Yet the slave io first submits the last received binlog file name:pos. +--source include/start_slave.inc + +# Here goes the cracker. +--connection master +INSERT INTO t1 SET a = 4; + +# +# Clean up +# +--connection master +DROP TABLE t1; +--eval SET @@GLOBAL. rpl_semi_sync_master_enabled = $sav_enabled_master + +--sync_slave_with_master +source include/stop_slave.inc; +--eval SET @@GLOBAL. rpl_semi_sync_slave_enabled = $sav_enabled_slave +source include/start_slave.inc; +--source include/rpl_end.inc diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index ef6d0706da0..48d2781cd46 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -2725,7 +2725,11 @@ void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, run hook first when all check has been made that slave seems to be requesting a reasonable position. i.e when transmit actually starts */ - if (RUN_HOOK(binlog_transmit, transmit_start, (thd, flags, log_ident, pos))) + + DBUG_ASSERT(pos == linfo.pos); + + if (RUN_HOOK(binlog_transmit, transmit_start, + (thd, flags, linfo.log_file_name, linfo.pos))) { info->errmsg= "Failed to run hook 'transmit_start'"; info->error= ER_UNKNOWN_ERROR; From 40e65e878eac4d88ec124610e5ede5132b3c4cf2 Mon Sep 17 00:00:00 2001 From: Andrei Elkin Date: Mon, 11 Nov 2019 21:12:14 +0200 Subject: [PATCH 5/9] rpl_semi_sync_gtid_reconnect results merge --- .../suite/rpl/r/rpl_semi_sync_gtid_reconnect.result | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mysql-test/suite/rpl/r/rpl_semi_sync_gtid_reconnect.result b/mysql-test/suite/rpl/r/rpl_semi_sync_gtid_reconnect.result index 3c720b94fde..4285228f162 100644 --- a/mysql-test/suite/rpl/r/rpl_semi_sync_gtid_reconnect.result +++ b/mysql-test/suite/rpl/r/rpl_semi_sync_gtid_reconnect.result @@ -1,27 +1,39 @@ include/master-slave.inc [connection master] +connection master; RESET MASTER; SET @@GLOBAL.rpl_semi_sync_master_enabled = 1; +connection slave; include/stop_slave.inc SET @@GLOBAL. rpl_semi_sync_slave_enabled = 1; include/start_slave.inc +connection master; CREATE TABLE t1 (a INT); INSERT INTO t1 SET a = 1; include/save_master_gtid.inc FLUSH LOGS; INSERT INTO t1 SET a = 2; +connection slave; +connection slave; include/stop_slave_sql.inc +connection master; INSERT INTO t1 SET a = 3; include/sync_slave_io_with_master.inc +connection slave; include/stop_slave_io.inc +connection master; RESET MASTER; SET @@global.gtid_binlog_state = '0-1-2'; +connection slave; CHANGE MASTER TO MASTER_USE_GTID = slave_pos; SET @@global.gtid_slave_pos = '0-1-2'; include/start_slave.inc +connection master; INSERT INTO t1 SET a = 4; +connection master; DROP TABLE t1; SET @@GLOBAL. rpl_semi_sync_master_enabled = 0; +connection slave; include/stop_slave.inc SET @@GLOBAL. rpl_semi_sync_slave_enabled = 0; include/start_slave.inc From e5f99a0c0ccbb6cbf4a816fed552af9c20cc32e9 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Wed, 30 Oct 2019 13:55:52 +0300 Subject: [PATCH 6/9] MDEV-20297 Support C++11 range-based for loop for List New iterator has the fastest possible implementation: just moves one pointer. It's faster that List_iterator and List_iterator_fast: both do more on increment. Overall patch brings: 1) work compile times 2) possibly(!) worse debug build performance 3) definitely better optimized build performance 4) ability to write less code 5) ability to write less bug-prone code --- sql/sql_list.h | 46 +++++++++++++++++++++++++ sql/sql_table.cc | 89 +++++++++++++++++------------------------------- 2 files changed, 78 insertions(+), 57 deletions(-) diff --git a/sql/sql_list.h b/sql/sql_list.h index 3585adbd714..35aaaf49c50 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -21,6 +21,7 @@ #endif #include "sql_alloc.h" +#include /** Simple intrusive linked list. @@ -532,6 +533,51 @@ public: List *res= new (mem_root) List; return res == NULL || res->push_back(first, mem_root) ? NULL : res; } + + class Iterator; + using value_type= T; + using iterator= Iterator; + using const_iterator= const Iterator; + + Iterator begin() const { return Iterator(first); } + Iterator end() const { return Iterator(); } + + class Iterator + { + public: + using iterator_category= std::forward_iterator_tag; + using value_type= T; + using difference_type= std::ptrdiff_t; + using pointer= T *; + using reference= T &; + + Iterator(list_node *p= &end_of_list) : node{p} {} + + Iterator &operator++() + { + DBUG_ASSERT(node != &end_of_list); + + node= node->next; + return *this; + } + + T operator++(int) + { + Iterator tmp(*this); + operator++(); + return tmp; + } + + T &operator*() { return *static_cast(node->info); } + + bool operator!=(const typename List::iterator &rhs) + { + return node != rhs.node; + } + + private: + list_node *node{&end_of_list}; + }; }; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 334834b2603..3430762fa49 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -54,7 +54,7 @@ #include "sql_audit.h" #include "sql_sequence.h" #include "tztime.h" - +#include #ifdef __WIN__ #include @@ -3022,34 +3022,37 @@ CHARSET_INFO* get_sql_field_charset(Column_definition *sql_field, void promote_first_timestamp_column(List *column_definitions) { - List_iterator_fast it(*column_definitions); - Create_field *column_definition; - - while ((column_definition= it++) != NULL) + for (Create_field &column_definition : *column_definitions) { - if (column_definition->is_timestamp_type() || // TIMESTAMP - column_definition->unireg_check == Field::TIMESTAMP_OLD_FIELD) // Legacy + if (column_definition.is_timestamp_type() || // TIMESTAMP + column_definition.unireg_check == Field::TIMESTAMP_OLD_FIELD) // Legacy { - DBUG_PRINT("info", ("field-ptr:%p", column_definition->field)); - if ((column_definition->flags & NOT_NULL_FLAG) != 0 && // NOT NULL, - column_definition->default_value == NULL && // no constant default, - column_definition->unireg_check == Field::NONE && // no function default - column_definition->vcol_info == NULL && - column_definition->period == NULL && - !(column_definition->flags & VERS_SYSTEM_FIELD)) // column isn't generated + DBUG_PRINT("info", ("field-ptr:%p", column_definition.field)); + if ((column_definition.flags & NOT_NULL_FLAG) != 0 && // NOT NULL, + column_definition.default_value == NULL && // no constant default, + column_definition.unireg_check == Field::NONE && // no function default + column_definition.vcol_info == NULL && + column_definition.period == NULL && + !(column_definition.flags & VERS_SYSTEM_FIELD)) // column isn't generated { DBUG_PRINT("info", ("First TIMESTAMP column '%s' was promoted to " "DEFAULT CURRENT_TIMESTAMP ON UPDATE " "CURRENT_TIMESTAMP", - column_definition->field_name.str + column_definition.field_name.str )); - column_definition->unireg_check= Field::TIMESTAMP_DNUN_FIELD; + column_definition.unireg_check= Field::TIMESTAMP_DNUN_FIELD; } return; } } } +static bool key_cmp(const Key_part_spec &a, const Key_part_spec &b) +{ + return a.length == b.length && + !lex_string_cmp(system_charset_info, &a.field_name, &b.field_name); +} + /** Check if there is a duplicate key. Report a warning for every duplicate key. @@ -3058,8 +3061,8 @@ void promote_first_timestamp_column(List *column_definitions) @param key_info Key meta-data info. @param key_list List of existing keys. */ -static void check_duplicate_key(THD *thd, Key *key, KEY *key_info, - List *key_list) +static void check_duplicate_key(THD *thd, const Key *key, const KEY *key_info, + const List *key_list) { /* We only check for duplicate indexes if it is requested and the @@ -3071,56 +3074,28 @@ static void check_duplicate_key(THD *thd, Key *key, KEY *key_info, if (!key->key_create_info.check_for_duplicate_indexes || key->generated) return; - List_iterator_fast key_list_iterator(*key_list); - List_iterator_fast key_column_iterator(key->columns); - Key *k; - - while ((k= key_list_iterator++)) + for (const Key &k : *key_list) { // Looking for a similar key... - if (k == key) + if (&k == key) break; - if (k->generated || - (key->type != k->type) || - (key->key_create_info.algorithm != k->key_create_info.algorithm) || - (key->columns.elements != k->columns.elements)) + if (k.generated || + (key->type != k.type) || + (key->key_create_info.algorithm != k.key_create_info.algorithm) || + (key->columns.elements != k.columns.elements)) { // Keys are different. continue; } - /* - Keys 'key' and 'k' might be identical. - Check that the keys have identical columns in the same order. - */ - - List_iterator_fast k_column_iterator(k->columns); - uint i; - key_column_iterator.rewind(); - - for (i= 0; i < key->columns.elements; ++i) + if (std::equal(key->columns.begin(), key->columns.end(), k.columns.begin(), + key_cmp)) { - Key_part_spec *c1= key_column_iterator++; - Key_part_spec *c2= k_column_iterator++; - - DBUG_ASSERT(c1 && c2); - - if (lex_string_cmp(system_charset_info, - &c1->field_name, &c2->field_name) || - (c1->length != c2->length)) - break; - } - - // Report a warning if we have two identical keys. - - if (i == key->columns.elements) - { - push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_DUP_INDEX, ER_THD(thd, ER_DUP_INDEX), - key_info->name.str); - break; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_DUP_INDEX, + ER_THD(thd, ER_DUP_INDEX), key_info->name.str); + return; } } } From 7df07c766651c37cda4daf8a6aa1fb1c38efc62c Mon Sep 17 00:00:00 2001 From: Sujatha Date: Tue, 5 Nov 2019 15:01:29 +0530 Subject: [PATCH 7/9] MDEV-20953: binlog_encryption.rpl_corruption failed in buildbot due to wrong error code Problem: ======== CURRENT_TEST: binlog_encryption.rpl_corruption mysqltest: In included file "./include/wait_for_slave_io_error.inc": ... At line 72: Slave stopped with wrong error code **** Slave stopped with wrong error code: 1743 (expected 1595,1913) **** Analysis: ======== The test emulates the corruption at the various stages of replication for example in binlog file, in network and in relay log etc. It verifies that all corruption cases are handled through appropriate error messages. The test cases which emulate network failure expect following errors. --ER_SLAVE_RELAY_LOG_WRITE_FAILURE (1595) --ER_NETWORK_READ_EVENT_CHECKSUM_FAILURE (1743) Ideally test should expect error codes as 1595 and 1743. But the test actually waits on incorrect error code 1595,1913 Fix: === Added appropriate error code for 'ER_NETWORK_READ_EVENT_CHECKSUM_FAILURE'. Replaced 1913 with 1743. --- mysql-test/extra/rpl_tests/rpl_corruption.inc | 8 ++++---- mysql-test/suite/binlog_encryption/rpl_corruption.result | 4 ++-- mysql-test/suite/rpl/r/rpl_corruption.result | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mysql-test/extra/rpl_tests/rpl_corruption.inc b/mysql-test/extra/rpl_tests/rpl_corruption.inc index 048a9d74249..1726ee4ba2f 100644 --- a/mysql-test/extra/rpl_tests/rpl_corruption.inc +++ b/mysql-test/extra/rpl_tests/rpl_corruption.inc @@ -122,11 +122,11 @@ SET GLOBAL master_verify_checksum=0; SET GLOBAL debug_dbug="+d,corrupt_read_log_event2_set"; --connection slave START SLAVE IO_THREAD; -# When the checksum error is detected, the slave sets error code 1913 +# When the checksum error is detected, the slave sets error code 1743 # (ER_NETWORK_READ_EVENT_CHECKSUM_FAILURE) in queue_event(), then immediately # sets error 1595 (ER_SLAVE_RELAY_LOG_WRITE_FAILURE) in handle_slave_io(). -# So we usually get 1595, but it is occasionally possible to get 1913. -let $slave_io_errno= 1595,1913; +# So we usually get 1595, but it is occasionally possible to get 1743. +let $slave_io_errno= 1595,1743; # ER_SLAVE_RELAY_LOG_WRITE_FAILURE, ER_NETWORK_READ_EVENT_CHECKSUM_FAILURE --source include/wait_for_slave_io_error.inc --connection master SET GLOBAL debug_dbug="-d,corrupt_read_log_event2_set"; @@ -138,7 +138,7 @@ SET GLOBAL master_verify_checksum=1; --connection slave SET GLOBAL debug_dbug="+d,corrupt_queue_event"; START SLAVE IO_THREAD; -let $slave_io_errno= 1595,1913; +let $slave_io_errno= 1595,1743; # ER_SLAVE_RELAY_LOG_WRITE_FAILURE, ER_NETWORK_READ_EVENT_CHECKSUM_FAILURE --source include/wait_for_slave_io_error.inc SET GLOBAL debug_dbug="-d,corrupt_queue_event"; diff --git a/mysql-test/suite/binlog_encryption/rpl_corruption.result b/mysql-test/suite/binlog_encryption/rpl_corruption.result index 51c2c6261b8..05db91bf058 100644 --- a/mysql-test/suite/binlog_encryption/rpl_corruption.result +++ b/mysql-test/suite/binlog_encryption/rpl_corruption.result @@ -25,14 +25,14 @@ SET GLOBAL debug_dbug="-d,corrupt_read_log_event2_set"; SET GLOBAL master_verify_checksum=0; SET GLOBAL debug_dbug="+d,corrupt_read_log_event2_set"; START SLAVE IO_THREAD; -include/wait_for_slave_io_error.inc [errno=1595,1913] +include/wait_for_slave_io_error.inc [errno=1595,1743] SET GLOBAL debug_dbug="-d,corrupt_read_log_event2_set"; SET GLOBAL debug_dbug= ""; SET GLOBAL master_verify_checksum=1; # 5. Slave. Corruption in network SET GLOBAL debug_dbug="+d,corrupt_queue_event"; START SLAVE IO_THREAD; -include/wait_for_slave_io_error.inc [errno=1595,1913] +include/wait_for_slave_io_error.inc [errno=1595,1743] SET GLOBAL debug_dbug="-d,corrupt_queue_event"; # 6. Slave. Corruption in relay log SET GLOBAL debug_dbug="+d,corrupt_read_log_event_char"; diff --git a/mysql-test/suite/rpl/r/rpl_corruption.result b/mysql-test/suite/rpl/r/rpl_corruption.result index 51c2c6261b8..05db91bf058 100644 --- a/mysql-test/suite/rpl/r/rpl_corruption.result +++ b/mysql-test/suite/rpl/r/rpl_corruption.result @@ -25,14 +25,14 @@ SET GLOBAL debug_dbug="-d,corrupt_read_log_event2_set"; SET GLOBAL master_verify_checksum=0; SET GLOBAL debug_dbug="+d,corrupt_read_log_event2_set"; START SLAVE IO_THREAD; -include/wait_for_slave_io_error.inc [errno=1595,1913] +include/wait_for_slave_io_error.inc [errno=1595,1743] SET GLOBAL debug_dbug="-d,corrupt_read_log_event2_set"; SET GLOBAL debug_dbug= ""; SET GLOBAL master_verify_checksum=1; # 5. Slave. Corruption in network SET GLOBAL debug_dbug="+d,corrupt_queue_event"; START SLAVE IO_THREAD; -include/wait_for_slave_io_error.inc [errno=1595,1913] +include/wait_for_slave_io_error.inc [errno=1595,1743] SET GLOBAL debug_dbug="-d,corrupt_queue_event"; # 6. Slave. Corruption in relay log SET GLOBAL debug_dbug="+d,corrupt_read_log_event_char"; From dc8380b65da2071735129c829a3ea7bca006bf5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 12 Nov 2019 14:41:24 +0200 Subject: [PATCH 8/9] MDEV-14602: Cleanup recv_dblwr_t::find_page() Avoid creating std::vector, and use single instead of double traversal. --- storage/innobase/log/log0recv.cc | 49 ++++++++++---------------------- 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index 3068a510b4e..9a20c7f7d22 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -27,7 +27,6 @@ Created 9/20/1997 Heikki Tuuri #include "univ.i" -#include #include #include #include @@ -4044,44 +4043,26 @@ recv_recovery_rollback_active(void) @param[in] page_no page number @return page frame @retval NULL if no page was found */ - const byte* recv_dblwr_t::find_page(ulint space_id, ulint page_no) { - typedef std::vector > - matches_t; + const byte *result= NULL; + lsn_t max_lsn= 0; - matches_t matches; - const byte* result = 0; + for (list::const_iterator i = pages.begin(); i != pages.end(); ++i) + { + const byte *page= *i; + if (page_get_page_no(page) != page_no || + page_get_space_id(page) != space_id) + continue; + const lsn_t lsn= mach_read_from_8(page + FIL_PAGE_LSN); + if (lsn <= max_lsn) + continue; + max_lsn= lsn; + result= page; + } - for (list::iterator i = pages.begin(); i != pages.end(); ++i) { - if (page_get_space_id(*i) == space_id - && page_get_page_no(*i) == page_no) { - matches.push_back(*i); - } - } - - if (matches.size() == 1) { - result = matches[0]; - } else if (matches.size() > 1) { - - lsn_t max_lsn = 0; - lsn_t page_lsn = 0; - - for (matches_t::iterator i = matches.begin(); - i != matches.end(); - ++i) { - - page_lsn = mach_read_from_8(*i + FIL_PAGE_LSN); - - if (page_lsn > max_lsn) { - max_lsn = page_lsn; - result = *i; - } - } - } - - return(result); + return result; } #ifndef DBUG_OFF From 2570cb8b91711b38e4a448cc92b95e49abb218b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 12 Nov 2019 15:46:57 +0200 Subject: [PATCH 9/9] MDEV-12353 preparation: Clean up mtr_t mtr_t::Impl, mtr_t::Command: Merge to mtr_t. MTR_MAGIC_N: Remove. MTR_STATE_COMMITTING: Remove. This state was only being set internally during mtr_t::commit(). mtr_t::Command::m_locks_released: Remove (set-and-never-read member). mtr_t::Command::m_start_lsn: Replaced with the return value of finish_write() and a parameter to release_blocks(). mtr_t::Command::m_end_lsn: Removed as a duplicate of mtr_t::m_commit_lsn. mtr_t::Command::prepare_write(): Replace a switch () with a comparison against 0. Only 2 m_log_mode are allowed. --- storage/innobase/include/mtr0mtr.h | 246 ++++++---------- storage/innobase/include/mtr0mtr.ic | 55 ++-- storage/innobase/include/mtr0types.h | 19 +- storage/innobase/mtr/mtr0mtr.cc | 423 +++++++++------------------ 4 files changed, 251 insertions(+), 492 deletions(-) diff --git a/storage/innobase/include/mtr0mtr.h b/storage/innobase/include/mtr0mtr.h index 4f518071bfb..30d2e937f1f 100644 --- a/storage/innobase/include/mtr0mtr.h +++ b/storage/innobase/include/mtr0mtr.h @@ -2,7 +2,7 @@ Copyright (c) 1995, 2017, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2013, 2018, MariaDB Corporation. +Copyright (c) 2013, 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 @@ -146,68 +146,7 @@ struct mtr_memo_slot_t { /** Mini-transaction handle and buffer */ struct mtr_t { - - /** State variables of the mtr */ - struct Impl { - - /** memo stack for locks etc. */ - mtr_buf_t m_memo; - - /** mini-transaction log */ - mtr_buf_t m_log; - - /** true if mtr has made at least one buffer pool page dirty */ - bool m_made_dirty; - - /** true if inside ibuf changes */ - bool m_inside_ibuf; - - /** true if the mini-transaction modified buffer pool pages */ - bool m_modifications; - - /** Count of how many page initial log records have been - written to the mtr log */ - ib_uint32_t m_n_log_recs; - - /** specifies which operations should be logged; default - value MTR_LOG_ALL */ - mtr_log_t m_log_mode; -#ifdef UNIV_DEBUG - /** Persistent user tablespace associated with the - mini-transaction, or 0 (TRX_SYS_SPACE) if none yet */ - ulint m_user_space_id; -#endif /* UNIV_DEBUG */ - /** User tablespace that is being modified by the - mini-transaction */ - fil_space_t* m_user_space; - /** Undo tablespace that is being modified by the - mini-transaction */ - fil_space_t* m_undo_space; - /** System tablespace if it is being modified by the - mini-transaction */ - fil_space_t* m_sys_space; - - /** State of the transaction */ - mtr_state_t m_state; - - /** Flush Observer */ - FlushObserver* m_flush_observer; - -#ifdef UNIV_DEBUG - /** For checking corruption. */ - ulint m_magic_n; -#endif /* UNIV_DEBUG */ - - /** Owning mini-transaction */ - mtr_t* m_mtr; - }; - - mtr_t() - { - m_impl.m_state = MTR_STATE_INIT; - } - - ~mtr_t() { } + mtr_t() : m_state(MTR_STATE_INIT) {} /** Release the free extents that was reserved using fsp_reserve_free_extents(). This is equivalent to calling @@ -236,14 +175,7 @@ struct mtr_t { /** Return current size of the buffer. @return savepoint */ - ulint get_savepoint() const - MY_ATTRIBUTE((warn_unused_result)) - { - ut_ad(is_active()); - ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); - - return(m_impl.m_memo.size()); - } + ulint get_savepoint() const {ut_ad(is_active()); return m_memo.size();} /** Release the (index tree) s-latch stored in an mtr memo after a savepoint. @@ -279,10 +211,10 @@ struct mtr_t { @return the system tablespace */ fil_space_t* set_sys_modified() { - if (!m_impl.m_sys_space) { + if (!m_sys_space) { lookup_sys_space(); } - return(m_impl.m_sys_space); + return m_sys_space; } /** Copy the tablespaces associated with the mini-transaction @@ -291,15 +223,15 @@ struct mtr_t { the same set of tablespaces as this one */ void set_spaces(const mtr_t& mtr) { - ut_ad(!m_impl.m_user_space_id); - ut_ad(!m_impl.m_user_space); - ut_ad(!m_impl.m_undo_space); - ut_ad(!m_impl.m_sys_space); + ut_ad(!m_user_space_id); + ut_ad(!m_user_space); + ut_ad(!m_undo_space); + ut_ad(!m_sys_space); - ut_d(m_impl.m_user_space_id = mtr.m_impl.m_user_space_id); - m_impl.m_user_space = mtr.m_impl.m_user_space; - m_impl.m_undo_space = mtr.m_impl.m_undo_space; - m_impl.m_sys_space = mtr.m_impl.m_sys_space; + ut_d(m_user_space_id = mtr.m_user_space_id); + m_user_space = mtr.m_user_space; + m_undo_space = mtr.m_undo_space; + m_sys_space = mtr.m_sys_space; } /** Set the tablespace associated with the mini-transaction @@ -308,13 +240,13 @@ struct mtr_t { @return the tablespace */ fil_space_t* set_named_space(ulint space_id) { - ut_ad(!m_impl.m_user_space_id); - ut_d(m_impl.m_user_space_id = space_id); + ut_ad(!m_user_space_id); + ut_d(m_user_space_id = space_id); if (!space_id) { return(set_sys_modified()); } else { lookup_user_space(space_id); - return(m_impl.m_user_space); + return m_user_space; } } @@ -381,18 +313,12 @@ struct mtr_t { void release_page(const void* ptr, mtr_memo_type_t type); /** Note that the mini-transaction has modified data. */ - void set_modified() - { - m_impl.m_modifications = true; - } + void set_modified() { m_modifications = true; } /** Set the state to not-modified. This will not log the changes. This is only used during redo log apply, to avoid logging the changes. */ - void discard_modifications() - { - m_impl.m_modifications = false; - } + void discard_modifications() { m_modifications = false; } /** Get the LSN of commit(). @return the commit LSN @@ -404,45 +330,28 @@ struct mtr_t { } /** Note that we are inside the change buffer code. */ - void enter_ibuf() - { - m_impl.m_inside_ibuf = true; - } + void enter_ibuf() { m_inside_ibuf = true; } /** Note that we have exited from the change buffer code. */ - void exit_ibuf() - { - m_impl.m_inside_ibuf = false; - } + void exit_ibuf() { m_inside_ibuf = false; } /** @return true if we are inside the change buffer code */ - bool is_inside_ibuf() const - { - return(m_impl.m_inside_ibuf); - } + bool is_inside_ibuf() const { return m_inside_ibuf; } /* @return true if the mini-transaction is active */ - bool is_active() const - { - return(m_impl.m_state == MTR_STATE_ACTIVE); - } + bool is_active() const { return m_state == MTR_STATE_ACTIVE; } /** Get flush observer @return flush observer */ - FlushObserver* get_flush_observer() const - { - return(m_impl.m_flush_observer); - } + FlushObserver* get_flush_observer() const { return m_flush_observer; } /** Set flush observer @param[in] observer flush observer */ void set_flush_observer(FlushObserver* observer) { - ut_ad(observer == NULL - || m_impl.m_log_mode == MTR_LOG_NO_REDO); - - m_impl.m_flush_observer = observer; + ut_ad(observer == NULL || m_log_mode == MTR_LOG_NO_REDO); + m_flush_observer = observer; } #ifdef UNIV_DEBUG @@ -482,65 +391,31 @@ struct mtr_t { void print() const; /** @return true if the mini-transaction has committed */ - bool has_committed() const - { - return(m_impl.m_state == MTR_STATE_COMMITTED); - } - - /** @return true if the mini-transaction is committing */ - bool is_committing() const - { - return(m_impl.m_state == MTR_STATE_COMMITTING); - } + bool has_committed() const { return m_state == MTR_STATE_COMMITTED; } /** @return true if mini-transaction contains modifications. */ - bool has_modifications() const - { - return(m_impl.m_modifications); - } + bool has_modifications() const { return m_modifications; } /** @return the memo stack */ - const mtr_buf_t* get_memo() const - { - return(&m_impl.m_memo); - } + const mtr_buf_t* get_memo() const { return &m_memo; } /** @return the memo stack */ - mtr_buf_t* get_memo() - { - return(&m_impl.m_memo); - } + mtr_buf_t* get_memo() { return &m_memo; } #endif /* UNIV_DEBUG */ /** @return true if a record was added to the mini-transaction */ - bool is_dirty() const - { - return(m_impl.m_made_dirty); - } + bool is_dirty() const { return m_made_dirty; } /** Note that a record has been added to the log */ - void added_rec() - { - ++m_impl.m_n_log_recs; - } + void added_rec() { ++m_n_log_recs; } /** Get the buffered redo log of this mini-transaction. @return redo log */ - const mtr_buf_t* get_log() const - { - ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); - - return(&m_impl.m_log); - } + const mtr_buf_t* get_log() const { return &m_log; } /** Get the buffered redo log of this mini-transaction. @return redo log */ - mtr_buf_t* get_log() - { - ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); - - return(&m_impl.m_log); - } + mtr_buf_t* get_log() { return &m_log; } /** Push an object to an mtr memo stack. @param object object @@ -560,15 +435,60 @@ private: @param[in] space_id tablespace ID */ void lookup_user_space(ulint space_id); - class Command; + /** Prepare to write the mini-transaction log to the redo log buffer. + @return number of bytes to write in finish_write() */ + inline ulint prepare_write(); - friend class Command; + /** Append the redo log records to the redo log buffer. + @param[in] len number of bytes to write + @return start_lsn */ + inline lsn_t finish_write(ulint len); -private: - Impl m_impl; + /** Release the resources */ + inline void release_resources(); + + /** memo stack for locks etc. */ + mtr_buf_t m_memo; + + /** mini-transaction log */ + mtr_buf_t m_log; + + /** true if mtr has made at least one buffer pool page dirty */ + bool m_made_dirty; + + /** true if inside ibuf changes */ + bool m_inside_ibuf; + + /** true if the mini-transaction modified buffer pool pages */ + bool m_modifications; + + /** Count of how many page initial log records have been + written to the mtr log */ + ib_uint32_t m_n_log_recs; + + /** specifies which operations should be logged; default + value MTR_LOG_ALL */ + mtr_log_t m_log_mode; +#ifdef UNIV_DEBUG + /** Persistent user tablespace associated with the + mini-transaction, or 0 (TRX_SYS_SPACE) if none yet */ + ulint m_user_space_id; +#endif /* UNIV_DEBUG */ + /** User tablespace that is being modified by the mini-transaction */ + fil_space_t* m_user_space; + /** Undo tablespace that is being modified by the mini-transaction */ + fil_space_t* m_undo_space; + /** System tablespace if being modified by the mini-transaction */ + fil_space_t* m_sys_space; + + /** State of the transaction */ + mtr_state_t m_state; + + /** Flush Observer */ + FlushObserver* m_flush_observer; /** LSN at commit time */ - volatile lsn_t m_commit_lsn; + lsn_t m_commit_lsn; }; #include "mtr0mtr.ic" diff --git a/storage/innobase/include/mtr0mtr.ic b/storage/innobase/include/mtr0mtr.ic index 574d0de24a5..7175ede0d6a 100644 --- a/storage/innobase/include/mtr0mtr.ic +++ b/storage/innobase/include/mtr0mtr.ic @@ -49,7 +49,6 @@ mtr_t::memo_push(void* object, mtr_memo_type_t type) ut_ad(object != NULL); ut_ad(type >= MTR_MEMO_PAGE_S_FIX); ut_ad(type <= MTR_MEMO_SX_LOCK); - ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); ut_ad(ut_is_2pow(type)); /* If this mtr has x-fixed a clean page then we set @@ -58,15 +57,13 @@ mtr_t::memo_push(void* object, mtr_memo_type_t type) can insert the dirtied page to the flush list. */ if ((type == MTR_MEMO_PAGE_X_FIX || type == MTR_MEMO_PAGE_SX_FIX) - && !m_impl.m_made_dirty) { + && !m_made_dirty) { - m_impl.m_made_dirty = is_block_dirtied( + m_made_dirty = is_block_dirtied( reinterpret_cast(object)); } - mtr_memo_slot_t* slot; - - slot = m_impl.m_memo.push(sizeof(*slot)); + mtr_memo_slot_t* slot = m_memo.push(sizeof(*slot)); slot->type = type; slot->object = object; @@ -81,11 +78,9 @@ mtr_t::release_s_latch_at_savepoint( rw_lock_t* lock) { ut_ad(is_active()); - ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); + ut_ad(m_memo.size() > savepoint); - ut_ad(m_impl.m_memo.size() > savepoint); - - mtr_memo_slot_t* slot = m_impl.m_memo.at(savepoint); + mtr_memo_slot_t* slot = m_memo.at(savepoint); ut_ad(slot->object == lock); ut_ad(slot->type == MTR_MEMO_S_LOCK); @@ -104,8 +99,7 @@ mtr_t::sx_latch_at_savepoint( buf_block_t* block) { ut_ad(is_active()); - ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); - ut_ad(m_impl.m_memo.size() > savepoint); + ut_ad(m_memo.size() > savepoint); ut_ad(!memo_contains_flagged( block, @@ -113,9 +107,7 @@ mtr_t::sx_latch_at_savepoint( | MTR_MEMO_PAGE_X_FIX | MTR_MEMO_PAGE_SX_FIX)); - mtr_memo_slot_t* slot; - - slot = m_impl.m_memo.at(savepoint); + mtr_memo_slot_t* slot = m_memo.at(savepoint); ut_ad(slot->object == block); @@ -124,8 +116,8 @@ mtr_t::sx_latch_at_savepoint( rw_lock_sx_lock(&block->lock); - if (!m_impl.m_made_dirty) { - m_impl.m_made_dirty = is_block_dirtied(block); + if (!m_made_dirty) { + m_made_dirty = is_block_dirtied(block); } slot->type = MTR_MEMO_PAGE_SX_FIX; @@ -140,8 +132,7 @@ mtr_t::x_latch_at_savepoint( buf_block_t* block) { ut_ad(is_active()); - ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); - ut_ad(m_impl.m_memo.size() > savepoint); + ut_ad(m_memo.size() > savepoint); ut_ad(!memo_contains_flagged( block, @@ -149,9 +140,7 @@ mtr_t::x_latch_at_savepoint( | MTR_MEMO_PAGE_X_FIX | MTR_MEMO_PAGE_SX_FIX)); - mtr_memo_slot_t* slot; - - slot = m_impl.m_memo.at(savepoint); + mtr_memo_slot_t* slot = m_memo.at(savepoint); ut_ad(slot->object == block); @@ -160,8 +149,8 @@ mtr_t::x_latch_at_savepoint( rw_lock_x_lock(&block->lock); - if (!m_impl.m_made_dirty) { - m_impl.m_made_dirty = is_block_dirtied(block); + if (!m_made_dirty) { + m_made_dirty = is_block_dirtied(block); } slot->type = MTR_MEMO_PAGE_X_FIX; @@ -176,11 +165,8 @@ mtr_t::release_block_at_savepoint( buf_block_t* block) { ut_ad(is_active()); - ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); - mtr_memo_slot_t* slot; - - slot = m_impl.m_memo.at(savepoint); + mtr_memo_slot_t* slot = m_memo.at(savepoint); ut_a(slot->object == block); @@ -198,10 +184,10 @@ Gets the logging mode of a mini-transaction. mtr_log_t mtr_t::get_log_mode() const { - ut_ad(m_impl.m_log_mode >= MTR_LOG_ALL); - ut_ad(m_impl.m_log_mode <= MTR_LOG_SHORT_INSERTS); + ut_ad(m_log_mode >= MTR_LOG_ALL); + ut_ad(m_log_mode <= MTR_LOG_SHORT_INSERTS); - return(m_impl.m_log_mode); + return m_log_mode; } /** @@ -214,7 +200,7 @@ mtr_t::set_log_mode(mtr_log_t mode) ut_ad(mode >= MTR_LOG_ALL); ut_ad(mode <= MTR_LOG_SHORT_INSERTS); - const mtr_log_t old_mode = m_impl.m_log_mode; + const mtr_log_t old_mode = m_log_mode; switch (old_mode) { case MTR_LOG_NO_REDO: @@ -233,9 +219,8 @@ mtr_t::set_log_mode(mtr_log_t mode) case MTR_LOG_ALL: /* MTR_LOG_NO_REDO can only be set before generating any redo log records. */ - ut_ad(mode != MTR_LOG_NO_REDO - || m_impl.m_n_log_recs == 0); - m_impl.m_log_mode = mode; + ut_ad(mode != MTR_LOG_NO_REDO || m_n_log_recs == 0); + m_log_mode = mode; return(old_mode); } diff --git a/storage/innobase/include/mtr0types.h b/storage/innobase/include/mtr0types.h index 1179da6d926..985ad7b81ea 100644 --- a/storage/innobase/include/mtr0types.h +++ b/storage/innobase/include/mtr0types.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 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 @@ -36,18 +36,18 @@ struct mtr_t; /** Logging modes for a mini-transaction */ enum mtr_log_t { /** Default mode: log all operations modifying disk-based data */ - MTR_LOG_ALL = 21, + MTR_LOG_ALL = 0, /** Log no operations and dirty pages are not added to the flush list. Set when applying log in crash recovery or when a modification of a ROW_FORMAT=COMPRESSED page is attempted. */ - MTR_LOG_NONE = 22, + MTR_LOG_NONE, /** Don't generate REDO log but add dirty pages to flush list */ - MTR_LOG_NO_REDO = 23, + MTR_LOG_NO_REDO, /** Inserts are logged in a shorter form */ - MTR_LOG_SHORT_INSERTS = 24 + MTR_LOG_SHORT_INSERTS }; /** @name Log item types @@ -267,15 +267,10 @@ enum mtr_memo_type_t { }; #endif /* !UNIV_CHECKSUM */ -#ifdef UNIV_DEBUG -# define MTR_MAGIC_N 54551 -#endif /* UNIV_DEBUG */ - enum mtr_state_t { MTR_STATE_INIT = 0, - MTR_STATE_ACTIVE = 12231, - MTR_STATE_COMMITTING = 56456, - MTR_STATE_COMMITTED = 34676 + MTR_STATE_ACTIVE, + MTR_STATE_COMMITTED }; #endif /* mtr0types_h */ diff --git a/storage/innobase/mtr/mtr0mtr.cc b/storage/innobase/mtr/mtr0mtr.cc index fc6f3321164..533b10b861e 100644 --- a/storage/innobase/mtr/mtr0mtr.cc +++ b/storage/innobase/mtr/mtr0mtr.cc @@ -343,6 +343,7 @@ struct ReleaseAll { } }; +#ifdef UNIV_DEBUG /** Check that all slots have been handled. */ struct DebugCheck { /** @return true always. */ @@ -352,6 +353,7 @@ struct DebugCheck { return(true); } }; +#endif /** Release a resource acquired by the mini-transaction. */ struct ReleaseBlocks { @@ -404,59 +406,6 @@ struct ReleaseBlocks { FlushObserver* m_flush_observer; }; -class mtr_t::Command { -public: - /** Constructor. - Takes ownership of the mtr->m_impl, is responsible for deleting it. - @param[in,out] mtr mini-transaction */ - explicit Command(mtr_t* mtr) : m_impl(&mtr->m_impl), m_locks_released() - {} - - /** Destructor */ - ~Command() - { - ut_ad(m_impl == 0); - } - - /** Write the redo log record, add dirty pages to the flush list and - release the resources. */ - void execute(); - - /** Release the blocks used in this mini-transaction. */ - void release_blocks(); - - /** Release the latches acquired by the mini-transaction. */ - void release_latches(); - - /** Release both the latches and blocks used in the mini-transaction. */ - void release_all(); - - /** Release the resources */ - void release_resources(); - - /** Append the redo log records to the redo log buffer. - @param[in] len number of bytes to write */ - void finish_write(ulint len); - -private: - /** Prepare to write the mini-transaction log to the redo log buffer. - @return number of bytes to write in finish_write() */ - ulint prepare_write(); - - /** The mini-transaction state. */ - mtr_t::Impl* m_impl; - - /** Set to 1 after the user thread releases the latches. The log - writer thread must wait for this to be set to 1. */ - volatile ulint m_locks_released; - - /** Start lsn of the possible log entry for this mtr */ - lsn_t m_start_lsn; - - /** End lsn of the possible log entry for this mtr */ - lsn_t m_end_lsn; -}; - /** Write the block contents to the REDO log */ struct mtr_write_log_t { /** Append a block to the redo log buffer. @@ -490,78 +439,77 @@ mtr_write_log( /** Start a mini-transaction. */ void mtr_t::start() { - UNIV_MEM_INVALID(this, sizeof(*this)); + UNIV_MEM_INVALID(this, sizeof *this); - UNIV_MEM_INVALID(&m_impl, sizeof(m_impl)); + new(&m_memo) mtr_buf_t(); + new(&m_log) mtr_buf_t(); - m_commit_lsn = 0; - - new(&m_impl.m_log) mtr_buf_t(); - new(&m_impl.m_memo) mtr_buf_t(); - - m_impl.m_mtr = this; - m_impl.m_log_mode = MTR_LOG_ALL; - m_impl.m_inside_ibuf = false; - m_impl.m_modifications = false; - m_impl.m_made_dirty = false; - m_impl.m_n_log_recs = 0; - m_impl.m_state = MTR_STATE_ACTIVE; - ut_d(m_impl.m_user_space_id = TRX_SYS_SPACE); - m_impl.m_user_space = NULL; - m_impl.m_undo_space = NULL; - m_impl.m_sys_space = NULL; - m_impl.m_flush_observer = NULL; - - ut_d(m_impl.m_magic_n = MTR_MAGIC_N); + m_made_dirty= false; + m_inside_ibuf= false; + m_modifications= false; + m_n_log_recs= 0; + m_log_mode= MTR_LOG_ALL; + ut_d(m_user_space_id= TRX_SYS_SPACE); + m_user_space= NULL; + m_undo_space= NULL; + m_sys_space= NULL; + m_state= MTR_STATE_ACTIVE; + m_flush_observer= NULL; + m_commit_lsn= 0; } /** Release the resources */ -void -mtr_t::Command::release_resources() +inline void mtr_t::release_resources() { - ut_ad(m_impl->m_magic_n == MTR_MAGIC_N); - - /* Currently only used in commit */ - ut_ad(m_impl->m_state == MTR_STATE_COMMITTING); - - ut_d(m_impl->m_memo.for_each_block_in_reverse(CIterate())); - - /* Reset the mtr buffers */ - m_impl->m_log.erase(); - - m_impl->m_memo.erase(); - - m_impl->m_state = MTR_STATE_COMMITTED; - - m_impl = 0; + ut_d(m_memo.for_each_block_in_reverse(CIterate())); + m_log.erase(); + m_memo.erase(); + m_state= MTR_STATE_COMMITTED; } /** Commit a mini-transaction. */ void mtr_t::commit() { - ut_ad(is_active()); - ut_ad(!is_inside_ibuf()); - ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); - m_impl.m_state = MTR_STATE_COMMITTING; + ut_ad(is_active()); + ut_ad(!is_inside_ibuf()); - /* This is a dirty read, for debugging. */ - ut_ad(!m_impl.m_modifications || !recv_no_log_write); + /* This is a dirty read, for debugging. */ + ut_ad(!m_modifications || !recv_no_log_write); + ut_ad(!m_modifications || m_log_mode != MTR_LOG_NONE); - Command cmd(this); + if (m_modifications + && (m_n_log_recs || m_log_mode == MTR_LOG_NO_REDO)) + { + ut_ad(!srv_read_only_mode || m_log_mode == MTR_LOG_NO_REDO); - if (m_impl.m_modifications - && (m_impl.m_n_log_recs > 0 - || m_impl.m_log_mode == MTR_LOG_NO_REDO)) { + lsn_t start_lsn; - ut_ad(!srv_read_only_mode - || m_impl.m_log_mode == MTR_LOG_NO_REDO); + if (const ulint len= prepare_write()) + start_lsn= finish_write(len); + else + start_lsn= m_commit_lsn; - cmd.execute(); - } else { - cmd.release_all(); - cmd.release_resources(); - } + if (m_made_dirty) + log_flush_order_mutex_enter(); + + /* It is now safe to release the log mutex because the + flush_order mutex will ensure that we are the first one + to insert into the flush list. */ + log_mutex_exit(); + + m_memo.for_each_block_in_reverse(CIterate + (ReleaseBlocks(start_lsn, m_commit_lsn, + m_flush_observer))); + if (m_made_dirty) + log_flush_order_mutex_exit(); + + m_memo.for_each_block_in_reverse(CIterate()); + } + else + m_memo.for_each_block_in_reverse(CIterate()); + + release_resources(); } /** Commit a mini-transaction that did not modify any pages, @@ -580,37 +528,31 @@ mtr_t::commit_checkpoint( ut_ad(log_mutex_own()); ut_ad(is_active()); ut_ad(!is_inside_ibuf()); - ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); ut_ad(get_log_mode() == MTR_LOG_ALL); - ut_ad(!m_impl.m_made_dirty); - ut_ad(m_impl.m_memo.size() == 0); + ut_ad(!m_made_dirty); + ut_ad(m_memo.size() == 0); ut_ad(!srv_read_only_mode); - ut_d(m_impl.m_state = MTR_STATE_COMMITTING); - ut_ad(write_mlog_checkpoint || m_impl.m_n_log_recs > 1); + ut_ad(write_mlog_checkpoint || m_n_log_recs > 1); - switch (m_impl.m_n_log_recs) { + switch (m_n_log_recs) { case 0: break; case 1: - *m_impl.m_log.front()->begin() |= MLOG_SINGLE_REC_FLAG; + *m_log.front()->begin() |= MLOG_SINGLE_REC_FLAG; break; default: - mlog_catenate_ulint( - &m_impl.m_log, MLOG_MULTI_REC_END, MLOG_1BYTE); + mlog_catenate_ulint(&m_log, MLOG_MULTI_REC_END, MLOG_1BYTE); } if (write_mlog_checkpoint) { - byte* ptr = m_impl.m_log.push(SIZE_OF_MLOG_CHECKPOINT); -#if SIZE_OF_MLOG_CHECKPOINT != 9 -# error SIZE_OF_MLOG_CHECKPOINT != 9 -#endif + byte* ptr = m_log.push(SIZE_OF_MLOG_CHECKPOINT); + compile_time_assert(SIZE_OF_MLOG_CHECKPOINT == 1 + 8); *ptr = MLOG_CHECKPOINT; mach_write_to_8(ptr + 1, checkpoint_lsn); } - Command cmd(this); - cmd.finish_write(m_impl.m_log.size()); - cmd.release_resources(); + finish_write(m_log.size()); + release_resources(); if (write_mlog_checkpoint) { DBUG_PRINT("ib_log", @@ -627,18 +569,12 @@ mtr_t::commit_checkpoint( bool mtr_t::is_named_space(ulint space) const { - ut_ad(!m_impl.m_sys_space - || m_impl.m_sys_space->id == TRX_SYS_SPACE); - ut_ad(!m_impl.m_undo_space - || m_impl.m_undo_space->id != TRX_SYS_SPACE); - ut_ad(!m_impl.m_user_space - || m_impl.m_user_space->id != TRX_SYS_SPACE); - ut_ad(!m_impl.m_sys_space - || m_impl.m_sys_space != m_impl.m_user_space); - ut_ad(!m_impl.m_sys_space - || m_impl.m_sys_space != m_impl.m_undo_space); - ut_ad(!m_impl.m_user_space - || m_impl.m_user_space != m_impl.m_undo_space); + ut_ad(!m_sys_space || m_sys_space->id == TRX_SYS_SPACE); + ut_ad(!m_undo_space || m_undo_space->id != TRX_SYS_SPACE); + ut_ad(!m_user_space || m_user_space->id != TRX_SYS_SPACE); + ut_ad(!m_sys_space || m_sys_space != m_user_space); + ut_ad(!m_sys_space || m_sys_space != m_undo_space); + ut_ad(!m_user_space || m_user_space != m_undo_space); switch (get_log_mode()) { case MTR_LOG_NONE: @@ -646,7 +582,7 @@ mtr_t::is_named_space(ulint space) const return(true); case MTR_LOG_ALL: case MTR_LOG_SHORT_INSERTS: - return(m_impl.m_user_space_id == space + return(m_user_space_id == space || is_predefined_tablespace(space)); } @@ -666,17 +602,16 @@ mtr_t::x_lock_space(ulint space_id, const char* file, unsigned line) { fil_space_t* space; - ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); ut_ad(is_active()); if (space_id == TRX_SYS_SPACE) { - space = m_impl.m_sys_space; + space = m_sys_space; if (!space) { - space = m_impl.m_sys_space = fil_space_get(space_id); + space = m_sys_space = fil_space_get(space_id); } - } else if ((space = m_impl.m_user_space) && space_id == space->id) { - } else if ((space = m_impl.m_undo_space) && space_id == space->id) { + } else if ((space = m_user_space) && space_id == space->id) { + } else if ((space = m_undo_space) && space_id == space->id) { } else if (get_log_mode() == MTR_LOG_NO_REDO) { space = fil_space_get(space_id); ut_ad(space->purpose == FIL_TYPE_TEMPORARY @@ -685,7 +620,7 @@ mtr_t::x_lock_space(ulint space_id, const char* file, unsigned line) || srv_is_tablespace_truncated(space->id)); } else { /* called from trx_rseg_create() */ - space = m_impl.m_undo_space = fil_space_get(space_id); + space = m_undo_space = fil_space_get(space_id); } ut_ad(space); @@ -701,9 +636,9 @@ mtr_t::x_lock_space(ulint space_id, const char* file, unsigned line) void mtr_t::lookup_sys_space() { - ut_ad(!m_impl.m_sys_space); - m_impl.m_sys_space = fil_space_get(TRX_SYS_SPACE); - ut_ad(m_impl.m_sys_space); + ut_ad(!m_sys_space); + m_sys_space = fil_space_get(TRX_SYS_SPACE); + ut_ad(m_sys_space); } /** Look up the user tablespace. @@ -712,10 +647,10 @@ void mtr_t::lookup_user_space(ulint space_id) { ut_ad(space_id != TRX_SYS_SPACE); - ut_ad(m_impl.m_user_space_id == space_id); - ut_ad(!m_impl.m_user_space); - m_impl.m_user_space = fil_space_get(space_id); - ut_ad(m_impl.m_user_space); + ut_ad(m_user_space_id == space_id); + ut_ad(!m_user_space); + m_user_space = fil_space_get(space_id); + ut_ad(m_user_space); } /** Set the tablespace associated with the mini-transaction @@ -724,14 +659,13 @@ mtr_t::lookup_user_space(ulint space_id) void mtr_t::set_named_space(fil_space_t* space) { - ut_ad(m_impl.m_user_space_id == TRX_SYS_SPACE); - ut_d(m_impl.m_user_space_id = space->id); + ut_ad(m_user_space_id == TRX_SYS_SPACE); + ut_d(m_user_space_id = space->id); if (space->id == TRX_SYS_SPACE) { - ut_ad(m_impl.m_sys_space == NULL - || m_impl.m_sys_space == space); - m_impl.m_sys_space = space; + ut_ad(!m_sys_space || m_sys_space == space); + m_sys_space = space; } else { - m_impl.m_user_space = space; + m_user_space = space; } } @@ -740,16 +674,15 @@ mtr_t::set_named_space(fil_space_t* space) bool mtr_t::memo_release(const void* object, ulint type) { - ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); ut_ad(is_active()); /* We cannot release a page that has been written to in the middle of a mini-transaction. */ - ut_ad(!m_impl.m_modifications || type != MTR_MEMO_PAGE_X_FIX); + ut_ad(!m_modifications || type != MTR_MEMO_PAGE_X_FIX); Iterate iteration(Find(object, type)); - if (!m_impl.m_memo.for_each_block_in_reverse(iteration)) { + if (!m_memo.for_each_block_in_reverse(iteration)) { memo_slot_release(iteration.functor.m_slot); return(true); } @@ -763,16 +696,15 @@ mtr_t::memo_release(const void* object, ulint type) void mtr_t::release_page(const void* ptr, mtr_memo_type_t type) { - ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); ut_ad(is_active()); /* We cannot release a page that has been written to in the middle of a mini-transaction. */ - ut_ad(!m_impl.m_modifications || type != MTR_MEMO_PAGE_X_FIX); + ut_ad(!m_modifications || type != MTR_MEMO_PAGE_X_FIX); Iterate iteration(FindPage(ptr, type)); - if (!m_impl.m_memo.for_each_block_in_reverse(iteration)) { + if (!m_memo.for_each_block_in_reverse(iteration)) { memo_slot_release(iteration.functor.get_slot()); return; } @@ -783,27 +715,20 @@ mtr_t::release_page(const void* ptr, mtr_memo_type_t type) /** Prepare to write the mini-transaction log to the redo log buffer. @return number of bytes to write in finish_write() */ -ulint -mtr_t::Command::prepare_write() +inline ulint mtr_t::prepare_write() { ut_ad(!recv_no_log_write); - switch (m_impl->m_log_mode) { - case MTR_LOG_SHORT_INSERTS: - ut_ad(0); - /* fall through (write no redo log) */ - case MTR_LOG_NO_REDO: - case MTR_LOG_NONE: - ut_ad(m_impl->m_log.size() == 0); + if (UNIV_UNLIKELY(m_log_mode != MTR_LOG_ALL)) { + ut_ad(m_log_mode == MTR_LOG_NO_REDO); + ut_ad(m_log.size() == 0); log_mutex_enter(); - m_end_lsn = m_start_lsn = log_sys->lsn; - return(0); - case MTR_LOG_ALL: - break; + m_commit_lsn = log_sys->lsn; + return 0; } - ulint len = m_impl->m_log.size(); - ulint n_recs = m_impl->m_n_log_recs; + ulint len = m_log.size(); + ulint n_recs = m_n_log_recs; ut_ad(len > 0); ut_ad(n_recs > 0); @@ -811,9 +736,9 @@ mtr_t::Command::prepare_write() log_buffer_extend((len + 1) * 2); } - ut_ad(m_impl->m_n_log_recs == n_recs); + ut_ad(m_n_log_recs == n_recs); - fil_space_t* space = m_impl->m_user_space; + fil_space_t* space = m_user_space; if (space != NULL && is_predefined_tablespace(space->id)) { /* Omit MLOG_FILE_NAME for predefined tablespaces. */ @@ -822,35 +747,32 @@ mtr_t::Command::prepare_write() log_mutex_enter(); - if (fil_names_write_if_was_clean(space, m_impl->m_mtr)) { + if (fil_names_write_if_was_clean(space, this)) { /* This mini-transaction was the first one to modify this tablespace since the latest checkpoint, so some MLOG_FILE_NAME records were appended to m_log. */ - ut_ad(m_impl->m_n_log_recs > n_recs); - mlog_catenate_ulint( - &m_impl->m_log, MLOG_MULTI_REC_END, MLOG_1BYTE); - len = m_impl->m_log.size(); + ut_ad(m_n_log_recs > n_recs); + mlog_catenate_ulint(&m_log, MLOG_MULTI_REC_END, MLOG_1BYTE); + len = m_log.size(); } else { /* This was not the first time of dirtying a tablespace since the latest checkpoint. */ - ut_ad(n_recs == m_impl->m_n_log_recs); + ut_ad(n_recs == m_n_log_recs); if (n_recs <= 1) { ut_ad(n_recs == 1); /* Flag the single log record as the only record in this mini-transaction. */ - *m_impl->m_log.front()->begin() - |= MLOG_SINGLE_REC_FLAG; + *m_log.front()->begin() |= MLOG_SINGLE_REC_FLAG; } else { /* Because this mini-transaction comprises multiple log records, append MLOG_MULTI_REC_END at the end. */ - mlog_catenate_ulint( - &m_impl->m_log, MLOG_MULTI_REC_END, - MLOG_1BYTE); + mlog_catenate_ulint(&m_log, MLOG_MULTI_REC_END, + MLOG_1BYTE); len++; } } @@ -862,98 +784,37 @@ mtr_t::Command::prepare_write() } /** Append the redo log records to the redo log buffer -@param[in] len number of bytes to write */ -void -mtr_t::Command::finish_write( - ulint len) +@param[in] len number of bytes to write +@return start_lsn */ +inline lsn_t mtr_t::finish_write(ulint len) { - ut_ad(m_impl->m_log_mode == MTR_LOG_ALL); + ut_ad(m_log_mode == MTR_LOG_ALL); ut_ad(log_mutex_own()); - ut_ad(m_impl->m_log.size() == len); + ut_ad(m_log.size() == len); ut_ad(len > 0); - if (m_impl->m_log.is_small()) { - const mtr_buf_t::block_t* front = m_impl->m_log.front(); + lsn_t start_lsn; + + if (m_log.is_small()) { + const mtr_buf_t::block_t* front = m_log.front(); ut_ad(len <= front->used()); - m_end_lsn = log_reserve_and_write_fast( - front->begin(), len, &m_start_lsn); + m_commit_lsn = log_reserve_and_write_fast(front->begin(), len, + &start_lsn); - if (m_end_lsn > 0) { - return; + if (m_commit_lsn) { + return start_lsn; } } /* Open the database log for log_write_low */ - m_start_lsn = log_reserve_and_open(len); + start_lsn = log_reserve_and_open(len); mtr_write_log_t write_log; - m_impl->m_log.for_each_block(write_log); + m_log.for_each_block(write_log); - m_end_lsn = log_close(); -} - -/** Release the latches and blocks acquired by this mini-transaction */ -void -mtr_t::Command::release_all() -{ - m_impl->m_memo.for_each_block_in_reverse(CIterate()); - - /* Note that we have released the latches. */ - m_locks_released = 1; -} - -/** Release the latches acquired by this mini-transaction */ -void -mtr_t::Command::release_latches() -{ - m_impl->m_memo.for_each_block_in_reverse(CIterate()); - - /* Note that we have released the latches. */ - m_locks_released = 1; -} - -/** Release the blocks used in this mini-transaction */ -void -mtr_t::Command::release_blocks() -{ - m_impl->m_memo.for_each_block_in_reverse( - CIterate( - ReleaseBlocks(m_start_lsn, m_end_lsn, - m_impl->m_flush_observer))); -} - -/** Write the redo log record, add dirty pages to the flush list and release -the resources. */ -void -mtr_t::Command::execute() -{ - ut_ad(m_impl->m_log_mode != MTR_LOG_NONE); - - if (const ulint len = prepare_write()) { - finish_write(len); - } - - if (m_impl->m_made_dirty) { - log_flush_order_mutex_enter(); - } - - /* It is now safe to release the log mutex because the - flush_order mutex will ensure that we are the first one - to insert into the flush list. */ - log_mutex_exit(); - - m_impl->m_mtr->m_commit_lsn = m_end_lsn; - - release_blocks(); - - if (m_impl->m_made_dirty) { - log_flush_order_mutex_exit(); - } - - release_latches(); - - release_resources(); + m_commit_lsn = log_close(); + return start_lsn; } /** Release the free extents that was reserved using @@ -966,23 +827,22 @@ mtr_t::release_free_extents(ulint n_reserved) { fil_space_t* space; - ut_ad(m_impl.m_undo_space == NULL); + ut_ad(!m_undo_space); - if (m_impl.m_user_space != NULL) { + if (m_user_space) { - ut_ad(m_impl.m_user_space->id - == m_impl.m_user_space_id); - ut_ad(memo_contains(get_memo(), &m_impl.m_user_space->latch, + ut_ad(m_user_space->id == m_user_space_id); + ut_ad(memo_contains(get_memo(), &m_user_space->latch, MTR_MEMO_X_LOCK)); - space = m_impl.m_user_space; + space = m_user_space; } else { - ut_ad(m_impl.m_sys_space->id == TRX_SYS_SPACE); - ut_ad(memo_contains(get_memo(), &m_impl.m_sys_space->latch, + ut_ad(m_sys_space->id == TRX_SYS_SPACE); + ut_ad(memo_contains(get_memo(), &m_sys_space->latch, MTR_MEMO_X_LOCK)); - space = m_impl.m_sys_space; + space = m_sys_space; } space->release_free_extents(n_reserved); @@ -1084,10 +944,9 @@ struct FlaggedCheck { bool mtr_t::memo_contains_flagged(const void* ptr, ulint flags) const { - ut_ad(m_impl.m_magic_n == MTR_MAGIC_N); - ut_ad(is_committing() || is_active()); + ut_ad(is_active()); - return !m_impl.m_memo.for_each_block_in_reverse( + return !m_memo.for_each_block_in_reverse( CIterate(FlaggedCheck(ptr, flags))); } @@ -1103,7 +962,7 @@ mtr_t::memo_contains_page_flagged( ulint flags) const { Iterate iteration(FindPage(ptr, flags)); - return m_impl.m_memo.for_each_block_in_reverse(iteration) + return m_memo.for_each_block_in_reverse(iteration) ? NULL : iteration.functor.get_block(); } @@ -1126,7 +985,7 @@ void mtr_t::print() const { ib::info() << "Mini-transaction handle: memo size " - << m_impl.m_memo.size() << " bytes log size " + << m_memo.size() << " bytes log size " << get_log()->size() << " bytes"; }