From edbf124515b36dc2a5ba40ca469d0d70507734a3 Mon Sep 17 00:00:00 2001 From: Monty Date: Wed, 13 May 2020 23:30:34 +0300 Subject: [PATCH 01/35] Ensure that auto_increment fields are marked properly on update MDEV-19622 Assertion failures in ha_partition::set_auto_increment_if_higher upon UPDATE on Aria table --- .../suite/parts/inc/partition_auto_increment.inc | 11 +++++++++++ .../parts/r/partition_auto_increment_innodb.result | 8 ++++++++ .../parts/r/partition_auto_increment_maria.result | 8 ++++++++ .../parts/r/partition_auto_increment_memory.result | 8 ++++++++ .../parts/r/partition_auto_increment_myisam.result | 8 ++++++++ sql/table.cc | 6 ++++++ 6 files changed, 49 insertions(+) diff --git a/mysql-test/suite/parts/inc/partition_auto_increment.inc b/mysql-test/suite/parts/inc/partition_auto_increment.inc index 199c8680864..4392d04db8a 100644 --- a/mysql-test/suite/parts/inc/partition_auto_increment.inc +++ b/mysql-test/suite/parts/inc/partition_auto_increment.inc @@ -861,6 +861,17 @@ SELECT * FROM t1; DROP TABLE t1; } +if (!$skip_update) +{ +--echo # +--echo # MDEV-19622 Assertion failures in +--echo # ha_partition::set_auto_increment_if_higher upon UPDATE on Aria table +--echo # +CREATE OR REPLACE TABLE t1 (pk INT AUTO_INCREMENT, a INT, KEY(pk)) ENGINE=myisam PARTITION BY HASH(a); +INSERT INTO t1 VALUES (1,1),(2,2); +UPDATE t1 SET pk = 0; +DROP TABLE t1; +} --echo ############################################################################## } diff --git a/mysql-test/suite/parts/r/partition_auto_increment_innodb.result b/mysql-test/suite/parts/r/partition_auto_increment_innodb.result index 6250f28eb00..76f1ddfceae 100644 --- a/mysql-test/suite/parts/r/partition_auto_increment_innodb.result +++ b/mysql-test/suite/parts/r/partition_auto_increment_innodb.result @@ -1101,4 +1101,12 @@ SELECT * FROM t1; a 0 DROP TABLE t1; +# +# MDEV-19622 Assertion failures in +# ha_partition::set_auto_increment_if_higher upon UPDATE on Aria table +# +CREATE OR REPLACE TABLE t1 (pk INT AUTO_INCREMENT, a INT, KEY(pk)) ENGINE=myisam PARTITION BY HASH(a); +INSERT INTO t1 VALUES (1,1),(2,2); +UPDATE t1 SET pk = 0; +DROP TABLE t1; ############################################################################## diff --git a/mysql-test/suite/parts/r/partition_auto_increment_maria.result b/mysql-test/suite/parts/r/partition_auto_increment_maria.result index 5acce3e9492..5a3902475a9 100644 --- a/mysql-test/suite/parts/r/partition_auto_increment_maria.result +++ b/mysql-test/suite/parts/r/partition_auto_increment_maria.result @@ -1148,4 +1148,12 @@ SELECT * FROM t1; a 0 DROP TABLE t1; +# +# MDEV-19622 Assertion failures in +# ha_partition::set_auto_increment_if_higher upon UPDATE on Aria table +# +CREATE OR REPLACE TABLE t1 (pk INT AUTO_INCREMENT, a INT, KEY(pk)) ENGINE=myisam PARTITION BY HASH(a); +INSERT INTO t1 VALUES (1,1),(2,2); +UPDATE t1 SET pk = 0; +DROP TABLE t1; ############################################################################## diff --git a/mysql-test/suite/parts/r/partition_auto_increment_memory.result b/mysql-test/suite/parts/r/partition_auto_increment_memory.result index e622ddaa259..c395f8ed0c9 100644 --- a/mysql-test/suite/parts/r/partition_auto_increment_memory.result +++ b/mysql-test/suite/parts/r/partition_auto_increment_memory.result @@ -1129,4 +1129,12 @@ SELECT * FROM t1; a 0 DROP TABLE t1; +# +# MDEV-19622 Assertion failures in +# ha_partition::set_auto_increment_if_higher upon UPDATE on Aria table +# +CREATE OR REPLACE TABLE t1 (pk INT AUTO_INCREMENT, a INT, KEY(pk)) ENGINE=myisam PARTITION BY HASH(a); +INSERT INTO t1 VALUES (1,1),(2,2); +UPDATE t1 SET pk = 0; +DROP TABLE t1; ############################################################################## diff --git a/mysql-test/suite/parts/r/partition_auto_increment_myisam.result b/mysql-test/suite/parts/r/partition_auto_increment_myisam.result index 4e67094b327..792423096b5 100644 --- a/mysql-test/suite/parts/r/partition_auto_increment_myisam.result +++ b/mysql-test/suite/parts/r/partition_auto_increment_myisam.result @@ -1148,4 +1148,12 @@ SELECT * FROM t1; a 0 DROP TABLE t1; +# +# MDEV-19622 Assertion failures in +# ha_partition::set_auto_increment_if_higher upon UPDATE on Aria table +# +CREATE OR REPLACE TABLE t1 (pk INT AUTO_INCREMENT, a INT, KEY(pk)) ENGINE=myisam PARTITION BY HASH(a); +INSERT INTO t1 VALUES (1,1),(2,2); +UPDATE t1 SET pk = 0; +DROP TABLE t1; ############################################################################## diff --git a/sql/table.cc b/sql/table.cc index d6d86d96016..a639b75fe7c 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -6470,6 +6470,12 @@ void TABLE::mark_columns_needed_for_update() } need_signal= true; } + else + { + if (found_next_number_field) + mark_auto_increment_column(); + } + if (file->ha_table_flags() & HA_PRIMARY_KEY_REQUIRED_FOR_DELETE) { /* From 910c31928ee62646f06e1584e43071df34738afb Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Wed, 13 May 2020 18:46:28 +0400 Subject: [PATCH 02/35] MDEV-22503 MDB limits DECIMAL column precision to 16 doing CTAS with floor/ceil over DECIMAL(X,Y) where X > 16 The DECIMAL data type branch in Item_func_int_val::fix_length_and_dec() incorrectly used DOUBLE-style length calculation, which resulted in a smaller data type than the actual result of FLOOR()/CEIL() needs. --- mysql-test/r/func_math.result | 718 ++++++++++++++++++++++++++++++++++ mysql-test/t/func_math.test | 47 +++ sql/item_func.cc | 3 + 3 files changed, 768 insertions(+) diff --git a/mysql-test/r/func_math.result b/mysql-test/r/func_math.result index c92549ae45e..23815bcfed4 100644 --- a/mysql-test/r/func_math.result +++ b/mysql-test/r/func_math.result @@ -994,5 +994,723 @@ SELECT -9223372036854775808 MOD -9223372036854775808; -9223372036854775808 MOD -9223372036854775808 0 # +# MDEV-22503 MDB limits DECIMAL column precision to 16 doing CTAS with floor/ceil over DECIMAL(X,Y) where X > 16 +# +CREATE TABLE t44 (d1 decimal(38,0) DEFAULT NULL); +INSERT INTO t44 VALUES (12345678901234567890123456789012345678); +SELECT FLOOR(d1) FROM t44; +FLOOR(d1) +12345678901234567890123456789012345678 +CREATE TABLE t45 AS SELECT FLOOR(d1) FROM t44; +SELECT * FROM t45; +FLOOR(d1) +12345678901234567890123456789012345678 +SHOW CREATE TABLE t45; +Table Create Table +t45 CREATE TABLE `t45` ( + `FLOOR(d1)` decimal(38,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t44, t45; +CREATE PROCEDURE p1(prec INT, scale INT) +BEGIN +DECLARE maxval VARCHAR(128) DEFAULT ''; +SET @type= CONCAT('DECIMAL(', prec, ',', scale,')'); +SET @stmt= CONCAT('CREATE TABLE t1 (a ', @type, ',b ', @type, 'unsigned)'); +PREPARE stmt FROM @stmt; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; +SET maxval= CONCAT(REPEAT('9', prec-scale), '.', REPEAT('9',scale)); +INSERT INTO t1 VALUES (maxval, maxval); +CREATE TABLE t2 AS SELECT a, b, FLOOR(a) AS fa, FLOOR(b) AS fb FROM t1; +SHOW CREATE TABLE t2; +SELECT * FROM t2; +DROP TABLE t1, t2; +END; +$$ +CREATE PROCEDURE p2(prec INT) +BEGIN +DECLARE scale INT DEFAULT 0; +WHILE scale < prec AND scale <= 30 DO +CALL p1(prec, scale); +SET scale= scale + 1; +END WHILE; +END; +$$ +CALL p2(38); +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,0) DEFAULT NULL, + `b` decimal(38,0) unsigned DEFAULT NULL, + `fa` decimal(38,0) DEFAULT NULL, + `fb` decimal(38,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999999999999999999999999999999999 +b 99999999999999999999999999999999999999 +fa 99999999999999999999999999999999999999 +fb 99999999999999999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,1) DEFAULT NULL, + `b` decimal(38,1) unsigned DEFAULT NULL, + `fa` decimal(37,0) DEFAULT NULL, + `fb` decimal(37,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999999999999999999999999999999999.9 +b 9999999999999999999999999999999999999.9 +fa 9999999999999999999999999999999999999 +fb 9999999999999999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,2) DEFAULT NULL, + `b` decimal(38,2) unsigned DEFAULT NULL, + `fa` decimal(36,0) DEFAULT NULL, + `fb` decimal(36,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999999999999999999999999999999.99 +b 999999999999999999999999999999999999.99 +fa 999999999999999999999999999999999999 +fb 999999999999999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,3) DEFAULT NULL, + `b` decimal(38,3) unsigned DEFAULT NULL, + `fa` decimal(35,0) DEFAULT NULL, + `fb` decimal(35,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999999999999999999999999999999.999 +b 99999999999999999999999999999999999.999 +fa 99999999999999999999999999999999999 +fb 99999999999999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,4) DEFAULT NULL, + `b` decimal(38,4) unsigned DEFAULT NULL, + `fa` decimal(34,0) DEFAULT NULL, + `fb` decimal(34,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999999999999999999999999999999.9999 +b 9999999999999999999999999999999999.9999 +fa 9999999999999999999999999999999999 +fb 9999999999999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,5) DEFAULT NULL, + `b` decimal(38,5) unsigned DEFAULT NULL, + `fa` decimal(33,0) DEFAULT NULL, + `fb` decimal(33,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999999999999999999999999999.99999 +b 999999999999999999999999999999999.99999 +fa 999999999999999999999999999999999 +fb 999999999999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,6) DEFAULT NULL, + `b` decimal(38,6) unsigned DEFAULT NULL, + `fa` decimal(32,0) DEFAULT NULL, + `fb` decimal(32,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999999999999999999999999999.999999 +b 99999999999999999999999999999999.999999 +fa 99999999999999999999999999999999 +fb 99999999999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,7) DEFAULT NULL, + `b` decimal(38,7) unsigned DEFAULT NULL, + `fa` decimal(31,0) DEFAULT NULL, + `fb` decimal(31,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999999999999999999999999999.9999999 +b 9999999999999999999999999999999.9999999 +fa 9999999999999999999999999999999 +fb 9999999999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,8) DEFAULT NULL, + `b` decimal(38,8) unsigned DEFAULT NULL, + `fa` decimal(30,0) DEFAULT NULL, + `fb` decimal(30,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999999999999999999999999.99999999 +b 999999999999999999999999999999.99999999 +fa 999999999999999999999999999999 +fb 999999999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,9) DEFAULT NULL, + `b` decimal(38,9) unsigned DEFAULT NULL, + `fa` decimal(29,0) DEFAULT NULL, + `fb` decimal(29,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999999999999999999999999.999999999 +b 99999999999999999999999999999.999999999 +fa 99999999999999999999999999999 +fb 99999999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,10) DEFAULT NULL, + `b` decimal(38,10) unsigned DEFAULT NULL, + `fa` decimal(28,0) DEFAULT NULL, + `fb` decimal(28,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999999999999999999999999.9999999999 +b 9999999999999999999999999999.9999999999 +fa 9999999999999999999999999999 +fb 9999999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,11) DEFAULT NULL, + `b` decimal(38,11) unsigned DEFAULT NULL, + `fa` decimal(27,0) DEFAULT NULL, + `fb` decimal(27,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999999999999999999999.99999999999 +b 999999999999999999999999999.99999999999 +fa 999999999999999999999999999 +fb 999999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,12) DEFAULT NULL, + `b` decimal(38,12) unsigned DEFAULT NULL, + `fa` decimal(26,0) DEFAULT NULL, + `fb` decimal(26,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999999999999999999999.999999999999 +b 99999999999999999999999999.999999999999 +fa 99999999999999999999999999 +fb 99999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,13) DEFAULT NULL, + `b` decimal(38,13) unsigned DEFAULT NULL, + `fa` decimal(25,0) DEFAULT NULL, + `fb` decimal(25,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999999999999999999999.9999999999999 +b 9999999999999999999999999.9999999999999 +fa 9999999999999999999999999 +fb 9999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,14) DEFAULT NULL, + `b` decimal(38,14) unsigned DEFAULT NULL, + `fa` decimal(24,0) DEFAULT NULL, + `fb` decimal(24,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999999999999999999.99999999999999 +b 999999999999999999999999.99999999999999 +fa 999999999999999999999999 +fb 999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,15) DEFAULT NULL, + `b` decimal(38,15) unsigned DEFAULT NULL, + `fa` decimal(23,0) DEFAULT NULL, + `fb` decimal(23,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999999999999999999.999999999999999 +b 99999999999999999999999.999999999999999 +fa 99999999999999999999999 +fb 99999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,16) DEFAULT NULL, + `b` decimal(38,16) unsigned DEFAULT NULL, + `fa` decimal(22,0) DEFAULT NULL, + `fb` decimal(22,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999999999999999999.9999999999999999 +b 9999999999999999999999.9999999999999999 +fa 9999999999999999999999 +fb 9999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,17) DEFAULT NULL, + `b` decimal(38,17) unsigned DEFAULT NULL, + `fa` decimal(21,0) DEFAULT NULL, + `fb` decimal(21,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999999999999999.99999999999999999 +b 999999999999999999999.99999999999999999 +fa 999999999999999999999 +fb 999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,18) DEFAULT NULL, + `b` decimal(38,18) unsigned DEFAULT NULL, + `fa` decimal(20,0) DEFAULT NULL, + `fb` decimal(20,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999999999999999.999999999999999999 +b 99999999999999999999.999999999999999999 +fa 99999999999999999999 +fb 99999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,19) DEFAULT NULL, + `b` decimal(38,19) unsigned DEFAULT NULL, + `fa` decimal(19,0) DEFAULT NULL, + `fb` decimal(19,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999999999999999.9999999999999999999 +b 9999999999999999999.9999999999999999999 +fa 9999999999999999999 +fb 9999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,20) DEFAULT NULL, + `b` decimal(38,20) unsigned DEFAULT NULL, + `fa` decimal(18,0) DEFAULT NULL, + `fb` bigint(17) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999999999999.99999999999999999999 +b 999999999999999999.99999999999999999999 +fa 999999999999999999 +fb 999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,21) DEFAULT NULL, + `b` decimal(38,21) unsigned DEFAULT NULL, + `fa` bigint(17) DEFAULT NULL, + `fb` bigint(17) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999999999999.999999999999999999999 +b 99999999999999999.999999999999999999999 +fa 99999999999999999 +fb 99999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,22) DEFAULT NULL, + `b` decimal(38,22) unsigned DEFAULT NULL, + `fa` bigint(17) DEFAULT NULL, + `fb` bigint(17) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999999999999.9999999999999999999999 +b 9999999999999999.9999999999999999999999 +fa 9999999999999999 +fb 9999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,23) DEFAULT NULL, + `b` decimal(38,23) unsigned DEFAULT NULL, + `fa` bigint(17) DEFAULT NULL, + `fb` bigint(17) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999999999.99999999999999999999999 +b 999999999999999.99999999999999999999999 +fa 999999999999999 +fb 999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,24) DEFAULT NULL, + `b` decimal(38,24) unsigned DEFAULT NULL, + `fa` bigint(17) DEFAULT NULL, + `fb` bigint(16) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999999999.999999999999999999999999 +b 99999999999999.999999999999999999999999 +fa 99999999999999 +fb 99999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,25) DEFAULT NULL, + `b` decimal(38,25) unsigned DEFAULT NULL, + `fa` bigint(16) DEFAULT NULL, + `fb` bigint(15) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999999999.9999999999999999999999999 +b 9999999999999.9999999999999999999999999 +fa 9999999999999 +fb 9999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,26) DEFAULT NULL, + `b` decimal(38,26) unsigned DEFAULT NULL, + `fa` bigint(15) DEFAULT NULL, + `fb` bigint(14) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999999.99999999999999999999999999 +b 999999999999.99999999999999999999999999 +fa 999999999999 +fb 999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,27) DEFAULT NULL, + `b` decimal(38,27) unsigned DEFAULT NULL, + `fa` bigint(14) DEFAULT NULL, + `fb` bigint(13) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999999.999999999999999999999999999 +b 99999999999.999999999999999999999999999 +fa 99999999999 +fb 99999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,28) DEFAULT NULL, + `b` decimal(38,28) unsigned DEFAULT NULL, + `fa` bigint(13) DEFAULT NULL, + `fb` bigint(12) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999999.9999999999999999999999999999 +b 9999999999.9999999999999999999999999999 +fa 9999999999 +fb 9999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,29) DEFAULT NULL, + `b` decimal(38,29) unsigned DEFAULT NULL, + `fa` bigint(12) DEFAULT NULL, + `fb` int(11) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999.99999999999999999999999999999 +b 999999999.99999999999999999999999999999 +fa 999999999 +fb 999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(38,30) DEFAULT NULL, + `b` decimal(38,30) unsigned DEFAULT NULL, + `fa` int(11) DEFAULT NULL, + `fb` int(10) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999.999999999999999999999999999999 +b 99999999.999999999999999999999999999999 +fa 99999999 +fb 99999999 +CALL p2(30); +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,0) DEFAULT NULL, + `b` decimal(30,0) unsigned DEFAULT NULL, + `fa` decimal(30,0) DEFAULT NULL, + `fb` decimal(31,0) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999999999999999999999999 +b 999999999999999999999999999999 +fa 999999999999999999999999999999 +fb 999999999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,1) DEFAULT NULL, + `b` decimal(30,1) unsigned DEFAULT NULL, + `fa` decimal(29,0) DEFAULT NULL, + `fb` decimal(30,0) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999999999999999999999999.9 +b 99999999999999999999999999999.9 +fa 99999999999999999999999999999 +fb 99999999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,2) DEFAULT NULL, + `b` decimal(30,2) unsigned DEFAULT NULL, + `fa` decimal(28,0) DEFAULT NULL, + `fb` decimal(29,0) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999999999999999999999999.99 +b 9999999999999999999999999999.99 +fa 9999999999999999999999999999 +fb 9999999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,3) DEFAULT NULL, + `b` decimal(30,3) unsigned DEFAULT NULL, + `fa` decimal(27,0) DEFAULT NULL, + `fb` decimal(28,0) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999999999999999999999.999 +b 999999999999999999999999999.999 +fa 999999999999999999999999999 +fb 999999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,4) DEFAULT NULL, + `b` decimal(30,4) unsigned DEFAULT NULL, + `fa` decimal(26,0) DEFAULT NULL, + `fb` decimal(27,0) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999999999999999999999.9999 +b 99999999999999999999999999.9999 +fa 99999999999999999999999999 +fb 99999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,5) DEFAULT NULL, + `b` decimal(30,5) unsigned DEFAULT NULL, + `fa` decimal(25,0) DEFAULT NULL, + `fb` decimal(26,0) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999999999999999999999.99999 +b 9999999999999999999999999.99999 +fa 9999999999999999999999999 +fb 9999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,6) DEFAULT NULL, + `b` decimal(30,6) unsigned DEFAULT NULL, + `fa` decimal(24,0) DEFAULT NULL, + `fb` decimal(25,0) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999999999999999999.999999 +b 999999999999999999999999.999999 +fa 999999999999999999999999 +fb 999999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,7) DEFAULT NULL, + `b` decimal(30,7) unsigned DEFAULT NULL, + `fa` decimal(23,0) DEFAULT NULL, + `fb` decimal(24,0) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999999999999999999.9999999 +b 99999999999999999999999.9999999 +fa 99999999999999999999999 +fb 99999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,8) DEFAULT NULL, + `b` decimal(30,8) unsigned DEFAULT NULL, + `fa` decimal(22,0) DEFAULT NULL, + `fb` decimal(23,0) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999999999999999999.99999999 +b 9999999999999999999999.99999999 +fa 9999999999999999999999 +fb 9999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,9) DEFAULT NULL, + `b` decimal(30,9) unsigned DEFAULT NULL, + `fa` decimal(21,0) DEFAULT NULL, + `fb` decimal(22,0) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999999999999999.999999999 +b 999999999999999999999.999999999 +fa 999999999999999999999 +fb 999999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,10) DEFAULT NULL, + `b` decimal(30,10) unsigned DEFAULT NULL, + `fa` decimal(20,0) DEFAULT NULL, + `fb` decimal(21,0) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999999999999999.9999999999 +b 99999999999999999999.9999999999 +fa 99999999999999999999 +fb 99999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,11) DEFAULT NULL, + `b` decimal(30,11) unsigned DEFAULT NULL, + `fa` decimal(19,0) DEFAULT NULL, + `fb` decimal(20,0) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999999999999999.99999999999 +b 9999999999999999999.99999999999 +fa 9999999999999999999 +fb 9999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,12) DEFAULT NULL, + `b` decimal(30,12) unsigned DEFAULT NULL, + `fa` decimal(18,0) DEFAULT NULL, + `fb` bigint(17) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999999999999.999999999999 +b 999999999999999999.999999999999 +fa 999999999999999999 +fb 999999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,13) DEFAULT NULL, + `b` decimal(30,13) unsigned DEFAULT NULL, + `fa` bigint(17) DEFAULT NULL, + `fb` bigint(17) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999999999999.9999999999999 +b 99999999999999999.9999999999999 +fa 99999999999999999 +fb 99999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,14) DEFAULT NULL, + `b` decimal(30,14) unsigned DEFAULT NULL, + `fa` bigint(17) DEFAULT NULL, + `fb` bigint(17) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999999999999.99999999999999 +b 9999999999999999.99999999999999 +fa 9999999999999999 +fb 9999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,15) DEFAULT NULL, + `b` decimal(30,15) unsigned DEFAULT NULL, + `fa` bigint(17) DEFAULT NULL, + `fb` bigint(17) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999999999.999999999999999 +b 999999999999999.999999999999999 +fa 999999999999999 +fb 999999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,16) DEFAULT NULL, + `b` decimal(30,16) unsigned DEFAULT NULL, + `fa` bigint(17) DEFAULT NULL, + `fb` bigint(16) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999999999.9999999999999999 +b 99999999999999.9999999999999999 +fa 99999999999999 +fb 99999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,17) DEFAULT NULL, + `b` decimal(30,17) unsigned DEFAULT NULL, + `fa` bigint(16) DEFAULT NULL, + `fb` bigint(15) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999999999.99999999999999999 +b 9999999999999.99999999999999999 +fa 9999999999999 +fb 9999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,18) DEFAULT NULL, + `b` decimal(30,18) unsigned DEFAULT NULL, + `fa` bigint(15) DEFAULT NULL, + `fb` bigint(14) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999999.999999999999999999 +b 999999999999.999999999999999999 +fa 999999999999 +fb 999999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,19) DEFAULT NULL, + `b` decimal(30,19) unsigned DEFAULT NULL, + `fa` bigint(14) DEFAULT NULL, + `fb` bigint(13) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999999.9999999999999999999 +b 99999999999.9999999999999999999 +fa 99999999999 +fb 99999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,20) DEFAULT NULL, + `b` decimal(30,20) unsigned DEFAULT NULL, + `fa` bigint(13) DEFAULT NULL, + `fb` bigint(12) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999999.99999999999999999999 +b 9999999999.99999999999999999999 +fa 9999999999 +fb 9999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,21) DEFAULT NULL, + `b` decimal(30,21) unsigned DEFAULT NULL, + `fa` bigint(12) DEFAULT NULL, + `fb` int(11) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999999.999999999999999999999 +b 999999999.999999999999999999999 +fa 999999999 +fb 999999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,22) DEFAULT NULL, + `b` decimal(30,22) unsigned DEFAULT NULL, + `fa` int(11) DEFAULT NULL, + `fb` int(10) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999999.9999999999999999999999 +b 99999999.9999999999999999999999 +fa 99999999 +fb 99999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,23) DEFAULT NULL, + `b` decimal(30,23) unsigned DEFAULT NULL, + `fa` int(10) DEFAULT NULL, + `fb` int(9) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999999.99999999999999999999999 +b 9999999.99999999999999999999999 +fa 9999999 +fb 9999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,24) DEFAULT NULL, + `b` decimal(30,24) unsigned DEFAULT NULL, + `fa` int(9) DEFAULT NULL, + `fb` int(8) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999999.999999999999999999999999 +b 999999.999999999999999999999999 +fa 999999 +fb 999999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,25) DEFAULT NULL, + `b` decimal(30,25) unsigned DEFAULT NULL, + `fa` int(8) DEFAULT NULL, + `fb` int(7) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99999.9999999999999999999999999 +b 99999.9999999999999999999999999 +fa 99999 +fb 99999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,26) DEFAULT NULL, + `b` decimal(30,26) unsigned DEFAULT NULL, + `fa` int(7) DEFAULT NULL, + `fb` int(6) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9999.99999999999999999999999999 +b 9999.99999999999999999999999999 +fa 9999 +fb 9999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,27) DEFAULT NULL, + `b` decimal(30,27) unsigned DEFAULT NULL, + `fa` int(6) DEFAULT NULL, + `fb` int(5) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 999.999999999999999999999999999 +b 999.999999999999999999999999999 +fa 999 +fb 999 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,28) DEFAULT NULL, + `b` decimal(30,28) unsigned DEFAULT NULL, + `fa` int(5) DEFAULT NULL, + `fb` int(4) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 99.9999999999999999999999999999 +b 99.9999999999999999999999999999 +fa 99 +fb 99 +Table t2 +Create Table CREATE TABLE `t2` ( + `a` decimal(30,29) DEFAULT NULL, + `b` decimal(30,29) unsigned DEFAULT NULL, + `fa` int(4) DEFAULT NULL, + `fb` int(3) unsigned DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +a 9.99999999999999999999999999999 +b 9.99999999999999999999999999999 +fa 9 +fb 9 +DROP PROCEDURE p2; +DROP PROCEDURE p1; +# # End of 10.1 tests # diff --git a/mysql-test/t/func_math.test b/mysql-test/t/func_math.test index 89e9c4f4d49..6edc342e9df 100644 --- a/mysql-test/t/func_math.test +++ b/mysql-test/t/func_math.test @@ -720,6 +720,53 @@ SELECT -9223372036854775808 MOD 9223372036854775808; SELECT -9223372036854775808 MOD -9223372036854775808; +--echo # +--echo # MDEV-22503 MDB limits DECIMAL column precision to 16 doing CTAS with floor/ceil over DECIMAL(X,Y) where X > 16 +--echo # + +CREATE TABLE t44 (d1 decimal(38,0) DEFAULT NULL); +INSERT INTO t44 VALUES (12345678901234567890123456789012345678); +SELECT FLOOR(d1) FROM t44; +CREATE TABLE t45 AS SELECT FLOOR(d1) FROM t44; +SELECT * FROM t45; +SHOW CREATE TABLE t45; +DROP TABLE t44, t45; + + +DELIMITER $$; +CREATE PROCEDURE p1(prec INT, scale INT) +BEGIN + DECLARE maxval VARCHAR(128) DEFAULT ''; + SET @type= CONCAT('DECIMAL(', prec, ',', scale,')'); + SET @stmt= CONCAT('CREATE TABLE t1 (a ', @type, ',b ', @type, 'unsigned)'); + PREPARE stmt FROM @stmt; + EXECUTE stmt; + DEALLOCATE PREPARE stmt; + SET maxval= CONCAT(REPEAT('9', prec-scale), '.', REPEAT('9',scale)); + INSERT INTO t1 VALUES (maxval, maxval); + CREATE TABLE t2 AS SELECT a, b, FLOOR(a) AS fa, FLOOR(b) AS fb FROM t1; + SHOW CREATE TABLE t2; + SELECT * FROM t2; + DROP TABLE t1, t2; +END; +$$ +CREATE PROCEDURE p2(prec INT) +BEGIN + DECLARE scale INT DEFAULT 0; + WHILE scale < prec AND scale <= 30 DO + CALL p1(prec, scale); + SET scale= scale + 1; + END WHILE; +END; +$$ +DELIMITER ;$$ +--vertical_results +CALL p2(38); +CALL p2(30); +--horizontal_results +DROP PROCEDURE p2; +DROP PROCEDURE p1; + --echo # --echo # End of 10.1 tests diff --git a/sql/item_func.cc b/sql/item_func.cc index 8407dc881be..f76f6515365 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2337,6 +2337,9 @@ void Item_func_int_val::fix_length_and_dec() if ((args[0]->max_length - args[0]->decimals) >= (DECIMAL_LONGLONG_DIGITS - 2)) { + fix_char_length( + my_decimal_precision_to_length_no_truncation( + args[0]->decimal_int_part(), 0, false)); set_handler_by_result_type(DECIMAL_RESULT); } else From dcb0bd59cedbd2e8fa4220986863549d614fb05e Mon Sep 17 00:00:00 2001 From: Krunal Bauskar Date: Thu, 14 May 2020 15:05:36 +0800 Subject: [PATCH 03/35] MDEV-22544: Inconsistent and Incorrect rw-lock stats - There are multiple inconsistency and incorrect way in which rw-lock stats are calculated. - shared rw-lock stats: "rounds" counter is incremented only once for N rounds done in spin-cycle. - all rw-lock stats: If the spin-cycle is short-circuited then attempts are re-counted. [If spin-cycle is interrupted, before it completes srv_n_spin_wait_rounds (default 30) rounds, spin_count is incremented to consider this. If thread resumes spin-cycle (due to unavailability of the locks) and is again interrupted or completed, spin_count is again incremented with the total count, failing to adjust the previous attempt increment]. - s/x rw-lock stats: spin_loop counter is not incremented at-all instead it is projected as 0 (in show engine output) and division to calculate spin-round per spin-loop is adjusted. As per the original semantics spin_loop counter should be incremented once per spin_loop execution. - sx rw-lock stats: sx locks increments spin_loop counter but instead of incrementing it once for a spin_loop invocation it does it multiple times based on how many time spin_loop flow is repeated for same instance post os-wait. --- storage/innobase/sync/sync0rw.cc | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/storage/innobase/sync/sync0rw.cc b/storage/innobase/sync/sync0rw.cc index df710a53cf6..b46126bde58 100644 --- a/storage/innobase/sync/sync0rw.cc +++ b/storage/innobase/sync/sync0rw.cc @@ -293,10 +293,13 @@ rw_lock_s_lock_spin( ut_ad(rw_lock_validate(lock)); + rw_lock_stats.rw_s_spin_wait_count.inc(); + lock_loop: /* Spin waiting for the writer field to become free */ HMT_low(); + ulint j = i; while (i < srv_n_spin_wait_rounds && lock->lock_word <= 0) { ut_delay(srv_spin_wait_delay); i++; @@ -307,7 +310,7 @@ lock_loop: os_thread_yield(); } - ++spin_count; + spin_count += lint(i - j); /* We try once again to obtain the lock */ if (rw_lock_s_lock_low(lock, pass, file_name, line)) { @@ -428,7 +431,7 @@ rw_lock_x_lock_wait_func( HMT_medium(); /* If there is still a reader, then go to sleep.*/ - ++n_spins; + n_spins += i; sync_cell_t* cell; @@ -654,6 +657,12 @@ rw_lock_x_lock_func( ut_ad(rw_lock_validate(lock)); ut_ad(!rw_lock_own(lock, RW_LOCK_S)); + if (rw_lock_x_lock_low(lock, pass, file_name, line)) { + /* Locking succeeded */ + return; + } + rw_lock_stats.rw_x_spin_wait_count.inc(); + lock_loop: if (rw_lock_x_lock_low(lock, pass, file_name, line)) { @@ -673,6 +682,7 @@ lock_loop: /* Spin waiting for the lock_word to become free */ HMT_low(); + ulint j = i; while (i < srv_n_spin_wait_rounds && lock->lock_word <= X_LOCK_HALF_DECR) { @@ -681,7 +691,7 @@ lock_loop: } HMT_medium(); - spin_count += i; + spin_count += lint(i - j); if (i >= srv_n_spin_wait_rounds) { @@ -749,11 +759,17 @@ rw_lock_sx_lock_func( sync_array_t* sync_arr; ulint spin_count = 0; uint64_t count_os_wait = 0; - ulint spin_wait_count = 0; ut_ad(rw_lock_validate(lock)); ut_ad(!rw_lock_own(lock, RW_LOCK_S)); + if (rw_lock_sx_lock_low(lock, pass, file_name, line)) { + /* Locking succeeded */ + return; + } + + rw_lock_stats.rw_sx_spin_wait_count.inc(); + lock_loop: if (rw_lock_sx_lock_low(lock, pass, file_name, line)) { @@ -765,16 +781,14 @@ lock_loop: } rw_lock_stats.rw_sx_spin_round_count.add(spin_count); - rw_lock_stats.rw_sx_spin_wait_count.add(spin_wait_count); /* Locking succeeded */ return; } else { - ++spin_wait_count; - /* Spin waiting for the lock_word to become free */ + ulint j = i; while (i < srv_n_spin_wait_rounds && lock->lock_word <= X_LOCK_HALF_DECR) { @@ -782,7 +796,7 @@ lock_loop: i++; } - spin_count += i; + spin_count += lint(i - j); if (i >= srv_n_spin_wait_rounds) { @@ -814,7 +828,6 @@ lock_loop: } rw_lock_stats.rw_sx_spin_round_count.add(spin_count); - rw_lock_stats.rw_sx_spin_wait_count.add(spin_wait_count); /* Locking succeeded */ return; From 31f34b20f3295db7e99877dcfe61b5798a6cfe95 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Thu, 14 May 2020 11:41:27 +0400 Subject: [PATCH 04/35] MDEV-22502 MDB crashes in CREATE TABLE AS SELECT when the precision of returning type = 0. TRUNCATE(decimal_5_5) erroneously tried to create a DECIMAL(0,0) column. Creating a DECIMAL(1,0) column instead. --- mysql-test/r/func_math.result | 18 ++++++++++++++++++ mysql-test/t/func_math.test | 12 ++++++++++++ sql/item_func.cc | 2 ++ 3 files changed, 32 insertions(+) diff --git a/mysql-test/r/func_math.result b/mysql-test/r/func_math.result index 23815bcfed4..fcb2e73e623 100644 --- a/mysql-test/r/func_math.result +++ b/mysql-test/r/func_math.result @@ -994,6 +994,24 @@ SELECT -9223372036854775808 MOD -9223372036854775808; -9223372036854775808 MOD -9223372036854775808 0 # +# MDEV-22502 MDB crashes in CREATE TABLE AS SELECT when the precision of returning type = 0 +# +CREATE TABLE t1 (d decimal(5,5)); +INSERT INTO t1 VALUES (0.55555); +SELECT TRUNCATE(d,0) FROM t1; +TRUNCATE(d,0) +0 +CREATE TABLE t2 AS SELECT TRUNCATE(d,0) FROM t1; +SELECT * FROM t2; +TRUNCATE(d,0) +0 +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `TRUNCATE(d,0)` decimal(1,0) DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1, t2; +# # MDEV-22503 MDB limits DECIMAL column precision to 16 doing CTAS with floor/ceil over DECIMAL(X,Y) where X > 16 # CREATE TABLE t44 (d1 decimal(38,0) DEFAULT NULL); diff --git a/mysql-test/t/func_math.test b/mysql-test/t/func_math.test index 6edc342e9df..56eb7640f65 100644 --- a/mysql-test/t/func_math.test +++ b/mysql-test/t/func_math.test @@ -719,6 +719,18 @@ SELECT 9223372036854775808 MOD -9223372036854775808; SELECT -9223372036854775808 MOD 9223372036854775808; SELECT -9223372036854775808 MOD -9223372036854775808; +--echo # +--echo # MDEV-22502 MDB crashes in CREATE TABLE AS SELECT when the precision of returning type = 0 +--echo # + +CREATE TABLE t1 (d decimal(5,5)); +INSERT INTO t1 VALUES (0.55555); +SELECT TRUNCATE(d,0) FROM t1; +CREATE TABLE t2 AS SELECT TRUNCATE(d,0) FROM t1; +SELECT * FROM t2; +SHOW CREATE TABLE t2; +DROP TABLE t1, t2; + --echo # --echo # MDEV-22503 MDB limits DECIMAL column precision to 16 doing CTAS with floor/ceil over DECIMAL(X,Y) where X > 16 diff --git a/sql/item_func.cc b/sql/item_func.cc index f76f6515365..c41d7809bf9 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2523,6 +2523,8 @@ void Item_func_round::fix_length_and_dec() precision-= decimals_delta - length_increase; decimals= MY_MIN(decimals_to_set, DECIMAL_MAX_SCALE); + if (!precision) + precision= 1; // DECIMAL(0,0) -> DECIMAL(1,0) max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, unsigned_flag); From 3c773cd855ba72439ee06a13dcf6143347dd984b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 14 May 2020 13:35:52 +0300 Subject: [PATCH 05/35] MDEV-19622: Fix a TokuDB result --- .../tokudb_parts/r/partition_auto_increment_tokudb.result | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/storage/tokudb/mysql-test/tokudb_parts/r/partition_auto_increment_tokudb.result b/storage/tokudb/mysql-test/tokudb_parts/r/partition_auto_increment_tokudb.result index b18f970d2ce..14b6052a7d3 100644 --- a/storage/tokudb/mysql-test/tokudb_parts/r/partition_auto_increment_tokudb.result +++ b/storage/tokudb/mysql-test/tokudb_parts/r/partition_auto_increment_tokudb.result @@ -1115,5 +1115,13 @@ SELECT * FROM t1; a 0 DROP TABLE t1; +# +# MDEV-19622 Assertion failures in +# ha_partition::set_auto_increment_if_higher upon UPDATE on Aria table +# +CREATE OR REPLACE TABLE t1 (pk INT AUTO_INCREMENT, a INT, KEY(pk)) ENGINE=myisam PARTITION BY HASH(a); +INSERT INTO t1 VALUES (1,1),(2,2); +UPDATE t1 SET pk = 0; +DROP TABLE t1; ############################################################################## SET GLOBAL tokudb_prelock_empty = @tokudb_prelock_empty_saved; From 11147bea20596b6f7bd3ff907d379667ae44b149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 14 May 2020 09:05:04 +0300 Subject: [PATCH 06/35] Fix GCC -Wstringop-truncation --- libmariadb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libmariadb b/libmariadb index 2759b87d729..cdfecebc993 160000 --- a/libmariadb +++ b/libmariadb @@ -1 +1 @@ -Subproject commit 2759b87d72926b7c9b5426437a7c8dd15ff57945 +Subproject commit cdfecebc9932a0dd5516c10505bfe78d79132e7d From 7d51c35988186da89645d9873c5e9386fee0a9fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 14 May 2020 09:06:38 +0300 Subject: [PATCH 07/35] Fix GCC -Wnonnull --- client/mysqltest.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/client/mysqltest.cc b/client/mysqltest.cc index b363a3217cd..ada0da2e8ab 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -587,9 +587,10 @@ ATTRIBUTE_NORETURN static void cleanup_and_exit(int exit_code); ATTRIBUTE_NORETURN -void really_die(const char *msg); +static void really_die(const char *msg); void report_or_die(const char *fmt, ...); -void die(const char *fmt, ...); +ATTRIBUTE_NORETURN +static void die(const char *fmt, ...); static void make_error_message(char *buf, size_t len, const char *fmt, va_list args); ATTRIBUTE_NORETURN ATTRIBUTE_FORMAT(printf, 1, 2) void abort_not_supported_test(const char *fmt, ...); @@ -1554,7 +1555,7 @@ static void make_error_message(char *buf, size_t len, const char *fmt, va_list a s+= my_snprintf(s, end -s, "\n", start_lineno); } -void die(const char *fmt, ...) +static void die(const char *fmt, ...) { char buff[DIE_BUFF_SIZE]; va_list args; @@ -1563,7 +1564,7 @@ void die(const char *fmt, ...) really_die(buff); } -void really_die(const char *msg) +static void really_die(const char *msg) { static int dying= 0; fflush(stdout); From a12aed0398a33e113befc25e49967c340f96025f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 14 May 2020 10:55:32 +0300 Subject: [PATCH 08/35] Fix GCC 9.3.0 -Wunused-but-set-variable --- storage/innobase/btr/btr0cur.cc | 2 +- storage/innobase/fsp/fsp0fsp.cc | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc index a187402328f..2bf657f037f 100644 --- a/storage/innobase/btr/btr0cur.cc +++ b/storage/innobase/btr/btr0cur.cc @@ -3050,7 +3050,7 @@ btr_cur_optimistic_insert( page_t* page; rec_t* dummy; ibool leaf; - ibool reorg; + ibool reorg __attribute__((unused)); ibool inherit = TRUE; ulint rec_size; dberr_t err; diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc index 78d562be9a3..d308ff9de71 100644 --- a/storage/innobase/fsp/fsp0fsp.cc +++ b/storage/innobase/fsp/fsp0fsp.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -3185,7 +3185,6 @@ fseg_free_extent( #endif /* BTR_CUR_HASH_ADAPT */ mtr_t* mtr) { - ulint first_page_in_extent; xdes_t* descr; ulint not_full_n_used; ulint descr_n_used; @@ -3200,7 +3199,9 @@ fseg_free_extent( == FSEG_MAGIC_N_VALUE); ut_d(space->modify_check(*mtr)); - first_page_in_extent = page - (page % FSP_EXTENT_SIZE); +#if defined BTR_CUR_HASH_ADAPT || defined UNIV_DEBUG + const ulint first_page_in_extent = page - (page % FSP_EXTENT_SIZE); +#endif /* BTR_CUR_HASH_ADAPT || UNIV_DEBUG */ #ifdef BTR_CUR_HASH_ADAPT if (ahi) { From fc58c1721631fcc6c9414482b3b7e90cd8e7325d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 14 May 2020 12:06:25 +0300 Subject: [PATCH 09/35] MDEV-21336 Memory leaks related to innodb_debug_sync This essentially reverts commit b393e2cb0c079b30563dcc87a62002c9c778643c. The leak might have been fixed, but because the DEBUG_SYNC instrumentation for InnoDB purge threads was reverted in 10.5 commit 5e62b6a5e06eb02cbde1e34e95e26f42d87fce02 as part of introducing a thread pool, it is easiest to revert the entire change. --- .../gcol/r/innodb_virtual_debug_purge.result | 45 ------------- .../gcol/t/innodb_virtual_debug_purge.test | 64 ------------------- .../suite/sys_vars/r/sysvars_innodb.result | 12 ---- storage/innobase/fil/fil0pagecompress.cc | 1 - storage/innobase/handler/ha_innodb.cc | 38 ----------- storage/innobase/include/srv0srv.h | 7 -- storage/innobase/row/row0purge.cc | 21 ------ storage/innobase/row/row0vers.cc | 1 - storage/innobase/srv/srv0srv.cc | 13 ---- 9 files changed, 202 deletions(-) diff --git a/mysql-test/suite/gcol/r/innodb_virtual_debug_purge.result b/mysql-test/suite/gcol/r/innodb_virtual_debug_purge.result index 11dfdf8b52e..c9d95dae579 100644 --- a/mysql-test/suite/gcol/r/innodb_virtual_debug_purge.result +++ b/mysql-test/suite/gcol/r/innodb_virtual_debug_purge.result @@ -233,48 +233,3 @@ set global debug_dbug= @saved_dbug; drop table t1; set debug_sync=reset; SET GLOBAL innodb_purge_rseg_truncate_frequency = @saved_frequency; -# -# MDEV-18546 ASAN heap-use-after-free -# in innobase_get_computed_value / row_purge -# -CREATE TABLE t1 ( -pk INT AUTO_INCREMENT, -b BIT(15), -v BIT(15) AS (b) VIRTUAL, -PRIMARY KEY(pk), -UNIQUE(v) -) ENGINE=InnoDB; -INSERT IGNORE INTO t1 (b) VALUES -(NULL),(b'011'),(b'000110100'), -(b'01101101010'),(b'01111001001011'),(NULL); -SET GLOBAL innodb_debug_sync = "ib_clust_v_col_before_row_allocated " - "SIGNAL before_row_allocated " - "WAIT_FOR flush_unlock"; -SET GLOBAL innodb_debug_sync = "ib_open_after_dict_open " - "SIGNAL purge_open " - "WAIT_FOR select_open"; -SET @saved_dbug= @@GLOBAL.debug_dbug; -set global debug_dbug= "d,ib_purge_virtual_index_callback"; -connect purge_waiter,localhost,root; -SET debug_sync= "now WAIT_FOR before_row_allocated"; -connection default; -REPLACE INTO t1 (pk, b) SELECT pk, b FROM t1; -connection purge_waiter; -connection default; -disconnect purge_waiter; -FLUSH TABLES; -SET GLOBAL innodb_debug_sync = reset; -SET debug_sync= "now SIGNAL flush_unlock WAIT_FOR purge_open"; -SET GLOBAL innodb_debug_sync = reset; -SET debug_sync= "ib_open_after_dict_open SIGNAL select_open"; -SELECT * FROM t1; -pk b v -1 NULL NULL -2   -3 4 4 -4 j j -5 K K -6 NULL NULL -DROP TABLE t1; -SET debug_sync= reset; -set global debug_dbug= @saved_dbug; diff --git a/mysql-test/suite/gcol/t/innodb_virtual_debug_purge.test b/mysql-test/suite/gcol/t/innodb_virtual_debug_purge.test index 62017e55fbc..238a4937e9f 100644 --- a/mysql-test/suite/gcol/t/innodb_virtual_debug_purge.test +++ b/mysql-test/suite/gcol/t/innodb_virtual_debug_purge.test @@ -323,67 +323,3 @@ drop table t1; --source include/wait_until_count_sessions.inc set debug_sync=reset; SET GLOBAL innodb_purge_rseg_truncate_frequency = @saved_frequency; - ---echo # ---echo # MDEV-18546 ASAN heap-use-after-free ---echo # in innobase_get_computed_value / row_purge ---echo # - -CREATE TABLE t1 ( - pk INT AUTO_INCREMENT, - b BIT(15), - v BIT(15) AS (b) VIRTUAL, - PRIMARY KEY(pk), - UNIQUE(v) -) ENGINE=InnoDB; -INSERT IGNORE INTO t1 (b) VALUES - (NULL),(b'011'),(b'000110100'), - (b'01101101010'),(b'01111001001011'),(NULL); - -SET GLOBAL innodb_debug_sync = "ib_clust_v_col_before_row_allocated " - "SIGNAL before_row_allocated " - "WAIT_FOR flush_unlock"; -SET GLOBAL innodb_debug_sync = "ib_open_after_dict_open " - "SIGNAL purge_open " - "WAIT_FOR select_open"; - -# In 10.2 trx_undo_roll_ptr_is_insert(t_roll_ptr) condition never pass in purge, -# so this condition is forced to pass in row_vers_old_has_index_entry -SET @saved_dbug= @@GLOBAL.debug_dbug; -set global debug_dbug= "d,ib_purge_virtual_index_callback"; - -# The purge starts from REPLACE command. To avoid possible race, separate -# connection is used. ---connect(purge_waiter,localhost,root) ---send -SET debug_sync= "now WAIT_FOR before_row_allocated"; - ---connection default -REPLACE INTO t1 (pk, b) SELECT pk, b FROM t1; - ---connection purge_waiter -# Now we will definitely catch ib_clust_v_col_before_row_allocated ---reap ---connection default ---disconnect purge_waiter - -# purge hangs on the sync point. table is purged, ref_count is set to 0 -FLUSH TABLES; - -# Avoid hang on repeating purge. -# Reset Will be applied after first record is purged -SET GLOBAL innodb_debug_sync = reset; - -SET debug_sync= "now SIGNAL flush_unlock WAIT_FOR purge_open"; - -# Avoid hang on repeating purge -SET GLOBAL innodb_debug_sync = reset; - -# select unblocks purge thread -SET debug_sync= "ib_open_after_dict_open SIGNAL select_open"; -SELECT * FROM t1; - -# Cleanup -DROP TABLE t1; -SET debug_sync= reset; -set global debug_dbug= @saved_dbug; \ No newline at end of file diff --git a/mysql-test/suite/sys_vars/r/sysvars_innodb.result b/mysql-test/suite/sys_vars/r/sysvars_innodb.result index 08739f6bf28..2b83e02d45a 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_innodb.result +++ b/mysql-test/suite/sys_vars/r/sysvars_innodb.result @@ -654,18 +654,6 @@ NUMERIC_BLOCK_SIZE NULL ENUM_VALUE_LIST OFF,ON READ_ONLY NO COMMAND_LINE_ARGUMENT REQUIRED -VARIABLE_NAME INNODB_DEBUG_SYNC -SESSION_VALUE NULL -DEFAULT_VALUE -VARIABLE_SCOPE GLOBAL -VARIABLE_TYPE VARCHAR -VARIABLE_COMMENT debug_sync for innodb purge threads. Use it to set up sync points for all purge threads at once. The commands will be applied sequentially at the beginning of purging the next undo record. -NUMERIC_MIN_VALUE NULL -NUMERIC_MAX_VALUE NULL -NUMERIC_BLOCK_SIZE NULL -ENUM_VALUE_LIST NULL -READ_ONLY NO -COMMAND_LINE_ARGUMENT NONE VARIABLE_NAME INNODB_DEFAULT_ENCRYPTION_KEY_ID SESSION_VALUE 1 DEFAULT_VALUE 1 diff --git a/storage/innobase/fil/fil0pagecompress.cc b/storage/innobase/fil/fil0pagecompress.cc index 89b8efc4e9b..2c0e4222660 100644 --- a/storage/innobase/fil/fil0pagecompress.cc +++ b/storage/innobase/fil/fil0pagecompress.cc @@ -27,7 +27,6 @@ Updated 14/02/2015 #include "fil0fil.h" #include "fil0pagecompress.h" -#include #include #include "mem0mem.h" diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 68011147bc5..b917bdab19e 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -19523,33 +19523,6 @@ innodb_log_checksums_update( thd, *static_cast(save)); } -#ifdef UNIV_DEBUG -static -void -innobase_debug_sync_callback(srv_slot_t *slot, const void *value) -{ - const char *value_str = *static_cast(value); - size_t len = strlen(value_str) + 1; - - - // One allocatoin for list node object and value. - void *buf = ut_malloc_nokey(sizeof(srv_slot_t::debug_sync_t) + len); - srv_slot_t::debug_sync_t *sync = new(buf) srv_slot_t::debug_sync_t(); - strcpy(reinterpret_cast(&sync[1]), value_str); - - rw_lock_x_lock(&slot->debug_sync_lock); - UT_LIST_ADD_LAST(slot->debug_sync, sync); - rw_lock_x_unlock(&slot->debug_sync_lock); -} -static -void -innobase_debug_sync_set(THD *thd, st_mysql_sys_var*, void *, const void *value) -{ - srv_for_each_thread(SRV_WORKER, innobase_debug_sync_callback, value); - srv_for_each_thread(SRV_PURGE, innobase_debug_sync_callback, value); -} -#endif - static SHOW_VAR innodb_status_variables_export[]= { {"Innodb", (char*) &show_innodb_vars, SHOW_FUNC}, {NullS, NullS, SHOW_LONG} @@ -21162,16 +21135,6 @@ static MYSQL_SYSVAR_BOOL(debug_force_scrubbing, 0, "Perform extra scrubbing to increase test exposure", NULL, NULL, FALSE); - -char *innobase_debug_sync; -static MYSQL_SYSVAR_STR(debug_sync, innobase_debug_sync, - PLUGIN_VAR_NOCMDARG, - "debug_sync for innodb purge threads. " - "Use it to set up sync points for all purge threads " - "at once. The commands will be applied sequentially at " - "the beginning of purging the next undo record.", - NULL, - innobase_debug_sync_set, NULL); #endif /* UNIV_DEBUG */ static MYSQL_SYSVAR_BOOL(instrument_semaphores, innodb_instrument_semaphores, @@ -21395,7 +21358,6 @@ static struct st_mysql_sys_var* innobase_system_variables[]= { MYSQL_SYSVAR(background_scrub_data_check_interval), #ifdef UNIV_DEBUG MYSQL_SYSVAR(debug_force_scrubbing), - MYSQL_SYSVAR(debug_sync), #endif MYSQL_SYSVAR(instrument_semaphores), MYSQL_SYSVAR(buf_dump_status_frequency), diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index 767e24e9265..5214953f308 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -1147,13 +1147,6 @@ struct srv_slot_t{ to do */ que_thr_t* thr; /*!< suspended query thread (only used for user threads) */ -#ifdef UNIV_DEBUG - struct debug_sync_t { - UT_LIST_NODE_T(debug_sync_t) debug_sync_list; - }; - UT_LIST_BASE_NODE_T(debug_sync_t) debug_sync; - rw_lock_t debug_sync_lock; -#endif }; #ifdef UNIV_DEBUG diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc index 5f2a854ba92..f4fd617f154 100644 --- a/storage/innobase/row/row0purge.cc +++ b/storage/innobase/row/row0purge.cc @@ -46,7 +46,6 @@ Created 3/14/1997 Heikki Tuuri #include "handler.h" #include "ha_innodb.h" #include "fil0fil.h" -#include "debug_sync.h" /************************************************************************* IMPORTANT NOTE: Any operation that generates redo MUST check that there @@ -1209,26 +1208,6 @@ row_purge_step( node->start(); -#ifdef UNIV_DEBUG - srv_slot_t *slot = thr->thread_slot; - ut_ad(slot); - - rw_lock_x_lock(&slot->debug_sync_lock); - while (UT_LIST_GET_LEN(slot->debug_sync)) { - srv_slot_t::debug_sync_t *sync = - UT_LIST_GET_FIRST(slot->debug_sync); - const char* sync_str = reinterpret_cast(&sync[1]); - bool result = debug_sync_set_action(current_thd, - sync_str, - strlen(sync_str)); - ut_a(!result); - - UT_LIST_REMOVE(slot->debug_sync, sync); - ut_free(sync); - } - rw_lock_x_unlock(&slot->debug_sync_lock); -#endif - if (!(node->undo_recs == NULL || ib_vector_is_empty(node->undo_recs))) { trx_purge_rec_t*purge_rec; diff --git a/storage/innobase/row/row0vers.cc b/storage/innobase/row/row0vers.cc index b148f7f54da..3627c659231 100644 --- a/storage/innobase/row/row0vers.cc +++ b/storage/innobase/row/row0vers.cc @@ -468,7 +468,6 @@ row_vers_build_clust_v_col( vcol_info->set_used(); maria_table = vcol_info->table(); } - DEBUG_SYNC(current_thd, "ib_clust_v_col_before_row_allocated"); innobase_allocate_row_for_vcol(thd, index, &local_heap, diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index 44c07693e6a..6eb67a1b2d4 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -2600,13 +2600,6 @@ DECLARE_THREAD(srv_worker_thread)( slot = srv_reserve_slot(SRV_WORKER); -#ifdef UNIV_DEBUG - UT_LIST_INIT(slot->debug_sync, - &srv_slot_t::debug_sync_t::debug_sync_list); - rw_lock_create(PFS_NOT_INSTRUMENTED, &slot->debug_sync_lock, - SYNC_NO_ORDER_CHECK); -#endif - ut_a(srv_n_purge_threads > 1); ut_a(ulong(my_atomic_loadlint(&srv_sys.n_threads_active[SRV_WORKER])) < srv_n_purge_threads); @@ -2854,12 +2847,6 @@ DECLARE_THREAD(srv_purge_coordinator_thread)( slot = srv_reserve_slot(SRV_PURGE); -#ifdef UNIV_DEBUG - UT_LIST_INIT(slot->debug_sync, - &srv_slot_t::debug_sync_t::debug_sync_list); - rw_lock_create(PFS_NOT_INSTRUMENTED, &slot->debug_sync_lock, - SYNC_NO_ORDER_CHECK); -#endif ulint rseg_history_len = trx_sys->rseg_history_len; do { From ee5152fc4bc0f164d546ce87c1d36d1a5f78591b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 14 May 2020 17:41:37 +0300 Subject: [PATCH 10/35] MDEV-22070 MSAN use-of-uninitialized-value in encryption.innodb-redo-badkey On a checksum failure of a ROW_FORMAT=COMPRESSED page, buf_LRU_free_one_page() would invoke buf_LRU_block_remove_hashed() which will read the uncompressed page frame, although it would not be initialized. With bad enough luck, fil_page_get_type(page) could return an unrecognized value and cause the server to abort. buf_page_io_complete(): On the corruption of a ROW_FORMAT=COMPRESSED page, zerofill the uncompressed page frame. --- storage/innobase/buf/buf0buf.cc | 10 +++++++--- storage/xtradb/buf/buf0buf.cc | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index 136d46b7027..0fa569e0254 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -2,7 +2,7 @@ Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. -Copyright (c) 2013, 2019, MariaDB Corporation. +Copyright (c) 2013, 2020, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -4931,9 +4931,8 @@ buf_page_io_complete(buf_page_t* bpage, bool evict) err = buf_page_check_corrupt(bpage, space); -database_corrupted: - if (err != DB_SUCCESS) { +database_corrupted: /* Not a real corruption if it was triggered by error injection */ DBUG_EXECUTE_IF("buf_page_import_corrupt_failure", @@ -4948,6 +4947,11 @@ database_corrupted: goto page_not_corrupt; ); + if (uncompressed && bpage->zip.data) { + memset(reinterpret_cast(bpage) + ->frame, 0, srv_page_size); + } + if (err == DB_PAGE_CORRUPTED) { ib_logf(IB_LOG_LEVEL_ERROR, "Database page corruption on disk" diff --git a/storage/xtradb/buf/buf0buf.cc b/storage/xtradb/buf/buf0buf.cc index 398e1e84994..5e92c101110 100644 --- a/storage/xtradb/buf/buf0buf.cc +++ b/storage/xtradb/buf/buf0buf.cc @@ -2,7 +2,7 @@ Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. -Copyright (c) 2013, 2019, MariaDB Corporation. +Copyright (c) 2013, 2020, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -4936,9 +4936,8 @@ buf_page_io_complete(buf_page_t* bpage) err = buf_page_check_corrupt(bpage, space); } -database_corrupted: - if (err != DB_SUCCESS) { +database_corrupted: /* Not a real corruption if it was triggered by error injection */ DBUG_EXECUTE_IF("buf_page_import_corrupt_failure", @@ -4953,6 +4952,11 @@ database_corrupted: goto page_not_corrupt; ); + if (uncompressed && bpage->zip.data) { + memset(reinterpret_cast(bpage) + ->frame, 0, srv_page_size); + } + if (err == DB_PAGE_CORRUPTED) { ib_logf(IB_LOG_LEVEL_ERROR, "Database page corruption on disk" From efd68f5e3184c54e04045a1350a1ff37dc313348 Mon Sep 17 00:00:00 2001 From: Varun Gupta Date: Mon, 11 May 2020 19:56:14 +0530 Subject: [PATCH 11/35] MDEV-22498: SIGSEGV in Bitmap<64u>::merge on SELECT For the case when the optimizer does the IN-EXISTS transformation, the equality condition is injected in the WHERE OR HAVING clause of the subquery. If the select list of the subquery has a reference to the parent select make sure to use the reference and not the original item. --- mysql-test/r/subselect4.result | 32 ++++++++++++++++++++++++++++++++ mysql-test/t/subselect4.test | 24 ++++++++++++++++++++++++ sql/item_subselect.cc | 9 ++++++--- 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/subselect4.result b/mysql-test/r/subselect4.result index a7e2bd9d1b5..863105b24b6 100644 --- a/mysql-test/r/subselect4.result +++ b/mysql-test/r/subselect4.result @@ -2534,3 +2534,35 @@ x c1 1 drop table t1; +# +# MDEV-22498: SIGSEGV in Bitmap<64u>::merge on SELECT +# +set @save_sql_select_limit= @@sql_select_limit; +SET sql_select_limit=0; +CREATE TABLE t1(b INT, c INT); +CREATE TABLE t2(a INT, b INT); +INSERT INTO t1 VALUES (1,1),(2,2),(3,3); +INSERT INTO t2 VALUES (1,1),(2,2),(3,3); +EXPLAIN EXTENDED SELECT sum(a), t2.a, t2.b FROM t2 HAVING t2.a IN (SELECT t2.b FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL Zero limit +2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 3 100.00 +Warnings: +Note 1276 Field or reference 'test.t2.b' of SELECT #2 was resolved in SELECT #1 +Note 1276 Field or reference 'test.t2.b' of SELECT #2 was resolved in SELECT #1 +Note 1003 select sum(`test`.`t2`.`a`) AS `sum(a)`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` having (`test`.`t2`.`a`,(select `test`.`t2`.`b` from `test`.`t1` where ((`test`.`t2`.`a`) = `test`.`t2`.`b`))) +SELECT sum(a), t2.a, t2.b FROM t2 HAVING t2.a IN (SELECT t2.b FROM t1); +sum(a) a b +SET @@sql_select_limit= @save_sql_select_limit; +EXPLAIN EXTENDED SELECT sum(a), t2.a, t2.b FROM t2 HAVING t2.a IN (SELECT t2.b FROM t1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 PRIMARY t2 ALL NULL NULL NULL NULL 3 100.00 +2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 3 100.00 +Warnings: +Note 1276 Field or reference 'test.t2.b' of SELECT #2 was resolved in SELECT #1 +Note 1276 Field or reference 'test.t2.b' of SELECT #2 was resolved in SELECT #1 +Note 1003 select sum(`test`.`t2`.`a`) AS `sum(a)`,`test`.`t2`.`a` AS `a`,`test`.`t2`.`b` AS `b` from `test`.`t2` having <`test`.`t2`.`a`,`test`.`t2`.`b`>((`test`.`t2`.`a`,(select `test`.`t2`.`b` from `test`.`t1` where ((`test`.`t2`.`a`) = `test`.`t2`.`b`)))) +SELECT sum(a), t2.a, t2.b FROM t2 HAVING t2.a IN (SELECT t2.b FROM t1); +sum(a) a b +6 1 1 +DROP TABLE t1,t2; diff --git a/mysql-test/t/subselect4.test b/mysql-test/t/subselect4.test index d5a40419185..f0b1d16be7b 100644 --- a/mysql-test/t/subselect4.test +++ b/mysql-test/t/subselect4.test @@ -2075,3 +2075,27 @@ insert into t1 values(2,1),(1,2); select (select c1 from t1 group by c1,c2 order by c1 limit 1) as x; (select c1 from t1 group by c1,c2 order by c1 limit 1); drop table t1; + +--echo # +--echo # MDEV-22498: SIGSEGV in Bitmap<64u>::merge on SELECT +--echo # + +set @save_sql_select_limit= @@sql_select_limit; +SET sql_select_limit=0; + +CREATE TABLE t1(b INT, c INT); +CREATE TABLE t2(a INT, b INT); +INSERT INTO t1 VALUES (1,1),(2,2),(3,3); +INSERT INTO t2 VALUES (1,1),(2,2),(3,3); + +let $query= +SELECT sum(a), t2.a, t2.b FROM t2 HAVING t2.a IN (SELECT t2.b FROM t1); + +eval EXPLAIN EXTENDED $query; +eval $query; + +SET @@sql_select_limit= @save_sql_select_limit; + +eval EXPLAIN EXTENDED $query; +eval $query; +DROP TABLE t1,t2; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index e3cb82df170..16ef8a192c5 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -2135,10 +2135,13 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, } else { + /* + No need to use real_item for the item, as the ref items that are possible + in the subquery either belong to views or to the parent select. + For such case we need to refer to the reference and not to the original + item. + */ Item *item= (Item*) select_lex->item_list.head(); - if (item->type() != REF_ITEM || - ((Item_ref*)item)->ref_type() != Item_ref::VIEW_REF) - item= item->real_item(); if (select_lex->table_list.elements) { From 1408e26d0b15ea95d3d017bb059cd65b53b00a86 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 15 May 2020 06:15:10 +0400 Subject: [PATCH 12/35] MDEV-22560 Crash on a table value constructor with an SP variable fix_fields_for_tvc() could call fix_fields() for Items that have already been fixed before. Changing fix_fields() to fix_fields_if_needed(). --- mysql-test/main/table_value_constr.result | 11 +++++++++++ mysql-test/main/table_value_constr.test | 13 +++++++++++++ sql/sql_tvc.cc | 9 ++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/table_value_constr.result b/mysql-test/main/table_value_constr.result index 03e378e68f2..b00cfd28dcc 100644 --- a/mysql-test/main/table_value_constr.result +++ b/mysql-test/main/table_value_constr.result @@ -2599,3 +2599,14 @@ a 2 1 drop view v1; +# +# MDEV-22560 Crash on a table value constructor with an SP variable +# +BEGIN NOT ATOMIC +DECLARE a INT DEFAULT 0; +VALUES (a) UNION SELECT 1; +END; +$$ +a +0 +1 diff --git a/mysql-test/main/table_value_constr.test b/mysql-test/main/table_value_constr.test index 4464eb7b77b..e7843c604dd 100644 --- a/mysql-test/main/table_value_constr.test +++ b/mysql-test/main/table_value_constr.test @@ -1326,3 +1326,16 @@ create view v1 as with t(a) as (values (2), (1)) select a from t; show create view v1; select * from v1; drop view v1; + + +--echo # +--echo # MDEV-22560 Crash on a table value constructor with an SP variable +--echo # + +DELIMITER $$; +BEGIN NOT ATOMIC + DECLARE a INT DEFAULT 0; + VALUES (a) UNION SELECT 1; +END; +$$ +DELIMITER ;$$ diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc index b4538248e07..92cd229eebb 100644 --- a/sql/sql_tvc.cc +++ b/sql/sql_tvc.cc @@ -52,7 +52,14 @@ bool fix_fields_for_tvc(THD *thd, List_iterator_fast &li) while ((item= it++)) { - if (item->fix_fields(thd, 0)) + /* + Some items have already been fixed. + For example Item_splocal items get fixed in + Item_splocal::append_for_log(), which is called from subst_spvars() + while replacing their values to NAME_CONST()s. + So fix only those that have not been. + */ + if (item->fix_fields_if_needed(thd, 0)) DBUG_RETURN(true); } } From 277aa85c9b42e2a7b778d196b307e45711ccc895 Mon Sep 17 00:00:00 2001 From: Monty Date: Fri, 15 May 2020 10:44:05 +0300 Subject: [PATCH 13/35] Fixed bugs found by valgrind Other things: - Removed innodb_encryption_tables.test from valgrind as it takes a REALLY long time --- mysql-test/suite/encryption/t/innodb_encryption_tables.test | 2 ++ sql/field.cc | 2 +- sql/log_event.cc | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mysql-test/suite/encryption/t/innodb_encryption_tables.test b/mysql-test/suite/encryption/t/innodb_encryption_tables.test index bc762faf12e..d03bc890ba4 100644 --- a/mysql-test/suite/encryption/t/innodb_encryption_tables.test +++ b/mysql-test/suite/encryption/t/innodb_encryption_tables.test @@ -1,6 +1,8 @@ -- source include/have_innodb.inc -- source include/have_example_key_management_plugin.inc -- source include/not_embedded.inc +# We can't run this test under valgrind as it 'takes forever' +-- source include/not_valgrind.inc create table innodb_normal(c1 bigint not null, b char(200)) engine=innodb; create table innodb_compact(c1 bigint not null, b char(200)) engine=innodb row_format=compact; diff --git a/sql/field.cc b/sql/field.cc index f96755f5f51..0e8dd26445f 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1438,7 +1438,7 @@ void Field::error_generated_column_function_is_not_allowed(THD *thd, QT_ITEM_IDENT_SKIP_TABLE_NAMES)); my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(error ? 0 : ME_JUST_WARNING), - tmp.c_ptr(), vcol_info->get_vcol_type_name(), + tmp.c_ptr_safe(), vcol_info->get_vcol_type_name(), const_cast(field_name)); } diff --git a/sql/log_event.cc b/sql/log_event.cc index 7341add598f..f9e4365ff94 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -10228,6 +10228,8 @@ const char *sql_ex_info::init(const char *buf, const char *buf_end, } else { + if (buf_end - buf < 7) + return 0; // Wrong data field_term_len= enclosed_len= line_term_len= line_start_len= escaped_len=1; field_term = buf++; // Use first byte in string enclosed= buf++; From d49233caf696ba5896ff9119ca1a07039368ab23 Mon Sep 17 00:00:00 2001 From: Varun Gupta Date: Thu, 14 May 2020 18:38:49 +0530 Subject: [PATCH 14/35] MDEV-18100: User defined aggregate functions not working correctly when the schema is changed The issue here was that when the schema was changed the value for the THD::server_status is ored with SERVER_SESSION_STATE_CHANGED. For custom aggregate functions, currently we check if the server_status is equal to SERVER_STATUS_LAST_ROW_SENT then we should terminate the execution of the custom aggregate function as there are no more rows to fetch. So the check should be that if the server status has the bit set for SERVER_STATUS_LAST_ROW_SENT then we should terminate the execution of the custom aggregate function. --- .../main/custom_aggregate_functions.result | 33 +++++++++++++++++++ .../main/custom_aggregate_functions.test | 33 +++++++++++++++++++ sql/item_sum.cc | 8 +++-- sql/sp_head.cc | 2 +- 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/mysql-test/main/custom_aggregate_functions.result b/mysql-test/main/custom_aggregate_functions.result index 0a27334f58e..8ad7b4b4dc0 100644 --- a/mysql-test/main/custom_aggregate_functions.result +++ b/mysql-test/main/custom_aggregate_functions.result @@ -1153,3 +1153,36 @@ i sum(i) NULL 8 drop function agg_sum; drop table t1; +# +# User defined aggregate functions not working correctly when the schema is changed +# +CREATE SCHEMA IF NOT EXISTS common_schema; +CREATE SCHEMA IF NOT EXISTS another_schema; +DROP FUNCTION IF EXISTS common_schema.add_ints | +Warnings: +Note 1305 FUNCTION common_schema.add_ints does not exist +CREATE FUNCTION common_schema.add_ints(int_1 INT, int_2 INT) RETURNS INT NO SQL +BEGIN +RETURN int_1 + int_2; +END | +DROP FUNCTION IF EXISTS common_schema.sum_ints | +Warnings: +Note 1305 FUNCTION common_schema.sum_ints does not exist +CREATE AGGREGATE FUNCTION common_schema.sum_ints(int_val INT) RETURNS INT +BEGIN +DECLARE result INT DEFAULT 0; +DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN result; +LOOP FETCH GROUP NEXT ROW; +SET result = common_schema.add_ints(result, int_val); +END LOOP; +END | +use common_schema; +SELECT common_schema.sum_ints(seq) FROM (SELECT 1 seq UNION ALL SELECT 2) t; +common_schema.sum_ints(seq) +3 +USE another_schema; +SELECT common_schema.sum_ints(seq) FROM (SELECT 1 seq UNION ALL SELECT 2) t; +common_schema.sum_ints(seq) +3 +drop database common_schema; +drop database another_schema; diff --git a/mysql-test/main/custom_aggregate_functions.test b/mysql-test/main/custom_aggregate_functions.test index ab799b48bdb..4d9dd0a4929 100644 --- a/mysql-test/main/custom_aggregate_functions.test +++ b/mysql-test/main/custom_aggregate_functions.test @@ -965,3 +965,36 @@ select i, sum(i) from t1 group by i with rollup; # Cleanup drop function agg_sum; drop table t1; + +--echo # +--echo # User defined aggregate functions not working correctly when the schema is changed +--echo # + +CREATE SCHEMA IF NOT EXISTS common_schema; +CREATE SCHEMA IF NOT EXISTS another_schema; +DELIMITER |; +DROP FUNCTION IF EXISTS common_schema.add_ints | +CREATE FUNCTION common_schema.add_ints(int_1 INT, int_2 INT) RETURNS INT NO SQL +BEGIN + RETURN int_1 + int_2; +END | +DROP FUNCTION IF EXISTS common_schema.sum_ints | +CREATE AGGREGATE FUNCTION common_schema.sum_ints(int_val INT) RETURNS INT +BEGIN + DECLARE result INT DEFAULT 0; + DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN result; + LOOP FETCH GROUP NEXT ROW; + SET result = common_schema.add_ints(result, int_val); + END LOOP; +END | + +DELIMITER ;| + +use common_schema; +SELECT common_schema.sum_ints(seq) FROM (SELECT 1 seq UNION ALL SELECT 2) t; + +USE another_schema; +SELECT common_schema.sum_ints(seq) FROM (SELECT 1 seq UNION ALL SELECT 2) t; + +drop database common_schema; +drop database another_schema; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 91b75b776e2..d843e87fa03 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1384,10 +1384,12 @@ Item_sum_sp::execute() bool res; uint old_server_status= thd->server_status; - /* We set server status so we can send a signal to exit from the - function with the return value. */ + /* + We set server status so we can send a signal to exit from the + function with the return value. + */ - thd->server_status= SERVER_STATUS_LAST_ROW_SENT; + thd->server_status|= SERVER_STATUS_LAST_ROW_SENT; res= Item_sp::execute(thd, &null_value, args, arg_count); thd->server_status= old_server_status; return res; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index ba130881d68..f36650bafe9 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -4431,7 +4431,7 @@ sp_instr_agg_cfetch::execute(THD *thd, uint *nextp) else { thd->spcont->pause_state= FALSE; - if (thd->server_status == SERVER_STATUS_LAST_ROW_SENT) + if (thd->server_status & SERVER_STATUS_LAST_ROW_SENT) { my_message(ER_SP_FETCH_NO_DATA, ER_THD(thd, ER_SP_FETCH_NO_DATA), MYF(0)); From af784385b4a2b286000fa2c658e34283fe7bba60 Mon Sep 17 00:00:00 2001 From: Monty Date: Fri, 15 May 2020 14:20:43 +0300 Subject: [PATCH 15/35] Fix for using uninitialized memory MDEV-22073 MSAN use-of-uninitialized-value in collect_statistics_for_table() Other things: innodb.analyze_table was changed to mainly test statistic collection. Was discussed with Marko. --- include/my_valgrind.h | 26 +++++++++++---- .../suite/innodb/r/analyze_table.result | 19 +++-------- mysql-test/suite/innodb/t/analyze_table.test | 33 ++++--------------- sql/field.cc | 6 ++++ sql/field.h | 9 +++++ sql/sql_statistics.cc | 1 + 6 files changed, 48 insertions(+), 46 deletions(-) diff --git a/include/my_valgrind.h b/include/my_valgrind.h index 04116e556ce..3cd4210dc3b 100644 --- a/include/my_valgrind.h +++ b/include/my_valgrind.h @@ -33,6 +33,7 @@ #if defined(HAVE_VALGRIND_MEMCHECK_H) && defined(HAVE_valgrind) # include # define MEM_UNDEFINED(a,len) VALGRIND_MAKE_MEM_UNDEFINED(a,len) +# define MEM_MAKE_DEFINED(a,len) VALGRIND_MAKE_MEM_DEFINED(a,len) # define MEM_NOACCESS(a,len) VALGRIND_MAKE_MEM_NOACCESS(a,len) # define MEM_CHECK_ADDRESSABLE(a,len) VALGRIND_CHECK_MEM_IS_ADDRESSABLE(a,len) # define MEM_CHECK_DEFINED(a,len) VALGRIND_CHECK_MEM_IS_DEFINED(a,len) @@ -42,12 +43,22 @@ /* How to do manual poisoning: https://github.com/google/sanitizers/wiki/AddressSanitizerManualPoisoning */ # define MEM_UNDEFINED(a,len) ASAN_UNPOISON_MEMORY_REGION(a,len) +# define MEM_MAKE_DEFINED(a,len) ((void) 0) # define MEM_NOACCESS(a,len) ASAN_POISON_MEMORY_REGION(a,len) # define MEM_CHECK_ADDRESSABLE(a,len) ((void) 0) # define MEM_CHECK_DEFINED(a,len) ((void) 0) # define REDZONE_SIZE 8 +#elif __has_feature(memory_sanitizer) +# include +# define MEM_UNDEFINED(a,len) __msan_allocated_memory(a,len) +# define MEM_MAKE_DEFINED(a,len) __msan_unpoison(a,len) +# define MEM_NOACCESS(a,len) ((void) 0) +# define MEM_CHECK_ADDRESSABLE(a,len) ((void) 0) +# define MEM_CHECK_DEFINED(a,len) __msan_check_mem_is_initialized(a,len) +# define REDZONE_SIZE 8 #else # define MEM_UNDEFINED(a,len) ((void) (a), (void) (len)) +# define MEM_MAKE_DEFINED(a,len) ((void) 0) # define MEM_NOACCESS(a,len) ((void) 0) # define MEM_CHECK_ADDRESSABLE(a,len) ((void) 0) # define MEM_CHECK_DEFINED(a,len) ((void) 0) @@ -55,12 +66,15 @@ https://github.com/google/sanitizers/wiki/AddressSanitizerManualPoisoning */ #endif /* HAVE_VALGRIND_MEMCHECK_H */ #ifndef DBUG_OFF -/* NOTE: Do not invoke TRASH_FILL directly! Use TRASH_ALLOC or TRASH_FREE. - -The MEM_UNDEFINED() call before memset() is for canceling the effect -of any previous MEM_NOACCESS(). We must invoke MEM_UNDEFINED() after -writing the dummy pattern, unless MEM_NOACCESS() is going to be invoked. -On AddressSanitizer, the MEM_UNDEFINED() in TRASH_ALLOC() has no effect. */ +/* + TRASH_FILL() has to call MEM_UNDEFINED() to cancel any effect of TRASH_FREE(). + This can happen in the case one does + TRASH_ALLOC(A,B) ; TRASH_FREE(A,B) ; TRASH_ALLOC(A,B) + to reuse the same memory in an internal memory allocator like MEM_ROOT. + For my_malloc() and safemalloc() the extra MEM_UNDEFINED is bit of an + overkill. + TRASH_FILL() is an internal function and should not be used externally. +*/ #define TRASH_FILL(A,B,C) do { const size_t trash_tmp= (B); MEM_UNDEFINED(A, trash_tmp); memset(A, C, trash_tmp); } while (0) #else #define TRASH_FILL(A,B,C) do { MEM_UNDEFINED((A), (B)); } while (0) diff --git a/mysql-test/suite/innodb/r/analyze_table.result b/mysql-test/suite/innodb/r/analyze_table.result index a5c25289ad1..830130821da 100644 --- a/mysql-test/suite/innodb/r/analyze_table.result +++ b/mysql-test/suite/innodb/r/analyze_table.result @@ -1,25 +1,16 @@ -CREATE PROCEDURE populate_t1() -BEGIN -DECLARE i int DEFAULT 1; -START TRANSACTION; -WHILE (i <= 1000000) DO -INSERT INTO t1 VALUES (i, i, CONCAT('a', i)); -SET i = i + 1; -END WHILE; -COMMIT; -END| +set use_stat_tables='preferably'; CREATE TABLE t1( class INT, id INT, title VARCHAR(100) ) ENGINE=InnoDB; +insert into t1 select seq, seq, concat('a', seq) from seq_1_to_500; SELECT COUNT(*) FROM t1; COUNT(*) -1000000 -SET GLOBAL innodb_stats_persistent_sample_pages=2000; +500 +set @@max_heap_table_size=16384; ANALYZE TABLE t1; Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected test.t1 analyze status OK DROP TABLE t1; -DROP PROCEDURE populate_t1; -SET GLOBAL innodb_stats_persistent_sample_pages=default; diff --git a/mysql-test/suite/innodb/t/analyze_table.test b/mysql-test/suite/innodb/t/analyze_table.test index e9db3668f02..538eed04ba4 100644 --- a/mysql-test/suite/innodb/t/analyze_table.test +++ b/mysql-test/suite/innodb/t/analyze_table.test @@ -1,23 +1,11 @@ -# -# BUG#22385442 - INNODB: DIFFICULT TO FIND FREE BLOCKS IN THE BUFFER POOL -# - --source include/have_innodb.inc ---source include/big_test.inc +--source include/have_sequence.inc -DELIMITER |; -CREATE PROCEDURE populate_t1() -BEGIN - DECLARE i int DEFAULT 1; +# +# MDEV-22073 MSAN use-of-uninitialized-value in collect_statistics_for_table() +# - START TRANSACTION; - WHILE (i <= 1000000) DO - INSERT INTO t1 VALUES (i, i, CONCAT('a', i)); - SET i = i + 1; - END WHILE; - COMMIT; -END| -DELIMITER ;| +set use_stat_tables='preferably'; CREATE TABLE t1( class INT, @@ -25,18 +13,11 @@ CREATE TABLE t1( title VARCHAR(100) ) ENGINE=InnoDB; --- disable_query_log -CALL populate_t1(); --- enable_query_log +insert into t1 select seq, seq, concat('a', seq) from seq_1_to_500; SELECT COUNT(*) FROM t1; -SET GLOBAL innodb_stats_persistent_sample_pages=2000; - +set @@max_heap_table_size=16384; ANALYZE TABLE t1; DROP TABLE t1; - -DROP PROCEDURE populate_t1; - -SET GLOBAL innodb_stats_persistent_sample_pages=default; diff --git a/sql/field.cc b/sql/field.cc index 0e8dd26445f..9ab76aa37a4 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -7772,6 +7772,12 @@ my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value) } +void Field_varstring::mark_unused_memory_as_defined() +{ + uint used_length __attribute__((unused)) = get_length(); + MEM_MAKE_DEFINED(get_data() + used_length, field_length - used_length); +} + int Field_varstring::cmp_max(const uchar *a_ptr, const uchar *b_ptr, uint max_len) diff --git a/sql/field.h b/sql/field.h index fa27b0d10a6..ea57a4f07cc 100644 --- a/sql/field.h +++ b/sql/field.h @@ -850,6 +850,14 @@ public: enum_check_fields check_level); int store(const LEX_STRING *ls, CHARSET_INFO *cs) { return store(ls->str, (uint32) ls->length, cs); } + /* + Mark unused memory in the field as defined. Mainly used to ensure + that if we write full field to disk (for example in + Count_distinct_field::add(), we don't write unitalized data to + disk which would confuse valgrind or MSAN. + */ + virtual void mark_unused_memory_as_defined() {} + virtual double val_real(void)=0; virtual longlong val_int(void)=0; /* @@ -3242,6 +3250,7 @@ public: int store(const char *to,uint length,CHARSET_INFO *charset); int store(longlong nr, bool unsigned_val); int store(double nr) { return Field_str::store(nr); } /* QQ: To be deleted */ + void mark_unused_memory_as_defined(); double val_real(void); longlong val_int(void); String *val_str(String*,String *); diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index f8723a4f8ee..0f7a1d42fd6 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -1671,6 +1671,7 @@ public: */ virtual bool add() { + table_field->mark_unused_memory_as_defined(); return tree->unique_add(table_field->ptr); } From a7c4e85dd665fd5d3b7585833b68cb234901783b Mon Sep 17 00:00:00 2001 From: Monty Date: Fri, 15 May 2020 11:50:59 +0300 Subject: [PATCH 16/35] Use history.h include file if readline is used This allows us to remove our own declarations of functions and structures that are declared in the history.h file. --- client/mysql.cc | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/client/mysql.cc b/client/mysql.cc index d5ab1a12c7a..2395f0055dc 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -91,6 +91,9 @@ extern "C" { #include #else #include +#if !defined(USE_LIBEDIT_INTERFACE) +#include +#endif #define HAVE_READLINE #define USE_POPEN #endif @@ -1043,22 +1046,6 @@ static const char *embedded_server_groups[]= { "server", "embedded", "mysql_SERVER", "mariadb_SERVER", 0 }; #ifdef HAVE_READLINE -/* - HIST_ENTRY is defined for libedit, but not for the real readline - Need to redefine it for real readline to find it -*/ -#if !defined(HAVE_HIST_ENTRY) -typedef struct _hist_entry { - const char *line; - const char *data; -} HIST_ENTRY; -#endif - -extern "C" int add_history(const char *command); /* From readline directory */ -extern "C" int read_history(const char *command); -extern "C" int write_history(const char *command); -extern "C" HIST_ENTRY *history_get(int num); -extern "C" int history_length; static int not_in_history(const char *line); static void initialize_readline (); static void fix_history(String *final_command); From 3eadb135fd7b7e2d40fd6b9a819ac3245043f781 Mon Sep 17 00:00:00 2001 From: Monty Date: Fri, 15 May 2020 11:51:31 +0300 Subject: [PATCH 17/35] Fixed access to uninitalized memory found by valgrind --- sql/sql_repl.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index aa81d33f2e7..d30de30f34a 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1958,7 +1958,7 @@ send_event_to_slave(binlog_send_info *info, Log_event_type event_type, pos= my_b_tell(log); if (repl_semisync_master.update_sync_header(info->thd, - (uchar*) packet->c_ptr(), + (uchar*) packet->c_ptr_safe(), info->log_file_name + info->dirlen, pos, &need_sync)) { @@ -1982,7 +1982,8 @@ send_event_to_slave(binlog_send_info *info, Log_event_type event_type, } } - if (need_sync && repl_semisync_master.flush_net(info->thd, packet->c_ptr())) + if (need_sync && repl_semisync_master.flush_net(info->thd, + packet->c_ptr_safe())) { info->error= ER_UNKNOWN_ERROR; return "Failed to run hook 'after_send_event'"; From 1cac6d48282459d8855c1cec1eeb4bc0e3db5f89 Mon Sep 17 00:00:00 2001 From: Eugene Kosov Date: Thu, 14 May 2020 15:24:47 +0300 Subject: [PATCH 18/35] span cleanup --- include/span.h | 62 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/include/span.h b/include/span.h index 0ed0158088c..0e8516933c6 100644 --- a/include/span.h +++ b/include/span.h @@ -24,11 +24,33 @@ this program; if not, write to the Free Software Foundation, Inc., namespace st_ { +namespace detail +{ + +template struct remove_cv +{ + typedef T type; +}; +template struct remove_cv +{ + typedef T type; +}; +template struct remove_cv +{ + typedef T type; +}; +template struct remove_cv +{ + typedef T type; +}; + +} // namespace detail + template class span { public: typedef ElementType element_type; - typedef ElementType value_type; + typedef typename detail::remove_cv::type value_type; typedef size_t size_type; typedef ptrdiff_t difference_type; typedef element_type *pointer; @@ -38,7 +60,6 @@ public: typedef pointer iterator; typedef const_pointer const_iterator; typedef std::reverse_iterator reverse_iterator; - typedef std::reverse_iterator const_reverse_iterator; span() : data_(NULL), size_(0) {} @@ -64,73 +85,72 @@ public: span &operator=(const span &other) { - data_= other.data_; - size_= other.size_; + data_= other.data(); + size_= other.size(); return *this; } template span first() const { assert(!empty()); - return span(data_, 1); + return span(data(), 1); } template span last() const { assert(!empty()); - return span(data_ + size() - 1, 1); + return span(data() + size() - 1, 1); } span first(size_type count) const { assert(!empty()); - return span(data_, 1); + return span(data(), 1); } span last(size_type count) const { assert(!empty()); - return span(data_ + size() - 1, 1); + return span(data() + size() - 1, 1); } span subspan(size_type offset, size_type count) const { assert(!empty()); assert(size() >= offset + count); - return span(data_ + offset, count); + return span(data() + offset, count); } size_type size() const { return size_; } - size_type size_bytes() const { return size_ * sizeof(ElementType); } - bool empty() const __attribute__((warn_unused_result)) { return size_ == 0; } + size_type size_bytes() const { return size() * sizeof(ElementType); } + bool empty() const __attribute__((warn_unused_result)) + { + return size() == 0; + } reference operator[](size_type idx) const { assert(size() > idx); - return data_[idx]; + return data()[idx]; } reference front() const { assert(!empty()); - return data_[0]; + return data()[0]; } reference back() const { assert(!empty()); - return data_[size() - 1]; - } - pointer data() const - { - assert(!empty()); - return data_; + return data()[size() - 1]; } + pointer data() const { return data_; } iterator begin() const { return data_; } iterator end() const { return data_ + size_; } reverse_iterator rbegin() const { - return std::reverse_iterator(std::advance(end(), -1)); + return std::reverse_iterator(end()); } reverse_iterator rend() const { - return std::reverse_iterator(std::advance(begin(), -1)); + return std::reverse_iterator(begin()); } private: From ff66d65a096ec02dda1ab449d84a40361551085c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 15 May 2020 17:09:20 +0300 Subject: [PATCH 19/35] Amend af784385b4a2b286000fa2c658e34283fe7bba60: Avoid vtable overhead When neither MSAN nor Valgrind are enabled, declare Field::mark_unused_memory_as_defined() as an empty inline function, instead of declaring it as a virtual function. --- include/my_valgrind.h | 4 +++- sql/field.cc | 7 +++++-- sql/field.h | 10 ++++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/include/my_valgrind.h b/include/my_valgrind.h index 3cd4210dc3b..dc0c9bcfad4 100644 --- a/include/my_valgrind.h +++ b/include/my_valgrind.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2010, 2019, MariaDB Corporation. +/* Copyright (C) 2010, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -32,6 +32,7 @@ #if defined(HAVE_VALGRIND_MEMCHECK_H) && defined(HAVE_valgrind) # include +# define HAVE_valgrind_or_MSAN # define MEM_UNDEFINED(a,len) VALGRIND_MAKE_MEM_UNDEFINED(a,len) # define MEM_MAKE_DEFINED(a,len) VALGRIND_MAKE_MEM_DEFINED(a,len) # define MEM_NOACCESS(a,len) VALGRIND_MAKE_MEM_NOACCESS(a,len) @@ -50,6 +51,7 @@ https://github.com/google/sanitizers/wiki/AddressSanitizerManualPoisoning */ # define REDZONE_SIZE 8 #elif __has_feature(memory_sanitizer) # include +# define HAVE_valgrind_or_MSAN # define MEM_UNDEFINED(a,len) __msan_allocated_memory(a,len) # define MEM_MAKE_DEFINED(a,len) __msan_unpoison(a,len) # define MEM_NOACCESS(a,len) ((void) 0) diff --git a/sql/field.cc b/sql/field.cc index 9ab76aa37a4..d1bd5860f06 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2000, 2017, Oracle and/or its affiliates. - Copyright (c) 2008, 2017, MariaDB + Copyright (c) 2008, 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 @@ -7772,11 +7772,14 @@ my_decimal *Field_varstring::val_decimal(my_decimal *decimal_value) } + +#ifdef HAVE_valgrind_or_MSAN void Field_varstring::mark_unused_memory_as_defined() { - uint used_length __attribute__((unused)) = get_length(); + uint used_length= get_length(); MEM_MAKE_DEFINED(get_data() + used_length, field_length - used_length); } +#endif int Field_varstring::cmp_max(const uchar *a_ptr, const uchar *b_ptr, diff --git a/sql/field.h b/sql/field.h index ea57a4f07cc..3007e666ba7 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1,7 +1,7 @@ #ifndef FIELD_INCLUDED #define FIELD_INCLUDED /* Copyright (c) 2000, 2015, Oracle and/or its affiliates. - Copyright (c) 2008, 2017, MariaDB Corporation. + Copyright (c) 2008, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -850,13 +850,17 @@ public: enum_check_fields check_level); int store(const LEX_STRING *ls, CHARSET_INFO *cs) { return store(ls->str, (uint32) ls->length, cs); } - /* +#ifdef HAVE_valgrind_or_MSAN + /** Mark unused memory in the field as defined. Mainly used to ensure that if we write full field to disk (for example in Count_distinct_field::add(), we don't write unitalized data to disk which would confuse valgrind or MSAN. */ virtual void mark_unused_memory_as_defined() {} +#else + void mark_unused_memory_as_defined() {} +#endif virtual double val_real(void)=0; virtual longlong val_int(void)=0; @@ -3250,7 +3254,9 @@ public: int store(const char *to,uint length,CHARSET_INFO *charset); int store(longlong nr, bool unsigned_val); int store(double nr) { return Field_str::store(nr); } /* QQ: To be deleted */ +#ifdef HAVE_valgrind_or_MSAN void mark_unused_memory_as_defined(); +#endif /* HAVE_valgrind_or_MSAN */ double val_real(void); longlong val_int(void); String *val_str(String*,String *); From ad6171b91cac33e70bb28fa6865488b2c65e858c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 15 May 2020 17:10:59 +0300 Subject: [PATCH 20/35] MDEV-22456 Dropping the adaptive hash index may cause DDL to lock up InnoDB If the InnoDB buffer pool contains many pages for a table or index that is being dropped or rebuilt, and if many of such pages are pointed to by the adaptive hash index, dropping the adaptive hash index may consume a lot of time. The time-consuming operation of dropping the adaptive hash index entries is being executed while the InnoDB data dictionary cache dict_sys is exclusively locked. It is not actually necessary to drop all adaptive hash index entries at the time a table or index is being dropped or rebuilt. We can let the LRU replacement policy of the buffer pool take care of this gradually. For this to work, we must detach the dict_table_t and dict_index_t objects from the main dict_sys cache, and once the last adaptive hash index entry for the detached table is removed (when the garbage page is evicted from the buffer pool) we can free the dict_table_t and dict_index_t object. Related to this, in MDEV-16283, we made ALTER TABLE...DISCARD TABLESPACE skip both the buffer pool eviction and the drop of the adaptive hash index. We shifted the burden to ALTER TABLE...IMPORT TABLESPACE or DROP TABLE. We can remove the eviction from DROP TABLE. We must retain the eviction in the ALTER TABLE...IMPORT TABLESPACE code path, so that in case the discarded table is being re-imported with the same tablespace identifier, the fresh data from the imported tablespace will replace any stale pages in the buffer pool. rpl.rpl_failed_drop_tbl_binlog: Remove the test. DROP TABLE can no longer be interrupted inside InnoDB. fseg_free_page(), fseg_free_step(), fseg_free_step_not_header(), fseg_free_page_low(), fseg_free_extent(): Remove the parameter that specifies whether the adaptive hash index should be dropped. btr_search_lazy_free(): Lazily free an index when the last reference to it is dropped from the adaptive hash index. buf_pool_clear_hash_index(): Declare static, and move to the same compilation unit with the bulk of the adaptive hash index code. dict_index_t::clone(), dict_index_t::clone_if_needed(): Clone an index that is being rebuilt while adaptive hash index entries exist. The original index will be inserted into dict_table_t::freed_indexes and dict_index_t::set_freed() will be called. dict_index_t::set_freed(), dict_index_t::freed(): Note that or check whether the index has been freed. We will use the impossible page number 1 to denote this condition. dict_index_t::n_ahi_pages(): Replaces btr_search_info_get_ref_count(). dict_index_t::detach_columns(): Move the assignment n_fields=0 to ha_innobase_inplace_ctx::clear_added_indexes(). We must have access to the columns when freeing the adaptive hash index. Note: dict_table_t::v_cols[] will remain valid. If virtual columns are dropped or added, the table definition will be reloaded in ha_innobase::commit_inplace_alter_table(). buf_page_mtr_lock(): Drop a stale adaptive hash index if needed. We will also reduce the number of btr_get_search_latch() calls and enclose some more code inside #ifdef BTR_CUR_HASH_ADAPT in order to benefit cmake -DWITH_INNODB_AHI=OFF. --- .../rpl/r/rpl_failed_drop_tbl_binlog.result | 32 -- .../rpl/t/rpl_failed_drop_tbl_binlog.test | 64 --- storage/innobase/btr/btr0btr.cc | 22 +- storage/innobase/btr/btr0cur.cc | 21 +- storage/innobase/btr/btr0pcur.cc | 9 +- storage/innobase/btr/btr0sea.cc | 364 +++++++++--------- storage/innobase/buf/buf0buf.cc | 89 ++--- storage/innobase/buf/buf0lru.cc | 160 -------- storage/innobase/dict/dict0crea.cc | 9 +- storage/innobase/dict/dict0dict.cc | 141 ++++--- storage/innobase/dict/dict0mem.cc | 7 + storage/innobase/dict/dict0stats.cc | 9 + storage/innobase/fsp/fsp0fsp.cc | 102 +---- storage/innobase/gis/gis0sea.cc | 6 +- storage/innobase/handler/handler0alter.cc | 16 +- storage/innobase/ibuf/ibuf0ibuf.cc | 2 +- storage/innobase/include/btr0cur.h | 13 +- storage/innobase/include/btr0pcur.h | 11 +- storage/innobase/include/btr0pcur.ic | 6 +- storage/innobase/include/btr0sea.h | 51 +-- storage/innobase/include/btr0sea.ic | 41 +- storage/innobase/include/buf0buf.h | 10 +- storage/innobase/include/buf0lru.h | 11 - storage/innobase/include/dict0mem.h | 24 +- storage/innobase/include/fsp0fsp.h | 47 +-- storage/innobase/row/row0import.cc | 14 +- storage/innobase/row/row0ins.cc | 8 +- storage/innobase/row/row0log.cc | 6 +- storage/innobase/row/row0merge.cc | 13 +- storage/innobase/row/row0mysql.cc | 32 +- storage/innobase/row/row0sel.cc | 92 ++--- storage/innobase/row/row0trunc.cc | 13 +- storage/innobase/trx/trx0purge.cc | 6 +- storage/innobase/trx/trx0undo.cc | 4 +- 34 files changed, 552 insertions(+), 903 deletions(-) delete mode 100644 mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result delete mode 100644 mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test diff --git a/mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result b/mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result deleted file mode 100644 index df36fa82e0f..00000000000 --- a/mysql-test/suite/rpl/r/rpl_failed_drop_tbl_binlog.result +++ /dev/null @@ -1,32 +0,0 @@ -include/master-slave.inc -[connection master] -create table t1 (a int) engine=innodb; -create table t2 (b longblob) engine=innodb; -create table t3 (c int) engine=innodb; -insert into t2 values (repeat('b',1024*1024)); -insert into t2 select * from t2; -insert into t2 select * from t2; -insert into t2 select * from t2; -insert into t2 select * from t2; -set debug_sync='rm_table_no_locks_before_delete_table SIGNAL nogo WAIT_FOR go EXECUTE 2'; -drop table t1, t2, t3; -connect foo,localhost,root; -set debug_sync='now SIGNAL go'; -kill query CONNECTION_ID; -connection master; -ERROR 70100: Query execution was interrupted -"Tables t2 and t3 should be listed" -SHOW TABLES; -Tables_in_test -t2 -t3 -include/show_binlog_events.inc -Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 # Gtid # # GTID #-#-# -master-bin.000001 # Query # # use `test`; DROP TABLE `t1` /* generated by server */ -connection slave; -drop table t2, t3; -connection master; -set debug_sync='RESET'; -drop table t2, t3; -include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test b/mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test deleted file mode 100644 index 281e2a2ab47..00000000000 --- a/mysql-test/suite/rpl/t/rpl_failed_drop_tbl_binlog.test +++ /dev/null @@ -1,64 +0,0 @@ -# ==== Purpose ==== -# -# Check that when the execution of a DROP TABLE command with single table -# fails it should not be written to the binary log. Also test that when the -# execution of DROP TABLE command with multiple tables fails the command -# should be written into the binary log. -# -# ==== Implementation ==== -# -# Steps: -# 0 - Create tables named t1, t2, t3 -# 1 - Execute DROP TABLE t1,t2,t3 command. -# 2 - Kill the DROP TABLE command while it is trying to drop table 't2'. -# 3 - Verify that tables t2,t3 are present after the DROP command execution -# was interrupted. -# 4 - Check that table 't1' is present in binary log as part of DROP -# command. -# -# ==== References ==== -# -# MDEV-20348: DROP TABLE IF EXISTS killed on master but was replicated. -# - ---source include/have_innodb.inc ---source include/have_debug_sync.inc ---source include/have_binlog_format_statement.inc ---source include/master-slave.inc - -create table t1 (a int) engine=innodb; -create table t2 (b longblob) engine=innodb; -create table t3 (c int) engine=innodb; -insert into t2 values (repeat('b',1024*1024)); -insert into t2 select * from t2; -insert into t2 select * from t2; -insert into t2 select * from t2; -insert into t2 select * from t2; -let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); - -let $id=`select connection_id()`; -set debug_sync='rm_table_no_locks_before_delete_table SIGNAL nogo WAIT_FOR go EXECUTE 2'; -send drop table t1, t2, t3; - -connect foo,localhost,root; -set debug_sync='now SIGNAL go'; -let $wait_condition=select 1 from information_schema.processlist where state like 'debug sync point:%'; -source include/wait_condition.inc; ---replace_result $id CONNECTION_ID -eval kill query $id; - -connection master; -error ER_QUERY_INTERRUPTED; -reap; - ---echo "Tables t2 and t3 should be listed" -SHOW TABLES; ---source include/show_binlog_events.inc ---sync_slave_with_master -drop table t2, t3; - -connection master; -set debug_sync='RESET'; -drop table t2, t3; - -source include/rpl_end.inc; diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index 2049bbd26fa..5f38e623985 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -2,7 +2,7 @@ Copyright (c) 1994, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2014, 2019, MariaDB Corporation. +Copyright (c) 2014, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -723,8 +723,10 @@ void btr_page_free(dict_index_t* index, buf_block_t* block, mtr_t* mtr, { ut_ad(mtr_is_block_fix(mtr, block, MTR_MEMO_PAGE_X_FIX, index->table)); #ifdef BTR_CUR_HASH_ADAPT - ut_ad(!block->index || !blob); - ut_ad(!block->index || page_is_leaf(block->frame)); + if (block->index && !block->index->freed()) { + ut_ad(!blob); + ut_ad(page_is_leaf(block->frame)); + } #endif ut_ad(index->space == block->page.id.space()); /* The root page is freed by btr_free_root(). */ @@ -751,7 +753,7 @@ void btr_page_free(dict_index_t* index, buf_block_t* block, mtr_t* mtr, fseg_free_page(seg_header, block->page.id.space(), block->page.id.page_no(), - block->index != NULL, mtr); + mtr); /* The page was marked free in the allocation bitmap, but it should remain exclusively latched until mtr_t::commit() or until it @@ -876,7 +878,7 @@ btr_page_get_father_node_ptr_func( err = btr_cur_search_to_nth_level( index, level + 1, tuple, PAGE_CUR_LE, latch_mode, cursor, 0, - file, line, mtr); + file, line, mtr, 0); if (err != DB_SUCCESS) { ib::warn() << " Error code: " << err @@ -1005,7 +1007,7 @@ static void btr_free_root(buf_block_t* block, mtr_t* mtr, bool invalidate) BTR_FREED_INDEX_ID, mtr); } - while (!fseg_free_step(header, true, mtr)) { + while (!fseg_free_step(header, mtr)) { /* Free the entire segment in small steps. */ } } @@ -1254,7 +1256,7 @@ leaf_loop: fsp0fsp. */ finished = fseg_free_step(root + PAGE_HEADER + PAGE_BTR_SEG_LEAF, - true, &mtr); + &mtr); mtr_commit(&mtr); if (!finished) { @@ -1274,7 +1276,7 @@ top_loop: #endif /* UNIV_BTR_DEBUG */ finished = fseg_free_step_not_header( - root + PAGE_HEADER + PAGE_BTR_SEG_TOP, true, &mtr); + root + PAGE_HEADER + PAGE_BTR_SEG_TOP, &mtr); mtr_commit(&mtr); if (!finished) { @@ -2342,7 +2344,7 @@ btr_insert_on_non_leaf_level_func( dberr_t err = btr_cur_search_to_nth_level( index, level, tuple, PAGE_CUR_LE, BTR_CONT_MODIFY_TREE, - &cursor, 0, file, line, mtr); + &cursor, 0, file, line, mtr, 0); if (err != DB_SUCCESS) { ib::warn() << " Error code: " << err @@ -2363,7 +2365,7 @@ btr_insert_on_non_leaf_level_func( btr_cur_search_to_nth_level(index, level, tuple, PAGE_CUR_RTREE_INSERT, BTR_CONT_MODIFY_TREE, - &cursor, 0, file, line, mtr); + &cursor, 0, file, line, mtr, 0); } ut_ad(cursor.flag == BTR_CUR_BINARY); diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc index 2bf657f037f..37ba133cb0f 100644 --- a/storage/innobase/btr/btr0cur.cc +++ b/storage/innobase/btr/btr0cur.cc @@ -864,8 +864,7 @@ search tuple should be performed in the B-tree. InnoDB does an insert immediately after the cursor. Thus, the cursor may end up on a user record, or on a page infimum record. */ dberr_t -btr_cur_search_to_nth_level( -/*========================*/ +btr_cur_search_to_nth_level_func( dict_index_t* index, /*!< in: index */ ulint level, /*!< in: the tree level of search */ const dtuple_t* tuple, /*!< in: data tuple; NOTE: n_fields_cmp in @@ -887,10 +886,12 @@ btr_cur_search_to_nth_level( to protect the record! */ btr_cur_t* cursor, /*!< in/out: tree cursor; the cursor page is s- or x-latched, but see also above! */ +#ifdef BTR_CUR_HASH_ADAPT ulint has_search_latch, /*!< in: info on the latch mode the caller currently has on search system: RW_S_LATCH, or 0 */ +#endif /* BTR_CUR_HASH_ADAPT */ const char* file, /*!< in: file name */ unsigned line, /*!< in: line where called */ mtr_t* mtr, /*!< in: mtr */ @@ -1053,6 +1054,7 @@ btr_cur_search_to_nth_level( } #ifdef BTR_CUR_HASH_ADAPT + rw_lock_t* const search_latch = btr_get_search_latch(index); # ifdef UNIV_SEARCH_PERF_STAT info->n_searches++; @@ -1073,8 +1075,7 @@ btr_cur_search_to_nth_level( will have to check it again. */ && btr_search_enabled && !modify_external - && rw_lock_get_writer(btr_get_search_latch(index)) - == RW_LOCK_NOT_LOCKED + && rw_lock_get_writer(search_latch) == RW_LOCK_NOT_LOCKED && btr_search_guess_on_hash(index, info, tuple, mode, latch_mode, cursor, has_search_latch, mtr)) { @@ -1098,10 +1099,12 @@ btr_cur_search_to_nth_level( /* If the hash search did not succeed, do binary search down the tree */ +#ifdef BTR_CUR_HASH_ADAPT if (has_search_latch) { /* Release possible search latch to obey latching order */ - btr_search_s_unlock(index); + rw_lock_s_unlock(search_latch); } +#endif /* BTR_CUR_HASH_ADAPT */ /* Store the position of the tree latch we push to mtr so that we know how to release it when we have latched leaf node(s) */ @@ -2155,9 +2158,11 @@ func_exit: ut_free(prev_tree_savepoints); } +#ifdef BTR_CUR_HASH_ADAPT if (has_search_latch) { - btr_search_s_lock(index); + rw_lock_s_lock(search_latch); } +#endif /* BTR_CUR_HASH_ADAPT */ if (mbr_adj) { /* remember that we will need to adjust parent MBR */ @@ -5746,7 +5751,7 @@ btr_estimate_n_rows_in_range_low( btr_cur_search_to_nth_level(index, 0, tuple1, mode1, BTR_SEARCH_LEAF | BTR_ESTIMATE, &cursor, 0, - __FILE__, __LINE__, &mtr); + __FILE__, __LINE__, &mtr, 0); ut_ad(!page_rec_is_infimum(btr_cur_get_rec(&cursor))); @@ -5800,7 +5805,7 @@ btr_estimate_n_rows_in_range_low( btr_cur_search_to_nth_level(index, 0, tuple2, mode2, BTR_SEARCH_LEAF | BTR_ESTIMATE, &cursor, 0, - __FILE__, __LINE__, &mtr); + __FILE__, __LINE__, &mtr, 0); const rec_t* rec = btr_cur_get_rec(&cursor); diff --git a/storage/innobase/btr/btr0pcur.cc b/storage/innobase/btr/btr0pcur.cc index 7a62e4c128a..4340c2f32b0 100644 --- a/storage/innobase/btr/btr0pcur.cc +++ b/storage/innobase/btr/btr0pcur.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2019, MariaDB Corporation. +Copyright (c) 2016, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -353,8 +353,11 @@ btr_pcur_restore_position_func( mode = PAGE_CUR_UNSUPP; } - btr_pcur_open_with_no_init_func(index, tuple, mode, latch_mode, - cursor, 0, file, line, mtr); + btr_pcur_open_with_no_init_func(index, tuple, mode, latch_mode, cursor, +#ifdef BTR_CUR_HASH_ADAPT + 0, +#endif /* BTR_CUR_HASH_ADAPT */ + file, line, mtr); /* Restore the old search mode */ cursor->search_mode = old_mode; diff --git a/storage/innobase/btr/btr0sea.cc b/storage/innobase/btr/btr0sea.cc index 7671895953e..f1075358ad2 100644 --- a/storage/innobase/btr/btr0sea.cc +++ b/storage/innobase/btr/btr0sea.cc @@ -2,7 +2,7 @@ Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2020, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -141,15 +141,8 @@ static void btr_search_check_free_space_in_heap(dict_index_t* index) { - hash_table_t* table; - mem_heap_t* heap; - - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_S)); - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_X)); - - table = btr_get_search_table(index); - - heap = table->heap; + hash_table_t* table = btr_get_search_table(index); + mem_heap_t* heap = table->heap; /* Note that we peek the value of heap->free_block without reserving the latch: this is ok, because we will not guarantee that there will @@ -157,8 +150,9 @@ btr_search_check_free_space_in_heap(dict_index_t* index) if (heap->free_block == NULL) { buf_block_t* block = buf_block_alloc(NULL); + rw_lock_t* latch = btr_get_search_latch(index); - btr_search_x_lock(index); + rw_lock_x_lock(latch); if (btr_search_enabled && heap->free_block == NULL) { @@ -167,7 +161,7 @@ btr_search_check_free_space_in_heap(dict_index_t* index) buf_block_free(block); } - btr_search_x_unlock(index); + rw_lock_x_unlock(latch); } } @@ -289,18 +283,88 @@ btr_search_disable_ref_count( { dict_index_t* index; - ut_ad(mutex_own(&dict_sys->mutex)); - for (index = dict_table_get_first_index(table); index != NULL; index = dict_table_get_next_index(index)) { - - ut_ad(rw_lock_own(btr_get_search_latch(index), RW_LOCK_X)); - index->search_info->ref_count = 0; } } +/** Lazily free detached metadata when removing the last reference. */ +ATTRIBUTE_COLD static void btr_search_lazy_free(dict_index_t *index) +{ + ut_ad(index->freed()); + dict_table_t *table= index->table; + /* Perform the skipped steps of dict_index_remove_from_cache_low(). */ + UT_LIST_REMOVE(table->freed_indexes, index); + rw_lock_free(&index->lock); + dict_mem_index_free(index); + + if (!UT_LIST_GET_LEN(table->freed_indexes) && + !UT_LIST_GET_LEN(table->indexes)) + { + ut_ad(table->id == 0); + dict_mem_table_free(table); + } +} + +/** Clear the adaptive hash index on all pages in the buffer pool. */ +static void buf_pool_clear_hash_index() +{ + ut_ad(btr_search_own_all(RW_LOCK_X)); + ut_ad(!btr_search_enabled); + + std::set garbage; + + for (ulong p = 0; p < srv_buf_pool_instances; p++) + { + buf_pool_t *buf_pool= buf_pool_from_array(p); + buf_chunk_t *chunks= buf_pool->chunks; + buf_chunk_t *chunk= chunks + buf_pool->n_chunks; + + while (--chunk >= chunks) + { + buf_block_t *block= chunk->blocks; + for (ulint i= chunk->size; i--; block++) + { + dict_index_t *index= block->index; + assert_block_ahi_valid(block); + + /* We can clear block->index and block->n_pointers when + btr_search_own_all(RW_LOCK_X); see the comments in buf0buf.h */ + + if (!index) + { +# if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG + ut_a(!block->n_pointers); +# endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ + continue; + } + + ut_d(buf_page_state state= buf_block_get_state(block)); + /* Another thread may have set the state to + BUF_BLOCK_REMOVE_HASH in buf_LRU_block_remove_hashed(). + + The state change in buf_page_realloc() is not observable here, + because in that case we would have !block->index. + + In the end, the entire adaptive hash index will be removed. */ + ut_ad(state == BUF_BLOCK_FILE_PAGE || state == BUF_BLOCK_REMOVE_HASH); +# if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG + block->n_pointers= 0; +# endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ + if (index->freed()) + garbage.insert(index); + block->index= NULL; + } + } + } + + for (std::set::iterator i= garbage.begin(); + i != garbage.end(); i++) + btr_search_lazy_free(*i); +} + /** Disable the adaptive hash search system and empty the index. @param[in] need_mutex need to acquire dict_sys->mutex */ void @@ -373,33 +437,6 @@ btr_search_enable() btr_search_x_unlock_all(); } -/** Returns the value of ref_count. The value is protected by latch. -@param[in] info search info -@param[in] index index identifier -@return ref_count value. */ -ulint -btr_search_info_get_ref_count( - btr_search_t* info, - dict_index_t* index) -{ - ulint ret = 0; - - if (!btr_search_enabled) { - return(ret); - } - - ut_ad(info); - - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_S)); - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_X)); - - btr_search_s_lock(index); - ret = info->ref_count; - btr_search_s_unlock(index); - - return(ret); -} - /** Updates the search info of an index about hash successes. NOTE that info is NOT protected by any semaphore, to save CPU time! Do not assume its fields are consistent. @@ -415,8 +452,8 @@ btr_search_info_update_hash( ulint n_unique; int cmp; - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_S)); - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_X)); + ut_ad(!rw_lock_own_flagged(btr_get_search_latch(index), + RW_LOCK_FLAG_X | RW_LOCK_FLAG_S)); if (dict_index_is_ibuf(index)) { /* So many deletes are performed on an insert buffer tree @@ -601,28 +638,25 @@ btr_search_update_hash_ref( buf_block_t* block, const btr_cur_t* cursor) { - dict_index_t* index; - ulint fold; - rec_t* rec; - ut_ad(cursor->flag == BTR_CUR_HASH_FAIL); - ut_ad(rw_lock_own(btr_get_search_latch(cursor->index), RW_LOCK_X)); + ut_ad(rw_lock_own_flagged(&block->lock, RW_LOCK_FLAG_X | RW_LOCK_FLAG_S)); ut_ad(page_align(btr_cur_get_rec(cursor)) == block->frame); ut_ad(page_is_leaf(block->frame)); assert_block_ahi_valid(block); - index = block->index; + dict_index_t* index = block->index; if (!index) { - return; } ut_ad(block->page.id.space() == index->space); ut_a(index == cursor->index); - ut_a(!dict_index_is_ibuf(index)); + ut_ad(!dict_index_is_ibuf(index)); + rw_lock_t* const latch = btr_get_search_latch(index); + rw_lock_x_lock(latch); if ((info->n_hash_potential > 0) && (block->curr_n_fields == info->n_fields) @@ -632,28 +666,30 @@ btr_search_update_hash_ref( rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs_init(offsets_); - rec = btr_cur_get_rec(cursor); + const rec_t* rec = btr_cur_get_rec(cursor); if (!page_rec_is_user_rec(rec)) { - - return; + goto func_exit; } - fold = rec_fold(rec, - rec_get_offsets(rec, index, offsets_, true, - ULINT_UNDEFINED, &heap), - block->curr_n_fields, - block->curr_n_bytes, index->id); + ulint fold = rec_fold( + rec, + rec_get_offsets(rec, index, offsets_, true, + ULINT_UNDEFINED, &heap), + block->curr_n_fields, + block->curr_n_bytes, index->id); if (UNIV_LIKELY_NULL(heap)) { mem_heap_free(heap); } - ut_ad(rw_lock_own(btr_get_search_latch(index), RW_LOCK_X)); ha_insert_for_fold(btr_get_search_table(index), fold, block, rec); MONITOR_INC(MONITOR_ADAPTIVE_HASH_ROW_ADDED); } + +func_exit: + rw_lock_x_unlock(latch); } /** Updates the search info. @@ -692,12 +728,7 @@ btr_search_info_update_slow( #ifdef UNIV_SEARCH_PERF_STAT btr_search_n_hash_fail++; #endif /* UNIV_SEARCH_PERF_STAT */ - - btr_search_x_lock(cursor->index); - btr_search_update_hash_ref(info, block, cursor); - - btr_search_x_unlock(cursor->index); } if (build_index) { @@ -898,7 +929,6 @@ btr_search_guess_on_hash( ulint has_search_latch, mtr_t* mtr) { - const rec_t* rec; ulint fold; index_id_t index_id; #ifdef notdefined @@ -944,33 +974,28 @@ btr_search_guess_on_hash( cursor->fold = fold; cursor->flag = BTR_CUR_HASH; + rw_lock_t* const latch = btr_get_search_latch(index); + if (!has_search_latch) { - btr_search_s_lock(index); + rw_lock_s_lock(latch); if (!btr_search_enabled) { - btr_search_s_unlock(index); - +fail: + if (!has_search_latch) { + rw_lock_s_unlock(latch); + } btr_search_failure(info, cursor); - return(FALSE); } } - ut_ad(rw_lock_get_writer(btr_get_search_latch(index)) != RW_LOCK_X); - ut_ad(rw_lock_get_reader_count(btr_get_search_latch(index)) > 0); + ut_ad(rw_lock_own(latch, RW_LOCK_S)); - rec = (rec_t*) ha_search_and_get_data( - btr_get_search_table(index), fold); + const rec_t* rec = static_cast( + ha_search_and_get_data(btr_get_search_table(index), fold)); - if (rec == NULL) { - - if (!has_search_latch) { - btr_search_s_unlock(index); - } - - btr_search_failure(info, cursor); - - return(FALSE); + if (!rec) { + goto fail; } buf_block_t* block = buf_block_from_ahi(rec); @@ -980,32 +1005,34 @@ btr_search_guess_on_hash( if (!buf_page_get_known_nowait( latch_mode, block, BUF_MAKE_YOUNG, __FILE__, __LINE__, mtr)) { - - if (!has_search_latch) { - btr_search_s_unlock(index); - } - - btr_search_failure(info, cursor); - - return(FALSE); + goto fail; } - btr_search_s_unlock(index); + const bool fail = index != block->index + && index_id == block->index->id; + ut_a(!fail || block->index->freed()); + rw_lock_s_unlock(latch); buf_block_dbg_add_level(block, SYNC_TREE_NODE_FROM_HASH); + if (UNIV_UNLIKELY(fail)) { + btr_search_drop_page_hash_index(block); + goto fail_and_release_page; + } + } else if (UNIV_UNLIKELY(index != block->index + && index_id == block->index->id)) { + ut_a(block->index->freed()); + goto fail_and_release_page; } if (buf_block_get_state(block) != BUF_BLOCK_FILE_PAGE) { ut_ad(buf_block_get_state(block) == BUF_BLOCK_REMOVE_HASH); - +fail_and_release_page: if (!has_search_latch) { - btr_leaf_page_release(block, latch_mode, mtr); } btr_search_failure(info, cursor); - return(FALSE); } @@ -1024,14 +1051,7 @@ btr_search_guess_on_hash( || !btr_search_check_guess(cursor, has_search_latch, tuple, mode, mtr)) { - - if (!has_search_latch) { - btr_leaf_page_release(block, latch_mode, mtr); - } - - btr_search_failure(info, cursor); - - return(FALSE); + goto fail_and_release_page; } if (info->n_hash_potential < BTR_SEARCH_BUILD_LIMIT + 5) { @@ -1115,30 +1135,26 @@ btr_search_drop_page_hash_index(buf_block_t* block) ulint* folds; ulint i; mem_heap_t* heap; - const dict_index_t* index; rec_offs* offsets; rw_lock_t* latch; - btr_search_t* info; retry: - /* Do a dirty check on block->index, return if the block is - not in the adaptive hash index. */ - index = block->index; /* This debug check uses a dirty read that could theoretically cause false positives while buf_pool_clear_hash_index() is executing. */ assert_block_ahi_valid(block); - if (index == NULL) { + if (!block->index) { return; } ut_ad(block->page.buf_fix_count == 0 || buf_block_get_state(block) == BUF_BLOCK_REMOVE_HASH || rw_lock_own_flagged(&block->lock, - RW_LOCK_FLAG_X | RW_LOCK_FLAG_S)); + RW_LOCK_FLAG_X | RW_LOCK_FLAG_S + | RW_LOCK_FLAG_SX)); ut_ad(page_is_leaf(block->frame)); - /* We must not dereference index here, because it could be freed + /* We must not dereference block->index here, because it could be freed if (index->table->n_ref_count == 0 && !mutex_own(&dict_sys->mutex)). Determine the ahi_slot based on the block contents. */ @@ -1156,18 +1172,12 @@ retry: rw_lock_s_lock(latch); assert_block_ahi_valid(block); - if (block->index == NULL) { + if (!block->index) { rw_lock_s_unlock(latch); return; } - /* The index associated with a block must remain the - same, because we are holding block->lock or the block is - not accessible by other threads (BUF_BLOCK_REMOVE_HASH), - or the index is not accessible to other threads - (buf_fix_count == 0 when DROP TABLE or similar is executing - buf_LRU_drop_page_hash_for_tablespace()). */ - ut_a(index == block->index); + dict_index_t* index = block->index; #ifdef MYSQL_INDEX_DISABLE_AHI ut_ad(!index->disable_ahi); #endif @@ -1176,7 +1186,7 @@ retry: ut_ad(index->space == FIL_NULL || block->page.id.space() == index->space); ut_a(index_id == index->id); - ut_a(!dict_index_is_ibuf(index)); + ut_ad(!dict_index_is_ibuf(index)); #ifdef UNIV_DEBUG switch (dict_index_get_online_status(index)) { case ONLINE_INDEX_CREATION: @@ -1281,9 +1291,14 @@ next_rec: folds[i], page); } - info = btr_search_get_info(block->index); - ut_a(info->ref_count > 0); - info->ref_count--; + switch (index->search_info->ref_count--) { + case 0: + ut_error; + case 1: + if (index->freed()) { + btr_search_lazy_free(index); + } + } block->index = NULL; @@ -1383,27 +1398,27 @@ btr_search_build_page_hash_index( rec_offs_init(offsets_); ut_ad(index); ut_ad(block->page.id.space() == index->space); - ut_a(!dict_index_is_ibuf(index)); + ut_ad(!dict_index_is_ibuf(index)); ut_ad(page_is_leaf(block->frame)); - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_X)); ut_ad(rw_lock_own_flagged(&block->lock, RW_LOCK_FLAG_X | RW_LOCK_FLAG_S)); + ut_ad(block->page.id.page_no() >= 3); - btr_search_s_lock(index); + rw_lock_t* const latch = btr_get_search_latch(index); + rw_lock_s_lock(latch); table = btr_get_search_table(index); page = buf_block_get_frame(block); - if (block->index && ((block->curr_n_fields != n_fields) - || (block->curr_n_bytes != n_bytes) - || (block->curr_left_side != left_side))) { - - btr_search_s_unlock(index); + const bool must_drop = block->index + && ((block->curr_n_fields != n_fields) + || (block->curr_n_bytes != n_bytes) + || (block->curr_left_side != left_side)); + rw_lock_s_unlock(latch); + if (must_drop) { btr_search_drop_page_hash_index(block); - } else { - btr_search_s_unlock(index); } /* Check that the values for hash index build are sensible */ @@ -1495,7 +1510,7 @@ btr_search_build_page_hash_index( btr_search_check_free_space_in_heap(index); - btr_search_x_lock(index); + rw_lock_x_lock(latch); if (!btr_search_enabled) { goto exit_func; @@ -1533,7 +1548,7 @@ btr_search_build_page_hash_index( MONITOR_INC_VALUE(MONITOR_ADAPTIVE_HASH_ROW_ADDED, n_cached); exit_func: assert_block_ahi_valid(block); - btr_search_x_unlock(index); + rw_lock_x_unlock(latch); ut_free(folds); ut_free(recs); @@ -1566,21 +1581,19 @@ btr_search_move_or_delete_hash_entries( ut_ad(rw_lock_own(&(block->lock), RW_LOCK_X)); ut_ad(rw_lock_own(&(new_block->lock), RW_LOCK_X)); - btr_search_s_lock(index); + rw_lock_t* const latch = btr_get_search_latch(index); + rw_lock_s_lock(latch); ut_a(!new_block->index || new_block->index == index); ut_a(!block->index || block->index == index); - ut_a(!(new_block->index || block->index) - || !dict_index_is_ibuf(index)); + ut_ad(!(new_block->index || block->index) + || !dict_index_is_ibuf(index)); assert_block_ahi_valid(block); assert_block_ahi_valid(new_block); if (new_block->index) { - - btr_search_s_unlock(index); - + rw_lock_s_unlock(latch); btr_search_drop_page_hash_index(block); - return; } @@ -1593,7 +1606,7 @@ btr_search_move_or_delete_hash_entries( new_block->n_bytes = block->curr_n_bytes; new_block->left_side = left_side; - btr_search_s_unlock(index); + rw_lock_s_unlock(latch); ut_a(n_fields > 0 || n_bytes > 0); @@ -1605,7 +1618,7 @@ btr_search_move_or_delete_hash_entries( return; } - btr_search_s_unlock(index); + rw_lock_s_unlock(latch); } /** Updates the page hash index when a single record is deleted from a page. @@ -1647,7 +1660,7 @@ btr_search_update_hash_on_delete(btr_cur_t* cursor) ut_ad(block->page.id.space() == index->space); ut_a(index == cursor->index); ut_a(block->curr_n_fields > 0 || block->curr_n_bytes > 0); - ut_a(!dict_index_is_ibuf(index)); + ut_ad(!dict_index_is_ibuf(index)); table = btr_get_search_table(index); @@ -1660,7 +1673,8 @@ btr_search_update_hash_on_delete(btr_cur_t* cursor) mem_heap_free(heap); } - btr_search_x_lock(index); + rw_lock_t* const latch = btr_get_search_latch(index); + rw_lock_x_lock(latch); assert_block_ahi_valid(block); if (block->index) { @@ -1676,7 +1690,7 @@ btr_search_update_hash_on_delete(btr_cur_t* cursor) assert_block_ahi_valid(block); } - btr_search_x_unlock(index); + rw_lock_x_unlock(latch); } /** Updates the page hash index when a single record is inserted on a page. @@ -1712,9 +1726,10 @@ btr_search_update_hash_node_on_insert(btr_cur_t* cursor) } ut_a(cursor->index == index); - ut_a(!dict_index_is_ibuf(index)); + ut_ad(!dict_index_is_ibuf(index)); - btr_search_x_lock(index); + rw_lock_t* const latch = btr_get_search_latch(index); + rw_lock_x_lock(latch); if (!block->index) { @@ -1738,10 +1753,9 @@ btr_search_update_hash_node_on_insert(btr_cur_t* cursor) func_exit: assert_block_ahi_valid(block); - btr_search_x_unlock(index); + rw_lock_x_unlock(latch); } else { - btr_search_x_unlock(index); - + rw_lock_x_unlock(latch); btr_search_update_hash_on_insert(cursor); } } @@ -1765,8 +1779,6 @@ btr_search_update_hash_on_insert(btr_cur_t* cursor) ulint next_fold = 0; /* remove warning (??? bug ???) */ ulint n_fields; ulint n_bytes; - ibool left_side; - ibool locked = FALSE; mem_heap_t* heap = NULL; rec_offs offsets_[REC_OFFS_NORMAL_SIZE]; rec_offs* offsets = offsets_; @@ -1803,11 +1815,11 @@ btr_search_update_hash_on_insert(btr_cur_t* cursor) ut_a(!index->disable_ahi); #endif ut_a(index == cursor->index); - ut_a(!dict_index_is_ibuf(index)); + ut_ad(!dict_index_is_ibuf(index)); n_fields = block->curr_n_fields; n_bytes = block->curr_n_bytes; - left_side = block->curr_left_side; + const bool left_side = block->curr_left_side; ins_rec = page_rec_get_next_const(rec); next_rec = page_rec_get_next_const(ins_rec); @@ -1824,17 +1836,18 @@ btr_search_update_hash_on_insert(btr_cur_t* cursor) n_bytes, index->id); } + rw_lock_t* const latch = btr_get_search_latch(index); + bool locked = false; + if (!page_rec_is_infimum(rec)) { offsets = rec_get_offsets( rec, index, offsets, true, btr_search_get_n_fields(n_fields, n_bytes), &heap); fold = rec_fold(rec, offsets, n_fields, n_bytes, index->id); } else { - if (left_side) { - - btr_search_x_lock(index); - - locked = TRUE; + locked = left_side; + if (locked) { + rw_lock_x_lock(latch); if (!btr_search_enabled) { goto function_exit; @@ -1849,10 +1862,8 @@ btr_search_update_hash_on_insert(btr_cur_t* cursor) if (fold != ins_fold) { if (!locked) { - - btr_search_x_lock(index); - - locked = TRUE; + locked = true; + rw_lock_x_lock(latch); if (!btr_search_enabled) { goto function_exit; @@ -1870,11 +1881,9 @@ check_next_rec: if (page_rec_is_supremum(next_rec)) { if (!left_side) { - if (!locked) { - btr_search_x_lock(index); - - locked = TRUE; + locked = true; + rw_lock_x_lock(latch); if (!btr_search_enabled) { goto function_exit; @@ -1888,12 +1897,9 @@ check_next_rec: } if (ins_fold != next_fold) { - if (!locked) { - - btr_search_x_lock(index); - - locked = TRUE; + locked = true; + rw_lock_x_lock(latch); if (!btr_search_enabled) { goto function_exit; @@ -1912,7 +1918,7 @@ function_exit: mem_heap_free(heap); } if (locked) { - btr_search_x_unlock(index); + rw_lock_x_unlock(latch); } } @@ -2019,7 +2025,7 @@ btr_search_hash_table_validate(ulint hash_table_id) == BUF_BLOCK_REMOVE_HASH); } - ut_a(!dict_index_is_ibuf(block->index)); + ut_ad(!dict_index_is_ibuf(block->index)); ut_ad(block->page.id.space() == block->index->space); page_index_id = btr_page_get_index_id(block->frame); diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index 11c2e0e5ad5..3beae8982b6 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -3165,66 +3165,6 @@ DECLARE_THREAD(buf_resize_thread)(void*) OS_THREAD_DUMMY_RETURN; } -#ifdef BTR_CUR_HASH_ADAPT -/** Clear the adaptive hash index on all pages in the buffer pool. */ -void -buf_pool_clear_hash_index() -{ - ulint p; - - ut_ad(btr_search_own_all(RW_LOCK_X)); - ut_ad(!buf_pool_resizing); - ut_ad(!btr_search_enabled); - - for (p = 0; p < srv_buf_pool_instances; p++) { - buf_pool_t* buf_pool = buf_pool_from_array(p); - buf_chunk_t* chunks = buf_pool->chunks; - buf_chunk_t* chunk = chunks + buf_pool->n_chunks; - - while (--chunk >= chunks) { - buf_block_t* block = chunk->blocks; - ulint i = chunk->size; - - for (; i--; block++) { - dict_index_t* index = block->index; - assert_block_ahi_valid(block); - - /* We can set block->index = NULL - and block->n_pointers = 0 - when btr_search_own_all(RW_LOCK_X); - see the comments in buf0buf.h */ - - if (!index) { -# if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG - ut_a(!block->n_pointers); -# endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ - continue; - } - - ut_d(buf_page_state state - = buf_block_get_state(block)); - /* Another thread may have set the - state to BUF_BLOCK_REMOVE_HASH in - buf_LRU_block_remove_hashed(). - - The state change in buf_page_realloc() - is not observable here, because in - that case we would have !block->index. - - In the end, the entire adaptive hash - index will be removed. */ - ut_ad(state == BUF_BLOCK_FILE_PAGE - || state == BUF_BLOCK_REMOVE_HASH); -# if defined UNIV_AHI_DEBUG || defined UNIV_DEBUG - block->n_pointers = 0; -# endif /* UNIV_AHI_DEBUG || UNIV_DEBUG */ - block->index = NULL; - } - } - } -} -#endif /* BTR_CUR_HASH_ADAPT */ - /********************************************************************//** Relocate a buffer control block. Relocates the block on the LRU list and in buf_pool->page_hash. Does not relocate bpage->list. @@ -4207,7 +4147,7 @@ static buf_block_t* buf_page_mtr_lock(buf_block_t *block, { case RW_NO_LATCH: fix_type= MTR_MEMO_BUF_FIX; - break; + goto done; case RW_S_LATCH: rw_lock_s_lock_inline(&block->lock, 0, file, line); fix_type= MTR_MEMO_PAGE_S_FIX; @@ -4223,6 +4163,15 @@ static buf_block_t* buf_page_mtr_lock(buf_block_t *block, break; } +#ifdef BTR_CUR_HASH_ADAPT + { + dict_index_t *index= block->index; + if (index && index->freed()) + btr_search_drop_page_hash_index(block); + } +#endif /* BTR_CUR_HASH_ADAPT */ + +done: mtr_memo_push(mtr, block, fix_type); return block; } @@ -4540,6 +4489,7 @@ evict_from_pool: buf_pool_mutex_exit(buf_pool); return(NULL); } + break; case BUF_BLOCK_ZIP_PAGE: @@ -5087,9 +5037,11 @@ buf_page_get_known_nowait( buf_pool = buf_pool_from_block(block); +#ifdef BTR_CUR_HASH_ADAPT if (mode == BUF_MAKE_YOUNG) { buf_page_make_young_if_needed(&block->page); } +#endif /* BTR_CUR_HASH_ADAPT */ ut_ad(!ibuf_inside(mtr) || mode == BUF_KEEP_OLD); @@ -5132,9 +5084,12 @@ buf_page_get_known_nowait( deleting a record from SYS_INDEXES. This check will be skipped in recv_recover_page() as well. */ - buf_page_mutex_enter(block); - ut_a(!block->page.file_page_was_freed); - buf_page_mutex_exit(block); +# ifdef BTR_CUR_HASH_ADAPT + ut_ad(!block->page.file_page_was_freed + || (block->index && block->index->freed())); +# else /* BTR_CUR_HASH_ADAPT */ + ut_ad(!block->page.file_page_was_freed); +# endif /* BTR_CUR_HASH_ADAPT */ } #endif /* UNIV_DEBUG */ @@ -5627,6 +5582,12 @@ buf_page_create( rw_lock_x_unlock(hash_lock); buf_block_free(free_block); +#ifdef BTR_CUR_HASH_ADAPT + if (block->page.state == BUF_BLOCK_FILE_PAGE + && UNIV_LIKELY_NULL(block->index)) { + btr_search_drop_page_hash_index(block); + } +#endif /* BTR_CUR_HASH_ADAPT */ if (!recv_recovery_is_on()) { return buf_page_get_with_no_latch(page_id, page_size, diff --git a/storage/innobase/buf/buf0lru.cc b/storage/innobase/buf/buf0lru.cc index 9feebd9a08e..6e2dba77513 100644 --- a/storage/innobase/buf/buf0lru.cc +++ b/storage/innobase/buf/buf0lru.cc @@ -222,166 +222,6 @@ buf_LRU_evict_from_unzip_LRU( } #ifdef BTR_CUR_HASH_ADAPT -/** Attempts to drop page hash index on a batch of pages belonging to a -particular space id. -@param[in] space_id space id -@param[in] arr array of page_no -@param[in] count number of entries in array */ -static -void -buf_LRU_drop_page_hash_batch(ulint space_id, const ulint* arr, ulint count) -{ - ut_ad(count <= BUF_LRU_DROP_SEARCH_SIZE); - - for (const ulint* const end = arr + count; arr != end; ) { - /* While our only caller - buf_LRU_drop_page_hash_for_tablespace() - is being executed for DROP TABLE or similar, - the table cannot be evicted from the buffer pool. */ - btr_search_drop_page_hash_when_freed( - page_id_t(space_id, *arr++)); - } -} - -/******************************************************************//** -When doing a DROP TABLE/DISCARD TABLESPACE we have to drop all page -hash index entries belonging to that table. This function tries to -do that in batch. Note that this is a 'best effort' attempt and does -not guarantee that ALL hash entries will be removed. */ -static -void -buf_LRU_drop_page_hash_for_tablespace( -/*==================================*/ - buf_pool_t* buf_pool, /*!< in: buffer pool instance */ - ulint id) /*!< in: space id */ -{ - ulint* page_arr = static_cast(ut_malloc_nokey( - sizeof(ulint) * BUF_LRU_DROP_SEARCH_SIZE)); - - ulint num_entries = 0; - - buf_pool_mutex_enter(buf_pool); - -scan_again: - for (buf_page_t* bpage = UT_LIST_GET_LAST(buf_pool->LRU); - bpage != NULL; - /* No op */) { - - buf_page_t* prev_bpage = UT_LIST_GET_PREV(LRU, bpage); - - ut_a(buf_page_in_file(bpage)); - - if (buf_page_get_state(bpage) != BUF_BLOCK_FILE_PAGE - || bpage->id.space() != id - || bpage->io_fix != BUF_IO_NONE) { - /* Compressed pages are never hashed. - Skip blocks of other tablespaces. - Skip I/O-fixed blocks (to be dealt with later). */ -next_page: - bpage = prev_bpage; - continue; - } - - buf_block_t* block = reinterpret_cast(bpage); - - mutex_enter(&block->mutex); - - /* This debug check uses a dirty read that could - theoretically cause false positives while - buf_pool_clear_hash_index() is executing. - (Other conflicting access paths to the adaptive hash - index should not be possible, because when a - tablespace is being discarded or dropped, there must - be no concurrect access to the contained tables.) */ - assert_block_ahi_valid(block); - - bool skip = bpage->buf_fix_count > 0 || !block->index; - - mutex_exit(&block->mutex); - - if (skip) { - /* Skip this block, because there are - no adaptive hash index entries - pointing to it, or because we cannot - drop them due to the buffer-fix. */ - goto next_page; - } - - /* Store the page number so that we can drop the hash - index in a batch later. */ - page_arr[num_entries] = bpage->id.page_no(); - ut_a(num_entries < BUF_LRU_DROP_SEARCH_SIZE); - ++num_entries; - - if (num_entries < BUF_LRU_DROP_SEARCH_SIZE) { - goto next_page; - } - - /* Array full. We release the buf_pool->mutex to obey - the latching order. */ - buf_pool_mutex_exit(buf_pool); - - buf_LRU_drop_page_hash_batch(id, page_arr, num_entries); - - num_entries = 0; - - buf_pool_mutex_enter(buf_pool); - - /* Note that we released the buf_pool mutex above - after reading the prev_bpage during processing of a - page_hash_batch (i.e.: when the array was full). - Because prev_bpage could belong to a compressed-only - block, it may have been relocated, and thus the - pointer cannot be trusted. Because bpage is of type - buf_block_t, it is safe to dereference. - - bpage can change in the LRU list. This is OK because - this function is a 'best effort' to drop as many - search hash entries as possible and it does not - guarantee that ALL such entries will be dropped. */ - - /* If, however, bpage has been removed from LRU list - to the free list then we should restart the scan. - bpage->state is protected by buf_pool mutex. */ - if (bpage != NULL - && buf_page_get_state(bpage) != BUF_BLOCK_FILE_PAGE) { - - goto scan_again; - } - } - - buf_pool_mutex_exit(buf_pool); - - /* Drop any remaining batch of search hashed pages. */ - buf_LRU_drop_page_hash_batch(id, page_arr, num_entries); - ut_free(page_arr); -} - -/** Try to drop the adaptive hash index for a tablespace. -@param[in,out] table table -@return whether anything was dropped */ -bool buf_LRU_drop_page_hash_for_tablespace(dict_table_t* table) -{ - for (dict_index_t* index = dict_table_get_first_index(table); - index != NULL; - index = dict_table_get_next_index(index)) { - if (btr_search_info_get_ref_count(btr_search_get_info(index), - index)) { - goto drop_ahi; - } - } - - return false; -drop_ahi: - ulint id = table->space; - for (ulint i = 0; i < srv_buf_pool_instances; i++) { - buf_LRU_drop_page_hash_for_tablespace(buf_pool_from_array(i), - id); - } - - return true; -} - /******************************************************************//** While flushing (or removing dirty) pages from a tablespace we don't want to hog the CPU and resources. Release the buffer pool and block diff --git a/storage/innobase/dict/dict0crea.cc b/storage/innobase/dict/dict0crea.cc index 54d2eca245f..1a3d6c51d98 100644 --- a/storage/innobase/dict/dict0crea.cc +++ b/storage/innobase/dict/dict0crea.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -26,7 +26,9 @@ Created 1/8/1996 Heikki Tuuri #include "dict0crea.h" #include "btr0pcur.h" -#include "btr0btr.h" +#ifdef BTR_CUR_HASH_ADAPT +# include "btr0sea.h" +#endif /* BTR_CUR_HASH_ADAPT */ #include "page0page.h" #include "mach0data.h" #include "dict0boot.h" @@ -1528,6 +1530,9 @@ dict_create_index_step( &node->table->fts->cache->init_lock); } +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!node->index->search_info->ref_count); +#endif /* BTR_CUR_HASH_ADAPT */ dict_index_remove_from_cache(node->table, node->index); node->index = NULL; diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index 8f94ccb4c63..cff213dea3d 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -1361,25 +1361,12 @@ dict_table_can_be_evicted( } #ifdef BTR_CUR_HASH_ADAPT + /* We cannot really evict the table if adaptive hash + index entries are pointing to any of its indexes. */ for (dict_index_t* index = dict_table_get_first_index(table); index != NULL; index = dict_table_get_next_index(index)) { - - btr_search_t* info = btr_search_get_info(index); - - /* We are not allowed to free the in-memory index - struct dict_index_t until all entries in the adaptive - hash index that point to any of the page belonging to - his b-tree index are dropped. This is so because - dropping of these entries require access to - dict_index_t struct. To avoid such scenario we keep - a count of number of such pages in the search_info and - only free the dict_index_t struct when this count - drops to zero. - - See also: dict_index_remove_from_cache_low() */ - - if (btr_search_info_get_ref_count(info, index) > 0) { + if (index->n_ahi_pages()) { return(FALSE); } } @@ -1391,6 +1378,71 @@ dict_table_can_be_evicted( return(FALSE); } +#ifdef BTR_CUR_HASH_ADAPT +/** @return a clone of this */ +dict_index_t *dict_index_t::clone() const +{ + ut_ad(n_fields); + ut_ad(!(type & (DICT_IBUF | DICT_SPATIAL | DICT_FTS))); + ut_ad(online_status == ONLINE_INDEX_COMPLETE); + ut_ad(is_committed()); + ut_ad(!is_dummy); + ut_ad(!parser); + ut_ad(!index_fts_syncing); + ut_ad(!online_log); + ut_ad(!rtr_track); + + const size_t size= sizeof *this + n_fields * sizeof(*fields) + +#ifdef BTR_CUR_ADAPT + sizeof *search_info + +#endif + 1 + strlen(name) + + n_uniq * (sizeof *stat_n_diff_key_vals + + sizeof *stat_n_sample_sizes + + sizeof *stat_n_non_null_key_vals); + + mem_heap_t* heap= mem_heap_create(size); + dict_index_t *index= static_cast(mem_heap_dup(heap, this, + sizeof *this)); + *index= *this; + rw_lock_create(index_tree_rw_lock_key, &index->lock, SYNC_INDEX_TREE); + index->heap= heap; + index->name= mem_heap_strdup(heap, name); + index->fields= static_cast + (mem_heap_dup(heap, fields, n_fields * sizeof *fields)); +#ifdef BTR_CUR_ADAPT + index->search_info= btr_search_info_create(index->heap); +#endif /* BTR_CUR_ADAPT */ + index->stat_n_diff_key_vals= static_cast + (mem_heap_zalloc(heap, n_uniq * sizeof *stat_n_diff_key_vals)); + index->stat_n_sample_sizes= static_cast + (mem_heap_zalloc(heap, n_uniq * sizeof *stat_n_sample_sizes)); + index->stat_n_non_null_key_vals= static_cast + (mem_heap_zalloc(heap, n_uniq * sizeof *stat_n_non_null_key_vals)); + memset(&index->zip_pad, 0, sizeof index->zip_pad); + return index; +} + +/** Clone this index for lazy dropping of the adaptive hash. +@return this or a clone */ +dict_index_t *dict_index_t::clone_if_needed() +{ + if (!search_info->ref_count) + return this; + dict_index_t *prev= UT_LIST_GET_PREV(indexes, this); + + UT_LIST_REMOVE(table->indexes, this); + UT_LIST_ADD_LAST(table->freed_indexes, this); + dict_index_t *index= clone(); + set_freed(); + if (prev) + UT_LIST_INSERT_AFTER(table->indexes, prev, index); + else + UT_LIST_ADD_FIRST(table->indexes, index); + return index; +} +#endif /* BTR_CUR_HASH_ADAPT */ + /**********************************************************************//** Make room in the table cache by evicting an unused table. The unused table should not be part of FK relationship and currently not used in any user @@ -2081,6 +2133,14 @@ dict_table_remove_from_cache_low( UT_DELETE(table->vc_templ); } +#ifdef BTR_CUR_HASH_ADAPT + if (UNIV_UNLIKELY(UT_LIST_GET_LEN(table->freed_indexes) != 0)) { + table->vc_templ = NULL; + table->id = 0; + return; + } +#endif /* BTR_CUR_HASH_ADAPT */ + dict_mem_table_free(table); } @@ -2310,6 +2370,8 @@ dict_index_remove_from_cache_low( ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); ut_ad(mutex_own(&dict_sys->mutex)); + ut_ad(table->id); + ut_ad(!index->freed()); /* No need to acquire the dict_index_t::lock here because there can't be any active operations on this index (or table). */ @@ -2319,13 +2381,22 @@ dict_index_remove_from_cache_low( row_log_free(index->online_log); } + /* Remove the index from the list of indexes of the table */ + UT_LIST_REMOVE(table->indexes, index); + + /* The index is being dropped, remove any compression stats for it. */ + if (!lru_evict && DICT_TF_GET_ZIP_SSIZE(index->table->flags)) { + mutex_enter(&page_zip_stat_per_index_mutex); + page_zip_stat_per_index.erase(index->id); + mutex_exit(&page_zip_stat_per_index_mutex); + } + + /* Remove the index from affected virtual column index list */ + index->detach_columns(); + #ifdef BTR_CUR_HASH_ADAPT /* We always create search info whether or not adaptive hash index is enabled or not. */ - btr_search_t* info = btr_search_get_info(index); - ulint retries = 0; - ut_ad(info); - /* We are not allowed to free the in-memory index struct dict_index_t until all entries in the adaptive hash index that point to any of the page belonging to his b-tree index @@ -2335,31 +2406,15 @@ dict_index_remove_from_cache_low( only free the dict_index_t struct when this count drops to zero. See also: dict_table_can_be_evicted() */ - do { - if (!btr_search_info_get_ref_count(info, index) - || !buf_LRU_drop_page_hash_for_tablespace(table)) { - break; - } - - ut_a(++retries < 10000); - } while (srv_shutdown_state == SRV_SHUTDOWN_NONE || !lru_evict); + if (index->n_ahi_pages()) { + index->set_freed(); + UT_LIST_ADD_LAST(table->freed_indexes, index); + return; + } #endif /* BTR_CUR_HASH_ADAPT */ rw_lock_free(&index->lock); - /* The index is being dropped, remove any compression stats for it. */ - if (!lru_evict && DICT_TF_GET_ZIP_SSIZE(index->table->flags)) { - mutex_enter(&page_zip_stat_per_index_mutex); - page_zip_stat_per_index.erase(index->id); - mutex_exit(&page_zip_stat_per_index_mutex); - } - - /* Remove the index from the list of indexes of the table */ - UT_LIST_REMOVE(table->indexes, index); - - /* Remove the index from affected virtual column index list */ - index->detach_columns(); - dict_mem_index_free(index); } @@ -5698,7 +5753,7 @@ dict_set_corrupted( btr_cur_search_to_nth_level(sys_index, 0, tuple, PAGE_CUR_LE, BTR_MODIFY_LEAF, - &cursor, 0, __FILE__, __LINE__, &mtr); + &cursor, 0, __FILE__, __LINE__, &mtr, 0); if (cursor.low_match == dtuple_get_n_fields(tuple)) { /* UPDATE SYS_INDEXES SET TYPE=index->type @@ -5800,7 +5855,7 @@ dict_index_set_merge_threshold( btr_cur_search_to_nth_level(sys_index, 0, tuple, PAGE_CUR_GE, BTR_MODIFY_LEAF, - &cursor, 0, __FILE__, __LINE__, &mtr); + &cursor, 0, __FILE__, __LINE__, &mtr, 0); if (cursor.up_match == dtuple_get_n_fields(tuple) && rec_get_n_fields_old(btr_cur_get_rec(&cursor)) diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc index 21a3c743f52..8327318e46c 100644 --- a/storage/innobase/dict/dict0mem.cc +++ b/storage/innobase/dict/dict0mem.cc @@ -126,6 +126,9 @@ dict_mem_table_create( lock_table_lock_list_init(&table->locks); UT_LIST_INIT(table->indexes, &dict_index_t::indexes); +#ifdef BTR_CUR_HASH_ADAPT + UT_LIST_INIT(table->freed_indexes, &dict_index_t::indexes); +#endif /* BTR_CUR_HASH_ADAPT */ table->heap = heap; @@ -181,6 +184,10 @@ dict_mem_table_free( { ut_ad(table); ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); + ut_ad(UT_LIST_GET_LEN(table->indexes) == 0); +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(UT_LIST_GET_LEN(table->freed_indexes) == 0); +#endif /* BTR_CUR_HASH_ADAPT */ ut_d(table->cached = FALSE); if (dict_table_has_fts_index(table) diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc index dab30a218e6..0da77dfdff7 100644 --- a/storage/innobase/dict/dict0stats.cc +++ b/storage/innobase/dict/dict0stats.cc @@ -424,6 +424,9 @@ dict_stats_table_clone_create( dict_table_stats_latch_create(t, false); UT_LIST_INIT(t->indexes, &dict_index_t::indexes); +#ifdef BTR_CUR_HASH_ADAPT + UT_LIST_INIT(t->freed_indexes, &dict_index_t::indexes); +#endif /* BTR_CUR_HASH_ADAPT */ for (index = dict_table_get_first_index(table); index != NULL; @@ -4005,6 +4008,9 @@ test_dict_stats_save() table.stat_clustered_index_size = TEST_CLUSTERED_INDEX_SIZE; table.stat_sum_of_other_index_sizes = TEST_SUM_OF_OTHER_INDEX_SIZES; UT_LIST_INIT(table.indexes, &dict_index_t::indexes); +#ifdef BTR_CUR_HASH_ADAPT + UT_LIST_INIT(table.freed_indexes, &dict_index_t::indexes); +#endif /* BTR_CUR_HASH_ADAPT */ UT_LIST_ADD_LAST(table.indexes, &index1); UT_LIST_ADD_LAST(table.indexes, &index2); ut_d(table.magic_n = DICT_TABLE_MAGIC_N); @@ -4154,6 +4160,9 @@ test_dict_stats_fetch_from_ps() /* craft a dummy dict_table_t */ table.name.m_name = (char*) (TEST_DATABASE_NAME "/" TEST_TABLE_NAME); UT_LIST_INIT(table.indexes, &dict_index_t::indexes); +#ifdef BTR_CUR_HASH_ADAPT + UT_LIST_INIT(table.freed_indexes, &dict_index_t::indexes); +#endif /* BTR_CUR_HASH_ADAPT */ UT_LIST_ADD_LAST(table.indexes, &index1); UT_LIST_ADD_LAST(table.indexes, &index2); ut_d(table.magic_n = DICT_TABLE_MAGIC_N); diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc index d308ff9de71..974a514e637 100644 --- a/storage/innobase/fsp/fsp0fsp.cc +++ b/storage/innobase/fsp/fsp0fsp.cc @@ -2976,8 +2976,6 @@ fseg_mark_page_used( @param[in,out] space tablespace @param[in] offset page number @param[in] page_size page size -@param[in] ahi whether we may need to drop the adaptive -hash index @param[in,out] mtr mini-transaction */ static void @@ -2986,9 +2984,6 @@ fseg_free_page_low( fil_space_t* space, page_no_t offset, const page_size_t& page_size, -#ifdef BTR_CUR_HASH_ADAPT - bool ahi, -#endif /* BTR_CUR_HASH_ADAPT */ mtr_t* mtr) { xdes_t* descr; @@ -3003,15 +2998,6 @@ fseg_free_page_low( == FSEG_MAGIC_N_VALUE); ut_ad(!((page_offset(seg_inode) - FSEG_ARR_OFFSET) % FSEG_INODE_SIZE)); ut_d(space->modify_check(*mtr)); -#ifdef BTR_CUR_HASH_ADAPT - /* Drop search system page hash index if the page is found in - the pool and is hashed */ - - if (ahi) { - btr_search_drop_page_hash_when_freed( - page_id_t(space->id, offset)); - } -#endif /* BTR_CUR_HASH_ADAPT */ descr = xdes_get_descriptor(space, offset, page_size, mtr); @@ -3097,22 +3083,13 @@ fseg_free_page_low( } } -#ifndef BTR_CUR_HASH_ADAPT -# define fseg_free_page_low(inode, space, offset, page_size, ahi, mtr) \ - fseg_free_page_low(inode, space, offset, page_size, mtr) -#endif /* !BTR_CUR_HASH_ADAPT */ - /**********************************************************************//** Frees a single page of a segment. */ void -fseg_free_page_func( +fseg_free_page( fseg_header_t* seg_header, /*!< in: segment header */ ulint space_id,/*!< in: space id */ ulint page, /*!< in: page offset */ -#ifdef BTR_CUR_HASH_ADAPT - bool ahi, /*!< in: whether we may need to drop - the adaptive hash index */ -#endif /* BTR_CUR_HASH_ADAPT */ mtr_t* mtr) /*!< in/out: mini-transaction */ { DBUG_ENTER("fseg_free_page"); @@ -3128,7 +3105,7 @@ fseg_free_page_func( &iblock); fil_block_check_type(*iblock, FIL_PAGE_INODE, mtr); - fseg_free_page_low(seg_inode, space, page, page_size, ahi, mtr); + fseg_free_page_low(seg_inode, space, page, page_size, mtr); ut_d(buf_page_set_file_page_was_freed(page_id_t(space_id, page))); @@ -3169,8 +3146,6 @@ fseg_page_is_free(fil_space_t* space, unsigned page) @param[in,out] space tablespace @param[in] page_size page size @param[in] page page number in the extent -@param[in] ahi whether we may need to drop - the adaptive hash index @param[in,out] mtr mini-transaction */ MY_ATTRIBUTE((nonnull)) static @@ -3180,9 +3155,6 @@ fseg_free_extent( fil_space_t* space, const page_size_t& page_size, ulint page, -#ifdef BTR_CUR_HASH_ADAPT - bool ahi, -#endif /* BTR_CUR_HASH_ADAPT */ mtr_t* mtr) { xdes_t* descr; @@ -3203,23 +3175,6 @@ fseg_free_extent( const ulint first_page_in_extent = page - (page % FSP_EXTENT_SIZE); #endif /* BTR_CUR_HASH_ADAPT || UNIV_DEBUG */ -#ifdef BTR_CUR_HASH_ADAPT - if (ahi) { - for (ulint i = 0; i < FSP_EXTENT_SIZE; i++) { - if (!xdes_mtr_get_bit(descr, XDES_FREE_BIT, i, mtr)) { - - /* Drop search system page hash index - if the page is found in the pool and - is hashed */ - - btr_search_drop_page_hash_when_freed( - page_id_t(space->id, - first_page_in_extent + i)); - } - } - } -#endif /* BTR_CUR_HASH_ADAPT */ - if (xdes_is_full(descr, mtr)) { flst_remove(seg_inode + FSEG_FULL, descr + XDES_FLST_NODE, mtr); @@ -3251,27 +3206,18 @@ fseg_free_extent( #endif /* UNIV_DEBUG */ } -#ifndef BTR_CUR_HASH_ADAPT -# define fseg_free_extent(inode, space, page_size, page, ahi, mtr) \ - fseg_free_extent(inode, space, page_size, page, mtr) -#endif /* !BTR_CUR_HASH_ADAPT */ - /**********************************************************************//** Frees part of a segment. This function can be used to free a segment by repeatedly calling this function in different mini-transactions. Doing the freeing in a single mini-transaction might result in too big a mini-transaction. -@return TRUE if freeing completed */ -ibool -fseg_free_step_func( +@return whether the freeing was completed */ +bool +fseg_free_step( fseg_header_t* header, /*!< in, own: segment header; NOTE: if the header resides on the first page of the frag list of the segment, this pointer becomes obsolete after the last freeing step */ -#ifdef BTR_CUR_HASH_ADAPT - bool ahi, /*!< in: whether we may need to drop - the adaptive hash index */ -#endif /* BTR_CUR_HASH_ADAPT */ mtr_t* mtr) /*!< in/out: mini-transaction */ { ulint n; @@ -3303,7 +3249,7 @@ fseg_free_step_func( if (inode == NULL) { ib::info() << "Double free of inode from " << page_id_t(space_id, header_page); - DBUG_RETURN(TRUE); + DBUG_RETURN(true); } fil_block_check_type(*iblock, FIL_PAGE_INODE, mtr); @@ -3313,9 +3259,9 @@ fseg_free_step_func( /* Free the extent held by the segment */ page = xdes_get_offset(descr); - fseg_free_extent(inode, space, page_size, page, ahi, mtr); + fseg_free_extent(inode, space, page_size, page, mtr); - DBUG_RETURN(FALSE); + DBUG_RETURN(false); } /* Free a frag page */ @@ -3325,13 +3271,13 @@ fseg_free_step_func( /* Freeing completed: free the segment inode */ fsp_free_seg_inode(space, page_size, inode, mtr); - DBUG_RETURN(TRUE); + DBUG_RETURN(true); } fseg_free_page_low( inode, space, fseg_get_nth_frag_page_no(inode, n, mtr), - page_size, ahi, mtr); + page_size, mtr); n = fseg_find_last_used_frag_page_slot(inode, mtr); @@ -3339,24 +3285,20 @@ fseg_free_step_func( /* Freeing completed: free the segment inode */ fsp_free_seg_inode(space, page_size, inode, mtr); - DBUG_RETURN(TRUE); + DBUG_RETURN(true); } - DBUG_RETURN(FALSE); + DBUG_RETURN(false); } /**********************************************************************//** Frees part of a segment. Differs from fseg_free_step because this function leaves the header page unfreed. -@return TRUE if freeing completed, except the header page */ -ibool -fseg_free_step_not_header_func( +@return whether the freeing was completed, except for the header page */ +bool +fseg_free_step_not_header( fseg_header_t* header, /*!< in: segment header which must reside on the first fragment page of the segment */ -#ifdef BTR_CUR_HASH_ADAPT - bool ahi, /*!< in: whether we may need to drop - the adaptive hash index */ -#endif /* BTR_CUR_HASH_ADAPT */ mtr_t* mtr) /*!< in/out: mini-transaction */ { ulint n; @@ -3382,29 +3324,27 @@ fseg_free_step_not_header_func( /* Free the extent held by the segment */ page = xdes_get_offset(descr); - fseg_free_extent(inode, space, page_size, page, ahi, mtr); + fseg_free_extent(inode, space, page_size, page, mtr); - return(FALSE); + return false; } /* Free a frag page */ n = fseg_find_last_used_frag_page_slot(inode, mtr); - if (n == ULINT_UNDEFINED) { - ut_error; - } + ut_a(n != ULINT_UNDEFINED); page_no = fseg_get_nth_frag_page_no(inode, n, mtr); if (page_no == page_get_page_no(page_align(header))) { - return(TRUE); + return true; } - fseg_free_page_low(inode, space, page_no, page_size, ahi, mtr); + fseg_free_page_low(inode, space, page_no, page_size, mtr); - return(FALSE); + return false; } /** Returns the first extent descriptor for a segment. diff --git a/storage/innobase/gis/gis0sea.cc b/storage/innobase/gis/gis0sea.cc index 3631ff35874..c372a3f1cd4 100644 --- a/storage/innobase/gis/gis0sea.cc +++ b/storage/innobase/gis/gis0sea.cc @@ -590,7 +590,7 @@ rtr_pcur_open_low( } btr_cur_search_to_nth_level(index, level, tuple, mode, latch_mode, - btr_cursor, 0, file, line, mtr); + btr_cursor, 0, file, line, mtr, 0); cursor->pos_state = BTR_PCUR_IS_POSITIONED; cursor->trx_if_known = NULL; @@ -759,7 +759,7 @@ static void rtr_get_father_node( btr_cur_search_to_nth_level( index, level, tuple, PAGE_CUR_RTREE_LOCATE, BTR_CONT_MODIFY_TREE, btr_cur, 0, - __FILE__, __LINE__, mtr); + __FILE__, __LINE__, mtr, 0); } else { /* btr_validate */ @@ -769,7 +769,7 @@ static void rtr_get_father_node( btr_cur_search_to_nth_level( index, level, tuple, PAGE_CUR_RTREE_LOCATE, BTR_CONT_MODIFY_TREE, btr_cur, 0, - __FILE__, __LINE__, mtr); + __FILE__, __LINE__, mtr, 0); rec = btr_cur_get_rec(btr_cur); n_fields = dtuple_get_n_fields_cmp(tuple); diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index edc7478f7de..9b1d99d3730 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -254,6 +254,7 @@ struct ha_innobase_inplace_ctx : public inplace_alter_handler_ctx for (ulint i = 0; i < num_to_add_index; i++) { if (!add_index[i]->is_committed()) { add_index[i]->detach_columns(); + add_index[i]->n_fields = 0; } } } @@ -8689,21 +8690,14 @@ foreign_fail: } if (ctx0->num_to_drop_vcol || ctx0->num_to_add_vcol) { + /* FIXME: this workaround does not seem to work with + partitioned tables */ DBUG_ASSERT(ctx0->old_table->get_ref_count() == 1); trx_commit_for_mysql(m_prebuilt->trx); -#ifdef BTR_CUR_HASH_ADAPT - if (btr_search_enabled) { - btr_search_disable(false); - btr_search_enable(); - } -#endif /* BTR_CUR_HASH_ADAPT */ - - char tb_name[FN_REFLEN]; - ut_strcpy(tb_name, m_prebuilt->table->name.m_name); - - tb_name[strlen(m_prebuilt->table->name.m_name)] = 0; + char tb_name[NAME_LEN * 2 + 1 + 1]; + strcpy(tb_name, m_prebuilt->table->name.m_name); dict_table_close(m_prebuilt->table, true, false); dict_table_remove_from_cache(m_prebuilt->table); m_prebuilt->table = dict_table_open_on_name( diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc index e02958b4ac3..1ee1624ca8b 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.cc +++ b/storage/innobase/ibuf/ibuf0ibuf.cc @@ -2109,7 +2109,7 @@ ibuf_remove_free_page(void) page from it. */ fseg_free_page(header_page + IBUF_HEADER + IBUF_TREE_SEG_HEADER, - IBUF_SPACE_ID, page_no, false, &mtr); + IBUF_SPACE_ID, page_no, &mtr); const page_id_t page_id(IBUF_SPACE_ID, page_no); diff --git a/storage/innobase/include/btr0cur.h b/storage/innobase/include/btr0cur.h index 19d0fb397be..f70fe687182 100644 --- a/storage/innobase/include/btr0cur.h +++ b/storage/innobase/include/btr0cur.h @@ -154,8 +154,7 @@ Note that if mode is PAGE_CUR_LE, which is used in inserts, then cursor->up_match and cursor->low_match both will have sensible values. If mode is PAGE_CUR_GE, then up_match will a have a sensible value. */ dberr_t -btr_cur_search_to_nth_level( -/*========================*/ +btr_cur_search_to_nth_level_func( dict_index_t* index, /*!< in: index */ ulint level, /*!< in: the tree level of search */ const dtuple_t* tuple, /*!< in: data tuple; NOTE: n_fields_cmp in @@ -181,16 +180,24 @@ btr_cur_search_to_nth_level( to protect the record! */ btr_cur_t* cursor, /*!< in/out: tree cursor; the cursor page is s- or x-latched, but see also above! */ +#ifdef BTR_CUR_HASH_ADAPT ulint has_search_latch, /*!< in: latch mode the caller currently has on search system: RW_S_LATCH, or 0 */ +#endif /* BTR_CUR_HASH_ADAPT */ const char* file, /*!< in: file name */ unsigned line, /*!< in: line where called */ mtr_t* mtr, /*!< in/out: mini-transaction */ - ib_uint64_t autoinc = 0); + ib_uint64_t autoinc); /*!< in: PAGE_ROOT_AUTO_INC to be written (0 if none) */ +#ifdef BTR_CUR_HASH_ADAPT +# define btr_cur_search_to_nth_level btr_cur_search_to_nth_level_func +#else /* BTR_CUR_HASH_ADAPT */ +# define btr_cur_search_to_nth_level(ix,lv,t,md,l,cur,has,file,line,m,ai) \ + btr_cur_search_to_nth_level_func(ix,lv,t,md,l,cur,file,line,m,ai) +#endif /* BTR_CUR_HASH_ADAPT */ /*****************************************************************//** Opens a cursor at either end of an index. diff --git a/storage/innobase/include/btr0pcur.h b/storage/innobase/include/btr0pcur.h index b79260e5ab6..26c7ebcee6d 100644 --- a/storage/innobase/include/btr0pcur.h +++ b/storage/innobase/include/btr0pcur.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -136,15 +136,22 @@ btr_pcur_open_with_no_init_func( page, but assume that the caller uses his btr search latch to protect the record! */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ +#ifdef BTR_CUR_HASH_ADAPT ulint has_search_latch, /*!< in: latch mode the caller currently has on search system: RW_S_LATCH, or 0 */ +#endif /* BTR_CUR_HASH_ADAPT */ const char* file, /*!< in: file name */ unsigned line, /*!< in: line where called */ mtr_t* mtr); /*!< in: mtr */ -#define btr_pcur_open_with_no_init(ix,t,md,l,cur,has,m) \ +#ifdef BTR_CUR_HASH_ADAPT +# define btr_pcur_open_with_no_init(ix,t,md,l,cur,has,m) \ btr_pcur_open_with_no_init_func(ix,t,md,l,cur,has,__FILE__,__LINE__,m) +#else /* BTR_CUR_HASH_ADAPT */ +# define btr_pcur_open_with_no_init(ix,t,md,l,cur,has,m) \ + btr_pcur_open_with_no_init_func(ix,t,md,l,cur,__FILE__,__LINE__,m) +#endif /* BTR_CUR_HASH_ADAPT */ /*****************************************************************//** Opens a persistent cursor at either end of an index. */ diff --git a/storage/innobase/include/btr0pcur.ic b/storage/innobase/include/btr0pcur.ic index 8b0da666250..b5b57050832 100644 --- a/storage/innobase/include/btr0pcur.ic +++ b/storage/innobase/include/btr0pcur.ic @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2015, 2019, MariaDB Corporation. +Copyright (c) 2015, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -496,10 +496,12 @@ btr_pcur_open_with_no_init_func( page, but assume that the caller uses his btr search latch to protect the record! */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ +#ifdef BTR_CUR_HASH_ADAPT ulint has_search_latch, /*!< in: latch mode the caller currently has on search system: RW_S_LATCH, or 0 */ +#endif /* BTR_CUR_HASH_ADAPT */ const char* file, /*!< in: file name */ unsigned line, /*!< in: line where called */ mtr_t* mtr) /*!< in: mtr */ @@ -516,7 +518,7 @@ btr_pcur_open_with_no_init_func( err = btr_cur_search_to_nth_level( index, 0, tuple, mode, latch_mode, btr_cursor, - has_search_latch, file, line, mtr); + has_search_latch, file, line, mtr, 0); cursor->pos_state = BTR_PCUR_IS_POSITIONED; diff --git a/storage/innobase/include/btr0sea.h b/storage/innobase/include/btr0sea.h index 645b3689ff6..b808ba036db 100644 --- a/storage/innobase/include/btr0sea.h +++ b/storage/innobase/include/btr0sea.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2018, MariaDB Corporation. +Copyright (c) 2018, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -54,15 +54,6 @@ btr_search_disable( void btr_search_enable(); -/** Returns the value of ref_count. The value is protected by latch. -@param[in] info search info -@param[in] index index identifier -@return ref_count value. */ -ulint -btr_search_info_get_ref_count( - btr_search_t* info, - dict_index_t* index); - /*********************************************************************//** Updates the search info. */ UNIV_INLINE @@ -156,18 +147,6 @@ btr_search_update_hash_on_delete(btr_cur_t* cursor); bool btr_search_validate(); -/** X-Lock the search latch (corresponding to given index) -@param[in] index index handler */ -UNIV_INLINE -void -btr_search_x_lock(const dict_index_t* index); - -/** X-Unlock the search latch (corresponding to given index) -@param[in] index index handler */ -UNIV_INLINE -void -btr_search_x_unlock(const dict_index_t* index); - /** Lock all search latches in exclusive mode. */ UNIV_INLINE void @@ -178,18 +157,6 @@ UNIV_INLINE void btr_search_x_unlock_all(); -/** S-Lock the search latch (corresponding to given index) -@param[in] index index handler */ -UNIV_INLINE -void -btr_search_s_lock(const dict_index_t* index); - -/** S-Unlock the search latch (corresponding to given index) -@param[in] index index handler */ -UNIV_INLINE -void -btr_search_s_unlock(const dict_index_t* index); - /** Lock all search latches in shared mode. */ UNIV_INLINE void @@ -236,12 +203,8 @@ btr_get_search_table(const dict_index_t* index); #else /* BTR_CUR_HASH_ADAPT */ # define btr_search_sys_create(size) # define btr_search_drop_page_hash_index(block) -# define btr_search_s_lock(index) -# define btr_search_s_unlock(index) # define btr_search_s_lock_all(index) # define btr_search_s_unlock_all(index) -# define btr_search_x_lock(index) -# define btr_search_x_unlock(index) # define btr_search_info_update(index, cursor) # define btr_search_move_or_delete_hash_entries(new_block, block, index) # define btr_search_update_hash_on_insert(cursor) @@ -327,6 +290,18 @@ struct btr_search_t{ }; #ifdef BTR_CUR_HASH_ADAPT +/** @return number of leaf pages pointed to by the adaptive hash index */ +inline ulint dict_index_t::n_ahi_pages() const +{ + if (!btr_search_enabled) + return 0; + rw_lock_t *latch = btr_get_search_latch(this); + rw_lock_s_lock(latch); + ulint ref_count= search_info->ref_count; + rw_lock_s_unlock(latch); + return ref_count; +} + /** The hash index system */ struct btr_search_sys_t{ hash_table_t** hash_tables; /*!< the adaptive hash tables, diff --git a/storage/innobase/include/btr0sea.ic b/storage/innobase/include/btr0sea.ic index 4972de16064..90877d23192 100644 --- a/storage/innobase/include/btr0sea.ic +++ b/storage/innobase/include/btr0sea.ic @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -62,8 +63,8 @@ btr_search_info_update( dict_index_t* index, /*!< in: index of the cursor */ btr_cur_t* cursor) /*!< in: cursor which was just positioned */ { - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_S)); - ut_ad(!rw_lock_own(btr_get_search_latch(index), RW_LOCK_X)); + ut_ad(!rw_lock_own_flagged(btr_get_search_latch(index), + RW_LOCK_FLAG_X | RW_LOCK_FLAG_S)); if (dict_index_is_spatial(index) || !btr_search_enabled) { return; @@ -87,24 +88,6 @@ btr_search_info_update( btr_search_info_update_slow(info, cursor); } -/** X-Lock the search latch (corresponding to given index) -@param[in] index index handler */ -UNIV_INLINE -void -btr_search_x_lock(const dict_index_t* index) -{ - rw_lock_x_lock(btr_get_search_latch(index)); -} - -/** X-Unlock the search latch (corresponding to given index) -@param[in] index index handler */ -UNIV_INLINE -void -btr_search_x_unlock(const dict_index_t* index) -{ - rw_lock_x_unlock(btr_get_search_latch(index)); -} - /** Lock all search latches in exclusive mode. */ UNIV_INLINE void @@ -125,24 +108,6 @@ btr_search_x_unlock_all() } } -/** S-Lock the search latch (corresponding to given index) -@param[in] index index handler */ -UNIV_INLINE -void -btr_search_s_lock(const dict_index_t* index) -{ - rw_lock_s_lock(btr_get_search_latch(index)); -} - -/** S-Unlock the search latch (corresponding to given index) -@param[in] index index handler */ -UNIV_INLINE -void -btr_search_s_unlock(const dict_index_t* index) -{ - rw_lock_s_unlock(btr_get_search_latch(index)); -} - /** Lock all search latches in shared mode. */ UNIV_INLINE void diff --git a/storage/innobase/include/buf0buf.h b/storage/innobase/include/buf0buf.h index 0536e2f8ac6..a9b8ef94bab 100644 --- a/storage/innobase/include/buf0buf.h +++ b/storage/innobase/include/buf0buf.h @@ -71,11 +71,13 @@ struct fil_addr_t; /* @} */ /** @name Modes for buf_page_get_known_nowait */ /* @{ */ -#define BUF_MAKE_YOUNG 51 /*!< Move the block to the +#ifdef BTR_CUR_HASH_ADAPT +# define BUF_MAKE_YOUNG 51 /*!< Move the block to the start of the LRU list if there is a danger that the block would drift out of the buffer pool*/ +#endif /* BTR_CUR_HASH_ADAPT */ #define BUF_KEEP_OLD 52 /*!< Preserve the current LRU position of the block. */ /* @} */ @@ -282,12 +284,6 @@ extern "C" os_thread_ret_t DECLARE_THREAD(buf_resize_thread)(void*); -#ifdef BTR_CUR_HASH_ADAPT -/** Clear the adaptive hash index on all pages in the buffer pool. */ -void -buf_pool_clear_hash_index(); -#endif /* BTR_CUR_HASH_ADAPT */ - /*********************************************************************//** Gets the current size of buffer buf_pool in bytes. @return size in bytes */ diff --git a/storage/innobase/include/buf0lru.h b/storage/innobase/include/buf0lru.h index 1efbb1f03ef..a72d98395af 100644 --- a/storage/innobase/include/buf0lru.h +++ b/storage/innobase/include/buf0lru.h @@ -49,17 +49,6 @@ These are low-level functions /** Minimum LRU list length for which the LRU_old pointer is defined */ #define BUF_LRU_OLD_MIN_LEN 512 /* 8 megabytes of 16k pages */ -#ifdef BTR_CUR_HASH_ADAPT -struct dict_table_t; -/** Try to drop the adaptive hash index for a tablespace. -@param[in,out] table table -@return whether anything was dropped */ -bool buf_LRU_drop_page_hash_for_tablespace(dict_table_t* table) - MY_ATTRIBUTE((warn_unused_result,nonnull)); -#else -# define buf_LRU_drop_page_hash_for_tablespace(table) -#endif /* BTR_CUR_HASH_ADAPT */ - /** Empty the flush list for all pages belonging to a tablespace. @param[in] id tablespace identifier @param[in,out] observer flush observer, diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index b259d2fb2ad..80d18dcbb2a 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -831,6 +831,9 @@ struct dict_index_t{ dict_table_t* table; /*!< back pointer to table */ unsigned space:32; /*!< space where the index tree is placed */ + /** root page number, or FIL_NULL if the index has been detached + from storage (DISCARD TABLESPACE or similar), + or 1 if the index is in table->freed_indexes */ unsigned page:32;/*!< index tree root page number */ unsigned merge_threshold:6; /*!< In the pessimistic delete, if the page @@ -1021,11 +1024,23 @@ struct dict_index_t{ for (unsigned i = 0; i < n_fields; i++) { fields[i].col->detach(*this); } - - n_fields = 0; } } +#ifdef BTR_CUR_HASH_ADAPT + /** @return a clone of this */ + dict_index_t* clone() const; + /** Clone this index for lazy dropping of the adaptive hash index. + @return this or a clone */ + dict_index_t* clone_if_needed(); + /** @return number of leaf pages pointed to by the adaptive hash index */ + inline ulint n_ahi_pages() const; + /** @return whether mark_freed() had been invoked */ + bool freed() const { return UNIV_UNLIKELY(page == 1); } + /** Note that the index is waiting for btr_search_lazy_free() */ + void set_freed() { ut_ad(!freed()); page= 1; } +#endif /* BTR_CUR_HASH_ADAPT */ + /** This ad-hoc class is used by record_size_info only. */ class record_size_info_t { public: @@ -1605,6 +1620,11 @@ struct dict_table_t { /** List of indexes of the table. */ UT_LIST_BASE_NODE_T(dict_index_t) indexes; +#ifdef BTR_CUR_HASH_ADAPT + /** List of detached indexes that are waiting to be freed along with + the last adaptive hash index entry */ + UT_LIST_BASE_NODE_T(dict_index_t) freed_indexes; +#endif /* BTR_CUR_HASH_ADAPT */ /** List of foreign key constraints in the table. These refer to columns in other tables. */ diff --git a/storage/innobase/include/fsp0fsp.h b/storage/innobase/include/fsp0fsp.h index a4e3b84b55e..8debdb00013 100644 --- a/storage/innobase/include/fsp0fsp.h +++ b/storage/innobase/include/fsp0fsp.h @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1995, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2013, 2019, MariaDB Corporation. +Copyright (c) 2013, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -568,22 +568,11 @@ fsp_get_available_space_in_free_extents( /**********************************************************************//** Frees a single page of a segment. */ void -fseg_free_page_func( +fseg_free_page( fseg_header_t* seg_header, /*!< in: segment header */ ulint space_id, /*!< in: space id */ ulint page, /*!< in: page offset */ -#ifdef BTR_CUR_HASH_ADAPT - bool ahi, /*!< in: whether we may need to drop - the adaptive hash index */ -#endif /* BTR_CUR_HASH_ADAPT */ mtr_t* mtr); /*!< in/out: mini-transaction */ -#ifdef BTR_CUR_HASH_ADAPT -# define fseg_free_page(header, space_id, page, ahi, mtr) \ - fseg_free_page_func(header, space_id, page, ahi, mtr) -#else /* BTR_CUR_HASH_ADAPT */ -# define fseg_free_page(header, space_id, page, ahi, mtr) \ - fseg_free_page_func(header, space_id, page, mtr) -#endif /* BTR_CUR_HASH_ADAPT */ /** Determine whether a page is free. @param[in,out] space tablespace @param[in] page page number @@ -596,45 +585,25 @@ Frees part of a segment. This function can be used to free a segment by repeatedly calling this function in different mini-transactions. Doing the freeing in a single mini-transaction might result in too big a mini-transaction. -@return TRUE if freeing completed */ -ibool -fseg_free_step_func( +@return whether the freeing was completed */ +bool +fseg_free_step( fseg_header_t* header, /*!< in, own: segment header; NOTE: if the header resides on the first page of the frag list of the segment, this pointer becomes obsolete after the last freeing step */ -#ifdef BTR_CUR_HASH_ADAPT - bool ahi, /*!< in: whether we may need to drop - the adaptive hash index */ -#endif /* BTR_CUR_HASH_ADAPT */ mtr_t* mtr) /*!< in/out: mini-transaction */ MY_ATTRIBUTE((warn_unused_result)); -#ifdef BTR_CUR_HASH_ADAPT -# define fseg_free_step(header, ahi, mtr) fseg_free_step_func(header, ahi, mtr) -#else /* BTR_CUR_HASH_ADAPT */ -# define fseg_free_step(header, ahi, mtr) fseg_free_step_func(header, mtr) -#endif /* BTR_CUR_HASH_ADAPT */ /**********************************************************************//** Frees part of a segment. Differs from fseg_free_step because this function leaves the header page unfreed. -@return TRUE if freeing completed, except the header page */ -ibool -fseg_free_step_not_header_func( +@return whether the freeing was completed, except for the header page */ +bool +fseg_free_step_not_header( fseg_header_t* header, /*!< in: segment header which must reside on the first fragment page of the segment */ -#ifdef BTR_CUR_HASH_ADAPT - bool ahi, /*!< in: whether we may need to drop - the adaptive hash index */ -#endif /* BTR_CUR_HASH_ADAPT */ mtr_t* mtr) /*!< in/out: mini-transaction */ MY_ATTRIBUTE((warn_unused_result)); -#ifdef BTR_CUR_HASH_ADAPT -# define fseg_free_step_not_header(header, ahi, mtr) \ - fseg_free_step_not_header_func(header, ahi, mtr) -#else /* BTR_CUR_HASH_ADAPT */ -# define fseg_free_step_not_header(header, ahi, mtr) \ - fseg_free_step_not_header_func(header, mtr) -#endif /* BTR_CUR_HASH_ADAPT */ /** Reset the page type. Data files created before MySQL 5.1.48 may contain garbage in FIL_PAGE_TYPE. diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc index b0fcecf0e39..d92bcb761e4 100644 --- a/storage/innobase/row/row0import.cc +++ b/storage/innobase/row/row0import.cc @@ -26,6 +26,9 @@ Created 2012-02-08 by Sunny Bains. #include "row0import.h" #include "btr0pcur.h" +#ifdef BTR_CUR_HASH_ADAPT +# include "btr0sea.h" +#endif #include "que0que.h" #include "dict0boot.h" #include "dict0load.h" @@ -4017,15 +4020,12 @@ row_import_for_mysql( index entries that point to cached garbage pages in the buffer pool, because PageConverter::operator() only evicted those pages that were replaced by the imported pages. We must - discard all remaining adaptive hash index entries, because the + detach any remaining adaptive hash index entries, because the adaptive hash index must be a subset of the table contents; false positives are not tolerated. */ - while (buf_LRU_drop_page_hash_for_tablespace(table)) { - if (trx_is_interrupted(trx) - || srv_shutdown_state != SRV_SHUTDOWN_NONE) { - err = DB_INTERRUPTED; - break; - } + for (dict_index_t* index = UT_LIST_GET_FIRST(table->indexes); index; + index = UT_LIST_GET_NEXT(indexes, index)) { + index = index->clone_if_needed(); } #endif /* BTR_CUR_HASH_ADAPT */ diff --git a/storage/innobase/row/row0ins.cc b/storage/innobase/row/row0ins.cc index 4c11c241cd6..123566132f4 100644 --- a/storage/innobase/row/row0ins.cc +++ b/storage/innobase/row/row0ins.cc @@ -2907,7 +2907,7 @@ row_ins_sec_index_entry_low( err = btr_cur_search_to_nth_level( index, 0, entry, PAGE_CUR_RTREE_INSERT, search_mode, - &cursor, 0, __FILE__, __LINE__, &mtr); + &cursor, 0, __FILE__, __LINE__, &mtr, 0); if (mode == BTR_MODIFY_LEAF && rtr_info.mbr_adj) { mtr_commit(&mtr); @@ -2922,7 +2922,7 @@ row_ins_sec_index_entry_low( err = btr_cur_search_to_nth_level( index, 0, entry, PAGE_CUR_RTREE_INSERT, search_mode, - &cursor, 0, __FILE__, __LINE__, &mtr); + &cursor, 0, __FILE__, __LINE__, &mtr, 0); mode = BTR_MODIFY_TREE; } @@ -2934,7 +2934,7 @@ row_ins_sec_index_entry_low( err = btr_cur_search_to_nth_level( index, 0, entry, PAGE_CUR_LE, search_mode, - &cursor, 0, __FILE__, __LINE__, &mtr); + &cursor, 0, __FILE__, __LINE__, &mtr, 0); } if (err != DB_SUCCESS) { @@ -3028,7 +3028,7 @@ row_ins_sec_index_entry_low( index, 0, entry, PAGE_CUR_LE, (search_mode & ~(BTR_INSERT | BTR_IGNORE_SEC_UNIQUE)), - &cursor, 0, __FILE__, __LINE__, &mtr); + &cursor, 0, __FILE__, __LINE__, &mtr, 0); } if (row_ins_must_modify_rec(&cursor)) { diff --git a/storage/innobase/row/row0log.cc b/storage/innobase/row/row0log.cc index e7b06e5a5b2..e46ee2c4f18 100644 --- a/storage/innobase/row/row0log.cc +++ b/storage/innobase/row/row0log.cc @@ -3102,7 +3102,7 @@ row_log_apply_op_low( ? BTR_MODIFY_TREE : BTR_MODIFY_LEAF, &cursor, 0, __FILE__, __LINE__, - &mtr); + &mtr, 0); ut_ad(dict_index_get_n_unique(index) > 0); /* This test is somewhat similar to row_ins_must_modify_rec(), @@ -3151,7 +3151,7 @@ row_log_apply_op_low( btr_cur_search_to_nth_level( index, 0, entry, PAGE_CUR_LE, BTR_MODIFY_TREE, &cursor, 0, - __FILE__, __LINE__, &mtr); + __FILE__, __LINE__, &mtr, 0); /* No other thread than the current one is allowed to modify the index tree. @@ -3254,7 +3254,7 @@ insert_the_rec: btr_cur_search_to_nth_level( index, 0, entry, PAGE_CUR_LE, BTR_MODIFY_TREE, &cursor, 0, - __FILE__, __LINE__, &mtr); + __FILE__, __LINE__, &mtr, 0); } /* We already determined that the diff --git a/storage/innobase/row/row0merge.cc b/storage/innobase/row/row0merge.cc index 1677a47fa45..8c8f1674374 100644 --- a/storage/innobase/row/row0merge.cc +++ b/storage/innobase/row/row0merge.cc @@ -46,6 +46,9 @@ Completed by Sunny Bains and Marko Makela #include "row0vers.h" #include "handler0alter.h" #include "btr0bulk.h" +#ifdef BTR_CUR_ADAPT +# include "btr0sea.h" +#endif /* BTR_CUR_ADAPT */ #include "ut0stage.h" #include "fil0crypt.h" @@ -162,7 +165,7 @@ public: PAGE_CUR_RTREE_INSERT, BTR_MODIFY_LEAF, &ins_cur, 0, __FILE__, __LINE__, - &mtr); + &mtr, 0); /* It need to update MBR in parent entry, so change search mode to BTR_MODIFY_TREE */ @@ -178,7 +181,7 @@ public: m_index, 0, dtuple, PAGE_CUR_RTREE_INSERT, BTR_MODIFY_TREE, &ins_cur, 0, - __FILE__, __LINE__, &mtr); + __FILE__, __LINE__, &mtr, 0); } error = btr_cur_optimistic_insert( @@ -201,8 +204,7 @@ public: PAGE_CUR_RTREE_INSERT, BTR_MODIFY_TREE, &ins_cur, 0, - __FILE__, __LINE__, &mtr); - + __FILE__, __LINE__, &mtr, 0); error = btr_cur_pessimistic_insert( flag, &ins_cur, &ins_offsets, @@ -3770,6 +3772,9 @@ row_merge_drop_indexes( we should exclude FTS entries from prebuilt->ins_node->entry_list in ins_node_create_entry_list(). */ +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!index->search_info->ref_count); +#endif /* BTR_CUR_HASH_ADAPT */ dict_index_remove_from_cache( table, index); index = prev; diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 45586a1e977..9bbd531cb9a 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -2459,6 +2459,9 @@ row_create_index_for_mysql( index->table = table; err = dict_create_index_tree_in_mem(index, trx); +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!index->search_info->ref_count); +#endif /* BTR_CUR_HASH_ADAPT */ if (err != DB_SUCCESS) { dict_index_remove_from_cache(table, index); @@ -3372,35 +3375,6 @@ row_drop_table_for_mysql( if (!dict_table_is_temporary(table)) { if (table->space != TRX_SYS_SPACE) { -#ifdef BTR_CUR_HASH_ADAPT - /* On DISCARD TABLESPACE, we would not drop the - adaptive hash index entries. If the tablespace is - missing here, delete-marking the record in SYS_INDEXES - would not free any pages in the buffer pool. Thus, - dict_index_remove_from_cache() would hang due to - adaptive hash index entries existing in the buffer - pool. To prevent this hang, and also to guarantee - that btr_search_drop_page_hash_when_freed() will avoid - calling btr_search_drop_page_hash_index() while we - hold the InnoDB dictionary lock, we will drop any - adaptive hash index entries upfront. */ - const bool immune = is_temp_name - || create_failed - || sqlcom == SQLCOM_CREATE_TABLE - || strstr(table->name.m_name, "/FTS"); - - while (buf_LRU_drop_page_hash_for_tablespace(table)) { - if ((!immune && trx_is_interrupted(trx)) - || srv_shutdown_state - != SRV_SHUTDOWN_NONE) { - err = DB_INTERRUPTED; - table->to_be_dropped = false; - dict_table_close(table, true, false); - goto funct_exit; - } - } -#endif /* BTR_CUR_HASH_ADAPT */ - /* Delete the link file if used. */ if (DICT_TF_HAS_DATA_DIR(table->flags)) { RemoteDatafile::delete_link_file(name); diff --git a/storage/innobase/row/row0sel.cc b/storage/innobase/row/row0sel.cc index 6420bf8a707..15486500b37 100644 --- a/storage/innobase/row/row0sel.cc +++ b/storage/innobase/row/row0sel.cc @@ -2,7 +2,7 @@ Copyright (c) 1997, 2017, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2008, Google Inc. -Copyright (c) 2015, 2019, MariaDB Corporation. +Copyright (c) 2015, 2020, MariaDB Corporation. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described @@ -1289,23 +1289,17 @@ void row_sel_open_pcur( /*==============*/ plan_t* plan, /*!< in: table plan */ - ibool search_latch_locked, - /*!< in: TRUE if the thread currently - has the search latch locked in - s-mode */ +#ifdef BTR_CUR_HASH_ADAPT + ulint has_search_latch, +#endif mtr_t* mtr) /*!< in: mtr */ { dict_index_t* index; func_node_t* cond; que_node_t* exp; ulint n_fields; - ulint has_search_latch = 0; /* RW_S_LATCH or 0 */ ulint i; - if (search_latch_locked) { - has_search_latch = RW_S_LATCH; - } - index = plan->index; /* Calculate the value of the search tuple: the exact match columns @@ -1357,6 +1351,11 @@ row_sel_open_pcur( plan->pcur_is_open = TRUE; } +#ifndef BTR_CUR_HASH_ADAPT +# define row_sel_open_pcur(plan, has_search_latch, mtr) \ + row_sel_open_pcur(plan, mtr) +#endif /* !BTR_CUR_HASH_ADAPT */ + /*********************************************************************//** Restores a stored pcur position to a table index. @return TRUE if the cursor should be moved to the next record after we @@ -1618,12 +1617,6 @@ row_sel( ut_ad(thr->run_node == node); -#ifdef BTR_CUR_HASH_ADAPT - ibool search_latch_locked = FALSE; -#else /* BTR_CUR_HASH_ADAPT */ -# define search_latch_locked false -#endif /* BTR_CUR_HASH_ADAPT */ - if (node->read_view) { /* In consistent reads, we try to do with the hash index and not to use the buffer page get. This is to reduce memory bus @@ -1648,6 +1641,10 @@ table_loop: plan = sel_node_get_nth_plan(node, node->fetch_table); index = plan->index; +#ifdef BTR_CUR_HASH_ADAPT + ulint has_search_latch = 0; + rw_lock_t* const latch = btr_get_search_latch(index); +#endif /* BTR_CUR_HASH_ADAPT */ if (plan->n_rows_prefetched > 0) { sel_dequeue_prefetched_row(plan); @@ -1672,26 +1669,22 @@ table_loop: #ifdef BTR_CUR_HASH_ADAPT if (consistent_read && plan->unique_search && !plan->pcur_is_open && !plan->must_get_clust) { - if (!search_latch_locked) { - btr_search_s_lock(index); - - search_latch_locked = TRUE; - } else if (rw_lock_get_writer(btr_get_search_latch(index)) - == RW_LOCK_X_WAIT) { - + if (!has_search_latch) { + has_search_latch = RW_S_LATCH; + rw_lock_s_lock(latch); + } else if (rw_lock_get_writer(latch) == RW_LOCK_X_WAIT) { /* There is an x-latch request waiting: release the s-latch for a moment; as an s-latch here is often kept for some 10 searches before being released, a waiting x-latch request would block other threads from acquiring an s-latch for a long time, lowering performance significantly in multiprocessors. */ - - btr_search_s_unlock(index); - btr_search_s_lock(index); + rw_lock_s_unlock(latch); + rw_lock_s_lock(latch); } switch (row_sel_try_search_shortcut(node, plan, - search_latch_locked, + has_search_latch, &mtr)) { case SEL_FOUND: goto next_table; @@ -1709,10 +1702,9 @@ table_loop: mtr.start(); } - if (search_latch_locked) { - btr_search_s_unlock(index); - - search_latch_locked = FALSE; + if (has_search_latch) { + has_search_latch = 0; + rw_lock_s_unlock(latch); } #endif /* BTR_CUR_HASH_ADAPT */ @@ -1720,7 +1712,7 @@ table_loop: /* Evaluate the expressions to build the search tuple and open the cursor */ - row_sel_open_pcur(plan, search_latch_locked, &mtr); + row_sel_open_pcur(plan, has_search_latch, &mtr); cursor_just_opened = TRUE; @@ -2117,7 +2109,9 @@ skip_lock: } next_rec: - ut_ad(!search_latch_locked); +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!has_search_latch); +#endif /* BTR_CUR_HASH_ADAPT */ if (mtr_has_extra_clust_latch) { @@ -2156,8 +2150,9 @@ next_table: plan->cursor_at_end = TRUE; } else { - ut_ad(!search_latch_locked); - +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!has_search_latch); +#endif /* BTR_CUR_HASH_ADAPT */ plan->stored_cursor_rec_processed = TRUE; btr_pcur_store_position(&(plan->pcur), &mtr); @@ -2248,8 +2243,9 @@ stop_for_a_while: inserted new records which should have appeared in the result set, which would result in the phantom problem. */ - ut_ad(!search_latch_locked); - +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!has_search_latch); +#endif /* BTR_CUR_HASH_ADAPT */ plan->stored_cursor_rec_processed = FALSE; btr_pcur_store_position(&(plan->pcur), &mtr); @@ -2266,7 +2262,9 @@ commit_mtr_for_a_while: plan->stored_cursor_rec_processed = TRUE; - ut_ad(!search_latch_locked); +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!has_search_latch); +#endif /* BTR_CUR_HASH_ADAPT */ btr_pcur_store_position(&(plan->pcur), &mtr); mtr.commit(); @@ -2280,7 +2278,9 @@ lock_wait_or_error: /* See the note at stop_for_a_while: the same holds for this case */ ut_ad(!btr_pcur_is_before_first_on_page(&plan->pcur) || !node->asc); - ut_ad(!search_latch_locked); +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!has_search_latch); +#endif /* BTR_CUR_HASH_ADAPT */ plan->stored_cursor_rec_processed = FALSE; btr_pcur_store_position(&(plan->pcur), &mtr); @@ -2289,8 +2289,8 @@ lock_wait_or_error: func_exit: #ifdef BTR_CUR_HASH_ADAPT - if (search_latch_locked) { - btr_search_s_unlock(index); + if (has_search_latch) { + rw_lock_s_unlock(latch); } #endif /* BTR_CUR_HASH_ADAPT */ ut_ad(!sync_check_iterate(dict_sync_check())); @@ -4460,7 +4460,6 @@ row_search_mvcc( && !prebuilt->templ_contains_blob && !prebuilt->used_in_HANDLER && (prebuilt->mysql_row_len < UNIV_PAGE_SIZE / 8)) { - mode = PAGE_CUR_GE; if (trx->mysql_n_tables_locked == 0 @@ -4480,7 +4479,8 @@ row_search_mvcc( and if we try that, we can deadlock on the adaptive hash index semaphore! */ - rw_lock_s_lock(btr_get_search_latch(index)); + rw_lock_t* const latch = btr_get_search_latch(index); + rw_lock_s_lock(latch); switch (row_sel_try_search_shortcut_for_mysql( &rec, prebuilt, &offsets, &heap, @@ -4534,7 +4534,7 @@ row_search_mvcc( err = DB_SUCCESS; - rw_lock_s_unlock(btr_get_search_latch(index)); + rw_lock_s_unlock(latch); goto func_exit; @@ -4544,7 +4544,7 @@ row_search_mvcc( err = DB_RECORD_NOT_FOUND; - rw_lock_s_unlock(btr_get_search_latch(index)); + rw_lock_s_unlock(latch); /* NOTE that we do NOT store the cursor position */ @@ -4561,7 +4561,7 @@ row_search_mvcc( mtr.commit(); mtr.start(); - rw_lock_s_unlock(btr_get_search_latch(index)); + rw_lock_s_unlock(latch); } } #endif /* BTR_CUR_HASH_ADAPT */ diff --git a/storage/innobase/row/row0trunc.cc b/storage/innobase/row/row0trunc.cc index 5e512c602e6..618e161bee4 100644 --- a/storage/innobase/row/row0trunc.cc +++ b/storage/innobase/row/row0trunc.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 2013, 2018, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -39,6 +39,7 @@ Created 2013-04-12 Sunny Bains #include "os0file.h" #include "que0que.h" #include "trx0undo.h" +#include "btr0sea.h" /* FIXME: For temporary tables, use a simple approach of btr_free() and btr_create() of each index tree. */ @@ -1953,7 +1954,6 @@ dberr_t row_truncate_table_for_mysql(dict_table_t* table, trx_t* trx) for (dict_index_t* index = UT_LIST_GET_FIRST(table->indexes); index != NULL; index = UT_LIST_GET_NEXT(indexes, index)) { - err = dict_truncate_index_tree_in_mem(index); if (err != DB_SUCCESS) { @@ -2003,6 +2003,15 @@ dberr_t row_truncate_table_for_mysql(dict_table_t* table, trx_t* trx) os_thread_sleep(2000000); DBUG_SUICIDE();); +#ifdef BTR_CUR_HASH_ADAPT + dict_table_x_unlock_indexes(table); + for (dict_index_t* index = UT_LIST_GET_FIRST(table->indexes); index; + index = UT_LIST_GET_NEXT(indexes, index)) { + index = index->clone_if_needed(); + } + dict_table_x_lock_indexes(table); +#endif /* BTR_CUR_HASH_ADAPT */ + /* Step-10: Re-create new indexes. */ if (!dict_table_is_temporary(table)) { diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc index 732435ccefb..81ba1b731bf 100644 --- a/storage/innobase/trx/trx0purge.cc +++ b/storage/innobase/trx/trx0purge.cc @@ -1,7 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2017, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2019, MariaDB Corporation. +Copyright (c) 2017, 2020, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -383,7 +383,7 @@ trx_purge_free_segment(trx_rseg_t* rseg, fil_addr_t hdr_addr) } if (fseg_free_step_not_header( - seg_hdr + TRX_UNDO_FSEG_HEADER, false, &mtr)) { + seg_hdr + TRX_UNDO_FSEG_HEADER, &mtr)) { break; } @@ -413,7 +413,7 @@ trx_purge_free_segment(trx_rseg_t* rseg, fil_addr_t hdr_addr) is not flooded with bufferfixed pages: see the note in fsp0fsp.cc. */ - } while (!fseg_free_step(seg_hdr + TRX_UNDO_FSEG_HEADER, false, &mtr)); + } while (!fseg_free_step(seg_hdr + TRX_UNDO_FSEG_HEADER, &mtr)); hist_size = mtr_read_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE, MLOG_4BYTES, &mtr); diff --git a/storage/innobase/trx/trx0undo.cc b/storage/innobase/trx/trx0undo.cc index 336506c7b65..f9e76e5d772 100644 --- a/storage/innobase/trx/trx0undo.cc +++ b/storage/innobase/trx/trx0undo.cc @@ -869,7 +869,7 @@ trx_undo_free_page( undo_page + TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_NODE, mtr); fseg_free_page(header_page + TRX_UNDO_SEG_HDR + TRX_UNDO_FSEG_HEADER, - space, page_no, false, mtr); + space, page_no, mtr); last_addr = flst_get_last(header_page + TRX_UNDO_SEG_HDR + TRX_UNDO_PAGE_LIST, mtr); @@ -1092,7 +1092,7 @@ trx_undo_seg_free( file_seg = seg_header + TRX_UNDO_FSEG_HEADER; - finished = fseg_free_step(file_seg, false, &mtr); + finished = fseg_free_step(file_seg, &mtr); if (finished) { /* Update the rseg header */ From 3df297271a02ef13babae6ff6a7e47a6bdb7d538 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Fri, 15 May 2020 20:16:58 +0400 Subject: [PATCH 21/35] MDEV-22579 No error when inserting DEFAULT(non_virtual_column) into a virtual column The code erroneously allowed both: INSERT INTO t1 (vcol) VALUES (DEFAULT); INSERT INTO t1 (vcol) VALUES (DEFAULT(non_virtual_column)); The former is OK, but the latter is not. Adding a new virtual method in Item: virtual bool vcol_assignment_allowed_value() const { return false; } Item_null, Item_param and Item_default_value override it. Item_default_value overrides it in the way to: - allow DEFAULT - disallow DEFAULT(col) --- mysql-test/suite/vcol/r/vcol_misc.result | 20 ++++++++++++++++ mysql-test/suite/vcol/t/vcol_misc.test | 30 ++++++++++++++++++++++++ sql/item.h | 11 +++++++++ sql/sql_base.cc | 6 ++--- 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/mysql-test/suite/vcol/r/vcol_misc.result b/mysql-test/suite/vcol/r/vcol_misc.result index 5e84a314b38..abbd73cead6 100644 --- a/mysql-test/suite/vcol/r/vcol_misc.result +++ b/mysql-test/suite/vcol/r/vcol_misc.result @@ -409,5 +409,25 @@ aaa Warnings: Warning 1918 Encountered illegal value '\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7' when converting to DOUBLE # +# MDEV-22579 No error when inserting DEFAULT(non_virtual_column) into a virtual column +# +SET sql_mode=STRICT_ALL_TABLES; +CREATE OR REPLACE TABLE t1 ( +a INT NOT NULL DEFAULT 10, +b INT AS (a+1) VIRTUAL +) ENGINE=MyISAM; +INSERT INTO t1 (b) VALUES (10); +ERROR HY000: The value specified for computed column 'b' in table 't1' has been ignored +INSERT INTO t1 (b) VALUES (DEFAULT(a)); +ERROR HY000: The value specified for computed column 'b' in table 't1' has been ignored +INSERT INTO t1 (b) VALUES (DEFAULT); +INSERT INTO t1 VALUES (10,10); +ERROR HY000: The value specified for computed column 'b' in table 't1' has been ignored +INSERT INTO t1 VALUES (10,DEFAULT(a)); +ERROR HY000: The value specified for computed column 'b' in table 't1' has been ignored +INSERT INTO t1 VALUES (10, DEFAULT); +DROP TABLE t1; +SET sql_mode=DEFAULT; +# # End of 10.1 tests # diff --git a/mysql-test/suite/vcol/t/vcol_misc.test b/mysql-test/suite/vcol/t/vcol_misc.test index d753f4c09cf..9440cc533f8 100644 --- a/mysql-test/suite/vcol/t/vcol_misc.test +++ b/mysql-test/suite/vcol/t/vcol_misc.test @@ -365,6 +365,36 @@ SELECT COLUMN_GET(@aaa, 'price' AS DECIMAL) aaa; SELECT COLUMN_GET(@aaa, 'price' AS INT) aaa; SELECT COLUMN_GET(@aaa, 'price' AS DOUBLE) aaa; + +--echo # +--echo # MDEV-22579 No error when inserting DEFAULT(non_virtual_column) into a virtual column +--echo # + +SET sql_mode=STRICT_ALL_TABLES; +CREATE OR REPLACE TABLE t1 ( + a INT NOT NULL DEFAULT 10, + b INT AS (a+1) VIRTUAL +) ENGINE=MyISAM; + +# Testing with a column list + +--error ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN +INSERT INTO t1 (b) VALUES (10); +--error ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN +INSERT INTO t1 (b) VALUES (DEFAULT(a)); +INSERT INTO t1 (b) VALUES (DEFAULT); + +# Testing without a column list +--error ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN +INSERT INTO t1 VALUES (10,10); +--error ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN +INSERT INTO t1 VALUES (10,DEFAULT(a)); +INSERT INTO t1 VALUES (10, DEFAULT); + +DROP TABLE t1; +SET sql_mode=DEFAULT; + + --echo # --echo # End of 10.1 tests --echo # diff --git a/sql/item.h b/sql/item.h index 8a90d99db9d..bf1087e7e6e 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1128,6 +1128,13 @@ public: a constant expression. Used in the optimizer to propagate basic constants. */ virtual bool basic_const_item() const { return 0; } + /* + Determines if the expression is allowed as + a virtual column assignment source: + INSERT INTO t1 (vcol) VALUES (10) -> error + INSERT INTO t1 (vcol) VALUES (NULL) -> ok + */ + virtual bool vcol_assignment_allowed_value() const { return false; } /* cloning of constant items (0 if it is not const) */ virtual Item *clone_item(THD *thd) { return 0; } virtual cond_result eq_cmp_result() const { return COND_OK; } @@ -2626,6 +2633,7 @@ public: collation.set(cs, DERIVATION_IGNORABLE, MY_REPERTOIRE_ASCII); } enum Type type() const { return NULL_ITEM; } + bool vcol_assignment_allowed_value() const { return true; } bool eq(const Item *item, bool binary_cmp) const { return null_eq(item); } double val_real(); longlong val_int(); @@ -2699,6 +2707,8 @@ public: DECIMAL_VALUE } state; + bool vcol_assignment_allowed_value() const { return state == NULL_VALUE; } + /* A buffer for string and long data values. Historically all allocated values returned from val_str() were treated as eligible to @@ -4837,6 +4847,7 @@ public: (const char *)NULL), arg(a) {} enum Type type() const { return DEFAULT_VALUE_ITEM; } + bool vcol_assignment_allowed_value() const { return arg == NULL; } bool eq(const Item *item, bool binary_cmp) const; bool fix_fields(THD *, Item **); virtual void print(String *str, enum_query_type query_type); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index c092faa986b..3533c241fbc 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8889,8 +8889,7 @@ fill_record(THD *thd, TABLE *table_arg, List &fields, List &values, rfield->field_index == table->next_number_field->field_index) table->auto_increment_field_not_null= TRUE; if (rfield->vcol_info && - value->type() != Item::DEFAULT_VALUE_ITEM && - value->type() != Item::NULL_ITEM && + !value->vcol_assignment_allowed_value() && table->s->table_category != TABLE_CATEGORY_TEMPORARY) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, @@ -9098,8 +9097,7 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List &values, if (field->field_index == autoinc_index) table->auto_increment_field_not_null= TRUE; if (field->vcol_info && - value->type() != Item::DEFAULT_VALUE_ITEM && - value->type() != Item::NULL_ITEM && + !value->vcol_assignment_allowed_value() && table->s->table_category != TABLE_CATEGORY_TEMPORARY) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, From 3f12a5968a56cb5030381a78b313fdb36024c7f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 15 May 2020 22:54:05 +0300 Subject: [PATCH 22/35] MDEV-13626: Make test more robust In commit b1742a5c951633213d756600ee73ba7bfa7800ff we forgot FLUSH TABLES, potentially causing errors for MyISAM system tables. --- mysql-test/suite/innodb/t/xa_recovery_debug.test | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mysql-test/suite/innodb/t/xa_recovery_debug.test b/mysql-test/suite/innodb/t/xa_recovery_debug.test index 21a38854adb..ada2940ed9c 100644 --- a/mysql-test/suite/innodb/t/xa_recovery_debug.test +++ b/mysql-test/suite/innodb/t/xa_recovery_debug.test @@ -4,6 +4,10 @@ # Embedded server does not support restarting --source include/not_embedded.inc +--disable_query_log +FLUSH TABLES; +--enable_query_log + --echo # --echo # Bug#20872655 XA ROLLBACK IS NOT CRASH-SAFE --echo # From 85651269b6186b53bae804413c76f4117fa225d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Sat, 16 May 2020 06:24:09 +0300 Subject: [PATCH 23/35] MDEV-18100: Clean up test --- mysql-test/main/custom_aggregate_functions.result | 2 ++ mysql-test/main/custom_aggregate_functions.test | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/mysql-test/main/custom_aggregate_functions.result b/mysql-test/main/custom_aggregate_functions.result index 8ad7b4b4dc0..0e73f963b45 100644 --- a/mysql-test/main/custom_aggregate_functions.result +++ b/mysql-test/main/custom_aggregate_functions.result @@ -1186,3 +1186,5 @@ common_schema.sum_ints(seq) 3 drop database common_schema; drop database another_schema; +USE test; +# End of 10.3 tests diff --git a/mysql-test/main/custom_aggregate_functions.test b/mysql-test/main/custom_aggregate_functions.test index 4d9dd0a4929..7442c7ec2ee 100644 --- a/mysql-test/main/custom_aggregate_functions.test +++ b/mysql-test/main/custom_aggregate_functions.test @@ -998,3 +998,7 @@ SELECT common_schema.sum_ints(seq) FROM (SELECT 1 seq UNION ALL SELECT 2) t; drop database common_schema; drop database another_schema; + +USE test; + +--echo # End of 10.3 tests From 4f26aea51b22eaf65a44d5f4d8587d2060953d35 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Sun, 17 May 2020 11:42:50 +0530 Subject: [PATCH 24/35] MDEV-21269 Parallel merging of fts index rebuild fails Problem: ======= - During alter rebuild, document read from old table is tokenzied parallelly by innodb_ft_sort_pll_degree threads and stores it in respective merge files. While doing the parallel merge, InnoDB wrongly skips the root level selection of merging buffer records. So it leads to insertion of merge records in non-ascending order. Solution: ========== Build selection tree for the root level also. So that root of selection tree can always contain sorted buffer. --- storage/innobase/row/row0ftsort.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/storage/innobase/row/row0ftsort.cc b/storage/innobase/row/row0ftsort.cc index ec65f295e7f..874854c2da9 100644 --- a/storage/innobase/row/row0ftsort.cc +++ b/storage/innobase/row/row0ftsort.cc @@ -1528,10 +1528,11 @@ row_fts_build_sel_tree( sel_tree[i + start] = int(i); } - for (i = treelevel; --i; ) { + i = treelevel; + do { row_fts_build_sel_tree_level( - sel_tree, i, mrec, offsets, index); - } + sel_tree, --i, mrec, offsets, index); + } while (i > 0); return(treelevel); } From 0a5668f5128c731a346abf41afdc6fed33164ffc Mon Sep 17 00:00:00 2001 From: Varun Gupta Date: Fri, 15 May 2020 02:37:16 +0530 Subject: [PATCH 25/35] MDEV-22556: Incorrect result for window function when using encrypt-tmp-files=ON The issue here is that end_of_file for encrypted temporary IO_CACHE (used by filesort) is updated using lseek. Encryption adds storage overhead and hides it from the caller by recalculating offsets and lengths. Two different IO_CACHE cannot possibly modify the same file because the encryption key is randomly generated and stored in the IO_CACHE. So when the tempfiles are encrypted DO NOT use lseek to change end_of_file. Further observations about updating end_of_file using lseek 1) The end_of_file update is only used for binlog index files 2) The whole point is to update file length when the file was modified via a different file descriptor. 3) The temporary IO_CACHE files can never be modified via a different file descriptor. 4) For encrypted temporary IO_CACHE, end_of_file should not be updated with lseek --- .../encryption/r/tempfiles_encrypted.result | 18 +++++++++++++++ .../encryption/t/tempfiles_encrypted.opt | 1 + .../encryption/t/tempfiles_encrypted.test | 23 +++++++++++++++++++ mysys/mf_iocache.c | 7 ++++-- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 mysql-test/suite/encryption/r/tempfiles_encrypted.result create mode 100644 mysql-test/suite/encryption/t/tempfiles_encrypted.opt create mode 100644 mysql-test/suite/encryption/t/tempfiles_encrypted.test diff --git a/mysql-test/suite/encryption/r/tempfiles_encrypted.result b/mysql-test/suite/encryption/r/tempfiles_encrypted.result new file mode 100644 index 00000000000..d08cb33e0fc --- /dev/null +++ b/mysql-test/suite/encryption/r/tempfiles_encrypted.result @@ -0,0 +1,18 @@ +# +# Tests when the temporary files are encrypted +# +select @@encrypt_tmp_files; +@@encrypt_tmp_files +1 +# +# MDEV-22556: Incorrect result for window function when using encrypt-tmp-files=ON +# +set @save_sort_buffer_size=@@sort_buffer_size; +set sort_buffer_size= 2000; +create table t1( a DECIMAL(12,0) DEFAULT NULL, b VARCHAR(20) DEFAULT NULL, c DECIMAL(12,0) DEFAULT NULL)engine=INNODB; +insert into t1 select seq, seq, seq from seq_1_to_5000; +select count(*) from (select a, b, c, ROW_NUMBER() OVER (PARTITION BY a) FROM t1)q; +count(*) +5000 +set @@sort_buffer_size=@save_sort_buffer_size; +drop table t1; diff --git a/mysql-test/suite/encryption/t/tempfiles_encrypted.opt b/mysql-test/suite/encryption/t/tempfiles_encrypted.opt new file mode 100644 index 00000000000..81877a8d1c5 --- /dev/null +++ b/mysql-test/suite/encryption/t/tempfiles_encrypted.opt @@ -0,0 +1 @@ +--encrypt-tmp_files=ON diff --git a/mysql-test/suite/encryption/t/tempfiles_encrypted.test b/mysql-test/suite/encryption/t/tempfiles_encrypted.test new file mode 100644 index 00000000000..96b981c1c06 --- /dev/null +++ b/mysql-test/suite/encryption/t/tempfiles_encrypted.test @@ -0,0 +1,23 @@ +--echo # +--echo # Tests when the temporary files are encrypted +--echo # + +source include/have_file_key_management_plugin.inc; +source include/have_sequence.inc; +source include/have_innodb.inc; + +select @@encrypt_tmp_files; + +--echo # +--echo # MDEV-22556: Incorrect result for window function when using encrypt-tmp-files=ON +--echo # + +set @save_sort_buffer_size=@@sort_buffer_size; +set sort_buffer_size= 2000; +create table t1( a DECIMAL(12,0) DEFAULT NULL, b VARCHAR(20) DEFAULT NULL, c DECIMAL(12,0) DEFAULT NULL)engine=INNODB; +insert into t1 select seq, seq, seq from seq_1_to_5000; +select count(*) from (select a, b, c, ROW_NUMBER() OVER (PARTITION BY a) FROM t1)q; + +set @@sort_buffer_size=@save_sort_buffer_size; + +drop table t1; diff --git a/mysys/mf_iocache.c b/mysys/mf_iocache.c index 54b89007b4c..51e8fe1a02f 100644 --- a/mysys/mf_iocache.c +++ b/mysys/mf_iocache.c @@ -504,8 +504,11 @@ my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, if (info->type == WRITE_CACHE) info->end_of_file= my_b_tell(info); else - info->end_of_file= mysql_file_seek(info->file, 0L, MY_SEEK_END, - MYF(0)); + { + if (!(info->myflags & MY_ENCRYPT)) + info->end_of_file= mysql_file_seek(info->file, 0L, + MY_SEEK_END, MYF(0)); + } } /* flush cache if we want to reuse it */ if (!clear_cache && my_b_flush_io_cache(info,1)) From 9ddeccc299112864d98ced1b0415c086b7d98a6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Otto=20Kek=C3=A4l=C3=A4inen?= Date: Sun, 5 Apr 2020 18:55:15 +0300 Subject: [PATCH 26/35] Travis-CI: Add missing build dependency dh-exec Backported from 30b44aaec7120f41ee1383536730947cfa427308. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 8f21dc4d337..c2edb30e9c1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,6 +48,7 @@ matrix: - cmake - debhelper - dh-apparmor + - dh-exec - dpatch - gdb - libaio-dev From 8d056affd86dcfa0546b4eb2ce133812871b2bc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Otto=20Kek=C3=A4l=C3=A4inen?= Date: Sun, 5 Apr 2020 23:55:23 +0300 Subject: [PATCH 27/35] Travis-CI: Shorten deb build log to keep it under 4 MB There is a 4 MB hard limit on Travis-CI and build output needs to be less than that. Silencing the 'make install' step gets rid of a lot of "Installing.." and "Missing.." and removing all mysql-test files will make the dh_missing warnings much shorter. Cherry-picked from 41952c85f1644690249ce624de7609cbebb93638. --- debian/rules | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/debian/rules b/debian/rules index 647c73e43e0..809911d4179 100755 --- a/debian/rules +++ b/debian/rules @@ -121,8 +121,13 @@ endif cp $(BUILDDIR)/support-files/mariadb.service debian/mariadb-server-10.2.mariadb.service cp $(BUILDDIR)/support-files/mariadb@.service debian/mariadb-server-10.2.mariadb@.service - # make install - cd $(BUILDDIR) && $(MAKE) install DESTDIR=$(TMP) + # Run 'make install' without output since it is uninteresting and + # silencing it helps to make overall build log shorter and more readable + @echo "Running $(MAKE) install DESTDIR=$(TMP) ..." + cd $(BUILDDIR) && $(MAKE) install DESTDIR=$(TMP) > /dev/null + + # If mariadb-test package is removed, also remove most of it's files + grep --quiet "Package: mariadb-test" debian/control || rm -rf $(TMP)/usr/share/mysql/mysql-test # Delete runnable files we don't want to have in the test data package. # This avoids triggering multiple Lintian errors. From c995090a537b79b4ab92e7cae0f74560325965d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Otto=20Kek=C3=A4l=C3=A4inen?= Date: Sun, 17 May 2020 15:52:35 +0300 Subject: [PATCH 28/35] Travis-CI: Remove builds that always fail to make CI useful again Also clean away dead code that is not used and will never have any use on the 10.2 branch. --- .travis.yml | 70 +++-------------------------------------------------- 1 file changed, 4 insertions(+), 66 deletions(-) diff --git a/.travis.yml b/.travis.yml index c2edb30e9c1..d38ba46b971 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,10 +8,8 @@ git: depth: 2 language: cpp -os: - - linux - - osx -osx_image: xcode9.1 +os: linux + compiler: - gcc - clang @@ -19,15 +17,9 @@ compiler: cache: apt: true ccache: true - directories: - - /usr/local/Cellar env: matrix: -# - GCC_VERSION=4.8 TYPE=Debug MYSQL_TEST_SUITES=rpl -# - GCC_VERSION=5 TYPE=Debug MYSQL_TEST_SUITES=main,archive,optimizer_unfixed_bugs,parts,sys_vars,unit,vcol,innodb,innodb_gis,innodb_zip,innodb_fts -# - GCC_VERSION=6 TYPE=Debug MYSQL_TEST_SUITES=binlog,binlog_encryption,encryption,rocksdb -# - GCC_VERSION=6 TYPE=Debug MYSQL_TEST_SUITES=csv,federated,funcs_1,funcs_2,gcol,handler,heap,json,maria,percona,perfschema,plugins,multi_source,roles - GCC_VERSION=4.8 TYPE=RelWithDebInfo MYSQL_TEST_SUITES=rpl - GCC_VERSION=5 TYPE=RelWithDebInfo MYSQL_TEST_SUITES=main,archive,optimizer_unfixed_bugs,parts,sys_vars,unit,vcol,innodb,innodb_gis,innodb_zip,innodb_fts - GCC_VERSION=6 TYPE=RelWithDebInfo MYSQL_TEST_SUITES=binlog,binlog_encryption,encryption,rocksdb @@ -35,8 +27,8 @@ env: matrix: exclude: - - os: osx - compiler: gcc + - compiler: clang + env: GCC_VERSION=5 TYPE=RelWithDebInfo MYSQL_TEST_SUITES=main,archive,optimizer_unfixed_bugs,parts,sys_vars,unit,vcol,innodb,innodb_gis,innodb_zip,innodb_fts include: - os: linux compiler: gcc @@ -87,51 +79,6 @@ matrix: - export MTR_MEM=/tmp - env DEB_BUILD_OPTIONS="parallel=6" debian/autobake-deb.sh; - ccache --show-stats - # Until OSX becomes a bit more stable: MDEV-12435 - allow_failures: - - os: osx - compiler: clang - env: GCC_VERSION=4.8 TYPE=RelWithDebInfo MYSQL_TEST_SUITES=rpl - - os: osx - compiler: clang - env: GCC_VERSION=5 TYPE=RelWithDebInfo MYSQL_TEST_SUITES=main,archive,optimizer_unfixed_bugs,parts,sys_vars,unit,vcol,innodb,innodb_gis,innodb_zip,innodb_fts - - os: osx - compiler: clang - env: GCC_VERSION=6 TYPE=RelWithDebInfo MYSQL_TEST_SUITES=binlog,binlog_encryption,encryption,rocksdb - - os: osx - compiler: clang - env: GCC_VERSION=6 TYPE=RelWithDebInfo MYSQL_TEST_SUITES=csv,federated,funcs_1,funcs_2,gcol,handler,heap,json,maria,percona,perfschema,plugins,multi_source,roles - -# Matrix include for coverity -# - env: -# - GCC_VERSION=6 -# addon: -# coverity_scan: -# # ref: https://scan.coverity.com/travis_ci -# # GitHub project metadata -# project: -# - name: MariaDB/server -# - description: MariaDB Server -# -# # Where email notification of build analysis results will be sent -# notification_email: security@mariadb.org -# -# # Commands to prepare for build_command -# build_command_prepend: -# - source .travis.compiler.sh -# - ${MYSQL_BUILD_CC} --version ; ${MYSQL_BUILD_CXX} --version -# - cmake . -# -DCMAKE_BUILD_TYPE=Debug -# -DWITH_SSL=system -DWITH_ZLIB=system -# -DWITHOUT_TOKUDB_STORAGE_ENGINE=ON -DWITHOUT_MROONGA_STORAGE_ENGINE=ON -# -# # The command that will be added as an argument to "cov-build" to compile your project for analysis, -# build_command: make -j 4 -# -# # Pattern to match selecting branches that will run analysis. -# # Take care in resource usage, and consider the build frequency allowances per -# # https://scan.coverity.com/faq#frequency - 7 per week is the current limit. -# branch_pattern: .*coverity.* addons: apt: @@ -178,15 +125,6 @@ addons: - libzmq-dev - uuid-dev -# libsystemd-daemon-dev # https://github.com/travis-ci/apt-package-whitelist/issues/3882 - -before_install: - - if [[ "${TRAVIS_OS_NAME}" == 'osx' ]]; then - brew update; - brew install gnutls lz4 lzo xz snappy ccache jemalloc curl ossp-uuid pcre; - brew link ccache; - fi - script: - ccache --version # Clang: From 7baa40dffae4c8175913ab58d320684f1d5f1bad Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Mon, 18 May 2020 16:37:51 +1000 Subject: [PATCH 29/35] MDEV-21976: mtr main.udf - broaden localhost (#1543) Localhost, depending on the platform can return any 127.0.0.1/8 address. --- mysql-test/r/udf.result | 6 +++--- mysql-test/t/udf.test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/udf.result b/mysql-test/r/udf.result index bcfc9941db9..c74b89a1b96 100644 --- a/mysql-test/r/udf.result +++ b/mysql-test/r/udf.result @@ -30,9 +30,9 @@ lookup("127.0.0.1") 127.0.0.1 select lookup(127,0,0,1); ERROR HY000: Can't initialize function 'lookup'; Wrong arguments to lookup; Use the source -select lookup("localhost"); -lookup("localhost") -127.0.0.1 +select lookup("localhost") rlike '^127\.\\d+\.\\d+.\\d+$'; +lookup("localhost") rlike '^127\.\\d+\.\\d+.\\d+$' +1 select reverse_lookup(); ERROR HY000: Can't initialize function 'reverse_lookup'; Wrong number of arguments to reverse_lookup; Use the source select reverse_lookup("127.0.0.1"); diff --git a/mysql-test/t/udf.test b/mysql-test/t/udf.test index 4ee412f80d8..e9ba8e941e3 100644 --- a/mysql-test/t/udf.test +++ b/mysql-test/t/udf.test @@ -47,7 +47,7 @@ select lookup(); select lookup("127.0.0.1"); --error ER_CANT_INITIALIZE_UDF select lookup(127,0,0,1); -select lookup("localhost"); +select lookup("localhost") rlike '^127\.\\d+\.\\d+.\\d+$'; --error ER_CANT_INITIALIZE_UDF select reverse_lookup(); From ea912d1605b463a16a0c186bf10e61c4af519439 Mon Sep 17 00:00:00 2001 From: Julius Goryavsky Date: Fri, 15 May 2020 18:35:19 +0200 Subject: [PATCH 30/35] MDEV-22554: galera_sst_mariabackup fails with "Failed to start mysqld.2" The problem is caused by the operation of netcat streamer and does not appear on systems where socat is installed. We need to add the "-N" option for netcat to call shutdown() on the socket when receiving EOF from STDIN. --- scripts/wsrep_sst_mariabackup.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/wsrep_sst_mariabackup.sh b/scripts/wsrep_sst_mariabackup.sh index 799276258f2..9aadcc0bc9b 100644 --- a/scripts/wsrep_sst_mariabackup.sh +++ b/scripts/wsrep_sst_mariabackup.sh @@ -201,7 +201,11 @@ get_transfer() tcmd="nc ${REMOTEIP} ${TSST_PORT}" elif nc -h 2>&1 | grep -q -- '-d\>';then # Debian netcat - tcmd="nc ${REMOTEIP} ${TSST_PORT}" + if nc -h 2>&1 | grep -q -- '-N\>';then + tcmd="nc -N ${REMOTEIP} ${TSST_PORT}" + else + tcmd="nc ${REMOTEIP} ${TSST_PORT}" + fi else # traditional netcat tcmd="nc -q0 ${REMOTEIP} ${TSST_PORT}" From f9d8571f383c820accbca662cc4f850f5d29c98c Mon Sep 17 00:00:00 2001 From: Julius Goryavsky Date: Fri, 15 May 2020 18:35:19 +0200 Subject: [PATCH 31/35] MDEV-22554: galera_sst_mariabackup fails with "Failed to start mysqld.2" The problem is caused by the operation of netcat streamer and does not appear on systems where socat is installed. We need to add the "-N" option for netcat to call shutdown() on the socket when receiving EOF from STDIN. --- scripts/wsrep_sst_mariabackup.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/wsrep_sst_mariabackup.sh b/scripts/wsrep_sst_mariabackup.sh index 4eb9917d341..1d6aedfb8eb 100644 --- a/scripts/wsrep_sst_mariabackup.sh +++ b/scripts/wsrep_sst_mariabackup.sh @@ -202,7 +202,11 @@ get_transfer() tcmd="nc ${REMOTEIP} ${TSST_PORT}" elif nc -h 2>&1 | grep -q -- '-d\>';then # Debian netcat - tcmd="nc ${REMOTEIP} ${TSST_PORT}" + if nc -h 2>&1 | grep -q -- '-N\>';then + tcmd="nc -N ${REMOTEIP} ${TSST_PORT}" + else + tcmd="nc ${REMOTEIP} ${TSST_PORT}" + fi else # traditional netcat tcmd="nc -q0 ${REMOTEIP} ${TSST_PORT}" From e0ddb077d95baac131e9ac8a701b8f9ff625b1a8 Mon Sep 17 00:00:00 2001 From: Julius Goryavsky Date: Fri, 15 May 2020 18:35:19 +0200 Subject: [PATCH 32/35] MDEV-22554: galera_sst_mariabackup fails with "Failed to start mysqld.2" The problem is caused by the operation of netcat streamer and does not appear on systems where socat is installed. We need to add the "-N" option for netcat to call shutdown() on the socket when receiving EOF from STDIN. --- scripts/wsrep_sst_mariabackup.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/wsrep_sst_mariabackup.sh b/scripts/wsrep_sst_mariabackup.sh index 799276258f2..9aadcc0bc9b 100644 --- a/scripts/wsrep_sst_mariabackup.sh +++ b/scripts/wsrep_sst_mariabackup.sh @@ -201,7 +201,11 @@ get_transfer() tcmd="nc ${REMOTEIP} ${TSST_PORT}" elif nc -h 2>&1 | grep -q -- '-d\>';then # Debian netcat - tcmd="nc ${REMOTEIP} ${TSST_PORT}" + if nc -h 2>&1 | grep -q -- '-N\>';then + tcmd="nc -N ${REMOTEIP} ${TSST_PORT}" + else + tcmd="nc ${REMOTEIP} ${TSST_PORT}" + fi else # traditional netcat tcmd="nc -q0 ${REMOTEIP} ${TSST_PORT}" From 5b6bcb59ac7791cf99caf683fb131b7be4b02341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 18 May 2020 14:04:31 +0300 Subject: [PATCH 33/35] MDEV-22611 Assertion btr_search_enabled failed during buffer pool resizing In commit ad6171b91cac33e70bb28fa6865488b2c65e858c (MDEV-22456) we removed the acquisition of the adaptive hash index latch from the caller of btr_search_update_hash_ref(). The tests innodb.innodb_buffer_pool_resize_with_chunks and innodb.innodb_buffer_pool_resize would occasionally fail starting with 10.3, due to MDEV-12288 causing more purge activity during the test. btr_search_update_hash_ref(): After acquiring the adaptive hash index latch, check that the adaptive hash index is still enabled on the page. --- storage/innobase/btr/btr0sea.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/storage/innobase/btr/btr0sea.cc b/storage/innobase/btr/btr0sea.cc index f1075358ad2..76aabf287c0 100644 --- a/storage/innobase/btr/btr0sea.cc +++ b/storage/innobase/btr/btr0sea.cc @@ -648,7 +648,7 @@ btr_search_update_hash_ref( dict_index_t* index = block->index; - if (!index) { + if (!index || !info->n_hash_potential) { return; } @@ -657,8 +657,9 @@ btr_search_update_hash_ref( ut_ad(!dict_index_is_ibuf(index)); rw_lock_t* const latch = btr_get_search_latch(index); rw_lock_x_lock(latch); + ut_ad(!block->index || block->index == index); - if ((info->n_hash_potential > 0) + if (block->index && (block->curr_n_fields == info->n_fields) && (block->curr_n_bytes == info->n_bytes) && (block->curr_left_side == info->left_side)) { From fde94b4cd6c916f118ccb2785c09dafef391298c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Lindstr=C3=B6m?= Date: Fri, 8 May 2020 11:35:15 +0300 Subject: [PATCH 34/35] MDEV-21483 : Galera MTR tests failed: galera.MW-328A galera.MW-328B Enable tests with additional galera output to find out actual reason for test failures. --- mysql-test/suite/galera/disabled.def | 2 -- mysql-test/suite/galera/r/MW-328C.result | 25 +++++++++++++++++ mysql-test/suite/galera/t/MW-328A.cnf | 4 +-- mysql-test/suite/galera/t/MW-328B.cnf | 4 +-- mysql-test/suite/galera/t/MW-328C.cnf | 7 +++++ mysql-test/suite/galera/t/MW-328C.test | 35 ++++++++++++++++++++++++ mysql-test/suite/galera/t/MW-328D.cnf | 7 +++++ mysql-test/suite/galera/t/MW-328E.cnf | 7 +++++ 8 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 mysql-test/suite/galera/r/MW-328C.result create mode 100644 mysql-test/suite/galera/t/MW-328C.cnf create mode 100644 mysql-test/suite/galera/t/MW-328C.test create mode 100644 mysql-test/suite/galera/t/MW-328D.cnf create mode 100644 mysql-test/suite/galera/t/MW-328E.cnf diff --git a/mysql-test/suite/galera/disabled.def b/mysql-test/suite/galera/disabled.def index e1868260af1..81970cd22aa 100644 --- a/mysql-test/suite/galera/disabled.def +++ b/mysql-test/suite/galera/disabled.def @@ -15,8 +15,6 @@ GCF-939 : MDEV-21520 galera.GCF-939 MDEV-16509 : MDEV-21523 galera.MDEV-16509 MDEV-20225 : MDEV-20886 galera.MDEV-20225 MW-286 : MDEV-18464 Killing thread can cause mutex deadlock if done concurrently with Galera/replication victim kill -MW-328A : MDEV-21483 galera.MW-328A galera.MW-328B -MW-328B : MDEV-21483 galera.MW-328A galera.MW-328B MW-329 : MDEV-19962 Galera test failure on MW-329 galera.galera_defaults : MDEV-21494 Galera test sporadic failure on galera.galera_defaults galera_as_slave_replication_bundle : MDEV-15785 OPTION_GTID_BEGIN is set in Gtid_log_event::do_apply_event() diff --git a/mysql-test/suite/galera/r/MW-328C.result b/mysql-test/suite/galera/r/MW-328C.result new file mode 100644 index 00000000000..7a00bb718de --- /dev/null +++ b/mysql-test/suite/galera/r/MW-328C.result @@ -0,0 +1,25 @@ +connection node_2; +connection node_1; +CREATE TABLE t1 (f1 INTEGER AUTO_INCREMENT PRIMARY KEY, f2 CHAR(20) DEFAULT 'abc') ENGINE=InnoDB; +INSERT INTO t1 (f1) VALUES (1); +CREATE TABLE t2 (f1 CHAR(20)) ENGINE=InnoDB; +CREATE PROCEDURE proc_update () +BEGIN +DECLARE CONTINUE HANDLER FOR SQLEXCEPTION BEGIN END; +SET SESSION wsrep_sync_wait = 0; +WHILE 1 DO +UPDATE t1 SET f2 = LEFT(MD5(RAND()), 4); +END WHILE; +END| +connect node_1X, 127.0.0.1, root, , test, $NODE_MYPORT_1; +connection node_1X; +CALL proc_update();; +connection node_2; +SET SESSION wsrep_retry_autocommit = 10000; +connection node_1; +connection node_1X; +Got one of the listed errors +connection node_1; +DROP PROCEDURE proc_update; +DROP TABLE t1, t2; +CALL mtr.add_suppression("conflict state ABORTED after post commit"); diff --git a/mysql-test/suite/galera/t/MW-328A.cnf b/mysql-test/suite/galera/t/MW-328A.cnf index a10ea88bdf2..e68f891792c 100644 --- a/mysql-test/suite/galera/t/MW-328A.cnf +++ b/mysql-test/suite/galera/t/MW-328A.cnf @@ -1,7 +1,7 @@ !include ../galera_2nodes.cnf [mysqld.1] -wsrep-debug=ON +wsrep-debug=SERVER [mysqld.2] -wsrep-debug=ON +wsrep-debug=SERVER diff --git a/mysql-test/suite/galera/t/MW-328B.cnf b/mysql-test/suite/galera/t/MW-328B.cnf index a10ea88bdf2..e68f891792c 100644 --- a/mysql-test/suite/galera/t/MW-328B.cnf +++ b/mysql-test/suite/galera/t/MW-328B.cnf @@ -1,7 +1,7 @@ !include ../galera_2nodes.cnf [mysqld.1] -wsrep-debug=ON +wsrep-debug=SERVER [mysqld.2] -wsrep-debug=ON +wsrep-debug=SERVER diff --git a/mysql-test/suite/galera/t/MW-328C.cnf b/mysql-test/suite/galera/t/MW-328C.cnf new file mode 100644 index 00000000000..e68f891792c --- /dev/null +++ b/mysql-test/suite/galera/t/MW-328C.cnf @@ -0,0 +1,7 @@ +!include ../galera_2nodes.cnf + +[mysqld.1] +wsrep-debug=SERVER + +[mysqld.2] +wsrep-debug=SERVER diff --git a/mysql-test/suite/galera/t/MW-328C.test b/mysql-test/suite/galera/t/MW-328C.test new file mode 100644 index 00000000000..7241dfbdbca --- /dev/null +++ b/mysql-test/suite/galera/t/MW-328C.test @@ -0,0 +1,35 @@ +# +# MW-328 Fix unnecessary/silent BF aborts +# + +# +# Make sure that a high value of wsrep_retry_autocommit +# masks all deadlock errors +# + +--source include/galera_cluster.inc +--source suite/galera/t/MW-328-header.inc + +--connection node_2 +--let $count = 100 + +SET SESSION wsrep_retry_autocommit = 10000; + +--disable_query_log + +while ($count) +{ + --error 0 + INSERT IGNORE INTO t2 SELECT f2 FROM t1; + + --disable_result_log + --error 0 + SELECT 1 FROM DUAL; + --enable_result_log + + --dec $count +} + +--enable_query_log + +--source suite/galera/t/MW-328-footer.inc diff --git a/mysql-test/suite/galera/t/MW-328D.cnf b/mysql-test/suite/galera/t/MW-328D.cnf new file mode 100644 index 00000000000..e68f891792c --- /dev/null +++ b/mysql-test/suite/galera/t/MW-328D.cnf @@ -0,0 +1,7 @@ +!include ../galera_2nodes.cnf + +[mysqld.1] +wsrep-debug=SERVER + +[mysqld.2] +wsrep-debug=SERVER diff --git a/mysql-test/suite/galera/t/MW-328E.cnf b/mysql-test/suite/galera/t/MW-328E.cnf new file mode 100644 index 00000000000..e68f891792c --- /dev/null +++ b/mysql-test/suite/galera/t/MW-328E.cnf @@ -0,0 +1,7 @@ +!include ../galera_2nodes.cnf + +[mysqld.1] +wsrep-debug=SERVER + +[mysqld.2] +wsrep-debug=SERVER From 386f168ab340791631e4d8979c4370ecef7e6b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 18 May 2020 14:49:44 +0300 Subject: [PATCH 35/35] MDEV-22456 after-merge fix: introduce Atomic_relaxed In the merge 9e6e43551fc61bc34152f8d60f5d72f0d3814787 we made Atomic_counter a more generic wrapper of std::atomic so that dict_index_t would support the implicit assignment operator. It is better to revert the changes to Atomic_counter and instead introduce Atomic_relaxed as a generic wrapper to std::atomic. Unlike Atomic_counter, we will not define operator++, operator+= or similar, because we want to make the operations more explicit in the users of Atomic_wrapper, because unlike loads and stores, atomic read-modify-write operations always incur some overhead. --- include/my_atomic.h | 45 +++++++++++++++++++++++++++++ include/my_counter.h | 13 +-------- storage/innobase/dict/dict0dict.cc | 11 ++----- storage/innobase/include/dict0mem.h | 6 ++-- storage/innobase/include/sync0rw.h | 6 ++-- storage/innobase/include/sync0rw.ic | 11 ++++--- storage/innobase/sync/sync0rw.cc | 4 +-- 7 files changed, 61 insertions(+), 35 deletions(-) diff --git a/include/my_atomic.h b/include/my_atomic.h index 1c54c24d455..88f6746ba3d 100644 --- a/include/my_atomic.h +++ b/include/my_atomic.h @@ -2,6 +2,7 @@ #define MY_ATOMIC_INCLUDED /* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2018, 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 @@ -169,4 +170,48 @@ my_atomic_casptr((P), (E), (D)) #endif +#ifdef __cplusplus +#include +/** + A wrapper for std::atomic, defaulting to std::memory_order_relaxed. + + When it comes to atomic loads or stores at std::memory_order_relaxed + on IA-32 or AMD64, this wrapper is only introducing some constraints + to the C++ compiler, to prevent some optimizations of loads or + stores. + + On POWER and ARM, atomic loads and stores involve different instructions + from normal loads and stores and will thus incur some overhead. + + Because atomic read-modify-write operations will always incur + overhead, we intentionally do not define + operator++(), operator--(), operator+=(), operator-=(), or similar, + to make the overhead stand out in the users of this code. +*/ +template class Atomic_relaxed +{ + std::atomic m; +public: + Atomic_relaxed(const Atomic_relaxed &rhs) + { m.store(rhs, std::memory_order_relaxed); } + Atomic_relaxed(Type val) : m(val) {} + Atomic_relaxed() {} + + operator Type() const { return m.load(std::memory_order_relaxed); } + Type operator=(const Type val) + { m.store(val, std::memory_order_relaxed); return val; } + Type operator=(const Atomic_relaxed &rhs) { return *this= Type{rhs}; } + Type fetch_add(const Type i, std::memory_order o= std::memory_order_relaxed) + { return m.fetch_add(i, o); } + Type fetch_sub(const Type i, std::memory_order o= std::memory_order_relaxed) + { return m.fetch_sub(i, o); } + bool compare_exchange_strong(Type& i1, const Type i2, + std::memory_order o1= std::memory_order_relaxed, + std::memory_order o2= std::memory_order_relaxed) + { return m.compare_exchange_strong(i1, i2, o1, o2); } + Type exchange(const Type i, std::memory_order o= std::memory_order_relaxed) + { return m.exchange(i, o); } +}; +#endif /* __cplusplus */ + #endif /* MY_ATOMIC_INCLUDED */ diff --git a/include/my_counter.h b/include/my_counter.h index 432dc7dda3d..c5cbe296df0 100644 --- a/include/my_counter.h +++ b/include/my_counter.h @@ -1,7 +1,7 @@ #ifndef MY_COUNTER_H_INCLUDED #define MY_COUNTER_H_INCLUDED /* - Copyright (C) 2018, 2020, MariaDB + Copyright (C) 2018 MariaDB Foundation 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 @@ -45,16 +45,5 @@ public: operator Type() const { return m_counter.load(std::memory_order_relaxed); } Type operator=(const Type val) { m_counter.store(val, std::memory_order_relaxed); return val; } - Type operator=(const Atomic_counter &rhs) { return *this= Type{rhs}; } - - Type fetch_add(const Type i, std::memory_order m) - { return m_counter.fetch_add(i, m); } - Type fetch_sub(const Type i, std::memory_order m) - { return m_counter.fetch_sub(i, m); } - bool compare_exchange_strong(Type& i1, const Type i2, - std::memory_order m1, std::memory_order m2) - { return m_counter.compare_exchange_strong(i1, i2, m1, m2); } - Type exchange(const Type i, std::memory_order m) - { return m_counter.exchange(i, m); } }; #endif /* MY_COUNTER_H_INCLUDED */ diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index c9abc03f682..e04d1dcbbe0 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -6158,10 +6158,7 @@ dict_index_zip_pad_update( beyond max pad size. */ if (info->pad + ZIP_PAD_INCR < (srv_page_size * zip_pad_max) / 100) { - /* Use atomics even though we have the mutex. - This is to ensure that we are able to read - info->pad atomically. */ - info->pad += ZIP_PAD_INCR; + info->pad.fetch_add(ZIP_PAD_INCR); MONITOR_INC(MONITOR_PAD_INCREMENTS); } @@ -6178,11 +6175,7 @@ dict_index_zip_pad_update( padding. */ if (info->n_rounds >= ZIP_PAD_SUCCESSFUL_ROUND_LIMIT && info->pad > 0) { - - /* Use atomics even though we have the mutex. - This is to ensure that we are able to read - info->pad atomically. */ - info->pad -= ZIP_PAD_INCR; + info->pad.fetch_sub(ZIP_PAD_INCR); info->n_rounds = 0; diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index b2be01ec637..78c723b8a76 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -931,7 +931,7 @@ an uncompressed page should be left as padding to avoid compression failures. This estimate is based on a self-adapting heuristic. */ struct zip_pad_info_t { SysMutex mutex; /*!< mutex protecting the info */ - Atomic_counter + Atomic_relaxed pad; /*!< number of bytes used as pad */ ulint success;/*!< successful compression ops during current round */ @@ -1107,10 +1107,10 @@ struct dict_index_t { /* @} */ private: /** R-tree split sequence number */ - Atomic_counter rtr_ssn; + Atomic_relaxed rtr_ssn; public: void set_ssn(node_seq_t ssn) { rtr_ssn= ssn; } - node_seq_t assign_ssn() { return ++rtr_ssn; } + node_seq_t assign_ssn() { return rtr_ssn.fetch_add(1) + 1; } node_seq_t ssn() const { return rtr_ssn; } rtr_info_track_t* diff --git a/storage/innobase/include/sync0rw.h b/storage/innobase/include/sync0rw.h index 6592988def8..48528eb4d30 100644 --- a/storage/innobase/include/sync0rw.h +++ b/storage/innobase/include/sync0rw.h @@ -569,10 +569,10 @@ struct rw_lock_t #endif /* UNIV_DEBUG */ { /** Holds the state of the lock. */ - Atomic_counter lock_word; + Atomic_relaxed lock_word; - /** 1: there are waiters */ - Atomic_counter waiters; + /** 0=no waiters, 1=waiters for X or SX lock exist */ + Atomic_relaxed waiters; /** number of granted SX locks. */ volatile ulint sx_recursive; diff --git a/storage/innobase/include/sync0rw.ic b/storage/innobase/include/sync0rw.ic index 2a7b008a532..70723b05944 100644 --- a/storage/innobase/include/sync0rw.ic +++ b/storage/innobase/include/sync0rw.ic @@ -355,16 +355,15 @@ rw_lock_s_unlock_func( ut_d(rw_lock_remove_debug_info(lock, pass, RW_LOCK_S)); /* Increment lock_word to indicate 1 less reader */ - int32_t lock_word = ++lock->lock_word; + int32_t lock_word = lock->lock_word.fetch_add(1); - if (lock_word == 0 || lock_word == -X_LOCK_HALF_DECR) { + if (lock_word == -1 || lock_word == -X_LOCK_HALF_DECR - 1) { /* wait_ex waiter exists. It may not be asleep, but we signal anyway. We do not wake other waiters, because they can't exist without wait_ex waiter and wait_ex waiter goes first.*/ os_event_set(lock->wait_ex_event); sync_array_object_signalled(); } else { - ut_ad(--lock_word); ut_ad(lock_word > -X_LOCK_DECR); ut_ad(lock_word < X_LOCK_DECR); } @@ -414,11 +413,11 @@ rw_lock_x_unlock_func( } else if (lock_word == -X_LOCK_DECR || lock_word == -(X_LOCK_DECR + X_LOCK_HALF_DECR)) { /* There are 2 x-locks */ - lock->lock_word += X_LOCK_DECR; + lock->lock_word.fetch_add(X_LOCK_DECR); } else { /* There are more than 2 x-locks. */ ut_ad(lock_word < -X_LOCK_DECR); - lock->lock_word++; + lock->lock_word.fetch_add(1); } ut_ad(rw_lock_validate(lock)); @@ -470,7 +469,7 @@ rw_lock_sx_unlock_func( /* still has x-lock */ ut_ad(lock_word == -X_LOCK_HALF_DECR || lock_word <= -(X_LOCK_DECR + X_LOCK_HALF_DECR)); - lock->lock_word += X_LOCK_HALF_DECR; + lock->lock_word.fetch_add(X_LOCK_HALF_DECR); } } diff --git a/storage/innobase/sync/sync0rw.cc b/storage/innobase/sync/sync0rw.cc index 22bebdb33da..fea94cc05f9 100644 --- a/storage/innobase/sync/sync0rw.cc +++ b/storage/innobase/sync/sync0rw.cc @@ -528,10 +528,10 @@ rw_lock_x_lock_low( exists. Add another. */ if (lock_word == 0 || lock_word == -X_LOCK_HALF_DECR) { - lock->lock_word -= X_LOCK_DECR; + lock->lock_word.fetch_sub(X_LOCK_DECR); } else { ut_ad(lock_word <= -X_LOCK_DECR); - lock->lock_word--; + lock->lock_word.fetch_sub(1); } }