1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

Merge 10.1 into 10.2

This commit is contained in:
Marko Mäkelä
2017-12-13 23:14:15 +02:00
36 changed files with 623 additions and 251 deletions

View File

@@ -166,8 +166,8 @@ static struct my_option my_long_options[]=
"server with which it was built/distributed.", "server with which it was built/distributed.",
&opt_version_check, &opt_version_check, 0, &opt_version_check, &opt_version_check, 0,
GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
{"write-binlog", OPT_WRITE_BINLOG, "All commands including those, " {"write-binlog", OPT_WRITE_BINLOG, "All commands including those "
"issued by mysqlcheck, are written to the binary log.", "issued by mysqlcheck are written to the binary log.",
&opt_write_binlog, &opt_write_binlog, 0, GET_BOOL, NO_ARG, &opt_write_binlog, &opt_write_binlog, 0, GET_BOOL, NO_ARG,
0, 0, 0, 0, 0, 0}, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, GET_NO_ARG, 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}

View File

@@ -63,6 +63,12 @@ typedef struct st_mysql_lex_string LEX_STRING;
/* NO and OK is the same used just to show semantics */ /* NO and OK is the same used just to show semantics */
#define ER_DYNCOL_NO ER_DYNCOL_OK #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 enum enum_dyncol_func_result
{ {
ER_DYNCOL_OK= 0, ER_DYNCOL_OK= 0,

View File

@@ -691,8 +691,7 @@ it was built/distributed. Defaults to on; use \fB\-\-skip\-version\-check\fR to
.sp .sp
Cause binary logging to be enabled while Cause binary logging to be enabled while
\fBmysql_upgrade\fR \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 runs\&.
\fB\-\-skip\-write\-binlog\fR)\&.
.RE .RE
.SH "COPYRIGHT" .SH "COPYRIGHT"
.br .br

View File

@@ -10272,6 +10272,21 @@ DROP TABLE allbytes;
SET sql_mode = DEFAULT; SET sql_mode = DEFAULT;
# End of ctype_backslash.inc # 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 # End of 10.0 tests
# #
# #

View File

@@ -3430,6 +3430,32 @@ a b
a 😁 b a ? b a 😁 b a ? b
DROP TABLE t1; 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 # End of 10.0 tests
# #
# #

View File

@@ -4556,6 +4556,27 @@ set global max_allowed_packet=default;
# End of 5.6 tests # 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 # Start of 10.1 tests
# #
# #

View File

@@ -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;

View File

@@ -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;

View File

@@ -1 +1 @@
--innodb-open-files=13 --innodb-open-files=20

View File

@@ -1878,6 +1878,18 @@ SELECT _utf8 0x7E, _utf8 X'7E', _utf8 B'01111110';
let $ctype_unescape_combinations=selected; let $ctype_unescape_combinations=selected;
--source include/ctype_unescape.inc --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 #
--echo # End of 10.0 tests --echo # End of 10.0 tests
--echo # --echo #

View File

@@ -1922,6 +1922,24 @@ INSERT IGNORE INTO t1 SELECT 'a 😁 b', 'a 😁 b';
SELECT * FROM t1; SELECT * FROM t1;
DROP TABLE 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 #
--echo # End of 10.0 tests --echo # End of 10.0 tests
--echo # --echo #

View File

@@ -1756,6 +1756,18 @@ set global max_allowed_packet=default;
--echo # End of 5.6 tests --echo # End of 5.6 tests
--echo # --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 #
--echo # Start of 10.1 tests --echo # Start of 10.1 tests
--echo # --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==")); SELECT f1,HEX(f2) FROM t1 WHERE f1='YQ==' AND (f2= from_base64("Yq==") OR f2= from_base64("YQ=="));
DROP TABLE t1; DROP TABLE t1;
--echo # --echo #
--echo # End of 10.1 tests --echo # End of 10.1 tests
--echo # --echo #

View File

@@ -4183,8 +4183,7 @@ mariadb_dyncol_json_internal(DYNAMIC_COLUMN *str, DYNAMIC_STRING *json,
} }
else else
{ {
if ((rc= mariadb_dyncol_val_str(json, &val, if ((rc= mariadb_dyncol_val_str(json, &val, DYNCOL_UTF, '"')) < 0)
&my_charset_utf8_general_ci, '"')) < 0)
goto err; goto err;
} }
} }

View File

@@ -273,7 +273,7 @@ void thr_end_alarm(thr_alarm_t *alarmed)
/* /*
Come here when some alarm in queue is due. Come here when some alarm in queue is due.
Mark all alarms with are finnished in list. 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 If alarm_aborted is set then all alarms are given and resent
every second. every second.
*/ */
@@ -425,7 +425,7 @@ void end_thr_alarm(my_bool free_structures)
if (alarm_aborted != 1) /* If memory not freed */ if (alarm_aborted != 1) /* If memory not freed */
{ {
mysql_mutex_lock(&LOCK_alarm); 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 */ alarm_aborted= -1; /* mark aborted */
if (alarm_queue.elements || (alarm_thread_running && free_structures)) if (alarm_queue.elements || (alarm_thread_running && free_structures))
{ {

View File

@@ -6976,20 +6976,20 @@ longlong Item_func_dyncol_exists::val_int()
null_value= 1; null_value= 1;
return 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.str= (char *) nm->ptr();
buf.length= nm->length(); buf.length= nm->length();
} }
else else
{ {
uint strlen= nm->length() * my_charset_utf8_general_ci.mbmaxlen + 1; uint strlen= nm->length() * DYNCOL_UTF->mbmaxlen + 1;
uint dummy_errors; uint dummy_errors;
buf.str= (char *) current_thd->alloc(strlen); buf.str= (char *) current_thd->alloc(strlen);
if (buf.str) if (buf.str)
{ {
buf.length= 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(), nm->ptr(), nm->length(), nm->charset(),
&dummy_errors); &dummy_errors);
} }

View File

@@ -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) String *Item_func_char::val_str(String *str)
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
@@ -4412,20 +4426,19 @@ bool Item_func_dyncol_create::prepare_arguments(THD *thd, bool force_names_arg)
if (res) if (res)
{ {
// guaranty UTF-8 string for names // 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].length= res->length();
keys_str[i].str= thd->strmake(res->ptr(), res->length()); keys_str[i].str= thd->strmake(res->ptr(), res->length());
} }
else else
{ {
uint strlen= res->length() * my_charset_utf8_general_ci.mbmaxlen + 1; uint strlen= res->length() * DYNCOL_UTF->mbmaxlen + 1;
uint dummy_errors; uint dummy_errors;
char *str= (char *) thd->alloc(strlen); if (char *str= (char *) thd->alloc(strlen))
if (str)
{ {
keys_str[i].length= 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(), res->ptr(), res->length(), res->charset(),
&dummy_errors); &dummy_errors);
keys_str[i].str= str; keys_str[i].str= str;
@@ -4645,9 +4658,10 @@ String *Item_func_dyncol_json::val_str(String *str)
char *ptr; char *ptr;
size_t length, alloc_length; size_t length, alloc_length;
dynstr_reassociate(&json, &ptr, &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; null_value= FALSE;
} }
str->set_charset(DYNCOL_UTF);
return str; return str;
null: null:
@@ -4747,20 +4761,20 @@ bool Item_dyncol_get::get_dyn_value(THD *thd, DYNAMIC_COLUMN_VALUE *val,
return 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.str= (char *) nm->ptr();
buf.length= nm->length(); buf.length= nm->length();
} }
else else
{ {
uint strlen= nm->length() * my_charset_utf8_general_ci.mbmaxlen + 1; uint strlen= nm->length() * DYNCOL_UTF->mbmaxlen + 1;
uint dummy_errors; uint dummy_errors;
buf.str= (char *) thd->alloc(strlen); buf.str= (char *) thd->alloc(strlen);
if (buf.str) if (buf.str)
{ {
buf.length= 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(), nm->ptr(), nm->length(), nm->charset(),
&dummy_errors); &dummy_errors);
} }
@@ -5184,7 +5198,6 @@ String *Item_func_dyncol_list::val_str(String *str)
goto null; goto null;
str->length(0); str->length(0);
str->set_charset(&my_charset_utf8_general_ci);
for (i= 0; i < count; i++) for (i= 0; i < count; i++)
{ {
append_identifier(current_thd, str, names[i].str, names[i].length); 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; null_value= FALSE;
if (names) if (names)
my_free(names); my_free(names);
str->set_charset(DYNCOL_UTF);
return str; return str;
null: null:

View File

@@ -870,6 +870,7 @@ public:
max_length= arg_count * 4; max_length= arg_count * 4;
} }
const char *func_name() const { return "char"; } 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) Item *get_copy(THD *thd, MEM_ROOT *mem_root)
{ return get_item_copy<Item_func_char>(thd, mem_root, this); } { 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 class Item_func_dyncol_json: public Item_str_func
{ {
public: 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"; } const char *func_name() const{ return "column_json"; }
String *val_str(String *); String *val_str(String *);
void fix_length_and_dec() void fix_length_and_dec()
{ {
max_length= MAX_BLOB_WIDTH; max_length= MAX_BLOB_WIDTH;
maybe_null= 1; maybe_null= 1;
collation.set(&my_charset_bin);
decimals= 0; decimals= 0;
} }
Item *get_copy(THD *thd, MEM_ROOT *mem_root) Item *get_copy(THD *thd, MEM_ROOT *mem_root)
@@ -1491,7 +1492,8 @@ public:
class Item_func_dyncol_list: public Item_str_func class Item_func_dyncol_list: public Item_str_func
{ {
public: 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; }; void fix_length_and_dec() { maybe_null= 1; max_length= MAX_BLOB_WIDTH; };
const char *func_name() const{ return "column_list"; } const char *func_name() const{ return "column_list"; }
String *val_str(String *); String *val_str(String *);

View File

@@ -3866,8 +3866,7 @@ innobase_init(
/* Currently, Galera does not support VATS lock schedule algorithm. */ /* Currently, Galera does not support VATS lock schedule algorithm. */
if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS if (innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_VATS
&& global_system_variables.wsrep_on) { && global_system_variables.wsrep_on) {
ib::info() << "In Galera environment Variance-Aware-Transaction-Sheduling Algorithm" ib::info() << "For Galera, using innodb_lock_schedule_algorithm=fcfs";
" is not supported. Falling back to First-Come-First-Served order. ";
innodb_lock_schedule_algorithm = INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS; innodb_lock_schedule_algorithm = INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS;
} }
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */

View File

@@ -380,9 +380,6 @@ struct que_thr_t{
UT_LIST_NODE_T(que_thr_t) UT_LIST_NODE_T(que_thr_t)
thrs; /*!< list of thread nodes of the fork thrs; /*!< list of thread nodes of the fork
node */ 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) UT_LIST_NODE_T(que_thr_t)
queue; /*!< list of runnable thread nodes in queue; /*!< list of runnable thread nodes in
the server task queue */ the server task queue */

View File

@@ -33,7 +33,8 @@ Created 3/26/1996 Heikki Tuuri
#include "mtr0mtr.h" #include "mtr0mtr.h"
#include "trx0sys.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 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) 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)); 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 Rollback or clean up any incomplete transactions which were
encountered in crash recovery. If the transaction already was encountered in crash recovery. If the transaction already was

View File

@@ -1178,7 +1178,16 @@ close_table:
if (!row_undo_search_clust_to_pcur(node)) { if (!row_undo_search_clust_to_pcur(node)) {
/* As long as this rolling-back transaction exists, /* As long as this rolling-back transaction exists,
the PRIMARY KEY value pointed to by the undo log 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 was not modified yet (the DB_ROLL_PTR does not match
node->roll_ptr) and thus there is nothing to roll back. node->roll_ptr) and thus there is nothing to roll back.
@@ -1186,8 +1195,11 @@ close_table:
record after successfully acquiring an exclusive lock record after successfully acquiring an exclusive lock
on the the clustered index record. That lock will not on the the clustered index record. That lock will not
be released before the transaction is committed or be released before the transaction is committed or
fully rolled back. */ fully rolled back. (Exception: if the server was
ut_ad(node->pcur.btr_cur.low_match == node->ref->n_fields); 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; goto close_table;
} }

View File

@@ -1,6 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved. 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 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 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); 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); err = row_undo(node, thr);
trx->error_state = err; trx->error_state = err;

View File

@@ -2633,8 +2633,6 @@ files_checked:
trx_temp_rseg_create(); trx_temp_rseg_create();
} }
srv_is_being_started = false;
ut_a(trx_purge_state() == PURGE_STATE_INIT); ut_a(trx_purge_state() == PURGE_STATE_INIT);
/* Create the master thread which does purge and other utility /* Create the master thread which does purge and other utility
@@ -2683,6 +2681,8 @@ files_checked:
purge_sys->state = PURGE_STATE_DISABLED; purge_sys->state = PURGE_STATE_DISABLED;
} }
srv_is_being_started = false;
if (!srv_read_only_mode) { if (!srv_read_only_mode) {
/* wake main loop of page cleaner up */ /* wake main loop of page cleaner up */
os_event_set(buf_flush_event); os_event_set(buf_flush_event);

View File

@@ -24,8 +24,10 @@ Transaction rollback
Created 3/26/1996 Heikki Tuuri 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 "trx0roll.h"
#include <mysql/service_wsrep.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; bool trx_rollback_or_clean_is_active;
/** In crash recovery, the current trx to be rolled back; NULL otherwise */ /** In crash recovery, the current trx to be rolled back; NULL otherwise */
static const trx_t* trx_roll_crash_recv_trx = NULL; const trx_t* trx_roll_crash_recv_trx;
/** 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;
/****************************************************************//** /****************************************************************//**
Finishes a transaction rollback. */ Finishes a transaction rollback. */
@@ -631,8 +626,6 @@ trx_rollback_active(
que_thr_t* thr; que_thr_t* thr;
roll_node_t* roll_node; roll_node_t* roll_node;
dict_table_t* table; dict_table_t* table;
int64_t rows_to_undo;
const char* unit = "";
ibool dictionary_locked = FALSE; ibool dictionary_locked = FALSE;
heap = mem_heap_create(512); heap = mem_heap_create(512);
@@ -651,28 +644,8 @@ trx_rollback_active(
ut_a(thr == que_fork_start_command(fork)); ut_a(thr == que_fork_start_command(fork));
trx_sys_mutex_enter();
trx_roll_crash_recv_trx = trx; 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) { if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) {
row_mysql_lock_data_dictionary(trx); row_mysql_lock_data_dictionary(trx);
dictionary_locked = TRUE; dictionary_locked = TRUE;
@@ -683,6 +656,17 @@ trx_rollback_active(
que_run_threads(roll_node->undo_thr); 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)); trx_rollback_finish(thr_get_trx(roll_node->undo_thr));
/* Free the memory reserved by the undo graph */ /* Free the memory reserved by the undo graph */
@@ -714,11 +698,13 @@ trx_rollback_active(
} }
} }
func_exit:
if (dictionary_locked) { if (dictionary_locked) {
row_mysql_unlock_data_dictionary(trx); 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); mem_heap_free(heap);
@@ -736,7 +722,7 @@ ibool
trx_rollback_resurrected( trx_rollback_resurrected(
/*=====================*/ /*=====================*/
trx_t* trx, /*!< in: transaction to rollback or clean */ 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 */ TRUE=roll back all non-PREPARED transactions */
{ {
ut_ad(trx_sys_mutex_own()); ut_ad(trx_sys_mutex_own());
@@ -747,40 +733,102 @@ trx_rollback_resurrected(
to accidentally clean up a non-recovered transaction here. */ to accidentally clean up a non-recovered transaction here. */
trx_mutex_enter(trx); trx_mutex_enter(trx);
bool is_recovered = trx->is_recovered; if (!trx->is_recovered) {
trx_state_t state = trx->state; func_exit:
trx_mutex_exit(trx); trx_mutex_exit(trx);
if (!is_recovered) {
return(FALSE); return(FALSE);
} }
switch (state) { switch (trx->state) {
case TRX_STATE_COMMITTED_IN_MEMORY: case TRX_STATE_COMMITTED_IN_MEMORY:
trx_mutex_exit(trx);
trx_sys_mutex_exit(); trx_sys_mutex_exit();
ib::info() << "Cleaning up trx with id " ib::info() << "Cleaning up trx with id " << ib::hex(trx->id);
<< trx_get_id_for_print(trx);
trx_cleanup_at_db_startup(trx); trx_cleanup_at_db_startup(trx);
trx_free_resurrected(trx); trx_free_resurrected(trx);
return(TRUE); return(TRUE);
case TRX_STATE_ACTIVE: 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_sys_mutex_exit();
trx_rollback_active(trx); 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); trx_free_for_background(trx);
return(TRUE); return(TRUE);
} }
return(FALSE); return(FALSE);
case TRX_STATE_PREPARED: case TRX_STATE_PREPARED:
return(FALSE); goto func_exit;
case TRX_STATE_NOT_STARTED: case TRX_STATE_NOT_STARTED:
case TRX_STATE_FORCED_ROLLBACK: case TRX_STATE_FORCED_ROLLBACK:
break; break;
} }
ut_error; 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); 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 /* If this function does a cleanup or rollback
then it will release the trx_sys->mutex, therefore then it will release the trx_sys->mutex, therefore
we need to reacquire it before retrying the loop. */ 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(); 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( ut_ad(trx_roll_check_undo_rec_ordering(
undo_no, undo->rseg->space, trx)); 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_no = undo_no;
trx->undo_rseg_space = undo->rseg->space; trx->undo_rseg_space = undo->rseg->space;
mutex_exit(&trx->undo_mutex); mutex_exit(&trx->undo_mutex);

View File

@@ -1826,10 +1826,14 @@ trx_undo_free_prepared(
/* fall through */ /* fall through */
case TRX_UNDO_ACTIVE: case TRX_UNDO_ACTIVE:
/* lock_trx_release_locks() assigns /* 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 ut_a(!srv_was_started
|| srv_read_only_mode || srv_read_only_mode
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO
|| srv_fast_shutdown);
break; break;
default: default:
ut_error; ut_error;
@@ -1854,10 +1858,14 @@ trx_undo_free_prepared(
/* fall through */ /* fall through */
case TRX_UNDO_ACTIVE: case TRX_UNDO_ACTIVE:
/* lock_trx_release_locks() assigns /* 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 ut_a(!srv_was_started
|| srv_read_only_mode || srv_read_only_mode
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO
|| srv_fast_shutdown);
break; break;
default: default:
ut_error; ut_error;

View File

@@ -6342,7 +6342,6 @@ my_bool translog_write_record(LSN *lsn,
short_trid, &parts, trn, hook_arg); short_trid, &parts, trn, hook_arg);
break; break;
case LOGRECTYPE_NOT_ALLOWED: case LOGRECTYPE_NOT_ALLOWED:
DBUG_ASSERT(0);
default: default:
DBUG_ASSERT(0); DBUG_ASSERT(0);
rc= 1; rc= 1;

View File

@@ -226,7 +226,6 @@ master-bin.000001 # Xid # # COMMIT /* XID */
master-bin.000001 # Rotate # # master-bin.000002;pos=POS master-bin.000001 # Rotate # # master-bin.000002;pos=POS
include/show_binlog_events.inc include/show_binlog_events.inc
Log_name Pos Event_type Server_id End_log_pos Info 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 # Gtid # # GTID #-#-#
master-bin.000002 # Query # # use `test`; create table t3 (a int)ENGINE=TokuDB master-bin.000002 # Query # # use `test`; create table t3 (a int)ENGINE=TokuDB
master-bin.000002 # Gtid # # GTID #-#-# 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 slave-bin.000001 # Rotate # # slave-bin.000002;pos=POS
include/show_binlog_events.inc include/show_binlog_events.inc
Log_name Pos Event_type Server_id End_log_pos Info 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 # Gtid # # GTID #-#-#
slave-bin.000002 # Query # # use `test`; create table t2 (n int)ENGINE=TokuDB slave-bin.000002 # Query # # use `test`; create table t2 (n int)ENGINE=TokuDB
slave-bin.000002 # Gtid # # BEGIN GTID #-#-# slave-bin.000002 # Gtid # # BEGIN GTID #-#-#

View File

@@ -222,7 +222,6 @@ master-bin.000001 # Xid # # COMMIT /* XID */
master-bin.000001 # Rotate # # master-bin.000002;pos=POS master-bin.000001 # Rotate # # master-bin.000002;pos=POS
include/show_binlog_events.inc include/show_binlog_events.inc
Log_name Pos Event_type Server_id End_log_pos Info 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 # Gtid # # GTID #-#-#
master-bin.000002 # Query # # use `test`; create table t3 (a int)ENGINE=TokuDB master-bin.000002 # Query # # use `test`; create table t3 (a int)ENGINE=TokuDB
master-bin.000002 # Gtid # # GTID #-#-# 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 slave-bin.000001 # Rotate # # slave-bin.000002;pos=POS
include/show_binlog_events.inc include/show_binlog_events.inc
Log_name Pos Event_type Server_id End_log_pos Info 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 # Gtid # # GTID #-#-#
slave-bin.000002 # Query # # use `test`; create table t2 (n int)ENGINE=TokuDB slave-bin.000002 # Query # # use `test`; create table t2 (n int)ENGINE=TokuDB
slave-bin.000002 # Gtid # # BEGIN GTID #-#-# slave-bin.000002 # Gtid # # BEGIN GTID #-#-#

View File

@@ -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 #ifndef HAVE_LZ4
if (innodb_compression_algorithm == PAGE_LZ4_ALGORITHM) { if (innodb_compression_algorithm == PAGE_LZ4_ALGORITHM) {
sql_print_error("InnoDB: innodb_compression_algorithm = %lu unsupported.\n" sql_print_error("InnoDB: innodb_compression_algorithm = %lu unsupported.\n"
@@ -5454,8 +5465,8 @@ innobase_kill_connection(
wsrep_thd_is_BF(current_thd, FALSE), wsrep_thd_is_BF(current_thd, FALSE),
lock_get_info(trx->lock.wait_lock).c_str()); lock_get_info(trx->lock.wait_lock).c_str());
if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE) && if (!wsrep_thd_is_BF(trx->mysql_thd, FALSE)
trx->abort_type == TRX_SERVER_ABORT) { && trx->abort_type == TRX_SERVER_ABORT) {
ut_ad(!lock_mutex_own()); ut_ad(!lock_mutex_own());
lock_mutex_enter(); lock_mutex_enter();
} }
@@ -20463,7 +20474,7 @@ static MYSQL_SYSVAR_ENUM(empty_free_list_algorithm,
&innodb_empty_free_list_algorithm_typelib); &innodb_empty_free_list_algorithm_typelib);
static MYSQL_SYSVAR_ENUM(lock_schedule_algorithm, innodb_lock_schedule_algorithm, 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" "The algorithm Innodb uses for deciding which locks to grant next when"
" a lock is released. Possible values are" " a lock is released. Possible values are"
" FCFS" " FCFS"

View File

@@ -384,9 +384,6 @@ struct que_thr_t{
UT_LIST_NODE_T(que_thr_t) UT_LIST_NODE_T(que_thr_t)
thrs; /*!< list of thread nodes of the fork thrs; /*!< list of thread nodes of the fork
node */ 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) UT_LIST_NODE_T(que_thr_t)
queue; /*!< list of runnable thread nodes in queue; /*!< list of runnable thread nodes in
the server task queue */ the server task queue */

View File

@@ -1,6 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. 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 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 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 "mtr0mtr.h"
#include "trx0sys.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 Determines if this transaction is rolling back an incomplete transaction
@@ -104,6 +106,11 @@ trx_undo_rec_release(
/*=================*/ /*=================*/
trx_t* trx, /*!< in/out: transaction */ trx_t* trx, /*!< in/out: transaction */
undo_no_t undo_no);/*!< in: undo number */ 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 Rollback or clean up any incomplete transactions which were
encountered in crash recovery. If the transaction already was encountered in crash recovery. If the transaction already was

View File

@@ -937,14 +937,21 @@ lock_reset_lock_and_trx_wait(
ib_logf(IB_LOG_LEVEL_INFO, ib_logf(IB_LOG_LEVEL_INFO,
"Trx id " TRX_ID_FMT "Trx id " TRX_ID_FMT
" is waiting a lock in statement %s" " is waiting a lock "
" for this trx id " TRX_ID_FMT " for this trx id " TRX_ID_FMT
" and statement %s wait_lock %p", " wait_lock %p",
lock->trx->id, lock->trx->id,
stmt ? stmt : "NULL",
trx_id, trx_id,
stmt2 ? stmt2 : "NULL",
lock->trx->lock.wait_lock); 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); ut_ad(lock->trx->lock.wait_lock == lock);
} }
@@ -1162,7 +1169,7 @@ lock_rec_has_to_wait(
type_mode, lock_is_on_supremum); type_mode, lock_is_on_supremum);
fprintf(stderr, fprintf(stderr,
"conflicts states: my %d locked %d\n", "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) ); wsrep_thd_conflict_state(lock2->trx->mysql_thd, FALSE) );
lock_rec_print(stderr, lock2); lock_rec_print(stderr, lock2);
if (for_locking) return FALSE; if (for_locking) return FALSE;
@@ -1714,7 +1721,7 @@ lock_rec_other_has_expl_req(
ulint heap_no,/*!< in: heap number of the record */ ulint heap_no,/*!< in: heap number of the record */
trx_id_t trx_id) /*!< in: transaction */ trx_id_t trx_id) /*!< in: transaction */
{ {
const lock_t* lock; lock_t* lock;
ut_ad(lock_mutex_own()); ut_ad(lock_mutex_own());
ut_ad(mode == LOCK_X || mode == LOCK_S); 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); for (lock = lock_rec_get_first(block, heap_no);
lock != NULL; 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 if (lock->trx->id != trx_id
&& (gap && (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. in the queue, so that we have to wait.
@return lock or NULL */ @return lock or NULL */
static static
const lock_t* lock_t*
lock_rec_other_has_conflicting( lock_rec_other_has_conflicting(
/*===========================*/ /*===========================*/
enum lock_mode mode, /*!< in: LOCK_S or LOCK_X, 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 */ ulint heap_no,/*!< in: heap number of the record */
const trx_t* trx) /*!< in: our transaction */ const trx_t* trx) /*!< in: our transaction */
{ {
const lock_t* lock; lock_t* lock;
ibool is_supremum; ibool is_supremum;
ut_ad(lock_mutex_own()); ut_ad(lock_mutex_own());
@@ -1831,13 +1838,16 @@ lock_rec_other_has_conflicting(
for (lock = lock_rec_get_first(block, heap_no); for (lock = lock_rec_get_first(block, heap_no);
lock != NULL; lock != NULL;
lock = lock_rec_get_next_const(heap_no, lock)) { lock = lock_rec_get_next(heap_no, lock)) {
#ifdef WITH_WSREP #ifdef WITH_WSREP
if (lock_rec_has_to_wait(TRUE, trx, mode, lock, is_supremum)) { if (lock_rec_has_to_wait(TRUE, trx, mode, lock, is_supremum)) {
if (wsrep_on_trx(trx)) { if (wsrep_on_trx(trx)) {
trx_mutex_enter(lock->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); trx_mutex_exit(lock->trx);
} }
#else #else
@@ -2045,15 +2055,17 @@ wsrep_print_wait_locks(
{ {
if (wsrep_debug && c_lock->trx->lock.wait_lock != c_lock) { if (wsrep_debug && c_lock->trx->lock.wait_lock != c_lock) {
fprintf(stderr, "WSREP: c_lock != wait lock\n"); 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); lock_table_print(stderr, c_lock);
else } else {
lock_rec_print(stderr, c_lock); 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); lock_table_print(stderr, c_lock->trx->lock.wait_lock);
else } else {
lock_rec_print(stderr, c_lock->trx->lock.wait_lock); lock_rec_print(stderr, c_lock->trx->lock.wait_lock);
}
} }
} }
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
@@ -2358,8 +2370,8 @@ lock_rec_create(
if (wsrep_debug) { if (wsrep_debug) {
fprintf( fprintf(
stderr, stderr,
"WSREP: c_lock canceled %llu\n", "WSREP: c_lock canceled " TRX_ID_FMT "\n",
(ulonglong) c_lock->trx->id); c_lock->trx->id);
} }
/* have to bail out here to avoid lock_set_lock... */ /* have to bail out here to avoid lock_set_lock... */
@@ -2551,6 +2563,16 @@ lock_rec_enqueue_waiting(
err = DB_LOCK_WAIT; 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. // Move it only when it does not cause a deadlock.
if (err != DB_DEADLOCK if (err != DB_DEADLOCK
&& innodb_lock_schedule_algorithm && innodb_lock_schedule_algorithm
@@ -2981,6 +3003,15 @@ lock_rec_has_to_wait_in_queue(
#ifdef WITH_WSREP #ifdef WITH_WSREP
if (wsrep_thd_is_BF(wait_lock->trx->mysql_thd, FALSE) && if (wsrep_thd_is_BF(wait_lock->trx->mysql_thd, FALSE) &&
wsrep_thd_is_BF(lock->trx->mysql_thd, TRUE)) { 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 */ /* don't wait for another BF lock */
continue; continue;
} }
@@ -3139,7 +3170,7 @@ lock_grant_and_move_on_page(
&& !lock_rec_has_to_wait_in_queue(lock)) { && !lock_rec_has_to_wait_in_queue(lock)) {
lock_grant(lock, false); lock_grant(lock, false);
if (previous != NULL) { if (previous != NULL) {
/* Move the lock to the head of the list. */ /* Move the lock to the head of the list. */
HASH_GET_NEXT(hash, previous) = HASH_GET_NEXT(hash, lock); HASH_GET_NEXT(hash, previous) = HASH_GET_NEXT(hash, lock);
@@ -5017,8 +5048,8 @@ lock_table_create(
} }
if (wsrep_debug) { if (wsrep_debug) {
fprintf(stderr, "WSREP: c_lock canceled %llu\n", fprintf(stderr, "WSREP: c_lock canceled " TRX_ID_FMT "\n",
(ulonglong) c_lock->trx->id); c_lock->trx->id);
} }
} }
if (c_lock) { if (c_lock) {
@@ -5297,7 +5328,7 @@ Checks if other transactions have an incompatible mode lock request in
the lock queue. the lock queue.
@return lock or NULL */ @return lock or NULL */
UNIV_INLINE UNIV_INLINE
const lock_t* lock_t*
lock_table_other_has_incompatible( lock_table_other_has_incompatible(
/*==============================*/ /*==============================*/
const trx_t* trx, /*!< in: transaction, or NULL if all 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 */ const dict_table_t* table, /*!< in: table */
enum lock_mode mode) /*!< in: lock mode */ enum lock_mode mode) /*!< in: lock mode */
{ {
const lock_t* lock; lock_t* lock;
ut_ad(lock_mutex_own()); ut_ad(lock_mutex_own());
@@ -5361,7 +5392,7 @@ lock_table(
#endif #endif
trx_t* trx; trx_t* trx;
dberr_t err; dberr_t err;
const lock_t* wait_for; lock_t* wait_for;
ut_ad(table != NULL); ut_ad(table != NULL);
ut_ad(thr != NULL); ut_ad(thr != NULL);
@@ -5412,13 +5443,13 @@ lock_table(
if (wait_for != NULL) { if (wait_for != NULL) {
#ifdef WITH_WSREP #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 #else
err = lock_table_enqueue_waiting(mode | flags, table, thr); err = lock_table_enqueue_waiting(mode | flags, table, thr);
#endif #endif
} else { } else {
#ifdef WITH_WSREP #ifdef WITH_WSREP
lock_table_create(c_lock, table, mode | flags, trx); lock_table_create(c_lock, table, mode | flags, trx);
#else #else
lock_table_create(table, mode | flags, trx); lock_table_create(table, mode | flags, trx);
#endif #endif
@@ -7101,10 +7132,10 @@ lock_rec_insert_check_and_lock(
on the successor, which produced an unnecessary deadlock. */ on the successor, which produced an unnecessary deadlock. */
#ifdef WITH_WSREP #ifdef WITH_WSREP
if ((c_lock = (ib_lock_t*)lock_rec_other_has_conflicting( if ((c_lock = lock_rec_other_has_conflicting(
static_cast<enum lock_mode>( static_cast<enum lock_mode>(
LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION), LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION),
block, next_rec_heap_no, trx))) { block, next_rec_heap_no, trx))) {
#else #else
if (lock_rec_other_has_conflicting( if (lock_rec_other_has_conflicting(
static_cast<enum lock_mode>( static_cast<enum lock_mode>(
@@ -7117,7 +7148,7 @@ lock_rec_insert_check_and_lock(
#ifdef WITH_WSREP #ifdef WITH_WSREP
err = lock_rec_enqueue_waiting(c_lock, 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); block, next_rec_heap_no, index, thr);
#else #else
err = lock_rec_enqueue_waiting( err = lock_rec_enqueue_waiting(

View File

@@ -191,22 +191,25 @@ lock_wait_table_reserve_slot(
/*********************************************************************//** /*********************************************************************//**
check if lock timeout was for priority thread, check if lock timeout was for priority thread,
as a side effect trigger lock monitor 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 */ @return false for regular lock timeout */
static ibool static
bool
wsrep_is_BF_lock_timeout( wsrep_is_BF_lock_timeout(
/*====================*/ const trx_t* trx,
trx_t* trx) /* in: trx to check for lock priority */ bool locked = true)
{ {
if (wsrep_on_trx(trx) && if (wsrep_on_trx(trx)
wsrep_thd_is_BF(trx->mysql_thd, FALSE)) { && wsrep_thd_is_BF(trx->mysql_thd, FALSE)) {
fprintf(stderr, "WSREP: BF lock wait long\n"); fprintf(stderr, "WSREP: BF lock wait long for trx " TRX_ID_FMT "\n", trx->id);
srv_print_innodb_monitor = TRUE; srv_print_innodb_monitor = TRUE;
srv_print_innodb_lock_monitor = TRUE; srv_print_innodb_lock_monitor = TRUE;
os_event_set(srv_monitor_event); os_event_set(srv_monitor_event);
return TRUE; return true;
} }
return FALSE; return false;
} }
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
/***************************************************************//** /***************************************************************//**
@@ -402,15 +405,15 @@ lock_wait_suspend_thread(
if (lock_wait_timeout < 100000000 if (lock_wait_timeout < 100000000
&& wait_time > (double) lock_wait_timeout) { && wait_time > (double) lock_wait_timeout) {
#ifdef WITH_WSREP #ifdef WITH_WSREP
if (!wsrep_on_trx(trx) || if (!wsrep_on_trx(trx) ||
(!wsrep_is_BF_lock_timeout(trx) && (!wsrep_is_BF_lock_timeout(trx) &&
trx->error_state != DB_DEADLOCK)) { trx->error_state != DB_DEADLOCK)) {
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
trx->error_state = DB_LOCK_WAIT_TIMEOUT; trx->error_state = DB_LOCK_WAIT_TIMEOUT;
#ifdef WITH_WSREP #ifdef WITH_WSREP
} }
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
MONITOR_INC(MONITOR_TIMEOUT); MONITOR_INC(MONITOR_TIMEOUT);
} }

View File

@@ -1,6 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1997, 2016, Oracle and/or its affiliates. All Rights Reserved. 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 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 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); 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); err = row_undo(node, thr);
trx->error_state = err; trx->error_state = err;

View File

@@ -24,6 +24,9 @@ Transaction rollback
Created 3/26/1996 Heikki Tuuri Created 3/26/1996 Heikki Tuuri
*******************************************************/ *******************************************************/
#include "my_config.h"
#include <my_systemd.h>
#include "trx0roll.h" #include "trx0roll.h"
#ifdef UNIV_NONINL #ifdef UNIV_NONINL
@@ -60,14 +63,7 @@ rollback */
bool trx_rollback_or_clean_is_active; bool trx_rollback_or_clean_is_active;
/** In crash recovery, the current trx to be rolled back; NULL otherwise */ /** In crash recovery, the current trx to be rolled back; NULL otherwise */
static const trx_t* trx_roll_crash_recv_trx = NULL; const trx_t* trx_roll_crash_recv_trx;
/** 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;
/****************************************************************//** /****************************************************************//**
Finishes a transaction rollback. */ Finishes a transaction rollback. */
@@ -564,8 +560,6 @@ trx_rollback_active(
que_thr_t* thr; que_thr_t* thr;
roll_node_t* roll_node; roll_node_t* roll_node;
dict_table_t* table; dict_table_t* table;
ib_int64_t rows_to_undo;
const char* unit = "";
ibool dictionary_locked = FALSE; ibool dictionary_locked = FALSE;
heap = mem_heap_create(512); heap = mem_heap_create(512);
@@ -584,30 +578,8 @@ trx_rollback_active(
ut_a(thr == que_fork_start_command(fork)); ut_a(thr == que_fork_start_command(fork));
mutex_enter(&trx_sys->mutex);
trx_roll_crash_recv_trx = trx; 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) { if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) {
row_mysql_lock_data_dictionary(trx); row_mysql_lock_data_dictionary(trx);
dictionary_locked = TRUE; dictionary_locked = TRUE;
@@ -618,6 +590,16 @@ trx_rollback_active(
que_run_threads(roll_node->undo_thr); 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)); trx_rollback_finish(thr_get_trx(roll_node->undo_thr));
/* Free the memory reserved by the undo graph */ /* 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) { if (dictionary_locked) {
row_mysql_unlock_data_dictionary(trx); 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); mem_heap_free(heap);
trx_roll_crash_recv_trx = NULL; trx_roll_crash_recv_trx = NULL;
@@ -685,7 +668,7 @@ ibool
trx_rollback_resurrected( trx_rollback_resurrected(
/*=====================*/ /*=====================*/
trx_t* trx, /*!< in: transaction to rollback or clean */ 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 */ TRUE=roll back all non-PREPARED transactions */
{ {
ut_ad(mutex_own(&trx_sys->mutex)); ut_ad(mutex_own(&trx_sys->mutex));
@@ -696,16 +679,15 @@ trx_rollback_resurrected(
to accidentally clean up a non-recovered transaction here. */ to accidentally clean up a non-recovered transaction here. */
trx_mutex_enter(trx); trx_mutex_enter(trx);
bool is_recovered = trx->is_recovered; if (!trx->is_recovered) {
trx_state_t state = trx->state; func_exit:
trx_mutex_exit(trx); trx_mutex_exit(trx);
if (!is_recovered) {
return(FALSE); return(FALSE);
} }
switch (state) { switch (trx->state) {
case TRX_STATE_COMMITTED_IN_MEMORY: case TRX_STATE_COMMITTED_IN_MEMORY:
trx_mutex_exit(trx);
mutex_exit(&trx_sys->mutex); mutex_exit(&trx_sys->mutex);
fprintf(stderr, fprintf(stderr,
"InnoDB: Cleaning up trx with id " TRX_ID_FMT "\n", "InnoDB: Cleaning up trx with id " TRX_ID_FMT "\n",
@@ -714,21 +696,83 @@ trx_rollback_resurrected(
trx_free_for_background(trx); trx_free_for_background(trx);
return(TRUE); return(TRUE);
case TRX_STATE_ACTIVE: 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); mutex_exit(&trx_sys->mutex);
trx_rollback_active(trx); 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); trx_free_for_background(trx);
return(TRUE); return(TRUE);
} }
return(FALSE); return(FALSE);
case TRX_STATE_PREPARED: case TRX_STATE_PREPARED:
return(FALSE); goto func_exit;
case TRX_STATE_NOT_STARTED: case TRX_STATE_NOT_STARTED:
break; break;
} }
ut_error; 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); 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 /* If this function does a cleanup or rollback
then it will release the trx_sys->mutex, therefore then it will release the trx_sys->mutex, therefore
we need to reacquire it before retrying the loop. */ 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); mutex_enter(&trx_sys->mutex);
@@ -1118,7 +1156,6 @@ trx_roll_pop_top_rec_of_trx(
undo_no_t undo_no; undo_no_t undo_no;
ibool is_insert; ibool is_insert;
trx_rseg_t* rseg; trx_rseg_t* rseg;
ulint progress_pct;
mtr_t mtr; mtr_t mtr;
rseg = trx->rseg; rseg = trx->rseg;
@@ -1176,27 +1213,6 @@ try_again:
ut_ad(undo_no + 1 == trx->undo_no); 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; trx->undo_no = undo_no;
if (!trx_undo_arr_store_info(trx, undo_no)) { if (!trx_undo_arr_store_info(trx, undo_no)) {

View File

@@ -2023,10 +2023,14 @@ trx_undo_free_prepared(
/* fall through */ /* fall through */
case TRX_UNDO_ACTIVE: case TRX_UNDO_ACTIVE:
/* lock_trx_release_locks() assigns /* 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 ut_a(srv_read_only_mode
|| srv_apply_log_only || srv_apply_log_only
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO
|| srv_fast_shutdown);
break; break;
default: default:
ut_error; ut_error;
@@ -2048,10 +2052,14 @@ trx_undo_free_prepared(
/* fall through */ /* fall through */
case TRX_UNDO_ACTIVE: case TRX_UNDO_ACTIVE:
/* lock_trx_release_locks() assigns /* 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 ut_a(srv_read_only_mode
|| srv_apply_log_only || srv_apply_log_only
|| srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO); || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO
|| srv_fast_shutdown);
break; break;
default: default:
ut_error; ut_error;