diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2017-12-13 23:14:15 +0200 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2017-12-14 08:40:01 +0200 |
commit | ece9c54e1062ea70b8cf31d69c1ce4db9ba12f7d (patch) | |
tree | 7f7fd7ea472ff807629fd5abe3a540072307fc49 | |
parent | 58eb4e5db9e333768cc927d5d5a63d68fbd74548 (diff) | |
parent | 7be5b6f0e6170461e7ccc6ec7947436af2e43e41 (diff) | |
download | mariadb-git-ece9c54e1062ea70b8cf31d69c1ce4db9ba12f7d.tar.gz |
Merge 10.1 into 10.2
36 files changed, 623 insertions, 251 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/r/ctype_utf8.result b/mysql-test/r/ctype_utf8.result index 3eb500b5c66..f8146f9fda2 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 8ca0d8c0297..0b620d9cccd 100644 --- a/mysql-test/r/func_str.result +++ b/mysql-test/r/func_str.result @@ -4556,6 +4556,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 # # 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/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/t/ctype_utf8.test b/mysql-test/t/ctype_utf8.test index c3ee7c13bdf..b6d2dc7d874 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 e039e48a888..421bf635193 100644 --- a/mysql-test/t/func_str.test +++ b/mysql-test/t/func_str.test @@ -1757,6 +1757,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 # @@ -1800,7 +1812,6 @@ SELECT f1,HEX(f2) FROM t1 WHERE f1='YQ==' AND (f2= from_base64( SELECT f1,HEX(f2) FROM t1 WHERE f1='YQ==' AND (f2= from_base64("Yq==") OR f2= from_base64("YQ==")); DROP TABLE t1; - --echo # --echo # End of 10.1 tests --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 1b84cd89cf9..38593a52495 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -6976,20 +6976,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 89e55234482..51f66ce3193 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2834,6 +2834,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); @@ -4412,20 +4426,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; @@ -4645,9 +4658,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: @@ -4747,20 +4761,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); } @@ -5184,7 +5198,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); @@ -5194,6 +5207,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 181a4e2a747..e851ab59b6a 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -870,6 +870,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); } }; @@ -1431,14 +1432,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) @@ -1491,7 +1492,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/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index e7a4532a604..9d5ead21701 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -3866,8 +3866,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/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/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/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/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/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 4c3d6a1c11c..b09eaf8058c 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; |