From 2820ad1c226bfd3be111eecb8eedc41b8533cc06 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 22 Jul 2021 17:07:17 -0700 Subject: [PATCH 01/12] MDEV-26202 Unexpected failure with query using indirectly a recursive CTE twice This bug was fixed by the patch for bug MDEV-26025. Only a new test case is added here. --- mysql-test/r/cte_recursive.result | 22 ++++++++++++++++++++++ mysql-test/t/cte_recursive.test | 14 ++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/mysql-test/r/cte_recursive.result b/mysql-test/r/cte_recursive.result index 1b1fd8b908d..168777f9402 100644 --- a/mysql-test/r/cte_recursive.result +++ b/mysql-test/r/cte_recursive.result @@ -4519,5 +4519,27 @@ drop procedure sp1; drop procedure sp2; drop table t1; # +# MDEV-26202: Recursive CTE used indirectly twice +# (fixed by the patch forMDEV-26025) +# +with recursive +rcte as ( SELECT 1 AS a +UNION ALL +SELECT cast(a + 1 as unsigned int) FROM rcte WHERE a < 3), +cte1 AS (SELECT a FROM rcte), +cte2 AS (SELECT a FROM cte1), +cte3 AS ( SELECT a FROM cte2) +SELECT * FROM cte2, cte3; +a a +1 1 +2 1 +3 1 +1 2 +2 2 +3 2 +1 3 +2 3 +3 3 +# # End of 10.2 tests # diff --git a/mysql-test/t/cte_recursive.test b/mysql-test/t/cte_recursive.test index cdd3a075e21..3c845f9d0e3 100644 --- a/mysql-test/t/cte_recursive.test +++ b/mysql-test/t/cte_recursive.test @@ -2884,6 +2884,20 @@ drop procedure sp2; drop table t1; +--echo # +--echo # MDEV-26202: Recursive CTE used indirectly twice +--echo # (fixed by the patch forMDEV-26025) +--echo # + +with recursive + rcte as ( SELECT 1 AS a + UNION ALL + SELECT cast(a + 1 as unsigned int) FROM rcte WHERE a < 3), + cte1 AS (SELECT a FROM rcte), + cte2 AS (SELECT a FROM cte1), + cte3 AS ( SELECT a FROM cte2) +SELECT * FROM cte2, cte3; + --echo # --echo # End of 10.2 tests --echo # From 4c4237e63fb9d766a99fb1d45049d6955fd7f8f2 Mon Sep 17 00:00:00 2001 From: Julius Goryavsky Date: Fri, 23 Jul 2021 08:21:28 +0200 Subject: [PATCH 02/12] MDEV-26080 fixup: fixed .result file for galera_roles test (one word must be enclosed in single quotes). --- mysql-test/suite/galera/r/galera_roles.result | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/suite/galera/r/galera_roles.result b/mysql-test/suite/galera/r/galera_roles.result index bef89acfc92..d3654e4d0de 100644 --- a/mysql-test/suite/galera/r/galera_roles.result +++ b/mysql-test/suite/galera/r/galera_roles.result @@ -26,7 +26,7 @@ connect foo_node_2,127.0.0.1,foo,,test,$port_2,; connection foo_node_1; SHOW GRANTS; Grants for foo@localhost -GRANT role1 TO 'foo'@'localhost' +GRANT 'role1' TO 'foo'@'localhost' GRANT USAGE ON *.* TO 'foo'@'localhost' FLUSH TABLES; ERROR 42000: Access denied; you need (at least one of) the RELOAD privilege(s) for this operation @@ -62,7 +62,7 @@ pr1 connection foo_node_2; SHOW GRANTS; Grants for foo@localhost -GRANT role1 TO 'foo'@'localhost' +GRANT 'role1' TO 'foo'@'localhost' GRANT USAGE ON *.* TO 'foo'@'localhost' FLUSH TABLES; ERROR 42000: Access denied; you need (at least one of) the RELOAD privilege(s) for this operation From 173e562dc2bd339de32d17de73b720e7ca863ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 23 Jul 2021 17:20:57 +0300 Subject: [PATCH 03/12] MDEV-26228 ASAN heap-use-after-free with ON UPDATE CASCADE In commit 83d2e0841ee30727c609f23957cc592399a3aca4 (MDEV-24041) we failed to notice that in addition to the bug with DELETE and ON DELETE CASCADE, there is another bug with UPDATE and ON UPDATE CASCADE. row_ins_foreign_fill_virtual(): Use the correct memory heap for everything that will be reachable from the cascade->update that we return to the caller. Note: It is correct to use the shorter-lived cascade->heap for rec_get_offsets(), because that memory will be abandoned when row_ins_foreign_fill_virtual() returns. --- mysql-test/suite/gcol/r/innodb_virtual_fk.result | 3 +++ mysql-test/suite/gcol/t/innodb_virtual_fk.test | 3 +++ storage/innobase/row/row0ins.cc | 6 +++--- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/mysql-test/suite/gcol/r/innodb_virtual_fk.result b/mysql-test/suite/gcol/r/innodb_virtual_fk.result index 252274f3e0a..367ed1223f7 100644 --- a/mysql-test/suite/gcol/r/innodb_virtual_fk.result +++ b/mysql-test/suite/gcol/r/innodb_virtual_fk.result @@ -809,15 +809,18 @@ generated_email_id int as (email_id), PRIMARY KEY (id), KEY mautic_generated_sent_date_email_id (generated_email_id), FOREIGN KEY (email_id) REFERENCES emails (id) ON DELETE SET NULL +ON UPDATE CASCADE ) ENGINE=InnoDB; CREATE TABLE emails_metadata ( email_id int, PRIMARY KEY (email_id), CONSTRAINT FK FOREIGN KEY (email_id) REFERENCES emails (id) ON DELETE CASCADE +ON UPDATE CASCADE ) ENGINE=InnoDB; INSERT INTO emails VALUES (1); INSERT INTO email_stats (id, email_id, date_sent) VALUES (1,1,'Jan'); INSERT INTO emails_metadata VALUES (1); +UPDATE emails SET id=2; DELETE FROM emails; DROP TABLE email_stats; DROP TABLE emails_metadata; diff --git a/mysql-test/suite/gcol/t/innodb_virtual_fk.test b/mysql-test/suite/gcol/t/innodb_virtual_fk.test index 24b6a4631e6..c99259531b3 100644 --- a/mysql-test/suite/gcol/t/innodb_virtual_fk.test +++ b/mysql-test/suite/gcol/t/innodb_virtual_fk.test @@ -670,6 +670,7 @@ CREATE TABLE email_stats ( PRIMARY KEY (id), KEY mautic_generated_sent_date_email_id (generated_email_id), FOREIGN KEY (email_id) REFERENCES emails (id) ON DELETE SET NULL + ON UPDATE CASCADE ) ENGINE=InnoDB; @@ -677,6 +678,7 @@ CREATE TABLE emails_metadata ( email_id int, PRIMARY KEY (email_id), CONSTRAINT FK FOREIGN KEY (email_id) REFERENCES emails (id) ON DELETE CASCADE + ON UPDATE CASCADE ) ENGINE=InnoDB; @@ -684,6 +686,7 @@ INSERT INTO emails VALUES (1); INSERT INTO email_stats (id, email_id, date_sent) VALUES (1,1,'Jan'); INSERT INTO emails_metadata VALUES (1); +UPDATE emails SET id=2; DELETE FROM emails; DROP TABLE email_stats; diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index 4dc9c66a536..929d3683ce6 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2020, MariaDB Corporation. +Copyright (c) 2016, 2021, 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 @@ -969,8 +969,8 @@ row_ins_foreign_fill_virtual( upd_field = update->fields + n_diff; upd_field->old_v_val = static_cast( - mem_heap_alloc(cascade->heap, - sizeof *upd_field->old_v_val)); + mem_heap_alloc(update->heap, + sizeof *upd_field->old_v_val)); dfield_copy(upd_field->old_v_val, vfield); From f29b3d6d8239d54e08868912093b16187086e133 Mon Sep 17 00:00:00 2001 From: Elena Stepanova Date: Sat, 24 Jul 2021 21:32:52 +0300 Subject: [PATCH 04/12] Some tests can take very long time when run with valgrind Set tests to non-valgrind: oqgraph.social encryption.innodb-page_encryption binlog_encryption.encrypted_master innodb.innodb-page_compression_lz4 main.lock_multi_bug38499 main.lock_multi_bug38691 --- mysql-test/suite/binlog_encryption/encrypted_master.test | 3 +++ mysql-test/suite/encryption/t/innodb-page_encryption.test | 3 +++ mysql-test/suite/innodb/t/innodb-page_compression_lz4.test | 2 ++ mysql-test/t/lock_multi_bug38499.test | 3 +++ mysql-test/t/lock_multi_bug38691.test | 2 ++ storage/oqgraph/mysql-test/oqgraph/social.test | 3 +++ 6 files changed, 16 insertions(+) diff --git a/mysql-test/suite/binlog_encryption/encrypted_master.test b/mysql-test/suite/binlog_encryption/encrypted_master.test index f67e93ce815..f6fc172c79e 100644 --- a/mysql-test/suite/binlog_encryption/encrypted_master.test +++ b/mysql-test/suite/binlog_encryption/encrypted_master.test @@ -18,6 +18,9 @@ # - with annotated events, default checksums and minimal binlog row image # +# The test can take very long time with valgrind +--source include/not_valgrind.inc + --source include/have_partition.inc --source encryption_algorithms.inc --source include/have_innodb.inc diff --git a/mysql-test/suite/encryption/t/innodb-page_encryption.test b/mysql-test/suite/encryption/t/innodb-page_encryption.test index df2d1d52aaa..d756f07aea0 100644 --- a/mysql-test/suite/encryption/t/innodb-page_encryption.test +++ b/mysql-test/suite/encryption/t/innodb-page_encryption.test @@ -1,6 +1,9 @@ -- source include/have_innodb.inc -- source include/have_file_key_management_plugin.inc +# The test can take very long time with valgrind +--source include/not_valgrind.inc + create table innodb_normal(c1 bigint not null, b char(200)) engine=innodb; show warnings; create table innodb_compact(c1 bigint not null, b char(200)) engine=innodb row_format=compact encrypted=yes encryption_key_id=1; diff --git a/mysql-test/suite/innodb/t/innodb-page_compression_lz4.test b/mysql-test/suite/innodb/t/innodb-page_compression_lz4.test index 1b1df674e3c..47d24d638e9 100644 --- a/mysql-test/suite/innodb/t/innodb-page_compression_lz4.test +++ b/mysql-test/suite/innodb/t/innodb-page_compression_lz4.test @@ -1,6 +1,8 @@ -- source include/have_innodb.inc -- source include/have_innodb_lz4.inc -- source include/not_embedded.inc +# The test can take very long time with valgrind +--source include/not_valgrind.inc let $innodb_compression_algorithm_orig=`SELECT @@innodb_compression_algorithm`; diff --git a/mysql-test/t/lock_multi_bug38499.test b/mysql-test/t/lock_multi_bug38499.test index b812984e516..c489712e5d8 100644 --- a/mysql-test/t/lock_multi_bug38499.test +++ b/mysql-test/t/lock_multi_bug38499.test @@ -2,6 +2,9 @@ # MySQL >= 5.0 # +# The test can take hours with valgrind +--source include/not_valgrind.inc + # Save the initial number of concurrent sessions --source include/count_sessions.inc diff --git a/mysql-test/t/lock_multi_bug38691.test b/mysql-test/t/lock_multi_bug38691.test index 881a0d8e502..9760c1a873a 100644 --- a/mysql-test/t/lock_multi_bug38691.test +++ b/mysql-test/t/lock_multi_bug38691.test @@ -4,6 +4,8 @@ # MySQL >= 5.0 # +# The test can take hours with valgrind +--source include/not_valgrind.inc # Save the initial number of concurrent sessions --source include/count_sessions.inc diff --git a/storage/oqgraph/mysql-test/oqgraph/social.test b/storage/oqgraph/mysql-test/oqgraph/social.test index 69eb7698bb9..a71b160cc1d 100644 --- a/storage/oqgraph/mysql-test/oqgraph/social.test +++ b/storage/oqgraph/mysql-test/oqgraph/social.test @@ -1,3 +1,6 @@ +# The test can take very long time with valgrind +--source include/not_valgrind.inc + --disable_warnings DROP TABLE IF EXISTS rsb, rsb_graph; --enable_warnings From af0b26f9b7ccdf082ec8a631d45726ef78f173b0 Mon Sep 17 00:00:00 2001 From: Rucha Deodhar Date: Tue, 8 Dec 2020 16:05:07 +0530 Subject: [PATCH 05/12] MDEV-23786: Assertion `!is_set() || (m_status == DA_OK_BULK && is_bulk_op())' failed for TokuDB engine CREATE TABLE Analysis: Assertion failure happens because the database doesn't exist to create the table but ha_tokudb::create() still returns false. So error is not reported. Fix: Store the error state and report the error. --- storage/tokudb/ha_tokudb.cc | 4 ++-- .../tokudb/mysql-test/tokudb/r/bug-23786.result | 10 ++++++++++ storage/tokudb/mysql-test/tokudb/t/bug-23786.test | 14 ++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 storage/tokudb/mysql-test/tokudb/r/bug-23786.result create mode 100644 storage/tokudb/mysql-test/tokudb/t/bug-23786.test diff --git a/storage/tokudb/ha_tokudb.cc b/storage/tokudb/ha_tokudb.cc index 12d3a5a060a..6c0e24fc82b 100644 --- a/storage/tokudb/ha_tokudb.cc +++ b/storage/tokudb/ha_tokudb.cc @@ -7260,7 +7260,7 @@ int ha_tokudb::create( // in the database directory, so automatic filename-based // discover_table_names() doesn't work either. So, it must force .frm // file to disk. - form->s->write_frm_image(); + error= form->s->write_frm_image(); #endif #if defined(TOKU_INCLUDE_OPTION_STRUCTS) && TOKU_INCLUDE_OPTION_STRUCTS @@ -7292,8 +7292,8 @@ int ha_tokudb::create( #endif // defined(TOKU_INCLUDE_OPTION_STRUCTS) && TOKU_INCLUDE_OPTION_STRUCTS const toku_compression_method compression_method = row_format_to_toku_compression_method(row_format); - bool create_from_engine = (create_info->table_options & HA_OPTION_CREATE_FROM_ENGINE); + if (error) { goto cleanup; } if (create_from_engine) { // table already exists, nothing to do error = 0; diff --git a/storage/tokudb/mysql-test/tokudb/r/bug-23786.result b/storage/tokudb/mysql-test/tokudb/r/bug-23786.result new file mode 100644 index 00000000000..1b21758b85a --- /dev/null +++ b/storage/tokudb/mysql-test/tokudb/r/bug-23786.result @@ -0,0 +1,10 @@ +# +# 10.2 Test +# +# MDEV-23786: Assertion `!is_set() || (m_status == DA_OK_BULK && +# is_bulk_op())'failed for TokuDB engine CREATE TABLE +# +set default_storage_engine='tokudb'; +CREATE TABLE _uppercase.t (a INT) ENGINE=TokuDB; +ERROR 42000: Unknown database '_uppercase' +# End of 10.2 Test diff --git a/storage/tokudb/mysql-test/tokudb/t/bug-23786.test b/storage/tokudb/mysql-test/tokudb/t/bug-23786.test new file mode 100644 index 00000000000..a8a0c4a1577 --- /dev/null +++ b/storage/tokudb/mysql-test/tokudb/t/bug-23786.test @@ -0,0 +1,14 @@ +source include/have_tokudb.inc; + +--echo # +--echo # 10.2 Test +--echo # +--echo # MDEV-23786: Assertion `!is_set() || (m_status == DA_OK_BULK && +--echo # is_bulk_op())'failed for TokuDB engine CREATE TABLE +--echo # + +set default_storage_engine='tokudb'; +--error ER_BAD_DB_ERROR +CREATE TABLE _uppercase.t (a INT) ENGINE=TokuDB; + +--echo # End of 10.2 Test From 0711a53a3320ffd0fed4c945dcc0b2e577adbf23 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Mon, 26 Jul 2021 12:35:48 +0300 Subject: [PATCH 06/12] fix clang build: check alignment the other way --- mysys/mf_qsort.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysys/mf_qsort.c b/mysys/mf_qsort.c index 3f91bb35354..b516639a341 100644 --- a/mysys/mf_qsort.c +++ b/mysys/mf_qsort.c @@ -114,7 +114,7 @@ qsort_t my_qsort(void *base_ptr, size_t count, size_t size, qsort_cmp cmp) stack[0].low=stack[0].high=0; #endif pivot = (char *) my_alloca((int) size); - ptr_cmp= size == sizeof(char*) && !((low - (char*) 0)& (sizeof(char*)-1)); + ptr_cmp= size == sizeof(char*) && (intptr_t)low % sizeof(char*) == 0; /* The following loop sorts elements between high and low */ do From ce870b2a2a54a1cab844be40a3fabdc8a2a53ce2 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Mon, 26 Jul 2021 14:19:26 +0530 Subject: [PATCH 07/12] MDEV-25998 InnoDB removes the tablespace from default encrypt list early Problem: ========= As a part of MDEV-14398 patch, InnoDB added and removed the tablespace from default encrypt list. But InnoDB removes the tablespace from the default encrypt list too early due to i) other encryption thread working on the tablespace ii) When tablespace is being flushed at the end of key rotation InnoDB fails to decrypt/encrypt the tablespace since the tablespace removed too early and it leads to test case failure. Solution: ========= Avoid the removal of tablespace from default_encrypt_list only when 1) Another active encryption thread working on tablespace 2) Eligible for tablespace key rotation 3) Tablespace is in flushing phase Removed the workaround in encryption.innodb_encryption_filekeys test case. --- .../t/innodb_encryption_filekeys.test | 18 ----------- storage/innobase/fil/fil0crypt.cc | 30 +++++++++++++++++-- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/mysql-test/suite/encryption/t/innodb_encryption_filekeys.test b/mysql-test/suite/encryption/t/innodb_encryption_filekeys.test index cd5724638b1..03447bbcfa6 100644 --- a/mysql-test/suite/encryption/t/innodb_encryption_filekeys.test +++ b/mysql-test/suite/encryption/t/innodb_encryption_filekeys.test @@ -48,12 +48,6 @@ while ($cnt) { real_sleep 1; dec $cnt; - if ($cnt == 200) - { - --disable_query_log - set global innodb_encrypt_tables = on; - --enable_query_log - } } } if (!$success) @@ -84,12 +78,6 @@ while ($cnt) { real_sleep 1; dec $cnt; - if ($cnt == 200) - { - --disable_query_log - set global innodb_encrypt_tables = off; - --enable_query_log - } } } if (!$success) @@ -119,12 +107,6 @@ while ($cnt) { real_sleep 1; dec $cnt; - if ($cnt == 200) - { - --disable_query_log - set global innodb_encrypt_tables=on; - --enable_query_log - } } } if (!$success) diff --git a/storage/innobase/fil/fil0crypt.cc b/storage/innobase/fil/fil0crypt.cc index 94899ed0f65..4e710b4bb4f 100644 --- a/storage/innobase/fil/fil0crypt.cc +++ b/storage/innobase/fil/fil0crypt.cc @@ -1092,6 +1092,33 @@ struct rotate_thread_t { } }; +/** Avoid the removal of the tablespace from +default_encrypt_list only when +1) Another active encryption thread working on tablespace +2) Eligible for tablespace key rotation +3) Tablespace is in flushing phase +@return true if tablespace should be removed from +default encrypt */ +static bool fil_crypt_must_remove(const fil_space_t &space) +{ + ut_ad(space.purpose == FIL_TYPE_TABLESPACE); + fil_space_crypt_t *crypt_data = space.crypt_data; + mutex_own(&fil_system->mutex); + const ulong encrypt_tables= srv_encrypt_tables; + if (!crypt_data) + return !encrypt_tables; + if (!crypt_data->is_key_found()) + return true; + + mutex_enter(&crypt_data->mutex); + const bool remove= (space.is_stopping() || crypt_data->not_encrypted()) && + (!crypt_data->rotate_state.flushing && + !encrypt_tables == !!crypt_data->min_key_version && + !crypt_data->rotate_state.active_threads); + mutex_exit(&crypt_data->mutex); + return remove; +} + /*********************************************************************** Check if space needs rotation given a key_state @param[in,out] state Key rotation state @@ -1429,8 +1456,7 @@ inline fil_space_t *fil_system_t::default_encrypt_next( If there is a change in innodb_encrypt_tables variables value then don't remove the last processed tablespace from the default encrypt list. */ - if (released && (!recheck || space->crypt_data) && - !encrypt == !srv_encrypt_tables) + if (released && !recheck && fil_crypt_must_remove(*space)) { ut_a(!default_encrypt_tables.empty()); default_encrypt_tables.remove(*space); From 0bd9f755b798718dab8e354d39238d5e1457fd39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Fri, 2 Jul 2021 08:56:23 +0300 Subject: [PATCH 08/12] MDEV-26062 : InnoDB: WSREP: referenced FK check fail: Lock wait index `PRIMARY` table `schema`.`child_table` Problem was that not all normal error codes where not handled after wsrep_row_upd_check_foreign_constraints() call. Furhermore, debug assertion did not contain all normal error cases. Changed ib:: calls to WSREP_ calls to use wsrep instrumentation. --- .../suite/galera/r/galera_fk_lock_wait.result | 52 +++++++++++++ .../suite/galera/t/galera_fk_lock_wait.test | 40 ++++++++++ storage/innobase/row/row0upd.cc | 76 ++++++++++--------- 3 files changed, 133 insertions(+), 35 deletions(-) create mode 100644 mysql-test/suite/galera/r/galera_fk_lock_wait.result create mode 100644 mysql-test/suite/galera/t/galera_fk_lock_wait.test diff --git a/mysql-test/suite/galera/r/galera_fk_lock_wait.result b/mysql-test/suite/galera/r/galera_fk_lock_wait.result new file mode 100644 index 00000000000..7bbbf40cb29 --- /dev/null +++ b/mysql-test/suite/galera/r/galera_fk_lock_wait.result @@ -0,0 +1,52 @@ +CREATE TABLE parent(parent_id int not null AUTO_INCREMENT PRIMARY KEY, +parent_name varchar(80)) ENGINE=InnoDB; +CREATE TABLE child(child_id int not null AUTO_INCREMENT PRIMARY KEY, +child_name varchar(80), +child_parent_id int not null, +CONSTRAINT `fk_child_parent` + FOREIGN KEY (child_parent_id) REFERENCES parent (parent_id) +ON DELETE CASCADE +ON UPDATE CASCADE) ENGINE=InnoDB; +INSERT INTO parent VALUES (1, 'first'),(2,'second'),(3,'foo'),(4,'tmp'); +INSERT INTO child VALUES (NULL,'first_child',1); +INSERT INTO child VALUES (NULL,'second_child',1); +INSERT INTO child VALUES (NULL,'first_child2',2); +INSERT INTO child VALUES (NULL,'first_child3',2); +INSERT INTO child VALUES (NULL,'first_child4',3); +BEGIN; +UPDATE parent SET parent_name = 'bar' WHERE parent_id = 2; +connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1; +SET SESSION innodb_lock_wait_timeout=2; +UPDATE child SET child_parent_id = 5 where child_parent_id = 2; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +connection node_1; +COMMIT; +SELECT * FROM parent; +parent_id parent_name +1 first +2 bar +3 foo +4 tmp +SELECT * FROM child; +child_id child_name child_parent_id +1 first_child 1 +3 second_child 1 +5 first_child2 2 +7 first_child3 2 +9 first_child4 3 +connection node_2; +SELECT * FROM parent; +parent_id parent_name +1 first +2 bar +3 foo +4 tmp +SELECT * FROM child; +child_id child_name child_parent_id +1 first_child 1 +3 second_child 1 +5 first_child2 2 +7 first_child3 2 +9 first_child4 3 +DROP TABLE child, parent; +disconnect node_1a; diff --git a/mysql-test/suite/galera/t/galera_fk_lock_wait.test b/mysql-test/suite/galera/t/galera_fk_lock_wait.test new file mode 100644 index 00000000000..150c7397f7e --- /dev/null +++ b/mysql-test/suite/galera/t/galera_fk_lock_wait.test @@ -0,0 +1,40 @@ +--source include/galera_cluster.inc + +CREATE TABLE parent(parent_id int not null AUTO_INCREMENT PRIMARY KEY, +parent_name varchar(80)) ENGINE=InnoDB; + +CREATE TABLE child(child_id int not null AUTO_INCREMENT PRIMARY KEY, +child_name varchar(80), +child_parent_id int not null, +CONSTRAINT `fk_child_parent` + FOREIGN KEY (child_parent_id) REFERENCES parent (parent_id) + ON DELETE CASCADE + ON UPDATE CASCADE) ENGINE=InnoDB; + +INSERT INTO parent VALUES (1, 'first'),(2,'second'),(3,'foo'),(4,'tmp'); +INSERT INTO child VALUES (NULL,'first_child',1); +INSERT INTO child VALUES (NULL,'second_child',1); +INSERT INTO child VALUES (NULL,'first_child2',2); +INSERT INTO child VALUES (NULL,'first_child3',2); +INSERT INTO child VALUES (NULL,'first_child4',3); + +BEGIN; +UPDATE parent SET parent_name = 'bar' WHERE parent_id = 2; + +--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1 +SET SESSION innodb_lock_wait_timeout=2; +--error ER_LOCK_WAIT_TIMEOUT +UPDATE child SET child_parent_id = 5 where child_parent_id = 2; + +--connection node_1 +COMMIT; +SELECT * FROM parent; +SELECT * FROM child; + +--connection node_2 + +SELECT * FROM parent; +SELECT * FROM child; +DROP TABLE child, parent; + +--disconnect node_1a diff --git a/storage/innobase/row/row0upd.cc b/storage/innobase/row/row0upd.cc index 429282906df..1f3e34d493a 100644 --- a/storage/innobase/row/row0upd.cc +++ b/storage/innobase/row/row0upd.cc @@ -52,6 +52,11 @@ Created 12/27/1996 Heikki Tuuri #include #include #include +#ifdef WITH_WSREP +#include "log.h" +#include "wsrep.h" +#endif /* WITH_WSREP */ + /* What kind of latch and lock can we assume when the control comes to ------------------------------------------------------------------- @@ -2461,34 +2466,30 @@ row_upd_sec_index_entry( err = DB_SUCCESS; break; case DB_LOCK_WAIT: - if (UNIV_UNLIKELY(wsrep_debug)) { - ib::warn() << "WSREP: sec index FK lock wait" - << " index " << index->name - << " table " << index->table->name - << " query " << wsrep_thd_query(trx->mysql_thd); - } - break; case DB_DEADLOCK: - if (UNIV_UNLIKELY(wsrep_debug)) { - ib::warn() << "WSREP: sec index FK check fail for deadlock" - << " index " << index->name - << " table " << index->table->name - << " query " << wsrep_thd_query(trx->mysql_thd); - } + case DB_LOCK_WAIT_TIMEOUT: + WSREP_DEBUG("Foreign key check fail: " + "%s on table %s index %s query %s", + ut_strerr(err), index->name, index->table->name, + wsrep_thd_query(trx->mysql_thd)); break; default: - ib::error() << "WSREP: referenced FK check fail: " << err - << " index " << index->name - << " table " << index->table->name - << " query " << wsrep_thd_query(trx->mysql_thd); - + WSREP_ERROR("Foreign key check fail: " + "%s on table %s index %s query %s", + ut_strerr(err), index->name, index->table->name, + wsrep_thd_query(trx->mysql_thd)); break; } } #endif /* WITH_WSREP */ } +#ifdef WITH_WSREP + ut_ad(err == DB_SUCCESS || err == DB_LOCK_WAIT + || err == DB_DEADLOCK || err == DB_LOCK_WAIT_TIMEOUT); +#else ut_ad(err == DB_SUCCESS); +#endif if (referenced) { @@ -2800,17 +2801,21 @@ check_fk: case DB_NO_REFERENCED_ROW: err = DB_SUCCESS; break; + case DB_LOCK_WAIT: case DB_DEADLOCK: - if (UNIV_UNLIKELY(wsrep_debug)) { - ib::warn() << "WSREP: sec index FK check fail for deadlock" - << " index " << index->name - << " table " << index->table->name; - } + case DB_LOCK_WAIT_TIMEOUT: + WSREP_DEBUG("Foreign key check fail: " + "%s on table %s index %s query %s", + ut_strerr(err), index->name, index->table->name, + wsrep_thd_query(trx->mysql_thd)); + goto err_exit; default: - ib::error() << "WSREP: referenced FK check fail: " << err - << " index " << index->name - << " table " << index->table->name; + WSREP_ERROR("Foreign key check fail: " + "%s on table %s index %s query %s", + ut_strerr(err), index->name, index->table->name, + wsrep_thd_query(trx->mysql_thd)); + goto err_exit; } #endif /* WITH_WSREP */ @@ -3027,18 +3032,19 @@ row_upd_del_mark_clust_rec( case DB_NO_REFERENCED_ROW: err = DB_SUCCESS; break; + case DB_LOCK_WAIT: case DB_DEADLOCK: - if (UNIV_UNLIKELY(wsrep_debug)) { - ib::warn() << "WSREP: sec index FK check fail for deadlock" - << " index " << index->name - << " table " << index->table->name; - } + case DB_LOCK_WAIT_TIMEOUT: + WSREP_DEBUG("Foreign key check fail: " + "%d on table %s index %s query %s", + err, index->name, index->table->name, + wsrep_thd_query(trx->mysql_thd)); break; default: - ib::error() << "WSREP: referenced FK check fail: " << err - << " index " << index->name - << " table " << index->table->name; - + WSREP_ERROR("Foreign key check fail: " + "%d on table %s index %s query %s", + err, index->name, index->table->name, + wsrep_thd_query(trx->mysql_thd)); break; } #endif /* WITH_WSREP */ From cf1fc59856daa4c5730737541126d5492a091722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 27 Jul 2021 08:52:01 +0300 Subject: [PATCH 09/12] MDEV-25594: Improve debug checks trx_t::will_lock: Changed the type to bool. trx_t::is_autocommit_non_locking(): Replaces trx_is_autocommit_non_locking(). trx_is_ac_nl_ro(): Remove (replaced with equivalent assertion expressions). assert_trx_nonlocking_or_in_list(): Remove. Replaced with at least as strict checks in each place. check_trx_state(): Moved to a static function; partially replaced with individual debug assertions implementing equivalent or stricter checks. This is a backport of commit 7b51d11cca8898f319ddde5d7048cb81b43fef06 from 10.5. --- storage/innobase/handler/ha_innodb.cc | 70 ++++-------- storage/innobase/handler/handler0alter.cc | 3 +- storage/innobase/include/trx0i_s.h | 6 +- storage/innobase/include/trx0sys.ic | 6 +- storage/innobase/include/trx0trx.h | 124 +++++----------------- storage/innobase/include/trx0trx.ic | 19 ++-- storage/innobase/lock/lock0lock.cc | 39 +++++-- storage/innobase/read/read0read.cc | 4 +- storage/innobase/trx/trx0i_s.cc | 20 +++- storage/innobase/trx/trx0roll.cc | 39 +++++-- storage/innobase/trx/trx0sys.cc | 7 +- storage/innobase/trx/trx0trx.cc | 84 +++++++-------- 12 files changed, 184 insertions(+), 237 deletions(-) diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 81f1dae85b6..00c9595aa87 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -4503,7 +4503,7 @@ innobase_commit_low( if (trx_is_started(trx)) { trx_commit_for_mysql(trx); } else { - trx->will_lock = 0; + trx->will_lock = false; #ifdef WITH_WSREP trx->wsrep = false; #endif /* WITH_WSREP */ @@ -4860,7 +4860,7 @@ innobase_rollback_trx( lock_unlock_table_autoinc(trx); if (!trx->has_logged()) { - trx->will_lock = 0; + trx->will_lock = false; #ifdef WITH_WSREP trx->wsrep = false; #endif @@ -8176,20 +8176,12 @@ ha_innobase::write_row( if (high_level_read_only) { ib_senderrf(ha_thd(), IB_LOG_LEVEL_WARN, ER_READ_ONLY_MODE); DBUG_RETURN(HA_ERR_TABLE_READONLY); - } else if (UNIV_UNLIKELY(m_prebuilt->trx != trx)) { - ib::error() << "The transaction object for the table handle is" - " at " << static_cast(m_prebuilt->trx) - << ", but for the current thread it is at " - << static_cast(trx); + } - fputs("InnoDB: Dump of 200 bytes around m_prebuilt: ", stderr); - ut_print_buf(stderr, ((const byte*) m_prebuilt) - 100, 200); - fputs("\nInnoDB: Dump of 200 bytes around ha_data: ", stderr); - ut_print_buf(stderr, ((const byte*) trx) - 100, 200); - putc('\n', stderr); - ut_error; - } else if (!trx_is_started(trx)) { - ++trx->will_lock; + ut_a(m_prebuilt->trx == trx); + + if (!trx_is_started(trx)) { + trx->will_lock = true; } #ifdef WITH_WSREP @@ -8964,7 +8956,7 @@ ha_innobase::update_row( ib_senderrf(ha_thd(), IB_LOG_LEVEL_WARN, ER_READ_ONLY_MODE); DBUG_RETURN(HA_ERR_TABLE_READONLY); } else if (!trx_is_started(trx)) { - ++trx->will_lock; + trx->will_lock = true; } if (m_upd_buf == NULL) { @@ -9110,7 +9102,7 @@ ha_innobase::delete_row( ib_senderrf(ha_thd(), IB_LOG_LEVEL_WARN, ER_READ_ONLY_MODE); DBUG_RETURN(HA_ERR_TABLE_READONLY); } else if (!trx_is_started(trx)) { - ++trx->will_lock; + trx->will_lock = true; } if (!m_prebuilt->upd_node) { @@ -9990,7 +9982,7 @@ ha_innobase::ft_init() them as regular read only transactions for now. */ if (!trx_is_started(trx)) { - ++trx->will_lock; + trx->will_lock = true; } DBUG_RETURN(rnd_init(false)); @@ -10056,7 +10048,7 @@ ha_innobase::ft_init_ext( them as regular read only transactions for now. */ if (!trx_is_started(trx)) { - ++trx->will_lock; + trx->will_lock = true; } dict_table_t* ft_table = m_prebuilt->table; @@ -13081,7 +13073,7 @@ create_table_info_t::allocate_trx() { m_trx = innobase_trx_allocate(m_thd); - m_trx->will_lock++; + m_trx->will_lock = true; m_trx->ddl = true; } @@ -13372,13 +13364,7 @@ inline int ha_innobase::delete_table(const char* name, enum_sql_command sqlcom) ut_a(name_len < 1000); - /* Either the transaction is already flagged as a locking transaction - or it hasn't been started yet. */ - - ut_a(!trx_is_started(trx) || trx->will_lock > 0); - - /* We are doing a DDL operation. */ - ++trx->will_lock; + trx->will_lock = true; /* Drop the table in InnoDB */ @@ -13555,14 +13541,7 @@ innobase_drop_database( #endif /* _WIN32 */ trx_t* trx = innobase_trx_allocate(thd); - - /* Either the transaction is already flagged as a locking transaction - or it hasn't been started yet. */ - - ut_a(!trx_is_started(trx) || trx->will_lock > 0); - - /* We are doing a DDL operation. */ - ++trx->will_lock; + trx->will_lock = true; ulint dummy; @@ -13606,7 +13585,7 @@ inline dberr_t innobase_rename_table(trx_t *trx, const char *from, DEBUG_SYNC_C("innodb_rename_table_ready"); trx_start_if_not_started(trx, true); - ut_ad(trx->will_lock > 0); + ut_ad(trx->will_lock); if (commit) { /* Serialize data dictionary operations with dictionary mutex: @@ -13651,7 +13630,7 @@ inline dberr_t innobase_rename_table(trx_t *trx, const char *from, /* Transaction must be flagged as a locking transaction or it hasn't been started yet. */ - ut_a(trx->will_lock > 0); + ut_a(trx->will_lock); error = row_rename_table_for_mysql(norm_from, norm_to, trx, commit, commit); @@ -13737,7 +13716,7 @@ int ha_innobase::truncate() if (!srv_safe_truncate) { if (!trx_is_started(m_prebuilt->trx)) { - ++m_prebuilt->trx->will_lock; + m_prebuilt->trx->will_lock = true; } dberr_t err = row_truncate_table_for_mysql( @@ -13791,8 +13770,7 @@ int ha_innobase::truncate() heap, ib_table->name.m_name, ib_table->id); const char* name = mem_heap_strdup(heap, ib_table->name.m_name); trx_t* trx = innobase_trx_allocate(m_user_thd); - - ++trx->will_lock; + trx->will_lock = true; trx_set_dict_operation(trx, TRX_DICT_OP_TABLE); row_mysql_lock_data_dictionary(trx); dict_stats_wait_bg_to_stop_using_table(ib_table, trx); @@ -13877,9 +13855,7 @@ ha_innobase::rename_table( } trx_t* trx = innobase_trx_allocate(thd); - - /* We are doing a DDL operation. */ - ++trx->will_lock; + trx->will_lock = true; trx_set_dict_operation(trx, TRX_DICT_OP_INDEX); dberr_t error = innobase_rename_table(trx, from, to, true); @@ -15880,7 +15856,7 @@ ha_innobase::start_stmt( innobase_register_trx(ht, thd, trx); if (!trx_is_started(trx)) { - ++trx->will_lock; + trx->will_lock = true; } DBUG_RETURN(0); @@ -16103,7 +16079,7 @@ ha_innobase::external_lock( && (m_prebuilt->select_lock_type != LOCK_NONE || m_prebuilt->stored_select_lock_type != LOCK_NONE)) { - ++trx->will_lock; + trx->will_lock = true; } DBUG_RETURN(0); @@ -16150,7 +16126,7 @@ ha_innobase::external_lock( && (m_prebuilt->select_lock_type != LOCK_NONE || m_prebuilt->stored_select_lock_type != LOCK_NONE)) { - ++trx->will_lock; + trx->will_lock = true; } DBUG_RETURN(0); @@ -16829,7 +16805,7 @@ ha_innobase::store_lock( && (m_prebuilt->select_lock_type != LOCK_NONE || m_prebuilt->stored_select_lock_type != LOCK_NONE)) { - ++trx->will_lock; + trx->will_lock = true; } return(to); diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 24a7c4a74ad..88e02ee4e32 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -967,7 +967,7 @@ ha_innobase::check_if_supported_inplace_alter( } } - m_prebuilt->trx->will_lock++; + m_prebuilt->trx->will_lock = true; if (!online) { /* We already determined that only a non-locking @@ -8954,7 +8954,6 @@ foreign_fail: m_prebuilt = ctx->prebuilt; } trx_start_if_not_started(user_trx, true); - user_trx->will_lock++; m_prebuilt->trx = user_trx; } DBUG_INJECT_CRASH("ib_commit_inplace_crash", diff --git a/storage/innobase/include/trx0i_s.h b/storage/innobase/include/trx0i_s.h index 7e766072272..ca65e053502 100644 --- a/storage/innobase/include/trx0i_s.h +++ b/storage/innobase/include/trx0i_s.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2007, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2021, 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 @@ -164,8 +164,8 @@ struct i_s_trx_row_t { /*!< detailed_error in trx_t */ ulint trx_is_read_only; /*!< trx_t::read_only */ - ulint trx_is_autocommit_non_locking; - /*!< trx_is_autocommit_non_locking(trx) + bool trx_is_autocommit_non_locking; + /*!< trx:t::is_autocommit_non_locking() */ }; diff --git a/storage/innobase/include/trx0sys.ic b/storage/innobase/include/trx0sys.ic index c85695630f0..8518934e94f 100644 --- a/storage/innobase/include/trx0sys.ic +++ b/storage/innobase/include/trx0sys.ic @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2018, 2019, MariaDB Corporation. +Copyright (c) 2018, 2021, 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 @@ -251,7 +251,9 @@ trx_rw_min_trx_id_low(void) if (trx == NULL) { id = trx_sys->max_trx_id; } else { - assert_trx_in_rw_list(trx); + ut_ad(!trx->read_only); + ut_ad(trx->in_rw_trx_list); + ut_ad(!trx->is_autocommit_non_locking()); id = trx->id; } diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index a7591fa1b19..90365400d72 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -502,99 +502,6 @@ from innodb_lock_wait_timeout via trx_t::mysql_thd. ? thd_lock_wait_timeout((t)->mysql_thd) \ : 0) -/** -Determine if the transaction is a non-locking autocommit select -(implied read-only). -@param t transaction -@return true if non-locking autocommit select transaction. */ -#define trx_is_autocommit_non_locking(t) \ -((t)->auto_commit && (t)->will_lock == 0) - -/** -Determine if the transaction is a non-locking autocommit select -with an explicit check for the read-only status. -@param t transaction -@return true if non-locking autocommit read-only transaction. */ -#define trx_is_ac_nl_ro(t) \ -((t)->read_only && trx_is_autocommit_non_locking((t))) - -/** -Assert that the transaction is in the trx_sys_t::rw_trx_list */ -#define assert_trx_in_rw_list(t) do { \ - ut_ad(!(t)->read_only); \ - ut_ad((t)->in_rw_trx_list \ - == !((t)->read_only || !(t)->rsegs.m_redo.rseg)); \ - check_trx_state(t); \ -} while (0) - -/** -Check transaction state */ -#define check_trx_state(t) do { \ - ut_ad(!trx_is_autocommit_non_locking((t))); \ - switch ((t)->state) { \ - case TRX_STATE_PREPARED: \ - case TRX_STATE_PREPARED_RECOVERED: \ - case TRX_STATE_ACTIVE: \ - case TRX_STATE_COMMITTED_IN_MEMORY: \ - continue; \ - case TRX_STATE_NOT_STARTED: \ - break; \ - } \ - ut_error; \ -} while (0) - -/** Check if transaction is free so that it can be re-initialized. -@param t transaction handle */ -#define assert_trx_is_free(t) do { \ - ut_ad(trx_state_eq((t), TRX_STATE_NOT_STARTED)); \ - ut_ad(!(t)->id); \ - ut_ad(!(t)->has_logged()); \ - ut_ad(!(t)->is_referenced()); \ - ut_ad(!(t)->is_wsrep()); \ - ut_ad(!MVCC::is_view_active((t)->read_view)); \ - ut_ad((t)->lock.wait_thr == NULL); \ - ut_ad(UT_LIST_GET_LEN((t)->lock.trx_locks) == 0); \ - ut_ad((t)->lock.table_locks.empty()); \ - ut_ad(!(t)->autoinc_locks \ - || ib_vector_is_empty((t)->autoinc_locks)); \ - ut_ad((t)->dict_operation == TRX_DICT_OP_NONE); \ -} while(0) - -/** Check if transaction is in-active so that it can be freed and put back to -transaction pool. -@param t transaction handle */ -#define assert_trx_is_inactive(t) do { \ - assert_trx_is_free((t)); \ - ut_ad((t)->dict_operation_lock_mode == 0); \ -} while(0) - -#ifdef UNIV_DEBUG -/*******************************************************************//** -Assert that an autocommit non-locking select cannot be in the -rw_trx_list and that it is a read-only transaction. -The tranasction must be in the mysql_trx_list. */ -# define assert_trx_nonlocking_or_in_list(t) \ - do { \ - if (trx_is_autocommit_non_locking(t)) { \ - trx_state_t t_state = (t)->state; \ - ut_ad((t)->read_only); \ - ut_ad(!(t)->is_recovered); \ - ut_ad(!(t)->in_rw_trx_list); \ - ut_ad((t)->in_mysql_trx_list); \ - ut_ad(t_state == TRX_STATE_NOT_STARTED \ - || t_state == TRX_STATE_ACTIVE); \ - } else { \ - check_trx_state(t); \ - } \ - } while (0) -#else /* UNIV_DEBUG */ -/*******************************************************************//** -Assert that an autocommit non-locking slect cannot be in the -rw_trx_list and that it is a read-only transaction. -The tranasction must be in the mysql_trx_list. */ -# define assert_trx_nonlocking_or_in_list(trx) ((void)0) -#endif /* UNIV_DEBUG */ - typedef std::vector > lock_list; /*******************************************************************//** @@ -1070,16 +977,15 @@ public: /*------------------------------*/ bool read_only; /*!< true if transaction is flagged as a READ-ONLY transaction. - if auto_commit && will_lock == 0 + if auto_commit && !will_lock then it will be handled as a AC-NL-RO-SELECT (Auto Commit Non-Locking Read Only Select). A read only transaction will not be assigned an UNDO log. */ bool auto_commit; /*!< true if it is an autocommit */ - ib_uint32_t will_lock; /*!< Will acquire some locks. Increment - each time we determine that a lock will - be acquired by the MySQL layer. */ + bool will_lock; /*!< set to inform trx_start_low() that + the transaction may acquire locks */ /*------------------------------*/ fts_trx_t* fts_trx; /*!< FTS information, or NULL if transaction hasn't modified tables @@ -1199,10 +1105,28 @@ public: inline void free(); + void assert_freed() const + { + ut_ad(state == TRX_STATE_NOT_STARTED); + ut_ad(!id); + ut_ad(!has_logged()); + ut_ad(!const_cast(this)->is_referenced()); + ut_ad(!is_wsrep()); + ut_ad(!trx_get_read_view(this)); + ut_ad(!lock.wait_thr); + ut_ad(UT_LIST_GET_LEN(lock.trx_locks) == 0); + ut_ad(lock.table_locks.empty()); + ut_ad(!autoinc_locks || ib_vector_is_empty(autoinc_locks)); + ut_ad(dict_operation == TRX_DICT_OP_NONE); + } + + /** @return whether this is a non-locking autocommit transaction */ + bool is_autocommit_non_locking() const { return auto_commit && !will_lock; } + private: - /** Assign a rollback segment for modifying temporary tables. - @return the assigned rollback segment */ - trx_rseg_t* assign_temp_rseg(); + /** Assign a rollback segment for modifying temporary tables. + @return the assigned rollback segment */ + trx_rseg_t *assign_temp_rseg(); }; /** diff --git a/storage/innobase/include/trx0trx.ic b/storage/innobase/include/trx0trx.ic index 4a5b1ba717f..2a53509b206 100644 --- a/storage/innobase/include/trx0trx.ic +++ b/storage/innobase/include/trx0trx.ic @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2019, MariaDB Corporation. +Copyright (c) 2016, 2021, 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 @@ -50,17 +50,18 @@ trx_state_eq( switch (trx->state) { case TRX_STATE_PREPARED: case TRX_STATE_PREPARED_RECOVERED: - ut_ad(!trx_is_autocommit_non_locking(trx)); + case TRX_STATE_COMMITTED_IN_MEMORY: + ut_ad(!trx->is_autocommit_non_locking()); return(trx->state == state); case TRX_STATE_ACTIVE: - - assert_trx_nonlocking_or_in_list(trx); - return(state == trx->state); - - case TRX_STATE_COMMITTED_IN_MEMORY: - - check_trx_state(trx); + if (trx->is_autocommit_non_locking()) { + ut_ad(!trx->is_recovered); + ut_ad(trx->read_only); + ut_ad(trx->mysql_thd); + ut_ad(!trx->in_rw_trx_list); + ut_ad(trx->in_mysql_trx_list); + } return(state == trx->state); case TRX_STATE_NOT_STARTED: diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index 506106a2269..4daf4fb07b8 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -1376,6 +1376,19 @@ wsrep_print_wait_locks( } #endif /* WITH_WSREP */ +#ifdef UNIV_DEBUG +/** Check transaction state */ +static void check_trx_state(const trx_t *trx) +{ + ut_ad(!trx->auto_commit || trx->will_lock); + const trx_state_t state= trx->state; + ut_ad(state == TRX_STATE_ACTIVE || + state == TRX_STATE_PREPARED_RECOVERED || + state == TRX_STATE_PREPARED || + state == TRX_STATE_COMMITTED_IN_MEMORY); +} +#endif + /** Create a new record lock and inserts it to the lock queue, without checking for deadlocks or conflicts. @param[in] type_mode lock mode and wait flag; type will be replaced @@ -3589,8 +3602,8 @@ lock_table_create( ut_ad(table && trx); ut_ad(lock_mutex_own()); ut_ad(trx_mutex_own(trx)); - - check_trx_state(trx); + ut_ad(trx->is_recovered || trx->state == TRX_STATE_ACTIVE); + ut_ad(!trx->auto_commit || trx->will_lock); if ((type_mode & LOCK_MODE_MASK) == LOCK_AUTO_INC) { ++table->n_waiting_or_granted_auto_inc_locks; @@ -4545,7 +4558,10 @@ lock_remove_recovered_trx_record_locks( trx != NULL; trx = UT_LIST_GET_NEXT(trx_list, trx)) { - assert_trx_in_rw_list(trx); + ut_ad(!trx->read_only); + ut_ad(trx->in_rw_trx_list); + ut_ad(!trx->is_autocommit_non_locking()); + ut_ad(trx->state != TRX_STATE_NOT_STARTED); if (!trx->is_recovered) { continue; @@ -5181,7 +5197,8 @@ lock_rec_queue_validate( ut_ad(!index || lock->index == index); trx_mutex_enter(lock->trx); - ut_ad(!trx_is_ac_nl_ro(lock->trx)); + ut_ad(!lock->trx->read_only + || !lock->trx->is_autocommit_non_locking()); ut_ad(trx_state_eq(lock->trx, TRX_STATE_COMMITTED_IN_MEMORY) || !lock_get_wait(lock) @@ -5260,8 +5277,7 @@ lock_rec_queue_validate( for (lock = lock_rec_get_first(lock_sys->rec_hash, block, heap_no); lock != NULL; lock = lock_rec_get_next_const(heap_no, lock)) { - - ut_ad(!trx_is_ac_nl_ro(lock->trx)); + ut_ad(!lock->trx->is_autocommit_non_locking()); if (index) { ut_a(lock->index == index); @@ -5357,7 +5373,8 @@ loop: } } - ut_ad(!trx_is_ac_nl_ro(lock->trx)); + ut_ad(!lock->trx->read_only + || !lock->trx->is_autocommit_non_locking()); /* Only validate the record queues when this thread is not holding a space->latch. */ @@ -5465,7 +5482,7 @@ lock_rec_validate( ib_uint64_t current; - ut_ad(!trx_is_ac_nl_ro(lock->trx)); + ut_ad(!lock->trx->is_autocommit_non_locking()); ut_ad(lock_get_type(lock) == LOCK_REC); current = ut_ull_create( @@ -7075,7 +7092,8 @@ DeadlockChecker::search() ut_ad(m_start != NULL); ut_ad(m_wait_lock != NULL); - check_trx_state(m_wait_lock->trx); + ut_ad(!m_wait_lock->trx->auto_commit || m_wait_lock->trx->will_lock); + ut_d(check_trx_state(m_wait_lock->trx)); ut_ad(m_mark_start <= s_lock_mark_counter); /* Look at the locks ahead of wait_lock in the lock queue. */ @@ -7235,7 +7253,8 @@ DeadlockChecker::check_and_resolve(const lock_t* lock, trx_t* trx) { ut_ad(lock_mutex_own()); ut_ad(trx_mutex_own(trx)); - check_trx_state(trx); + ut_ad(trx->state == TRX_STATE_ACTIVE); + ut_ad(!trx->auto_commit || trx->will_lock); ut_ad(!srv_read_only_mode); if (!innobase_deadlock_detect) { diff --git a/storage/innobase/read/read0read.cc b/storage/innobase/read/read0read.cc index 3fd52d5d6dd..a115672a8a6 100644 --- a/storage/innobase/read/read0read.cc +++ b/storage/innobase/read/read0read.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2019, MariaDB Corporation. +Copyright (c) 2019, 2021, 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 @@ -574,7 +574,7 @@ MVCC::view_open(ReadView*& view, trx_t* trx) Therefore we must set the low limit id after we reset the closed status after the check. */ - if (trx_is_autocommit_non_locking(trx) && view->empty()) { + if (trx->is_autocommit_non_locking() && view->empty()) { view->m_closed = false; diff --git a/storage/innobase/trx/trx0i_s.cc b/storage/innobase/trx/trx0i_s.cc index 2b9d6c96acd..05579aeb223 100644 --- a/storage/innobase/trx/trx0i_s.cc +++ b/storage/innobase/trx/trx0i_s.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2007, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2021, 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 @@ -577,7 +577,7 @@ thd_done: row->trx_is_read_only = trx->read_only; - row->trx_is_autocommit_non_locking = trx_is_autocommit_non_locking(trx); + row->trx_is_autocommit_non_locking = trx->is_autocommit_non_locking(); return(TRUE); } @@ -1259,7 +1259,21 @@ fetch_data_into_cache_low( continue; } - assert_trx_nonlocking_or_in_list(trx); + +#ifdef UNIV_DEBUG + if (trx->is_autocommit_non_locking()) { + ut_ad(trx->read_only); + ut_ad(!trx->is_recovered); + ut_ad(trx->mysql_thd); + ut_ad(trx->in_mysql_trx_list); + const trx_state_t state = trx->state; + ut_ad(state == TRX_STATE_NOT_STARTED + || state == TRX_STATE_ACTIVE); + } + else { + ut_ad(trx->state != TRX_STATE_NOT_STARTED); + } +#endif /* UNIV_DEBUG */ ut_ad(trx->in_rw_trx_list == rw_trx_list); diff --git a/storage/innobase/trx/trx0roll.cc b/storage/innobase/trx/trx0roll.cc index c986a866fe2..ec2d3c1d03f 100644 --- a/storage/innobase/trx/trx0roll.cc +++ b/storage/innobase/trx/trx0roll.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2020, MariaDB Corporation. +Copyright (c) 2016, 2021, 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 @@ -81,12 +81,19 @@ trx_rollback_to_savepoint_low( heap = mem_heap_create(512); roll_node = roll_node_create(heap); + ut_ad(!trx->in_rollback); if (savept != NULL) { roll_node->savept = savept; - check_trx_state(trx); + ut_ad(trx->mysql_thd); + ut_ad(trx->in_mysql_trx_list); + ut_ad(!trx->is_recovered); + ut_ad(trx->state == TRX_STATE_ACTIVE); } else { - assert_trx_nonlocking_or_in_list(trx); + ut_d(trx_state_t state = trx->state); + ut_ad(state == TRX_STATE_ACTIVE + || state == TRX_STATE_PREPARED + || state == TRX_STATE_PREPARED_RECOVERED); } trx->error_state = DB_SUCCESS; @@ -187,7 +194,8 @@ dberr_t trx_rollback_for_mysql(trx_t* trx) switch (trx->state) { case TRX_STATE_NOT_STARTED: - trx->will_lock = 0; + trx->will_lock = false; + ut_ad(trx->mysql_thd); ut_ad(trx->in_mysql_trx_list); #ifdef WITH_WSREP trx->wsrep = false; @@ -196,12 +204,14 @@ dberr_t trx_rollback_for_mysql(trx_t* trx) case TRX_STATE_ACTIVE: ut_ad(trx->in_mysql_trx_list); - assert_trx_nonlocking_or_in_list(trx); + ut_ad(trx->mysql_thd); + ut_ad(!trx->is_recovered); + ut_ad(!trx->is_autocommit_non_locking() || trx->read_only); return(trx_rollback_for_mysql_low(trx)); case TRX_STATE_PREPARED: case TRX_STATE_PREPARED_RECOVERED: - ut_ad(!trx_is_autocommit_non_locking(trx)); + ut_ad(!trx->is_autocommit_non_locking()); if (trx->has_logged_persistent()) { /* The XA ROLLBACK of a XA PREPARE transaction will consist of multiple mini-transactions. @@ -245,7 +255,7 @@ dberr_t trx_rollback_for_mysql(trx_t* trx) return(trx_rollback_for_mysql_low(trx)); case TRX_STATE_COMMITTED_IN_MEMORY: - check_trx_state(trx); + ut_ad(!trx->is_autocommit_non_locking()); break; } @@ -274,7 +284,9 @@ trx_rollback_last_sql_stat_for_mysql( return(DB_SUCCESS); case TRX_STATE_ACTIVE: - assert_trx_nonlocking_or_in_list(trx); + ut_ad(trx->mysql_thd); + ut_ad(!trx->is_recovered); + ut_ad(!trx->is_autocommit_non_locking() || trx->read_only); trx->op_info = "rollback of SQL statement"; @@ -768,7 +780,11 @@ trx_roll_must_shutdown() t != NULL; t = UT_LIST_GET_NEXT(trx_list, t)) { - assert_trx_in_rw_list(t); + ut_ad(!t->read_only); + ut_ad(t->in_rw_trx_list); + ut_ad(!t->is_autocommit_non_locking()); + ut_ad(t->state != TRX_STATE_NOT_STARTED); + if (t->is_recovered && trx_state_eq(t, TRX_STATE_ACTIVE)) { n_trx++; @@ -831,7 +847,10 @@ trx_rollback_or_clean_recovered( trx != NULL; trx = UT_LIST_GET_NEXT(trx_list, trx)) { - assert_trx_in_rw_list(trx); + ut_ad(!trx->read_only); + ut_ad(trx->in_rw_trx_list); + ut_ad(!trx->is_autocommit_non_locking()); + ut_ad(trx->state != TRX_STATE_NOT_STARTED); /* If this function does a cleanup or rollback then it will release the trx_sys->mutex, therefore diff --git a/storage/innobase/trx/trx0sys.cc b/storage/innobase/trx/trx0sys.cc index 9138e9475bf..c8c68c37333 100644 --- a/storage/innobase/trx/trx0sys.cc +++ b/storage/innobase/trx/trx0sys.cc @@ -513,7 +513,9 @@ trx_sys_init_at_db_start() trx = UT_LIST_GET_NEXT(trx_list, trx)) { ut_ad(trx->is_recovered); - assert_trx_in_rw_list(trx); + ut_ad(!trx->read_only); + ut_ad(trx->in_rw_trx_list); + ut_ad(!trx->is_autocommit_non_locking()); if (trx_state_eq(trx, TRX_STATE_ACTIVE)) { rows_to_undo += trx->undo_no; @@ -1026,7 +1028,8 @@ trx_sys_validate_trx_list_low( trx != NULL; prev_trx = trx, trx = UT_LIST_GET_NEXT(trx_list, prev_trx)) { - check_trx_state(trx); + ut_ad(!trx->is_autocommit_non_locking()); + ut_ad(trx->state != TRX_STATE_NOT_STARTED); ut_a(prev_trx == NULL || prev_trx->id > trx->id); } diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index d1b35bd84a3..3e558a7181d 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -133,7 +133,7 @@ trx_init( trx->auto_commit = false; - trx->will_lock = 0; + trx->will_lock = false; trx->ddl = false; @@ -336,13 +336,13 @@ trx_t *trx_allocate_for_background() MEM_MAKE_DEFINED(trx, sizeof *trx); #endif - assert_trx_is_free(trx); + trx->assert_freed(); mem_heap_t* heap; ib_alloc_t* alloc; /* We just got trx from pool, it should be non locking */ - ut_ad(trx->will_lock == 0); + ut_ad(!trx->will_lock); ut_ad(trx->state == TRX_STATE_NOT_STARTED); DBUG_LOG("trx", "Create: " << trx); @@ -369,7 +369,8 @@ trx_t *trx_allocate_for_background() /** Free the memory to trx_pools */ inline void trx_t::free() { - assert_trx_is_inactive(this); + assert_freed(); + ut_ad(!dict_operation_lock_mode); MEM_CHECK_DEFINED(this, sizeof *this); @@ -539,7 +540,8 @@ trx_validate_state_before_free(trx_t* trx) } trx->dict_operation = TRX_DICT_OP_NONE; - assert_trx_is_inactive(trx); + trx->assert_freed(); + ut_ad(!trx->dict_operation_lock_mode); } /** Free and initialize a transaction object instantinated during recovery. @@ -636,7 +638,9 @@ trx_free_prepared( trx->release_locks(); trx_undo_free_prepared(trx); - assert_trx_in_rw_list(trx); + ut_ad(!trx->read_only); + ut_ad(trx->in_rw_trx_list); + ut_ad(!trx->is_autocommit_non_locking()); ut_a(!trx->read_only); @@ -685,7 +689,7 @@ trx_disconnect_from_mysql( trx->is_recovered = true; trx->mysql_thd = NULL; /* todo/fixme: suggest to do it at innodb prepare */ - trx->will_lock = 0; + trx->will_lock = false; } trx_sys_mutex_exit(); @@ -1177,11 +1181,10 @@ void trx_t::remove_flush_observer() /** Assign a rollback segment for modifying temporary tables. @return the assigned rollback segment */ -trx_rseg_t* -trx_t::assign_temp_rseg() +trx_rseg_t *trx_t::assign_temp_rseg() { ut_ad(!rsegs.m_noredo.rseg); - ut_ad(!trx_is_autocommit_non_locking(this)); + ut_ad(!is_autocommit_non_locking()); compile_time_assert(ut_is_2pow(TRX_SYS_N_RSEGS)); /* Choose a temporary rollback segment between 0 and 127 @@ -1235,8 +1238,8 @@ trx_start_low( && thd_trx_is_read_only(trx->mysql_thd)); if (!trx->auto_commit) { - ++trx->will_lock; - } else if (trx->will_lock == 0) { + trx->will_lock = true; + } else if (!trx->will_lock) { trx->read_only = true; } @@ -1305,7 +1308,7 @@ trx_start_low( trx_sys_mutex_exit(); } else { - if (!trx_is_autocommit_non_locking(trx)) { + if (!trx->is_autocommit_non_locking()) { /* If this is a read-only transaction that is writing to a temporary table then it needs a transaction id @@ -1691,12 +1694,16 @@ trx_commit_in_memory( { trx->must_flush_log_later = false; - if (trx_is_autocommit_non_locking(trx)) { + if (trx->is_autocommit_non_locking()) { ut_ad(trx->id == 0); ut_ad(trx->read_only); + ut_ad(!trx->will_lock); ut_a(!trx->is_recovered); ut_ad(trx->rsegs.m_redo.rseg == NULL); ut_ad(!trx->in_rw_trx_list); + ut_ad(trx->in_mysql_trx_list); + ut_ad(trx->mysql_thd); + ut_ad(trx->state == TRX_STATE_ACTIVE); /* Note: We are asserting without holding the lock mutex. But that is OK because this transaction is not waiting and cannot @@ -1712,8 +1719,6 @@ trx_commit_in_memory( and it cannot be removed from the mysql_trx_list and freed without first acquiring the trx_sys_t::mutex. */ - ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE)); - if (trx->read_view != NULL) { trx_sys->mvcc->view_close(trx->read_view, false); } @@ -1866,7 +1871,7 @@ trx_commit_in_memory( /* trx->in_mysql_trx_list would hold between trx_allocate_for_mysql() and trx_free_for_mysql(). It does not hold for recovered transactions or system transactions. */ - assert_trx_is_free(trx); + trx->assert_freed(); trx_init(trx); @@ -1885,30 +1890,20 @@ trx_commit_low( mtr_t* mtr) /*!< in/out: mini-transaction (will be committed), or NULL if trx made no modifications */ { - assert_trx_nonlocking_or_in_list(trx); - ut_ad(!trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY)); ut_ad(!mtr || mtr->is_active()); ut_ad(!mtr == !trx->has_logged()); /* undo_no is non-zero if we're doing the final commit. */ if (trx->fts_trx != NULL && trx->undo_no != 0) { - dberr_t error; - - ut_a(!trx_is_autocommit_non_locking(trx)); - - error = fts_commit(trx); + ut_a(!trx->is_autocommit_non_locking()); /* FTS-FIXME: Temporarily tolerate DB_DUPLICATE_KEY instead of dying. This is a possible scenario if there is a crash between insert to DELETED table committing and transaction committing. The fix would be able to return error from this function */ - if (error != DB_SUCCESS && error != DB_DUPLICATE_KEY) { - /* FTS-FIXME: once we can return values from this - function, we should do so and signal an error - instead of just dying. */ - - ut_error; + if (dberr_t error = fts_commit(trx)) { + ut_a(error == DB_DUPLICATE_KEY); } } @@ -2279,7 +2274,6 @@ trx_print_low( /*!< in: mem_heap_get_size(trx->lock.lock_heap) */ { ibool newline; - const char* op_info; ut_ad(trx_sys_mutex_own()); @@ -2308,9 +2302,7 @@ trx_print_low( fprintf(f, ", state %lu", (ulong) trx->state); ut_ad(0); state_ok: - - /* prevent a race condition */ - op_info = trx->op_info; + const char* op_info = trx->op_info; if (*op_info) { putc(' ', f); @@ -2549,7 +2541,7 @@ trx_assert_started( /* Non-locking autocommits should not hold any locks and this function is only called from the locking code. */ - check_trx_state(trx); + ut_ad(!trx->is_autocommit_non_locking()); /* trx->state can change from or to NOT_STARTED while we are holding trx_sys->mutex for non-locking autocommit selects but not for other @@ -2754,7 +2746,9 @@ trx_recover_for_mysql( trx != NULL; trx = UT_LIST_GET_NEXT(trx_list, trx)) { - assert_trx_in_rw_list(trx); + ut_ad(!trx->read_only); + ut_ad(trx->in_rw_trx_list); + ut_ad(!trx->is_autocommit_non_locking()); /* The state of a read-write transaction cannot change from or to NOT_STARTED while we are holding the @@ -2821,7 +2815,9 @@ trx_t* trx_get_trx_by_xid_low(const XID* xid) trx != NULL; trx = UT_LIST_GET_NEXT(trx_list, trx)) { trx_mutex_enter(trx); - assert_trx_in_rw_list(trx); + ut_ad(!trx->read_only); + ut_ad(trx->in_rw_trx_list); + ut_ad(!trx->is_autocommit_non_locking()); /* Compare two X/Open XA transaction id's: their length should be the same and binary comparison @@ -2948,7 +2944,7 @@ trx_start_internal_low( /* Ensure it is not flagged as an auto-commit-non-locking transaction. */ - trx->will_lock = 1; + trx->will_lock = true; trx->internal = true; @@ -2964,7 +2960,7 @@ trx_start_internal_read_only_low( /* Ensure it is not flagged as an auto-commit-non-locking transaction. */ - trx->will_lock = 1; + trx->will_lock = true; trx->internal = true; @@ -2985,13 +2981,7 @@ trx_start_for_ddl_low( the data dictionary will be locked in crash recovery. */ trx_set_dict_operation(trx, op); - - /* Ensure it is not flagged as an auto-commit-non-locking - transation. */ - trx->will_lock = 1; - trx->ddl= true; - trx_start_internal_low(trx); return; @@ -3002,7 +2992,7 @@ trx_start_for_ddl_low( trx->ddl = true; ut_ad(trx->dict_operation != TRX_DICT_OP_NONE); - ut_ad(trx->will_lock > 0); + ut_ad(trx->will_lock); return; case TRX_STATE_PREPARED: @@ -3028,7 +3018,7 @@ trx_set_rw_mode( { ut_ad(trx->rsegs.m_redo.rseg == 0); ut_ad(!trx->in_rw_trx_list); - ut_ad(!trx_is_autocommit_non_locking(trx)); + ut_ad(!trx->is_autocommit_non_locking()); ut_ad(!trx->read_only); if (high_level_read_only) { From da094188f60bf67e3d90227304a4ea256fe2630f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 27 Jul 2021 08:52:59 +0300 Subject: [PATCH 10/12] MDEV-24393 InnoDB disregards --skip-external-locking On POSIX systems, InnoDB would unconditionally acquire advisory locks on the files that it opens. On Linux, this would be observable by a large number of entries in /proc/locks. Other storage engines would only acquire advisory locks on files based on the Boolean configuration parameter external_locking. Let InnoDB do the same. NOTE: The --skip-external-locking is activated by default. To have InnoDB acquire advisory locks, --external-locking must be specified. Reviewed by: Sergei Golubchik --- include/my_sys.h | 5 +++-- storage/innobase/os/os0file.cc | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/my_sys.h b/include/my_sys.h index ac1730eeaff..1d2fff0e476 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2013, Oracle and/or its affiliates. - Copyright (c) 2010, 2017, MariaDB Corporation. + Copyright (c) 2010, 2021, 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 @@ -279,7 +279,8 @@ extern int my_umask_dir, extern my_bool my_use_symdir; extern ulong my_default_record_cache_size; -extern my_bool my_disable_locking, my_disable_async_io, +extern MYSQL_PLUGIN_IMPORT my_bool my_disable_locking; +extern my_bool my_disable_async_io, my_disable_flush_key_blocks, my_disable_symlinks; extern my_bool my_disable_sync, my_disable_copystat_in_redel; extern char wild_many,wild_one,wild_prefix; diff --git a/storage/innobase/os/os0file.cc b/storage/innobase/os/os0file.cc index 884cadaf9f6..a793f9b30cb 100644 --- a/storage/innobase/os/os0file.cc +++ b/storage/innobase/os/os0file.cc @@ -1122,6 +1122,10 @@ os_file_lock( int fd, const char* name) { + if (my_disable_locking) { + return 0; + } + struct flock lk; lk.l_type = F_WRLCK; From e11cae71feb853dc6f2917a2b7ad2f41b294a9dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 27 Jul 2021 09:26:24 +0300 Subject: [PATCH 11/12] MDEV-25998 fixup: Assert that a mutex is being held --- storage/innobase/fil/fil0crypt.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/innobase/fil/fil0crypt.cc b/storage/innobase/fil/fil0crypt.cc index 4e710b4bb4f..310693dd02f 100644 --- a/storage/innobase/fil/fil0crypt.cc +++ b/storage/innobase/fil/fil0crypt.cc @@ -1,6 +1,6 @@ /***************************************************************************** Copyright (C) 2013, 2015, Google Inc. All Rights Reserved. -Copyright (c) 2014, 2020, MariaDB Corporation. +Copyright (c) 2014, 2021, 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 @@ -1103,7 +1103,7 @@ static bool fil_crypt_must_remove(const fil_space_t &space) { ut_ad(space.purpose == FIL_TYPE_TABLESPACE); fil_space_crypt_t *crypt_data = space.crypt_data; - mutex_own(&fil_system->mutex); + ut_ad(mutex_own(&fil_system->mutex)); const ulong encrypt_tables= srv_encrypt_tables; if (!crypt_data) return !encrypt_tables; From afe00bb7cce7de279e48a83ededc71b3f8cfafcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 27 Jul 2021 10:44:01 +0300 Subject: [PATCH 12/12] MDEV-25998 fixup: Avoid a hang btr_scrub_start_space(): Avoid an unnecessary tablespace lookup and related acquisition of fil_system->mutex. In MariaDB Server 10.3 we would get deadlocks between that mutex and a crypt_data mutex. The fix was developed by Thirunarayanan Balathandayuthapani. --- storage/innobase/btr/btr0scrub.cc | 16 ++++------------ storage/innobase/fil/fil0crypt.cc | 2 +- storage/innobase/include/btr0scrub.h | 7 +------ 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/storage/innobase/btr/btr0scrub.cc b/storage/innobase/btr/btr0scrub.cc index 60d9c90c310..685e98909db 100644 --- a/storage/innobase/btr/btr0scrub.cc +++ b/storage/innobase/btr/btr0scrub.cc @@ -1,5 +1,5 @@ // Copyright (c) 2014, Google Inc. -// Copyright (c) 2017, MariaDB Corporation. +// Copyright (c) 2017, 2021, MariaDB Corporation. /**************************************************//** @file btr/btr0scrub.cc @@ -835,20 +835,12 @@ btr_scrub_page( /**************************************************************//** Start iterating a space */ -UNIV_INTERN -bool -btr_scrub_start_space( -/*===================*/ - ulint space, /*!< in: space */ - btr_scrub_t* scrub_data) /*!< in/out: scrub data */ +bool btr_scrub_start_space(const fil_space_t &space, btr_scrub_t *scrub_data) { - bool found; - scrub_data->space = space; + scrub_data->space = space.id; scrub_data->current_table = NULL; scrub_data->current_index = NULL; - const page_size_t page_size = fil_space_get_page_size(space, &found); - - scrub_data->compressed = page_size.is_compressed(); + scrub_data->compressed = FSP_FLAGS_GET_ZIP_SSIZE(space.flags) != 0; scrub_data->scrubbing = check_scrub_setting(scrub_data); return scrub_data->scrubbing; } diff --git a/storage/innobase/fil/fil0crypt.cc b/storage/innobase/fil/fil0crypt.cc index 310693dd02f..ff6294e85b7 100644 --- a/storage/innobase/fil/fil0crypt.cc +++ b/storage/innobase/fil/fil0crypt.cc @@ -1200,7 +1200,7 @@ fil_crypt_space_needs_rotation( key_state->rotate_key_age); crypt_data->rotate_state.scrubbing.is_active = - btr_scrub_start_space(space->id, &state->scrub_data); + btr_scrub_start_space(*space, &state->scrub_data); time_t diff = time(0) - crypt_data->rotate_state.scrubbing. last_scrub_completed; diff --git a/storage/innobase/include/btr0scrub.h b/storage/innobase/include/btr0scrub.h index feaf61784d0..0f17467fb70 100644 --- a/storage/innobase/include/btr0scrub.h +++ b/storage/innobase/include/btr0scrub.h @@ -141,12 +141,7 @@ btr_scrub_skip_page( /**************************************************************** Start iterating a space * @return true if scrubbing is turned on */ -UNIV_INTERN -bool -btr_scrub_start_space( -/*===================*/ - ulint space, /*!< in: space */ - btr_scrub_t* scrub_data); /*!< in/out: scrub data */ +bool btr_scrub_start_space(const fil_space_t &space, btr_scrub_t *scrub_data); /** Complete iterating a space. @param[in,out] scrub_data scrub data */