Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions mysql-test/suite/binlog/r/binlog_spurious_ddl_errors.result
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
SET @old_binlog_format= @@global.binlog_format;
INSTALL PLUGIN example SONAME 'ha_example';
################################################################################
# Verifies if ER_BINLOG_STMT_MODE_AND_ROW_ENGINE happens by setting the binlog
# format to STATEMENT and the transaction isolation level to READ COMMITTED as
# such changes force Innodb to accept changes in the row format.
#
# When CREATE TABLE, ALTER TABLE, CREATE INDEX and CREATE TRIGGER are executed
# any error should be triggered.
#
# In contrast, CREATE TABLE ... SELECT should trigger the following error:
# ER_BINLOG_STMT_MODE_AND_ROW_ENGINE.
# If the binlog format is set to STATEMENT and the transaction isolation
# level to READ COMMITTED, InnoDB must override the STATEMENT format
# configuration and write using ROW format.
################################################################################
SET binlog_format = STATEMENT;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
CREATE TABLE t_row (a VARCHAR(100)) ENGINE = InnoDB;
SET statement binlog_format = row for insert into t_row values ('a'), ('b'), ('c');
ALTER TABLE t_row ADD COLUMN b INT;
CREATE TRIGGER trig_row BEFORE INSERT ON t_row FOR EACH ROW INSERT INTO t_stmt VALUES (1);
CREATE INDEX i ON t_row(a);
# Flush binlogs to ensure the next statement can be analyzed alone
FLUSH BINARY LOGS;
CREATE TABLE t_row_new ENGINE = InnoDB SELECT * FROM t_row;
ERROR HY000: Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT and at least one table uses a storage engine limited to row-based logging.
Warnings:
Note 1665 Statement cannot be logged as STATEMENT as at least one table is limited to row-based logging. Switching temporarly to row format
FLUSH BINARY LOGS;
# MYSQL_BINLOG binlog_file --result-file=binlog_out --base64-output=never --verbose
include/assert_grep.inc [Ensure CREATE TABLE ... SELECT in STATEMENT uses ROW binlogging when InnoDB cannot log using STATEMENT format]
include/assert_grep.inc [Ensure CREATE TABLE ... SELECT in STATEMENT writes all rows when InnoDB cannot log using STATEMENT format]
DROP TABLE t_row;
DROP TABLE t_row_new;


################################################################################
Expand Down
313 changes: 313 additions & 0 deletions mysql-test/suite/binlog/t/binlog_close_during_commit.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
#
# Purpose:
# Exercise the race where a statement passes the per-THD
# thd->binlog_ready() check, then the global binary log is closed by
# another connection before the statement acquires LOCK_log to commit
# into the binlog. The statement must still commit in the storage
# engine, and the error log must contain exactly one ER_ERROR_ON_WRITE
# entry per binlog-close event (not one per affected transaction).
# After the close, the binary log remains closed and subsequent
# transactions continue to be processed in engines but are not
# binlogged, with no further ER_ERROR_ON_WRITE entries logged.
#
# Methodology:
# DEBUG_SYNC point 'commit_after_binlog_ready_before_LOCK_log'
# pauses the statement in MYSQL_BIN_LOG::write_transaction_to_binlog
# immediately after the thd->binlog_ready() check, but before the
# transaction is queued for group commit (and before LOCK_log is
# taken). From a second connection, the binlog is closed by setting
# DBUG injection 'fault_injection_new_file_rotate_event' and running
# FLUSH LOGS. The fault makes the rotation fail, which leaves the
# binlog in a closed state (see the "Binlog could be closed if
# rotation fails" comment in trx_group_commit_with_engines). The
# first connection, still paused at the sync point, is then resumed
# and reaches LOCK_log to find the binlog closed.
#
# Five statement types are covered:
# 1) DML on a transactional table (InnoDB)
# 2) DML on a non-transactional table (MyISAM)
# 3) Multi-table UPDATE targeting both a trans and a non-trans
# table
# 4) DML selecting from a temporary table
# 5) DDL: CREATE TABLE (atomic DDL, InnoDB)
#
# References:
# MDEV-38796: branch context for the binlog_ready() refactor
#

