From 38b6dc5a3d11d9917c02ad10293807b5a0a4f5a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 23 Apr 2019 17:25:07 +0300 Subject: [PATCH 1/4] Fix the linking of async_example --- client/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 6ef14ff7283..56e891ea284 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved. -# Copyright (c) 2008, 2018, MariaDB Corporation +# Copyright (c) 2008, 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 @@ -80,7 +80,7 @@ ENDIF(WIN32) ADD_EXECUTABLE(async_example async_example.c) TARGET_LINK_LIBRARIES(async_example mysqlclient) -SET_TARGET_PROPERTIES (mysqlcheck mysqldump mysqlimport mysql_upgrade mysqlshow mysqlslap mysql_plugin +SET_TARGET_PROPERTIES (mysqlcheck mysqldump mysqlimport mysql_upgrade mysqlshow mysqlslap mysql_plugin async_example PROPERTIES HAS_CXX TRUE) ADD_DEFINITIONS(-DHAVE_DLOPEN) From e5aa8ea52552759453fdd86eeab0007df1781b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 23 Apr 2019 17:56:43 +0300 Subject: [PATCH 2/4] MDEV-18139 ALTER IGNORE ... ADD FOREIGN KEY causes bogus error dict_create_foreign_constraints_low(): Tolerate the keywords IGNORE and ONLINE between the keywords ALTER and TABLE. We should really remove the hacky FOREIGN KEY constraint parser from InnoDB. --- mysql-test/suite/innodb/r/foreign_key.result | 20 +++++++++++++++++ mysql-test/suite/innodb/t/foreign_key.test | 22 +++++++++++++++++++ storage/innobase/dict/dict0dict.c | 23 +++++++++++++++----- storage/xtradb/dict/dict0dict.c | 23 +++++++++++++++----- 4 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 mysql-test/suite/innodb/r/foreign_key.result create mode 100644 mysql-test/suite/innodb/t/foreign_key.test diff --git a/mysql-test/suite/innodb/r/foreign_key.result b/mysql-test/suite/innodb/r/foreign_key.result new file mode 100644 index 00000000000..39f99513ee0 --- /dev/null +++ b/mysql-test/suite/innodb/r/foreign_key.result @@ -0,0 +1,20 @@ +# +# MDEV-18630 Conditional jump or move depends on uninitialised value +# in ib_push_warning / dict_create_foreign_constraints_low +# +CREATE TABLE t1 (a INT) ENGINE=InnoDB; +ALTER IGNORE TABLE t1 ADD FOREIGN KEY (a) REFERENCES t2 (b); +ERROR HY000: Can't create table 'test.#sql-temporary' (errno: 150) +SHOW WARNINGS; +Level Code Message +Warning 150 Alter table `test`.`t1` with foreign key constraint failed. Referenced table `test`.`t2` not found in the data dictionary near 'FOREIGN KEY (a) REFERENCES t2 (b)'. +Error 1005 Can't create table 'test.#sql-temporary' (errno: 150) +DROP TABLE t1; +# +# MDEV-18139 ALTER IGNORE ... ADD FOREIGN KEY causes bogus error +# +CREATE TABLE t1 (f1 INT, f2 INT, f3 INT, KEY(f1)) ENGINE=InnoDB; +CREATE TABLE t2 (f INT, KEY(f)) ENGINE=InnoDB; +ALTER TABLE t1 ADD FOREIGN KEY (f2) REFERENCES t2 (f); +ALTER IGNORE TABLE t1 ADD FOREIGN KEY (f3) REFERENCES t1 (f1); +DROP TABLE t1, t2; diff --git a/mysql-test/suite/innodb/t/foreign_key.test b/mysql-test/suite/innodb/t/foreign_key.test new file mode 100644 index 00000000000..281193d60ec --- /dev/null +++ b/mysql-test/suite/innodb/t/foreign_key.test @@ -0,0 +1,22 @@ +--source include/have_innodb.inc + +--echo # +--echo # MDEV-18630 Conditional jump or move depends on uninitialised value +--echo # in ib_push_warning / dict_create_foreign_constraints_low +--echo # +CREATE TABLE t1 (a INT) ENGINE=InnoDB; +--replace_regex /#sql-[0-9_a-f-]*/#sql-temporary/ +--error ER_CANT_CREATE_TABLE +ALTER IGNORE TABLE t1 ADD FOREIGN KEY (a) REFERENCES t2 (b); +--replace_regex /#sql-[0-9_a-f-]*/#sql-temporary/ +SHOW WARNINGS; +DROP TABLE t1; + +--echo # +--echo # MDEV-18139 ALTER IGNORE ... ADD FOREIGN KEY causes bogus error +--echo # +CREATE TABLE t1 (f1 INT, f2 INT, f3 INT, KEY(f1)) ENGINE=InnoDB; +CREATE TABLE t2 (f INT, KEY(f)) ENGINE=InnoDB; +ALTER TABLE t1 ADD FOREIGN KEY (f2) REFERENCES t2 (f); +ALTER IGNORE TABLE t1 ADD FOREIGN KEY (f3) REFERENCES t1 (f1); +DROP TABLE t1, t2; diff --git a/storage/innobase/dict/dict0dict.c b/storage/innobase/dict/dict0dict.c index 588c2968b6a..fcb9a48e4c0 100644 --- a/storage/innobase/dict/dict0dict.c +++ b/storage/innobase/dict/dict0dict.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 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 @@ -3787,6 +3788,9 @@ dict_create_foreign_constraints_low( } goto loop; + } else { + strncpy(create_name, name, sizeof create_name); + create_name[(sizeof create_name) - 1] = '\0'; } if (table == NULL) { @@ -3811,11 +3815,20 @@ dict_create_foreign_constraints_low( goto loop; } - ptr = dict_accept(cs, ptr, "TABLE", &success); - - if (!success) { - - goto loop; + orig = ptr; + for (;;) { + ptr = dict_accept(cs, ptr, "TABLE", &success); + if (success) { + break; + } + ptr = dict_accept(cs, ptr, "ONLINE", &success); + if (success) { + continue; + } + ptr = dict_accept(cs, ptr, "IGNORE", &success); + if (!success) { + goto loop; + } } /* We are doing an ALTER TABLE: scan the table name we are altering */ diff --git a/storage/xtradb/dict/dict0dict.c b/storage/xtradb/dict/dict0dict.c index 5da4509599e..12d3a600e54 100644 --- a/storage/xtradb/dict/dict0dict.c +++ b/storage/xtradb/dict/dict0dict.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 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 @@ -3921,6 +3922,9 @@ dict_create_foreign_constraints_low( } goto loop; + } else { + strncpy(create_name, name, sizeof create_name); + create_name[(sizeof create_name) - 1] = '\0'; } if (table == NULL) { @@ -3945,11 +3949,20 @@ dict_create_foreign_constraints_low( goto loop; } - ptr = dict_accept(cs, ptr, "TABLE", &success); - - if (!success) { - - goto loop; + orig = ptr; + for (;;) { + ptr = dict_accept(cs, ptr, "TABLE", &success); + if (success) { + break; + } + ptr = dict_accept(cs, ptr, "ONLINE", &success); + if (success) { + continue; + } + ptr = dict_accept(cs, ptr, "IGNORE", &success); + if (!success) { + goto loop; + } } /* We are doing an ALTER TABLE: scan the table name we are altering */ From cb8d888c42aa9504db2df686f0e963b99079e287 Mon Sep 17 00:00:00 2001 From: Sujatha Sivakumar Date: Wed, 24 Apr 2019 11:40:52 +0530 Subject: [PATCH 3/4] MDEV-17260: Memory leaks in mysqlbinlog Problem: ======== The mysqlbinlog tool is leaking memory, causing failures in various tests when compiling and testing with AddressSanitizer or LeakSanitizer like this: cmake -DCMAKE_BUILD_TYPE=Debug -DWITH_ASAN:BOOL=ON /path/to/source make -j$(nproc) cd mysql-test ASAN_OPTIONS=abort_on_error=1 ./mtr --parallel=auto rpl.rpl_row_mysqlbinlog CURRENT_TEST: rpl.rpl_row_mysqlbinlog Direct leak of 112 byte(s) in 1 object(s) allocated from: #0 0x4eff87 in __interceptor_malloc (/dev/shm/5.5/client/mysqlbinlog+0x4eff87) #1 0x60eaab in my_malloc /mariadb/5.5/mysys/my_malloc.c:41:10 #2 0x5300dd in Log_event::read_log_event(char const*, unsigned int, char const**, Format_description_log_event const*, char) /mariadb/5.5/sql/log_event.cc:1568: #3 0x564a9c in dump_remote_log_entries(st_print_event_info*, char const*) /mariadb/5.5/client/mysqlbinlog.cc:1978:17 Analysis: ======== 'mysqlbinlog' tool is being used to read binary log events from a remote server. While reading binary log, if a fake rotate event is found following actions are taken. If 'to-last-log' option is specified, then fake rotate event is processed. In the absence of 'to-last-log' skip the fake rotate event. In this skipped case the fake rotate event object is not getting cleaned up resulting in memory leak. Fix: === Cleanup the fake rotate event. This issues is already fixed in MariaDB 10.0.23 and higher versions as part of commit c3018b0ff4fb02c029787d03867adf0530607bab --- client/mysqlbinlog.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/mysqlbinlog.cc b/client/mysqlbinlog.cc index 250dc609891..1a11e3e697b 100644 --- a/client/mysqlbinlog.cc +++ b/client/mysqlbinlog.cc @@ -2020,6 +2020,7 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info, if ((rev->ident_len != logname_len) || memcmp(rev->new_log_ident, logname, logname_len)) { + delete ev; DBUG_RETURN(OK_CONTINUE); } /* @@ -2028,6 +2029,7 @@ static Exit_status dump_remote_log_entries(PRINT_EVENT_INFO *print_event_info, log. If we are running with to_last_remote_log, we print it, because it serves as a useful marker between binlogs then. */ + delete ev; continue; } len= 1; // fake Rotate, so don't increment old_off From d5da8ae04d57556f517c0f03afeafe73c6cc75d1 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Wed, 24 Apr 2019 12:31:24 +0530 Subject: [PATCH 4/4] MDEV-15772 Potential list overrun during XA recovery InnoDB could return the same list again and again if the buffer passed to trx_recover_for_mysql() is smaller than the number of transactions that InnoDB recovered in XA PREPARE state. We introduce the transaction state TRX_PREPARED_RECOVERED, which is like TRX_PREPARED, but will be set during trx_recover_for_mysql() so that each transaction will only be returned once. Because init_server_components() is invoking ha_recover() twice, we must reset the state of the transactions back to TRX_PREPARED after returning the complete list, so that repeated traversals will see the complete list again, instead of seeing an empty list. Without this tweak, the test main.tc_heuristic_recover would hang in MariaDB 10.1. --- mysql-test/suite/innodb/r/xa_debug.result | 310 ++++++++++++++++++++++ mysql-test/suite/innodb/t/xa_debug.test | 45 ++++ sql/handler.cc | 1 + storage/innobase/include/trx0sys.ic | 12 +- storage/innobase/include/trx0trx.h | 3 + storage/innobase/lock/lock0lock.c | 39 +-- storage/innobase/read/read0read.c | 36 ++- storage/innobase/trx/trx0roll.c | 2 + storage/innobase/trx/trx0sys.c | 3 +- storage/innobase/trx/trx0trx.c | 27 +- storage/xtradb/include/trx0trx.h | 3 + storage/xtradb/lock/lock0lock.c | 39 +-- storage/xtradb/trx/trx0roll.c | 2 + storage/xtradb/trx/trx0sys.c | 3 +- storage/xtradb/trx/trx0trx.c | 27 +- 15 files changed, 481 insertions(+), 71 deletions(-) create mode 100644 mysql-test/suite/innodb/r/xa_debug.result create mode 100644 mysql-test/suite/innodb/t/xa_debug.test diff --git a/mysql-test/suite/innodb/r/xa_debug.result b/mysql-test/suite/innodb/r/xa_debug.result new file mode 100644 index 00000000000..43a3b299468 --- /dev/null +++ b/mysql-test/suite/innodb/r/xa_debug.result @@ -0,0 +1,310 @@ +call mtr.add_suppression("Found 50 prepared XA transactions"); +create table t1 (a int) engine=innodb; +insert into t1 values(1); +xa start 'test50'; +insert into t1 values(1); +xa end 'test50'; +xa prepare 'test50'; +xa start 'test49'; +insert into t1 values(1); +xa end 'test49'; +xa prepare 'test49'; +xa start 'test48'; +insert into t1 values(1); +xa end 'test48'; +xa prepare 'test48'; +xa start 'test47'; +insert into t1 values(1); +xa end 'test47'; +xa prepare 'test47'; +xa start 'test46'; +insert into t1 values(1); +xa end 'test46'; +xa prepare 'test46'; +xa start 'test45'; +insert into t1 values(1); +xa end 'test45'; +xa prepare 'test45'; +xa start 'test44'; +insert into t1 values(1); +xa end 'test44'; +xa prepare 'test44'; +xa start 'test43'; +insert into t1 values(1); +xa end 'test43'; +xa prepare 'test43'; +xa start 'test42'; +insert into t1 values(1); +xa end 'test42'; +xa prepare 'test42'; +xa start 'test41'; +insert into t1 values(1); +xa end 'test41'; +xa prepare 'test41'; +xa start 'test40'; +insert into t1 values(1); +xa end 'test40'; +xa prepare 'test40'; +xa start 'test39'; +insert into t1 values(1); +xa end 'test39'; +xa prepare 'test39'; +xa start 'test38'; +insert into t1 values(1); +xa end 'test38'; +xa prepare 'test38'; +xa start 'test37'; +insert into t1 values(1); +xa end 'test37'; +xa prepare 'test37'; +xa start 'test36'; +insert into t1 values(1); +xa end 'test36'; +xa prepare 'test36'; +xa start 'test35'; +insert into t1 values(1); +xa end 'test35'; +xa prepare 'test35'; +xa start 'test34'; +insert into t1 values(1); +xa end 'test34'; +xa prepare 'test34'; +xa start 'test33'; +insert into t1 values(1); +xa end 'test33'; +xa prepare 'test33'; +xa start 'test32'; +insert into t1 values(1); +xa end 'test32'; +xa prepare 'test32'; +xa start 'test31'; +insert into t1 values(1); +xa end 'test31'; +xa prepare 'test31'; +xa start 'test30'; +insert into t1 values(1); +xa end 'test30'; +xa prepare 'test30'; +xa start 'test29'; +insert into t1 values(1); +xa end 'test29'; +xa prepare 'test29'; +xa start 'test28'; +insert into t1 values(1); +xa end 'test28'; +xa prepare 'test28'; +xa start 'test27'; +insert into t1 values(1); +xa end 'test27'; +xa prepare 'test27'; +xa start 'test26'; +insert into t1 values(1); +xa end 'test26'; +xa prepare 'test26'; +xa start 'test25'; +insert into t1 values(1); +xa end 'test25'; +xa prepare 'test25'; +xa start 'test24'; +insert into t1 values(1); +xa end 'test24'; +xa prepare 'test24'; +xa start 'test23'; +insert into t1 values(1); +xa end 'test23'; +xa prepare 'test23'; +xa start 'test22'; +insert into t1 values(1); +xa end 'test22'; +xa prepare 'test22'; +xa start 'test21'; +insert into t1 values(1); +xa end 'test21'; +xa prepare 'test21'; +xa start 'test20'; +insert into t1 values(1); +xa end 'test20'; +xa prepare 'test20'; +xa start 'test19'; +insert into t1 values(1); +xa end 'test19'; +xa prepare 'test19'; +xa start 'test18'; +insert into t1 values(1); +xa end 'test18'; +xa prepare 'test18'; +xa start 'test17'; +insert into t1 values(1); +xa end 'test17'; +xa prepare 'test17'; +xa start 'test16'; +insert into t1 values(1); +xa end 'test16'; +xa prepare 'test16'; +xa start 'test15'; +insert into t1 values(1); +xa end 'test15'; +xa prepare 'test15'; +xa start 'test14'; +insert into t1 values(1); +xa end 'test14'; +xa prepare 'test14'; +xa start 'test13'; +insert into t1 values(1); +xa end 'test13'; +xa prepare 'test13'; +xa start 'test12'; +insert into t1 values(1); +xa end 'test12'; +xa prepare 'test12'; +xa start 'test11'; +insert into t1 values(1); +xa end 'test11'; +xa prepare 'test11'; +xa start 'test10'; +insert into t1 values(1); +xa end 'test10'; +xa prepare 'test10'; +xa start 'test9'; +insert into t1 values(1); +xa end 'test9'; +xa prepare 'test9'; +xa start 'test8'; +insert into t1 values(1); +xa end 'test8'; +xa prepare 'test8'; +xa start 'test7'; +insert into t1 values(1); +xa end 'test7'; +xa prepare 'test7'; +xa start 'test6'; +insert into t1 values(1); +xa end 'test6'; +xa prepare 'test6'; +xa start 'test5'; +insert into t1 values(1); +xa end 'test5'; +xa prepare 'test5'; +xa start 'test4'; +insert into t1 values(1); +xa end 'test4'; +xa prepare 'test4'; +xa start 'test3'; +insert into t1 values(1); +xa end 'test3'; +xa prepare 'test3'; +xa start 'test2'; +insert into t1 values(1); +xa end 'test2'; +xa prepare 'test2'; +xa start 'test1'; +insert into t1 values(1); +xa end 'test1'; +xa prepare 'test1'; +xa recover; +formatID gtrid_length bqual_length data +1 5 0 test1 +1 5 0 test2 +1 5 0 test3 +1 5 0 test4 +1 5 0 test5 +1 5 0 test6 +1 5 0 test7 +1 5 0 test8 +1 5 0 test9 +1 6 0 test10 +1 6 0 test11 +1 6 0 test12 +1 6 0 test13 +1 6 0 test14 +1 6 0 test15 +1 6 0 test16 +1 6 0 test17 +1 6 0 test18 +1 6 0 test19 +1 6 0 test20 +1 6 0 test21 +1 6 0 test22 +1 6 0 test23 +1 6 0 test24 +1 6 0 test25 +1 6 0 test26 +1 6 0 test27 +1 6 0 test28 +1 6 0 test29 +1 6 0 test30 +1 6 0 test31 +1 6 0 test32 +1 6 0 test33 +1 6 0 test34 +1 6 0 test35 +1 6 0 test36 +1 6 0 test37 +1 6 0 test38 +1 6 0 test39 +1 6 0 test40 +1 6 0 test41 +1 6 0 test42 +1 6 0 test43 +1 6 0 test44 +1 6 0 test45 +1 6 0 test46 +1 6 0 test47 +1 6 0 test48 +1 6 0 test49 +1 6 0 test50 +xa recover; +formatID gtrid_length bqual_length data +1 5 0 test1 +1 5 0 test2 +1 5 0 test3 +1 5 0 test4 +1 5 0 test5 +1 5 0 test6 +1 5 0 test7 +1 5 0 test8 +1 5 0 test9 +1 6 0 test10 +1 6 0 test11 +1 6 0 test12 +1 6 0 test13 +1 6 0 test14 +1 6 0 test15 +1 6 0 test16 +1 6 0 test17 +1 6 0 test18 +1 6 0 test19 +1 6 0 test20 +1 6 0 test21 +1 6 0 test22 +1 6 0 test23 +1 6 0 test24 +1 6 0 test25 +1 6 0 test26 +1 6 0 test27 +1 6 0 test28 +1 6 0 test29 +1 6 0 test30 +1 6 0 test31 +1 6 0 test32 +1 6 0 test33 +1 6 0 test34 +1 6 0 test35 +1 6 0 test36 +1 6 0 test37 +1 6 0 test38 +1 6 0 test39 +1 6 0 test40 +1 6 0 test41 +1 6 0 test42 +1 6 0 test43 +1 6 0 test44 +1 6 0 test45 +1 6 0 test46 +1 6 0 test47 +1 6 0 test48 +1 6 0 test49 +1 6 0 test50 +xa recover; +formatID gtrid_length bqual_length data +drop table t1; diff --git a/mysql-test/suite/innodb/t/xa_debug.test b/mysql-test/suite/innodb/t/xa_debug.test new file mode 100644 index 00000000000..5724891bb65 --- /dev/null +++ b/mysql-test/suite/innodb/t/xa_debug.test @@ -0,0 +1,45 @@ +-- source include/have_innodb.inc +-- source include/have_debug.inc +-- source include/not_embedded.inc + +call mtr.add_suppression("Found 50 prepared XA transactions"); +create table t1 (a int) engine=innodb; +insert into t1 values(1); + +let $trial = 50; +while ($trial) +{ +--connect (con$trial, localhost, root,,) +let $st_pre = `select concat('test', $trial)`; +eval xa start '$st_pre'; +insert into t1 values(1); +eval xa end '$st_pre'; +eval xa prepare '$st_pre'; +dec $trial; +} + +connection default; +# Kill and restart the server. +-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- shutdown_server 0 +-- source include/wait_until_disconnected.inc + +-- exec echo "restart:--debug_dbug=+d,min_xa_len" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect +-- enable_reconnect +-- source include/wait_until_connected_again.inc +-- disable_reconnect +--sorted_result +xa recover; +--sorted_result +xa recover; +--disable_query_log +let $trial = 50; +while ($trial) +{ +let $st_pre = `select concat('test', $trial)`; +eval xa commit '$st_pre'; +dec $trial; +} +--enable_query_log +xa recover; +drop table t1; diff --git a/sql/handler.cc b/sql/handler.cc index ab4d9fd37c9..8c6270a042e 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1740,6 +1740,7 @@ int ha_recover(HASH *commit_list) for (info.len= MAX_XID_LIST_SIZE ; info.list==0 && info.len > MIN_XID_LIST_SIZE; info.len/=2) { + DBUG_EXECUTE_IF("min_xa_len", info.len = 16;); info.list=(XID *)my_malloc(info.len*sizeof(XID), MYF(0)); } if (!info.list) diff --git a/storage/innobase/include/trx0sys.ic b/storage/innobase/include/trx0sys.ic index 4e7c0ee1f3a..5d33f348822 100644 --- a/storage/innobase/include/trx0sys.ic +++ b/storage/innobase/include/trx0sys.ic @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2011, 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 @@ -353,10 +354,13 @@ trx_is_active( } trx = trx_get_on_id(trx_id); - if (trx && (trx->conc_state == TRX_ACTIVE - || trx->conc_state == TRX_PREPARED)) { - - return(TRUE); + if (trx) { + switch (trx->conc_state) { + case TRX_ACTIVE: + case TRX_PREPARED: + case TRX_PREPARED_RECOVERED: + return(TRUE); + } } return(FALSE); diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index 4ade245f03e..9cbc7987eff 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. 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 @@ -741,6 +742,8 @@ struct trx_struct{ #define TRX_ACTIVE 1 #define TRX_COMMITTED_IN_MEMORY 2 #define TRX_PREPARED 3 /* Support for 2PC/XA */ +#define TRX_PREPARED_RECOVERED 4 /* XA PREPARE transaction that + was returned to ha_recover() */ /* Transaction execution states when trx->conc_state == TRX_ACTIVE */ #define TRX_QUE_RUNNING 0 /* transaction is running */ diff --git a/storage/innobase/lock/lock0lock.c b/storage/innobase/lock/lock0lock.c index 6a3ca383ade..28ecb30e2b7 100644 --- a/storage/innobase/lock/lock0lock.c +++ b/storage/innobase/lock/lock0lock.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 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 @@ -4782,6 +4783,22 @@ print_rec: } #ifdef UNIV_DEBUG +/** Validate the state of a transaction that holds locks */ +static void lock_trx_state_validate(const trx_t* trx) +{ + switch (trx->conc_state) { + case TRX_NOT_STARTED: + break; + case TRX_ACTIVE: + case TRX_PREPARED: + case TRX_PREPARED_RECOVERED: + case TRX_COMMITTED_IN_MEMORY: + return; + + } + ut_ad(!"wrong state"); +} + /*********************************************************************//** Validates the lock queue on a table. @return TRUE if ok */ @@ -4798,9 +4815,7 @@ lock_table_queue_validate( lock = UT_LIST_GET_FIRST(table->locks); while (lock) { - ut_a(((lock->trx)->conc_state == TRX_ACTIVE) - || ((lock->trx)->conc_state == TRX_PREPARED) - || ((lock->trx)->conc_state == TRX_COMMITTED_IN_MEMORY)); + ut_d(lock_trx_state_validate(lock->trx)); if (!lock_get_wait(lock)) { @@ -4848,15 +4863,7 @@ lock_rec_queue_validate( lock = lock_rec_get_first(block, heap_no); while (lock) { - switch(lock->trx->conc_state) { - case TRX_ACTIVE: - case TRX_PREPARED: - case TRX_COMMITTED_IN_MEMORY: - break; - default: - ut_error; - } - + ut_d(lock_trx_state_validate(lock->trx)); ut_a(trx_in_trx_list(lock->trx)); if (lock_get_wait(lock)) { @@ -4935,9 +4942,7 @@ lock_rec_queue_validate( lock = lock_rec_get_first(block, heap_no); while (lock) { - ut_a(lock->trx->conc_state == TRX_ACTIVE - || lock->trx->conc_state == TRX_PREPARED - || lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY); + ut_d(lock_trx_state_validate(lock->trx)); ut_a(trx_in_trx_list(lock->trx)); if (index) { @@ -5014,10 +5019,8 @@ loop: } } + ut_d(lock_trx_state_validate(lock->trx)); ut_a(trx_in_trx_list(lock->trx)); - ut_a(lock->trx->conc_state == TRX_ACTIVE - || lock->trx->conc_state == TRX_PREPARED - || lock->trx->conc_state == TRX_COMMITTED_IN_MEMORY); # ifdef UNIV_SYNC_DEBUG /* Only validate the record queues when this thread is not diff --git a/storage/innobase/read/read0read.c b/storage/innobase/read/read0read.c index b87a3715f65..1de78aae708 100644 --- a/storage/innobase/read/read0read.c +++ b/storage/innobase/read/read0read.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1997, 2009, Innobase Oy. 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 @@ -272,15 +273,19 @@ read_view_open_now( view->low_limit_id = view->low_limit_no; n = 0; - trx = UT_LIST_GET_FIRST(trx_sys->trx_list); /* No active transaction should be visible, except cr_trx */ - while (trx) { - if (trx->id != cr_trx_id - && (trx->conc_state == TRX_ACTIVE - || trx->conc_state == TRX_PREPARED)) { + for (trx = UT_LIST_GET_FIRST(trx_sys->trx_list); trx; + trx = UT_LIST_GET_NEXT(trx_list, trx)) { + if (trx->id == cr_trx_id) { + continue; + } + switch (trx->conc_state) { + case TRX_ACTIVE: + case TRX_PREPARED: + case TRX_PREPARED_RECOVERED: read_view_set_nth_trx_id(view, n, trx->id); n++; @@ -296,8 +301,6 @@ read_view_open_now( view->low_limit_no = trx->no; } } - - trx = UT_LIST_GET_NEXT(trx_list, trx); } view->n_trx_ids = n; @@ -437,18 +440,15 @@ read_cursor_view_create_for_mysql( view->low_limit_id = view->low_limit_no; n = 0; - trx = UT_LIST_GET_FIRST(trx_sys->trx_list); /* No active transaction should be visible */ - - while (trx) { - - if (trx->conc_state == TRX_ACTIVE - || trx->conc_state == TRX_PREPARED) { - - read_view_set_nth_trx_id(view, n, trx->id); - - n++; + for (trx = UT_LIST_GET_FIRST(trx_sys->trx_list); trx; + trx = UT_LIST_GET_NEXT(trx_list, trx)) { + switch (trx->conc_state) { + case TRX_ACTIVE: + case TRX_PREPARED: + case TRX_PREPARED_RECOVERED: + read_view_set_nth_trx_id(view, n++, trx->id); /* NOTE that a transaction whose trx number is < trx_sys->max_trx_id can still be active, if it is @@ -461,8 +461,6 @@ read_cursor_view_create_for_mysql( view->low_limit_no = trx->no; } } - - trx = UT_LIST_GET_NEXT(trx_list, trx); } view->n_trx_ids = n; diff --git a/storage/innobase/trx/trx0roll.c b/storage/innobase/trx/trx0roll.c index 638e06191bc..a6fa9dc73a9 100644 --- a/storage/innobase/trx/trx0roll.c +++ b/storage/innobase/trx/trx0roll.c @@ -2,6 +2,7 @@ Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 2017, 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 @@ -564,6 +565,7 @@ loop: switch (trx->conc_state) { case TRX_NOT_STARTED: case TRX_PREPARED: + case TRX_PREPARED_RECOVERED: continue; case TRX_COMMITTED_IN_MEMORY: diff --git a/storage/innobase/trx/trx0sys.c b/storage/innobase/trx/trx0sys.c index 256e22d1b50..8fcc935e673 100644 --- a/storage/innobase/trx/trx0sys.c +++ b/storage/innobase/trx/trx0sys.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2013, 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 @@ -1031,7 +1032,7 @@ trx_sys_init_at_db_start(void) trx = UT_LIST_GET_FIRST(trx_sys->trx_list); for (;;) { - + ut_ad(trx->conc_state != TRX_PREPARED_RECOVERED); if (trx->conc_state != TRX_PREPARED) { rows_to_undo += trx->undo_no; } diff --git a/storage/innobase/trx/trx0trx.c b/storage/innobase/trx/trx0trx.c index 151e70013f9..7a53a78f2d9 100644 --- a/storage/innobase/trx/trx0trx.c +++ b/storage/innobase/trx/trx0trx.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2011, Innobase Oy. 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 @@ -348,7 +349,8 @@ trx_free_prepared( trx_t* trx) /*!< in, own: trx object */ { ut_ad(mutex_own(&kernel_mutex)); - ut_a(trx->conc_state == TRX_PREPARED); + ut_a(trx->conc_state == TRX_PREPARED + || trx->conc_state == TRX_PREPARED_RECOVERED); ut_a(trx->magic_n == TRX_MAGIC_N); /* Prepared transactions are sort of active; they allow @@ -932,10 +934,11 @@ trx_commit_off_kernel( lsn = 0; } - ut_ad(trx->conc_state == TRX_ACTIVE || trx->conc_state == TRX_PREPARED); + ut_ad(trx->conc_state == TRX_ACTIVE || trx->conc_state == TRX_PREPARED + || trx->conc_state == TRX_PREPARED_RECOVERED); ut_ad(mutex_own(&kernel_mutex)); - if (UNIV_UNLIKELY(trx->conc_state == TRX_PREPARED)) { + if (UNIV_UNLIKELY(trx->conc_state != TRX_ACTIVE)) { ut_a(trx_n_prepared > 0); trx_n_prepared--; } @@ -2072,6 +2075,7 @@ trx_recover_for_mysql( while (trx) { if (trx->conc_state == TRX_PREPARED) { + trx->conc_state = TRX_PREPARED_RECOVERED; xid_list[count] = trx->xid; if (count == 0) { @@ -2096,13 +2100,25 @@ trx_recover_for_mysql( count++; if (count == len) { - break; + goto partial; } } trx = UT_LIST_GET_NEXT(trx_list, trx); } + /* After returning the full list, reset the state, because + init_server_components() wants to recover the collection of + transactions twice, by first calling tc_log->open() and then + ha_recover() directly. */ + for (trx = UT_LIST_GET_FIRST(trx_sys->trx_list); trx; + trx = UT_LIST_GET_NEXT(trx_list, trx)) { + if (trx->conc_state == TRX_PREPARED_RECOVERED) { + trx->conc_state = TRX_PREPARED; + } + } + +partial: mutex_exit(&kernel_mutex); if (count > 0){ @@ -2144,7 +2160,8 @@ trx_get_trx_by_xid( the same */ if (trx->is_recovered - && trx->conc_state == TRX_PREPARED + && (trx->conc_state == TRX_PREPARED + || trx->conc_state == TRX_PREPARED_RECOVERED) && xid->gtrid_length == trx->xid.gtrid_length && xid->bqual_length == trx->xid.bqual_length && memcmp(xid->data, trx->xid.data, diff --git a/storage/xtradb/include/trx0trx.h b/storage/xtradb/include/trx0trx.h index fcfcb14ae3b..c3844cce84e 100644 --- a/storage/xtradb/include/trx0trx.h +++ b/storage/xtradb/include/trx0trx.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2010, Innobase Oy. 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 @@ -802,6 +803,8 @@ struct trx_struct{ #define TRX_ACTIVE 1 #define TRX_COMMITTED_IN_MEMORY 2 #define TRX_PREPARED 3 /* Support for 2PC/XA */ +#define TRX_PREPARED_RECOVERED 4 /* XA PREPARE transaction that + was returned to ha_recover() */ /* Transaction execution states when trx->conc_state == TRX_ACTIVE */ #define TRX_QUE_RUNNING 0 /* transaction is running */ diff --git a/storage/xtradb/lock/lock0lock.c b/storage/xtradb/lock/lock0lock.c index 6e574eb282e..d1be28c7f30 100644 --- a/storage/xtradb/lock/lock0lock.c +++ b/storage/xtradb/lock/lock0lock.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 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 @@ -4811,6 +4812,22 @@ print_rec: } #ifdef UNIV_DEBUG +/** Validate the state of a transaction that holds locks */ +static void lock_trx_state_validate(const trx_t* trx) +{ + switch (trx->state) { + case TRX_NOT_STARTED: + break; + case TRX_ACTIVE: + case TRX_PREPARED: + case TRX_PREPARED_RECOVERED: + case TRX_COMMITTED_IN_MEMORY: + return; + + } + ut_ad(!"wrong state"); +} + /*********************************************************************//** Validates the lock queue on a table. @return TRUE if ok */ @@ -4827,9 +4844,7 @@ lock_table_queue_validate( lock = UT_LIST_GET_FIRST(table->locks); while (lock) { - ut_a(((lock->trx)->state == TRX_ACTIVE) - || ((lock->trx)->state == TRX_PREPARED) - || ((lock->trx)->state == TRX_COMMITTED_IN_MEMORY)); + ut_d(lock_trx_state_validate(lock->trx)); if (!lock_get_wait(lock)) { @@ -4877,15 +4892,7 @@ lock_rec_queue_validate( lock = lock_rec_get_first(block, heap_no); while (lock) { - switch(lock->trx->state) { - case TRX_ACTIVE: - case TRX_PREPARED: - case TRX_COMMITTED_IN_MEMORY: - break; - default: - ut_error; - } - + ut_d(lock_trx_state_validate(lock->trx)); ut_a(trx_in_trx_list(lock->trx)); if (lock_get_wait(lock)) { @@ -4964,9 +4971,7 @@ lock_rec_queue_validate( lock = lock_rec_get_first(block, heap_no); while (lock) { - ut_a(lock->trx->state == TRX_ACTIVE - || lock->trx->state == TRX_PREPARED - || lock->trx->state == TRX_COMMITTED_IN_MEMORY); + ut_d(lock_trx_state_validate(lock->trx)); ut_a(trx_in_trx_list(lock->trx)); if (index) { @@ -5043,10 +5048,8 @@ loop: } } + ut_d(lock_trx_state_validate(lock->trx)); ut_a(trx_in_trx_list(lock->trx)); - ut_a(lock->trx->state == TRX_ACTIVE - || lock->trx->state == TRX_PREPARED - || lock->trx->state == TRX_COMMITTED_IN_MEMORY); # ifdef UNIV_SYNC_DEBUG /* Only validate the record queues when this thread is not diff --git a/storage/xtradb/trx/trx0roll.c b/storage/xtradb/trx/trx0roll.c index 8c0438ab975..e715780f165 100644 --- a/storage/xtradb/trx/trx0roll.c +++ b/storage/xtradb/trx/trx0roll.c @@ -2,6 +2,7 @@ Copyright (c) 1996, 2009, Innobase Oy. All Rights Reserved. Copyright (c) 2017, 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 @@ -564,6 +565,7 @@ loop: switch (trx->state) { case TRX_NOT_STARTED: case TRX_PREPARED: + case TRX_PREPARED_RECOVERED: continue; case TRX_COMMITTED_IN_MEMORY: diff --git a/storage/xtradb/trx/trx0sys.c b/storage/xtradb/trx/trx0sys.c index f801331dbf0..ea3196d182c 100644 --- a/storage/xtradb/trx/trx0sys.c +++ b/storage/xtradb/trx/trx0sys.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2013, 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 @@ -1355,7 +1356,7 @@ trx_sys_init_at_db_start(void) trx = UT_LIST_GET_FIRST(trx_sys->trx_list); for (;;) { - + ut_ad(trx->state != TRX_PREPARED_RECOVERED); if (trx->state != TRX_PREPARED) { rows_to_undo += trx->undo_no; } diff --git a/storage/xtradb/trx/trx0trx.c b/storage/xtradb/trx/trx0trx.c index dd7b8bfdd1d..da9de9f004b 100644 --- a/storage/xtradb/trx/trx0trx.c +++ b/storage/xtradb/trx/trx0trx.c @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2011, Innobase Oy. 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 @@ -481,7 +482,8 @@ trx_free_prepared( trx_t* trx) /*!< in, own: trx object */ { ut_ad(mutex_own(&kernel_mutex)); - ut_a(trx->state == TRX_PREPARED); + ut_a(trx->state == TRX_PREPARED + || trx->state == TRX_PREPARED_RECOVERED); ut_a(trx->magic_n == TRX_MAGIC_N); /* Prepared transactions are sort of active; they allow @@ -1148,10 +1150,11 @@ trx_commit_off_kernel( lsn = 0; } - ut_ad(trx->state == TRX_ACTIVE || trx->state == TRX_PREPARED); + ut_ad(trx->state == TRX_ACTIVE || trx->state == TRX_PREPARED + || trx->state == TRX_PREPARED_RECOVERED); ut_ad(mutex_own(&kernel_mutex)); - if (UNIV_UNLIKELY(trx->state == TRX_PREPARED)) { + if (UNIV_UNLIKELY(trx->state != TRX_ACTIVE)) { ut_a(trx_n_prepared > 0); trx_n_prepared--; } @@ -2359,6 +2362,7 @@ trx_recover_for_mysql( while (trx) { if (trx->state == TRX_PREPARED) { + trx->state = TRX_PREPARED_RECOVERED; xid_list[count] = trx->xid; if (count == 0) { @@ -2383,13 +2387,25 @@ trx_recover_for_mysql( count++; if (count == len) { - break; + goto partial; } } trx = UT_LIST_GET_NEXT(trx_list, trx); } + /* After returning the full list, reset the state, because + init_server_components() wants to recover the collection of + transactions twice, by first calling tc_log->open() and then + ha_recover() directly. */ + for (trx = UT_LIST_GET_FIRST(trx_sys->trx_list); trx; + trx = UT_LIST_GET_NEXT(trx_list, trx)) { + if (trx->state == TRX_PREPARED_RECOVERED) { + trx->state = TRX_PREPARED; + } + } + +partial: mutex_exit(&kernel_mutex); if (count > 0){ @@ -2431,7 +2447,8 @@ trx_get_trx_by_xid( the same */ if (trx->is_recovered - && trx->state == TRX_PREPARED + && (trx->state == TRX_PREPARED + || trx->state == TRX_PREPARED_RECOVERED) && xid->gtrid_length == trx->xid.gtrid_length && xid->bqual_length == trx->xid.bqual_length && memcmp(xid->data, trx->xid.data,