Merge 10.5 into 10.6

This commit is contained in:
Marko Mäkelä 2021-05-18 11:13:45 +03:00
commit f09d33f521
31 changed files with 606 additions and 329 deletions

View File

@ -114,7 +114,7 @@ static void warning(const char *format, ...) ATTRIBUTE_FORMAT(printf, 1, 2);
static bool one_database=0, one_table=0, to_last_remote_log= 0, disable_log_bin= 0; static bool one_database=0, one_table=0, to_last_remote_log= 0, disable_log_bin= 0;
static bool opt_hexdump= 0, opt_version= 0; static bool opt_hexdump= 0, opt_version= 0;
const char *base64_output_mode_names[]= const char *base64_output_mode_names[]=
{"NEVER", "AUTO", "ALWAYS", "UNSPEC", "DECODE-ROWS", NullS}; {"NEVER", "AUTO", "UNSPEC", "DECODE-ROWS", NullS};
TYPELIB base64_output_mode_typelib= TYPELIB base64_output_mode_typelib=
{ array_elements(base64_output_mode_names) - 1, "", { array_elements(base64_output_mode_names) - 1, "",
base64_output_mode_names, NULL }; base64_output_mode_names, NULL };
@ -836,53 +836,6 @@ static bool shall_skip_table(const char *log_tblname)
strcmp(log_tblname, table); strcmp(log_tblname, table);
} }
/**
Prints the given event in base64 format.
The header is printed to the head cache and the body is printed to
the body cache of the print_event_info structure. This allows all
base64 events corresponding to the same statement to be joined into
one BINLOG statement.
@param[in] ev Log_event to print.
@param[in,out] result_file FILE to which the output will be written.
@param[in,out] print_event_info Parameters and context state
determining how to print.
@retval ERROR_STOP An error occurred - the program should terminate.
@retval OK_CONTINUE No error, the program should continue.
*/
static Exit_status
write_event_header_and_base64(Log_event *ev, FILE *result_file,
PRINT_EVENT_INFO *print_event_info)
{
IO_CACHE *head= &print_event_info->head_cache;
IO_CACHE *body= &print_event_info->body_cache;
DBUG_ENTER("write_event_header_and_base64");
/* Write header and base64 output to cache */
if (ev->print_header(head, print_event_info, FALSE))
DBUG_RETURN(ERROR_STOP);
DBUG_ASSERT(print_event_info->base64_output_mode == BASE64_OUTPUT_ALWAYS);
if (ev->print_base64(body, print_event_info,
print_event_info->base64_output_mode !=
BASE64_OUTPUT_DECODE_ROWS))
DBUG_RETURN(ERROR_STOP);
/* Read data from cache and write to result file */
if (copy_event_cache_to_file_and_reinit(head, result_file) ||
copy_event_cache_to_file_and_reinit(body, result_file))
{
error("Error writing event to file.");
DBUG_RETURN(ERROR_STOP);
}
DBUG_RETURN(OK_CONTINUE);
}
static bool print_base64(PRINT_EVENT_INFO *print_event_info, Log_event *ev) static bool print_base64(PRINT_EVENT_INFO *print_event_info, Log_event *ev)
{ {
/* /*
@ -1134,19 +1087,9 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
qe->flags|= LOG_EVENT_SUPPRESS_USE_F; qe->flags|= LOG_EVENT_SUPPRESS_USE_F;
} }
print_use_stmt(print_event_info, qe); print_use_stmt(print_event_info, qe);
if (opt_base64_output_mode == BASE64_OUTPUT_ALWAYS)
{
if ((retval= write_event_header_and_base64(ev, result_file,
print_event_info)) !=
OK_CONTINUE)
goto end;
}
else
{
print_skip_replication_statement(print_event_info, ev); print_skip_replication_statement(print_event_info, ev);
if (ev->print(result_file, print_event_info)) if (ev->print(result_file, print_event_info))
goto err; goto err;
}
if (head->error == -1) if (head->error == -1)
goto err; goto err;
break; break;
@ -1170,19 +1113,9 @@ Exit_status process_event(PRINT_EVENT_INFO *print_event_info, Log_event *ev,
filename and use LOCAL), prepared in the 'case EXEC_LOAD_EVENT' filename and use LOCAL), prepared in the 'case EXEC_LOAD_EVENT'
below. below.
*/ */
if (opt_base64_output_mode == BASE64_OUTPUT_ALWAYS)
{
if ((retval= write_event_header_and_base64(ce, result_file,
print_event_info)) !=
OK_CONTINUE)
goto end;
}
else
{
print_skip_replication_statement(print_event_info, ev); print_skip_replication_statement(print_event_info, ev);
if (ce->print(result_file, print_event_info, TRUE)) if (ce->print(result_file, print_event_info, TRUE))
goto err; goto err;
}
// If this binlog is not 3.23 ; why this test?? // If this binlog is not 3.23 ; why this test??
if (glob_description_event->binlog_version >= 3) if (glob_description_event->binlog_version >= 3)
{ {
@ -1589,12 +1522,10 @@ static struct my_option my_options[] =
"--verbose option is also given; " "--verbose option is also given; "
"'auto' prints base64 only when necessary (i.e., for row-based events and " "'auto' prints base64 only when necessary (i.e., for row-based events and "
"format description events); " "format description events); "
"'always' prints base64 whenever possible. " "If no --base64-output=name option is given at all, the default is "
"--base64-output with no 'name' argument is equivalent to " "'auto'.",
"--base64-output=always and is also deprecated. If no "
"--base64-output[=name] option is given at all, the default is 'auto'.",
&opt_base64_output_mode_str, &opt_base64_output_mode_str, &opt_base64_output_mode_str, &opt_base64_output_mode_str,
0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
/* /*
mysqlbinlog needs charsets knowledge, to be able to convert a charset mysqlbinlog needs charsets knowledge, to be able to convert a charset
number found in binlog to a charset name (to be able to print things number found in binlog to a charset name (to be able to print things
@ -2038,10 +1969,6 @@ get_one_option(const struct my_option *opt, const char *argument, const char *fi
stop_datetime= convert_str_to_timestamp(stop_datetime_str); stop_datetime= convert_str_to_timestamp(stop_datetime_str);
break; break;
case OPT_BASE64_OUTPUT_MODE: case OPT_BASE64_OUTPUT_MODE:
if (argument == NULL)
opt_base64_output_mode= BASE64_OUTPUT_ALWAYS;
else
{
int val; int val;
if ((val= find_type_with_warning(argument, &base64_output_mode_typelib, if ((val= find_type_with_warning(argument, &base64_output_mode_typelib,
@ -2050,8 +1977,7 @@ get_one_option(const struct my_option *opt, const char *argument, const char *fi
sf_leaking_memory= 1; /* no memory leak reports here */ sf_leaking_memory= 1; /* no memory leak reports here */
die(); die();
} }
opt_base64_output_mode= (enum_base64_output_mode) (val - 1); opt_base64_output_mode= (enum_base64_output_mode)(val - 1);
}
break; break;
case OPT_REWRITE_DB: // db_from->db_to case OPT_REWRITE_DB: // db_from->db_to
{ {
@ -2901,8 +2827,7 @@ static Exit_status check_header(IO_CACHE* file,
(ulonglong)tmp_pos); (ulonglong)tmp_pos);
return ERROR_STOP; return ERROR_STOP;
} }
if (opt_base64_output_mode == BASE64_OUTPUT_AUTO if (opt_base64_output_mode == BASE64_OUTPUT_AUTO)
|| opt_base64_output_mode == BASE64_OUTPUT_ALWAYS)
{ {
/* /*
process_event will delete *description_event and set it to process_event will delete *description_event and set it to

View File

@ -141,7 +141,7 @@ Display a help message and exit\&.
.\} .\}
.\" mysqlbinlog: base64-output option .\" mysqlbinlog: base64-output option
.\" base64-output option: mysqlbinlog .\" base64-output option: mysqlbinlog
\fB\-\-base64\-output[=\fR\fB\fIvalue\fR\fR\fB]\fR \fB\-\-base64\-output=\fR\fB\fIvalue\fR\fR\fB\fR
.sp .sp
This option determines when events should be displayed encoded as base\-64 strings using This option determines when events should be displayed encoded as base\-64 strings using
BINLOG BINLOG
@ -192,22 +192,6 @@ to re\-execute binary log file contents\&. The other option values are intended
.sp -1 .sp -1
.IP \(bu 2.3 .IP \(bu 2.3
.\} .\}
ALWAYS
displays
BINLOG
statements whenever possible\&. This is the implied value if the option is given as
\fB\-\-base64\-output\fR
without a value\&. Both ALWAYS and not giving a value are deprecated.
.RE
.sp
.RS 4
.ie n \{\
\h'-04'\(bu\h'+03'\c
.\}
.el \{\
.sp -1
.IP \(bu 2.3
.\}
NEVER NEVER
causes causes
BINLOG BINLOG

View File

@ -775,7 +775,7 @@ call p_verify_status_increment(2, 0, 2, 0);
commit; commit;
call p_verify_status_increment(0, 0, 0, 0); call p_verify_status_increment(0, 0, 0, 0);
check table t1, t2, t3; check table t1, t2, t3;
call p_verify_status_increment(6, 0, 6, 0); call p_verify_status_increment(4, 0, 4, 0);
commit; commit;
call p_verify_status_increment(0, 0, 0, 0); call p_verify_status_increment(0, 0, 0, 0);
drop view v1; drop view v1;

View File

@ -139,7 +139,7 @@ sub do_args($$$$$) {
my $v = $debuggers{$k}; my $v = $debuggers{$k};
# on windows mtr args are quoted (for system), otherwise not (for exec) # on windows mtr args are quoted (for system), otherwise not (for exec)
sub quote($) { $_[0] =~ / / ? "\"$_[0]\"" : $_[0] } sub quote($) { $_[0] =~ /[; ]/ ? "\"$_[0]\"" : $_[0] }
sub unquote($) { $_[0] =~ s/^"(.*)"$/$1/; $_[0] } sub unquote($) { $_[0] =~ s/^"(.*)"$/$1/; $_[0] }
sub quote_from_mtr($) { IS_WINDOWS() ? $_[0] : quote($_[0]) } sub quote_from_mtr($) { IS_WINDOWS() ? $_[0] : quote($_[0]) }
sub unquote_for_mtr($) { IS_WINDOWS() ? $_[0] : unquote($_[0]) } sub unquote_for_mtr($) { IS_WINDOWS() ? $_[0] : unquote($_[0]) }

View File

@ -879,7 +879,7 @@ Table Op Msg_type Msg_text
test.t1 check status OK test.t1 check status OK
test.t2 check status OK test.t2 check status OK
test.t3 check status OK test.t3 check status OK
call p_verify_status_increment(6, 0, 6, 0); call p_verify_status_increment(4, 0, 4, 0);
SUCCESS SUCCESS
commit; commit;

View File

@ -878,8 +878,19 @@ ROLLBACK /* added by mysqlbinlog */;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/; /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
End of 5.0 tests End of 5.0 tests
End of 5.1 tests End of 5.1 tests
# Expect deprecation warning. #
# Expect deprecation warning again. # Expect error for invalid --base64-output argument value.
# MYSQL_BINLOG std_data/master-bin.000001 --base64-output=always 2>&1
Unknown option to base64-output: always
Alternatives are: 'NEVER','AUTO','UNSPEC','DECODE-ROWS'
#
# Expect error for incomplete --base64-output argument.
# MYSQL_BINLOG std_data/master-bin.000001 --base64-output 2>&1
mysqlbinlog: option '--base64-output' requires an argument
#
# Ensure --base64-output=auto outputs the same result as unspecified
# MYSQL_BINLOG -v MYSQLD_DATADIR/master-bin.000001 > MYSQLTEST_VARDIR/tmp/mysqlbinlog_nob64spec.out
# MYSQL_BINLOG --base64-output=auto -v MYSQLD_DATADIR/master-bin.000001 > MYSQLTEST_VARDIR/tmp/mysqlbinlog_b64auto.out
RESET MASTER; RESET MASTER;
CREATE DATABASE test1; CREATE DATABASE test1;
USE test1; USE test1;

View File

@ -521,18 +521,32 @@ remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog.warn.empty;
remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog.warn; remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog.warn;
# #
# WL#5625: Deprecate mysqlbinlog options --base64-output=always and --base64-output # MDEV-25222: Remove mysqlbinlog options --base64-output=always and --base64-output
# #
--echo # Expect deprecation warning. --echo #
--exec $MYSQL_BINLOG --base64-output=always std_data/master-bin.000001 > /dev/null 2> $MYSQLTEST_VARDIR/tmp/mysqlbinlog.warn --echo # Expect error for invalid --base64-output argument value.
--cat_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog.warn --echo # MYSQL_BINLOG std_data/master-bin.000001 --base64-output=always 2>&1
--error 1
--exec $MYSQL_BINLOG std_data/master-bin.000001 --base64-output=always 2>&1
--echo # Expect deprecation warning again. --echo #
--exec $MYSQL_BINLOG --base64-output std_data/master-bin.000001 > /dev/null 2> $MYSQLTEST_VARDIR/tmp/mysqlbinlog.warn --echo # Expect error for incomplete --base64-output argument.
--cat_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog.warn --echo # MYSQL_BINLOG std_data/master-bin.000001 --base64-output 2>&1
# The error produces the absolute path of the mysqlbinlog executable, remove it.
--replace_regex /.*mysqlbinlog.*:/mysqlbinlog:/i
--error 1
--exec $MYSQL_BINLOG std_data/master-bin.000001 --base64-output 2>&1
--echo #
--echo # Ensure --base64-output=auto outputs the same result as unspecified
--echo # MYSQL_BINLOG -v MYSQLD_DATADIR/master-bin.000001 > MYSQLTEST_VARDIR/tmp/mysqlbinlog_nob64spec.out
--exec $MYSQL_BINLOG -v $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_nob64spec.out
--echo # MYSQL_BINLOG --base64-output=auto -v MYSQLD_DATADIR/master-bin.000001 > MYSQLTEST_VARDIR/tmp/mysqlbinlog_b64auto.out
--exec $MYSQL_BINLOG --base64-output=auto -v $MYSQLD_DATADIR/master-bin.000001 > $MYSQLTEST_VARDIR/tmp/mysqlbinlog_b64auto.out
--diff_files $MYSQLTEST_VARDIR/tmp/mysqlbinlog_nob64spec.out $MYSQLTEST_VARDIR/tmp/mysqlbinlog_b64auto.out
--remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog_nob64spec.out
--remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog_b64auto.out
# Clean up this part of the test.
--remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog.warn
# BUG#50914 # BUG#50914
# This test verifies if the approach of the mysqlbinlog prints # This test verifies if the approach of the mysqlbinlog prints

View File

@ -0,0 +1,40 @@
#
# Kill OPTIMIZE command prior to table modification
#
RESET MASTER;
CREATE TABLE t1 (f INT) ENGINE=INNODB;
CREATE TABLE t2 (f INT) ENGINE=INNODB;
connect con1,127.0.0.1,root,,test,$MASTER_MYPORT,;
connection con1;
SET debug_sync='admin_command_kill_before_modify SIGNAL ready_to_be_killed WAIT_FOR master_cont';
OPTIMIZE TABLE t1,t2;
connection default;
SET debug_sync='now WAIT_FOR ready_to_be_killed';
KILL THD_ID;
SET debug_sync = 'reset';
disconnect con1;
include/show_binlog_events.inc
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (f INT) ENGINE=INNODB
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE t2 (f INT) ENGINE=INNODB
DROP TABLE t1,t2;
FLUSH LOGS;
#
# Kill OPTIMIZE command after table modification
#
CREATE TABLE t1 (f INT) ENGINE=INNODB;
CREATE TABLE t2 (f INT) ENGINE=INNODB;
connect con1,127.0.0.1,root,,test,$MASTER_MYPORT,;
connection con1;
SET debug_sync='admin_command_kill_after_modify SIGNAL ready_to_be_killed WAIT_FOR master_cont';
OPTIMIZE TABLE t1,t2;
connection default;
SET debug_sync='now WAIT_FOR ready_to_be_killed';
KILL THD_ID;
SET debug_sync = 'reset';
disconnect con1;
DROP TABLE t1,t2;
FLUSH LOGS;
FOUND 1 /OPTIMIZE TABLE t1,t2/ in mysqlbinlog.out

View File

@ -0,0 +1,95 @@
# ==== Purpose ====
#
# Test verifies that when an admin command execution is interrupted by KILL
# command it should stop its execution. The admin command in binary log should
# contain only the list of tables which have successfully executed admin
# command prior to kill.
#
# ==== Implementation ====
#
# Steps:
# 0 - Create two table t1,t2.
# 1 - Execute OPTIMIZE TABLE t1,t2 command.
# 2 - Using debug sync mechanism kill OPTIMIZE TABLE command at a stage
# where it has not optimized any table.
# 3 - Check that OPTIMIZE TABLE command is not written to binary log.
# 4 - Using debug sync mechanism hold the execution of OPTIMIZE TABLE after
# t1 table optimization. Now kill the OPTIMIZE TABLE command.
# 5 - Observe the binlog output, the OPTIMIZE TABLE command should display `t1,t2`.
# 6 - Please note that, we binlog the entire query even if at least one
# table is modified as admin commands are safe to replicate and they will
# not make the slave to diverge.
#
# ==== References ====
#
# MDEV-22530: Aborting OPTIMIZE TABLE still logs in binary log and replicates to the Slave server.
#
--source include/have_log_bin.inc
--source include/have_debug.inc
--source include/have_debug_sync.inc
--source include/have_innodb.inc
--echo #
--echo # Kill OPTIMIZE command prior to table modification
--echo #
RESET MASTER;
CREATE TABLE t1 (f INT) ENGINE=INNODB;
CREATE TABLE t2 (f INT) ENGINE=INNODB;
--connect(con1,127.0.0.1,root,,test,$MASTER_MYPORT,)
--connection con1
SET debug_sync='admin_command_kill_before_modify SIGNAL ready_to_be_killed WAIT_FOR master_cont';
--send OPTIMIZE TABLE t1,t2
--connection default
SET debug_sync='now WAIT_FOR ready_to_be_killed';
--let $thd_id= `SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST WHERE INFO LIKE '%OPTIMIZE TABLE %'`
# Now kill.
--replace_result $thd_id THD_ID
eval KILL $thd_id;
SET debug_sync = 'reset';
--disconnect con1
--source include/show_binlog_events.inc
DROP TABLE t1,t2;
FLUSH LOGS;
--echo #
--echo # Kill OPTIMIZE command after table modification
--echo #
CREATE TABLE t1 (f INT) ENGINE=INNODB;
CREATE TABLE t2 (f INT) ENGINE=INNODB;
--connect(con1,127.0.0.1,root,,test,$MASTER_MYPORT,)
--connection con1
SET debug_sync='admin_command_kill_after_modify SIGNAL ready_to_be_killed WAIT_FOR master_cont';
--send OPTIMIZE TABLE t1,t2
--connection default
SET debug_sync='now WAIT_FOR ready_to_be_killed';
--let $thd_id= `SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST WHERE INFO LIKE '%OPTIMIZE TABLE %'`
# Now kill.
--replace_result $thd_id THD_ID
eval KILL $thd_id;
SET debug_sync = 'reset';
--disconnect con1
DROP TABLE t1,t2;
let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1);
FLUSH LOGS;
--let $MYSQLD_DATADIR= `select @@datadir`
--exec $MYSQL_BINLOG $MYSQLD_DATADIR/$binlog_file > $MYSQLTEST_VARDIR/tmp/mysqlbinlog.out
--let SEARCH_PATTERN= OPTIMIZE TABLE t1,t2
--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.out
--source include/search_pattern_in_file.inc
--remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog.out

View File

@ -16,7 +16,6 @@ galera_gtid_2_cluster : MDEV-23775 Galera test failure on galera_3nodes.galera_g
galera_ist_gcache_rollover : MDEV-23578 WSREP: exception caused by message: {v=0,t=1,ut=255,o=4,s=0,sr=0,as=1,f=6,src=50524cfe,srcvid=view_id(REG,50524cfe,4),insvid=view_id(UNKNOWN,00000000,0),ru=00000000,r=[-1,-1],fs=75,nl=(} galera_ist_gcache_rollover : MDEV-23578 WSREP: exception caused by message: {v=0,t=1,ut=255,o=4,s=0,sr=0,as=1,f=6,src=50524cfe,srcvid=view_id(REG,50524cfe,4),insvid=view_id(UNKNOWN,00000000,0),ru=00000000,r=[-1,-1],fs=75,nl=(}
galera_load_data_ist : MDEV-24639 galera_3nodes.galera_load_data_ist MTR failed with SIGABRT: query 'reap' failed: 2013: Lost connection to server during query galera_load_data_ist : MDEV-24639 galera_3nodes.galera_load_data_ist MTR failed with SIGABRT: query 'reap' failed: 2013: Lost connection to server during query
galera_load_data_ist : MDEV-24639 galera_3nodes.galera_load_data_ist MTR failed with SIGABRT: query 'reap' failed: 2013: Lost connection to server during query galera_load_data_ist : MDEV-24639 galera_3nodes.galera_load_data_ist MTR failed with SIGABRT: query 'reap' failed: 2013: Lost connection to server during query
galera_pc_bootstrap : MDEV-24650 galera_pc_bootstrap MTR failed: Could not execute 'check-testcase' before testcase
galera_safe_to_bootstrap : MDEV-24097 galera_3nodes.galera_safe_to_bootstrap MTR sporadaically fails: Failed to start mysqld or mysql_shutdown failed galera_safe_to_bootstrap : MDEV-24097 galera_3nodes.galera_safe_to_bootstrap MTR sporadaically fails: Failed to start mysqld or mysql_shutdown failed
galera_slave_options_do : MDEV-8798 galera_slave_options_do : MDEV-8798
galera_slave_options_ignore : MDEV-8798 galera_slave_options_ignore : MDEV-8798

View File

@ -0,0 +1,79 @@
include/rpl_init.inc [topology=1->2]
connection server_1;
FLUSH TABLES;
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
connection server_2;
SET @save_slave_parallel_threads= @@GLOBAL.slave_parallel_threads;
SET @save_slave_parallel_mode= @@GLOBAL.slave_parallel_mode;
include/stop_slave.inc
SET GLOBAL slave_parallel_threads=2;
SET GLOBAL slave_parallel_mode=optimistic;
include/start_slave.inc
connection server_1;
CREATE TABLE t1(a INT) ENGINE=INNODB;
OPTIMIZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 optimize note Table does not support optimize, doing recreate + analyze instead
test.t1 optimize status OK
INSERT INTO t1 VALUES(1);
INSERT INTO t1 SELECT 1+a FROM t1;
INSERT INTO t1 SELECT 2+a FROM t1;
connection server_2;
#
# Verify that following admin commands are marked as ddl
# 'OPTIMIZE TABLE', 'REPAIR TABLE' and 'ANALYZE TABLE'
#
connection server_1;
OPTIMIZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 optimize note Table does not support optimize, doing recreate + analyze instead
test.t1 optimize status OK
REPAIR TABLE t1;
Table Op Msg_type Msg_text
test.t1 repair note The storage engine for the table doesn't support repair
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze status Engine-independent statistics collected
test.t1 analyze status OK
FLUSH LOGS;
FOUND 1 /GTID 0-1-8 ddl/ in mysqlbinlog.out
FOUND 1 /GTID 0-1-9 ddl/ in mysqlbinlog.out
FOUND 1 /GTID 0-1-10 ddl/ in mysqlbinlog.out
#
# Clean up
#
DROP TABLE t1;
connection server_2;
FLUSH LOGS;
#
# Check that ALTER TABLE commands with ANALYZE, OPTIMIZE and REPAIR on
# partitions will be marked as DDL in binary log.
#
connection server_1;
CREATE TABLE t1(id INT) PARTITION BY RANGE (id) (PARTITION p0 VALUES LESS THAN (100),
PARTITION pmax VALUES LESS THAN (MAXVALUE));
INSERT INTO t1 VALUES (1), (10), (100), (1000);
ALTER TABLE t1 ANALYZE PARTITION p0;
Table Op Msg_type Msg_text
test.t1 analyze status Engine-independent statistics collected
test.t1 analyze status OK
ALTER TABLE t1 OPTIMIZE PARTITION p0;
Table Op Msg_type Msg_text
test.t1 optimize status OK
ALTER TABLE t1 REPAIR PARTITION p0;
Table Op Msg_type Msg_text
test.t1 repair status OK
FLUSH LOGS;
FOUND 1 /GTID 0-1-14 ddl/ in mysqlbinlog.out
FOUND 1 /GTID 0-1-15 ddl/ in mysqlbinlog.out
FOUND 1 /GTID 0-1-16 ddl/ in mysqlbinlog.out
#
# Clean up
#
DROP TABLE t1;
connection server_2;
include/stop_slave.inc
SET GLOBAL slave_parallel_threads= @save_slave_parallel_threads;
SET GLOBAL slave_parallel_mode= @save_slave_parallel_mode;
include/start_slave.inc
include/rpl_end.inc

View File

@ -0,0 +1,142 @@
# ==== Purpose ====
#
# Test verifies that there is no deadlock or assertion in
# slave_parallel_mode=optimistic configuration while applying admin command
# like 'OPTIMIZE TABLE', 'REPAIR TABLE' and 'ANALYZE TABLE'.
#
# ==== Implementation ====
#
# Steps:
# 0 - Create a table, execute OPTIMIZE TABLE command on the table followed
# by some DMLS.
# 1 - No assert should happen on slave server.
# 2 - Assert that 'OPTIMIZE TABLE', 'REPAIR TABLE' and 'ANALYZE TABLE' are
# marked as 'DDL' in the binary log.
#
# ==== References ====
#
# MDEV-17515: GTID Replication in optimistic mode deadlock
#
--source include/have_partition.inc
--source include/have_innodb.inc
--let $rpl_topology=1->2
--source include/rpl_init.inc
--connection server_1
FLUSH TABLES;
ALTER TABLE mysql.gtid_slave_pos ENGINE=InnoDB;
--connection server_2
SET @save_slave_parallel_threads= @@GLOBAL.slave_parallel_threads;
SET @save_slave_parallel_mode= @@GLOBAL.slave_parallel_mode;
--source include/stop_slave.inc
SET GLOBAL slave_parallel_threads=2;
SET GLOBAL slave_parallel_mode=optimistic;
--source include/start_slave.inc
--connection server_1
CREATE TABLE t1(a INT) ENGINE=INNODB;
OPTIMIZE TABLE t1;
INSERT INTO t1 VALUES(1);
INSERT INTO t1 SELECT 1+a FROM t1;
INSERT INTO t1 SELECT 2+a FROM t1;
--save_master_pos
--connection server_2
--sync_with_master
--echo #
--echo # Verify that following admin commands are marked as ddl
--echo # 'OPTIMIZE TABLE', 'REPAIR TABLE' and 'ANALYZE TABLE'
--echo #
--connection server_1
OPTIMIZE TABLE t1;
--let optimize_gtid= `SELECT @@GLOBAL.gtid_binlog_pos`
REPAIR TABLE t1;
--let repair_gtid= `SELECT @@GLOBAL.gtid_binlog_pos`
ANALYZE TABLE t1;
--let analyze_gtid= `SELECT @@GLOBAL.gtid_binlog_pos`
let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1);
FLUSH LOGS;
--let $MYSQLD_DATADIR= `select @@datadir`
--exec $MYSQL_BINLOG $MYSQLD_DATADIR/$binlog_file > $MYSQLTEST_VARDIR/tmp/mysqlbinlog.out
--let SEARCH_PATTERN= GTID $optimize_gtid ddl
--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.out
--source include/search_pattern_in_file.inc
--let SEARCH_PATTERN= GTID $repair_gtid ddl
--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.out
--source include/search_pattern_in_file.inc
--let SEARCH_PATTERN= GTID $analyze_gtid ddl
--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.out
--source include/search_pattern_in_file.inc
--echo #
--echo # Clean up
--echo #
DROP TABLE t1;
--remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog.out
--save_master_pos
--connection server_2
--sync_with_master
FLUSH LOGS;
--echo #
--echo # Check that ALTER TABLE commands with ANALYZE, OPTIMIZE and REPAIR on
--echo # partitions will be marked as DDL in binary log.
--echo #
--connection server_1
CREATE TABLE t1(id INT) PARTITION BY RANGE (id) (PARTITION p0 VALUES LESS THAN (100),
PARTITION pmax VALUES LESS THAN (MAXVALUE));
INSERT INTO t1 VALUES (1), (10), (100), (1000);
ALTER TABLE t1 ANALYZE PARTITION p0;
--let analyze_gtid= `SELECT @@GLOBAL.gtid_binlog_pos`
ALTER TABLE t1 OPTIMIZE PARTITION p0;
--let optimize_gtid= `SELECT @@GLOBAL.gtid_binlog_pos`
ALTER TABLE t1 REPAIR PARTITION p0;
--let repair_gtid= `SELECT @@GLOBAL.gtid_binlog_pos`
let $binlog_file= query_get_value(SHOW MASTER STATUS, File, 1);
FLUSH LOGS;
--exec $MYSQL_BINLOG $MYSQLD_DATADIR/$binlog_file > $MYSQLTEST_VARDIR/tmp/mysqlbinlog.out
--let SEARCH_PATTERN= GTID $analyze_gtid ddl
--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.out
--source include/search_pattern_in_file.inc
--let SEARCH_PATTERN= GTID $optimize_gtid ddl
--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.out
--source include/search_pattern_in_file.inc
--let SEARCH_PATTERN= GTID $repair_gtid ddl
--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/mysqlbinlog.out
--source include/search_pattern_in_file.inc
--echo #
--echo # Clean up
--echo #
DROP TABLE t1;
--remove_file $MYSQLTEST_VARDIR/tmp/mysqlbinlog.out
--save_master_pos
--connection server_2
--sync_with_master
--source include/stop_slave.inc
SET GLOBAL slave_parallel_threads= @save_slave_parallel_threads;
SET GLOBAL slave_parallel_mode= @save_slave_parallel_mode;
--source include/start_slave.inc
--source include/rpl_end.inc

View File

@ -4,6 +4,10 @@
# hang when the parallel workers were idle. # hang when the parallel workers were idle.
# The bug reported scenario is extented to cover the multi-sources case as well as # The bug reported scenario is extented to cover the multi-sources case as well as
# checking is done for both the idle and busy workers cases. # checking is done for both the idle and busy workers cases.
#
# MDEV-25336 Parallel replication causes failed assert while restarting
# Since this test case involves slave restart this will help in testing
# Mdev-25336 too.
--source include/have_innodb.inc --source include/have_innodb.inc
--source include/have_binlog_format_mixed.inc --source include/have_binlog_format_mixed.inc
@ -26,7 +30,7 @@ select @@global.slave_parallel_workers as two;
# At this point worker threads have no assignement. # At this point worker threads have no assignement.
# Shutdown must not hang. # Shutdown must not hang.
# In 10.2/10.3 there should not be any assert failure `prev != 0 && next != 0'
--connection server_3 --connection server_3
--write_file $MYSQLTEST_VARDIR/tmp/mysqld.3.expect --write_file $MYSQLTEST_VARDIR/tmp/mysqld.3.expect
wait wait
@ -75,6 +79,7 @@ insert into t1 values (1);
--connection server_3 --connection server_3
--sync_with_master 0,'' --sync_with_master 0,''
# In 10.2/10.3 there should not be any assert failure `prev != 0 && next != 0'
# At this point worker threads have no assignement. # At this point worker threads have no assignement.
# Shutdown must not hang. # Shutdown must not hang.
@ -117,6 +122,7 @@ insert into t1 values (2);
insert into t2 values (2); insert into t2 values (2);
# In 10.2/10.3 there should not be any assert failure `prev != 0 && next != 0'
# At this point there's a good chance the worker threads are busy. # At this point there's a good chance the worker threads are busy.
# SHUTDOWN must proceed without any delay as above. # SHUTDOWN must proceed without any delay as above.
--connection server_3 --connection server_3

View File

@ -1876,9 +1876,19 @@ struct THD_TRANS
CREATED_TEMP_TABLE= 2, CREATED_TEMP_TABLE= 2,
DROPPED_TEMP_TABLE= 4, DROPPED_TEMP_TABLE= 4,
DID_WAIT= 8, DID_WAIT= 8,
DID_DDL= 0x10 DID_DDL= 0x10,
EXECUTED_TABLE_ADMIN_CMD= 0x20
}; };
void mark_executed_table_admin_cmd()
{
DBUG_PRINT("debug", ("mark_executed_table_admin_cmd"));
m_unsafe_rollback_flags|= EXECUTED_TABLE_ADMIN_CMD;
}
bool trans_executed_admin_cmd()
{
return (m_unsafe_rollback_flags & EXECUTED_TABLE_ADMIN_CMD) != 0;
}
void mark_created_temp_table() void mark_created_temp_table()
{ {
DBUG_PRINT("debug", ("mark_created_temp_table")); DBUG_PRINT("debug", ("mark_created_temp_table"));

View File

@ -822,9 +822,8 @@ bool copy_event_cache_to_file_and_reinit(IO_CACHE *cache, FILE *file);
enum enum_base64_output_mode { enum enum_base64_output_mode {
BASE64_OUTPUT_NEVER= 0, BASE64_OUTPUT_NEVER= 0,
BASE64_OUTPUT_AUTO= 1, BASE64_OUTPUT_AUTO= 1,
BASE64_OUTPUT_ALWAYS= 2, BASE64_OUTPUT_UNSPEC= 2,
BASE64_OUTPUT_UNSPEC= 3, BASE64_OUTPUT_DECODE_ROWS= 3,
BASE64_OUTPUT_DECODE_ROWS= 4,
/* insert new output modes here */ /* insert new output modes here */
BASE64_OUTPUT_MODE_COUNT BASE64_OUTPUT_MODE_COUNT
}; };

View File

@ -3262,8 +3262,10 @@ Gtid_log_event::Gtid_log_event(THD *thd_arg, uint64 seq_no_arg,
flags2|= FL_WAITED; flags2|= FL_WAITED;
if (thd_arg->transaction->stmt.trans_did_ddl() || if (thd_arg->transaction->stmt.trans_did_ddl() ||
thd_arg->transaction->stmt.has_created_dropped_temp_table() || thd_arg->transaction->stmt.has_created_dropped_temp_table() ||
thd_arg->transaction->stmt.trans_executed_admin_cmd() ||
thd_arg->transaction->all.trans_did_ddl() || thd_arg->transaction->all.trans_did_ddl() ||
thd_arg->transaction->all.has_created_dropped_temp_table()) thd_arg->transaction->all.has_created_dropped_temp_table() ||
thd_arg->transaction->all.trans_executed_admin_cmd())
flags2|= FL_DDL; flags2|= FL_DDL;
else if (is_transactional && !is_tmp_table) else if (is_transactional && !is_tmp_table)
flags2|= FL_TRANSACTIONAL; flags2|= FL_TRANSACTIONAL;

View File

@ -210,8 +210,8 @@ a file name for --relay-log-index option", opt_relaylog_index_name);
*/ */
sql_print_warning("Neither --relay-log nor --relay-log-index were used;" sql_print_warning("Neither --relay-log nor --relay-log-index were used;"
" so replication " " so replication "
"may break when this MySQL server acts as a " "may break when this MariaDB server acts as a "
"slave and has his hostname changed!! Please " "replica and has its hostname changed. Please "
"use '--log-basename=#' or '--relay-log=%s' to avoid " "use '--log-basename=#' or '--relay-log=%s' to avoid "
"this problem.", ln); "this problem.", ln);
name_warning_sent= 1; name_warning_sent= 1;

View File

@ -1,5 +1,5 @@
/* Copyright (c) 2010, 2015, Oracle and/or its affiliates. /* Copyright (c) 2010, 2015, Oracle and/or its affiliates.
Copyright (c) 2011, 2020, MariaDB Copyright (c) 2011, 2021, MariaDB
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -490,7 +490,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
int (handler::*operator_func)(THD *, int (handler::*operator_func)(THD *,
HA_CHECK_OPT *), HA_CHECK_OPT *),
int (view_operator_func)(THD *, TABLE_LIST*, int (view_operator_func)(THD *, TABLE_LIST*,
HA_CHECK_OPT *)) HA_CHECK_OPT *),
bool is_cmd_replicated)
{ {
TABLE_LIST *table; TABLE_LIST *table;
List<Item> field_list; List<Item> field_list;
@ -501,6 +502,8 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
int compl_result_code; int compl_result_code;
bool need_repair_or_alter= 0; bool need_repair_or_alter= 0;
wait_for_commit* suspended_wfc; wait_for_commit* suspended_wfc;
bool is_table_modified= false;
DBUG_ENTER("mysql_admin_table"); DBUG_ENTER("mysql_admin_table");
DBUG_PRINT("enter", ("extra_open_options: %u", extra_open_options)); DBUG_PRINT("enter", ("extra_open_options: %u", extra_open_options));
@ -560,6 +563,10 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
bool open_for_modify= org_open_for_modify; bool open_for_modify= org_open_for_modify;
DBUG_PRINT("admin", ("table: '%s'.'%s'", db, table->table_name.str)); DBUG_PRINT("admin", ("table: '%s'.'%s'", db, table->table_name.str));
DEBUG_SYNC(thd, "admin_command_kill_before_modify");
if (thd->is_killed())
break;
strxmov(table_name, db, ".", table->table_name.str, NullS); strxmov(table_name, db, ".", table->table_name.str, NullS);
thd->open_options|= extra_open_options; thd->open_options|= extra_open_options;
table->lock_type= lock_type; table->lock_type= lock_type;
@ -1212,6 +1219,13 @@ send_result_message:
break; break;
} }
} }
/*
Admin commands acquire table locks and these locks are not detected by
parallel replication deadlock detection-and-handling mechanism. Hence
they must be marked as DDL so that they are not scheduled in parallel
with conflicting DMLs resulting in deadlock.
*/
thd->transaction->stmt.mark_executed_table_admin_cmd();
if (table->table && !table->view) if (table->table && !table->view)
{ {
/* /*
@ -1253,10 +1267,9 @@ send_result_message:
} }
else else
{ {
if (trans_commit_stmt(thd) || if (trans_commit_stmt(thd))
(stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END) &&
trans_commit_implicit(thd)))
goto err; goto err;
is_table_modified= true;
} }
close_thread_tables(thd); close_thread_tables(thd);
thd->release_transactional_locks(); thd->release_transactional_locks();
@ -1279,6 +1292,16 @@ send_result_message:
if (protocol->write()) if (protocol->write())
goto err; goto err;
DEBUG_SYNC(thd, "admin_command_kill_after_modify");
}
if (is_table_modified && is_cmd_replicated &&
(!opt_readonly || thd->slave_thread) && !thd->lex->no_write_to_binlog)
{
thd->get_stmt_da()->set_overwrite_status(true);
auto res= write_bin_log(thd, true, thd->query(), thd->query_length());
thd->get_stmt_da()->set_overwrite_status(false);
if (res)
goto err;
} }
my_eof(thd); my_eof(thd);
@ -1341,7 +1364,7 @@ bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables,
check_opt.key_cache= key_cache; check_opt.key_cache= key_cache;
DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt, DBUG_RETURN(mysql_admin_table(thd, tables, &check_opt,
"assign_to_keycache", TL_READ_NO_INSERT, 0, 0, "assign_to_keycache", TL_READ_NO_INSERT, 0, 0,
0, 0, &handler::assign_to_keycache, 0)); 0, 0, &handler::assign_to_keycache, 0, false));
} }
@ -1368,7 +1391,7 @@ bool mysql_preload_keys(THD* thd, TABLE_LIST* tables)
*/ */
DBUG_RETURN(mysql_admin_table(thd, tables, 0, DBUG_RETURN(mysql_admin_table(thd, tables, 0,
"preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0, "preload_keys", TL_READ_NO_INSERT, 0, 0, 0, 0,
&handler::preload_keys, 0)); &handler::preload_keys, 0, false));
} }
@ -1389,17 +1412,8 @@ bool Sql_cmd_analyze_table::execute(THD *thd)
WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table); WSREP_TO_ISOLATION_BEGIN_WRTCHK(NULL, NULL, first_table);
res= mysql_admin_table(thd, first_table, &m_lex->check_opt, res= mysql_admin_table(thd, first_table, &m_lex->check_opt,
"analyze", lock_type, 1, 0, 0, 0, "analyze", lock_type, 1, 0, 0, 0,
&handler::ha_analyze, 0); &handler::ha_analyze, 0, true);
/* ! we write after unlocking the table */
if (!res && !m_lex->no_write_to_binlog && (!opt_readonly || thd->slave_thread))
{
/*
Presumably, ANALYZE and binlog writing doesn't require synchronization
*/
thd->get_stmt_da()->set_overwrite_status(true);
res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
thd->get_stmt_da()->set_overwrite_status(false);
}
m_lex->first_select_lex()->table_list.first= first_table; m_lex->first_select_lex()->table_list.first= first_table;
m_lex->query_tables= first_table; m_lex->query_tables= first_table;
@ -1425,7 +1439,7 @@ bool Sql_cmd_check_table::execute(THD *thd)
res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "check", res= mysql_admin_table(thd, first_table, &m_lex->check_opt, "check",
lock_type, 0, 0, HA_OPEN_FOR_REPAIR, 0, lock_type, 0, 0, HA_OPEN_FOR_REPAIR, 0,
&handler::ha_check, &view_check); &handler::ha_check, &view_check, false);
m_lex->first_select_lex()->table_list.first= first_table; m_lex->first_select_lex()->table_list.first= first_table;
m_lex->query_tables= first_table; m_lex->query_tables= first_table;
@ -1450,17 +1464,8 @@ bool Sql_cmd_optimize_table::execute(THD *thd)
mysql_recreate_table(thd, first_table, true) : mysql_recreate_table(thd, first_table, true) :
mysql_admin_table(thd, first_table, &m_lex->check_opt, mysql_admin_table(thd, first_table, &m_lex->check_opt,
"optimize", TL_WRITE, 1, 0, 0, 0, "optimize", TL_WRITE, 1, 0, 0, 0,
&handler::ha_optimize, 0); &handler::ha_optimize, 0, true);
/* ! we write after unlocking the table */
if (!res && !m_lex->no_write_to_binlog && (!opt_readonly || thd->slave_thread))
{
/*
Presumably, OPTIMIZE and binlog writing doesn't require synchronization
*/
thd->get_stmt_da()->set_overwrite_status(true);
res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
thd->get_stmt_da()->set_overwrite_status(false);
}
m_lex->first_select_lex()->table_list.first= first_table; m_lex->first_select_lex()->table_list.first= first_table;
m_lex->query_tables= first_table; m_lex->query_tables= first_table;
@ -1483,18 +1488,8 @@ bool Sql_cmd_repair_table::execute(THD *thd)
TL_WRITE, 1, TL_WRITE, 1,
MY_TEST(m_lex->check_opt.sql_flags & TT_USEFRM), MY_TEST(m_lex->check_opt.sql_flags & TT_USEFRM),
HA_OPEN_FOR_REPAIR, &prepare_for_repair, HA_OPEN_FOR_REPAIR, &prepare_for_repair,
&handler::ha_repair, &view_repair); &handler::ha_repair, &view_repair, true);
/* ! we write after unlocking the table */
if (!res && !m_lex->no_write_to_binlog && (!opt_readonly || thd->slave_thread))
{
/*
Presumably, REPAIR and binlog writing doesn't require synchronization
*/
thd->get_stmt_da()->set_overwrite_status(true);
res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
thd->get_stmt_da()->set_overwrite_status(false);
}
m_lex->first_select_lex()->table_list.first= first_table; m_lex->first_select_lex()->table_list.first= first_table;
m_lex->query_tables= first_table; m_lex->query_tables= first_table;