--source include/have_log_bin.inc
--source include/have_debug.inc
--source include/have_debug_sync.inc
--source include/have_innodb.inc
--source include/have_binlog_format_row.inc

call mtr.add_suppression("\\[ERROR\\] Error writing file '.*master-bin.*' .*errno: 9");
call mtr.add_suppression("\\[ERROR\\] Error writing file");

# Search the per-test section of the primary's error log for the
# expected ER_ERROR_ON_WRITE entry.
--let $error_log= $MYSQLTEST_VARDIR/log/mysqld.1.err
--let SEARCH_FILE= $error_log


--echo #
--echo # Sub-case 1: DML on a transactional table
--echo #
create table t_trans (id int primary key) engine=innodb;
create table t2 (id int primary key) engine=innodb;

--connect (con1,localhost,root,,test)
--connect (con3,localhost,root,,test)

--connection con1
SET DEBUG_SYNC= 'commit_after_binlog_ready_before_LOCK_log SIGNAL paused WAIT_FOR go';
SET DEBUG_DBUG= '+d,commit_after_binlog_ready_before_LOCK_log';
--send insert into t_trans values (1)

--connection default
SET DEBUG_SYNC= 'now WAIT_FOR paused';

--echo # Trigger the binlog error and permanent closure via a failed rotation
SET DEBUG_DBUG= '+d,fault_injection_new_file_rotate_event';
--error ER_ERROR_ON_WRITE
FLUSH LOGS;

SET DEBUG_DBUG= '';
SET DEBUG_SYNC= 'now SIGNAL go';

--connection con1
--reap

--connection default
--echo # con1's statement must have committed in the engine
select * from t_trans;
--echo # con2's (default conn's) statement must have committed in the engine
select * from t2;

--echo # A follow-up commit on a third connection must also succeed without
--echo # logging another ER_ERROR_ON_WRITE
--connection con3
insert into t2 values (102);
select * from t2 order by id;

--connection default
--let $SEARCH_PATTERN= Error writing file
--source include/search_pattern_in_file.inc

--disconnect con1
--disconnect con3
drop table t_trans, t2;

--echo # Reopen the binlog for the next sub-case
--source include/restart_mysqld.inc


--echo #
--echo # Sub-case 2: DML on a non-transactional table
--echo #
create table t_myisam (id int primary key) engine=myisam;
create table t2 (id int primary key) engine=innodb;

--connect (con1,localhost,root,,test)
--connect (con3,localhost,root,,test)

--connection con1
SET DEBUG_SYNC= 'commit_after_binlog_ready_before_LOCK_log SIGNAL paused WAIT_FOR go';
--send insert into t_myisam values (1)

--connection default
SET DEBUG_SYNC= 'now WAIT_FOR paused';

--echo # Trigger the binlog error and permanent closure via a failed rotation
SET DEBUG_DBUG= '+d,fault_injection_new_file_rotate_event';
--error ER_ERROR_ON_WRITE
FLUSH LOGS;

SET DEBUG_DBUG= '';
SET DEBUG_SYNC= 'now SIGNAL go';

--connection con1
--reap

--connection default
--echo # con1's statement must have committed in the engine
select * from t_myisam;
--echo # con2's (default conn's) statement must have committed in the engine
select * from t2;

--connection con3
insert into t2 values (202);
select * from t2 order by id;

--connection default
--let $SEARCH_PATTERN= Error writing file
--source include/search_pattern_in_file.inc

--disconnect con1
--disconnect con3
drop table t_myisam, t2;

--source include/restart_mysqld.inc


--echo #
--echo # Sub-case 3: Multi-table UPDATE on trans + non-trans
--echo #
create table t_trans (id int primary key, v int) engine=innodb;
create table t_myisam (id int primary key, v int) engine=myisam;
create table t2 (id int primary key) engine=innodb;
insert into t_trans values (1, 0), (2, 0);
insert into t_myisam values (1, 0), (2, 0);

--connect (con1,localhost,root,,test)
--connect (con3,localhost,root,,test)

--connection con1
SET DEBUG_SYNC= 'commit_after_binlog_ready_before_LOCK_log SIGNAL paused WAIT_FOR go';
--send update t_trans, t_myisam set t_trans.v = 1, t_myisam.v = 1 where t_trans.id = t_myisam.id

