diff options
58 files changed, 765 insertions, 440 deletions
diff --git a/client/mysql_upgrade.c b/client/mysql_upgrade.c index 7ba15b772b5..14a47bce352 100644 --- a/client/mysql_upgrade.c +++ b/client/mysql_upgrade.c @@ -166,8 +166,8 @@ static struct my_option my_long_options[]= "server with which it was built/distributed.", &opt_version_check, &opt_version_check, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, - {"write-binlog", OPT_WRITE_BINLOG, "All commands including those, " - "issued by mysqlcheck, are written to the binary log.", + {"write-binlog", OPT_WRITE_BINLOG, "All commands including those " + "issued by mysqlcheck are written to the binary log.", &opt_write_binlog, &opt_write_binlog, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} diff --git a/include/ma_dyncol.h b/include/ma_dyncol.h index cb03ccb5382..300474e061e 100644 --- a/include/ma_dyncol.h +++ b/include/ma_dyncol.h @@ -63,6 +63,12 @@ typedef struct st_mysql_lex_string LEX_STRING; /* NO and OK is the same used just to show semantics */ #define ER_DYNCOL_NO ER_DYNCOL_OK +#ifdef HAVE_CHARSET_utf8mb4 +#define DYNCOL_UTF (&my_charset_utf8mb4_general_ci) +#else +#define DYNCOL_UTF (&my_charset_utf8_general_ci) +#endif + enum enum_dyncol_func_result { ER_DYNCOL_OK= 0, diff --git a/man/mysql_upgrade.1 b/man/mysql_upgrade.1 index 876e224aa83..13b2a07cecb 100644 --- a/man/mysql_upgrade.1 +++ b/man/mysql_upgrade.1 @@ -691,8 +691,7 @@ it was built/distributed. Defaults to on; use \fB\-\-skip\-version\-check\fR to .sp Cause binary logging to be enabled while \fBmysql_upgrade\fR -runs\&. This is the default behavior; to disable binary logging during the upgrade, use the inverse of this option (that is, start the program with -\fB\-\-skip\-write\-binlog\fR)\&. +runs\&. .RE .SH "COPYRIGHT" .br diff --git a/mysql-test/include/kill_and_restart_mysqld.inc b/mysql-test/include/kill_and_restart_mysqld.inc deleted file mode 100644 index b67fb7350b4..00000000000 --- a/mysql-test/include/kill_and_restart_mysqld.inc +++ /dev/null @@ -1,15 +0,0 @@ -if (!$restart_parameters) -{ - let $restart_parameters = restart; -} - ---let $_server_id= `SELECT @@server_id` ---let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.$_server_id.expect - ---echo # Kill and $restart_parameters ---exec echo "$restart_parameters" > $_expect_file_name ---shutdown_server 0 ---source include/wait_until_disconnected.inc ---enable_reconnect ---source include/wait_until_connected_again.inc ---disable_reconnect diff --git a/mysql-test/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result index 7381c1c4004..dfe96d1a904 100644 --- a/mysql-test/r/ctype_utf8.result +++ b/mysql-test/r/ctype_utf8.result @@ -10272,6 +10272,21 @@ DROP TABLE allbytes; SET sql_mode = DEFAULT; # End of ctype_backslash.inc # +# MDEV-12681 Wrong VIEW results for CHAR(0xDF USING latin1) +# +SET NAMES utf8; +SELECT CHAR(0xDF USING latin1); +CHAR(0xDF USING latin1) +ß +CREATE OR REPLACE VIEW v1 AS SELECT CHAR(0xDF USING latin1) AS c; +SHOW CREATE VIEW v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select char(0xdf using latin1) AS `c` utf8 utf8_general_ci +SELECT * FROM v1; +c +ß +DROP VIEW v1; +# # End of 10.0 tests # # diff --git a/mysql-test/r/ctype_utf8mb4.result b/mysql-test/r/ctype_utf8mb4.result index cf3000baf6a..fbe95d9f44b 100644 --- a/mysql-test/r/ctype_utf8mb4.result +++ b/mysql-test/r/ctype_utf8mb4.result @@ -3430,6 +3430,32 @@ a b a 😁 b a ? b DROP TABLE t1; # +# MDEV-8949: COLUMN_CREATE unicode name breakage +# +SET NAMES utf8mb4; +SELECT COLUMN_JSON(COLUMN_CREATE(_utf8mb4 0xF09F988E, 1)); +COLUMN_JSON(COLUMN_CREATE(_utf8mb4 0xF09F988E, 1)) +{"😎":1} +SELECT COLUMN_LIST(COLUMN_CREATE(_utf8mb4 0xF09F988E, 1)); +COLUMN_LIST(COLUMN_CREATE(_utf8mb4 0xF09F988E, 1)) +`😎` +SELECT COLUMN_GET(COLUMN_CREATE(_utf8mb4 0xF09F988E, 1), _utf8mb4 0xF09F988E +as int); +COLUMN_GET(COLUMN_CREATE(_utf8mb4 0xF09F988E, 1), _utf8mb4 0xF09F988E +as int) +1 +CREATE TABLE t1 AS SELECT +COLUMN_LIST(COLUMN_CREATE('a',1)), +COLUMN_JSON(COLUMN_CREATE('b',1)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `COLUMN_LIST(COLUMN_CREATE('a',1))` longtext CHARACTER SET utf8mb4 DEFAULT NULL, + `COLUMN_JSON(COLUMN_CREATE('b',1))` longtext CHARACTER SET utf8mb4 DEFAULT NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +DROP TABLE t1; +SET NAMES default; +# # End of 10.0 tests # # diff --git a/mysql-test/r/func_str.result b/mysql-test/r/func_str.result index b1862d46613..da03ae6a18e 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -4734,6 +4734,27 @@ set global max_allowed_packet=default; # End of 5.6 tests # # +# Start of 10.0 tests +# +# +# MDEV-12681 Wrong VIEW results for CHAR(0xDF USING latin1) +# +EXPLAIN EXTENDED SELECT CHAR(0xDF USING latin1); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select char(0xdf using latin1) AS `CHAR(0xDF USING latin1)` +EXPLAIN EXTENDED SELECT CHAR(0xDF USING `binary`); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select char(0xdf) AS `CHAR(0xDF USING ``binary``)` +EXPLAIN EXTENDED SELECT CHAR(0xDF); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select char(0xdf) AS `CHAR(0xDF)` +# # Start of 10.1 tests # # @@ -4843,6 +4864,12 @@ YQ== 61 Yq== 62 DROP TABLE t1; # +# End of 10.1 tests +# +# +# Start of 10.3 tests +# +# # MDEV-12685 Oracle-compatible function CHR() # select chr(65); @@ -4859,12 +4886,6 @@ utf8 3 1 drop database mysqltest1; use test; # -# End of 10.1 tests -# -# -# Start of 10.3 tests -# -# # MDEV-12592 Illegal mix of collations with the HEX function # SET NAMES utf8; diff --git a/mysql-test/suite/innodb/r/recovery_shutdown.result b/mysql-test/suite/innodb/r/recovery_shutdown.result new file mode 100644 index 00000000000..7b6c9863b59 --- /dev/null +++ b/mysql-test/suite/innodb/r/recovery_shutdown.result @@ -0,0 +1,64 @@ +# +# MDEV-13797 InnoDB may hang if shutdown is initiated soon after startup +# while rolling back recovered incomplete transactions +# +CREATE TABLE t (a INT) ENGINE=InnoDB; +BEGIN; +COMMIT; +connect con$c,localhost,root,,; +CREATE TABLE t8 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB; +BEGIN; +INSERT INTO t8 (a) SELECT NULL FROM t; +UPDATE t8 SET a=a+100, b=a; +DELETE FROM t8; +connect con$c,localhost,root,,; +CREATE TABLE t7 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB; +BEGIN; +INSERT INTO t7 (a) SELECT NULL FROM t; +UPDATE t7 SET a=a+100, b=a; +DELETE FROM t7; +connect con$c,localhost,root,,; +CREATE TABLE t6 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB; +BEGIN; +INSERT INTO t6 (a) SELECT NULL FROM t; +UPDATE t6 SET a=a+100, b=a; +DELETE FROM t6; +connect con$c,localhost,root,,; +CREATE TABLE t5 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB; +BEGIN; +INSERT INTO t5 (a) SELECT NULL FROM t; +UPDATE t5 SET a=a+100, b=a; +DELETE FROM t5; +connect con$c,localhost,root,,; +CREATE TABLE t4 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB; +BEGIN; +INSERT INTO t4 (a) SELECT NULL FROM t; +UPDATE t4 SET a=a+100, b=a; +DELETE FROM t4; +connect con$c,localhost,root,,; +CREATE TABLE t3 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB; +BEGIN; +INSERT INTO t3 (a) SELECT NULL FROM t; +UPDATE t3 SET a=a+100, b=a; +DELETE FROM t3; +connect con$c,localhost,root,,; +CREATE TABLE t2 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB; +BEGIN; +INSERT INTO t2 (a) SELECT NULL FROM t; +UPDATE t2 SET a=a+100, b=a; +DELETE FROM t2; +connect con$c,localhost,root,,; +CREATE TABLE t1 (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB; +BEGIN; +INSERT INTO t1 (a) SELECT NULL FROM t; +UPDATE t1 SET a=a+100, b=a; +DELETE FROM t1; +INSERT INTO t1(a) SELECT NULL FROM t; +INSERT INTO t1(a) SELECT NULL FROM t1; +INSERT INTO t1(a) SELECT NULL FROM t1; +INSERT INTO t1(a) SELECT NULL FROM t1; +INSERT INTO t1(a) SELECT NULL FROM t1; +connection default; +SET GLOBAL innodb_flush_log_at_trx_commit=1; +CREATE TABLE u(a SERIAL) ENGINE=INNODB; +DROP TABLE t,u; diff --git a/mysql-test/suite/innodb/r/update_time.result b/mysql-test/suite/innodb/r/update_time.result index ab3eeb08dc7..d8b9069b1ae 100644 --- a/mysql-test/suite/innodb/r/update_time.result +++ b/mysql-test/suite/innodb/r/update_time.result @@ -43,7 +43,6 @@ XA PREPARE 'xatrx'; CONNECT con1,localhost,root,,; call mtr.add_suppression("Found 1 prepared XA transactions"); FLUSH TABLES; -# Kill and restart SELECT update_time FROM information_schema.tables WHERE table_name = 't'; update_time NULL diff --git a/mysql-test/suite/innodb/t/recovery_shutdown.test b/mysql-test/suite/innodb/t/recovery_shutdown.test new file mode 100644 index 00000000000..fbe26368e1b --- /dev/null +++ b/mysql-test/suite/innodb/t/recovery_shutdown.test @@ -0,0 +1,59 @@ +--source include/have_innodb.inc +--source include/not_embedded.inc + +--echo # +--echo # MDEV-13797 InnoDB may hang if shutdown is initiated soon after startup +--echo # while rolling back recovered incomplete transactions +--echo # + +CREATE TABLE t (a INT) ENGINE=InnoDB; +let $size = 100; +let $trx = 8; +let $c = $size; +BEGIN; +--disable_query_log +while ($c) { +INSERT INTO t VALUES(); +dec $c; +} +--enable_query_log +COMMIT; + +let $c = $trx; +while ($c) +{ +connect (con$c,localhost,root,,); +eval CREATE TABLE t$c (a SERIAL, b INT UNIQUE, c INT UNIQUE) ENGINE=InnoDB; +BEGIN; +eval INSERT INTO t$c (a) SELECT NULL FROM t; +eval UPDATE t$c SET a=a+$size, b=a; +eval DELETE FROM t$c; +dec $c; +} + +INSERT INTO t1(a) SELECT NULL FROM t; +INSERT INTO t1(a) SELECT NULL FROM t1; +INSERT INTO t1(a) SELECT NULL FROM t1; +INSERT INTO t1(a) SELECT NULL FROM t1; +INSERT INTO t1(a) SELECT NULL FROM t1; + +--connection default +SET GLOBAL innodb_flush_log_at_trx_commit=1; +CREATE TABLE u(a SERIAL) ENGINE=INNODB; + +--let $shutdown_timeout=0 +--source include/restart_mysqld.inc +--let $shutdown_timeout=60 +--source include/restart_mysqld.inc + +--disable_query_log +let $c = $trx; +while ($c) +{ +disconnect con$c; +eval DROP TABLE t$c; +dec $c; +} +--enable_query_log + +DROP TABLE t,u; diff --git a/mysql-test/suite/innodb/t/table_definition_cache_debug.opt b/mysql-test/suite/innodb/t/table_definition_cache_debug.opt index 6195e055dc8..6d341857397 100644 --- a/mysql-test/suite/innodb/t/table_definition_cache_debug.opt +++ b/mysql-test/suite/innodb/t/table_definition_cache_debug.opt @@ -1 +1 @@ ---innodb-open-files=13 +--innodb-open-files=20 diff --git a/mysql-test/suite/innodb/t/update_time.test b/mysql-test/suite/innodb/t/update_time.test index e2213c5d9b7..1a00e3b9f73 100644 --- a/mysql-test/suite/innodb/t/update_time.test +++ b/mysql-test/suite/innodb/t/update_time.test @@ -67,7 +67,8 @@ CONNECT (con1,localhost,root,,); call mtr.add_suppression("Found 1 prepared XA transactions"); FLUSH TABLES; ---source include/kill_and_restart_mysqld.inc +--let $shutdown_timeout=0 +--source include/restart_mysqld.inc SELECT update_time FROM information_schema.tables WHERE table_name = 't'; diff --git a/mysql-test/suite/innodb_gis/r/kill_server.result b/mysql-test/suite/innodb_gis/r/kill_server.result index 579317872b4..eac4feb442f 100644 --- a/mysql-test/suite/innodb_gis/r/kill_server.result +++ b/mysql-test/suite/innodb_gis/r/kill_server.result @@ -36,6 +36,5 @@ call mtr.add_suppression("InnoDB: A copy of page \[page id: space=[0-9]+, page n START TRANSACTION; CALL insert_t1(5000); COMMIT; -# Kill and restart drop procedure insert_t1; drop table t1; diff --git a/mysql-test/suite/innodb_gis/r/rtree_compress2.result b/mysql-test/suite/innodb_gis/r/rtree_compress2.result index 3522a1097aa..04940f2b3b1 100644 --- a/mysql-test/suite/innodb_gis/r/rtree_compress2.result +++ b/mysql-test/suite/innodb_gis/r/rtree_compress2.result @@ -20,6 +20,5 @@ call mtr.add_suppression("InnoDB: A copy of page \[page id: space=[0-9]+, page n START TRANSACTION; CALL insert_t1(5000); COMMIT; -# Kill and restart drop procedure insert_t1; drop table t1; diff --git a/mysql-test/suite/innodb_gis/r/rtree_crash.result b/mysql-test/suite/innodb_gis/r/rtree_crash.result deleted file mode 100644 index 12ca15a420d..00000000000 --- a/mysql-test/suite/innodb_gis/r/rtree_crash.result +++ /dev/null @@ -1,41 +0,0 @@ -create table t1 (c1 int, c2 geometry not null, spatial index (c2))engine=innodb; -create procedure insert_t1(IN total int) -begin -declare i int default 1; -while (i <= total) DO -insert into t1 values (i, Point(i, i)); -set i = i + 1; -end while; -end| -CALL insert_t1(5000); -select count(*) from t1; -count(*) -5000 -check table t1; -Table Op Msg_type Msg_text -test.t1 check status OK -truncate table t1; -CALL insert_t1(10000); -select count(*) from t1; -count(*) -10000 -drop index c2 on t1; -create spatial index idx on t1(c2); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `c1` int(11) DEFAULT NULL, - `c2` geometry NOT NULL, - SPATIAL KEY `idx` (`c2`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -check table t1; -Table Op Msg_type Msg_text -test.t1 check status OK -truncate table t1; -call mtr.add_suppression("InnoDB: page [0-9]+ in the doublewrite buffer is not within space bounds.*"); -START TRANSACTION; -CALL insert_t1(5000); -COMMIT; -# Kill and restart -drop procedure insert_t1; -drop table t1; diff --git a/mysql-test/suite/innodb_gis/r/rtree_recovery.result b/mysql-test/suite/innodb_gis/r/rtree_recovery.result index d2f4409f38c..3572e3e0bed 100644 --- a/mysql-test/suite/innodb_gis/r/rtree_recovery.result +++ b/mysql-test/suite/innodb_gis/r/rtree_recovery.result @@ -18,7 +18,6 @@ end while; end| CALL insert_t1(367); COMMIT; -# Kill and restart check table t1; Table Op Msg_type Msg_text test.t1 check status OK @@ -29,7 +28,6 @@ CALL update_t1(367); SET @poly1 = ST_GeomFromText('POLYGON((10000 10000, 10000 10350, 10350 10350, 10350 10000, 10000 10000))'); delete from t1 where ST_Contains(@poly1, c2); COMMIT; -# Kill and restart check table t1; Table Op Msg_type Msg_text test.t1 check status OK diff --git a/mysql-test/suite/innodb_gis/r/types.result b/mysql-test/suite/innodb_gis/r/types.result index ac1802085d6..7d823927df1 100644 --- a/mysql-test/suite/innodb_gis/r/types.result +++ b/mysql-test/suite/innodb_gis/r/types.result @@ -53,7 +53,6 @@ COMMIT; INSERT INTO t_wl6455 VALUES(11, POINT(11,11)); BEGIN; INSERT INTO t_wl6455 VALUES(1, POINT(1,1)); -# Kill and restart CHECK TABLE t_wl6455; Table Op Msg_type Msg_text test.t_wl6455 check status OK diff --git a/mysql-test/suite/innodb_gis/t/kill_server.test b/mysql-test/suite/innodb_gis/t/kill_server.test index 9b6469606ca..028bbbdd40d 100644 --- a/mysql-test/suite/innodb_gis/t/kill_server.test +++ b/mysql-test/suite/innodb_gis/t/kill_server.test @@ -58,7 +58,8 @@ CALL insert_t1(5000); COMMIT; ---source include/kill_and_restart_mysqld.inc +--let $shutdown_timeout=0 +--source include/restart_mysqld.inc # Clean up. drop procedure insert_t1; diff --git a/mysql-test/suite/innodb_gis/t/rtree_compress2.test b/mysql-test/suite/innodb_gis/t/rtree_compress2.test index 5956fc9a774..86ef1171833 100644 --- a/mysql-test/suite/innodb_gis/t/rtree_compress2.test +++ b/mysql-test/suite/innodb_gis/t/rtree_compress2.test @@ -51,7 +51,8 @@ CALL insert_t1(5000); COMMIT; ---source include/kill_and_restart_mysqld.inc +--let $shutdown_timeout=0 +--source include/restart_mysqld.inc # Clean up. drop procedure insert_t1; diff --git a/mysql-test/suite/innodb_gis/t/rtree_recovery.test b/mysql-test/suite/innodb_gis/t/rtree_recovery.test index 9e332c089f2..d995048ceac 100644 --- a/mysql-test/suite/innodb_gis/t/rtree_recovery.test +++ b/mysql-test/suite/innodb_gis/t/rtree_recovery.test @@ -44,7 +44,8 @@ delimiter ;| CALL insert_t1(367); COMMIT; ---source include/kill_and_restart_mysqld.inc +--let $shutdown_timeout=0 +--source include/restart_mysqld.inc # Check table. check table t1; @@ -63,7 +64,7 @@ SET @poly1 = ST_GeomFromText('POLYGON((10000 10000, 10000 10350, 10350 10350, 10 delete from t1 where ST_Contains(@poly1, c2); COMMIT; ---source include/kill_and_restart_mysqld.inc +--source include/restart_mysqld.inc # Check table. check table t1; diff --git a/mysql-test/suite/innodb_gis/t/types.test b/mysql-test/suite/innodb_gis/t/types.test index bd03e7b1dbe..65f65e5ae1f 100644 --- a/mysql-test/suite/innodb_gis/t/types.test +++ b/mysql-test/suite/innodb_gis/t/types.test @@ -73,7 +73,8 @@ INSERT INTO t_wl6455 VALUES(11, POINT(11,11)); BEGIN; INSERT INTO t_wl6455 VALUES(1, POINT(1,1)); ---source include/kill_and_restart_mysqld.inc +--let $shutdown_timeout=0 +--source include/restart_mysqld.inc CHECK TABLE t_wl6455; SELECT ST_AsText(g) FROM t_wl6455; diff --git a/mysql-test/suite/storage_engine/type_bit_indexes.result b/mysql-test/suite/storage_engine/type_bit_indexes.result index e7c0cf656c5..af8ddf7d6c9 100644 --- a/mysql-test/suite/storage_engine/type_bit_indexes.result +++ b/mysql-test/suite/storage_engine/type_bit_indexes.result @@ -69,7 +69,7 @@ INSERT INTO t1 (a,b,c,d) VALUES (1,0xFFFF,0xFFFFFFFF,0xFFFFFFFFFFFFFFFF); EXPLAIN SELECT HEX(b+c) FROM t1 WHERE c > 1 OR HEX(b) < 0xFFFFFF; id select_type table type possible_keys key key_len ref rows Extra -# # # # # NULL # # # # +# # # # # b_c # # # # SELECT HEX(b+c) FROM t1 WHERE c > 1 OR HEX(b) < 0xFFFFFF; HEX(b+c) 10 @@ -98,7 +98,7 @@ INSERT INTO t1 (a,b,c,d) VALUES (1,0xFFFF,0xFFFFFFFF,0xFFFFFFFFFFFFFFFF); EXPLAIN SELECT DISTINCT a+0 FROM t1 ORDER BY a; id select_type table type possible_keys key key_len ref rows Extra -# # # # # NULL # # # # +# # # # # a # # # # SELECT DISTINCT a+0 FROM t1 ORDER BY a; a+0 0 diff --git a/mysql-test/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test index adb846a0df2..b839042b91e 100644 --- a/mysql-test/t/ctype_utf8.test +++ b/mysql-test/t/ctype_utf8.test @@ -1879,6 +1879,18 @@ let $ctype_unescape_combinations=selected; --source include/ctype_unescape.inc --echo # +--echo # MDEV-12681 Wrong VIEW results for CHAR(0xDF USING latin1) +--echo # + +SET NAMES utf8; +SELECT CHAR(0xDF USING latin1); +CREATE OR REPLACE VIEW v1 AS SELECT CHAR(0xDF USING latin1) AS c; +SHOW CREATE VIEW v1; +SELECT * FROM v1; +DROP VIEW v1; + + +--echo # --echo # End of 10.0 tests --echo # diff --git a/mysql-test/t/ctype_utf8mb4.test b/mysql-test/t/ctype_utf8mb4.test index 94b5d77ad90..7aa644fe8a9 100644 --- a/mysql-test/t/ctype_utf8mb4.test +++ b/mysql-test/t/ctype_utf8mb4.test @@ -1923,6 +1923,24 @@ SELECT * FROM t1; DROP TABLE t1; --echo # +--echo # MDEV-8949: COLUMN_CREATE unicode name breakage +--echo # + +SET NAMES utf8mb4; +SELECT COLUMN_JSON(COLUMN_CREATE(_utf8mb4 0xF09F988E, 1)); +SELECT COLUMN_LIST(COLUMN_CREATE(_utf8mb4 0xF09F988E, 1)); +SELECT COLUMN_GET(COLUMN_CREATE(_utf8mb4 0xF09F988E, 1), _utf8mb4 0xF09F988E +as int); + +CREATE TABLE t1 AS SELECT + COLUMN_LIST(COLUMN_CREATE('a',1)), + COLUMN_JSON(COLUMN_CREATE('b',1)); +SHOW CREATE TABLE t1; +DROP TABLE t1; + +SET NAMES default; + +--echo # --echo # End of 10.0 tests --echo # diff --git a/mysql-test/t/func_str.test b/mysql-test/t/func_str.test index a75330ab981..dbb7502a3ec 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -1810,6 +1810,18 @@ set global max_allowed_packet=default; --echo # --echo # +--echo # Start of 10.0 tests +--echo # + +--echo # +--echo # MDEV-12681 Wrong VIEW results for CHAR(0xDF USING latin1) +--echo # + +EXPLAIN EXTENDED SELECT CHAR(0xDF USING latin1); +EXPLAIN EXTENDED SELECT CHAR(0xDF USING `binary`); +EXPLAIN EXTENDED SELECT CHAR(0xDF); + +--echo # --echo # Start of 10.1 tests --echo # @@ -1854,6 +1866,15 @@ SELECT f1,HEX(f2) FROM t1 WHERE f1='YQ==' AND (f2= from_base64( DROP TABLE t1; --echo # +--echo # End of 10.1 tests +--echo # + + +--echo # +--echo # Start of 10.3 tests +--echo # + +--echo # --echo # MDEV-12685 Oracle-compatible function CHR() --echo # select chr(65); @@ -1865,15 +1886,6 @@ drop database mysqltest1; use test; --echo # ---echo # End of 10.1 tests ---echo # - - ---echo # ---echo # Start of 10.3 tests ---echo # - ---echo # --echo # MDEV-12592 Illegal mix of collations with the HEX function --echo # diff --git a/mysys/ma_dyncol.c b/mysys/ma_dyncol.c index 125b3a4632d..3c15e373182 100644 --- a/mysys/ma_dyncol.c +++ b/mysys/ma_dyncol.c @@ -4183,8 +4183,7 @@ mariadb_dyncol_json_internal(DYNAMIC_COLUMN *str, DYNAMIC_STRING *json, } else { - if ((rc= mariadb_dyncol_val_str(json, &val, - &my_charset_utf8_general_ci, '"')) < 0) + if ((rc= mariadb_dyncol_val_str(json, &val, DYNCOL_UTF, '"')) < 0) goto err; } } diff --git a/mysys/thr_alarm.c b/mysys/thr_alarm.c index 9d917d3dd59..61ef3657161 100644 --- a/mysys/thr_alarm.c +++ b/mysys/thr_alarm.c @@ -273,7 +273,7 @@ void thr_end_alarm(thr_alarm_t *alarmed) /* Come here when some alarm in queue is due. Mark all alarms with are finnished in list. - Shedule alarms to be sent again after 1-10 sec (many alarms at once) + Schedule alarms to be sent again after 1-10 sec (many alarms at once) If alarm_aborted is set then all alarms are given and resent every second. */ @@ -425,7 +425,7 @@ void end_thr_alarm(my_bool free_structures) if (alarm_aborted != 1) /* If memory not freed */ { mysql_mutex_lock(&LOCK_alarm); - DBUG_PRINT("info",("Resheduling %d waiting alarms",alarm_queue.elements)); + DBUG_PRINT("info",("Rescheduling %d waiting alarms",alarm_queue.elements)); alarm_aborted= -1; /* mark aborted */ if (alarm_queue.elements || (alarm_thread_running && free_structures)) { diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 587dc14c529..d7429af76f7 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -6940,20 +6940,20 @@ longlong Item_func_dyncol_exists::val_int() null_value= 1; return 1; } - if (my_charset_same(nm->charset(), &my_charset_utf8_general_ci)) + if (my_charset_same(nm->charset(), DYNCOL_UTF)) { buf.str= (char *) nm->ptr(); buf.length= nm->length(); } else { - uint strlen= nm->length() * my_charset_utf8_general_ci.mbmaxlen + 1; + uint strlen= nm->length() * DYNCOL_UTF->mbmaxlen + 1; uint dummy_errors; buf.str= (char *) current_thd->alloc(strlen); if (buf.str) { buf.length= - copy_and_convert(buf.str, strlen, &my_charset_utf8_general_ci, + copy_and_convert(buf.str, strlen, DYNCOL_UTF, nm->ptr(), nm->length(), nm->charset(), &dummy_errors); } diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index d7e526906c6..b39fcb78fe9 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2918,6 +2918,20 @@ String *Item_func_make_set::val_str(String *str) } +void Item_func_char::print(String *str, enum_query_type query_type) +{ + str->append(Item_func_char::func_name()); + str->append('('); + print_args(str, 0, query_type); + if (collation.collation != &my_charset_bin) + { + str->append(C_STRING_WITH_LEN(" using ")); + str->append(collation.collation->csname); + } + str->append(')'); +} + + String *Item_func_char::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -4514,20 +4528,19 @@ bool Item_func_dyncol_create::prepare_arguments(THD *thd, bool force_names_arg) if (res) { // guaranty UTF-8 string for names - if (my_charset_same(res->charset(), &my_charset_utf8_general_ci)) + if (my_charset_same(res->charset(), DYNCOL_UTF)) { keys_str[i].length= res->length(); keys_str[i].str= thd->strmake(res->ptr(), res->length()); } else { - uint strlen= res->length() * my_charset_utf8_general_ci.mbmaxlen + 1; + uint strlen= res->length() * DYNCOL_UTF->mbmaxlen + 1; uint dummy_errors; - char *str= (char *) thd->alloc(strlen); - if (str) + if (char *str= (char *) thd->alloc(strlen)) { keys_str[i].length= - copy_and_convert(str, strlen, &my_charset_utf8_general_ci, + copy_and_convert(str, strlen, DYNCOL_UTF, res->ptr(), res->length(), res->charset(), &dummy_errors); keys_str[i].str= str; @@ -4747,9 +4760,10 @@ String *Item_func_dyncol_json::val_str(String *str) char *ptr; size_t length, alloc_length; dynstr_reassociate(&json, &ptr, &length, &alloc_length); - str->reset(ptr, length, alloc_length, &my_charset_utf8_general_ci); + str->reset(ptr, length, alloc_length, DYNCOL_UTF); null_value= FALSE; } + str->set_charset(DYNCOL_UTF); return str; null: @@ -4849,20 +4863,20 @@ bool Item_dyncol_get::get_dyn_value(THD *thd, DYNAMIC_COLUMN_VALUE *val, return 1; } - if (my_charset_same(nm->charset(), &my_charset_utf8_general_ci)) + if (my_charset_same(nm->charset(), DYNCOL_UTF)) { buf.str= (char *) nm->ptr(); buf.length= nm->length(); } else { - uint strlen= nm->length() * my_charset_utf8_general_ci.mbmaxlen + 1; + uint strlen= nm->length() * DYNCOL_UTF->mbmaxlen + 1; uint dummy_errors; buf.str= (char *) thd->alloc(strlen); if (buf.str) { buf.length= - copy_and_convert(buf.str, strlen, &my_charset_utf8_general_ci, + copy_and_convert(buf.str, strlen, DYNCOL_UTF, nm->ptr(), nm->length(), nm->charset(), &dummy_errors); } @@ -5286,7 +5300,6 @@ String *Item_func_dyncol_list::val_str(String *str) goto null; str->length(0); - str->set_charset(&my_charset_utf8_general_ci); for (i= 0; i < count; i++) { append_identifier(current_thd, str, names[i].str, names[i].length); @@ -5296,6 +5309,7 @@ String *Item_func_dyncol_list::val_str(String *str) null_value= FALSE; if (names) my_free(names); + str->set_charset(DYNCOL_UTF); return str; null: diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index fbcf69d00ce..49f82f7028c 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -978,6 +978,7 @@ public: max_length= arg_count * 4; } const char *func_name() const { return "char"; } + void print(String *str, enum_query_type query_type); Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_char>(thd, mem_root, this); } }; @@ -1585,14 +1586,14 @@ public: class Item_func_dyncol_json: public Item_str_func { public: - Item_func_dyncol_json(THD *thd, Item *str): Item_str_func(thd, str) {} + Item_func_dyncol_json(THD *thd, Item *str): Item_str_func(thd, str) + {collation.set(DYNCOL_UTF);} const char *func_name() const{ return "column_json"; } String *val_str(String *); void fix_length_and_dec() { max_length= MAX_BLOB_WIDTH; maybe_null= 1; - collation.set(&my_charset_bin); decimals= 0; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -1645,7 +1646,8 @@ public: class Item_func_dyncol_list: public Item_str_func { public: - Item_func_dyncol_list(THD *thd, Item *str): Item_str_func(thd, str) {}; + Item_func_dyncol_list(THD *thd, Item *str): Item_str_func(thd, str) + {collation.set(DYNCOL_UTF);} void fix_length_and_dec() { maybe_null= 1; max_length= MAX_BLOB_WIDTH; }; const char *func_name() const{ return "column_list"; } String *val_str(String *); diff --git a/storage/innobase/ha/ha0ha.cc b/storage/innobase/ha/ha0ha.cc index f620db6f62e..da542d4f742 100644 --- a/storage/innobase/ha/ha0ha.cc +++ b/storage/innobase/ha/ha0ha.cc @@ -489,62 +489,4 @@ ha_validate( return(ok); } #endif /* defined UNIV_AHI_DEBUG || defined UNIV_DEBUG */ - -/*************************************************************//** -Prints info of a hash table. */ -void -ha_print_info( -/*==========*/ - FILE* file, /*!< in: file where to print */ - hash_table_t* table) /*!< in: hash table */ -{ -#ifdef UNIV_DEBUG -/* Some of the code here is disabled for performance reasons in production -builds, see http://bugs.mysql.com/36941 */ -#define PRINT_USED_CELLS -#endif /* UNIV_DEBUG */ - -#ifdef PRINT_USED_CELLS - hash_cell_t* cell; - ulint cells = 0; - ulint i; -#endif /* PRINT_USED_CELLS */ - ulint n_bufs; - - ut_ad(table); - ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); -#ifdef PRINT_USED_CELLS - for (i = 0; i < hash_get_n_cells(table); i++) { - - cell = hash_get_nth_cell(table, i); - - if (cell->node) { - - cells++; - } - } -#endif /* PRINT_USED_CELLS */ - - fprintf(file, "Hash table size %lu", - (ulong) hash_get_n_cells(table)); - -#ifdef PRINT_USED_CELLS - fprintf(file, ", used cells %lu", (ulong) cells); -#endif /* PRINT_USED_CELLS */ - - if (table->heaps == NULL && table->heap != NULL) { - - /* This calculation is intended for the adaptive hash - index: how many buffer frames we have reserved? */ - - n_bufs = UT_LIST_GET_LEN(table->heap->base) - 1; - - if (table->heap->free_block) { - n_bufs++; - } - - fprintf(file, ", node heap has %lu buffer(s)\n", - (ulong) n_bufs); - } -} #endif /* BTR_CUR_HASH_ADAPT */ diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 3263260c0b3..b065155be2d 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -3864,8 +3864,7 @@ innobase_init( /* Currently, Galera does not support VATS lock schedule algorithm. */ if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS && global_system_variables.wsrep_on) { - ib::info() << "In Galera environment Variance-Aware-Transaction-Sheduling Algorithm" - " is not supported. Falling back to First-Come-First-Served order. "; + ib::info() << "For Galera, using innodb_lock_schedule_algorithm=fcfs"; innodb_lock_schedule_algorithm = INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS; } #endif /* WITH_WSREP */ diff --git a/storage/innobase/include/ha0ha.h b/storage/innobase/include/ha0ha.h index db53b6c6580..ca4cb0a5f8f 100644 --- a/storage/innobase/include/ha0ha.h +++ b/storage/innobase/include/ha0ha.h @@ -198,13 +198,6 @@ ha_validate( ulint start_index, /*!< in: start index */ ulint end_index); /*!< in: end index */ #endif /* defined UNIV_AHI_DEBUG || defined UNIV_DEBUG */ -/*************************************************************//** -Prints info of a hash table. */ -void -ha_print_info( -/*==========*/ - FILE* file, /*!< in: file where to print */ - hash_table_t* table); /*!< in: hash table */ /** The hash table external chain node */ struct ha_node_t { diff --git a/storage/innobase/include/que0que.h b/storage/innobase/include/que0que.h index 763b16820d8..13be7291f00 100644 --- a/storage/innobase/include/que0que.h +++ b/storage/innobase/include/que0que.h @@ -381,9 +381,6 @@ struct que_thr_t{ thrs; /*!< list of thread nodes of the fork node */ UT_LIST_NODE_T(que_thr_t) - trx_thrs; /*!< lists of threads in wait list of - the trx */ - UT_LIST_NODE_T(que_thr_t) queue; /*!< list of runnable thread nodes in the server task queue */ ulint fk_cascade_depth; /*!< maximum cascading call depth diff --git a/storage/innobase/include/trx0roll.h b/storage/innobase/include/trx0roll.h index 8908376bff1..f7b999ae70a 100644 --- a/storage/innobase/include/trx0roll.h +++ b/storage/innobase/include/trx0roll.h @@ -33,7 +33,8 @@ Created 3/26/1996 Heikki Tuuri #include "mtr0mtr.h" #include "trx0sys.h" -extern bool trx_rollback_or_clean_is_active; +extern bool trx_rollback_or_clean_is_active; +extern const trx_t* trx_roll_crash_recv_trx; /*******************************************************************//** Determines if this transaction is rolling back an incomplete transaction @@ -62,6 +63,10 @@ trx_undo_rec_t* trx_roll_pop_top_rec_of_trx(trx_t* trx, roll_ptr_t* roll_ptr, mem_heap_t* heap) MY_ATTRIBUTE((nonnull, warn_unused_result)); +/** Report progress when rolling back a row of a recovered transaction. +@return whether the rollback should be aborted due to pending shutdown */ +bool +trx_roll_must_shutdown(); /*******************************************************************//** Rollback or clean up any incomplete transactions which were encountered in crash recovery. If the transaction already was diff --git a/storage/innobase/row/row0quiesce.cc b/storage/innobase/row/row0quiesce.cc index dd6289c91e6..21cc67620f6 100644 --- a/storage/innobase/row/row0quiesce.cc +++ b/storage/innobase/row/row0quiesce.cc @@ -520,7 +520,7 @@ row_quiesce_table_start( ut_ad(fil_space_get(table->space) != NULL); ib::info() << "Sync to disk of " << table->name << " started."; - if (trx_purge_state() != PURGE_STATE_DISABLED) { + if (srv_undo_sources) { trx_purge_stop(); } @@ -603,7 +603,7 @@ row_quiesce_table_complete( ib::info() << "Deleting the meta-data file '" << cfg_name << "'"; } - if (trx_purge_state() != PURGE_STATE_DISABLED) { + if (srv_undo_sources) { trx_purge_run(); } diff --git a/storage/innobase/row/row0umod.cc b/storage/innobase/row/row0umod.cc index 2269711ffef..b13b770f0bd 100644 --- a/storage/innobase/row/row0umod.cc +++ b/storage/innobase/row/row0umod.cc @@ -1178,7 +1178,16 @@ close_table: if (!row_undo_search_clust_to_pcur(node)) { /* As long as this rolling-back transaction exists, the PRIMARY KEY value pointed to by the undo log - record must exist. But, it is possible that the record + record should exist. + + However, if InnoDB is killed during a rollback, or + shut down during the rollback of recovered + transactions, then after restart we may try to roll + back some of the same undo log records again, because + trx_roll_try_truncate() is not being invoked after + every undo log record. + + It is also possible that the record was not modified yet (the DB_ROLL_PTR does not match node->roll_ptr) and thus there is nothing to roll back. @@ -1186,8 +1195,11 @@ close_table: record after successfully acquiring an exclusive lock on the the clustered index record. That lock will not be released before the transaction is committed or - fully rolled back. */ - ut_ad(node->pcur.btr_cur.low_match == node->ref->n_fields); + fully rolled back. (Exception: if the server was + killed, restarted, and shut down again before the + rollback of the recovered transaction was completed, + it is possible that the transaction was partially + rolled back and locks released.) */ goto close_table; } diff --git a/storage/innobase/row/row0undo.cc b/storage/innobase/row/row0undo.cc index 5a0cd0dc985..252dfb4a6a6 100644 --- a/storage/innobase/row/row0undo.cc +++ b/storage/innobase/row/row0undo.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, 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 @@ -339,6 +340,13 @@ row_undo_step( ut_ad(que_node_get_type(node) == QUE_NODE_UNDO); + if (UNIV_UNLIKELY(trx == trx_roll_crash_recv_trx) + && trx_roll_must_shutdown()) { + /* Shutdown has been initiated. */ + trx->error_state = DB_INTERRUPTED; + return(NULL); + } + err = row_undo(node, thr); trx->error_state = err; diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index b6d9fbe635d..19be088912a 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -1323,9 +1323,28 @@ srv_printf_innodb_monitor( #ifdef BTR_CUR_HASH_ADAPT for (ulint i = 0; i < btr_ahi_parts; ++i) { - rw_lock_s_lock(btr_search_latches[i]); - ha_print_info(file, btr_search_sys->hash_tables[i]); - rw_lock_s_unlock(btr_search_latches[i]); + const hash_table_t* table = btr_search_sys->hash_tables[i]; + + ut_ad(table->magic_n == HASH_TABLE_MAGIC_N); + /* this is only used for buf_pool->page_hash */ + ut_ad(!table->heaps); + /* this is used for the adaptive hash index */ + ut_ad(table->heap); + + const mem_heap_t* heap = table->heap; + /* The heap may change during the following call, + so the data displayed may be garbage. We intentionally + avoid acquiring btr_search_latches[] so that the + diagnostic output will not stop here even in case another + thread hangs while holding btr_search_latches[]. + + This should be safe from crashes, because + table->heap will be pointing to the same object + for the full lifetime of the server. Even during + btr_search_disable() the heap will stay valid. */ + fprintf(file, "Hash table size " ULINTPF + ", node heap has " ULINTPF " buffer(s)\n", + table->n_cells, heap->base.count - !heap->free_block); } fprintf(file, @@ -2853,6 +2872,9 @@ DECLARE_THREAD(srv_purge_coordinator_thread)( purge_sys->running = false; + /* Ensure that the wait in trx_purge_stop() will terminate. */ + os_event_set(purge_sys->event); + rw_lock_x_unlock(&purge_sys->latch); #ifdef UNIV_DEBUG_THREAD_CREATION diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc index d776595568b..886d47e44c0 100644 --- a/storage/innobase/srv/srv0start.cc +++ b/storage/innobase/srv/srv0start.cc @@ -2633,8 +2633,6 @@ files_checked: trx_temp_rseg_create(); } - srv_is_being_started = false; - ut_a(trx_purge_state() == PURGE_STATE_INIT); /* Create the master thread which does purge and other utility @@ -2683,6 +2681,8 @@ files_checked: purge_sys->state = PURGE_STATE_DISABLED; } + srv_is_being_started = false; + if (!srv_read_only_mode) { /* wake main loop of page cleaner up */ os_event_set(buf_flush_event); diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc index 61c36637a4e..4fd9333c0ba 100644 --- a/storage/innobase/trx/trx0purge.cc +++ b/storage/innobase/trx/trx0purge.cc @@ -1770,52 +1770,48 @@ void trx_purge_stop(void) /*================*/ { - ut_a(srv_n_purge_threads > 0); - rw_lock_x_lock(&purge_sys->latch); - const int64_t sig_count = os_event_reset(purge_sys->event); - const purge_state_t state = purge_sys->state; - - ut_a(state == PURGE_STATE_RUN || state == PURGE_STATE_STOP); - - ++purge_sys->n_stop; - - if (state == PURGE_STATE_RUN) { + switch (purge_sys->state) { + case PURGE_STATE_INIT: + case PURGE_STATE_DISABLED: + ut_error; + case PURGE_STATE_EXIT: + /* Shutdown must have been initiated during + FLUSH TABLES FOR EXPORT. */ + ut_ad(!srv_undo_sources); +unlock: + rw_lock_x_unlock(&purge_sys->latch); + break; + case PURGE_STATE_STOP: + ut_ad(srv_n_purge_threads > 0); + ++purge_sys->n_stop; + purge_sys->state = PURGE_STATE_STOP; + if (!purge_sys->running) { + goto unlock; + } + ib::info() << "Waiting for purge to stop"; + do { + rw_lock_x_unlock(&purge_sys->latch); + os_thread_sleep(10000); + rw_lock_x_lock(&purge_sys->latch); + } while (purge_sys->running); + goto unlock; + case PURGE_STATE_RUN: + ut_ad(srv_n_purge_threads > 0); + ++purge_sys->n_stop; ib::info() << "Stopping purge"; /* We need to wakeup the purge thread in case it is suspended, so that it can acknowledge the state change. */ + const int64_t sig_count = os_event_reset(purge_sys->event); + purge_sys->state = PURGE_STATE_STOP; srv_purge_wakeup(); - } - - purge_sys->state = PURGE_STATE_STOP; - - if (state != PURGE_STATE_STOP) { rw_lock_x_unlock(&purge_sys->latch); /* Wait for purge coordinator to signal that it is suspended. */ os_event_wait_low(purge_sys->event, sig_count); - } else { - bool once = true; - - /* Wait for purge to signal that it has actually stopped. */ - while (purge_sys->running) { - - if (once) { - ib::info() << "Waiting for purge to stop"; - once = false; - } - - rw_lock_x_unlock(&purge_sys->latch); - - os_thread_sleep(10000); - - rw_lock_x_lock(&purge_sys->latch); - } - - rw_lock_x_unlock(&purge_sys->latch); } MONITOR_INC_VALUE(MONITOR_PURGE_STOP_COUNT, 1); @@ -1830,8 +1826,12 @@ trx_purge_run(void) rw_lock_x_lock(&purge_sys->latch); switch (purge_sys->state) { - case PURGE_STATE_INIT: case PURGE_STATE_EXIT: + /* Shutdown must have been initiated during + FLUSH TABLES FOR EXPORT. */ + ut_ad(!srv_undo_sources); + break; + case PURGE_STATE_INIT: case PURGE_STATE_DISABLED: ut_error; diff --git a/storage/innobase/trx/trx0roll.cc b/storage/innobase/trx/trx0roll.cc index d6857b892da..c9c77acba11 100644 --- a/storage/innobase/trx/trx0roll.cc +++ b/storage/innobase/trx/trx0roll.cc @@ -24,8 +24,10 @@ Transaction rollback Created 3/26/1996 Heikki Tuuri *******************************************************/ -#include "ha_prototypes.h" +#include "my_config.h" +#include <my_systemd.h> +#include "ha_prototypes.h" #include "trx0roll.h" #include <mysql/service_wsrep.h> @@ -56,14 +58,7 @@ static const ulint TRX_ROLL_TRUNC_THRESHOLD = 1; bool trx_rollback_or_clean_is_active; /** In crash recovery, the current trx to be rolled back; NULL otherwise */ -static const trx_t* trx_roll_crash_recv_trx = NULL; - -/** In crash recovery we set this to the undo n:o of the current trx to be -rolled back. Then we can print how many % the rollback has progressed. */ -static undo_no_t trx_roll_max_undo_no; - -/** Auxiliary variable which tells the previous progress % we printed */ -static ulint trx_roll_progress_printed_pct; +const trx_t* trx_roll_crash_recv_trx; /****************************************************************//** Finishes a transaction rollback. */ @@ -631,8 +626,6 @@ trx_rollback_active( que_thr_t* thr; roll_node_t* roll_node; dict_table_t* table; - int64_t rows_to_undo; - const char* unit = ""; ibool dictionary_locked = FALSE; heap = mem_heap_create(512); @@ -651,28 +644,8 @@ trx_rollback_active( ut_a(thr == que_fork_start_command(fork)); - trx_sys_mutex_enter(); - trx_roll_crash_recv_trx = trx; - trx_roll_max_undo_no = trx->undo_no; - - trx_roll_progress_printed_pct = 0; - - rows_to_undo = trx_roll_max_undo_no; - - trx_sys_mutex_exit(); - - if (rows_to_undo > 1000000000) { - rows_to_undo = rows_to_undo / 1000000; - unit = "M"; - } - - const trx_id_t trx_id = trx_get_id_for_print(trx); - - ib::info() << "Rolling back trx with id " << trx_id << ", " - << rows_to_undo << unit << " rows to undo"; - if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) { row_mysql_lock_data_dictionary(trx); dictionary_locked = TRUE; @@ -683,6 +656,17 @@ trx_rollback_active( que_run_threads(roll_node->undo_thr); + if (trx->error_state != DB_SUCCESS) { + ut_ad(trx->error_state == DB_INTERRUPTED); + ut_ad(!srv_is_being_started); + ut_ad(!srv_undo_sources); + ut_ad(srv_fast_shutdown); + ut_ad(!dictionary_locked); + que_graph_free(static_cast<que_t*>( + roll_node->undo_thr->common.parent)); + goto func_exit; + } + trx_rollback_finish(thr_get_trx(roll_node->undo_thr)); /* Free the memory reserved by the undo graph */ @@ -714,11 +698,13 @@ trx_rollback_active( } } +func_exit: if (dictionary_locked) { row_mysql_unlock_data_dictionary(trx); } - ib::info() << "Rollback of trx with id " << trx_id << " completed"; + ib::info() << "Rollback of trx with id " << ib::hex(trx->id) + << " completed"; mem_heap_free(heap); @@ -736,7 +722,7 @@ ibool trx_rollback_resurrected( /*=====================*/ trx_t* trx, /*!< in: transaction to rollback or clean */ - ibool all) /*!< in: FALSE=roll back dictionary transactions; + ibool* all) /*!< in/out: FALSE=roll back dictionary transactions; TRUE=roll back all non-PREPARED transactions */ { ut_ad(trx_sys_mutex_own()); @@ -747,40 +733,102 @@ trx_rollback_resurrected( to accidentally clean up a non-recovered transaction here. */ trx_mutex_enter(trx); - bool is_recovered = trx->is_recovered; - trx_state_t state = trx->state; - trx_mutex_exit(trx); - - if (!is_recovered) { + if (!trx->is_recovered) { +func_exit: + trx_mutex_exit(trx); return(FALSE); } - switch (state) { + switch (trx->state) { case TRX_STATE_COMMITTED_IN_MEMORY: + trx_mutex_exit(trx); trx_sys_mutex_exit(); - ib::info() << "Cleaning up trx with id " - << trx_get_id_for_print(trx); + ib::info() << "Cleaning up trx with id " << ib::hex(trx->id); trx_cleanup_at_db_startup(trx); trx_free_resurrected(trx); return(TRUE); case TRX_STATE_ACTIVE: - if (all || trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) { + if (!srv_is_being_started + && !srv_undo_sources && srv_fast_shutdown) { +fake_prepared: + trx->state = TRX_STATE_PREPARED; + trx_sys->n_prepared_trx++; + trx_sys->n_prepared_recovered_trx++; + *all = FALSE; + goto func_exit; + } + trx_mutex_exit(trx); + + if (*all || trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) { trx_sys_mutex_exit(); trx_rollback_active(trx); + if (trx->error_state != DB_SUCCESS) { + ut_ad(trx->error_state == DB_INTERRUPTED); + trx->error_state = DB_SUCCESS; + ut_ad(!srv_undo_sources); + ut_ad(srv_fast_shutdown); + mutex_enter(&trx_sys->mutex); + trx_mutex_enter(trx); + goto fake_prepared; + } trx_free_for_background(trx); return(TRUE); } return(FALSE); case TRX_STATE_PREPARED: - return(FALSE); + goto func_exit; case TRX_STATE_NOT_STARTED: case TRX_STATE_FORCED_ROLLBACK: break; } ut_error; - return(FALSE); + goto func_exit; +} + +/** Report progress when rolling back a row of a recovered transaction. +@return whether the rollback should be aborted due to pending shutdown */ +bool +trx_roll_must_shutdown() +{ + const trx_t* trx = trx_roll_crash_recv_trx; + ut_ad(trx); + ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE)); + ut_ad(trx->in_rollback); + + if (trx_get_dict_operation(trx) == TRX_DICT_OP_NONE + && !srv_is_being_started + && !srv_undo_sources && srv_fast_shutdown) { + return true; + } + + ib_time_t time = ut_time(); + mutex_enter(&trx_sys->mutex); + mutex_enter(&recv_sys->mutex); + + if (recv_sys->report(time)) { + ulint n_trx = 0, n_rows = 0; + for (const trx_t* t = UT_LIST_GET_FIRST(trx_sys->rw_trx_list); + t != NULL; + t = UT_LIST_GET_NEXT(trx_list, t)) { + + assert_trx_in_rw_list(t); + if (t->is_recovered + && trx_state_eq(t, TRX_STATE_ACTIVE)) { + n_trx++; + n_rows += t->undo_no; + } + } + ib::info() << "To roll back: " << n_trx << " transactions, " + << n_rows << " rows"; + sd_notifyf(0, "STATUS=To roll back: " ULINTPF " transactions, " + ULINTPF " rows", n_trx, n_rows); + } + + mutex_exit(&recv_sys->mutex); + mutex_exit(&trx_sys->mutex); + return false; } /*******************************************************************//** @@ -825,17 +873,11 @@ trx_rollback_or_clean_recovered( assert_trx_in_rw_list(trx); - if (srv_shutdown_state != SRV_SHUTDOWN_NONE - && srv_fast_shutdown != 0) { - all = FALSE; - break; - } - /* If this function does a cleanup or rollback then it will release the trx_sys->mutex, therefore we need to reacquire it before retrying the loop. */ - if (trx_rollback_resurrected(trx, all)) { + if (trx_rollback_resurrected(trx, &all)) { trx_sys_mutex_enter(); @@ -1042,27 +1084,6 @@ trx_roll_pop_top_rec_of_trx(trx_t* trx, roll_ptr_t* roll_ptr, mem_heap_t* heap) ut_ad(trx_roll_check_undo_rec_ordering( undo_no, undo->rseg->space, trx)); - /* We print rollback progress info if we are in a crash recovery - and the transaction has at least 1000 row operations to undo. */ - - if (trx == trx_roll_crash_recv_trx && trx_roll_max_undo_no > 1000) { - - ulint progress_pct = 100 - (ulint) - ((undo_no * 100) / trx_roll_max_undo_no); - if (progress_pct != trx_roll_progress_printed_pct) { - if (trx_roll_progress_printed_pct == 0) { - fprintf(stderr, - "\nInnoDB: Progress in percents:" - " %lu", (ulong) progress_pct); - } else { - fprintf(stderr, - " %lu", (ulong) progress_pct); - } - fflush(stderr); - trx_roll_progress_printed_pct = progress_pct; - } - } - trx->undo_no = undo_no; trx->undo_rseg_space = undo->rseg->space; mutex_exit(&trx->undo_mutex); diff --git a/storage/innobase/trx/trx0undo.cc b/storage/innobase/trx/trx0undo.cc index 5e6b67007ad..11df7011027 100644 --- a/storage/innobase/trx/trx0undo.cc +++ b/storage/innobase/trx/trx0undo.cc @@ -1826,10 +1826,14 @@ trx_undo_free_prepared( /* fall through */ case TRX_UNDO_ACTIVE: /* lock_trx_release_locks() assigns - trx->is_recovered=false */ + trx->is_recovered=false and + trx->state = TRX_STATE_COMMITTED_IN_MEMORY, + also for transactions that we faked + to TRX_STATE_PREPARED in trx_rollback_resurrected(). */ ut_a(!srv_was_started || srv_read_only_mode - || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); + || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO + || srv_fast_shutdown); break; default: ut_error; @@ -1854,10 +1858,14 @@ trx_undo_free_prepared( /* fall through */ case TRX_UNDO_ACTIVE: /* lock_trx_release_locks() assigns - trx->is_recovered=false */ + trx->is_recovered=false and + trx->state = TRX_STATE_COMMITTED_IN_MEMORY, + also for transactions that we faked + to TRX_STATE_PREPARED in trx_rollback_resurrected(). */ ut_a(!srv_was_started || srv_read_only_mode - || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); + || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO + || srv_fast_shutdown); break; default: ut_error; diff --git a/storage/maria/ma_loghandler.c b/storage/maria/ma_loghandler.c index bc30adad43a..e30ddb756a6 100644 --- a/storage/maria/ma_loghandler.c +++ b/storage/maria/ma_loghandler.c @@ -6342,7 +6342,6 @@ my_bool translog_write_record(LSN *lsn, short_trid, &parts, trn, hook_arg); break; case LOGRECTYPE_NOT_ALLOWED: - DBUG_ASSERT(0); default: DBUG_ASSERT(0); rc= 1; diff --git a/storage/rocksdb/mysql-test/storage_engine/disabled.def b/storage/rocksdb/mysql-test/storage_engine/disabled.def index 0643b2052e2..930e1d82b87 100644 --- a/storage/rocksdb/mysql-test/storage_engine/disabled.def +++ b/storage/rocksdb/mysql-test/storage_engine/disabled.def @@ -14,6 +14,7 @@ lock_concurrent : MDEV-13148 - LOCK TABLE on RocksDB table fails with a bog optimize_table : MDEV-13148 - LOCK TABLE on RocksDB table fails with a bogus error message repair_table : MDEV-13148 - LOCK TABLE on RocksDB table fails with a bogus error message select_high_prio : Not supported +show_engine : SHOW ENGINE produces different number of lines depending on previous tests show_table_status : MDEV-13152 - Indeterministic row number in SHOW TABLE STATUS on RocksDB table tbl_opt_data_dir : Not supported tbl_opt_index_dir : Not supported diff --git a/storage/rocksdb/mysql-test/storage_engine/type_bit_indexes.rdiff b/storage/rocksdb/mysql-test/storage_engine/type_bit_indexes.rdiff new file mode 100644 index 00000000000..e53a33b4fba --- /dev/null +++ b/storage/rocksdb/mysql-test/storage_engine/type_bit_indexes.rdiff @@ -0,0 +1,20 @@ +--- suite/storage_engine/type_bit_indexes.result 2017-12-12 20:34:34.000000000 +0200 ++++ suite/storage_engine/type_bit_indexes.reject 2017-12-12 20:35:24.539330056 +0200 +@@ -69,7 +69,7 @@ + (1,0xFFFF,0xFFFFFFFF,0xFFFFFFFFFFFFFFFF); + EXPLAIN SELECT HEX(b+c) FROM t1 WHERE c > 1 OR HEX(b) < 0xFFFFFF; + id select_type table type possible_keys key key_len ref rows Extra +-# # # # # b_c # # # # ++# # # # # NULL # # # # + SELECT HEX(b+c) FROM t1 WHERE c > 1 OR HEX(b) < 0xFFFFFF; + HEX(b+c) + 10 +@@ -98,7 +98,7 @@ + (1,0xFFFF,0xFFFFFFFF,0xFFFFFFFFFFFFFFFF); + EXPLAIN SELECT DISTINCT a+0 FROM t1 ORDER BY a; + id select_type table type possible_keys key key_len ref rows Extra +-# # # # # a # # # # ++# # # # # NULL # # # # + SELECT DISTINCT a+0 FROM t1 ORDER BY a; + a+0 + 0 diff --git a/storage/rocksdb/mysql-test/storage_engine/type_enum_indexes.rdiff b/storage/rocksdb/mysql-test/storage_engine/type_enum_indexes.rdiff new file mode 100644 index 00000000000..be83fb6e212 --- /dev/null +++ b/storage/rocksdb/mysql-test/storage_engine/type_enum_indexes.rdiff @@ -0,0 +1,11 @@ +--- suite/storage_engine/type_enum_indexes.result 2017-03-12 04:38:50.000000000 +0200 ++++ suite/storage_engine/type_enum_indexes.reject 2017-12-12 20:36:47.455331726 +0200 +@@ -30,7 +30,7 @@ + t1 0 a_b 2 b # # NULL NULL # # + EXPLAIN SELECT a FROM t1 WHERE b > 'test2' ORDER BY a; + id select_type table type possible_keys key key_len ref rows Extra +-# # # # # a_b # # # # ++# # # # # NULL # # # # + SELECT a FROM t1 WHERE b > 'test2' ORDER BY a; + a + Africa diff --git a/storage/rocksdb/mysql-test/storage_engine/type_set_indexes.rdiff b/storage/rocksdb/mysql-test/storage_engine/type_set_indexes.rdiff new file mode 100644 index 00000000000..2703e81b745 --- /dev/null +++ b/storage/rocksdb/mysql-test/storage_engine/type_set_indexes.rdiff @@ -0,0 +1,20 @@ +--- suite/storage_engine/type_set_indexes.result 2017-03-12 04:38:50.000000000 +0200 ++++ suite/storage_engine/type_set_indexes.reject 2017-12-12 20:37:16.187332305 +0200 +@@ -97,7 +97,7 @@ + Warning 1265 Data truncated for column 'b' at row 7 + EXPLAIN SELECT a FROM t1 WHERE FIND_IN_SET('Europe',a) > 0; + id select_type table type possible_keys key key_len ref rows Extra +-# # # # # a # # # # ++# # # # # NULL # # # # + SELECT a FROM t1 WHERE FIND_IN_SET('Europe',a) > 0; + a + Africa,Europe,Asia +@@ -124,7 +124,7 @@ + Warning 1265 Data truncated for column 'b' at row 7 + EXPLAIN SELECT DISTINCT a, b FROM t1 ORDER BY b DESC, a; + id select_type table type possible_keys key key_len ref rows Extra +-# # # # # b_a # # # # ++# # # # # NULL # # # # + SELECT DISTINCT a, b FROM t1 ORDER BY b DESC, a; + a b + test1,test3 diff --git a/storage/tokudb/mysql-test/rpl/r/rpl_tokudb_row_log.result b/storage/tokudb/mysql-test/rpl/r/rpl_tokudb_row_log.result index 73c010c6eb7..ab33725fa3f 100644 --- a/storage/tokudb/mysql-test/rpl/r/rpl_tokudb_row_log.result +++ b/storage/tokudb/mysql-test/rpl/r/rpl_tokudb_row_log.result @@ -226,7 +226,6 @@ master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Rotate # # master-bin.000002;pos=POS include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000002 # Binlog_checkpoint # # master-bin.000002 master-bin.000002 # Gtid # # GTID #-#-# master-bin.000002 # Query # # use `test`; create table t3 (a int)ENGINE=TokuDB master-bin.000002 # Gtid # # GTID #-#-# @@ -268,7 +267,6 @@ slave-bin.000001 # Query # # use `test`; create table t3 (a int)ENGINE=TokuDB slave-bin.000001 # Rotate # # slave-bin.000002;pos=POS include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info -slave-bin.000002 # Binlog_checkpoint # # slave-bin.000002 slave-bin.000002 # Gtid # # GTID #-#-# slave-bin.000002 # Query # # use `test`; create table t2 (n int)ENGINE=TokuDB slave-bin.000002 # Gtid # # BEGIN GTID #-#-# diff --git a/storage/tokudb/mysql-test/rpl/r/rpl_tokudb_stm_log.result b/storage/tokudb/mysql-test/rpl/r/rpl_tokudb_stm_log.result index 83335b0237c..652ef18c039 100644 --- a/storage/tokudb/mysql-test/rpl/r/rpl_tokudb_stm_log.result +++ b/storage/tokudb/mysql-test/rpl/r/rpl_tokudb_stm_log.result @@ -222,7 +222,6 @@ master-bin.000001 # Xid # # COMMIT /* XID */ master-bin.000001 # Rotate # # master-bin.000002;pos=POS include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000002 # Binlog_checkpoint # # master-bin.000002 master-bin.000002 # Gtid # # GTID #-#-# master-bin.000002 # Query # # use `test`; create table t3 (a int)ENGINE=TokuDB master-bin.000002 # Gtid # # GTID #-#-# @@ -260,7 +259,6 @@ slave-bin.000001 # Query # # use `test`; create table t3 (a int)ENGINE=TokuDB slave-bin.000001 # Rotate # # slave-bin.000002;pos=POS include/show_binlog_events.inc Log_name Pos Event_type Server_id End_log_pos Info -slave-bin.000002 # Binlog_checkpoint # # slave-bin.000002 slave-bin.000002 # Gtid # # GTID #-#-# slave-bin.000002 # Query # # use `test`; create table t2 (n int)ENGINE=TokuDB slave-bin.000002 # Gtid # # BEGIN GTID #-#-# diff --git a/storage/xtradb/handler/ha_innodb.cc b/storage/xtradb/handler/ha_innodb.cc index 987e86c3927..f4184d48c32 100644 --- a/storage/xtradb/handler/ha_innodb.cc +++ b/storage/xtradb/handler/ha_innodb.cc @@ -3903,6 +3903,17 @@ innobase_init( } } +#ifdef WITH_WSREP + /* Currently, Galera does not support VATS lock schedule algorithm. */ + if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS + && global_system_variables.wsrep_on) { + /* Do not allow InnoDB startup with VATS and Galera */ + sql_print_error("In Galera, innodb_lock_schedule_algorithm=vats" + " is not supported."); + goto error; + } +#endif /* WITH_WSREP */ + #ifndef HAVE_LZ4 if (innodb_compression_algorithm == PAGE_LZ4_ALGORITHM) { sql_print_error("InnoDB: innodb_compression_algorithm = %lu unsupported.\n" @@ -5454,8 +5465,8 @@ innobase_kill_connection( wsrep_thd_is_BF(current_thd, FALSE), lock_get_info(trx->lock.wait_lock).c_str()); - if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE) && - trx->abort_type == TRX_SERVER_ABORT) { + if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE) + && trx->abort_type == TRX_SERVER_ABORT) { ut_ad(!lock_mutex_own()); lock_mutex_enter(); } @@ -20463,7 +20474,7 @@ static MYSQL_SYSVAR_ENUM(empty_free_list_algorithm, &innodb_empty_free_list_algorithm_typelib); static MYSQL_SYSVAR_ENUM(lock_schedule_algorithm, innodb_lock_schedule_algorithm, - PLUGIN_VAR_RQCMDARG, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, "The algorithm Innodb uses for deciding which locks to grant next when" " a lock is released. Possible values are" " FCFS" diff --git a/storage/xtradb/include/que0que.h b/storage/xtradb/include/que0que.h index e5b2a1ba3fc..005f28d2af1 100644 --- a/storage/xtradb/include/que0que.h +++ b/storage/xtradb/include/que0que.h @@ -385,9 +385,6 @@ struct que_thr_t{ thrs; /*!< list of thread nodes of the fork node */ UT_LIST_NODE_T(que_thr_t) - trx_thrs; /*!< lists of threads in wait list of - the trx */ - UT_LIST_NODE_T(que_thr_t) queue; /*!< list of runnable thread nodes in the server task queue */ ulint fk_cascade_depth; /*!< maximum cascading call depth diff --git a/storage/xtradb/include/trx0roll.h b/storage/xtradb/include/trx0roll.h index b2e9d8a077f..565079b17b4 100644 --- a/storage/xtradb/include/trx0roll.h +++ b/storage/xtradb/include/trx0roll.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, 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 @@ -33,7 +34,8 @@ Created 3/26/1996 Heikki Tuuri #include "mtr0mtr.h" #include "trx0sys.h" -extern bool trx_rollback_or_clean_is_active; +extern bool trx_rollback_or_clean_is_active; +extern const trx_t* trx_roll_crash_recv_trx; /*******************************************************************//** Determines if this transaction is rolling back an incomplete transaction @@ -104,6 +106,11 @@ trx_undo_rec_release( /*=================*/ trx_t* trx, /*!< in/out: transaction */ undo_no_t undo_no);/*!< in: undo number */ +/** Report progress when rolling back a row of a recovered transaction. +@return whether the rollback should be aborted due to pending shutdown */ +UNIV_INTERN +bool +trx_roll_must_shutdown(); /*******************************************************************//** Rollback or clean up any incomplete transactions which were encountered in crash recovery. If the transaction already was diff --git a/storage/xtradb/lock/lock0lock.cc b/storage/xtradb/lock/lock0lock.cc index ddaeff69f10..20e3f5adeb7 100644 --- a/storage/xtradb/lock/lock0lock.cc +++ b/storage/xtradb/lock/lock0lock.cc @@ -937,14 +937,21 @@ lock_reset_lock_and_trx_wait( ib_logf(IB_LOG_LEVEL_INFO, "Trx id " TRX_ID_FMT - " is waiting a lock in statement %s" + " is waiting a lock " " for this trx id " TRX_ID_FMT - " and statement %s wait_lock %p", + " wait_lock %p", lock->trx->id, - stmt ? stmt : "NULL", trx_id, - stmt2 ? stmt2 : "NULL", lock->trx->lock.wait_lock); + + if (stmt) { + ib_logf(IB_LOG_LEVEL_INFO, " SQL1: %s\n", stmt); + } + + if (stmt2) { + ib_logf(IB_LOG_LEVEL_INFO, " SQL2: %s\n", stmt2); + } + ut_ad(lock->trx->lock.wait_lock == lock); } @@ -1162,7 +1169,7 @@ lock_rec_has_to_wait( type_mode, lock_is_on_supremum); fprintf(stderr, "conflicts states: my %d locked %d\n", - wsrep_thd_conflict_state(trx->mysql_thd, FALSE), + wsrep_thd_conflict_state(trx->mysql_thd, FALSE), wsrep_thd_conflict_state(lock2->trx->mysql_thd, FALSE) ); lock_rec_print(stderr, lock2); if (for_locking) return FALSE; @@ -1714,7 +1721,7 @@ lock_rec_other_has_expl_req( ulint heap_no,/*!< in: heap number of the record */ trx_id_t trx_id) /*!< in: transaction */ { - const lock_t* lock; + lock_t* lock; ut_ad(lock_mutex_own()); ut_ad(mode == LOCK_X || mode == LOCK_S); @@ -1723,7 +1730,7 @@ lock_rec_other_has_expl_req( for (lock = lock_rec_get_first(block, heap_no); lock != NULL; - lock = lock_rec_get_next_const(heap_no, lock)) { + lock = lock_rec_get_next(heap_no, lock)) { if (lock->trx->id != trx_id && (gap @@ -1810,7 +1817,7 @@ Checks if some other transaction has a conflicting explicit lock request in the queue, so that we have to wait. @return lock or NULL */ static -const lock_t* +lock_t* lock_rec_other_has_conflicting( /*===========================*/ enum lock_mode mode, /*!< in: LOCK_S or LOCK_X, @@ -1822,7 +1829,7 @@ lock_rec_other_has_conflicting( ulint heap_no,/*!< in: heap number of the record */ const trx_t* trx) /*!< in: our transaction */ { - const lock_t* lock; + lock_t* lock; ibool is_supremum; ut_ad(lock_mutex_own()); @@ -1831,13 +1838,16 @@ lock_rec_other_has_conflicting( for (lock = lock_rec_get_first(block, heap_no); lock != NULL; - lock = lock_rec_get_next_const(heap_no, lock)) { + lock = lock_rec_get_next(heap_no, lock)) { #ifdef WITH_WSREP if (lock_rec_has_to_wait(TRUE, trx, mode, lock, is_supremum)) { if (wsrep_on_trx(trx)) { trx_mutex_enter(lock->trx); - wsrep_kill_victim(trx, lock); + /* Below function will roll back either trx + or lock->trx depending on priority of the + transaction. */ + wsrep_kill_victim(const_cast<trx_t*>(trx), lock); trx_mutex_exit(lock->trx); } #else @@ -2045,15 +2055,17 @@ wsrep_print_wait_locks( { if (wsrep_debug && c_lock->trx->lock.wait_lock != c_lock) { fprintf(stderr, "WSREP: c_lock != wait lock\n"); - if (lock_get_type_low(c_lock) & LOCK_TABLE) + if (lock_get_type_low(c_lock) & LOCK_TABLE) { lock_table_print(stderr, c_lock); - else + } else { lock_rec_print(stderr, c_lock); + } - if (lock_get_type_low(c_lock->trx->lock.wait_lock) & LOCK_TABLE) + if (lock_get_type_low(c_lock->trx->lock.wait_lock) & LOCK_TABLE) { lock_table_print(stderr, c_lock->trx->lock.wait_lock); - else + } else { lock_rec_print(stderr, c_lock->trx->lock.wait_lock); + } } } #endif /* WITH_WSREP */ @@ -2358,8 +2370,8 @@ lock_rec_create( if (wsrep_debug) { fprintf( stderr, - "WSREP: c_lock canceled %llu\n", - (ulonglong) c_lock->trx->id); + "WSREP: c_lock canceled " TRX_ID_FMT "\n", + c_lock->trx->id); } /* have to bail out here to avoid lock_set_lock... */ @@ -2551,6 +2563,16 @@ lock_rec_enqueue_waiting( err = DB_LOCK_WAIT; } +#ifdef WITH_WSREP + if (!lock_get_wait(lock) && wsrep_thd_is_BF(trx->mysql_thd, FALSE)) { + if (wsrep_debug) { + fprintf(stderr, "WSREP: BF thread got lock granted early, ID " TRX_ID_FMT + "\n", + lock->trx->id); + } + return(DB_SUCCESS); + } +#endif /* WITH_WSREP */ // Move it only when it does not cause a deadlock. if (err != DB_DEADLOCK && innodb_lock_schedule_algorithm @@ -2981,6 +3003,15 @@ lock_rec_has_to_wait_in_queue( #ifdef WITH_WSREP if (wsrep_thd_is_BF(wait_lock->trx->mysql_thd, FALSE) && wsrep_thd_is_BF(lock->trx->mysql_thd, TRUE)) { + if (wsrep_debug) { + fprintf(stderr, + "BF-BF lock conflict " TRX_ID_FMT + " : " TRX_ID_FMT "\n", + wait_lock->trx->id, + lock->trx->id); + lock_rec_print(stderr, wait_lock); + lock_rec_print(stderr, lock); + } /* don't wait for another BF lock */ continue; } @@ -3139,7 +3170,7 @@ lock_grant_and_move_on_page( && !lock_rec_has_to_wait_in_queue(lock)) { lock_grant(lock, false); - + if (previous != NULL) { /* Move the lock to the head of the list. */ HASH_GET_NEXT(hash, previous) = HASH_GET_NEXT(hash, lock); @@ -5017,8 +5048,8 @@ lock_table_create( } if (wsrep_debug) { - fprintf(stderr, "WSREP: c_lock canceled %llu\n", - (ulonglong) c_lock->trx->id); + fprintf(stderr, "WSREP: c_lock canceled " TRX_ID_FMT "\n", + c_lock->trx->id); } } if (c_lock) { @@ -5297,7 +5328,7 @@ Checks if other transactions have an incompatible mode lock request in the lock queue. @return lock or NULL */ UNIV_INLINE -const lock_t* +lock_t* lock_table_other_has_incompatible( /*==============================*/ const trx_t* trx, /*!< in: transaction, or NULL if all @@ -5308,7 +5339,7 @@ lock_table_other_has_incompatible( const dict_table_t* table, /*!< in: table */ enum lock_mode mode) /*!< in: lock mode */ { - const lock_t* lock; + lock_t* lock; ut_ad(lock_mutex_own()); @@ -5361,7 +5392,7 @@ lock_table( #endif trx_t* trx; dberr_t err; - const lock_t* wait_for; + lock_t* wait_for; ut_ad(table != NULL); ut_ad(thr != NULL); @@ -5412,13 +5443,13 @@ lock_table( if (wait_for != NULL) { #ifdef WITH_WSREP - err = lock_table_enqueue_waiting((ib_lock_t*)wait_for, mode | flags, table, thr); + err = lock_table_enqueue_waiting(wait_for, mode | flags, table, thr); #else err = lock_table_enqueue_waiting(mode | flags, table, thr); #endif } else { #ifdef WITH_WSREP - lock_table_create(c_lock, table, mode | flags, trx); + lock_table_create(c_lock, table, mode | flags, trx); #else lock_table_create(table, mode | flags, trx); #endif @@ -7101,10 +7132,10 @@ lock_rec_insert_check_and_lock( on the successor, which produced an unnecessary deadlock. */ #ifdef WITH_WSREP - if ((c_lock = (ib_lock_t*)lock_rec_other_has_conflicting( - static_cast<enum lock_mode>( - LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION), - block, next_rec_heap_no, trx))) { + if ((c_lock = lock_rec_other_has_conflicting( + static_cast<enum lock_mode>( + LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION), + block, next_rec_heap_no, trx))) { #else if (lock_rec_other_has_conflicting( static_cast<enum lock_mode>( @@ -7117,7 +7148,7 @@ lock_rec_insert_check_and_lock( #ifdef WITH_WSREP err = lock_rec_enqueue_waiting(c_lock, - LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION, + LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION, block, next_rec_heap_no, index, thr); #else err = lock_rec_enqueue_waiting( diff --git a/storage/xtradb/lock/lock0wait.cc b/storage/xtradb/lock/lock0wait.cc index ca9d05a4829..a0f557e18e5 100644 --- a/storage/xtradb/lock/lock0wait.cc +++ b/storage/xtradb/lock/lock0wait.cc @@ -191,22 +191,25 @@ lock_wait_table_reserve_slot( /*********************************************************************//** check if lock timeout was for priority thread, as a side effect trigger lock monitor +@param[in] trx transaction owning the lock +@param[in] locked true if trx and lock_sys_mutex is ownd @return false for regular lock timeout */ -static ibool +static +bool wsrep_is_BF_lock_timeout( -/*====================*/ - trx_t* trx) /* in: trx to check for lock priority */ + const trx_t* trx, + bool locked = true) { - if (wsrep_on_trx(trx) && - wsrep_thd_is_BF(trx->mysql_thd, FALSE)) { - fprintf(stderr, "WSREP: BF lock wait long\n"); - srv_print_innodb_monitor = TRUE; - srv_print_innodb_lock_monitor = TRUE; - os_event_set(srv_monitor_event); - return TRUE; - } - return FALSE; - } + if (wsrep_on_trx(trx) + && wsrep_thd_is_BF(trx->mysql_thd, FALSE)) { + fprintf(stderr, "WSREP: BF lock wait long for trx " TRX_ID_FMT "\n", trx->id); + srv_print_innodb_monitor = TRUE; + srv_print_innodb_lock_monitor = TRUE; + os_event_set(srv_monitor_event); + return true; + } + return false; +} #endif /* WITH_WSREP */ /***************************************************************//** @@ -402,15 +405,15 @@ lock_wait_suspend_thread( if (lock_wait_timeout < 100000000 && wait_time > (double) lock_wait_timeout) { #ifdef WITH_WSREP - if (!wsrep_on_trx(trx) || - (!wsrep_is_BF_lock_timeout(trx) && - trx->error_state != DB_DEADLOCK)) { + if (!wsrep_on_trx(trx) || + (!wsrep_is_BF_lock_timeout(trx) && + trx->error_state != DB_DEADLOCK)) { #endif /* WITH_WSREP */ - trx->error_state = DB_LOCK_WAIT_TIMEOUT; + trx->error_state = DB_LOCK_WAIT_TIMEOUT; #ifdef WITH_WSREP - } + } #endif /* WITH_WSREP */ MONITOR_INC(MONITOR_TIMEOUT); } diff --git a/storage/xtradb/row/row0undo.cc b/storage/xtradb/row/row0undo.cc index 82b1ab049fa..552b99ab4d4 100644 --- a/storage/xtradb/row/row0undo.cc +++ b/storage/xtradb/row/row0undo.cc @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2017, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -348,6 +349,13 @@ row_undo_step( ut_ad(que_node_get_type(node) == QUE_NODE_UNDO); + if (UNIV_UNLIKELY(trx == trx_roll_crash_recv_trx) + && trx_roll_must_shutdown()) { + /* Shutdown has been initiated. */ + trx->error_state = DB_INTERRUPTED; + return(NULL); + } + err = row_undo(node, thr); trx->error_state = err; diff --git a/storage/xtradb/trx/trx0roll.cc b/storage/xtradb/trx/trx0roll.cc index 335ef8859c4..9a5fcea71de 100644 --- a/storage/xtradb/trx/trx0roll.cc +++ b/storage/xtradb/trx/trx0roll.cc @@ -24,6 +24,9 @@ Transaction rollback Created 3/26/1996 Heikki Tuuri *******************************************************/ +#include "my_config.h" +#include <my_systemd.h> + #include "trx0roll.h" #ifdef UNIV_NONINL @@ -60,14 +63,7 @@ rollback */ bool trx_rollback_or_clean_is_active; /** In crash recovery, the current trx to be rolled back; NULL otherwise */ -static const trx_t* trx_roll_crash_recv_trx = NULL; - -/** In crash recovery we set this to the undo n:o of the current trx to be -rolled back. Then we can print how many % the rollback has progressed. */ -static undo_no_t trx_roll_max_undo_no; - -/** Auxiliary variable which tells the previous progress % we printed */ -static ulint trx_roll_progress_printed_pct; +const trx_t* trx_roll_crash_recv_trx; /****************************************************************//** Finishes a transaction rollback. */ @@ -564,8 +560,6 @@ trx_rollback_active( que_thr_t* thr; roll_node_t* roll_node; dict_table_t* table; - ib_int64_t rows_to_undo; - const char* unit = ""; ibool dictionary_locked = FALSE; heap = mem_heap_create(512); @@ -584,30 +578,8 @@ trx_rollback_active( ut_a(thr == que_fork_start_command(fork)); - mutex_enter(&trx_sys->mutex); - trx_roll_crash_recv_trx = trx; - trx_roll_max_undo_no = trx->undo_no; - - trx_roll_progress_printed_pct = 0; - - rows_to_undo = trx_roll_max_undo_no; - - mutex_exit(&trx_sys->mutex); - - if (rows_to_undo > 1000000000) { - rows_to_undo = rows_to_undo / 1000000; - unit = "M"; - } - - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Rolling back trx with id " TRX_ID_FMT ", %lu%s" - " rows to undo\n", - trx->id, - (ulong) rows_to_undo, unit); - if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) { row_mysql_lock_data_dictionary(trx); dictionary_locked = TRUE; @@ -618,6 +590,16 @@ trx_rollback_active( que_run_threads(roll_node->undo_thr); + if (trx->error_state != DB_SUCCESS) { + ut_ad(trx->error_state == DB_INTERRUPTED); + ut_ad(!srv_undo_sources); + ut_ad(srv_fast_shutdown); + ut_ad(!dictionary_locked); + que_graph_free(static_cast<que_t*>( + roll_node->undo_thr->common.parent)); + goto func_exit; + } + trx_rollback_finish(thr_get_trx(roll_node->undo_thr)); /* Free the memory reserved by the undo graph */ @@ -662,13 +644,14 @@ trx_rollback_active( } } + ib_logf(IB_LOG_LEVEL_INFO, + "Rollback of trx with id " TRX_ID_FMT " completed", trx->id); + +func_exit: if (dictionary_locked) { row_mysql_unlock_data_dictionary(trx); } - ib_logf(IB_LOG_LEVEL_INFO, - "Rollback of trx with id " TRX_ID_FMT " completed", trx->id); - mem_heap_free(heap); trx_roll_crash_recv_trx = NULL; @@ -685,7 +668,7 @@ ibool trx_rollback_resurrected( /*=====================*/ trx_t* trx, /*!< in: transaction to rollback or clean */ - ibool all) /*!< in: FALSE=roll back dictionary transactions; + ibool* all) /*!< in/out: FALSE=roll back dictionary transactions; TRUE=roll back all non-PREPARED transactions */ { ut_ad(mutex_own(&trx_sys->mutex)); @@ -696,16 +679,15 @@ trx_rollback_resurrected( to accidentally clean up a non-recovered transaction here. */ trx_mutex_enter(trx); - bool is_recovered = trx->is_recovered; - trx_state_t state = trx->state; - trx_mutex_exit(trx); - - if (!is_recovered) { + if (!trx->is_recovered) { +func_exit: + trx_mutex_exit(trx); return(FALSE); } - switch (state) { + switch (trx->state) { case TRX_STATE_COMMITTED_IN_MEMORY: + trx_mutex_exit(trx); mutex_exit(&trx_sys->mutex); fprintf(stderr, "InnoDB: Cleaning up trx with id " TRX_ID_FMT "\n", @@ -714,21 +696,83 @@ trx_rollback_resurrected( trx_free_for_background(trx); return(TRUE); case TRX_STATE_ACTIVE: - if (all || trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) { + if (!srv_undo_sources && srv_fast_shutdown) { +fake_prepared: + trx->state = TRX_STATE_PREPARED; + trx_sys->n_prepared_trx++; + trx_sys->n_prepared_recovered_trx++; + *all = FALSE; + goto func_exit; + } + trx_mutex_exit(trx); + + if (*all || trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) { mutex_exit(&trx_sys->mutex); trx_rollback_active(trx); + if (trx->error_state != DB_SUCCESS) { + ut_ad(trx->error_state == DB_INTERRUPTED); + ut_ad(!srv_undo_sources); + ut_ad(srv_fast_shutdown); + mutex_enter(&trx_sys->mutex); + trx_mutex_enter(trx); + goto fake_prepared; + } trx_free_for_background(trx); return(TRUE); } return(FALSE); case TRX_STATE_PREPARED: - return(FALSE); + goto func_exit; case TRX_STATE_NOT_STARTED: break; } ut_error; - return(FALSE); + goto func_exit; +} + +/** Report progress when rolling back a row of a recovered transaction. +@return whether the rollback should be aborted due to pending shutdown */ +UNIV_INTERN +bool +trx_roll_must_shutdown() +{ + const trx_t* trx = trx_roll_crash_recv_trx; + ut_ad(trx); + ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE)); + + if (trx_get_dict_operation(trx) == TRX_DICT_OP_NONE + && !srv_undo_sources && srv_fast_shutdown) { + return true; + } + + ib_time_t time = ut_time(); + mutex_enter(&trx_sys->mutex); + mutex_enter(&recv_sys->mutex); + + if (recv_sys->report(time)) { + ulint n_trx = 0, n_rows = 0; + for (const trx_t* t = UT_LIST_GET_FIRST(trx_sys->rw_trx_list); + t != NULL; + t = UT_LIST_GET_NEXT(trx_list, t)) { + + assert_trx_in_rw_list(t); + if (t->is_recovered + && trx_state_eq(t, TRX_STATE_ACTIVE)) { + n_trx++; + n_rows += t->undo_no; + } + } + ib_logf(IB_LOG_LEVEL_INFO, + "To roll back: " ULINTPF " transactions, " + ULINTPF " rows", n_trx, n_rows); + sd_notifyf(0, "STATUS=To roll back: " ULINTPF " transactions, " + ULINTPF " rows", n_trx, n_rows); + } + + mutex_exit(&recv_sys->mutex); + mutex_exit(&trx_sys->mutex); + return false; } /*******************************************************************//** @@ -775,17 +819,11 @@ trx_rollback_or_clean_recovered( assert_trx_in_rw_list(trx); - if (srv_shutdown_state != SRV_SHUTDOWN_NONE - && srv_fast_shutdown != 0) { - all = FALSE; - break; - } - /* If this function does a cleanup or rollback then it will release the trx_sys->mutex, therefore we need to reacquire it before retrying the loop. */ - if (trx_rollback_resurrected(trx, all)) { + if (trx_rollback_resurrected(trx, &all)) { mutex_enter(&trx_sys->mutex); @@ -1118,7 +1156,6 @@ trx_roll_pop_top_rec_of_trx( undo_no_t undo_no; ibool is_insert; trx_rseg_t* rseg; - ulint progress_pct; mtr_t mtr; rseg = trx->rseg; @@ -1176,27 +1213,6 @@ try_again: ut_ad(undo_no + 1 == trx->undo_no); - /* We print rollback progress info if we are in a crash recovery - and the transaction has at least 1000 row operations to undo. */ - - if (trx == trx_roll_crash_recv_trx && trx_roll_max_undo_no > 1000) { - - progress_pct = 100 - (ulint) - ((undo_no * 100) / trx_roll_max_undo_no); - if (progress_pct != trx_roll_progress_printed_pct) { - if (trx_roll_progress_printed_pct == 0) { - fprintf(stderr, - "\nInnoDB: Progress in percents:" - " %lu", (ulong) progress_pct); - } else { - fprintf(stderr, - " %lu", (ulong) progress_pct); - } - fflush(stderr); - trx_roll_progress_printed_pct = progress_pct; - } - } - trx->undo_no = undo_no; if (!trx_undo_arr_store_info(trx, undo_no)) { diff --git a/storage/xtradb/trx/trx0undo.cc b/storage/xtradb/trx/trx0undo.cc index 3259bcb70b1..24d14e06080 100644 --- a/storage/xtradb/trx/trx0undo.cc +++ b/storage/xtradb/trx/trx0undo.cc @@ -2023,10 +2023,14 @@ trx_undo_free_prepared( /* fall through */ case TRX_UNDO_ACTIVE: /* lock_trx_release_locks() assigns - trx->is_recovered=false */ + trx->is_recovered=false and + trx->state = TRX_STATE_COMMITTED_IN_MEMORY, + also for transactions that we faked + to TRX_STATE_PREPARED in trx_rollback_resurrected(). */ ut_a(srv_read_only_mode || srv_apply_log_only - || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); + || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO + || srv_fast_shutdown); break; default: ut_error; @@ -2048,10 +2052,14 @@ trx_undo_free_prepared( /* fall through */ case TRX_UNDO_ACTIVE: /* lock_trx_release_locks() assigns - trx->is_recovered=false */ + trx->is_recovered=false and + trx->state = TRX_STATE_COMMITTED_IN_MEMORY, + also for transactions that we faked + to TRX_STATE_PREPARED in trx_rollback_resurrected(). */ ut_a(srv_read_only_mode || srv_apply_log_only - || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); + || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO + || srv_fast_shutdown); break; default: ut_error; |