View File

@ -5382,7 +5382,8 @@ public:
transaction->all.m_unsafe_rollback_flags|= transaction->all.m_unsafe_rollback_flags|=
(transaction->stmt.m_unsafe_rollback_flags & (transaction->stmt.m_unsafe_rollback_flags &
(THD_TRANS::DID_WAIT | THD_TRANS::CREATED_TEMP_TABLE | (THD_TRANS::DID_WAIT | THD_TRANS::CREATED_TEMP_TABLE |
THD_TRANS::DROPPED_TEMP_TABLE | THD_TRANS::DID_DDL)); THD_TRANS::DROPPED_TEMP_TABLE | THD_TRANS::DID_DDL |
THD_TRANS::EXECUTED_TABLE_ADMIN_CMD));
} }

View File

@ -6098,6 +6098,7 @@ extern "C" int execute_sql_command(const char *command,
new_thd->store_globals(); new_thd->store_globals();
new_thd->security_ctx->skip_grants(); new_thd->security_ctx->skip_grants();
new_thd->query_cache_is_applicable= 0; new_thd->query_cache_is_applicable= 0;
new_thd->variables.wsrep_on= 0;
bzero((char*) &new_thd->net, sizeof(new_thd->net)); bzero((char*) &new_thd->net, sizeof(new_thd->net));
thd= new_thd; thd= new_thd;
} }