--connection default
SET DEBUG_SYNC= 'now WAIT_FOR paused';

--echo # Trigger the binlog error and permanent closure via a failed rotation
SET DEBUG_DBUG= '+d,fault_injection_new_file_rotate_event';
--error ER_ERROR_ON_WRITE
FLUSH LOGS;

SET DEBUG_DBUG= '';
SET DEBUG_SYNC= 'now SIGNAL go';

--connection con1
--reap

--connection default
--echo # con1's statement must have committed in the engine
select * from t_trans order by id;
select * from t_myisam order by id;
--echo # con2's (default conn's) statement must have committed in the engine
select * from t2;

--connection con3
insert into t2 values (302);
select * from t2 order by id;

--connection default
--let $SEARCH_PATTERN= Error writing file
--source include/search_pattern_in_file.inc

--disconnect con1
--disconnect con3
drop table t_trans, t_myisam, t2;

--source include/restart_mysqld.inc


--echo #
--echo # Sub-case 4: DML selecting from a temporary table
--echo #
# Temporary table DML still funnels through the same commit path as
# regular DML; the binlog cache is statement-cached but the commit
# goes through write_transaction_to_binlog.
create table t_trans (id int primary key) engine=innodb;
create table t2 (id int primary key) engine=innodb;

--connect (con1,localhost,root,,test)
--connect (con3,localhost,root,,test)

--connection con1
create temporary table t_tmp (id int primary key) engine=innodb;
insert into t_tmp values (1);
SET DEBUG_SYNC= 'commit_after_binlog_ready_before_LOCK_log SIGNAL paused WAIT_FOR go';
--send insert into t_trans (id) select id from t_tmp

--connection default
SET DEBUG_SYNC= 'now WAIT_FOR paused';

--echo # Trigger the binlog error and permanent closure via a failed rotation
SET DEBUG_DBUG= '+d,fault_injection_new_file_rotate_event';
--error ER_ERROR_ON_WRITE
FLUSH LOGS;

SET DEBUG_DBUG= '';
SET DEBUG_SYNC= 'now SIGNAL go';

--connection con1
--reap
--echo # con1's statement must have committed in the engine
select * from t_tmp;
select * from t_trans;
drop temporary table t_tmp;

--connection default
--echo # con2's (default conn's) statement must have committed in the engine
select * from t2;

--connection con3
insert into t2 values (402);
select * from t2 order by id;

--connection default
--let $SEARCH_PATTERN= Error writing file
--source include/search_pattern_in_file.inc

--disconnect con1
--disconnect con3
drop table t_trans, t2;

--source include/restart_mysqld.inc


--echo #
--echo # Sub-case 5: DDL (CREATE TABLE)
--echo #
create table t2 (id int primary key) engine=innodb;

--connect (con1,localhost,root,,test)
--connect (con3,localhost,root,,test)

--connection con1
SET DEBUG_SYNC= 'commit_after_binlog_ready_before_LOCK_log SIGNAL paused WAIT_FOR go';
--send create table t_ddl (id int primary key) engine=innodb

--connection default
SET DEBUG_SYNC= 'now WAIT_FOR paused';

--echo # Trigger the binlog error and permanent closure via a failed rotation
SET DEBUG_DBUG= '+d,fault_injection_new_file_rotate_event';
--error ER_ERROR_ON_WRITE
FLUSH LOGS;

SET DEBUG_DBUG= '';
SET DEBUG_SYNC= 'now SIGNAL go';

--connection con1
--reap

--connection default
--echo # DDL must have taken effect in the data dictionary
--replace_column 6 # 7 # 8 # 9 #
show create table t_ddl;
--echo # con2's (default conn's) statement must have committed in the engine
select * from t2;

--connection con3
insert into t2 values (502);
select * from t2 order by id;

--connection default
--let $SEARCH_PATTERN= Error writing file
--source include/search_pattern_in_file.inc

--disconnect con1
--disconnect con3
drop table t_ddl, t2;

--source include/restart_mysqld.inc


--echo #
--echo # Cleanup
--echo #
SET DEBUG_SYNC= 'RESET';

--echo # End of binlog_close_during_commit.test
Loading
Loading