Commit 41c642e6 authored by Alfranio Correia's avatar Alfranio Correia

BUG#51291 Unfortunate effect around variable binlog_direct_non_transactional_updates

BUG#46364 introduced the flag binlog_direct_non_transactional_updates which
would make N-changes to be written to the binary log upon committing the
statement when "ON". On the other hand, when "OFF" the option was supposed
to mimic the behavior in 5.1. However, the implementation was not mimicking
the behavior correctly and the following bugs popped up:

  Case #1: N-changes executed within a transaction would go into
           the S-cache. When later in the same transaction a
           T-change occurs, N-changes following it were written
           to the T-cache instead of the S-cache. In some cases,
           this raises problems. For example, a
           Table_map_log_event being written initially into the
           S-cache, together with the initial N-changes, would be
           absent from the T-cache. This would log N-changes
           orphaned from a Table_map_log_event (thence discarded
           at the slave). (MIXED and ROW)

   Case #2: When rolling back a transaction, the N-changes that
            might be in the T-cache were disregarded and
            truncated along with the T-changes. (MIXED and ROW)

   Case #3: When a MIXED statement (TN) is ahead of any other
            T-changes in the transaction and it fails, it is kept
            in the T-cache until the transaction ends. This is
            not the case in 5.1 or Betony (5.5.2). In these, the
            failed TN statement would be written to the binlog at
            the same instant it had failed and not deferred until
            transaction end. (SBR)

To fix these problems, we have decided to do what follows:

   For Case #1 and #2, we circumvent them:

      1. by not letting binlog_direct_non_transactional_updates
         affect MIXED and RBR. These modes will keep the behavior
         provided by WL#2687. Although this will make Celosia to
         behave differently from 5.1, an execution will be always
         safe under such modes in the sense that slaves will never
         go out sync. In 5.1, using either MIXED or ROW while
         mixing N-statements and T-statements was not safe.

   For Case #3, we don't actually fix it. We:

      1. keep it and make all MIXED statements whether they end
         up failing or not or whether they are up front in the
         transaction or after some transactional change to always
         be stored in the T-cache. This means that it is written
         to the binary log on transaction commit/rollback only.

      2. We make the warning message even more specific about the
         MIXED statement and SBR.

mysql-test/extra/rpl_tests/rpl_mixing_engines.test:
  Updated the test case to avoid checking inconsistencies between the master and slave
  when session.binlog_direct_non_transactional_updates is ON and the format is statement.
  
  In this scenario, they will diverge because a counter (within a triger) is incremented
  and associated to the issued statement. However, an n-statement is logged ahead of
  the transaction and thus is not executed by the same order in the slave and thus gets
  a different value from the counter.
mysql-test/suite/binlog/r/binlog_multi_engine.result:
  Updated the test case with the new error ER_BINLOG_UNSAFE_MIXED_STATEMENT.
mysql-test/suite/binlog/r/binlog_stm_mix_innodb_myisam.result:
  Updated the test case with the new error ER_BINLOG_UNSAFE_MIXED_STATEMENT.
mysql-test/suite/ndb/r/ndb_binlog_format.result:
  Updated the test case with the new error ER_BINLOG_UNSAFE_MIXED_STATEMENT.
mysql-test/suite/rpl/r/rpl_concurrency_error.result:
  Updated the test case with the new error ER_BINLOG_UNSAFE_MIXED_STATEMENT.
mysql-test/suite/rpl/r/rpl_stm_binlog_max_cache_size.result:
  Updated the test case with the new error ER_BINLOG_UNSAFE_MIXED_STATEMENT.
mysql-test/suite/rpl/r/rpl_stm_mixing_engines.result:
  Updated the test case with the new error ER_BINLOG_UNSAFE_MIXED_STATEMENT.
mysql-test/suite/rpl/r/rpl_stm_stop_middle_group.result:
  Updated the test case with the new error ER_BINLOG_UNSAFE_MIXED_STATEMENT.
sql/log.cc:
  Checked if either a trx-cache or a non-trx-cache should be used. 
  
  If bin_log_direct_non_trans_update is active or the format is either
  MIXED or ROW, the cache to be used depends on the flag is_transactional.
  
  When the format is STMT, the non-trx-cache should be used if the statement
  is non-transactional and the trx-cache is empty, i.e. if any transactional
  statement has not committed yet. Otherwise, the trx-cache should be used.
sql/share/errmsg-utf8.txt:
  Added the new unsafe error ER_BINLOG_UNSAFE_MIXED_STATEMENT.
sql/sql_class.cc:
  Started printing ER_BINLOG_UNSAFE_MIXED_STATEMENT, when there
  is a mixed-statement.
  
  Organized the names of the variables and added comments.
sql/sql_lex.cc:
  Added the new unsafe error ER_BINLOG_UNSAFE_MIXED_STATEMENT.
sql/sql_lex.h:
  Added the new unsafe error ER_BINLOG_UNSAFE_MIXED_STATEMENT.
parent 42c96c7e
......@@ -1767,7 +1767,7 @@ sync_slave_with_master;
--exec $MYSQL_DUMP --compact --order-by-primary --skip-extended-insert --no-create-info test > $MYSQLTEST_VARDIR/tmp/test-nmt-master.sql
--exec $MYSQL_DUMP_SLAVE --compact --order-by-primary --skip-extended-insert --no-create-info test > $MYSQLTEST_VARDIR/tmp/test-nmt-slave.sql
if (`select @@session.binlog_format != 'STATEMENT'`)
if (`select @@session.binlog_direct_non_transactional_updates = 0 || @@session.binlog_format != 'STATEMENT'`)
{
--diff_files $MYSQLTEST_VARDIR/tmp/test-nmt-master.sql $MYSQLTEST_VARDIR/tmp/test-nmt-slave.sql
}
......
......@@ -8,14 +8,14 @@ INSERT INTO t1b VALUES (1,1), (1,2), (2,1), (2,2);
INSERT INTO t1m VALUES (1,1), (1,2), (2,1), (2,2);
UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
The last event before the COMMIT is use `test`; UPDATE t1m, t1b SET m = 2, b = 3 WHERE n = c
*** Please look in binlog_multi_engine.test if you have a diff here ****
START TRANSACTION;
INSERT INTO t1n VALUES (1,1), (1,2), (2,1), (2,2);
UPDATE t1m, t1n SET m = 2, e = 3 WHERE n = f;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
UPDATE t1n, t1b SET e = 2, b = 3 WHERE f = c;
COMMIT;
TRUNCATE t1m;
......
......@@ -8,7 +8,7 @@ begin;
insert into t1 values(1);
insert into t2 select * from t1;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
commit;
show binlog events from <binlog_start>;
Log_name Pos Event_type Server_id End_log_pos Info
......@@ -23,7 +23,7 @@ begin;
insert into t1 values(2);
insert into t2 select * from t1;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
rollback;
Warnings:
Warning 1196 Some non-transactional changed tables couldn't be rolled back
......@@ -42,7 +42,7 @@ savepoint my_savepoint;
insert into t1 values(4);
insert into t2 select * from t1;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
rollback to savepoint my_savepoint;
Warnings:
Warning 1196 Some non-transactional changed tables couldn't be rolled back
......@@ -65,7 +65,7 @@ savepoint my_savepoint;
insert into t1 values(6);
insert into t2 select * from t1;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
rollback to savepoint my_savepoint;
Warnings:
Warning 1196 Some non-transactional changed tables couldn't be rolled back
......@@ -95,7 +95,7 @@ begin;
insert into t1 values(8);
insert into t2 select * from t1;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
select get_lock("a",10);
get_lock("a",10)
1
......@@ -111,7 +111,7 @@ reset master;
insert into t1 values(9);
insert into t2 select * from t1;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
show binlog events from <binlog_start>;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Query # # BEGIN
......@@ -127,7 +127,7 @@ insert into t1 values(10);
begin;
insert into t2 select * from t1;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
show binlog events from <binlog_start>;
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000001 # Query # # BEGIN
......@@ -427,7 +427,7 @@ begin;
insert into t1 values(8);
insert into t2 select * from t1;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
select get_lock("a",10);
get_lock("a",10)
1
......@@ -454,7 +454,7 @@ insert into ti values (1);
insert into ti values (2) ;
insert into tt select * from ti;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
rollback;
Warnings:
Warning 1196 Some non-transactional changed tables couldn't be rolled back
......@@ -473,7 +473,7 @@ count(*)
0
insert into ti select * from tt;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
select * from ti /* that is what slave would miss - a bug */;
a
1
......@@ -501,7 +501,7 @@ count(*)
0
insert into ti select * from tt;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
select * from tt /* that is what otherwise slave missed - the bug */;
a
1
......@@ -718,7 +718,7 @@ insert into ti values (1);
insert into ti values (2) ;
insert into tt select * from ti;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
rollback;
Warnings:
Warning 1196 Some non-transactional changed tables couldn't be rolled back
......@@ -737,7 +737,7 @@ count(*)
0
insert into ti select * from tt;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
select * from ti /* that is what slave would miss - bug#28960 */;
a
1
......@@ -765,7 +765,7 @@ count(*)
0
insert into ti select * from tt;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
select * from tt /* that is what otherwise slave missed - the bug */;
a
1
......
......@@ -9,12 +9,12 @@ INSERT INTO t1 VALUES (1,1), (1,2), (2,1), (2,2);
INSERT INTO t2 VALUES (1,1), (1,2), (2,1), (2,2);
UPDATE t1, t2 SET m = 2, b = 3 WHERE n = c;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
START TRANSACTION;
INSERT INTO t3 VALUES (1,1), (1,2), (2,1), (2,2);
UPDATE t1, t3 SET m = 2, e = 3 WHERE n = f;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
UPDATE t3, t2 SET e = 2, b = 3 WHERE f = c;
COMMIT;
show binlog events from <binlog_start>;
......
......@@ -22,7 +22,7 @@ SET AUTOCOMMIT = 1;
BEGIN;
UPDATE t SET f = 'yellow 2' WHERE i = 3;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
SET AUTOCOMMIT = 1;
BEGIN;
UPDATE t SET f = 'magenta 2' WHERE f = 'red';
......@@ -51,7 +51,7 @@ SET AUTOCOMMIT = 1;
BEGIN;
UPDATE t SET f = 'gray 2' WHERE i = 3;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
SET AUTOCOMMIT = 1;
BEGIN;
UPDATE t SET f = 'dark blue 2' WHERE f = 'red';
......@@ -77,7 +77,7 @@ master-bin.000001 # Xid # # COMMIT /* XID */
SET AUTOCOMMIT = 0;
UPDATE t SET f = 'yellow 1' WHERE i = 3;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
SET AUTOCOMMIT = 0;
UPDATE t SET f = 'magenta 1' WHERE f = 'red';
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
......@@ -104,7 +104,7 @@ master-bin.000001 # Query # # ROLLBACK
SET AUTOCOMMIT = 0;
UPDATE t SET f = 'gray 1' WHERE i = 3;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
SET AUTOCOMMIT = 0;
UPDATE t SET f = 'dark blue 1' WHERE f = 'red';
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
......
......@@ -40,7 +40,7 @@ Got one of the listed errors
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
BEGIN;
Got one of the listed errors
Got one of the listed errors
......
......@@ -53,7 +53,7 @@ set @@global.debug="+d,stop_slave_middle_group";
set @@global.debug="+d,incomplete_group_in_relay_log";
update tm as t1, ti as t2 set t1.a=t1.a * 2, t2.a=t2.a * 2;
Warnings:
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Non-transactional reads or writes are unsafe if they occur after transactional reads or writes inside a transaction.
Note 1592 Unsafe statement binlogged in statement format since BINLOG_FORMAT = STATEMENT. Reason for unsafeness: Statements that read from both transactional and non-transactional tables and write to any of them are unsafe.
SELECT "Fatal error: ... The slave SQL is stopped, leaving the current group of events unfinished with a non-transaction table changed. If the group consists solely of Row-based events, you can try restarting the slave with --slave-exec-mode=IDEMPOTENT, which ignores duplicate key, key not found, and similar errors (see documentation for details)." AS Last_SQL_Error, @check as `true`;
Last_SQL_Error true
Fatal error: ... The slave SQL is stopped, leaving the current group of events unfinished with a non-transaction table changed. If the group consists solely of Row-based events, you can try restarting the slave with --slave-exec-mode=IDEMPOTENT, which ignores duplicate key, key not found, and similar errors (see documentation for details). 1
......
......@@ -1631,7 +1631,10 @@ binlog_flush_stmt_cache(THD *thd, binlog_cache_mngr *cache_mngr)
*/
bool const is_transactional= FALSE;
IO_CACHE *cache_log= &cache_mngr->stmt_cache.cache_log;
thd->binlog_flush_pending_rows_event(TRUE, is_transactional);
if (thd->binlog_flush_pending_rows_event(TRUE, is_transactional))
DBUG_RETURN(1);
Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE, TRUE, 0);
if ((error= mysql_bin_log.write(thd, cache_log, &qev,
cache_mngr->stmt_cache.has_incident())))
......@@ -4180,7 +4183,7 @@ bool MYSQL_BIN_LOG::is_query_in_union(THD *thd, query_id_t query_id_param)
}
/**
This function checks if a transactional talbe was updated by the
This function checks if a transactional table was updated by the
current transaction.
@param thd The client thread that executed the current statement.
......@@ -4193,11 +4196,11 @@ trans_has_updated_trans_table(const THD* thd)
binlog_cache_mngr *const cache_mngr=
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
return (cache_mngr ? my_b_tell (&cache_mngr->trx_cache.cache_log) : 0);
return (cache_mngr ? !cache_mngr->trx_cache.empty() : 0);
}
/**
This function checks if a transactional talbe was updated by the
This function checks if a transactional table was updated by the
current statement.
@param thd The client thread that executed the current statement.
......@@ -4209,7 +4212,8 @@ stmt_has_updated_trans_table(const THD *thd)
{
Ha_trx_info *ha_info;
for (ha_info= thd->transaction.stmt.ha_list; ha_info; ha_info= ha_info->next())
for (ha_info= thd->transaction.stmt.ha_list; ha_info;
ha_info= ha_info->next())
{
if (ha_info->is_trx_read_write() && ha_info->ht() != binlog_hton)
return (TRUE);
......@@ -4219,11 +4223,14 @@ stmt_has_updated_trans_table(const THD *thd)
/**
This function checks if either a trx-cache or a non-trx-cache should
be used. If @c bin_log_direct_non_trans_update is active, the cache
to be used depends on the flag @c is_transactional.
be used. If @c bin_log_direct_non_trans_update is active or the format
is either MIXED or ROW, the cache to be used depends on the flag @c
is_transactional.
Otherswise, we use the trx-cache if either the @c is_transactional
is true or the trx-cache is not empty.
On the other hand, if binlog_format is STMT or direct option is
OFF, the trx-cache should be used if and only if the statement is
transactional or the trx-cache is not empty. Otherwise, the
non-trx-cache should be used.
@param thd The client thread.
@param is_transactional The changes are related to a trx-table.
......@@ -4236,8 +4243,9 @@ bool use_trans_cache(const THD* thd, bool is_transactional)
(binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
return
(thd->variables.binlog_direct_non_trans_update ? is_transactional :
(cache_mngr->trx_cache.empty() && !is_transactional ? FALSE : TRUE));
((thd->variables.binlog_format != BINLOG_FORMAT_STMT ||
thd->variables.binlog_direct_non_trans_update) ? is_transactional :
(is_transactional || cache_mngr->trx_cache.empty()));
}
/*
......
......@@ -6324,3 +6324,6 @@ ER_TOO_LONG_INDEX_COMMENT
ER_BINLOG_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE
eng "Mixing self-logging and non-self-logging engines in a statement is unsafe."
ER_BINLOG_UNSAFE_MIXED_STATEMENT
eng "Statements that read from both transactional and non-transactional tables and write to any of them are unsafe."
......@@ -3592,10 +3592,28 @@ int THD::decide_logging_format(TABLE_LIST *tables)
handler::Table_flags flags_write_all_set=
HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE;
/*
If different types of engines are about to be updated.
For example: Innodb and Falcon; Innodb and MyIsam.
*/
my_bool multi_write_engine= FALSE;
my_bool multi_engine= FALSE;
my_bool trans_non_trans_multi_engine= FALSE;
my_bool all_trans_engines= TRUE;
/*
If different types of engines are about to be accessed
and any of them is about to be updated. For example:
Innodb and Falcon; Innodb and MyIsam.
*/
my_bool multi_access_engine= FALSE;
/*
If non-transactional and transactional engines are about
to be accessed and any of them is about to be updated.
For example: Innodb and MyIsam.
*/
my_bool trans_non_trans_access_engines= FALSE;
/*
If all engines that are about to be updated are
transactional.
*/
my_bool all_trans_write_engines= TRUE;
TABLE* prev_write_table= NULL;
TABLE* prev_access_table= NULL;
......@@ -3629,8 +3647,8 @@ int THD::decide_logging_format(TABLE_LIST *tables)
if (prev_write_table && prev_write_table->file->ht !=
table->table->file->ht)
multi_write_engine= TRUE;
all_trans_engines= all_trans_engines &&
table->table->file->has_transactions();
all_trans_write_engines= all_trans_write_engines &&
table->table->file->has_transactions();
prev_write_table= table->table;
flags_write_all_set &= flags;
flags_write_some_set |= flags;
......@@ -3638,8 +3656,8 @@ int THD::decide_logging_format(TABLE_LIST *tables)
flags_some_set |= flags;
if (prev_access_table && prev_access_table->file->ht != table->table->file->ht)
{
multi_engine= TRUE;
trans_non_trans_multi_engine= trans_non_trans_multi_engine ||
multi_access_engine= TRUE;
trans_non_trans_access_engines= trans_non_trans_access_engines ||
(prev_access_table->file->has_transactions() !=
table->table->file->has_transactions());
}
......@@ -3650,9 +3668,9 @@ int THD::decide_logging_format(TABLE_LIST *tables)
DBUG_PRINT("info", ("flags_write_some_set: 0x%llx", flags_write_some_set));
DBUG_PRINT("info", ("flags_some_set: 0x%llx", flags_some_set));
DBUG_PRINT("info", ("multi_write_engine: %d", multi_write_engine));
DBUG_PRINT("info", ("multi_engine: %d", multi_engine));
DBUG_PRINT("info", ("trans_non_trans_multi_engine: %d",
trans_non_trans_multi_engine));
DBUG_PRINT("info", ("multi_access_engine: %d", multi_access_engine));
DBUG_PRINT("info", ("trans_non_trans_access_engines: %d",
trans_non_trans_access_engines));
int error= 0;
int unsafe_flags;
......@@ -3661,8 +3679,10 @@ int THD::decide_logging_format(TABLE_LIST *tables)
Set the statement as unsafe if:
. it is a mixed statement, i.e. access transactional and non-transactional
tables, and updates at least one;
or
tables, and update any of them;
or:
. an early statement updated a transactional table;
. and, the current statement updates a non-transactional table.
......@@ -3726,8 +3746,9 @@ int THD::decide_logging_format(TABLE_LIST *tables)
isolation level but if we have pure repeatable read or serializable the
lock history on the slave will be different from the master.
*/
if (trans_non_trans_multi_engine ||
(trans_has_updated_trans_table(this) && !all_trans_engines))
if (!trans_non_trans_access_engines)
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_MIXED_STATEMENT);
else if (trans_has_updated_trans_table(this) && !all_trans_write_engines)
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_NONTRANS_AFTER_TRANS);
/*
......@@ -3740,7 +3761,7 @@ int THD::decide_logging_format(TABLE_LIST *tables)
(flags_write_some_set & HA_HAS_OWN_BINLOGGING))
my_error((error= ER_BINLOG_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE),
MYF(0));
else if (multi_engine && flags_some_set & HA_HAS_OWN_BINLOGGING)
else if (multi_access_engine && flags_some_set & HA_HAS_OWN_BINLOGGING)
lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE);
/* both statement-only and row-only engines involved */
......
......@@ -53,7 +53,8 @@ Query_tables_list::binlog_stmt_unsafe_errcode[BINLOG_STMT_UNSAFE_COUNT] =
ER_BINLOG_UNSAFE_SYSTEM_VARIABLE,
ER_BINLOG_UNSAFE_SYSTEM_FUNCTION,
ER_BINLOG_UNSAFE_NONTRANS_AFTER_TRANS,
ER_BINLOG_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE
ER_BINLOG_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE,
ER_BINLOG_UNSAFE_MIXED_STATEMENT,
};
......
......@@ -1147,6 +1147,12 @@ class Query_tables_list
*/
BINLOG_STMT_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE,
/**
Statements that read from both transactional and non-transactional
tables and write to any of them are unsafe.
*/
BINLOG_STMT_UNSAFE_MIXED_STATEMENT,
/* The last element of this enumeration type. */
BINLOG_STMT_UNSAFE_COUNT
};
......
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