diff options
author | Oleksandr Byelkin <sanja@mariadb.com> | 2020-11-14 20:05:35 +0100 |
---|---|---|
committer | Oleksandr Byelkin <sanja@mariadb.com> | 2020-11-14 20:05:35 +0100 |
commit | 10aa5764835ba5bc5fa49a1051e1cc647c0f301b (patch) | |
tree | 951d6bc7bf5f5958141bdaab19ea8fb9be5c915f | |
parent | 6083c5a05407dcb137e4ae1d39b944c9c392c48e (diff) | |
parent | 81b9c78500a9326a85290992a9340eb887b74048 (diff) | |
download | mariadb-git-10aa5764835ba5bc5fa49a1051e1cc647c0f301b.tar.gz |
Merge branch '10.5' into 10.6
63 files changed, 2027 insertions, 431 deletions
diff --git a/include/wsrep.h b/include/wsrep.h index f8a1863b966..703b89f966a 100644 --- a/include/wsrep.h +++ b/include/wsrep.h @@ -25,21 +25,24 @@ #define DBUG_ASSERT_IF_WSREP(A) DBUG_ASSERT(A) #define WSREP_MYSQL_DB (char *)"mysql" +#define WSREP_TO_ISOLATION_BEGIN_IF(db_, table_, table_list_) \ + if (WSREP_ON && WSREP(thd) && wsrep_to_isolation_begin(thd, db_, table_, table_list_)) -#define WSREP_TO_ISOLATION_BEGIN(db_, table_, table_list_) \ - if (WSREP(thd) && wsrep_to_isolation_begin(thd, db_, table_, table_list_)) \ +#define WSREP_TO_ISOLATION_BEGIN(db_, table_, table_list_) \ + if (WSREP_ON && WSREP(thd) && wsrep_to_isolation_begin(thd, db_, table_, table_list_)) \ goto wsrep_error_label; #define WSREP_TO_ISOLATION_BEGIN_CREATE(db_, table_, table_list_, create_info_) \ - if (WSREP(thd) && \ + if (WSREP_ON && WSREP(thd) && \ wsrep_to_isolation_begin(thd, db_, table_, \ - table_list_, NULL, create_info_)) \ + table_list_, nullptr, nullptr, create_info_))\ goto wsrep_error_label; -#define WSREP_TO_ISOLATION_BEGIN_ALTER(db_, table_, table_list_, alter_info_, create_info_) \ - if (WSREP(thd) && wsrep_thd_is_local(thd) && \ - wsrep_to_isolation_begin(thd, db_, table_, \ - table_list_, alter_info_, create_info_)) \ +#define WSREP_TO_ISOLATION_BEGIN_ALTER(db_, table_, table_list_, alter_info_, fk_tables_, create_info_) \ + if (WSREP(thd) && wsrep_thd_is_local(thd) && \ + wsrep_to_isolation_begin(thd, db_, table_, \ + table_list_, alter_info_, \ + fk_tables_, create_info_)) \ goto wsrep_error_label; #define WSREP_TO_ISOLATION_END \ @@ -55,6 +58,10 @@ if (WSREP(thd) && !thd->lex->no_write_to_binlog \ && wsrep_to_isolation_begin(thd, db_, table_, table_list_)) goto wsrep_error_label; +#define WSREP_TO_ISOLATION_BEGIN_FK_TABLES(db_, table_, table_list_, fk_tables) \ + if (WSREP(thd) && !thd->lex->no_write_to_binlog \ + && wsrep_to_isolation_begin(thd, db_, table_, table_list_, NULL, fk_tables)) + #define WSREP_SYNC_WAIT(thd_, before_) \ { if (WSREP_CLIENT(thd_) && \ wsrep_sync_wait(thd_, before_)) goto wsrep_error_label; } @@ -68,7 +75,8 @@ #define WSREP_DEBUG(...) #define WSREP_ERROR(...) #define WSREP_TO_ISOLATION_BEGIN(db_, table_, table_list_) do { } while(0) -#define WSREP_TO_ISOLATION_BEGIN_ALTER(db_, table_, table_list_, alter_info_, create_info_) +#define WSREP_TO_ISOLATION_BEGIN_ALTER(db_, table_, table_list_, alter_info_, fk_tables_, create_info_) +#define WSREP_TO_ISOLATION_BEGIN_FK_TABLES(db_, table_, table_list_, fk_tables_) #define WSREP_TO_ISOLATION_END #define WSREP_TO_ISOLATION_BEGIN_CREATE(db_, table_, table_list_, create_info_) #define WSREP_TO_ISOLATION_BEGIN_WRTCHK(db_, table_, table_list_) diff --git a/mysql-test/main/cte_recursive.result b/mysql-test/main/cte_recursive.result index deb791f25a3..d8ea858ad5a 100644 --- a/mysql-test/main/cte_recursive.result +++ b/mysql-test/main/cte_recursive.result @@ -3998,6 +3998,240 @@ YEAR d1 d2 DROP PROCEDURE p; DROP TABLE t1,t2,t3,t4; # +# MDEV-23619: recursive CTE used only in the second operand of UNION +# +create table t1 ( +a bigint(10) not null auto_increment, +b int(5) not null, +c bigint(10) default null, +primary key (a) +) engine myisam; +insert into t1 values +(1,3,12), (2,7,15), (3,1,3), (4,3,1); +explain with recursive r_cte as +( select * from t1 as s +union +select t1.* from t1, r_cte as r where t1.c = r.a ) +select 0 as b FROM dual union all select b FROM r_cte as t; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL No tables used +2 DERIVED s ALL NULL NULL NULL NULL 4 +3 RECURSIVE UNION t1 ALL NULL NULL NULL NULL 4 Using where +3 RECURSIVE UNION <derived2> ref key0 key0 9 test.t1.c 2 +NULL UNION RESULT <union2,3> ALL NULL NULL NULL NULL NULL +4 RECURSIVE UNION <derived2> ALL NULL NULL NULL NULL 4 +with recursive r_cte as +( select * from t1 as s +union +select t1.* from t1, r_cte as r where t1.c = r.a ) +select 0 as b FROM dual union all select b FROM r_cte as t; +b +0 +3 +7 +1 +3 +analyze format=json with recursive r_cte as +( select * from t1 as s +union +select t1.* from t1, r_cte as r where t1.c = r.a ) +select 0 as b FROM dual union all select b FROM r_cte as t; +ANALYZE +{ + "query_block": { + "union_result": { + "table_name": "<union1,4>", + "access_type": "ALL", + "r_loops": 0, + "r_rows": null, + "query_specifications": [ + { + "query_block": { + "select_id": 1, + "table": { + "message": "No tables used" + } + } + }, + { + "query_block": { + "select_id": 4, + "operation": "UNION", + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "table": { + "table_name": "<derived2>", + "access_type": "ALL", + "r_loops": 1, + "rows": 4, + "r_rows": 4, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100, + "materialized": { + "query_block": { + "recursive_union": { + "table_name": "<union2,3>", + "access_type": "ALL", + "r_loops": 0, + "r_rows": null, + "query_specifications": [ + { + "query_block": { + "select_id": 2, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "table": { + "table_name": "s", + "access_type": "ALL", + "r_loops": 1, + "rows": 4, + "r_rows": 4, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100 + } + } + }, + { + "query_block": { + "select_id": 3, + "operation": "UNION", + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "table": { + "table_name": "t1", + "access_type": "ALL", + "r_loops": 1, + "rows": 4, + "r_rows": 4, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100, + "attached_condition": "t1.c is not null" + }, + "table": { + "table_name": "<derived2>", + "access_type": "ref", + "possible_keys": ["key0"], + "key": "key0", + "key_length": "9", + "used_key_parts": ["a"], + "ref": ["test.t1.c"], + "r_loops": 4, + "rows": 2, + "r_rows": 0.5, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100 + } + } + } + ] + } + } + } + } + } + } + ] + } + } +} +prepare stmt from "with recursive r_cte as +( select * from t1 as s +union +select t1.* from t1, r_cte as r where t1.c = r.a ) +select 0 as b FROM dual union all select b FROM r_cte as t"; +execute stmt; +b +0 +3 +7 +1 +3 +execute stmt; +b +0 +3 +7 +1 +3 +deallocate prepare stmt; +#checking hanging cte that uses a recursive cte +explain with h_cte as +( with recursive r_cte as +( select * from t1 as s +union +select t1.* from t1, r_cte as r where t1.c = r.a ) +select 0 as b FROM dual union all select b FROM r_cte as t) +select * from t1 as tt; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY tt ALL NULL NULL NULL NULL 4 +with h_cte as +( with recursive r_cte as +( select * from t1 as s +union +select t1.* from t1, r_cte as r where t1.c = r.a ) +select 0 as b FROM dual union all select b FROM r_cte as t) +select * from t1 as tt; +a b c +1 3 12 +2 7 15 +3 1 3 +4 3 1 +analyze format=json with h_cte as +( with recursive r_cte as +( select * from t1 as s +union +select t1.* from t1, r_cte as r where t1.c = r.a ) +select 0 as b FROM dual union all select b FROM r_cte as t) +select * from t1 as tt; +ANALYZE +{ + "query_block": { + "select_id": 1, + "r_loops": 1, + "r_total_time_ms": "REPLACED", + "table": { + "table_name": "tt", + "access_type": "ALL", + "r_loops": 1, + "rows": 4, + "r_rows": 4, + "r_table_time_ms": "REPLACED", + "r_other_time_ms": "REPLACED", + "filtered": 100, + "r_filtered": 100 + } + } +} +prepare stmt from "with h_cte as +( with recursive r_cte as +( select * from t1 as s +union +select t1.* from t1, r_cte as r where t1.c = r.a ) +select 0 as b FROM dual union all select b FROM r_cte as t) +select * from t1 as tt"; +execute stmt; +a b c +1 3 12 +2 7 15 +3 1 3 +4 3 1 +execute stmt; +a b c +1 3 12 +2 7 15 +3 1 3 +4 3 1 +deallocate prepare stmt; +drop table t1; +# # End of 10.2 tests # # diff --git a/mysql-test/main/cte_recursive.test b/mysql-test/main/cte_recursive.test index 023d961ce5e..0e29824e97e 100644 --- a/mysql-test/main/cte_recursive.test +++ b/mysql-test/main/cte_recursive.test @@ -2676,6 +2676,56 @@ DROP PROCEDURE p; DROP TABLE t1,t2,t3,t4; --echo # +--echo # MDEV-23619: recursive CTE used only in the second operand of UNION +--echo # + +create table t1 ( + a bigint(10) not null auto_increment, + b int(5) not null, + c bigint(10) default null, + primary key (a) +) engine myisam; +insert into t1 values + (1,3,12), (2,7,15), (3,1,3), (4,3,1); + +let $q= +with recursive r_cte as +( select * from t1 as s + union + select t1.* from t1, r_cte as r where t1.c = r.a ) +select 0 as b FROM dual union all select b FROM r_cte as t; + +eval explain $q; +eval $q; +--source include/analyze-format.inc +eval analyze format=json $q; +eval prepare stmt from "$q"; +execute stmt; +execute stmt; +deallocate prepare stmt; + +--echo #checking hanging cte that uses a recursive cte +let $q1= +with h_cte as +( with recursive r_cte as + ( select * from t1 as s + union + select t1.* from t1, r_cte as r where t1.c = r.a ) + select 0 as b FROM dual union all select b FROM r_cte as t) +select * from t1 as tt; + +eval explain $q1; +eval $q1; +--source include/analyze-format.inc +eval analyze format=json $q1; +eval prepare stmt from "$q1"; +execute stmt; +execute stmt; +deallocate prepare stmt; + +drop table t1; + +--echo # --echo # End of 10.2 tests --echo # diff --git a/mysql-test/main/lock_user.result b/mysql-test/main/lock_user.result index 5048c5a9bee..3ee8bbf2296 100644 --- a/mysql-test/main/lock_user.result +++ b/mysql-test/main/lock_user.result @@ -130,5 +130,42 @@ connection default; # alter user user1@localhost account lock; ERROR HY000: Access denied, this account is locked +# +# MDEV-24098 SHOW CREATE USER invalid for both PASSWORD EXPIRE and +# and LOCKED +# +alter user user1@localhost PASSWORD EXPIRE; +show create user user1@localhost; +CREATE USER for user1@localhost +CREATE USER `user1`@`localhost` ACCOUNT LOCK PASSWORD EXPIRE +drop user user1@localhost; +# +# MDEV-24098 CREATE USER/ALTER USER PASSWORD EXPIRE/LOCK in +# either order. +# +create user user1@localhost PASSWORD EXPIRE ACCOUNT LOCK; +show create user user1@localhost; +CREATE USER for user1@localhost +CREATE USER `user1`@`localhost` ACCOUNT LOCK PASSWORD EXPIRE +drop user user1@localhost; +create user user1@localhost ACCOUNT LOCK PASSWORD EXPIRE; +show create user user1@localhost; +CREATE USER for user1@localhost +CREATE USER `user1`@`localhost` ACCOUNT LOCK PASSWORD EXPIRE +alter user user1@localhost PASSWORD EXPIRE NEVER ACCOUNT UNLOCK ; +show create user user1@localhost; +CREATE USER for user1@localhost +CREATE USER `user1`@`localhost` PASSWORD EXPIRE +alter user user1@localhost ACCOUNT LOCK PASSWORD EXPIRE DEFAULT; +show create user user1@localhost; +CREATE USER for user1@localhost +CREATE USER `user1`@`localhost` ACCOUNT LOCK PASSWORD EXPIRE +alter user user1@localhost PASSWORD EXPIRE INTERVAL 60 DAY ACCOUNT UNLOCK; +select * from mysql.global_priv where user='user1'; +Host User Priv +localhost user1 {"access":0,"version_id":XXX,"plugin":"mysql_native_password","authentication_string":"","account_locked":false,"password_last_changed":0,"password_lifetime":60} +show create user user1@localhost; +CREATE USER for user1@localhost +CREATE USER `user1`@`localhost` PASSWORD EXPIRE drop user user1@localhost; drop user user2@localhost; diff --git a/mysql-test/main/lock_user.test b/mysql-test/main/lock_user.test index 17ce1cc79da..4e480c19360 100644 --- a/mysql-test/main/lock_user.test +++ b/mysql-test/main/lock_user.test @@ -137,6 +137,33 @@ alter user user1@localhost account lock; --error ER_ACCOUNT_HAS_BEEN_LOCKED --change_user user1 +--echo # +--echo # MDEV-24098 SHOW CREATE USER invalid for both PASSWORD EXPIRE and +--echo # and LOCKED +--echo # +alter user user1@localhost PASSWORD EXPIRE; +show create user user1@localhost; +drop user user1@localhost; + +--echo # +--echo # MDEV-24098 CREATE USER/ALTER USER PASSWORD EXPIRE/LOCK in +--echo # either order. +--echo # +create user user1@localhost PASSWORD EXPIRE ACCOUNT LOCK; +show create user user1@localhost; +drop user user1@localhost; +create user user1@localhost ACCOUNT LOCK PASSWORD EXPIRE; +show create user user1@localhost; +alter user user1@localhost PASSWORD EXPIRE NEVER ACCOUNT UNLOCK ; +show create user user1@localhost; +alter user user1@localhost ACCOUNT LOCK PASSWORD EXPIRE DEFAULT; +show create user user1@localhost; +# note output needs to be corrected by MDEV-24114: password expire users cannot be unexpired +alter user user1@localhost PASSWORD EXPIRE INTERVAL 60 DAY ACCOUNT UNLOCK; +--replace_regex /"version_id":[0-9]*,/"version_id":XXX,/ +select * from mysql.global_priv where user='user1'; +show create user user1@localhost; + drop user user1@localhost; drop user user2@localhost; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 37089f6dec6..b2f948fe31d 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -647,6 +647,7 @@ sub run_test_server ($$$) { # Client disconnected mtr_verbose("Child closed socket"); $s->remove($sock); + $sock->close; if (--$childs == 0){ return ("Completed", $test_failure, $completed, $extra_warnings); } @@ -816,6 +817,7 @@ sub run_test_server ($$$) { # Test failure due to warnings, force is off return ("Warnings in log", 1, $completed, $extra_warnings); } + next; } elsif ($line =~ /^SPENT/) { add_total_times($line); @@ -4102,6 +4104,7 @@ sub run_testcase ($$) { if (start_servers($tinfo)) { report_failure_and_restart($tinfo); + unlink $path_current_testlog; return 1; } } diff --git a/mysql-test/suite/galera/r/MDEV-24063.result b/mysql-test/suite/galera/r/MDEV-24063.result new file mode 100644 index 00000000000..757cc07a642 --- /dev/null +++ b/mysql-test/suite/galera/r/MDEV-24063.result @@ -0,0 +1,8 @@ +connection node_2; +connection node_1; +connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2; +connection node_2a; +CREATE TABLE t1 (f1 INTEGER PRIMARY KEY); +connection node_2; +SET GLOBAL wsrep_on=OFF; +DROP TABLE t1; diff --git a/mysql-test/suite/galera/r/galera_ddl_fk_conflict.result b/mysql-test/suite/galera/r/galera_ddl_fk_conflict.result new file mode 100644 index 00000000000..5f09345b79b --- /dev/null +++ b/mysql-test/suite/galera/r/galera_ddl_fk_conflict.result @@ -0,0 +1,657 @@ +connection node_2; +connection node_1; +connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1; +connection node_1a; +SET SESSION wsrep_sync_wait=0; +connect node_1b, 127.0.0.1, root, , test, $NODE_MYPORT_1; +connection node_1b; +SET SESSION wsrep_sync_wait=0; +###################################################################### +# Test for OPTIMIZE +###################################################################### +###################################################################### +# +# Scenario #1: DML working on FK parent table BF aborted by DDL +# over child table +# +###################################################################### +connection node_1; +SET SESSION wsrep_sync_wait=0; +CREATE TABLE p1 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p1 VALUES (1, 'INITIAL VALUE'); +CREATE TABLE p2 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p2 VALUES (1, 'INITIAL VALUE'); +INSERT INTO p2 VALUES (2, 'INITIAL VALUE'); +CREATE TABLE c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT INTO c1 VALUES (1,1); +CREATE TABLE c2 (pk INTEGER PRIMARY KEY, fk1 INTEGER, fk2 INTEGER, FOREIGN KEY (fk1) REFERENCES p1(pk), FOREIGN KEY (fk2) REFERENCES p2(pk)); +INSERT INTO c2 VALUES (1,1,1), (2,1,2); +connection node_1; +SET AUTOCOMMIT=ON; +START TRANSACTION; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +connection node_2; +SET SESSION wsrep_sync_wait=0; +OPTIMIZE TABLE c1 ; +Table Op Msg_type Msg_text +test.c1 optimize note Table does not support optimize, doing recreate + analyze instead +test.c1 optimize status OK +connection node_1; +COMMIT; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +###################################################################### +# +# Scenario #2: DML working on FK parent table tries to replicate, but +# fails in certification for earlier DDL on child table +# +###################################################################### +connection node_1; +BEGIN; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +OPTIMIZE TABLE c1 ; +Table Op Msg_type Msg_text +test.c1 optimize note Table does not support optimize, doing recreate + analyze instead +test.c1 optimize status OK +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +COMMIT; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +connection node_1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +###################################################################### +# +# Scenario #3: 2 DMLs working on two FK parent tables try to replicate, +# but fails in certification for earlier DDL on child table +# which is child to both FK parents +# +###################################################################### +connection node_1; +BEGIN; +connection node_1b; +BEGIN; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +OPTIMIZE TABLE c2 ; +Table Op Msg_type Msg_text +test.c2 optimize note Table does not support optimize, doing recreate + analyze instead +test.c2 optimize status OK +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +COMMIT; +connection node_1b; +UPDATE p2 SET f2 = 'TO DEADLOCK' WHERE pk = 2; +COMMIT; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +connection node_1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +connection node_1b; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +DROP TABLE c1, c2; +DROP TABLE p1, p2; +###################################################################### +# Test for OPTIMIZE +###################################################################### +connection node_1; +SET SESSION wsrep_sync_wait=0; +CREATE TABLE p1 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p1 VALUES (1, 'INITIAL VALUE'); +CREATE TABLE c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT INTO c1 VALUES (1,1); +###################################################################### +# +# Scenario #4: DML working on FK parent table tries to replicate, but +# fails in certification for earlier DDL on child table +# and another temporary table. TMP table should be skipped +# but FK child table should be replicated with proper keys +# +###################################################################### +connection node_1; +BEGIN; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +CREATE TEMPORARY TABLE tmp (i int); +OPTIMIZE TABLE c1, tmp ; +Table Op Msg_type Msg_text +test.c1 optimize note Table does not support optimize, doing recreate + analyze instead +test.c1 optimize status OK +test.tmp optimize note Table does not support optimize, doing recreate + analyze instead +test.tmp optimize status OK +DROP TABLE tmp; +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +COMMIT; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +connection node_1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +DROP TABLE c1; +DROP TABLE p1; +###################################################################### +# Test for REPAIR +###################################################################### +###################################################################### +# +# Scenario #1: DML working on FK parent table BF aborted by DDL +# over child table +# +###################################################################### +connection node_1; +SET SESSION wsrep_sync_wait=0; +CREATE TABLE p1 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p1 VALUES (1, 'INITIAL VALUE'); +CREATE TABLE p2 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p2 VALUES (1, 'INITIAL VALUE'); +INSERT INTO p2 VALUES (2, 'INITIAL VALUE'); +CREATE TABLE c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT INTO c1 VALUES (1,1); +CREATE TABLE c2 (pk INTEGER PRIMARY KEY, fk1 INTEGER, fk2 INTEGER, FOREIGN KEY (fk1) REFERENCES p1(pk), FOREIGN KEY (fk2) REFERENCES p2(pk)); +INSERT INTO c2 VALUES (1,1,1), (2,1,2); +connection node_1; +SET AUTOCOMMIT=ON; +START TRANSACTION; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +connection node_2; +SET SESSION wsrep_sync_wait=0; +REPAIR TABLE c1 ; +Table Op Msg_type Msg_text +test.c1 repair note The storage engine for the table doesn't support repair +connection node_1; +COMMIT; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +###################################################################### +# +# Scenario #2: DML working on FK parent table tries to replicate, but +# fails in certification for earlier DDL on child table +# +###################################################################### +connection node_1; +BEGIN; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +REPAIR TABLE c1 ; +Table Op Msg_type Msg_text +test.c1 repair note The storage engine for the table doesn't support repair +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +COMMIT; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +connection node_1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +###################################################################### +# +# Scenario #3: 2 DMLs working on two FK parent tables try to replicate, +# but fails in certification for earlier DDL on child table +# which is child to both FK parents +# +###################################################################### +connection node_1; +BEGIN; +connection node_1b; +BEGIN; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +REPAIR TABLE c2 ; +Table Op Msg_type Msg_text +test.c2 repair note The storage engine for the table doesn't support repair +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +COMMIT; +connection node_1b; +UPDATE p2 SET f2 = 'TO DEADLOCK' WHERE pk = 2; +COMMIT; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +connection node_1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +connection node_1b; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +DROP TABLE c1, c2; +DROP TABLE p1, p2; +###################################################################### +# Test for REPAIR +###################################################################### +connection node_1; +SET SESSION wsrep_sync_wait=0; +CREATE TABLE p1 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p1 VALUES (1, 'INITIAL VALUE'); +CREATE TABLE c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT INTO c1 VALUES (1,1); +###################################################################### +# +# Scenario #4: DML working on FK parent table tries to replicate, but +# fails in certification for earlier DDL on child table +# and another temporary table. TMP table should be skipped +# but FK child table should be replicated with proper keys +# +###################################################################### +connection node_1; +BEGIN; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +CREATE TEMPORARY TABLE tmp (i int); +REPAIR TABLE c1, tmp ; +Table Op Msg_type Msg_text +test.c1 repair note The storage engine for the table doesn't support repair +test.tmp repair note The storage engine for the table doesn't support repair +DROP TABLE tmp; +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +COMMIT; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +connection node_1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +DROP TABLE c1; +DROP TABLE p1; +###################################################################### +# Test for ALTER ENGINE=INNODB +###################################################################### +###################################################################### +# +# Scenario #1: DML working on FK parent table BF aborted by DDL +# over child table +# +###################################################################### +connection node_1; +SET SESSION wsrep_sync_wait=0; +CREATE TABLE p1 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p1 VALUES (1, 'INITIAL VALUE'); +CREATE TABLE p2 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p2 VALUES (1, 'INITIAL VALUE'); +INSERT INTO p2 VALUES (2, 'INITIAL VALUE'); +CREATE TABLE c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT INTO c1 VALUES (1,1); +CREATE TABLE c2 (pk INTEGER PRIMARY KEY, fk1 INTEGER, fk2 INTEGER, FOREIGN KEY (fk1) REFERENCES p1(pk), FOREIGN KEY (fk2) REFERENCES p2(pk)); +INSERT INTO c2 VALUES (1,1,1), (2,1,2); +connection node_1; +SET AUTOCOMMIT=ON; +START TRANSACTION; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +connection node_2; +SET SESSION wsrep_sync_wait=0; +ALTER TABLE c1 ENGINE=INNODB; +connection node_1; +COMMIT; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +###################################################################### +# +# Scenario #2: DML working on FK parent table tries to replicate, but +# fails in certification for earlier DDL on child table +# +###################################################################### +connection node_1; +BEGIN; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +ALTER TABLE c1 ENGINE=INNODB; +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +COMMIT; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +connection node_1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +###################################################################### +# +# Scenario #3: 2 DMLs working on two FK parent tables try to replicate, +# but fails in certification for earlier DDL on child table +# which is child to both FK parents +# +###################################################################### +connection node_1; +BEGIN; +connection node_1b; +BEGIN; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +ALTER TABLE c2 ENGINE=INNODB; +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +COMMIT; +connection node_1b; +UPDATE p2 SET f2 = 'TO DEADLOCK' WHERE pk = 2; +COMMIT; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +connection node_1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +connection node_1b; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +DROP TABLE c1, c2; +DROP TABLE p1, p2; +###################################################################### +# Test for TRUNCATE +###################################################################### +###################################################################### +# +# Scenario #1: DML working on FK parent table BF aborted by DDL +# over child table +# +###################################################################### +connection node_1; +SET SESSION wsrep_sync_wait=0; +CREATE TABLE p1 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p1 VALUES (1, 'INITIAL VALUE'); +CREATE TABLE p2 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p2 VALUES (1, 'INITIAL VALUE'); +INSERT INTO p2 VALUES (2, 'INITIAL VALUE'); +CREATE TABLE c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT INTO c1 VALUES (1,1); +CREATE TABLE c2 (pk INTEGER PRIMARY KEY, fk1 INTEGER, fk2 INTEGER, FOREIGN KEY (fk1) REFERENCES p1(pk), FOREIGN KEY (fk2) REFERENCES p2(pk)); +INSERT INTO c2 VALUES (1,1,1), (2,1,2); +connection node_1; +SET AUTOCOMMIT=ON; +START TRANSACTION; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +connection node_2; +SET SESSION wsrep_sync_wait=0; +TRUNCATE TABLE c1 ; +connection node_1; +COMMIT; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +###################################################################### +# +# Scenario #2: DML working on FK parent table tries to replicate, but +# fails in certification for earlier DDL on child table +# +###################################################################### +connection node_1; +BEGIN; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +TRUNCATE TABLE c1 ; +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +COMMIT; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +connection node_1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +###################################################################### +# +# Scenario #3: 2 DMLs working on two FK parent tables try to replicate, +# but fails in certification for earlier DDL on child table +# which is child to both FK parents +# +###################################################################### +connection node_1; +BEGIN; +connection node_1b; +BEGIN; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'dbug=d,apply_monitor_slave_enter_sync'; +connection node_2; +TRUNCATE TABLE c2 ; +connection node_1a; +SET SESSION wsrep_on = 0; +SET SESSION wsrep_on = 1; +SET GLOBAL wsrep_provider_options = 'dbug='; +connection node_1; +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +COMMIT; +connection node_1b; +UPDATE p2 SET f2 = 'TO DEADLOCK' WHERE pk = 2; +COMMIT; +connection node_1a; +SET GLOBAL wsrep_provider_options = 'signal=apply_monitor_slave_enter_sync'; +connection node_1; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +connection node_1b; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +SELECT 'I deadlocked'; +I deadlocked +I deadlocked +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +connection node_2; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +EXPECT_1 +1 +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; +EXPECT_2 +2 +DROP TABLE c1, c2; +DROP TABLE p1, p2; diff --git a/mysql-test/suite/galera/r/galera_trigger.result b/mysql-test/suite/galera/r/galera_trigger.result index 40b63b327af..2a31a104404 100644 --- a/mysql-test/suite/galera/r/galera_trigger.result +++ b/mysql-test/suite/galera/r/galera_trigger.result @@ -45,34 +45,34 @@ connection node_2; set session wsrep_sync_wait=15; insert into t1(value) values (3); insert into t1(value) values (4); -select * from t2; -id tbl action -1 t1 INSERT -3 t1 INSERT -4 t1 INSERT -6 t1 INSERT +select tbl, action from t2; +tbl action +t1 INSERT +t1 INSERT +t1 INSERT +t1 INSERT connection node_1; drop trigger if exists log_insert; insert into t1(value) values (5); -select * from t2; -id tbl action -1 t1 INSERT -3 t1 INSERT -4 t1 INSERT -6 t1 INSERT +select tbl, action from t2; +tbl action +t1 INSERT +t1 INSERT +t1 INSERT +t1 INSERT connection node_2; insert into t1(value) values (6); -select * from t2; -id tbl action -1 t1 INSERT -3 t1 INSERT -4 t1 INSERT -6 t1 INSERT +select tbl, action from t2; +tbl action +t1 INSERT +t1 INSERT +t1 INSERT +t1 INSERT connection node_1; -select * from t2; -id tbl action -1 t1 INSERT -3 t1 INSERT -4 t1 INSERT -6 t1 INSERT +select tbl, action from t2; +tbl action +t1 INSERT +t1 INSERT +t1 INSERT +t1 INSERT drop table t1, t2; diff --git a/mysql-test/suite/galera/t/MDEV-24063.test b/mysql-test/suite/galera/t/MDEV-24063.test new file mode 100644 index 00000000000..24c5071cb15 --- /dev/null +++ b/mysql-test/suite/galera/t/MDEV-24063.test @@ -0,0 +1,20 @@ +# +# MDEV-24063 +# +# my_bool wsrep_thd_is_aborting(const THD*): +# Assertion `((&(&thd->LOCK_thd_data)->m_mutex)->count > 0 && +# pthread_equal(pthread_self(), (&(&thd->LOCK_thd_data)->m_mutex)->thread))' failed. +# + +--source include/galera_cluster.inc + +--connect node_2a, 127.0.0.1, root, , test, $NODE_MYPORT_2 +--connection node_2a +CREATE TABLE t1 (f1 INTEGER PRIMARY KEY); + +--connection node_2 +SET GLOBAL wsrep_on=OFF; +--source include/shutdown_mysqld.inc +--source include/start_mysqld.inc + +DROP TABLE t1; diff --git a/mysql-test/suite/galera/t/galera_ddl_fk_conflict.inc b/mysql-test/suite/galera/t/galera_ddl_fk_conflict.inc new file mode 100644 index 00000000000..06b7bbe41c4 --- /dev/null +++ b/mysql-test/suite/galera/t/galera_ddl_fk_conflict.inc @@ -0,0 +1,192 @@ +# +# Test for MDL BF-BF lock conflict +# There are some DDL statements, which take extensive MDL lock for +# a table referenced by foreign key constraint from the actual affetec table. +# This extensive MDL lock may cause MDL BF-BF confclict situations, if the +# FK parent table is not listed as certification key in the replication write set. +# i.e. if replication allows such DDL to apply in parallel with regular DML operating +# on the FK parent table. +# +# This test has two scenarios, where DML modifies FK parent table in node 1, +# and offending DDL for FK child table is sent from node 2. +# +# param: $table_admin_command +# DDL table command to test, script will build full SQL statement: +# $table_admin_command TABLE c; +# +# param: $table_admin_command_end +# Optional additional SQL syntax to end the SQL statement, if any +# $table_admin_command TABLE c $table_admin_command_end; +# +# scenario 1, can be used to test if a DDL statement causes such MDL locking vulnerability. +# call this test script with some table DDL command in $table_admin_command +# if scenario 1 passes (especially COMMIT does fail for ER_LOCK_DEADLOCK), +# then this particular DDL is vulnerable. scenraio 2 should fail for this DDL +# unless code has not been fixed to append parent table certification keys for it. +# + +--echo ###################################################################### +--echo # Test for $table_admin_command $table_admin_command_end +--echo ###################################################################### + + +--echo ###################################################################### +--echo # +--echo # Scenario #1: DML working on FK parent table BF aborted by DDL +--echo # over child table +--echo # +--echo ###################################################################### + +--connection node_1 +SET SESSION wsrep_sync_wait=0; + +CREATE TABLE p1 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p1 VALUES (1, 'INITIAL VALUE'); + + +CREATE TABLE p2 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p2 VALUES (1, 'INITIAL VALUE'); +INSERT INTO p2 VALUES (2, 'INITIAL VALUE'); + +CREATE TABLE c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT INTO c1 VALUES (1,1); + +CREATE TABLE c2 (pk INTEGER PRIMARY KEY, fk1 INTEGER, fk2 INTEGER, FOREIGN KEY (fk1) REFERENCES p1(pk), FOREIGN KEY (fk2) REFERENCES p2(pk)); +INSERT INTO c2 VALUES (1,1,1), (2,1,2); + +--connection node_1 +SET AUTOCOMMIT=ON; +START TRANSACTION; + +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; + +--connection node_2 +SET SESSION wsrep_sync_wait=0; +# wait for tables to be created in node 2 and all rows inserted as well +--let $wait_condition = SELECT COUNT(*) = 2 FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE NAME LIKE 'test/c%' +--source include/wait_condition.inc +--let $wait_condition = SELECT COUNT(*) = 2 FROM c2 +--source include/wait_condition.inc + +# replicate the DDL to be tested +--eval $table_admin_command TABLE c1 $table_admin_command_end + +--connection node_1 +--error ER_LOCK_DEADLOCK +COMMIT; + +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; + +--connection node_2 +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; + +--echo ###################################################################### +--echo # +--echo # Scenario #2: DML working on FK parent table tries to replicate, but +--echo # fails in certification for earlier DDL on child table +--echo # +--echo ###################################################################### + +--connection node_1 +BEGIN; + +# Block the applier on node #1 and issue DDL on node 2 +--let $galera_sync_point = apply_monitor_slave_enter_sync +--source include/galera_set_sync_point.inc + +--connection node_2 +--eval $table_admin_command TABLE c1 $table_admin_command_end + +--connection node_1a +--source include/galera_wait_sync_point.inc +--source include/galera_clear_sync_point.inc +--let $expected_cert_failures = `SELECT VARIABLE_VALUE+1 FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_cert_failures'` + +--connection node_1 +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +--send COMMIT + +--connection node_1a +--let $wait_condition = SELECT VARIABLE_VALUE = $expected_cert_failures FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_cert_failures' +--source include/wait_condition.inc + +--let $galera_sync_point = apply_monitor_slave_enter_sync +--source include/galera_signal_sync_point.inc + +--connection node_1 +--error ER_LOCK_DEADLOCK +--reap + +SELECT 'I deadlocked'; + +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; + +--connection node_2 +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; + + +--echo ###################################################################### +--echo # +--echo # Scenario #3: 2 DMLs working on two FK parent tables try to replicate, +--echo # but fails in certification for earlier DDL on child table +--echo # which is child to both FK parents +--echo # +--echo ###################################################################### + +--connection node_1 +BEGIN; + +--connection node_1b +BEGIN; + +--connection node_1a +# Block the applier on node #1 and issue DDL on node 2 +--let $galera_sync_point = apply_monitor_slave_enter_sync +--source include/galera_set_sync_point.inc + +--connection node_2 +--eval $table_admin_command TABLE c2 $table_admin_command_end + +--connection node_1a +--source include/galera_wait_sync_point.inc +--source include/galera_clear_sync_point.inc +--let $expected_cert_failures = `SELECT VARIABLE_VALUE+2 FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_cert_failures'` + +--connection node_1 +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +--send COMMIT + +--connection node_1b +UPDATE p2 SET f2 = 'TO DEADLOCK' WHERE pk = 2; +--send COMMIT + +--connection node_1a +--let $wait_condition = SELECT VARIABLE_VALUE = $expected_cert_failures FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_cert_failures' +--source include/wait_condition.inc + +--let $galera_sync_point = apply_monitor_slave_enter_sync +--source include/galera_signal_sync_point.inc + +--connection node_1 +--error ER_LOCK_DEADLOCK +--reap +SELECT 'I deadlocked'; + +--connection node_1b +--error ER_LOCK_DEADLOCK +--reap +SELECT 'I deadlocked'; + +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; + +--connection node_2 +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; +SELECT COUNT(*) AS EXPECT_2 FROM p2 WHERE f2 = 'INITIAL VALUE'; + +DROP TABLE c1, c2; +DROP TABLE p1, p2; diff --git a/mysql-test/suite/galera/t/galera_ddl_fk_conflict.test b/mysql-test/suite/galera/t/galera_ddl_fk_conflict.test new file mode 100644 index 00000000000..88837933e5a --- /dev/null +++ b/mysql-test/suite/galera/t/galera_ddl_fk_conflict.test @@ -0,0 +1,37 @@ +# +# MDL BF-BF lock conflict +# + +--source include/galera_cluster.inc +--source include/have_innodb.inc +--source include/have_debug_sync.inc +--source include/galera_have_debug_sync.inc + +# sync point controlling session +--connect node_1a, 127.0.0.1, root, , test, $NODE_MYPORT_1 +--connection node_1a +SET SESSION wsrep_sync_wait=0; + +# secondary conflicting DML victim session +--connect node_1b, 127.0.0.1, root, , test, $NODE_MYPORT_1 +--connection node_1b +SET SESSION wsrep_sync_wait=0; + +--let $table_admin_command = OPTIMIZE +--source galera_ddl_fk_conflict.inc +--source galera_ddl_fk_conflict_with_tmp.inc + +--let $table_admin_command = REPAIR +--source galera_ddl_fk_conflict.inc +--source galera_ddl_fk_conflict_with_tmp.inc + +--let $table_admin_command = ALTER +--let $table_admin_command_end = ENGINE=INNODB +--source galera_ddl_fk_conflict.inc + +--let $table_admin_command = TRUNCATE +--let $table_admin_command_end = +--source galera_ddl_fk_conflict.inc + +# CHECK and ANALYZE are not affected + diff --git a/mysql-test/suite/galera/t/galera_ddl_fk_conflict_with_tmp.inc b/mysql-test/suite/galera/t/galera_ddl_fk_conflict_with_tmp.inc new file mode 100644 index 00000000000..acf3c54180b --- /dev/null +++ b/mysql-test/suite/galera/t/galera_ddl_fk_conflict_with_tmp.inc @@ -0,0 +1,69 @@ +--echo ###################################################################### +--echo # Test for $table_admin_command $table_admin_command_end +--echo ###################################################################### + + +--connection node_1 +SET SESSION wsrep_sync_wait=0; + +CREATE TABLE p1 (pk INTEGER PRIMARY KEY, f2 CHAR(30)); +INSERT INTO p1 VALUES (1, 'INITIAL VALUE'); + + +CREATE TABLE c1 (pk INTEGER PRIMARY KEY, fk INTEGER, FOREIGN KEY (fk) REFERENCES p1(pk)); +INSERT INTO c1 VALUES (1,1); + +--echo ###################################################################### +--echo # +--echo # Scenario #4: DML working on FK parent table tries to replicate, but +--echo # fails in certification for earlier DDL on child table +--echo # and another temporary table. TMP table should be skipped +--echo # but FK child table should be replicated with proper keys +--echo # +--echo ###################################################################### + +--connection node_1 +BEGIN; + +# Block the applier on node #1 and issue DDL on node 2 +--let $galera_sync_point = apply_monitor_slave_enter_sync +--source include/galera_set_sync_point.inc + +--connection node_2 +# wait for tables to be created in node 2 and all rows inserted as well +--let $wait_condition = SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES WHERE NAME LIKE 'test/c1' +--source include/wait_condition.inc +--let $wait_condition = SELECT COUNT(*) = 1 FROM c1 +--source include/wait_condition.inc +CREATE TEMPORARY TABLE tmp (i int); +--eval $table_admin_command TABLE c1, tmp $table_admin_command_end +DROP TABLE tmp; + +--connection node_1a +--source include/galera_wait_sync_point.inc +--source include/galera_clear_sync_point.inc +--let $expected_cert_failures = `SELECT VARIABLE_VALUE+1 FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_cert_failures'` + +--connection node_1 +UPDATE p1 SET f2 = 'TO DEADLOCK' WHERE pk = 1; +--send COMMIT + +--connection node_1a +--let $wait_condition = SELECT VARIABLE_VALUE = $expected_cert_failures FROM information_schema.global_status WHERE VARIABLE_NAME = 'wsrep_local_cert_failures' +--source include/wait_condition.inc + +--let $galera_sync_point = apply_monitor_slave_enter_sync +--source include/galera_signal_sync_point.inc + +--connection node_1 +--error ER_LOCK_DEADLOCK +--reap + +SELECT 'I deadlocked'; +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; + +--connection node_2 +SELECT COUNT(*) AS EXPECT_1 FROM p1 WHERE f2 = 'INITIAL VALUE'; + +DROP TABLE c1; +DROP TABLE p1; diff --git a/mysql-test/suite/galera/t/galera_trigger.test b/mysql-test/suite/galera/t/galera_trigger.test index f85a75d0ff1..3c66b941e32 100644 --- a/mysql-test/suite/galera/t/galera_trigger.test +++ b/mysql-test/suite/galera/t/galera_trigger.test @@ -55,18 +55,18 @@ insert into t1(value) values (2); set session wsrep_sync_wait=15; insert into t1(value) values (3); insert into t1(value) values (4); -select * from t2; +select tbl, action from t2; --connection node_1 drop trigger if exists log_insert; insert into t1(value) values (5); -select * from t2; +select tbl, action from t2; --connection node_2 insert into t1(value) values (6); -select * from t2; +select tbl, action from t2; --connection node_1 -select * from t2; +select tbl, action from t2; drop table t1, t2; diff --git a/mysql-test/suite/galera/t/mysql-wsrep#198-master.opt b/mysql-test/suite/galera/t/mysql-wsrep#198-master.opt deleted file mode 100644 index beae84b3862..00000000000 --- a/mysql-test/suite/galera/t/mysql-wsrep#198-master.opt +++ /dev/null @@ -1 +0,0 @@ ---log-bin diff --git a/mysql-test/suite/galera/t/mysql-wsrep#198.cnf b/mysql-test/suite/galera/t/mysql-wsrep#198.cnf new file mode 100644 index 00000000000..d599f3940e4 --- /dev/null +++ b/mysql-test/suite/galera/t/mysql-wsrep#198.cnf @@ -0,0 +1,9 @@ +!include ../galera_2nodes.cnf + +[mysqld.1] +log-bin +wsrep-debug=1 + +[mysqld.1] +log-bin +wsrep-debug=1 diff --git a/mysql-test/suite/galera/t/mysql-wsrep#198.test b/mysql-test/suite/galera/t/mysql-wsrep#198.test index a80d030a8b0..a8190f2ae61 100644 --- a/mysql-test/suite/galera/t/mysql-wsrep#198.test +++ b/mysql-test/suite/galera/t/mysql-wsrep#198.test @@ -1,5 +1,6 @@ --source include/galera_cluster.inc --source include/have_innodb.inc +--source include/force_restart.inc CREATE TABLE t1 (id INT PRIMARY KEY) ENGINE=InnoDB; CREATE TABLE t2 (id INT PRIMARY KEY) ENGINE=InnoDB; diff --git a/mysql-test/suite/maria/repair.result b/mysql-test/suite/maria/repair.result index 6bb9e1b5b9e..296f251aa36 100644 --- a/mysql-test/suite/maria/repair.result +++ b/mysql-test/suite/maria/repair.result @@ -22,3 +22,19 @@ i 1 UNLOCK TABLES; DROP TABLE t1; +# +# MDEV-23824 SIGSEGV in end_io_cache on REPAIR LOCAL TABLE for Aria table +# +CREATE TABLE t1 (i INT) ENGINE=Aria; +INSERT INTO t1 VALUES (1); +SET max_session_mem_used=50000; +REPAIR LOCAL TABLE t1 USE_FRM; +Table Op Msg_type Msg_text +t1 repair error Failed to open partially repaired table +Warnings: +Error 1290 The MariaDB server is running with the --max-thread-mem-used=50000 option so it cannot execute this statement +REPAIR LOCAL TABLE t1; +Table Op Msg_type Msg_text +test.t1 repair Error The MariaDB server is running with the --max-thread-mem-used=50000 option so it cannot execute this statement +test.t1 repair error Corrupt +DROP TABLE t1; diff --git a/mysql-test/suite/maria/repair.test b/mysql-test/suite/maria/repair.test index 2f713950d3e..9603a949f9b 100644 --- a/mysql-test/suite/maria/repair.test +++ b/mysql-test/suite/maria/repair.test @@ -22,3 +22,14 @@ SELECT * FROM INFORMATION_SCHEMA.TABLES; SELECT * FROM t1; UNLOCK TABLES; DROP TABLE t1; + +--echo # +--echo # MDEV-23824 SIGSEGV in end_io_cache on REPAIR LOCAL TABLE for Aria table +--echo # + +CREATE TABLE t1 (i INT) ENGINE=Aria; +INSERT INTO t1 VALUES (1); +SET max_session_mem_used=50000; +REPAIR LOCAL TABLE t1 USE_FRM; +REPAIR LOCAL TABLE t1; +DROP TABLE t1; diff --git a/mysql-test/suite/perfschema/r/sxlock_func.result b/mysql-test/suite/perfschema/r/sxlock_func.result index ad544f78a28..b51234feda6 100644 --- a/mysql-test/suite/perfschema/r/sxlock_func.result +++ b/mysql-test/suite/perfschema/r/sxlock_func.result @@ -16,7 +16,6 @@ wait/synch/sxlock/innodb/dict_operation_lock wait/synch/sxlock/innodb/fil_space_latch wait/synch/sxlock/innodb/fts_cache_init_rw_lock wait/synch/sxlock/innodb/fts_cache_rw_lock -wait/synch/sxlock/innodb/index_online_log wait/synch/sxlock/innodb/index_tree_rw_lock wait/synch/sxlock/innodb/trx_i_s_cache_lock wait/synch/sxlock/innodb/trx_purge_latch diff --git a/mysql-test/suite/period/r/create.result b/mysql-test/suite/period/r/create.result index b454661afc9..69d7e918804 100644 --- a/mysql-test/suite/period/r/create.result +++ b/mysql-test/suite/period/r/create.result @@ -45,6 +45,10 @@ ERROR 42000: Incorrect column specifier for column 's' create or replace table t (id int primary key, s date, e date, period for mytime(s,x)); ERROR 42S22: Unknown column 'x' in 'mytime' +# MDEV-18842: Unfortunate error message when the same column is used +# for application period start and end +create or replace table t (s date, t date, period for apt(s,s)); +ERROR 42000: Column 's' specified twice create or replace table t (id int primary key, s date, e date, period for mytime(s,e), period for mytime2(s,e)); diff --git a/mysql-test/suite/period/t/create.test b/mysql-test/suite/period/t/create.test index 2e3de795698..49dcc6ad3c7 100644 --- a/mysql-test/suite/period/t/create.test +++ b/mysql-test/suite/period/t/create.test @@ -31,6 +31,12 @@ create or replace table t (id int primary key, s time, e time, --error ER_BAD_FIELD_ERROR create or replace table t (id int primary key, s date, e date, period for mytime(s,x)); + +--echo # MDEV-18842: Unfortunate error message when the same column is used +--echo # for application period start and end +--error ER_FIELD_SPECIFIED_TWICE +create or replace table t (s date, t date, period for apt(s,s)); + --error ER_MORE_THAN_ONE_PERIOD create or replace table t (id int primary key, s date, e date, period for mytime(s,e), diff --git a/mysql-test/suite/rpl/r/rpl_gtid_delete_domain.result b/mysql-test/suite/rpl/r/rpl_gtid_delete_domain.result index 74648501fbe..9c36973427a 100644 --- a/mysql-test/suite/rpl/r/rpl_gtid_delete_domain.result +++ b/mysql-test/suite/rpl/r/rpl_gtid_delete_domain.result @@ -22,7 +22,7 @@ START SLAVE IO_THREAD; include/wait_for_slave_io_error.inc [errno=1236] connection master; FLUSH BINARY LOGS; -PURGE BINARY LOGS TO 'master-bin.000002';; +include/wait_for_purge.inc "master-bin.000002" FLUSH BINARY LOGS DELETE_DOMAIN_ID=(11); SELECT @@global.gtid_binlog_pos, @@global.gtid_binlog_state; @@global.gtid_binlog_pos @@global.gtid_binlog_state diff --git a/mysql-test/suite/rpl/t/rpl_gtid_delete_domain.test b/mysql-test/suite/rpl/t/rpl_gtid_delete_domain.test index 0262998798a..5537b6fbf86 100644 --- a/mysql-test/suite/rpl/t/rpl_gtid_delete_domain.test +++ b/mysql-test/suite/rpl/t/rpl_gtid_delete_domain.test @@ -53,17 +53,11 @@ START SLAVE IO_THREAD; # adjust the master binlog state FLUSH BINARY LOGS; --let $purge_to_binlog= query_get_value(SHOW MASTER STATUS, File, 1) ---eval PURGE BINARY LOGS TO '$purge_to_binlog'; +--let $purge_binlogs_to=$purge_to_binlog +--source include/wait_for_purge.inc + # with final removal of the extra domain -###adding to debug info to catch the failure (1076): ---error 0,1076 --eval FLUSH BINARY LOGS DELETE_DOMAIN_ID=($extra_domain_id) - -if ($mysql_errno == 1076) { - --echo ### Failure "Could not delete gtid domain" - --source include/show_rpl_debug_info.inc - } - SELECT @@global.gtid_binlog_pos, @@global.gtid_binlog_state; --connection slave diff --git a/scripts/mysqld_safe.sh b/scripts/mysqld_safe.sh index c284e75a248..85c9363081c 100644 --- a/scripts/mysqld_safe.sh +++ b/scripts/mysqld_safe.sh @@ -449,7 +449,8 @@ mysqld_ld_preload_text() { set_malloc_lib() { malloc_lib="$1" if expr "$malloc_lib" : "\(tcmalloc\|jemalloc\)" > /dev/null ; then - if ! my_which ldconfig > /dev/null 2>&1 + export PATH=$PATH:/sbin + if ! command -v ldconfig > /dev/null 2>&1 then log_error "ldconfig command not found, required for ldconfig -p" exit 1 diff --git a/sql/log.h b/sql/log.h index 58e681985eb..eaf7cde1c07 100644 --- a/sql/log.h +++ b/sql/log.h @@ -728,7 +728,7 @@ public: { bytes_written = 0; } - void harvest_bytes_written(ulonglong* counter) + void harvest_bytes_written(Atomic_counter<uint64> *counter) { #ifndef DBUG_OFF char buf1[22],buf2[22]; diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 941616a26d5..b7127d86fcd 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -461,7 +461,7 @@ static inline int add_relay_log(Relay_log_info* rli,LOG_INFO* linfo) DBUG_RETURN(1); } rli->log_space_total += s.st_size; - DBUG_PRINT("info",("log_space_total: %llu", rli->log_space_total)); + DBUG_PRINT("info",("log_space_total: %llu", uint64(rli->log_space_total))); DBUG_RETURN(0); } @@ -1254,7 +1254,7 @@ int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset, mysql_mutex_unlock(rli->relay_log.get_log_lock()); } err: - DBUG_PRINT("info",("log_space_total: %llu",rli->log_space_total)); + DBUG_PRINT("info",("log_space_total: %llu", uint64(rli->log_space_total))); mysql_mutex_unlock(&rli->data_lock); DBUG_RETURN(error); } diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index 4223e015ecd..e3b59aa615c 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -240,7 +240,8 @@ public: threads, the SQL thread sets it to unblock the I/O thread and make it temporarily forget about the constraint. */ - ulonglong log_space_limit,log_space_total; + ulonglong log_space_limit; + Atomic_counter<uint64> log_space_total; bool ignore_log_space_limit; /* diff --git a/sql/slave.cc b/sql/slave.cc index cc50638ae5b..da21df7d3c9 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2818,7 +2818,7 @@ static bool wait_for_relay_log_space(Relay_log_info* rli) DBUG_PRINT("info", ("log_space_limit=%llu log_space_total=%llu " "ignore_log_space_limit=%d " "sql_force_rotate_relay=%d", - rli->log_space_limit, rli->log_space_total, + rli->log_space_limit, uint64(rli->log_space_total), (int) rli->ignore_log_space_limit, (int) rli->sql_force_rotate_relay)); } @@ -5084,7 +5084,7 @@ Stopping slave I/O thread due to out-of-memory error from master"); { DBUG_PRINT("info", ("log_space_limit=%llu log_space_total=%llu " "ignore_log_space_limit=%d", - rli->log_space_limit, rli->log_space_total, + rli->log_space_limit, uint64(rli->log_space_total), (int) rli->ignore_log_space_limit)); } #endif diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index ef96e8f2484..483be6e39d6 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -9087,6 +9087,9 @@ bool mysql_show_create_user(THD *thd, LEX_USER *lex_user) append_identifier(thd, &result, username, strlen(username)); add_user_parameters(thd, &result, acl_user, false); + if (acl_user->account_locked) + result.append(STRING_WITH_LEN(" ACCOUNT LOCK")); + if (acl_user->password_expired) result.append(STRING_WITH_LEN(" PASSWORD EXPIRE")); else if (!acl_user->password_lifetime) @@ -9098,9 +9101,6 @@ bool mysql_show_create_user(THD *thd, LEX_USER *lex_user) result.append(STRING_WITH_LEN(" DAY")); } - if (acl_user->account_locked) - result.append(STRING_WITH_LEN(" ACCOUNT LOCK")); - protocol->prepare_for_resend(); protocol->store(result.ptr(), result.length(), result.charset()); if (protocol->write()) diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index dc9b971f2ed..2d6f891f56f 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2010, 2015, Oracle and/or its affiliates. - Copyright (c) 2011, 2019, MariaDB + Copyright (c) 2011, 2020, MariaDB 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 @@ -31,7 +31,7 @@ #include "strfunc.h" #include "sql_admin.h" #include "sql_statistics.h" - +#include "wsrep_mysqld.h" /* Prepare, run and cleanup for mysql_recreate_table() */ static bool admin_recreate_table(THD *thd, TABLE_LIST *table_list) @@ -90,10 +90,10 @@ static int send_check_errmsg(THD *thd, TABLE_LIST* table, static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, HA_CHECK_OPT *check_opt) { - int error= 0; + int error= 0, create_error= 0; TABLE tmp_table, *table; TABLE_LIST *pos_in_locked_tables= 0; - TABLE_SHARE *share; + TABLE_SHARE *share= 0; bool has_mdl_lock= FALSE; char from[FN_REFLEN],tmp[FN_REFLEN+32]; const char **ext; @@ -206,6 +206,17 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, HA_EXTRA_NOT_USED, NULL); table_list->table= 0; } + else + { + /* + Table open failed, maybe because we run out of memory. + Close all open tables and relaese all MDL locks + */ + tdc_release_share(share); + share->tdc->flush(thd, true); + share= 0; + } + /* After this point we have an exclusive metadata lock on our table in both cases when table was successfully open in mysql_admin_table() @@ -219,11 +230,8 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, goto end; } if (dd_recreate_table(thd, table_list->db.str, table_list->table_name.str)) - { - error= send_check_errmsg(thd, table_list, "repair", - "Failed generating table from .frm file"); - goto end; - } + create_error= send_check_errmsg(thd, table_list, "repair", + "Failed generating table from .frm file"); /* 'FALSE' for 'using_transactions' means don't postpone invalidation till the end of a transaction, but do it @@ -236,6 +244,8 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, "Failed restoring .MYD file"); goto end; } + if (create_error) + goto end; if (thd->locked_tables_list.locked_tables()) { @@ -263,7 +273,8 @@ end: if (table == &tmp_table) { closefrm(table); - tdc_release_share(table->s); + if (share) + tdc_release_share(share); } /* In case of a temporary table there will be no metadata lock. */ if (unlikely(error) && has_mdl_lock) @@ -418,6 +429,50 @@ dbug_err: return open_error; } +#ifdef WITH_WSREP + /* + OPTIMIZE, REPAIR and ALTER may take MDL locks not only for the affected table, but + also for the table referenced by foreign key constraint. + This wsrep_toi_replication() function handles TOI replication for OPTIMIZE and REPAIR + so that certification keys for potential FK parent tables are also appended in the + write set. + ALTER TABLE case is handled elsewhere. + */ +static bool wsrep_toi_replication(THD *thd, TABLE_LIST *tables) +{ + if (!WSREP(thd) || !WSREP_CLIENT(thd)) return false; + + LEX *lex= thd->lex; + /* only handle OPTIMIZE and REPAIR here */ + switch (lex->sql_command) + { + case SQLCOM_OPTIMIZE: + case SQLCOM_REPAIR: + break; + default: + return false; + } + + close_thread_tables(thd); + wsrep::key_array keys; + + wsrep_append_fk_parent_table(thd, tables, &keys); + + /* now TOI replication, with no locks held */ + if (keys.empty()) + { + WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, tables); + } else { + WSREP_TO_ISOLATION_BEGIN_FK_TABLES(NULL, NULL, tables, &keys) { + return true; + } + } + return false; + + wsrep_error_label: + return true; +} +#endif /* WITH_WSREP */ /* RETURN VALUES @@ -486,6 +541,13 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, close_thread_tables(thd); for (table= tables; table; table= table->next_local) table->table= NULL; +#ifdef WITH_WSREP + if (wsrep_toi_replication(thd, tables)) + { + WSREP_INFO("wsrep TOI replication of has failed, skipping OPTIMIZE"); + goto err; + } +#endif /* WITH_WSREP */ for (table= tables; table; table= table->next_local) { @@ -592,6 +654,12 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, #endif DBUG_PRINT("admin", ("table: %p", table->table)); + if (table->schema_table) + { + result_code= HA_ADMIN_NOT_IMPLEMENTED; + goto send_result; + } + if (prepare_func) { DBUG_PRINT("admin", ("calling prepare_func")); @@ -650,12 +718,6 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, goto send_result; } - if (table->schema_table) - { - result_code= HA_ADMIN_NOT_IMPLEMENTED; - goto send_result; - } - if ((table->table->db_stat & HA_READ_ONLY) && open_for_modify) { /* purecov: begin inspected */ @@ -1338,10 +1400,10 @@ bool Sql_cmd_analyze_table::execute(THD *thd) m_lex->first_select_lex()->table_list.first= first_table; m_lex->query_tables= first_table; -error: #ifdef WITH_WSREP -wsrep_error_label: -#endif + wsrep_error_label: +#endif /* WITH_WSREP */ +error: DBUG_RETURN(res); } @@ -1380,7 +1442,6 @@ bool Sql_cmd_optimize_table::execute(THD *thd) if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table, FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ - WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); res= (specialflag & SPECIAL_NO_NEW_FUNC) ? mysql_recreate_table(thd, first_table, true) : @@ -1399,9 +1460,6 @@ bool Sql_cmd_optimize_table::execute(THD *thd) m_lex->query_tables= first_table; error: -#ifdef WITH_WSREP -wsrep_error_label: -#endif DBUG_RETURN(res); } @@ -1416,7 +1474,6 @@ bool Sql_cmd_repair_table::execute(THD *thd) if (check_table_access(thd, SELECT_ACL | INSERT_ACL, first_table, FALSE, UINT_MAX, FALSE)) goto error; /* purecov: inspected */ - WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "repair", TL_WRITE, 1, MY_TEST(m_lex->check_opt.sql_flags & TT_USEFRM), @@ -1435,8 +1492,5 @@ bool Sql_cmd_repair_table::execute(THD *thd) m_lex->query_tables= first_table; error: -#ifdef WITH_WSREP -wsrep_error_label: -#endif DBUG_RETURN(res); } diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index ede30263e48..0184f6beaf4 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -490,6 +490,25 @@ bool Sql_cmd_alter_table::execute(THD *thd) if (check_grant(thd, priv_needed, first_table, FALSE, UINT_MAX, FALSE)) DBUG_RETURN(TRUE); /* purecov: inspected */ +#ifdef WITH_WSREP + if (WSREP(thd) && WSREP_CLIENT(thd) && + (!thd->is_current_stmt_binlog_format_row() || + !thd->find_temporary_table(first_table))) + { + wsrep::key_array keys; + wsrep_append_fk_parent_table(thd, first_table, &keys); + + WSREP_TO_ISOLATION_BEGIN_ALTER(lex->name.str ? select_lex->db.str + : first_table->db.str, + lex->name.str ? lex->name.str + : first_table->table_name.str, + first_table, &alter_info, &keys, + used_engine ? &create_info : nullptr); + + thd->variables.auto_increment_offset = 1; + thd->variables.auto_increment_increment = 1; + } +#endif if (lex->name.str && !test_all_bits(priv, INSERT_ACL | CREATE_ACL)) { @@ -517,20 +536,6 @@ bool Sql_cmd_alter_table::execute(THD *thd) thd->work_part_info= 0; #endif -#ifdef WITH_WSREP - if (WSREP(thd) && - (!thd->is_current_stmt_binlog_format_row() || - !thd->find_temporary_table(first_table))) - { - WSREP_TO_ISOLATION_BEGIN_ALTER((lex->name.str ? select_lex->db.str : first_table->db.str), - (lex->name.str ? lex->name.str : first_table->table_name.str), - first_table, &alter_info, used_engine ? &create_info : NULL); - - thd->variables.auto_increment_offset = 1; - thd->variables.auto_increment_increment = 1; - } -#endif - result= mysql_alter_table(thd, &select_lex->db, &lex->name, &create_info, first_table, diff --git a/sql/sql_class.h b/sql/sql_class.h index f6b772ef439..73b0f36611b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3447,7 +3447,7 @@ public: void awake_no_mutex(killed_state state_to_set); void awake(killed_state state_to_set) { - bool wsrep_on_local= WSREP_NNULL(this); + bool wsrep_on_local= variables.wsrep_on; /* mutex locking order (LOCK_thd_data - LOCK_thd_kill)) requires to grab LOCK_thd_data here diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 60a7a4f6ce0..9620b0d0961 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -5430,12 +5430,14 @@ void st_select_lex::set_explain_type(bool on_the_fly) /* pos_in_table_list=NULL for e.g. post-join aggregation JOIN_TABs. */ - if (tab->table && tab->table->pos_in_table_list && - tab->table->pos_in_table_list->with && - tab->table->pos_in_table_list->with->is_recursive) + if (!tab->table); + else if (const TABLE_LIST *pos= tab->table->pos_in_table_list) { - uses_cte= true; - break; + if (pos->with && pos->with->is_recursive) + { + uses_cte= true; + break; + } } } if (uses_cte) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 3e59ad3afc9..861eb614592 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -4469,6 +4469,12 @@ public: int add_period(Lex_ident name, Lex_ident_sys_st start, Lex_ident_sys_st end) { + if (lex_string_cmp(system_charset_info, &start, &end) == 0) + { + my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), start.str); + return 1; + } + Table_period_info &info= create_info.period_info; if (check_exists && info.name.streq(name)) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index bb9d82a7039..ac637931161 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -13778,6 +13778,9 @@ void JOIN::join_free() for (tmp_unit= select_lex->first_inner_unit(); tmp_unit; tmp_unit= tmp_unit->next_unit()) + { + if (tmp_unit->with_element && tmp_unit->with_element->is_recursive) + continue; for (sl= tmp_unit->first_select(); sl; sl= sl->next_select()) { Item_subselect *subselect= sl->master_unit()->item; @@ -13795,7 +13798,7 @@ void JOIN::join_free() /* Can't unlock if at least one JOIN is still needed */ can_unlock= can_unlock && full_local; } - + } /* We are not using tables anymore Unlock all tables. We may be in an INSERT .... SELECT statement. diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index 8ed996c0a8d..5e89c4d19ee 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -427,9 +427,23 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref) bool hton_can_recreate; #ifdef WITH_WSREP - if (WSREP(thd) && - wsrep_to_isolation_begin(thd, table_ref->db.str, table_ref->table_name.str, NULL)) - DBUG_RETURN(TRUE); + if (WSREP(thd)) + { + wsrep::key_array keys; + wsrep_append_fk_parent_table(thd, table_ref, &keys); + if (keys.empty()) + { + WSREP_TO_ISOLATION_BEGIN_IF(table_ref->db.str, table_ref->table_name.str, NULL) + { + DBUG_RETURN(TRUE); + } + } else { + WSREP_TO_ISOLATION_BEGIN_FK_TABLES(NULL, NULL, table_ref, &keys) + { + DBUG_RETURN(TRUE); + } + } + } #endif /* WITH_WSREP */ if (lock_table(thd, table_ref, &hton_can_recreate)) diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 99a199886ce..021720f594e 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -2541,13 +2541,7 @@ bool st_select_lex_unit::cleanup() { DBUG_RETURN(FALSE); } - /* - When processing a PS/SP or an EXPLAIN command cleanup of a unit can - be performed immediately when the unit is reached in the cleanup - traversal initiated by the cleanup of the main unit. - */ - if (!thd->stmt_arena->is_stmt_prepare() && !thd->lex->describe && - with_element && with_element->is_recursive && union_result) + if (with_element && with_element->is_recursive && union_result) { select_union_recursive *result= with_element->rec_result; if (++result->cleanup_count == with_element->rec_outer_references) @@ -2729,27 +2723,31 @@ bool st_select_lex::cleanup() if (join) { + List_iterator<TABLE_LIST> ti(leaf_tables); + TABLE_LIST *tbl; + while ((tbl= ti++)) + { + if (tbl->is_recursive_with_table() && + !tbl->is_with_table_recursive_reference()) + { + /* + If query is killed before open_and_process_table() for tbl + is called then 'with' is already set, but 'derived' is not. + */ + st_select_lex_unit *unit= tbl->with->spec; + error|= (bool) error | (uint) unit->cleanup(); + } + } DBUG_ASSERT((st_select_lex*)join->select_lex == this); error= join->destroy(); delete join; join= 0; } - for (TABLE_LIST *tbl= get_table_list(); tbl; tbl= tbl->next_local) - { - if (tbl->is_recursive_with_table() && - !tbl->is_with_table_recursive_reference()) - { - /* - If query is killed before open_and_process_table() for tbl - is called then 'with' is already set, but 'derived' is not. - */ - st_select_lex_unit *unit= tbl->with->spec; - error|= (bool) error | (uint) unit->cleanup(); - } - } for (SELECT_LEX_UNIT *lex_unit= first_inner_unit(); lex_unit ; lex_unit= lex_unit->next_unit()) { + if (lex_unit->with_element && lex_unit->with_element->is_recursive) + continue; error= (bool) ((uint) error | (uint) lex_unit->cleanup()); } inner_refs_list.empty(); @@ -2769,8 +2767,12 @@ void st_select_lex::cleanup_all_joins(bool full) join->cleanup(full); for (unit= first_inner_unit(); unit; unit= unit->next_unit()) + { + if (unit->with_element && unit->with_element->is_recursive) + continue; for (sl= unit->first_select(); sl; sl= sl->next_select()) sl->cleanup_all_joins(full); + } DBUG_VOID_RETURN; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 1b37896f18b..607a68caf43 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2501,7 +2501,7 @@ create: Lex->pop_select(); //main select } | create_or_replace USER_SYM opt_if_not_exists clear_privileges - grant_list opt_require_clause opt_resource_options opt_account_locking opt_password_expiration + grant_list opt_require_clause opt_resource_options opt_account_locking_and_opt_password_expiration { if (unlikely(Lex->set_command_with_check(SQLCOM_CREATE_USER, $1 | $3))) @@ -7312,7 +7312,7 @@ alter: } OPTIONS_SYM '(' server_options_list ')' { } /* ALTER USER foo is allowed for MySQL compatibility. */ | ALTER USER_SYM opt_if_exists clear_privileges grant_list - opt_require_clause opt_resource_options opt_account_locking opt_password_expiration + opt_require_clause opt_resource_options opt_account_locking_and_opt_password_expiration { Lex->create_info.set($3); Lex->sql_command= SQLCOM_ALTER_USER; @@ -7348,39 +7348,46 @@ alter: } stmt_end {} ; -opt_account_locking: - /* Nothing */ {} - | ACCOUNT_SYM LOCK_SYM +account_locking_option: + LOCK_SYM { Lex->account_options.account_locked= ACCOUNTLOCK_LOCKED; } - | ACCOUNT_SYM UNLOCK_SYM + | UNLOCK_SYM { Lex->account_options.account_locked= ACCOUNTLOCK_UNLOCKED; } ; -opt_password_expiration: - /* Nothing */ {} - | PASSWORD_SYM EXPIRE_SYM + +opt_password_expire_option: + /* empty */ { Lex->account_options.password_expire= PASSWORD_EXPIRE_NOW; } - | PASSWORD_SYM EXPIRE_SYM NEVER_SYM + | NEVER_SYM { Lex->account_options.password_expire= PASSWORD_EXPIRE_NEVER; } - | PASSWORD_SYM EXPIRE_SYM DEFAULT + | DEFAULT { Lex->account_options.password_expire= PASSWORD_EXPIRE_DEFAULT; } - | PASSWORD_SYM EXPIRE_SYM INTERVAL_SYM NUM DAY_SYM + | INTERVAL_SYM NUM DAY_SYM { Lex->account_options.password_expire= PASSWORD_EXPIRE_INTERVAL; - if (!(Lex->account_options.num_expiration_days= atoi($4.str))) - my_yyabort_error((ER_WRONG_VALUE, MYF(0), "DAY", $4.str)); + if (!(Lex->account_options.num_expiration_days= atoi($2.str))) + my_yyabort_error((ER_WRONG_VALUE, MYF(0), "DAY", $2.str)); } ; +opt_account_locking_and_opt_password_expiration: + /* empty */ + | ACCOUNT_SYM account_locking_option + | PASSWORD_SYM EXPIRE_SYM opt_password_expire_option + | ACCOUNT_SYM account_locking_option PASSWORD_SYM EXPIRE_SYM opt_password_expire_option + | PASSWORD_SYM EXPIRE_SYM opt_password_expire_option ACCOUNT_SYM account_locking_option + ; + ev_alter_on_schedule_completion: /* empty */ { $$= 0;} | ON SCHEDULE_SYM ev_schedule_time { $$= 1; } diff --git a/sql/wsrep_client_service.cc b/sql/wsrep_client_service.cc index b9c59623e6c..c01c256e872 100644 --- a/sql/wsrep_client_service.cc +++ b/sql/wsrep_client_service.cc @@ -245,6 +245,16 @@ void Wsrep_client_service::will_replay() mysql_mutex_unlock(&LOCK_wsrep_replaying); } +void Wsrep_client_service::signal_replayed() +{ + DBUG_ASSERT(m_thd == current_thd); + mysql_mutex_lock(&LOCK_wsrep_replaying); + --wsrep_replaying; + DBUG_ASSERT(wsrep_replaying >= 0); + mysql_cond_broadcast(&COND_wsrep_replaying); + mysql_mutex_unlock(&LOCK_wsrep_replaying); +} + enum wsrep::provider::status Wsrep_client_service::replay() { @@ -273,14 +283,15 @@ enum wsrep::provider::status Wsrep_client_service::replay() } delete replayer_thd; - - mysql_mutex_lock(&LOCK_wsrep_replaying); - --wsrep_replaying; - mysql_cond_broadcast(&COND_wsrep_replaying); - mysql_mutex_unlock(&LOCK_wsrep_replaying); DBUG_RETURN(ret); } +enum wsrep::provider::status Wsrep_client_service::replay_unordered() +{ + DBUG_ASSERT(0); + return wsrep::provider::error_not_implemented; +} + void Wsrep_client_service::wait_for_replayers(wsrep::unique_lock<wsrep::mutex>& lock) { DBUG_ASSERT(m_thd == current_thd); @@ -300,6 +311,12 @@ void Wsrep_client_service::wait_for_replayers(wsrep::unique_lock<wsrep::mutex>& lock.lock(); } +enum wsrep::provider::status Wsrep_client_service::commit_by_xid() +{ + DBUG_ASSERT(0); + return wsrep::provider::error_not_implemented; +} + void Wsrep_client_service::debug_sync(const char* sync_point) { DBUG_ASSERT(m_thd == current_thd); diff --git a/sql/wsrep_client_service.h b/sql/wsrep_client_service.h index aa58d99c3cf..253d2f43ac3 100644 --- a/sql/wsrep_client_service.h +++ b/sql/wsrep_client_service.h @@ -48,8 +48,19 @@ public: void emergency_shutdown() { throw wsrep::not_implemented_error(); } void will_replay(); + void signal_replayed(); enum wsrep::provider::status replay(); + enum wsrep::provider::status replay_unordered(); void wait_for_replayers(wsrep::unique_lock<wsrep::mutex>&); + enum wsrep::provider::status commit_by_xid(); + bool is_explicit_xa() + { + return false; + } + bool is_xa_rollback() + { + return false; + } void debug_sync(const char*); void debug_crash(const char*); int bf_rollback(); diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index a6b0991e82b..1dfeb782bbe 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -1261,6 +1261,51 @@ void wsrep_keys_free(wsrep_key_arr_t* key_arr) key_arr->keys_len= 0; } +void +wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* tables, wsrep::key_array* keys) +{ + if (!WSREP(thd) || !WSREP_CLIENT(thd)) return; + TABLE_LIST *table; + + thd->mdl_context.release_transactional_locks(); + uint counter; + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); + + if (thd->open_temporary_tables(tables) || + open_tables(thd, &tables, &counter, MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL)) + { + WSREP_DEBUG("unable to open table for FK checks for %s", thd->query()); + } + + for (table= tables; table; table= table->next_local) + { + if (!is_temporary_table(table) && table->table) + { + FOREIGN_KEY_INFO *f_key_info; + List<FOREIGN_KEY_INFO> f_key_list; + + table->table->file->get_foreign_key_list(thd, &f_key_list); + List_iterator_fast<FOREIGN_KEY_INFO> it(f_key_list); + while ((f_key_info=it++)) + { + WSREP_DEBUG("appended fkey %s", f_key_info->referenced_table->str); + keys->push_back(wsrep_prepare_key_for_toi(f_key_info->referenced_db->str, + f_key_info->referenced_table->str, + wsrep::key::shared)); + } + } + } + + /* close the table and release MDL locks */ + close_thread_tables(thd); + thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + for (table= tables; table; table= table->next_local) + { + table->table= NULL; + table->mdl_request.ticket= NULL; + } +} + /*! * @param db Database string * @param table Table string @@ -1511,10 +1556,11 @@ wsrep_prepare_keys_for_alter_add_fk(const char* child_table_db, return ret; } -wsrep::key_array wsrep_prepare_keys_for_toi(const char* db, - const char* table, - const TABLE_LIST* table_list, - const Alter_info* alter_info) +wsrep::key_array wsrep_prepare_keys_for_toi(const char *db, + const char *table, + const TABLE_LIST *table_list, + const Alter_info *alter_info, + const wsrep::key_array *fk_tables) { wsrep::key_array ret; if (db || table) @@ -1534,8 +1580,13 @@ wsrep::key_array wsrep_prepare_keys_for_toi(const char* db, ret.insert(ret.end(), fk.begin(), fk.end()); } } + if (fk_tables && !fk_tables->empty()) + { + ret.insert(ret.end(), fk_tables->begin(), fk_tables->end()); + } return ret; } + /* * Construct Query_log_Event from thd query and serialize it * into buffer. @@ -2069,9 +2120,10 @@ fail: -1: TOI replication failed */ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, - const TABLE_LIST* table_list, - const Alter_info* alter_info, - const HA_CREATE_INFO* create_info) + const TABLE_LIST *table_list, + const Alter_info *alter_info, + const wsrep::key_array *fk_tables, + const HA_CREATE_INFO *create_info) { DBUG_ASSERT(wsrep_OSU_method_get(thd) == WSREP_OSU_TOI); @@ -2102,7 +2154,7 @@ static int wsrep_TOI_begin(THD *thd, const char *db, const char *table, struct wsrep_buf buff= { buf, buf_len }; wsrep::key_array key_array= - wsrep_prepare_keys_for_toi(db, table, table_list, alter_info); + wsrep_prepare_keys_for_toi(db, table, table_list, alter_info, fk_tables); if (thd->has_read_only_protection()) { @@ -2250,8 +2302,9 @@ static void wsrep_RSU_end(THD *thd) int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, const TABLE_LIST* table_list, - const Alter_info* alter_info, - const HA_CREATE_INFO* create_info) + const Alter_info *alter_info, + const wsrep::key_array *fk_tables, + const HA_CREATE_INFO *create_info) { /* No isolation for applier or replaying threads. @@ -2305,7 +2358,8 @@ int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, { switch (wsrep_OSU_method_get(thd)) { case WSREP_OSU_TOI: - ret= wsrep_TOI_begin(thd, db_, table_, table_list, alter_info, create_info); + ret= wsrep_TOI_begin(thd, db_, table_, table_list, alter_info, fk_tables, + create_info); break; case WSREP_OSU_RSU: ret= wsrep_RSU_begin(thd, db_, table_); diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index 4461d73a928..eb32636d67f 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -215,6 +215,7 @@ wsrep_sync_wait_upto (THD* thd, wsrep_gtid_t* upto, int timeout); extern void wsrep_last_committed_id (wsrep_gtid_t* gtid); extern int wsrep_check_opts(); extern void wsrep_prepend_PATH (const char* path); +void wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* table, wsrep::key_array* keys); /* Other global variables */ extern wsrep_seqno_t wsrep_locked_seqno; @@ -362,8 +363,9 @@ struct HA_CREATE_INFO; int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, const TABLE_LIST* table_list, - const Alter_info* alter_info= NULL, - const HA_CREATE_INFO* create_info= NULL); + const Alter_info* alter_info= nullptr, + const wsrep::key_array *fk_tables= nullptr, + const HA_CREATE_INFO* create_info= nullptr); bool wsrep_should_replicate_ddl(THD* thd, const enum legacy_db_type db_type); bool wsrep_should_replicate_ddl_iterate(THD* thd, const TABLE_LIST* table_list); @@ -607,6 +609,9 @@ void wsrep_deinit_server(); */ enum wsrep::streaming_context::fragment_unit wsrep_fragment_unit(ulong unit); +wsrep::key wsrep_prepare_key_for_toi(const char* db, const char* table, + enum wsrep::key::type type); + #else /* !WITH_WSREP */ /* These macros are needed to compile MariaDB without WSREP support diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index 9a180614afd..acd221b8b52 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -3483,7 +3483,7 @@ re_evict: if (fix_block->page.ibuf_exist) { fix_block->page.ibuf_exist = false; ibuf_merge_or_delete_for_page(fix_block, page_id, - zip_size, true); + zip_size); } if (rw_latch == RW_X_LATCH) { @@ -3550,7 +3550,7 @@ buf_page_get_gen( { rw_lock_x_lock_inline(&block->lock, 0, file, line); block->page.ibuf_exist= false; - ibuf_merge_or_delete_for_page(block, page_id, block->zip_size(), true); + ibuf_merge_or_delete_for_page(block, page_id, block->zip_size()); if (rw_latch == RW_X_LATCH) { @@ -3772,18 +3772,29 @@ loop: ut_ad(0); break; case BUF_BLOCK_FILE_PAGE: - buf_block_buf_fix_inc(block, __FILE__, __LINE__); + if (!mtr->have_x_latch(*block)) { - const auto num_fix_count= mtr->get_fix_count(block) + 1; - while (block->page.io_fix() != BUF_IO_NONE || - num_fix_count != block->page.buf_fix_count()) + buf_block_buf_fix_inc(block, __FILE__, __LINE__); { - mysql_mutex_unlock(&buf_pool.mutex); - os_thread_yield(); - mysql_mutex_lock(&buf_pool.mutex); + while (block->page.io_fix() != BUF_IO_NONE || + block->page.buf_fix_count() != 1) + { + timespec abstime; + set_timespec_nsec(abstime, 1000000); + mysql_cond_timedwait(&buf_pool.done_flush_list, &buf_pool.mutex, + &abstime); + } } + rw_lock_x_lock(&block->lock); + mtr_memo_push(mtr, block, MTR_MEMO_PAGE_X_FIX); + } + else + { + ut_ad(!block->page.ibuf_exist); +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!block->index); +#endif } - rw_lock_x_lock(&block->lock); #ifdef BTR_CUR_HASH_ADAPT drop_hash_entry= block->index; #endif @@ -3808,6 +3819,7 @@ loop: buf_page_free_descriptor(&block->page); block= free_block; buf_block_buf_fix_inc(block, __FILE__, __LINE__); + mtr_memo_push(mtr, block, MTR_MEMO_PAGE_X_FIX); break; } @@ -3818,12 +3830,10 @@ loop: btr_search_drop_page_hash_index(block); #endif /* BTR_CUR_HASH_ADAPT */ - mtr_memo_push(mtr, block, MTR_MEMO_PAGE_X_FIX); - if (block->page.ibuf_exist) { if (!recv_recovery_is_on()) - ibuf_merge_or_delete_for_page(nullptr, page_id, zip_size, true); + ibuf_merge_or_delete_for_page(nullptr, page_id, zip_size); block->page.ibuf_exist= false; } @@ -3885,7 +3895,7 @@ loop: /* Delete possible entries for the page from the insert buffer: such can exist if the page belonged to an index which was dropped */ if (!recv_recovery_is_on()) - ibuf_merge_or_delete_for_page(nullptr, page_id, zip_size, true); + ibuf_merge_or_delete_for_page(nullptr, page_id, zip_size); static_assert(FIL_PAGE_PREV + 4 == FIL_PAGE_NEXT, "adjacent"); memset_aligned<8>(block->frame + FIL_PAGE_PREV, 0xff, 8); diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 0da00cabeb1..64f73c35ce7 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -528,6 +528,7 @@ static PSI_mutex_info all_innodb_mutexes[] = { PSI_KEY(ibuf_bitmap_mutex), PSI_KEY(ibuf_mutex), PSI_KEY(ibuf_pessimistic_insert_mutex), + PSI_KEY(index_online_log), PSI_KEY(log_sys_mutex), PSI_KEY(page_zip_stat_per_index_mutex), PSI_KEY(purge_sys_pq_mutex), @@ -574,7 +575,6 @@ static PSI_rwlock_info all_innodb_rwlocks[] = { PSI_RWLOCK_KEY(trx_i_s_cache_lock), PSI_RWLOCK_KEY(trx_purge_latch), PSI_RWLOCK_KEY(index_tree_rw_lock), - PSI_RWLOCK_KEY(index_online_log), }; # endif /* UNIV_PFS_RWLOCK */ @@ -15005,24 +15005,6 @@ struct table_list_item { const char* name; }; -/** Structure to compare two st_tablename objects using their -db and tablename. It is used in the ordering of cascade_fk_set. -It returns true if the first argument precedes the second argument -and false otherwise. */ -struct tablename_compare { - - bool operator()(const st_handler_tablename lhs, - const st_handler_tablename rhs) const - { - int cmp = strcmp(lhs.db, rhs.db); - if (cmp == 0) { - cmp = strcmp(lhs.tablename, rhs.tablename); - } - - return(cmp < 0); - } -}; - /*****************************************************************//** Checks if ALTER TABLE may change the storage engine of the table. Changing storage engines is not allowed for tables for which there diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h index c7ca3c26a13..5a6eea5bc9b 100644 --- a/storage/innobase/handler/ha_innodb.h +++ b/storage/innobase/handler/ha_innodb.h @@ -51,12 +51,7 @@ struct ha_table_option_struct uint encryption; /*!< DEFAULT, ON, OFF */ ulonglong encryption_key_id; /*!< encryption key id */ }; -/* JAN: TODO: MySQL 5.7 handler.h */ -struct st_handler_tablename -{ - const char *db; - const char *tablename; -}; + /** The class defining a handle to an Innodb table */ class ha_innobase final : public handler { diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc index 91a450b0711..0d81f81135c 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.cc +++ b/storage/innobase/ibuf/ibuf0ibuf.cc @@ -4170,19 +4170,11 @@ insert buffer. If the page is not read, but created in the buffer pool, this function deletes its buffered entries from the insert buffer; there can exist entries for such a page if the page belonged to an index which subsequently was dropped. -@param[in,out] block if page has been read from disk, -pointer to the page x-latched, else NULL -@param[in] page_id page id of the index page -@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0 -@param[in] update_ibuf_bitmap normally this is set, but -if we have deleted or are deleting the tablespace, then we naturally do not -want to update a non-existent bitmap page */ -void -ibuf_merge_or_delete_for_page( - buf_block_t* block, - const page_id_t page_id, - ulint zip_size, - bool update_ibuf_bitmap) +@param block X-latched page to try to apply changes to, or NULL to discard +@param page_id page identifier +@param zip_size ROW_FORMAT=COMPRESSED page size, or 0 */ +void ibuf_merge_or_delete_for_page(buf_block_t *block, const page_id_t page_id, + ulint zip_size) { btr_pcur_t pcur; #ifdef UNIV_IBUF_DEBUG @@ -4211,47 +4203,33 @@ ibuf_merge_or_delete_for_page( return; } - fil_space_t* space; - - if (update_ibuf_bitmap) { - space = fil_space_t::get(page_id.space()); - - if (UNIV_UNLIKELY(!space)) { - /* Do not try to read the bitmap page from the - non-existent tablespace, delete the ibuf records */ - block = NULL; - update_ibuf_bitmap = false; - } else { - ulint bitmap_bits = 0; + fil_space_t* space = fil_space_t::get(page_id.space()); - ibuf_mtr_start(&mtr); + if (UNIV_UNLIKELY(!space)) { + block = NULL; + } else { + ulint bitmap_bits = 0; - buf_block_t* bitmap_page = ibuf_bitmap_get_map_page( - page_id, zip_size, &mtr); + ibuf_mtr_start(&mtr); - if (bitmap_page - && fil_page_get_type(bitmap_page->frame) - != FIL_PAGE_TYPE_ALLOCATED) { - bitmap_bits = ibuf_bitmap_page_get_bits( - bitmap_page->frame, page_id, zip_size, - IBUF_BITMAP_BUFFERED, &mtr); - } + buf_block_t* bitmap_page = ibuf_bitmap_get_map_page( + page_id, zip_size, &mtr); + + if (bitmap_page + && fil_page_get_type(bitmap_page->frame) + != FIL_PAGE_TYPE_ALLOCATED) { + bitmap_bits = ibuf_bitmap_page_get_bits( + bitmap_page->frame, page_id, zip_size, + IBUF_BITMAP_BUFFERED, &mtr); + } - ibuf_mtr_commit(&mtr); + ibuf_mtr_commit(&mtr); - if (!bitmap_bits) { - /* No changes are buffered for this page. */ - space->release(); - return; - } + if (!bitmap_bits) { + /* No changes are buffered for this page. */ + space->release(); + return; } - } else if (block != NULL - && (ibuf_fixed_addr_page(page_id, physical_size) - || fsp_descr_page(page_id, physical_size))) { - - return; - } else { - space = NULL; } mem_heap_t* heap = mem_heap_create(512); @@ -4297,12 +4275,11 @@ loop: ibuf.index, search_tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF, &pcur, &mtr); - if (block != NULL) { + if (block) { ut_ad(rw_lock_own(&block->lock, RW_LOCK_X)); buf_block_buf_fix_inc(block, __FILE__, __LINE__); rw_lock_x_lock(&block->lock); - mtr.set_named_space(space); mtr.memo_push(block, MTR_MEMO_PAGE_X_FIX); /* This is a user page (secondary index leaf page), but we pretend that it is a change buffer page in @@ -4311,7 +4288,9 @@ loop: the block is io-fixed. Other threads must not try to latch an io-fixed block. */ buf_block_dbg_add_level(block, SYNC_IBUF_TREE_NODE); - } else if (update_ibuf_bitmap) { + } + + if (space) { mtr.set_named_space(space); } @@ -4467,7 +4446,7 @@ loop: } reset_bit: - if (!update_ibuf_bitmap) { + if (!space) { } else if (buf_block_t* bitmap = ibuf_bitmap_get_map_page( page_id, zip_size, &mtr)) { /* FIXME: update the bitmap byte only once! */ diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h index 9667ad7a731..f7c4d5dca29 100644 --- a/storage/innobase/include/dict0dict.h +++ b/storage/innobase/include/dict0dict.h @@ -910,19 +910,6 @@ inline ulint dict_tf_get_zip_size(ulint flags) : 0; } -/** Determine the extent size (in pages) for the given table -@param[in] table the table whose extent size is being - calculated. -@return extent size in pages (256, 128 or 64) */ -inline ulint dict_table_extent_size(const dict_table_t* table) -{ - if (ulint zip_size = table->space->zip_size()) { - return (1U << 20) / zip_size; - } - - return FSP_EXTENT_SIZE; -} - /********************************************************************//** Checks if a column is in the ordering columns of the clustered index of a table. Column prefixes are treated like whole columns. diff --git a/storage/innobase/include/dyn0buf.h b/storage/innobase/include/dyn0buf.h index e89733fc9ed..cb8b998f0ea 100644 --- a/storage/innobase/include/dyn0buf.h +++ b/storage/innobase/include/dyn0buf.h @@ -331,6 +331,24 @@ public: } /** + Iterate over each block and call the functor. + @return false if iteration was terminated. */ + template <typename Functor> + bool for_each_block(const Functor& functor) const + { + for (typename list_t::iterator it = m_list.begin(), + end = m_list.end(); + it != end; ++it) { + + if (!functor(&*it)) { + return false; + } + } + + return(true); + } + + /** Iterate over all the blocks in reverse and call the iterator @return false if iteration was terminated. */ template <typename Functor> diff --git a/storage/innobase/include/ibuf0ibuf.h b/storage/innobase/include/ibuf0ibuf.h index 061f1bc4e8e..cb418e57532 100644 --- a/storage/innobase/include/ibuf0ibuf.h +++ b/storage/innobase/include/ibuf0ibuf.h @@ -329,19 +329,11 @@ insert buffer. If the page is not read, but created in the buffer pool, this function deletes its buffered entries from the insert buffer; there can exist entries for such a page if the page belonged to an index which subsequently was dropped. -@param[in,out] block if page has been read from disk, -pointer to the page x-latched, else NULL -@param[in] page_id page id of the index page -@param[in] zip_size ROW_FORMAT=COMPRESSED page size, or 0 -@param[in] update_ibuf_bitmap normally this is set, but -if we have deleted or are deleting the tablespace, then we naturally do not -want to update a non-existent bitmap page */ -void -ibuf_merge_or_delete_for_page( - buf_block_t* block, - const page_id_t page_id, - ulint zip_size, - bool update_ibuf_bitmap); +@param block X-latched page to try to apply changes to, or NULL to discard +@param page_id page identifier +@param zip_size ROW_FORMAT=COMPRESSED page size, or 0 */ +void ibuf_merge_or_delete_for_page(buf_block_t *block, const page_id_t page_id, + ulint zip_size); /** Delete all change buffer entries for a tablespace, in DISCARD TABLESPACE, IMPORT TABLESPACE, or crash recovery. diff --git a/storage/innobase/include/mtr0mtr.h b/storage/innobase/include/mtr0mtr.h index cf8f37c3fa5..f8ab7cf440f 100644 --- a/storage/innobase/include/mtr0mtr.h +++ b/storage/innobase/include/mtr0mtr.h @@ -151,6 +151,10 @@ struct mtr_t { return old_mode; } + /** Check if we are holding a block latch in exclusive mode + @param block buffer pool block to search for */ + bool have_x_latch(const buf_block_t &block) const; + /** Copy the tablespaces associated with the mini-transaction (needed for generating FILE_MODIFY records) @param[in] mtr mini-transaction that may modify diff --git a/storage/innobase/include/sync0rw.h b/storage/innobase/include/sync0rw.h index fce598f0930..084acc51d1f 100644 --- a/storage/innobase/include/sync0rw.h +++ b/storage/innobase/include/sync0rw.h @@ -506,7 +506,7 @@ the pass value == 0. */ bool rw_lock_own( /*========*/ - rw_lock_t* lock, /*!< in: rw-lock */ + const rw_lock_t*lock, /*!< in: rw-lock */ ulint lock_type) /*!< in: lock type: RW_LOCK_S, RW_LOCK_X */ MY_ATTRIBUTE((warn_unused_result)); diff --git a/storage/innobase/include/trx0trx.h b/storage/innobase/include/trx0trx.h index 2294025a1aa..2aa0ed1e0e3 100644 --- a/storage/innobase/include/trx0trx.h +++ b/storage/innobase/include/trx0trx.h @@ -985,20 +985,6 @@ public: /*------------------------------*/ char* detailed_error; /*!< detailed error message for last error, or empty. */ - /* Lock wait statistics */ - ulint n_rec_lock_waits; - /*!< Number of record lock waits, - might not be exactly correct. */ - ulint n_table_lock_waits; - /*!< Number of table lock waits, - might not be exactly correct. */ - ulint total_rec_lock_wait_time; - /*!< Total rec lock wait time up - to this moment. */ - ulint total_table_lock_wait_time; - /*!< Total table lock wait time - up to this moment. */ - rw_trx_hash_element_t *rw_trx_hash_element; LF_PINS *rw_trx_hash_pins; ulint magic_n; diff --git a/storage/innobase/mtr/mtr0mtr.cc b/storage/innobase/mtr/mtr0mtr.cc index dede92ad755..a6f74bf927f 100644 --- a/storage/innobase/mtr/mtr0mtr.cc +++ b/storage/innobase/mtr/mtr0mtr.cc @@ -893,29 +893,48 @@ inline std::pair<lsn_t,bool> mtr_t::finish_write(ulint len) return std::make_pair(start_lsn, flush); } -/** Find buffer fix count of the given block acquired by the -mini-transaction */ -struct FindBlock +/** Find out whether a block was X-latched by the mini-transaction */ +struct FindBlockX { - int32_t num_fix; - const buf_block_t *const block; + const buf_block_t █ - FindBlock(const buf_block_t *block_buf): num_fix(0), block(block_buf) {} + FindBlockX(const buf_block_t &block): block(block) {} - bool operator()(const mtr_memo_slot_t* slot) + /** @return whether the block was not found x-latched */ + bool operator()(const mtr_memo_slot_t *slot) const { - if (slot->object == block) - num_fix++; - return true; + return slot->object != &block || slot->type == MTR_MEMO_PAGE_X_FIX; + } +}; + +#ifdef UNIV_DEBUG +/** Assert that the block is not present in the mini-transaction */ +struct FindNoBlock +{ + const buf_block_t █ + + FindNoBlock(const buf_block_t &block): block(block) {} + + /** @return whether the block was not found */ + bool operator()(const mtr_memo_slot_t *slot) const + { + return slot->object != █ } }; +#endif /* UNIV_DEBUG */ -uint32_t mtr_t::get_fix_count(const buf_block_t *block) const +bool mtr_t::have_x_latch(const buf_block_t &block) const { - Iterate<FindBlock> iteration((FindBlock(block))); - if (m_memo.for_each_block(iteration)) - return iteration.functor.num_fix; - return 0; + if (m_memo.for_each_block(CIterate<FindBlockX>(FindBlockX(block)))) + { + ut_ad(m_memo.for_each_block(CIterate<FindNoBlock>(FindNoBlock(block)))); + ut_ad(!memo_contains_flagged(&block, + MTR_MEMO_PAGE_S_FIX | MTR_MEMO_PAGE_SX_FIX | + MTR_MEMO_BUF_FIX | MTR_MEMO_MODIFY)); + return false; + } + ut_ad(rw_lock_own(&block.lock, RW_LOCK_X)); + return true; } #ifdef UNIV_DEBUG @@ -931,13 +950,13 @@ bool mtr_t::memo_contains(const rw_lock_t &lock, mtr_memo_type_t type) switch (type) { case MTR_MEMO_X_LOCK: - ut_ad(rw_lock_own(const_cast<rw_lock_t*>(&lock), RW_LOCK_X)); + ut_ad(rw_lock_own(&lock, RW_LOCK_X)); break; case MTR_MEMO_SX_LOCK: - ut_ad(rw_lock_own(const_cast<rw_lock_t*>(&lock), RW_LOCK_SX)); + ut_ad(rw_lock_own(&lock, RW_LOCK_SX)); break; case MTR_MEMO_S_LOCK: - ut_ad(rw_lock_own(const_cast<rw_lock_t*>(&lock), RW_LOCK_S)); + ut_ad(rw_lock_own(&lock, RW_LOCK_S)); break; default: break; diff --git a/storage/innobase/os/os0file.cc b/storage/innobase/os/os0file.cc index 01eed0c75f6..ec1b08b2582 100644 --- a/storage/innobase/os/os0file.cc +++ b/storage/innobase/os/os0file.cc @@ -4262,72 +4262,152 @@ os_file_set_umask(ulint umask) } #ifdef _WIN32 -static int win32_get_block_size(HANDLE volume_handle, const char *volume_name) + +/* Checks whether physical drive is on SSD.*/ +static bool is_drive_on_ssd(DWORD nr) { - STORAGE_ACCESS_ALIGNMENT_DESCRIPTOR disk_alignment; - STORAGE_PROPERTY_QUERY storage_query; - DWORD tmp; - - memset(&storage_query, 0, sizeof(storage_query)); - storage_query.PropertyId = StorageAccessAlignmentProperty; - storage_query.QueryType = PropertyStandardQuery; - - if (os_win32_device_io_control(volume_handle, - IOCTL_STORAGE_QUERY_PROPERTY, - &storage_query, - sizeof storage_query, - &disk_alignment, - sizeof disk_alignment, - &tmp) && tmp == sizeof disk_alignment) { - return disk_alignment.BytesPerPhysicalSector; - } + char physical_drive_path[32]; + snprintf(physical_drive_path, sizeof(physical_drive_path), + "\\\\.\\PhysicalDrive%lu", nr); + + HANDLE h= CreateFile(physical_drive_path, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + if (h == INVALID_HANDLE_VALUE) + return false; - switch (GetLastError()) { - case ERROR_INVALID_FUNCTION: - case ERROR_NOT_SUPPORTED: - break; - default: - os_file_handle_error_no_exit( - volume_name, - "DeviceIoControl(IOCTL_STORAGE_QUERY_PROPERTY / StorageAccessAlignmentProperty)", - FALSE); - } - return 512; + DEVICE_SEEK_PENALTY_DESCRIPTOR seek_penalty; + STORAGE_PROPERTY_QUERY storage_query{}; + storage_query.PropertyId= StorageDeviceSeekPenaltyProperty; + storage_query.QueryType= PropertyStandardQuery; + + bool on_ssd= false; + DWORD bytes_written; + if (DeviceIoControl(h, IOCTL_STORAGE_QUERY_PROPERTY, &storage_query, + sizeof storage_query, &seek_penalty, sizeof seek_penalty, + &bytes_written, nullptr)) + { + on_ssd= seek_penalty.IncursSeekPenalty; + } + else + { + on_ssd= false; + } + CloseHandle(h); + return on_ssd; } -static bool win32_is_ssd(HANDLE volume_handle) +/* + Checks whether volume is on SSD, by checking all physical drives + in that volume. +*/ +static bool is_volume_on_ssd(const char *volume_mount_point) { - DWORD tmp; - DEVICE_SEEK_PENALTY_DESCRIPTOR seek_penalty; - STORAGE_PROPERTY_QUERY storage_query; - memset(&storage_query, 0, sizeof(storage_query)); - - storage_query.PropertyId = StorageDeviceSeekPenaltyProperty; - storage_query.QueryType = PropertyStandardQuery; - - if (os_win32_device_io_control(volume_handle, - IOCTL_STORAGE_QUERY_PROPERTY, - &storage_query, - sizeof storage_query, - &seek_penalty, - sizeof seek_penalty, - &tmp) && tmp == sizeof(seek_penalty)){ - return !seek_penalty.IncursSeekPenalty; + char volume_name[MAX_PATH]; + + if (!GetVolumeNameForVolumeMountPoint(volume_mount_point, volume_name, + array_elements(volume_name))) + { + /* This can fail, e.g if file is on network share */ + return false; } - DEVICE_TRIM_DESCRIPTOR trim; - storage_query.PropertyId = StorageDeviceTrimProperty; - if (os_win32_device_io_control(volume_handle, - IOCTL_STORAGE_QUERY_PROPERTY, - &storage_query, - sizeof storage_query, - &trim, - sizeof trim, - &tmp) && tmp == sizeof trim) { - return trim.TrimEnabled; + /* Chomp last backslash, this is needed to open volume.*/ + size_t length= strlen(volume_name); + if (length && volume_name[length - 1] == '\\') + volume_name[length - 1]= 0; + + /* Open volume handle */ + HANDLE volume_handle= CreateFile( + volume_name, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + + if (volume_handle == INVALID_HANDLE_VALUE) + return false; + + /* + Enumerate all volume extends, check whether all of them are on SSD + */ + + /* Anticipate common case where there is only one extent.*/ + VOLUME_DISK_EXTENTS single_extent; + + /* But also have a place to manage allocated data.*/ + std::unique_ptr<BYTE[]> lifetime; + + DWORD bytes_written; + VOLUME_DISK_EXTENTS *extents= nullptr; + if (DeviceIoControl(volume_handle, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, + nullptr, 0, &single_extent, sizeof(single_extent), + &bytes_written, nullptr)) + { + /* Worked on the first try. Use the preallocated buffer.*/ + extents= &single_extent; } - return false; + else + { + VOLUME_DISK_EXTENTS *last_query= &single_extent; + while (GetLastError() == ERROR_MORE_DATA) + { + DWORD extentCount= last_query->NumberOfDiskExtents; + DWORD allocatedSize= + FIELD_OFFSET(VOLUME_DISK_EXTENTS, Extents[extentCount]); + lifetime.reset(new BYTE[allocatedSize]); + last_query= (VOLUME_DISK_EXTENTS *) lifetime.get(); + if (DeviceIoControl(volume_handle, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, + nullptr, 0, last_query, allocatedSize, + &bytes_written, nullptr)) + { + extents= last_query; + break; + } + } + } + CloseHandle(volume_handle); + if (!extents) + return false; + + for (DWORD i= 0; i < extents->NumberOfDiskExtents; i++) + if (!is_drive_on_ssd(extents->Extents[i].DiskNumber)) + return false; + + return true; +} + +#include <unordered_map> +static bool is_file_on_ssd(char *file_path) +{ + /* Cache of volume_path => volume_info, protected by rwlock.*/ + static std::unordered_map<std::string, bool> cache; + static SRWLOCK lock= SRWLOCK_INIT; + + /* Preset result, in case something fails, e.g we're on network drive.*/ + char volume_path[MAX_PATH]; + if (!GetVolumePathName(file_path, volume_path, array_elements(volume_path))) + return false; + + /* Try cached volume info first.*/ + std::string volume_path_str(volume_path); + bool found; + bool result; + AcquireSRWLockShared(&lock); + auto e= cache.find(volume_path_str); + if ((found= e != cache.end())) + result= e->second; + ReleaseSRWLockShared(&lock); + + if (found) + return result; + + result= is_volume_on_ssd(volume_path); + + /* Update cache */ + AcquireSRWLockExclusive(&lock); + cache[volume_path_str]= result; + ReleaseSRWLockExclusive(&lock); + return result; } + #endif /** Determine some file metadata when creating or reading the file. @@ -4364,48 +4444,12 @@ void fil_node_t::find_metadata(os_file_t file space->atomic_write_supported = space->purpose == FIL_TYPE_TEMPORARY || space->purpose == FIL_TYPE_IMPORT; #ifdef _WIN32 - block_size = 512; - on_ssd = false; - // Open volume for this file, find out it "physical bytes per sector" - char volume[MAX_PATH + 4]; - if (!GetVolumePathName(name, volume + 4, MAX_PATH)) { - os_file_handle_error_no_exit(name, - "GetVolumePathName()", FALSE); - return; - } - // Special prefix required for volume names. - memcpy(volume, "\\\\.\\", 4); - - size_t len = strlen(volume); - if (volume[len - 1] == '\\') { - // Trim trailing backslash from volume name. - volume[len - 1] = 0; - } - - HANDLE volume_handle = CreateFile(volume, FILE_READ_ATTRIBUTES, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - 0, OPEN_EXISTING, 0, 0); - - if (volume_handle != INVALID_HANDLE_VALUE) { - block_size = win32_get_block_size(volume_handle, volume); - on_ssd = win32_is_ssd(volume_handle); - CloseHandle(volume_handle); + on_ssd = is_file_on_ssd(name); + FILE_STORAGE_INFO info; + if (GetFileInformationByHandleEx( + file, FileStorageInfo, &info, sizeof(info))) { + block_size = info.PhysicalBytesPerSectorForAtomicity; } else { - /* - Report error, unless it is expected, e.g - missing permissions, or error when trying to - open volume for UNC share. - */ - if (GetLastError() != ERROR_ACCESS_DENIED - && GetDriveType(volume) == DRIVE_FIXED) { - os_file_handle_error_no_exit(volume, "CreateFile()", FALSE); - } - } - - /* Currently we support file block size up to 4KiB */ - if (block_size > 4096) { - block_size = 4096; - } else if (block_size < 512) { block_size = 512; } #else diff --git a/storage/innobase/sync/sync0rw.cc b/storage/innobase/sync/sync0rw.cc index b86e11900b8..29a5c6cd036 100644 --- a/storage/innobase/sync/sync0rw.cc +++ b/storage/innobase/sync/sync0rw.cc @@ -990,7 +990,7 @@ the pass value == 0. bool rw_lock_own( /*========*/ - rw_lock_t* lock, /*!< in: rw-lock */ + const rw_lock_t*lock, /*!< in: rw-lock */ ulint lock_type) /*!< in: lock type: RW_LOCK_S, RW_LOCK_X */ { diff --git a/storage/innobase/trx/trx0rec.cc b/storage/innobase/trx/trx0rec.cc index 1b1cabb6963..49b0a21330d 100644 --- a/storage/innobase/trx/trx0rec.cc +++ b/storage/innobase/trx/trx0rec.cc @@ -59,12 +59,12 @@ const dtuple_t trx_undo_metadata = { static ulint trx_undo_left(const buf_block_t *undo_block, const byte *ptr) { ut_ad(ptr >= &undo_block->frame[TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE]); - ut_ad(ptr <= &undo_block->frame[srv_page_size - 10 - FIL_PAGE_DATA_END]); - /* The 10 is supposed to be an extra safety margin (and needed for compatibility with older versions) */ - return srv_page_size - ulint(ptr - undo_block->frame) - + lint left= srv_page_size - (ptr - undo_block->frame) - (10 + FIL_PAGE_DATA_END); + ut_ad(left >= 0); + return left < 0 ? 0 : static_cast<ulint>(left); } /**********************************************************************//** diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index 074667a22f9..bd54af76d9d 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -460,10 +460,6 @@ void trx_t::free() MEM_NOACCESS(&xid, sizeof xid); MEM_NOACCESS(&mod_tables, sizeof mod_tables); MEM_NOACCESS(&detailed_error, sizeof detailed_error); - MEM_NOACCESS(&n_rec_lock_waits, sizeof n_rec_lock_waits); - MEM_NOACCESS(&n_table_lock_waits, sizeof n_table_lock_waits); - MEM_NOACCESS(&total_rec_lock_wait_time, sizeof total_rec_lock_wait_time); - MEM_NOACCESS(&total_table_lock_wait_time, sizeof total_table_lock_wait_time); MEM_NOACCESS(&magic_n, sizeof magic_n); trx_pools->mem_free(this); } diff --git a/storage/maria/ma_check.c b/storage/maria/ma_check.c index 2b390c6c267..a0c755b3a7d 100644 --- a/storage/maria/ma_check.c +++ b/storage/maria/ma_check.c @@ -2360,6 +2360,14 @@ static int initialize_variables_for_repair(HA_CHECK *param, { MARIA_SHARE *share= info->s; + /* + We have to clear these variables first, as the cleanup-in-case-of-error + handling may touch these. + */ + bzero((char*) sort_info, sizeof(*sort_info)); + bzero((char*) sort_param, sizeof(*sort_param)); + bzero(&info->rec_cache, sizeof(info->rec_cache)); + if (share->data_file_type == NO_RECORD) { _ma_check_print_error(param, @@ -2374,9 +2382,6 @@ static int initialize_variables_for_repair(HA_CHECK *param, if (share->lock.update_status) (*share->lock.update_status)(info->lock.status_param); - bzero((char*) sort_info, sizeof(*sort_info)); - bzero((char*) sort_param, sizeof(*sort_param)); - param->testflag|= T_REP; /* for easy checking */ if (share->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD)) param->testflag|= T_CALC_CHECKSUM; @@ -2404,7 +2409,6 @@ static int initialize_variables_for_repair(HA_CHECK *param, set_data_file_type(sort_info, info->s); sort_info->org_data_file_type= share->data_file_type; - bzero(&info->rec_cache, sizeof(info->rec_cache)); info->rec_cache.file= info->dfile.file; info->update= (short) (HA_STATE_CHANGED | HA_STATE_ROW_CHANGED); @@ -2891,9 +2895,13 @@ err: _ma_reset_state(info); end_io_cache(¶m->read_cache); - end_io_cache(&sort_info.new_info->rec_cache); + if (sort_info.new_info) + { + end_io_cache(&sort_info.new_info->rec_cache); + sort_info.new_info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); + } info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); - sort_info.new_info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); + sort_param.sort_info->info->in_check_table= 0; /* this below could fail, shouldn't we detect error? */ if (got_error) @@ -4110,10 +4118,13 @@ err: maria_scan_end(sort_info.info); _ma_reset_state(info); - end_io_cache(&sort_info.new_info->rec_cache); + if (sort_info.new_info) + { + end_io_cache(&sort_info.new_info->rec_cache); + sort_info.new_info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); + } end_io_cache(¶m->read_cache); info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); - sort_info.new_info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); if (got_error) { if (! param->error_printed) @@ -4642,10 +4653,13 @@ err: the share by remove_io_thread() or it was not yet started (if the error happend before creating the thread). */ - end_io_cache(&sort_info.new_info->rec_cache); + if (sort_info.new_info) + { + end_io_cache(&sort_info.new_info->rec_cache); + sort_info.new_info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); + } end_io_cache(¶m->read_cache); info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); - sort_info.new_info->opt_flag&= ~(READ_CACHE_USED | WRITE_CACHE_USED); /* Destroy the new data cache in case of non-quick repair. All slave threads did either detach from the share by remove_io_thread() diff --git a/storage/maria/ma_delete_table.c b/storage/maria/ma_delete_table.c index 90e6b5250c1..fa93a7100b0 100644 --- a/storage/maria/ma_delete_table.c +++ b/storage/maria/ma_delete_table.c @@ -30,6 +30,7 @@ int maria_delete_table(const char *name) { MARIA_HA *info; myf sync_dir; + int got_error= 0, error; DBUG_ENTER("maria_delete_table"); #ifdef EXTRA_DEBUG @@ -41,9 +42,13 @@ int maria_delete_table(const char *name) Unfortunately it is necessary to open the table just to check this. We use 'open_for_repair' to be able to open even a crashed table. */ + my_errno= 0; if (!(info= maria_open(name, O_RDONLY, HA_OPEN_FOR_REPAIR, 0))) { sync_dir= 0; + /* Ignore not found errors and wrong symlink errors */ + if (my_errno != ENOENT && my_errno != HA_WRONG_CREATE_OPTION) + got_error= my_errno;; } else { @@ -78,7 +83,9 @@ int maria_delete_table(const char *name) DBUG_RETURN(1); } - DBUG_RETURN(maria_delete_table_files(name, 0, sync_dir | MY_WME)); + if (!(error= maria_delete_table_files(name, 0, sync_dir | MY_WME))) + error= got_error; + DBUG_RETURN(error); } /** diff --git a/storage/maria/ma_open.c b/storage/maria/ma_open.c index f32fd6dd19d..28c24dc6ed1 100644 --- a/storage/maria/ma_open.c +++ b/storage/maria/ma_open.c @@ -1476,7 +1476,7 @@ static void setup_key_functions(register MARIA_KEYDEF *keyinfo) /** - @brief Function to save and store the header in the index file (.MYI) + @brief Function to save and store the header in the index file (.MAI) Operates under MARIA_SHARE::intern_lock if requested. Sets MARIA_SHARE::MARIA_STATE_INFO::is_of_horizon if transactional table. diff --git a/unittest/sql/mf_iocache-t.cc b/unittest/sql/mf_iocache-t.cc index 4c0a078c52c..cc97d3b221a 100644 --- a/unittest/sql/mf_iocache-t.cc +++ b/unittest/sql/mf_iocache-t.cc @@ -96,7 +96,7 @@ void sql_print_error(const char *format, ...) /*** end of encryption tweaks and stubs ****************************/ -IO_CACHE info; +static IO_CACHE info; #define CACHE_SIZE 16384 #define INFO_TAIL ", pos_in_file = %llu, pos_in_mem = %lu", \ diff --git a/wsrep-lib b/wsrep-lib -Subproject 2da6e4894e1df5d1db51db2bbc49255e02251b9 +Subproject 41a6e9dad79c921134e44cf974b6b7ca3b84e53 |