Commit 8ebce535 authored by Andrei Elkin's avatar Andrei Elkin

Bug #40221 Replication failure on RBR + UPDATE the primary key

A transaction could result in having an extra event after a query that
errored e.g because of a dup key. Such a query is rolled back in
innodb, as specified, but has not been in binlog.
It appeares that the binlog engine did not always register for a query
(statement) because the previous query had not reset at its statement
commit time. Because of that fact there was no roll-back to the
trx_data->before_stmt_pos position and a the pending event of the
errorred query could become flushed to the binlog file.

Fixed with deploying the reset of trx_data->before_stmt_pos at the end
of the query processing.


mysql-test/suite/binlog/r/binlog_innodb_row.result:
  a new test file to contain tests dealing with binlogging innodb with
  the row-based format.
mysql-test/suite/binlog/t/binlog_innodb_row.test:
  a new test file to contain tests dealing with binlogging innodb with
  the row-based format.
sql/log.cc:
  Flushing the pending row event at binlog_end_trans() is moved down to the commit
  branch.
  Resetting of trx_data->before_stmt_pos to the uninitialized value at the end of the
  statement is implemented in binlog_commit() and binlog_rollback().
  Asserting emptiness the transaction cache after reset() and the uninitilized value for the statement's savepoint binlog position.
parent 014143e8
CREATE TABLE t1 (pk int auto_increment primary key) ENGINE=innodb;
show create table t1;
Table Create Table
t1 CREATE TABLE `t1` (
`pk` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`pk`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
reset master;
begin;
insert into t1 values (1),(2);
*** the following UPDATE query wont generate any updates for the binlog ***
update t1 set pk = 3 where pk < 3;
ERROR 23000: Duplicate entry '3' for key 'PRIMARY'
commit;
*** Results of the test: the binlog must have only Write_rows events not any Update_rows ***
show binlog events from <binlog_start>;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Query # # use `test`; BEGIN
master-bin.000001 # Table_map # # table_id: # (test.t1)
master-bin.000001 # Write_rows # # table_id: # flags: STMT_END_F
master-bin.000001 # Xid # # COMMIT /* XID */
drop table t1;
#
# Tests of innodb/binlog with the row binlog format
#
source include/have_innodb.inc;
source include/have_log_bin.inc;
source include/have_binlog_format_row.inc;
#
# Bug #40221 Replication failure on RBR + UPDATE the primary key
#
CREATE TABLE t1 (pk int auto_increment primary key) ENGINE=innodb;
show create table t1;
reset master;
begin;
insert into t1 values (1),(2);
--echo *** the following UPDATE query wont generate any updates for the binlog ***
--error ER_DUP_ENTRY
update t1 set pk = 3 where pk < 3;
commit;
--echo *** Results of the test: the binlog must have only Write_rows events not any Update_rows ***
source include/show_binlog_events.inc;
drop table t1;
...@@ -207,6 +207,7 @@ class binlog_trx_data { ...@@ -207,6 +207,7 @@ class binlog_trx_data {
truncate(0); truncate(0);
before_stmt_pos= MY_OFF_T_UNDEF; before_stmt_pos= MY_OFF_T_UNDEF;
trans_log.end_of_file= max_binlog_cache_size; trans_log.end_of_file= max_binlog_cache_size;
DBUG_ASSERT(empty());
} }
Rows_log_event *pending() const Rows_log_event *pending() const
...@@ -1377,8 +1378,6 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data, ...@@ -1377,8 +1378,6 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data,
FLAGSTR(thd->options, OPTION_NOT_AUTOCOMMIT), FLAGSTR(thd->options, OPTION_NOT_AUTOCOMMIT),
FLAGSTR(thd->options, OPTION_BEGIN))); FLAGSTR(thd->options, OPTION_BEGIN)));
thd->binlog_flush_pending_rows_event(TRUE);
/* /*
NULL denotes ROLLBACK with nothing to replicate: i.e., rollback of NULL denotes ROLLBACK with nothing to replicate: i.e., rollback of
only transactional tables. If the transaction contain changes to only transactional tables. If the transaction contain changes to
...@@ -1387,6 +1386,7 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data, ...@@ -1387,6 +1386,7 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data,
*/ */
if (end_ev != NULL) if (end_ev != NULL)
{ {
thd->binlog_flush_pending_rows_event(TRUE);
/* /*
Doing a commit or a rollback including non-transactional tables, Doing a commit or a rollback including non-transactional tables,
i.e., ending a transaction where we might write the transaction i.e., ending a transaction where we might write the transaction
...@@ -1435,6 +1435,7 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data, ...@@ -1435,6 +1435,7 @@ binlog_end_trans(THD *thd, binlog_trx_data *trx_data,
mysql_bin_log.update_table_map_version(); mysql_bin_log.update_table_map_version();
} }
DBUG_ASSERT(thd->binlog_get_pending_rows_event() == NULL);
DBUG_RETURN(error); DBUG_RETURN(error);
} }
...@@ -1466,6 +1467,7 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all) ...@@ -1466,6 +1467,7 @@ static int binlog_prepare(handlerton *hton, THD *thd, bool all)
*/ */
static int binlog_commit(handlerton *hton, THD *thd, bool all) static int binlog_commit(handlerton *hton, THD *thd, bool all)
{ {
int error= 0;
DBUG_ENTER("binlog_commit"); DBUG_ENTER("binlog_commit");
binlog_trx_data *const trx_data= binlog_trx_data *const trx_data=
(binlog_trx_data*) thd_get_ha_data(thd, binlog_hton); (binlog_trx_data*) thd_get_ha_data(thd, binlog_hton);
...@@ -1552,10 +1554,14 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all) ...@@ -1552,10 +1554,14 @@ static int binlog_commit(handlerton *hton, THD *thd, bool all)
{ {
Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE); Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE);
qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE) qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE)
int error= binlog_end_trans(thd, trx_data, &qev, all); error= binlog_end_trans(thd, trx_data, &qev, all);
DBUG_RETURN(error); goto end;
} }
DBUG_RETURN(0);
end:
if (!all)
trx_data->before_stmt_pos = MY_OFF_T_UNDEF; // part of the stmt commit
DBUG_RETURN(error);
} }
/** /**
...@@ -1615,6 +1621,8 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all) ...@@ -1615,6 +1621,8 @@ static int binlog_rollback(handlerton *hton, THD *thd, bool all)
*/ */
error= binlog_end_trans(thd, trx_data, 0, all); error= binlog_end_trans(thd, trx_data, 0, all);
} }
if (!all)
trx_data->before_stmt_pos = MY_OFF_T_UNDEF; // part of the stmt rollback
DBUG_RETURN(error); DBUG_RETURN(error);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment