List:Commits« Previous MessageNext Message »
From:He Zhenxing Date:June 9 2009 10:06am
Subject:Re: bzr commit into mysql-5.1-bugteam branch (alfranio.correia:2912)
Bug#43929
View as plain text  
Hi Alfranio,

Nice work, thank you!

This bug is targeted 5.0+, which means it should go to 5.0 too, so
please recommit this against 5.0-bugteam, patch approved after recommit.

Alfranio Correia wrote:
> #At
> file:///home/acorreia/workspace.sun/repository.mysql/bzrwork/bug-43929/mysql-5.1-bugteam/
> based on revid:bjorn.munch@stripped
> 
>  2912 Alfranio Correia	2009-06-06
>       BUG#43929 binlog corruption when max_binlog_cache_size is exceeded
>       
>       Large transactions and statements may corrupt the binary log if the size of
> the
>       cache, which is set by the max_binlog_cache_size, is not enough to store the
>       the changes.
>       
>       In a nutshell, to fix the bug, we save the position of the next character in
> the
>       cache before starting processing a statement. If there is a problem, we simply
>       restore the position thus removing any effect of the statement from the cache.
>       Unfortunately, to avoid corrupting the binary log, we may end up loosing
> changes
>       on non-transactional tables if they do not fit in the cache. In such cases, we
>       store an Incident_log_event in order to stop the slave and alert users that
> some
>       changes were not logged.
>       
>       Precisely, for every non-transactional changes that do not fit into the cache,
>       we do the following:
>         a) the statement is *not* logged
>         b) an incident event is logged after committing/rolling back the
> transaction,
>         if any. Note that if a failure happens before writing the incident event to
>         the binary log, the slave will not stop and the master will not have
> reported
>         any error.
>         c) its respective statement gives an error
>       
>       For transactional changes that do not fit into the cache, we do the following:
>         a) the statement is *not* logged
>         b) its respective statement gives an error
>       
>       To work properly, this patch requires two additional things. Firstly, callers
> to
>       MYSQL_BIN_LOG::write and THD::binlog_query must handle any error returned and
>       take the appropriate actions such as undoing the effects of a statement. We
>       already changed some calls in the sql_insert.cc, sql_update.cc and
> sql_insert.cc
>       modules but the remaining calls spread all over the code should be handled in
>       BUG#37148. Secondly, statements must be either classified as DDL or DML
> because
>       DDLs that do not get into the cache must generate an incident event since they
>       cannot be rolled back.
> 
>     added:
>       mysql-test/suite/rpl/r/rpl_binlog_max_cache_size.result
>       mysql-test/suite/rpl/t/rpl_binlog_max_cache_size-master.opt
>       mysql-test/suite/rpl/t/rpl_binlog_max_cache_size.test
>     modified:
>       mysql-test/include/commit.inc
>       mysql-test/r/commit_1innodb.result
>       sql/log.cc
>       sql/log.h
>       sql/sql_delete.cc
>       sql/sql_insert.cc
>       sql/sql_update.cc
> === modified file 'mysql-test/include/commit.inc'
> --- a/mysql-test/include/commit.inc	2009-01-23 12:22:05 +0000
> +++ b/mysql-test/include/commit.inc	2009-06-06 13:43:02 +0000
> @@ -669,13 +669,9 @@ call p_verify_status_increment(1, 0, 1, 
>  insert t1 set a=3;
>  call p_verify_status_increment(2, 2, 2, 2);
>  savepoint a;
> -call p_verify_status_increment(0, 0, 0, 0);
> +call p_verify_status_increment(1, 0, 0, 0);
>  insert t1 set a=4;
> ---echo # Binlog does not register itself this time for other than the 1st
> ---echo # statement of the transaction with MIXED/STATEMENT binlog_format.
> ---echo # It needs registering with the ROW format. Therefore 1,0,2,2 are 
> ---echo # the correct arguments to this test after bug#40221 fixed.
> -call p_verify_status_increment(1, 0, 2, 2);
> +call p_verify_status_increment(2, 2, 2, 2);
>  release savepoint a;
>  rollback;
>  call p_verify_status_increment(0, 0, 0, 0);
> 
> === modified file 'mysql-test/r/commit_1innodb.result'
> --- a/mysql-test/r/commit_1innodb.result	2009-02-10 14:44:58 +0000
> +++ b/mysql-test/r/commit_1innodb.result	2009-06-06 13:43:02 +0000
> @@ -766,15 +766,11 @@ call p_verify_status_increment(2, 2, 2, 
>  SUCCESS
>  
>  savepoint a;
> -call p_verify_status_increment(0, 0, 0, 0);
> +call p_verify_status_increment(1, 0, 0, 0);
>  SUCCESS
>  
>  insert t1 set a=4;
> -# Binlog does not register itself this time for other than the 1st
> -# statement of the transaction with MIXED/STATEMENT binlog_format.
> -# It needs registering with the ROW format. Therefore 1,0,2,2 are 
> -# the correct arguments to this test after bug#40221 fixed.
> -call p_verify_status_increment(1, 0, 2, 2);
> +call p_verify_status_increment(2, 2, 2, 2);
>  SUCCESS
>  
>  release savepoint a;
> 
> === added file 'mysql-test/suite/rpl/r/rpl_binlog_max_cache_size.result'
> --- a/mysql-test/suite/rpl/r/rpl_binlog_max_cache_size.result	1970-01-01 00:00:00
> +0000
> +++ b/mysql-test/suite/rpl/r/rpl_binlog_max_cache_size.result	2009-06-06 13:43:02
> +0000
> @@ -0,0 +1,135 @@
> +stop slave;
> +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
> +reset master;
> +reset slave;
> +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
> +start slave;
> +CREATE TABLE t1(a INT PRIMARY KEY, data VARCHAR(30000)) ENGINE=Innodb;
> +CREATE TABLE t2(a INT PRIMARY KEY, data VARCHAR(30000)) ENGINE=MyIsam;
> +CREATE TABLE t3(a INT PRIMARY KEY, data VARCHAR(30000)) ENGINE=Innodb;
>
> +########################################################################################
> +#                                   1 - SINGLE STATEMENT
>
> +########################################################################################
> +*** Single statment on transactional table ***
> +Got one of the listed errors
> +*** Single statment on non-transactional table ***
> +*** After WL#2687 the difference between STATEMENT/MIXED and ROW will not exist.
> ***
> +Got one of the listed errors
> +*** Single statment on both transactional and non-transactional tables. ***
> +*** After WL#2687 we will be able to change the order of the tables. ***
> +Got one of the listed errors
> +SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
> +START SLAVE SQL_THREAD;
> +TRUNCATE TABLE t1;
> +TRUNCATE TABLE t2;
> +TRUNCATE TABLE t3;
> +BEGIN;
> +Got one of the listed errors
> +Got one of the listed errors
> +Got one of the listed errors
> +BEGIN;
> +Got one of the listed errors
> +Got one of the listed errors
> +Got one of the listed errors
> +BEGIN;
> +Got one of the listed errors
> +Got one of the listed errors
> +source include/diff_master_slave.inc;
>
> +########################################################################################
> +#                                     3 - BEGIN - COMMIT
>
> +########################################################################################
> +TRUNCATE TABLE t1;
> +TRUNCATE TABLE t2;
> +TRUNCATE TABLE t3;
> +BEGIN;
> +Got one of the listed errors
> +Got one of the listed errors
> +Got one of the listed errors
> +COMMIT;
> +source include/diff_master_slave.inc;
>
> +########################################################################################
> +#                                      4 - BEGIN - ROLLBACK
>
> +########################################################################################
> +TRUNCATE TABLE t1;
> +TRUNCATE TABLE t2;
> +TRUNCATE TABLE t3;
> +BEGIN;
> +Got one of the listed errors
> +Got one of the listed errors
> +Got one of the listed errors
> +ROLLBACK;
> +Warnings:
> +Warning	1196	Some non-transactional changed tables couldn't be rolled back
> +source include/diff_master_slave.inc;
>
> +########################################################################################
> +#                                         5 - PROCEDURE 
>
> +########################################################################################
> +TRUNCATE TABLE t1;
> +TRUNCATE TABLE t2;
> +TRUNCATE TABLE t3;
> +CREATE PROCEDURE p1(pd VARCHAR(30000))
> +BEGIN
> +INSERT INTO t1 (a, data) VALUES (1, pd);
> +INSERT INTO t1 (a, data) VALUES (2, pd);
> +INSERT INTO t1 (a, data) VALUES (3, pd);
> +INSERT INTO t1 (a, data) VALUES (4, pd);
> +INSERT INTO t1 (a, data) VALUES (5, 's');
> +END//
> +TRUNCATE TABLE t1;
> +TRUNCATE TABLE t1;
> +BEGIN;
> +Got one of the listed errors
> +COMMIT;
> +TRUNCATE TABLE t1;
> +BEGIN;
> +Got one of the listed errors
> +ROLLBACK;
> +source include/diff_master_slave.inc;
>
> +########################################################################################
> +#                                           6 - XID
>
> +########################################################################################
> +TRUNCATE TABLE t1;
> +TRUNCATE TABLE t2;
> +TRUNCATE TABLE t3;
> +BEGIN;
> +Got one of the listed errors
> +Got one of the listed errors
> +Got one of the listed errors
> +ROLLBACK TO sv;
> +Warnings:
> +Warning	1196	Some non-transactional changed tables couldn't be rolled back
> +COMMIT;
> +source include/diff_master_slave.inc;
>
> +########################################################################################
> +#                                        7 - NON-TRANS TABLE
>
> +########################################################################################
> +TRUNCATE TABLE t1;
> +TRUNCATE TABLE t2;
> +TRUNCATE TABLE t3;
> +BEGIN;
> +Got one of the listed errors
> +Got one of the listed errors
> +Got one of the listed errors
> +Got one of the listed errors
> +Got one of the listed errors
> +COMMIT;
> +BEGIN;
> +Got one of the listed errors
> +COMMIT;
>
> +########################################################################################
> +#                                        CLEAN
>
> +########################################################################################
> +DROP TABLE t1;
> +DROP TABLE t2;
> +DROP TABLE t3;
> +DROP TABLE IF EXISTS t4;
> +DROP TABLE IF EXISTS t5;
> +DROP TABLE IF EXISTS t6;
> +DROP PROCEDURE p1;
> +DROP TABLE t1;
> +DROP TABLE t2;
> +DROP TABLE t3;
> +DROP TABLE IF EXISTS t4;
> +DROP TABLE IF EXISTS t5;
> +DROP TABLE IF EXISTS t6;
> +DROP PROCEDURE p1;
> 
> === added file 'mysql-test/suite/rpl/t/rpl_binlog_max_cache_size-master.opt'
> --- a/mysql-test/suite/rpl/t/rpl_binlog_max_cache_size-master.opt	1970-01-01 00:00:00
> +0000
> +++ b/mysql-test/suite/rpl/t/rpl_binlog_max_cache_size-master.opt	2009-06-06 13:43:02
> +0000
> @@ -0,0 +1 @@
> +--binlog_cache_size=4096 --max_binlog_cache_size=7680
> 
> === added file 'mysql-test/suite/rpl/t/rpl_binlog_max_cache_size.test'
> --- a/mysql-test/suite/rpl/t/rpl_binlog_max_cache_size.test	1970-01-01 00:00:00
> +0000
> +++ b/mysql-test/suite/rpl/t/rpl_binlog_max_cache_size.test	2009-06-06 13:43:02
> +0000
> @@ -0,0 +1,395 @@
>
> +########################################################################################
> +#    This test verifies if the binlog is not corrupted when the cache buffer is not
> +#    big enough to accommodate the changes and is divided in five steps:
> +#
> +#    1 - Single Statements:
> +#    1.1 - Single statement on transactional table.
> +#    1.2 - Single statement on non-transactional table. 
> +#    1.3 - Single statement on both transactional and non-transactional tables.
> +#    In both 1.2 and 1.3, an incident event is logged to notify the user that the
> +#    master and slave are diverging.
> +#
> +#    2 - Transactions ended by an implicit commit.
> +#
> +#    3 - Transactions ended by a COMMIT.
> +#
> +#    4 - Transactions ended by a ROLLBACK.
> +#
> +#    5 - Transactions with a failing statement that updates a non-transactional
> +#    table. In this case, a failure means that the statement does not get into
> +#    the cache and an incident event is logged to notify the user that the master
> +#    and slave are diverging.
> +#    
>
> +########################################################################################
> +
>
> +########################################################################################
> +#                                Configuring the environment
>
> +########################################################################################
> +--source include/have_innodb.inc
> +--source include/master-slave.inc
> +--source include/not_embedded.inc
> +--source include/not_windows.inc
> +
> +CREATE TABLE t1(a INT PRIMARY KEY, data VARCHAR(30000)) ENGINE=Innodb;
> +CREATE TABLE t2(a INT PRIMARY KEY, data VARCHAR(30000)) ENGINE=MyIsam;
> +CREATE TABLE t3(a INT PRIMARY KEY, data VARCHAR(30000)) ENGINE=Innodb;
> +
> +let $data = `select concat('"', repeat('a',2000), '"')`;
> +
> +--echo
> ########################################################################################
> +--echo #                                   1 - SINGLE STATEMENT
> +--echo
> ########################################################################################
> +
> +connection master;
> +
> +--echo *** Single statment on transactional table ***
> +--disable_query_log
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +eval INSERT INTO t1 (a, data) VALUES (1,
> +     CONCAT($data, $data, $data, $data, $data));
> +--enable_query_log
> +
> +--echo *** Single statment on non-transactional table ***
> +--echo *** After WL#2687 the difference between STATEMENT/MIXED and ROW will not
> exist. ***
> +--disable_query_log
> +--disable_warnings
> +if (`SELECT @@binlog_format = 'STATEMENT' || @@binlog_format = 'MIXED'`)
> +{
> +  eval INSERT INTO t2 (a, data) VALUES (2,
> +       CONCAT($data, $data, $data, $data, $data, $data));
> +  --echo Got one of the listed errors
> +}
> +if (`SELECT @@binlog_format = 'ROW'`)
> +{
> +  --error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +  eval INSERT INTO t2 (a, data) VALUES (2,
> +       CONCAT($data, $data, $data, $data, $data, $data));
> +
> +  connection slave;
> +  --source include/wait_for_slave_sql_to_stop.inc
> +  SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
> +  START SLAVE SQL_THREAD;
> +  --source include/wait_for_slave_sql_to_start.inc
> +}
> +--enable_warnings
> +--enable_query_log
> +
> +connection master;
> +
> +--disable_query_log
> +eval INSERT INTO t1 (a, data) VALUES (3, $data);
> +eval INSERT INTO t1 (a, data) VALUES (4, $data);
> +eval INSERT INTO t1 (a, data) VALUES (5, $data);
> +eval INSERT INTO t2 (a, data) VALUES (3, $data);
> +eval INSERT INTO t2 (a, data) VALUES (4, $data);
> +eval INSERT INTO t2 (a, data) VALUES (5, $data);
> +--enable_query_log
> +
> +--echo *** Single statment on both transactional and non-transactional tables. ***
> +--echo *** After WL#2687 we will be able to change the order of the tables. ***
> +--disable_query_log
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +eval UPDATE t2, t1 SET t2.data = CONCAT($data, $data, $data, $data),
> +                       t1.data = CONCAT($data, $data, $data, $data);
> +--enable_query_log
> +
> +connection slave;
> +--source include/wait_for_slave_sql_to_stop.inc
> +SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
> +START SLAVE SQL_THREAD;
> +--source include/wait_for_slave_sql_to_start.inc
> +
> +#--echo
> ########################################################################################
> +#--echo #                             2 - BEGIN - IMPLICIT COMMIT by DDL
> +#--echo
> ########################################################################################
> +
> +connection master;
> +TRUNCATE TABLE t1;
> +TRUNCATE TABLE t2;
> +TRUNCATE TABLE t3;
> +
> +BEGIN;
> +--disable_query_log
> +--eval INSERT INTO t1 (a, data) VALUES (1, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (2, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (3, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (4, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (5, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (6, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (7, 's');
> +--eval INSERT INTO t2 (a, data) VALUES (8, 's');
> +--eval INSERT INTO t1 (a, data) VALUES (9, 's');
> +--enable_query_log
> +
> +--disable_query_log
> +ALTER TABLE t3 ADD COLUMN d int;
> +--enable_query_log
> +
> +--disable_query_log
> +--eval INSERT INTO t2 (a, data) VALUES (10, $data);
> +--eval INSERT INTO t2 (a, data) VALUES (11, $data);
> +--eval INSERT INTO t2 (a, data) VALUES (12, $data);
> +--eval INSERT INTO t2 (a, data) VALUES (13, $data);
> +--enable_query_log
> +
> +BEGIN;
> +--disable_query_log
> +--eval INSERT INTO t1 (a, data) VALUES (14, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (15, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (16, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (17, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (18, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (19, 's');
> +--eval INSERT INTO t2 (a, data) VALUES (20, 's');
> +--eval INSERT INTO t1 (a, data) VALUES (21, 's');
> +--enable_query_log
> +
> +if (`SELECT @@binlog_format = 'STATEMENT' || @@binlog_format = 'MIXED'`)
> +{
> +  --disable_query_log
> +  CREATE TABLE t4 SELECT * FROM t1;
> +  --enable_query_log
> +  --echo Got one of the listed errors
> +}
> +if (`SELECT @@binlog_format = 'ROW'`)
> +{
> +  --disable_query_log
> +  --error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +  CREATE TABLE t4 SELECT * FROM t1;
> +  --enable_query_log
> +}
> +
> +--disable_query_log
> +--eval INSERT INTO t2 (a, data) VALUES (15, $data);
> +--enable_query_log
> +
> +BEGIN;
> +--disable_query_log
> +--eval INSERT INTO t1 (a, data) VALUES (22, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (23, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (24, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (25, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (26, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (27, 's');
> +--eval INSERT INTO t2 (a, data) VALUES (28, 's');
> +--eval INSERT INTO t1 (a, data) VALUES (29, 's');
> +--enable_query_log
> +
> +--disable_query_log
> +CREATE TABLE t5 (a int);
> +--enable_query_log
> +
> +let $diff_statement= SELECT * FROM t1;
> +--source include/diff_master_slave.inc
> +
> +--echo
> ########################################################################################
> +--echo #                                     3 - BEGIN - COMMIT
> +--echo
> ########################################################################################
> +
> +connection master;
> +TRUNCATE TABLE t1;
> +TRUNCATE TABLE t2;
> +TRUNCATE TABLE t3;
> +
> +BEGIN;
> +--disable_query_log
> +--eval INSERT INTO t1 (a, data) VALUES (1, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (2, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (3, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (4, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (5, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (6, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (7, 's');
> +--eval INSERT INTO t2 (a, data) VALUES (8, 's');
> +--eval INSERT INTO t1 (a, data) VALUES (9, 's');
> +--enable_query_log
> +COMMIT;
> +
> +let $diff_statement= SELECT * FROM t1;
> +--source include/diff_master_slave.inc
> +
> +--echo
> ########################################################################################
> +--echo #                                      4 - BEGIN - ROLLBACK
> +--echo
> ########################################################################################
> +
> +connection master;
> +TRUNCATE TABLE t1;
> +TRUNCATE TABLE t2;
> +TRUNCATE TABLE t3;
> +
> +BEGIN;
> +--disable_query_log
> +--eval INSERT INTO t1 (a, data) VALUES (1, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (2, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (3, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (4, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (5, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (6, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (7, 's');
> +--eval INSERT INTO t2 (a, data) VALUES (8, 's');
> +--eval INSERT INTO t1 (a, data) VALUES (9, 's');
> +--enable_query_log
> +ROLLBACK;
> +
> +let $diff_statement= SELECT * FROM t1;
> +--source include/diff_master_slave.inc
> +
> +--echo
> ########################################################################################
> +--echo #                                         5 - PROCEDURE 
> +--echo
> ########################################################################################
> +
> +connection master;
> +TRUNCATE TABLE t1;
> +TRUNCATE TABLE t2;
> +TRUNCATE TABLE t3;
> +
> +DELIMITER //;
> +
> +CREATE PROCEDURE p1(pd VARCHAR(30000))
> +BEGIN
> +  INSERT INTO t1 (a, data) VALUES (1, pd);
> +  INSERT INTO t1 (a, data) VALUES (2, pd);
> +  INSERT INTO t1 (a, data) VALUES (3, pd);
> +  INSERT INTO t1 (a, data) VALUES (4, pd);
> +  INSERT INTO t1 (a, data) VALUES (5, 's');
> +END//
> +
> +DELIMITER ;//
> +
> +TRUNCATE TABLE t1;
> +
> +--disable_query_log
> +eval CALL p1($data);
> +--enable_query_log
> +
> +TRUNCATE TABLE t1;
> +
> +BEGIN;
> +--disable_query_log
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +eval CALL p1($data);
> +--enable_query_log
> +COMMIT;
> +
> +TRUNCATE TABLE t1;
> +
> +BEGIN;
> +--disable_query_log
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +eval CALL p1($data);
> +--enable_query_log
> +ROLLBACK;
> +
> +let $diff_statement= SELECT * FROM t1;
> +--source include/diff_master_slave.inc
> +
> +--echo
> ########################################################################################
> +--echo #                                           6 - XID
> +--echo
> ########################################################################################
> +
> +connection master;
> +TRUNCATE TABLE t1;
> +TRUNCATE TABLE t2;
> +TRUNCATE TABLE t3;
> +
> +BEGIN;
> +--disable_query_log
> +--eval INSERT INTO t1 (a, data) VALUES (1, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (2, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (3, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (4, $data);
> +SAVEPOINT sv;
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (5, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (6, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (7, 's');
> +--eval INSERT INTO t2 (a, data) VALUES (8, 's');
> +--eval INSERT INTO t1 (a, data) VALUES (9, 's');
> +--enable_query_log
> +ROLLBACK TO sv;
> +COMMIT;
> +
> +let $diff_statement= SELECT * FROM t1;
> +--source include/diff_master_slave.inc
> +
> +--echo
> ########################################################################################
> +--echo #                                        7 - NON-TRANS TABLE
> +--echo
> ########################################################################################
> +
> +connection master;
> +TRUNCATE TABLE t1;
> +TRUNCATE TABLE t2;
> +TRUNCATE TABLE t3;
> +
> +BEGIN;
> +--disable_query_log
> +--eval INSERT INTO t1 (a, data) VALUES (1, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (2, $data);
> +--eval INSERT INTO t2 (a, data) VALUES (3, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (4, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (5, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (6, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (7, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval UPDATE t2 SET data= CONCAT($data, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (8, 's');
> +--eval INSERT INTO t1 (a, data) VALUES (9, 's');
> +--eval INSERT INTO t2 (a, data) VALUES (10, 's');
> +--eval INSERT INTO t1 (a, data) VALUES (11, 's');
> +--enable_query_log
> +COMMIT;
> +
> +BEGIN;
> +--disable_query_log
> +--eval INSERT INTO t1 (a, data) VALUES (15, $data);
> +--eval INSERT INTO t1 (a, data) VALUES (16, $data);
> +--eval INSERT INTO t2 (a, data) VALUES (17, $data);
> +--error ER_TRANS_CACHE_FULL, ER_ERROR_ON_WRITE
> +--eval INSERT INTO t1 (a, data) VALUES (18, $data);
> +--enable_query_log
> +COMMIT;
> +
> +connection slave;
> +--source include/wait_for_slave_sql_to_stop.inc
> +
> +--echo
> ########################################################################################
> +--echo #                                        CLEAN
> +--echo
> ########################################################################################
> +
> +--disable_warnings
> +connection master;
> +DROP TABLE t1;
> +DROP TABLE t2;
> +DROP TABLE t3;
> +DROP TABLE IF EXISTS t4;
> +DROP TABLE IF EXISTS t5;
> +DROP TABLE IF EXISTS t6;
> +DROP PROCEDURE p1;
> +connection slave;
> +DROP TABLE t1;
> +DROP TABLE t2;
> +DROP TABLE t3;
> +DROP TABLE IF EXISTS t4;
> +DROP TABLE IF EXISTS t5;
> +DROP TABLE IF EXISTS t6;
> +DROP PROCEDURE p1;
> +--enable_warnings
> 
> === modified file 'sql/log.cc'
> --- a/sql/log.cc	2009-04-09 15:25:25 +0000
> +++ b/sql/log.cc	2009-06-06 13:43:02 +0000
> @@ -153,7 +153,8 @@ private:
>  class binlog_trx_data {
>  public:
>    binlog_trx_data()
> -    : at_least_one_stmt(0), m_pending(0), before_stmt_pos(MY_OFF_T_UNDEF)
> +    : at_least_one_stmt(0), incident(FALSE), m_pending(0),
> +    before_stmt_pos(MY_OFF_T_UNDEF)
>    {
>      trans_log.end_of_file= max_binlog_cache_size;
>    }
> @@ -184,6 +185,7 @@ public:
>      delete pending();
>      set_pending(0);
>      reinit_io_cache(&trans_log, WRITE_CACHE, pos, 0, 0);
> +    trans_log.end_of_file= max_binlog_cache_size;
>      if (pos < before_stmt_pos)
>        before_stmt_pos= MY_OFF_T_UNDEF;
>  
> @@ -206,6 +208,7 @@ public:
>      if (!empty())
>        truncate(0);
>      before_stmt_pos= MY_OFF_T_UNDEF;
> +    incident= FALSE;
>      trans_log.end_of_file= max_binlog_cache_size;
>      DBUG_ASSERT(empty());
>    }
> @@ -222,11 +225,22 @@ public:
>  
>    IO_CACHE trans_log;                         // The transaction cache
>  
> +  void set_incident(void)
> +  {
> +    incident= TRUE;
> +  }
> +  
> +  bool has_incident(void)
> +  {
> +    return(incident);
> +  }
> +
>    /**
>      Boolean that is true if there is at least one statement in the
>      transaction cache.
>    */
>    bool at_least_one_stmt;
> +  bool incident;
>  
>  private:
>    /*
> @@ -1391,7 +1405,8 @@ binlog_end_trans(THD *thd, binlog_trx_da
>    */
>    if (end_ev != NULL)
>    {
> -    thd->binlog_flush_pending_rows_event(TRUE);
> +    if (thd->binlog_flush_pending_rows_event(TRUE))
> +      DBUG_RETURN(1);
>      /*
>        Doing a commit or a rollback including non-transactional tables,
>        i.e., ending a transaction where we might write the transaction
> @@ -1402,7 +1417,8 @@ binlog_end_trans(THD *thd, binlog_trx_da
>        were, we would have to ensure that we're not ending a statement
>        inside a stored function.
>       */
> -    error= mysql_bin_log.write(thd, &trx_data->trans_log, end_ev);
> +    error= mysql_bin_log.write(thd, &trx_data->trans_log, end_ev,
> +                               trx_data->has_incident());
>      trx_data->reset();
>  
>      /*
> @@ -1428,7 +1444,11 @@ binlog_end_trans(THD *thd, binlog_trx_da
>       */
>      thd->binlog_remove_pending_rows_event(TRUE);
>      if (all || !(thd->options & (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT)))
> +    {
> +      if (trx_data->has_incident())
> +        mysql_bin_log.write_incident(thd, TRUE);
>        trx_data->reset();
> +    }
>      else                                        // ...statement
>        trx_data->truncate(trx_data->before_stmt_pos);
>  
> @@ -1545,9 +1565,11 @@ static int binlog_rollback(handlerton *h
>                         YESNO(all),
>                         YESNO(thd->transaction.all.modified_non_trans_table),
>                         YESNO(thd->transaction.stmt.modified_non_trans_table)));
> -  if (all && thd->transaction.all.modified_non_trans_table ||
> -      !all && thd->transaction.stmt.modified_non_trans_table ||
> -      (thd->options & OPTION_KEEP_LOG))
> +  if ((all && thd->transaction.all.modified_non_trans_table) ||
> +      (!all && thd->transaction.stmt.modified_non_trans_table &&
> +       !mysql_bin_log.check_write_error(thd)) ||
> +      ((thd->options & OPTION_KEEP_LOG) &&
> +        !mysql_bin_log.check_write_error(thd)))
>    {
>      /*
>        We write the transaction cache with a rollback last if we have
> @@ -1561,14 +1583,22 @@ static int binlog_rollback(handlerton *h
>      qev.error_code= 0; // see comment in MYSQL_LOG::write(THD, IO_CACHE)
>      error= binlog_end_trans(thd, trx_data, &qev, all);
>    }
> -  else if (all && !thd->transaction.all.modified_non_trans_table ||
> -           !all && !thd->transaction.stmt.modified_non_trans_table)
> +  else
>    {
>      /*
> -      If we have modified only transactional tables, we can truncate
> -      the transaction cache without writing anything to the binary
> -      log.
> +      We reach this point if either only transactional tables were modified or
> +      the effect of a statement that did not get into the binlog needs to be
> +      rolled back. In the latter case, if a statement changed non-transactional
> +      tables or had the OPTION_KEEP_LOG associated, we write an incident event
> +      to the binlog in order to stop slaves and notify users that some changes
> +      on the master did not get into the binlog and slaves will be inconsistent.
> +      On the other hand, if a statement is transactional, we just safely roll it
> +      back.
>       */
> +    if ((thd->transaction.stmt.modified_non_trans_table ||
> +        (thd->options & OPTION_KEEP_LOG)) &&
> +        mysql_bin_log.check_write_error(thd))
> +      trx_data->set_incident();
>      error= binlog_end_trans(thd, trx_data, 0, all);
>    }
>    if (!all)
> @@ -1576,6 +1606,44 @@ static int binlog_rollback(handlerton *h
>    DBUG_RETURN(error);
>  }
>  
> +void MYSQL_BIN_LOG::set_write_error(THD *thd)
> +{
> +  DBUG_ENTER("MYSQL_BIN_LOG::set_write_error");
> +
> +  write_error= 1;
> +
> +  if (check_write_error(thd))
> +    DBUG_VOID_RETURN;
> +
> +  if (my_errno == EFBIG)
> +    my_message(ER_TRANS_CACHE_FULL, ER(ER_TRANS_CACHE_FULL), MYF(MY_WME));
> +  else
> +    my_error(ER_ERROR_ON_WRITE, MYF(MY_WME), name, errno);
> +
> +  DBUG_VOID_RETURN;
> +}
> +
> +bool MYSQL_BIN_LOG::check_write_error(THD *thd)
> +{
> +  DBUG_ENTER("MYSQL_BIN_LOG::check_write_error");
> +
> +  bool checked= FALSE;
> +
> +  if (!thd->is_error())
> +    DBUG_RETURN(checked);
> +
> +  switch (thd->main_da.sql_errno())
> +  {
> +    case ER_TRANS_CACHE_FULL:
> +    case ER_ERROR_ON_WRITE:
> +    case ER_BINLOG_LOGGING_IMPOSSIBLE:
> +      checked= TRUE;
> +    break;
> +  }
> +
> +  DBUG_RETURN(checked);
> +}
> +
>  /**
>    @note
>    How do we handle this (unlikely but legal) case:
> @@ -3851,6 +3919,7 @@ MYSQL_BIN_LOG::flush_and_set_pending_row
>      if (pending->write(file))
>      {
>        pthread_mutex_unlock(&LOCK_log);
> +      set_write_error(thd);
>        DBUG_RETURN(1);
>      }
>  
> @@ -3925,7 +3994,8 @@ bool MYSQL_BIN_LOG::write(Log_event *eve
>    */
>    bool const end_stmt=
>      thd->prelocked_mode && thd->lex->requires_prelocking();
> -  thd->binlog_flush_pending_rows_event(end_stmt);
> +  if (thd->binlog_flush_pending_rows_event(end_stmt))
> +    DBUG_RETURN(error);
>  
>    pthread_mutex_lock(&LOCK_log);
>  
> @@ -3976,8 +4046,7 @@ bool MYSQL_BIN_LOG::write(Log_event *eve
>          DBUG_PRINT("info", ("Using trans_log: cache: %d, trans_log_pos: %lu",
>                              event_info->get_cache_stmt(),
>                              (ulong) trans_log_pos));
> -        if (trans_log_pos == 0)
> -          thd->binlog_start_trans_and_stmt();
> +        thd->binlog_start_trans_and_stmt();
>          file= trans_log;
>        }
>        /*
> @@ -4055,7 +4124,8 @@ bool MYSQL_BIN_LOG::write(Log_event *eve
>         Write the SQL command
>       */
>  
> -    if (event_info->write(file))
> +    if (event_info->write(file) || 
> +        DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0))
>        goto err;
>  
>      if (file == &log_file) // we are writing to the real log (disk)
> @@ -4069,13 +4139,7 @@ bool MYSQL_BIN_LOG::write(Log_event *eve
>  
>  err:
>      if (error)
> -    {
> -      if (my_errno == EFBIG)
> -	my_message(ER_TRANS_CACHE_FULL, ER(ER_TRANS_CACHE_FULL), MYF(0));
> -      else
> -	my_error(ER_ERROR_ON_WRITE, MYF(0), name, errno);
> -      write_error=1;
> -    }
> +      set_write_error(thd);
>    }
>  
>    if (event_info->flags & LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F)
> @@ -4327,6 +4391,29 @@ int MYSQL_BIN_LOG::write_cache(IO_CACHE 
>    return 0;                                     // All OK
>  }
>  
> +bool MYSQL_BIN_LOG::write_incident(THD *thd, bool lock)
> +{
> +  uint error= 0;
> +  DBUG_ENTER("MYSQL_BIN_LOG::write_incident");
> +  LEX_STRING const write_error_msg=
> +    { C_STRING_WITH_LEN("error writing to the binary log") };
> +  Incident incident= INCIDENT_LOST_EVENTS;
> +  Incident_log_event ev(thd, incident, write_error_msg);
> +  if (lock)
> +    pthread_mutex_lock(&LOCK_log);
> +  ev.write(&log_file);
> +  if (lock)
> +  {
> +    if (!error && !(error= flush_and_sync()))
> +    {
> +      signal_update();
> +      rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED);
> +    }
> +    pthread_mutex_unlock(&LOCK_log);
> +  }
> +  DBUG_RETURN(error);
> +}
> +
>  /**
>    Write a cached log entry to the binary log.
>    - To support transaction over replication, we wrap the transaction
> @@ -4348,7 +4435,8 @@ int MYSQL_BIN_LOG::write_cache(IO_CACHE 
>      'cache' needs to be reinitialized after this functions returns.
>  */
>  
> -bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event)
> +bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event,
> +                          bool incident)
>  {
>    DBUG_ENTER("MYSQL_BIN_LOG::write(THD *, IO_CACHE *, Log_event *)");
>    VOID(pthread_mutex_lock(&LOCK_log));
> @@ -4408,6 +4496,10 @@ bool MYSQL_BIN_LOG::write(THD *thd, IO_C
>  
>        if (commit_event && commit_event->write(&log_file))
>          goto err;
> +
> +      if (incident && write_incident(thd, FALSE))
> +        goto err;
> +
>        if (flush_and_sync())
>          goto err;
>        DBUG_EXECUTE_IF("half_binlogged_transaction", abort(););
> 
> === modified file 'sql/log.h'
> --- a/sql/log.h	2009-01-23 12:22:05 +0000
> +++ b/sql/log.h	2009-06-06 13:43:02 +0000
> @@ -356,9 +356,12 @@ public:
>    void new_file();
>  
>    bool write(Log_event* event_info); // binary log write
> -  bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event);
> +  bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event, bool incident);
> +  bool write_incident(THD *thd, bool lock);
>  
>    int  write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync);
> +  void set_write_error(THD *thd);
> +  bool check_write_error(THD *thd);
>  
>    void start_union_events(THD *thd, query_id_t query_id_param);
>    void stop_union_events(THD *thd);
> 
> === modified file 'sql/sql_delete.cc'
> --- a/sql/sql_delete.cc	2009-05-15 13:25:29 +0000
> +++ b/sql/sql_delete.cc	2009-06-06 13:43:02 +0000
> @@ -404,7 +404,7 @@ cleanup:
>                                          thd->query, thd->query_length,
>                                          is_trans, FALSE, killed_status);
>  
> -      if (log_result && transactional_table)
> +      if (log_result)
>        {
>  	error=1;
>        }
> 
> === modified file 'sql/sql_insert.cc'
> --- a/sql/sql_insert.cc	2009-05-15 12:57:51 +0000
> +++ b/sql/sql_insert.cc	2009-06-06 13:43:02 +0000
> @@ -893,8 +893,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *t
>  	if (thd->binlog_query(THD::ROW_QUERY_TYPE,
>  			      thd->query, thd->query_length,
>  			      transactional_table, FALSE,
> -			      (error>0) ? thd->killed : THD::NOT_KILLED) &&
> -	    transactional_table)
> +			      (error>0) ? thd->killed : THD::NOT_KILLED))
>          {
>  	  error=1;
>  	}
> 
> === modified file 'sql/sql_update.cc'
> --- a/sql/sql_update.cc	2009-05-15 13:03:22 +0000
> +++ b/sql/sql_update.cc	2009-06-06 13:43:02 +0000
> @@ -799,8 +799,7 @@ int mysql_update(THD *thd,
>          thd->clear_error();
>        if (thd->binlog_query(THD::ROW_QUERY_TYPE,
>                              thd->query, thd->query_length,
> -                            transactional_table, FALSE, killed_status) &&
> -          transactional_table)
> +                            transactional_table, FALSE, killed_status))
>        {
>          error=1;				// Rollback update
>        }
> @@ -2079,8 +2078,7 @@ bool multi_update::send_eof()
>          thd->clear_error();
>        if (thd->binlog_query(THD::ROW_QUERY_TYPE,
>                              thd->query, thd->query_length,
> -                            transactional_tables, FALSE, killed_status) &&
> -          trans_safe)
> +                            transactional_tables, FALSE, killed_status))
>        {
>  	local_error= 1;				// Rollback update
>        }
> 

Thread
bzr commit into mysql-5.1-bugteam branch (alfranio.correia:2912)Bug#43929Alfranio Correia6 Jun
  • Re: bzr commit into mysql-5.1-bugteam branch (alfranio.correia:2912)Bug#43929Luís Soares8 Jun
    • Re: bzr commit into mysql-5.1-bugteam branch (alfranio.correia:2912)Bug#43929Alfranio Correia9 Jun
      • Re: bzr commit into mysql-5.1-bugteam branch (alfranio.correia:2912)Bug#43929Luís Soares9 Jun
        • Re: bzr commit into mysql-5.1-bugteam branch (alfranio.correia:2912)Bug#43929Alfranio Correia9 Jun
          • Re: bzr commit into mysql-5.1-bugteam branch (alfranio.correia:2912)Bug#43929Luís Soares9 Jun
  • Re: bzr commit into mysql-5.1-bugteam branch (alfranio.correia:2912)Bug#43929He Zhenxing9 Jun
    • Re: bzr commit into mysql-5.1-bugteam branch (alfranio.correia:2912)Bug#43929Alfranio Correia9 Jun