View File

@ -3866,7 +3866,7 @@ innobase_commit_low(
if (trx_is_started(trx)) { if (trx_is_started(trx)) {
trx_commit_for_mysql(trx); trx_commit_for_mysql(trx);
} else { } else {
trx->will_lock = 0; trx->will_lock = false;
#ifdef WITH_WSREP #ifdef WITH_WSREP
trx->wsrep = false; trx->wsrep = false;
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
@ -7360,7 +7360,7 @@ ha_innobase::write_row(
} }
if (!trx_is_started(trx)) { if (!trx_is_started(trx)) {
++trx->will_lock; trx->will_lock = true;
} }
ins_mode_t vers_set_fields; ins_mode_t vers_set_fields;
@ -8116,7 +8116,7 @@ ha_innobase::update_row(
if (is_read_only()) { if (is_read_only()) {
DBUG_RETURN(HA_ERR_TABLE_READONLY); DBUG_RETURN(HA_ERR_TABLE_READONLY);
} else if (!trx_is_started(trx)) { } else if (!trx_is_started(trx)) {
++trx->will_lock; trx->will_lock = true;
} }
if (m_upd_buf == NULL) { if (m_upd_buf == NULL) {
@ -8284,7 +8284,7 @@ ha_innobase::delete_row(
if (is_read_only()) { if (is_read_only()) {
DBUG_RETURN(HA_ERR_TABLE_READONLY); DBUG_RETURN(HA_ERR_TABLE_READONLY);
} else if (!trx_is_started(trx)) { } else if (!trx_is_started(trx)) {
++trx->will_lock; trx->will_lock = true;
} }
if (!m_prebuilt->upd_node) { if (!m_prebuilt->upd_node) {
@ -8554,8 +8554,12 @@ ha_innobase::index_read(
/* For R-Tree index, we will always place the page lock to /* For R-Tree index, we will always place the page lock to
pages being searched */ pages being searched */
if (dict_index_is_spatial(index)) { if (index->is_spatial() && !m_prebuilt->trx->will_lock) {
++m_prebuilt->trx->will_lock; if (trx_is_started(m_prebuilt->trx)) {
DBUG_RETURN(HA_ERR_READ_ONLY_TRANSACTION);
} else {
m_prebuilt->trx->will_lock = true;
}
} }
/* Note that if the index for which the search template is built is not /* Note that if the index for which the search template is built is not
@ -9117,7 +9121,7 @@ ha_innobase::ft_init()
them as regular read only transactions for now. */ them as regular read only transactions for now. */
if (!trx_is_started(trx)) { if (!trx_is_started(trx)) {
++trx->will_lock; trx->will_lock = true;
} }
DBUG_RETURN(rnd_init(false)); DBUG_RETURN(rnd_init(false));
@ -9183,7 +9187,7 @@ ha_innobase::ft_init_ext(
them as regular read only transactions for now. */ them as regular read only transactions for now. */
if (!trx_is_started(trx)) { if (!trx_is_started(trx)) {
++trx->will_lock; trx->will_lock = true;
} }
dict_table_t* ft_table = m_prebuilt->table; dict_table_t* ft_table = m_prebuilt->table;
@ -12707,8 +12711,7 @@ void
create_table_info_t::allocate_trx() create_table_info_t::allocate_trx()
{ {
m_trx = innobase_trx_allocate(m_thd); m_trx = innobase_trx_allocate(m_thd);
m_trx->will_lock = true;
m_trx->will_lock = 1;
} }
/** Create a new table to an InnoDB database. /** Create a new table to an InnoDB database.
@ -13026,13 +13029,7 @@ inline int ha_innobase::delete_table(const char* name, enum_sql_command sqlcom)
ut_a(name_len < 1000); ut_a(name_len < 1000);
/* Either the transaction is already flagged as a locking transaction trx->will_lock = true;
or it hasn't been started yet. */
ut_a(!trx_is_started(trx) || trx->will_lock > 0);
/* We are doing a DDL operation. */
++trx->will_lock;
/* Drop the table in InnoDB */ /* Drop the table in InnoDB */
@ -13206,14 +13203,7 @@ innobase_drop_database(
#endif /* _WIN32 */ #endif /* _WIN32 */
trx_t* trx = innobase_trx_allocate(thd); trx_t* trx = innobase_trx_allocate(thd);
trx->will_lock = true;
/* Either the transaction is already flagged as a locking transaction
or it hasn't been started yet. */
ut_a(!trx_is_started(trx) || trx->will_lock > 0);
/* We are doing a DDL operation. */
++trx->will_lock;
ulint dummy; ulint dummy;
@ -13250,7 +13240,7 @@ inline dberr_t innobase_rename_table(trx_t *trx, const char *from,
DEBUG_SYNC_C("innodb_rename_table_ready"); DEBUG_SYNC_C("innodb_rename_table_ready");
trx_start_if_not_started(trx, true); trx_start_if_not_started(trx, true);
ut_ad(trx->will_lock > 0); ut_ad(trx->will_lock);
if (commit) { if (commit) {
/* Serialize data dictionary operations with dictionary mutex: /* Serialize data dictionary operations with dictionary mutex:
@ -13355,7 +13345,7 @@ int ha_innobase::truncate()
const char* name = mem_heap_strdup(heap, ib_table->name.m_name); const char* name = mem_heap_strdup(heap, ib_table->name.m_name);
trx_t* trx = innobase_trx_allocate(m_user_thd); trx_t* trx = innobase_trx_allocate(m_user_thd);
trx->will_lock = 1; trx->will_lock = true;
trx->dict_operation = true; trx->dict_operation = true;
row_mysql_lock_data_dictionary(trx); row_mysql_lock_data_dictionary(trx);
dict_stats_wait_bg_to_stop_using_table(ib_table, trx); dict_stats_wait_bg_to_stop_using_table(ib_table, trx);
@ -13442,7 +13432,7 @@ ha_innobase::rename_table(
trx_t* trx = innobase_trx_allocate(thd); trx_t* trx = innobase_trx_allocate(thd);
/* We are doing a DDL operation. */ /* We are doing a DDL operation. */
trx->will_lock = 1; trx->will_lock = true;
trx->dict_operation = true; trx->dict_operation = true;
dberr_t error = innobase_rename_table(trx, from, to, true); dberr_t error = innobase_rename_table(trx, from, to, true);
@ -15316,7 +15306,7 @@ ha_innobase::start_stmt(
innobase_register_trx(ht, thd, trx); innobase_register_trx(ht, thd, trx);
if (!trx_is_started(trx)) { if (!trx_is_started(trx)) {
++trx->will_lock; trx->will_lock = true;
} }
DBUG_RETURN(0); DBUG_RETURN(0);
@ -15561,7 +15551,7 @@ ha_innobase::external_lock(
&& (m_prebuilt->select_lock_type != LOCK_NONE && (m_prebuilt->select_lock_type != LOCK_NONE
|| m_prebuilt->stored_select_lock_type != LOCK_NONE)) { || m_prebuilt->stored_select_lock_type != LOCK_NONE)) {
++trx->will_lock; trx->will_lock = true;
} }
DBUG_RETURN(0); DBUG_RETURN(0);
@ -15600,7 +15590,7 @@ ha_innobase::external_lock(
&& (m_prebuilt->select_lock_type != LOCK_NONE && (m_prebuilt->select_lock_type != LOCK_NONE
|| m_prebuilt->stored_select_lock_type != LOCK_NONE)) { || m_prebuilt->stored_select_lock_type != LOCK_NONE)) {
++trx->will_lock; trx->will_lock = true;
} }
DBUG_RETURN(0); DBUG_RETURN(0);
@ -15946,7 +15936,7 @@ ha_innobase::store_lock(
&& (m_prebuilt->select_lock_type != LOCK_NONE && (m_prebuilt->select_lock_type != LOCK_NONE
|| m_prebuilt->stored_select_lock_type != LOCK_NONE)) { || m_prebuilt->stored_select_lock_type != LOCK_NONE)) {
++trx->will_lock; trx->will_lock = true;
} }
return(to); return(to);

View File

@ -2360,7 +2360,7 @@ innodb_instant_alter_column_allowed_reason:
} }
} }
m_prebuilt->trx->will_lock++; m_prebuilt->trx->will_lock = true;
/* When changing a NULL column to NOT NULL and specifying a /* When changing a NULL column to NOT NULL and specifying a
DEFAULT value, ensure that the DEFAULT expression is a constant. DEFAULT value, ensure that the DEFAULT expression is a constant.
@ -11382,7 +11382,6 @@ foreign_fail:
m_prebuilt = ctx->prebuilt; m_prebuilt = ctx->prebuilt;
} }
trx_start_if_not_started(user_trx, true); trx_start_if_not_started(user_trx, true);
user_trx->will_lock++;
m_prebuilt->trx = user_trx; m_prebuilt->trx = user_trx;
} }
DBUG_INJECT_CRASH("ib_commit_inplace_crash", DBUG_INJECT_CRASH("ib_commit_inplace_crash",

View File

@ -150,7 +150,7 @@ struct i_s_trx_row_t {
bool trx_is_read_only; bool trx_is_read_only;
/*!< trx_t::read_only */ /*!< trx_t::read_only */
bool trx_is_autocommit_non_locking; bool trx_is_autocommit_non_locking;
/*!< trx_is_autocommit_non_locking(trx) /*!< trx:t::is_autocommit_non_locking()
*/ */
}; };

View File

@ -514,7 +514,7 @@ class rw_trx_hash_t
static void validate_element(trx_t *trx) static void validate_element(trx_t *trx)
{ {
ut_ad(!trx->read_only || !trx->rsegs.m_redo.rseg); ut_ad(!trx->read_only || !trx->rsegs.m_redo.rseg);
ut_ad(!trx_is_autocommit_non_locking(trx)); ut_ad(!trx->is_autocommit_non_locking());
/* trx->state can be anything except TRX_STATE_NOT_STARTED */ /* trx->state can be anything except TRX_STATE_NOT_STARTED */
ut_d(trx->mutex_lock()); ut_d(trx->mutex_lock());
ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE) || ut_ad(trx_state_eq(trx, TRX_STATE_ACTIVE) ||

View File

@ -332,64 +332,6 @@ from innodb_lock_wait_timeout via trx_t::mysql_thd.
? thd_lock_wait_timeout((t)->mysql_thd) \ ? thd_lock_wait_timeout((t)->mysql_thd) \
: 0) : 0)
/**
Determine if the transaction is a non-locking autocommit select
(implied read-only).
@param t transaction
@return true if non-locking autocommit select transaction. */
#define trx_is_autocommit_non_locking(t) \
((t)->auto_commit && (t)->will_lock == 0)
/**
Determine if the transaction is a non-locking autocommit select
with an explicit check for the read-only status.
@param t transaction
@return true if non-locking autocommit read-only transaction. */
#define trx_is_ac_nl_ro(t) \
((t)->read_only && trx_is_autocommit_non_locking((t)))
/**
Check transaction state */
#define check_trx_state(t) do { \
ut_ad(!trx_is_autocommit_non_locking((t))); \
switch ((t)->state) { \
case TRX_STATE_PREPARED: \
case TRX_STATE_PREPARED_RECOVERED: \
case TRX_STATE_ACTIVE: \
case TRX_STATE_COMMITTED_IN_MEMORY: \
continue; \
case TRX_STATE_NOT_STARTED: \
break; \
} \
ut_error; \
} while (0)
#ifdef UNIV_DEBUG
/*******************************************************************//**
Assert that an autocommit non-locking select cannot be in the
rw_trx_hash and that it is a read-only transaction.
The transaction must have mysql_thd assigned. */
# define assert_trx_nonlocking_or_in_list(t) \
do { \
if (trx_is_autocommit_non_locking(t)) { \
trx_state_t t_state = (t)->state; \
ut_ad((t)->read_only); \
ut_ad(!(t)->is_recovered); \
ut_ad((t)->mysql_thd); \
ut_ad(t_state == TRX_STATE_NOT_STARTED \
|| t_state == TRX_STATE_ACTIVE); \
} else { \
check_trx_state(t); \
} \
} while (0)
#else /* UNIV_DEBUG */
/*******************************************************************//**
Assert that an autocommit non-locking slect cannot be in the
rw_trx_hash and that it is a read-only transaction.
The transaction must have mysql_thd assigned. */
# define assert_trx_nonlocking_or_in_list(trx) ((void)0)
#endif /* UNIV_DEBUG */
typedef std::vector<ib_lock_t*, ut_allocator<ib_lock_t*> > lock_list; typedef std::vector<ib_lock_t*, ut_allocator<ib_lock_t*> > lock_list;
/** The locks and state of an active transaction. Protected by /** The locks and state of an active transaction. Protected by
@ -882,16 +824,15 @@ public:
/*------------------------------*/ /*------------------------------*/
bool read_only; /*!< true if transaction is flagged bool read_only; /*!< true if transaction is flagged
as a READ-ONLY transaction. as a READ-ONLY transaction.
if auto_commit && will_lock == 0 if auto_commit && !will_lock
then it will be handled as a then it will be handled as a
AC-NL-RO-SELECT (Auto Commit Non-Locking AC-NL-RO-SELECT (Auto Commit Non-Locking
Read Only Select). A read only Read Only Select). A read only
transaction will not be assigned an transaction will not be assigned an
UNDO log. */ UNDO log. */
bool auto_commit; /*!< true if it is an autocommit */ bool auto_commit; /*!< true if it is an autocommit */
ib_uint32_t will_lock; /*!< Will acquire some locks. Increment bool will_lock; /*!< set to inform trx_start_low() that
each time we determine that a lock will the transaction may acquire locks */
be acquired by the MySQL layer. */
/*------------------------------*/ /*------------------------------*/
fts_trx_t* fts_trx; /*!< FTS information, or NULL if fts_trx_t* fts_trx; /*!< FTS information, or NULL if
transaction hasn't modified tables transaction hasn't modified tables
@ -1062,6 +1003,9 @@ public:
it->second.end_bulk_insert(); it->second.end_bulk_insert();
} }
/** @return whether this is a non-locking autocommit transaction */
bool is_autocommit_non_locking() const { return auto_commit && !will_lock; }
/** This has to be invoked on SAVEPOINT or at the start of a statement. /** This has to be invoked on SAVEPOINT or at the start of a statement.
Even if TRX_UNDO_EMPTY records were written for any table to cover an Even if TRX_UNDO_EMPTY records were written for any table to cover an
insert into an empty table, subsequent operations will have to be covered insert into an empty table, subsequent operations will have to be covered

View File

@ -49,11 +49,15 @@ trx_state_eq(
case TRX_STATE_PREPARED: case TRX_STATE_PREPARED:
case TRX_STATE_PREPARED_RECOVERED: case TRX_STATE_PREPARED_RECOVERED:
case TRX_STATE_COMMITTED_IN_MEMORY: case TRX_STATE_COMMITTED_IN_MEMORY:
ut_ad(!trx_is_autocommit_non_locking(trx)); ut_ad(!trx->is_autocommit_non_locking());
return(trx->state == state); return(trx->state == state);
case TRX_STATE_ACTIVE: case TRX_STATE_ACTIVE:
assert_trx_nonlocking_or_in_list(trx); if (trx->is_autocommit_non_locking()) {
ut_ad(!trx->is_recovered);
ut_ad(trx->read_only);
ut_ad(trx->mysql_thd);
}
return(state == trx->state); return(state == trx->state);
case TRX_STATE_NOT_STARTED: case TRX_STATE_NOT_STARTED:

View File

@ -1097,6 +1097,19 @@ static void lock_reset_lock_and_trx_wait(lock_t *lock)
lock->type_mode&= ~LOCK_WAIT; lock->type_mode&= ~LOCK_WAIT;
} }
#ifdef UNIV_DEBUG
/** Check transaction state */
static void check_trx_state(const trx_t *trx)
{
ut_ad(!trx->auto_commit || trx->will_lock);
const auto state= trx->state;
ut_ad(state == TRX_STATE_ACTIVE ||
state == TRX_STATE_PREPARED_RECOVERED ||
state == TRX_STATE_PREPARED ||
state == TRX_STATE_COMMITTED_IN_MEMORY);
}
#endif
/** Create a new record lock and inserts it to the lock queue, /** Create a new record lock and inserts it to the lock queue,
without checking for deadlocks or conflicts. without checking for deadlocks or conflicts.
@param[in] c_lock conflicting lock @param[in] c_lock conflicting lock
@ -1127,7 +1140,7 @@ lock_rec_create_low(
ut_ad(dict_index_is_clust(index) || !dict_index_is_online_ddl(index)); ut_ad(dict_index_is_clust(index) || !dict_index_is_online_ddl(index));
ut_ad(!(type_mode & LOCK_TABLE)); ut_ad(!(type_mode & LOCK_TABLE));
ut_ad(trx->state != TRX_STATE_NOT_STARTED); ut_ad(trx->state != TRX_STATE_NOT_STARTED);
ut_ad(!trx_is_autocommit_non_locking(trx)); ut_ad(!trx->is_autocommit_non_locking());
/* If rec is the supremum record, then we reset the gap and /* If rec is the supremum record, then we reset the gap and
LOCK_REC_NOT_GAP bits, as all locks on the supremum are LOCK_REC_NOT_GAP bits, as all locks on the supremum are
@ -3083,7 +3096,7 @@ lock_table_create(
ut_ad(trx->mutex_is_owner()); ut_ad(trx->mutex_is_owner());
ut_ad(!trx->is_wsrep() || lock_sys.is_writer()); ut_ad(!trx->is_wsrep() || lock_sys.is_writer());
ut_ad(trx->state == TRX_STATE_ACTIVE || trx->is_recovered); ut_ad(trx->state == TRX_STATE_ACTIVE || trx->is_recovered);
ut_ad(!trx_is_autocommit_non_locking(trx)); ut_ad(!trx->is_autocommit_non_locking());
switch (LOCK_MODE_MASK & type_mode) { switch (LOCK_MODE_MASK & type_mode) {
case LOCK_AUTO_INC: case LOCK_AUTO_INC:
@ -4334,7 +4347,8 @@ lock_rec_queue_validate(
ut_ad(!index || lock->index == index); ut_ad(!index || lock->index == index);
lock->trx->mutex_lock(); lock->trx->mutex_lock();
ut_ad(!trx_is_ac_nl_ro(lock->trx)); ut_ad(!lock->trx->read_only
|| !lock->trx->is_autocommit_non_locking());
ut_ad(trx_state_eq(lock->trx, ut_ad(trx_state_eq(lock->trx,
TRX_STATE_COMMITTED_IN_MEMORY) TRX_STATE_COMMITTED_IN_MEMORY)
|| !lock->is_waiting() || !lock->is_waiting()
@ -4420,8 +4434,8 @@ func_exit:
for (lock = lock_sys_t::get_first(cell, id, heap_no); for (lock = lock_sys_t::get_first(cell, id, heap_no);
lock != NULL; lock != NULL;
lock = lock_rec_get_next_const(heap_no, lock)) { lock = lock_rec_get_next_const(heap_no, lock)) {
ut_ad(!lock->trx->read_only
ut_ad(!trx_is_ac_nl_ro(lock->trx)); || !lock->trx->is_autocommit_non_locking());
ut_ad(!page_rec_is_metadata(rec)); ut_ad(!page_rec_is_metadata(rec));
if (index) { if (index) {
@ -4497,7 +4511,8 @@ loop:
} }
} }
ut_ad(!trx_is_ac_nl_ro(lock->trx)); ut_ad(!lock->trx->read_only
|| !lock->trx->is_autocommit_non_locking());
/* Only validate the record queues when this thread is not /* Only validate the record queues when this thread is not
holding a tablespace latch. */ holding a tablespace latch. */
@ -4560,7 +4575,8 @@ lock_rec_validate(
lock != NULL; lock != NULL;
lock = static_cast<const lock_t*>(HASH_GET_NEXT(hash, lock))) { lock = static_cast<const lock_t*>(HASH_GET_NEXT(hash, lock))) {
ut_ad(!trx_is_ac_nl_ro(lock->trx)); ut_ad(!lock->trx->read_only
|| !lock->trx->is_autocommit_non_locking());
ut_ad(!lock->is_table()); ut_ad(!lock->is_table());
page_id_t current(lock->un_member.rec_lock.page_id); page_id_t current(lock->un_member.rec_lock.page_id);

View File

@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2018, 2020, MariaDB Corporation. Copyright (c) 2018, 2021, 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
@ -221,7 +221,7 @@ void ReadView::open(trx_t *trx)
else if (likely(!srv_read_only_mode)) else if (likely(!srv_read_only_mode))
{ {
m_creator_trx_id= trx->id; m_creator_trx_id= trx->id;
if (trx_is_autocommit_non_locking(trx) && empty() && if (trx->is_autocommit_non_locking() && empty() &&
low_limit_id() == trx_sys.get_max_trx_id()) low_limit_id() == trx_sys.get_max_trx_id())
m_open.store(true, std::memory_order_relaxed); m_open.store(true, std::memory_order_relaxed);
else else

View File

@ -521,7 +521,7 @@ thd_done:
row->trx_is_read_only = trx->read_only; row->trx_is_read_only = trx->read_only;
row->trx_is_autocommit_non_locking = trx_is_autocommit_non_locking(trx); row->trx_is_autocommit_non_locking = trx->is_autocommit_non_locking();
return(TRUE); return(TRUE);
} }
@ -1165,7 +1165,24 @@ static void fetch_data_into_cache_low(trx_i_s_cache_t *cache, const trx_t *trx)
{ {
i_s_locks_row_t *requested_lock_row; i_s_locks_row_t *requested_lock_row;
assert_trx_nonlocking_or_in_list(trx); #ifdef UNIV_DEBUG
{
const auto state= trx->state;
if (trx->is_autocommit_non_locking())
{
ut_ad(trx->read_only);
ut_ad(!trx->is_recovered);
ut_ad(trx->mysql_thd);
ut_ad(state == TRX_STATE_NOT_STARTED || state == TRX_STATE_ACTIVE);
}
else
ut_ad(state == TRX_STATE_ACTIVE ||
state == TRX_STATE_PREPARED ||
state == TRX_STATE_PREPARED_RECOVERED ||
state == TRX_STATE_COMMITTED_IN_MEMORY);
}
#endif /* UNIV_DEBUG */
if (add_trx_relevant_locks_to_cache(cache, trx, &requested_lock_row)) if (add_trx_relevant_locks_to_cache(cache, trx, &requested_lock_row))
{ {

View File

@ -99,10 +99,21 @@ inline void trx_t::rollback_low(trx_savept_t *savept)
roll_node_t *roll_node= roll_node_create(heap); roll_node_t *roll_node= roll_node_create(heap);
roll_node->savept= savept; roll_node->savept= savept;
ut_ad(!in_rollback);
#ifdef UNIV_DEBUG
{
const auto s= state;
ut_ad(s == TRX_STATE_ACTIVE ||
s == TRX_STATE_PREPARED ||
s == TRX_STATE_PREPARED_RECOVERED);
if (savept) if (savept)
check_trx_state(this); {
else ut_ad(s == TRX_STATE_ACTIVE);
assert_trx_nonlocking_or_in_list(this); ut_ad(mysql_thd);
ut_ad(!is_recovered);
}
}
#endif
error_state = DB_SUCCESS; error_state = DB_SUCCESS;
@ -198,7 +209,7 @@ dberr_t trx_rollback_for_mysql(trx_t* trx)
switch (trx->state) { switch (trx->state) {
case TRX_STATE_NOT_STARTED: case TRX_STATE_NOT_STARTED:
trx->will_lock = 0; trx->will_lock = false;
ut_ad(trx->mysql_thd); ut_ad(trx->mysql_thd);
#ifdef WITH_WSREP #ifdef WITH_WSREP
trx->wsrep= false; trx->wsrep= false;
@ -208,12 +219,13 @@ dberr_t trx_rollback_for_mysql(trx_t* trx)
case TRX_STATE_ACTIVE: case TRX_STATE_ACTIVE:
ut_ad(trx->mysql_thd); ut_ad(trx->mysql_thd);
assert_trx_nonlocking_or_in_list(trx); ut_ad(!trx->is_recovered);
ut_ad(!trx->is_autocommit_non_locking() || trx->read_only);
return(trx_rollback_for_mysql_low(trx)); return(trx_rollback_for_mysql_low(trx));
case TRX_STATE_PREPARED: case TRX_STATE_PREPARED:
case TRX_STATE_PREPARED_RECOVERED: case TRX_STATE_PREPARED_RECOVERED:
ut_ad(!trx_is_autocommit_non_locking(trx)); ut_ad(!trx->is_autocommit_non_locking());
if (trx->rsegs.m_redo.undo || trx->rsegs.m_redo.old_insert) { if (trx->rsegs.m_redo.undo || trx->rsegs.m_redo.old_insert) {
/* The XA ROLLBACK of a XA PREPARE transaction /* The XA ROLLBACK of a XA PREPARE transaction
will consist of multiple mini-transactions. will consist of multiple mini-transactions.
@ -260,7 +272,7 @@ dberr_t trx_rollback_for_mysql(trx_t* trx)
return(trx_rollback_for_mysql_low(trx)); return(trx_rollback_for_mysql_low(trx));
case TRX_STATE_COMMITTED_IN_MEMORY: case TRX_STATE_COMMITTED_IN_MEMORY:
check_trx_state(trx); ut_ad(!trx->is_autocommit_non_locking());
break; break;
} }
@ -289,7 +301,9 @@ trx_rollback_last_sql_stat_for_mysql(
return(DB_SUCCESS); return(DB_SUCCESS);
case TRX_STATE_ACTIVE: case TRX_STATE_ACTIVE:
assert_trx_nonlocking_or_in_list(trx); ut_ad(trx->mysql_thd);
ut_ad(!trx->is_recovered);
ut_ad(!trx->is_autocommit_non_locking() || trx->read_only);
trx->op_info = "rollback of SQL statement"; trx->op_info = "rollback of SQL statement";

View File

@ -129,7 +129,7 @@ trx_init(
trx->auto_commit = false; trx->auto_commit = false;
trx->will_lock = 0; trx->will_lock = false;
trx->internal = false; trx->internal = false;
@ -342,7 +342,7 @@ trx_t *trx_create()
ib_alloc_t* alloc; ib_alloc_t* alloc;
/* We just got trx from pool, it should be non locking */ /* We just got trx from pool, it should be non locking */
ut_ad(trx->will_lock == 0); ut_ad(!trx->will_lock);
ut_ad(!trx->rw_trx_hash_pins); ut_ad(!trx->rw_trx_hash_pins);
DBUG_LOG("trx", "Create: " << trx); DBUG_LOG("trx", "Create: " << trx);
@ -542,7 +542,7 @@ void trx_disconnect_prepared(trx_t *trx)
trx->is_recovered= true; trx->is_recovered= true;
trx->mysql_thd= NULL; trx->mysql_thd= NULL;
/* todo/fixme: suggest to do it at innodb prepare */ /* todo/fixme: suggest to do it at innodb prepare */
trx->will_lock= 0; trx->will_lock= false;
trx_sys.rw_trx_hash.put_pins(trx); trx_sys.rw_trx_hash.put_pins(trx);
} }
@ -882,11 +882,10 @@ static trx_rseg_t* trx_assign_rseg_low()
/** Assign a rollback segment for modifying temporary tables. /** Assign a rollback segment for modifying temporary tables.
@return the assigned rollback segment */ @return the assigned rollback segment */
trx_rseg_t* trx_rseg_t *trx_t::assign_temp_rseg()
trx_t::assign_temp_rseg()
{ {
ut_ad(!rsegs.m_noredo.rseg); ut_ad(!rsegs.m_noredo.rseg);
ut_ad(!trx_is_autocommit_non_locking(this)); ut_ad(!is_autocommit_non_locking());
compile_time_assert(ut_is_2pow(TRX_SYS_N_RSEGS)); compile_time_assert(ut_is_2pow(TRX_SYS_N_RSEGS));
/* Choose a temporary rollback segment between 0 and 127 /* Choose a temporary rollback segment between 0 and 127
@ -933,8 +932,8 @@ trx_start_low(
&& thd_trx_is_read_only(trx->mysql_thd)); && thd_trx_is_read_only(trx->mysql_thd));
if (!trx->auto_commit) { if (!trx->auto_commit) {
++trx->will_lock; trx->will_lock = true;
} else if (trx->will_lock == 0) { } else if (!trx->will_lock) {
trx->read_only = true; trx->read_only = true;
} }
@ -972,7 +971,7 @@ trx_start_low(
trx_sys.register_rw(trx); trx_sys.register_rw(trx);
} else { } else {
if (!trx_is_autocommit_non_locking(trx)) { if (!trx->is_autocommit_non_locking()) {
/* If this is a read-only transaction that is writing /* If this is a read-only transaction that is writing
to a temporary table then it needs a transaction id to a temporary table then it needs a transaction id
@ -1267,12 +1266,15 @@ inline void trx_t::commit_in_memory(const mtr_t *mtr)
must_flush_log_later= false; must_flush_log_later= false;
read_view.close(); read_view.close();
if (trx_is_autocommit_non_locking(this)) if (is_autocommit_non_locking())
{ {
ut_ad(id == 0); ut_ad(id == 0);
ut_ad(read_only); ut_ad(read_only);
ut_ad(!will_lock);
ut_a(!is_recovered); ut_a(!is_recovered);
ut_ad(!rsegs.m_redo.rseg); ut_ad(!rsegs.m_redo.rseg);
ut_ad(mysql_thd);
ut_ad(state == TRX_STATE_ACTIVE);
/* Note: We do not have to hold any lock_sys latch here, because /* Note: We do not have to hold any lock_sys latch here, because
this is a non-locking transaction. */ this is a non-locking transaction. */
@ -1284,12 +1286,11 @@ inline void trx_t::commit_in_memory(const mtr_t *mtr)
However, the freezing of trx_sys.trx_list will protect the trx_t However, the freezing of trx_sys.trx_list will protect the trx_t
instance and it cannot be removed from the trx_list and freed instance and it cannot be removed from the trx_list and freed
without first unfreezing trx_list. */ without first unfreezing trx_list. */
ut_ad(trx_state_eq(this, TRX_STATE_ACTIVE)); state= TRX_STATE_NOT_STARTED;
MONITOR_INC(MONITOR_TRX_NL_RO_COMMIT); MONITOR_INC(MONITOR_TRX_NL_RO_COMMIT);
DBUG_LOG("trx", "Autocommit in memory: " << this); DBUG_LOG("trx", "Autocommit in memory: " << this);
state= TRX_STATE_NOT_STARTED;
} }
else else
{ {
@ -1439,8 +1440,6 @@ inline void trx_t::commit_in_memory(const mtr_t *mtr)
@param mtr mini-transaction (if there are any persistent modifications) */ @param mtr mini-transaction (if there are any persistent modifications) */
void trx_t::commit_low(mtr_t *mtr) void trx_t::commit_low(mtr_t *mtr)
{ {
assert_trx_nonlocking_or_in_list(this);
ut_ad(!trx_state_eq(this, TRX_STATE_COMMITTED_IN_MEMORY));
ut_ad(!mtr || mtr->is_active()); ut_ad(!mtr || mtr->is_active());
ut_d(bool aborted = in_rollback && error_state == DB_DEADLOCK); ut_d(bool aborted = in_rollback && error_state == DB_DEADLOCK);
ut_ad(!mtr == (aborted || !has_logged_or_recovered())); ut_ad(!mtr == (aborted || !has_logged_or_recovered()));
@ -1449,13 +1448,13 @@ void trx_t::commit_low(mtr_t *mtr)
/* undo_no is non-zero if we're doing the final commit. */ /* undo_no is non-zero if we're doing the final commit. */
if (fts_trx && undo_no) if (fts_trx && undo_no)
{ {
ut_a(!trx_is_autocommit_non_locking(this)); ut_a(!is_autocommit_non_locking());
dberr_t error= fts_commit(this);
/* FTS-FIXME: Temporarily tolerate DB_DUPLICATE_KEY instead of /* FTS-FIXME: Temporarily tolerate DB_DUPLICATE_KEY instead of
dying. This is a possible scenario if there is a crash between dying. This is a possible scenario if there is a crash between
insert to DELETED table committing and transaction committing. The insert to DELETED table committing and transaction committing. The
fix would be able to return error from this function */ fix would be able to return error from this function */
ut_a(error == DB_SUCCESS || error == DB_DUPLICATE_KEY); if (dberr_t error= fts_commit(this))
ut_a(error == DB_DUPLICATE_KEY);
} }
#ifndef DBUG_OFF #ifndef DBUG_OFF
@ -1700,9 +1699,6 @@ trx_print_low(
ulint heap_size) ulint heap_size)
/*!< in: mem_heap_get_size(trx->lock.lock_heap) */ /*!< in: mem_heap_get_size(trx->lock.lock_heap) */
{ {
ibool newline;
const char* op_info;
if (const trx_id_t id = trx->id) { if (const trx_id_t id = trx->id) {
fprintf(f, "TRANSACTION " TRX_ID_FMT, trx->id); fprintf(f, "TRANSACTION " TRX_ID_FMT, trx->id);
} else { } else {
@ -1729,11 +1725,7 @@ trx_print_low(
fprintf(f, ", state %lu", (ulong) trx->state); fprintf(f, ", state %lu", (ulong) trx->state);
ut_ad(0); ut_ad(0);
state_ok: state_ok:
if (const char *op_info = trx->op_info) {
/* prevent a race condition */
op_info = trx->op_info;
if (*op_info) {
putc(' ', f); putc(' ', f);
fputs(op_info, f); fputs(op_info, f);
} }
@ -1750,18 +1742,18 @@ state_ok:
(ulong) trx->mysql_n_tables_locked); (ulong) trx->mysql_n_tables_locked);
} }
newline = TRUE; bool newline = true;
if (trx->in_rollback) { /* dirty read for performance reasons */ if (trx->in_rollback) { /* dirty read for performance reasons */
fputs("ROLLING BACK ", f); fputs("ROLLING BACK ", f);
} else if (trx->lock.wait_lock) { } else if (trx->lock.wait_lock) {
fputs("LOCK WAIT ", f); fputs("LOCK WAIT ", f);
} else { } else {
newline = FALSE; newline = false;
} }
if (n_trx_locks > 0 || heap_size > 400) { if (n_trx_locks > 0 || heap_size > 400) {
newline = TRUE; newline = true;
fprintf(f, "%lu lock struct(s), heap size %lu," fprintf(f, "%lu lock struct(s), heap size %lu,"
" %lu row lock(s)", " %lu row lock(s)",
@ -1771,7 +1763,7 @@ state_ok:
} }
if (trx->undo_no != 0) { if (trx->undo_no != 0) {
newline = TRUE; newline = true;
fprintf(f, ", undo log entries " TRX_ID_FMT, trx->undo_no); fprintf(f, ", undo log entries " TRX_ID_FMT, trx->undo_no);
} }
@ -2150,7 +2142,7 @@ trx_start_internal_low(
/* Ensure it is not flagged as an auto-commit-non-locking /* Ensure it is not flagged as an auto-commit-non-locking
transaction. */ transaction. */
trx->will_lock = 1; trx->will_lock = true;
trx->internal = true; trx->internal = true;
@ -2166,7 +2158,7 @@ trx_start_internal_read_only_low(
/* Ensure it is not flagged as an auto-commit-non-locking /* Ensure it is not flagged as an auto-commit-non-locking
transaction. */ transaction. */
trx->will_lock = 1; trx->will_lock = true;
trx->internal = true; trx->internal = true;
@ -2181,8 +2173,6 @@ void trx_start_for_ddl_low(trx_t *trx)
/* Flag this transaction as a dictionary operation, so that /* Flag this transaction as a dictionary operation, so that
the data dictionary will be locked in crash recovery. */ the data dictionary will be locked in crash recovery. */
trx->dict_operation= true; trx->dict_operation= true;
/* Ensure it is not flagged as an auto-commit-non-locking transaction. */
trx->will_lock= 1;
trx_start_internal_low(trx); trx_start_internal_low(trx);
} }
@ -2199,7 +2189,7 @@ trx_set_rw_mode(
trx_t* trx) /*!< in/out: transaction that is RW */ trx_t* trx) /*!< in/out: transaction that is RW */
{ {
ut_ad(trx->rsegs.m_redo.rseg == 0); ut_ad(trx->rsegs.m_redo.rseg == 0);
ut_ad(!trx_is_autocommit_non_locking(trx)); ut_ad(!trx->is_autocommit_non_locking());
ut_ad(!trx->read_only); ut_ad(!trx->read_only);
ut_ad(trx->id == 0); ut_ad(trx->id == 0);