List:Commits« Previous MessageNext Message »
From:Sven Sandberg Date:December 16 2009 1:28pm
Subject:Re: bzr commit into mysql-5.1-rep+3 branch (Dao-Gang.Qu:3125) Bug#47863
View as plain text  
Hi Dao-Gang,

Thanks for fixing this. You did a good job, but I think some more work 
is needed, especially with the test case. Some comments:

(C1) In the test case, I think we should test to change binlog_format in 
the following scenarios:

  (1) with AUTOCOMMIT=1, outside a transaction
  (2) right after BEGIN
  (3) after BEGIN + non-transactional update
  (4) after BEGIN + transactional update
  (5) with AUTOCOMMIT=0, right after COMMIT
  (6) with AUTOCOMMIT=0, after COMMIT + non-transactional update
  (7) with AUTOCOMMIT=0, after COMMIT + transactional update

For each case above, I think we should test both @@global.binlog_format 
and @@session.binlog_format. I think there should be an error for 
@@session.binlog_format in case (3), (4), (6), (7), but not (1), (2), 
(5). I think there should never be any error for @global.binlog_format.

(C2) The test does not have to be a replication test, we don't check 
anything on the slave. I suggest to move the test to the suite/binlog.

(C3) Suggest simplification of the test case - see comment inline.

(C4) Please use named constant instead of numeric error code in test 
case - see comment inline.

(C5) I suggest to only make the session variable read-only in 
transactions. The global variable can still be changed. See details inline.

(C6) I suggest a small rewording of the error message.

/Sven


Dao-Gang.Qu@stripped wrote:
> #At file:///home/daogangqu/mysql/bzrwork/bug47863/mysql-5.1-rep%2B3/ based on
> revid:aelkin@stripped
> 
>  3125 Dao-Gang.Qu@stripped	2009-12-14
>       Bug #47863  	binlog_format should be writable only at transaction boundaries
>       
>       When @@session.binlog_format is modified inside a transaction, 
>       it can cause slave to go out of sync.
>       
>       To fix the problem, make the session variable 'binlog_format' 
>       read-only inside a transaction.
>      @ mysql-test/suite/rpl/r/rpl_binlog_format.result
>         Test result for bug#47863.
>      @ mysql-test/suite/rpl/t/rpl_binlog_format.test
>         Added the test file to verify if the session variable 'binlog_format' 
>         is read-only inside a transaction and in sub-statements.
>      @ sql/set_var.cc
>         Added code to make the session variable 'binlog_format' 
>         read-only inside a transaction.
> 
>     added:
>       mysql-test/suite/rpl/r/rpl_binlog_format.result
>       mysql-test/suite/rpl/t/rpl_binlog_format.test
>     modified:
>       sql/set_var.cc
>       sql/share/errmsg.txt
> === added file 'mysql-test/suite/rpl/r/rpl_binlog_format.result'
> --- a/mysql-test/suite/rpl/r/rpl_binlog_format.result	1970-01-01 00:00:00 +0000
> +++ b/mysql-test/suite/rpl/r/rpl_binlog_format.result	2009-12-14 06:20:05 +0000
> @@ -0,0 +1,78 @@
> +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;
> +call mtr.add_suppression("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.");
> +create table t1 (a int) engine= myisam;
> +create table t2 (a int) engine= innodb;
> +set @@session.binlog_format= statement;
> +# The value of 'binlog_format' is changed to STATEMENT.
> +insert into t1 values (1);
> +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.
> +# Check the session variable 'binlog_format' is read-only
> +# inside a transaction.
> +set @@global.binlog_format= row;
> +ERROR HY000: Cannot change binary logging format while a transaction is in progress
> +insert into t1 values (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.
> +commit;
> +begin;
> +insert into t2 values (3);
> +# Check the session variable 'binlog_format' is read-only
> +# inside a transaction.
> +set @@global.binlog_format= row;
> +ERROR HY000: Cannot change binary logging format while a transaction is in progress
> +insert into t2 values (4);
> +commit;
> +# The value of 'binlog_format' is not changed to ROW.
> +select * from t2;
> +a
> +1
> +3
> +4
> +select * from t1;
> +a
> +1
> +2
> +select * from t2;
> +a
> +1
> +3
> +4
> +select * from t1;
> +a
> +1
> +2
> +create table t3(a int, b int) engine= innodb;
> +create table t4(a int) engine= innodb;
> +create table t5(a int) engine= innodb;
> +create trigger tr2 after insert on t3 for each row begin
> +insert into t4(a) values(1);
> +set @@session.binlog_format= row; 
> +insert into t4(a) values(2);
> +insert into t5(a) values(3);
> +end |
> +# Check the session variable 'binlog_format' is read-only
> +# in sub-statements.
> +insert into t3(a,b) values(1,1);
> +ERROR HY000: Cannot change the binary logging format inside a stored function or
> trigger
> +# The value of 'binlog_format' is not changed to ROW.
> +select * from t4;
> +a
> +select * from t4;
> +a
> +drop table t1;
> +drop table t2;
> +drop table t3;
> +drop table t4;
> +drop table t5;
> 
> === added file 'mysql-test/suite/rpl/t/rpl_binlog_format.test'
> --- a/mysql-test/suite/rpl/t/rpl_binlog_format.test	1970-01-01 00:00:00 +0000
> +++ b/mysql-test/suite/rpl/t/rpl_binlog_format.test	2009-12-14 06:20:05 +0000
> @@ -0,0 +1,94 @@
> +#
> +# BUG#47863
> +# This test verifies if the session variable 'binlog_format' 
> +# is read-only inside a transaction and in sub-statements.
> +#
> +
> +source include/master-slave.inc;
> +source include/have_innodb.inc;
> +source include/have_binlog_format_row.inc;
> +
> +call mtr.add_suppression("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.");
> +create table t1 (a int) engine= myisam;
> +create table t2 (a int) engine= innodb;
> +
> +set @@session.binlog_format= statement;
> +if (`SELECT @@session.binlog_format = 'STATEMENT'`)
> +{
> +  --echo # The value of 'binlog_format' is changed to STATEMENT.
> +}

(C3) I think the I think the 'if' construct is too complicated - it's 
more clear to just execute 'SELECT @@session.binlog_format'.

> +
> +insert into t1 values (1);
> +            
> +begin;      
> +  insert into t2 select * from t1;
> +--echo # Check the session variable 'binlog_format' is read-only
> +--echo # inside a transaction.
> +--error 1658

(C4) Please use named constants instead of numeric error codes:

--error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT

(same suggestion at two places below)

> +  set @@global.binlog_format= row;
> +  insert into t1 values (2);
> +commit;
> +
> +begin;
> +  insert into t2 values (3);
> +--echo # Check the session variable 'binlog_format' is read-only
> +--echo # inside a transaction.
> +--error 1658

(C4) Please use named constant (see above).

> +  set @@global.binlog_format= row;
> +  insert into t2 values (4);
> +commit;
> +
> +if (`SELECT @@session.binlog_format = 'STATEMENT'`)
> +{
> +  --echo # The value of 'binlog_format' is not changed to ROW.
> +}

(C3) Please execute 'SELECT @@session.binlog_format' instead (see above).

> +
> +select * from t2; 
> +select * from t1;
> + 
> +sync_slave_with_master;
> +connection slave; 
> +select * from t2; 
> +select * from t1;
> +
> +connection master;
> +create table t3(a int, b int) engine= innodb;
> +create table t4(a int) engine= innodb;
> +create table t5(a int) engine= innodb;
> +delimiter |;
> +eval create trigger tr2 after insert on t3 for each row begin
> +    insert into t4(a) values(1);
> +    set @@session.binlog_format= row; 
> +    insert into t4(a) values(2);
> +    insert into t5(a) values(3);
> +end |
> +delimiter ;|
> +
> +--echo # Check the session variable 'binlog_format' is read-only
> +--echo # in sub-statements.
> +--error 1560

(C4) Please use named constant (see above).

> +insert into t3(a,b) values(1,1);
> +
> +if (`SELECT @@session.binlog_format = 'STATEMENT'`)
> +{
> +  --echo # The value of 'binlog_format' is not changed to ROW.
> +}

(C3) Please execute 'SELECT @@session.binlog_format' instead (see above).

> +
> +select * from t4;
> +
> +sync_slave_with_master;
> +connection slave;
> +select * from t4;
> +
> +connection master;
> +drop table t1;
> +drop table t2;
> +drop table t3;
> +drop table t4;
> +drop table t5;
> +sync_slave_with_master;
> +
> 
> === modified file 'sql/set_var.cc'
> --- a/sql/set_var.cc	2009-11-30 18:20:26 +0000
> +++ b/sql/set_var.cc	2009-12-14 06:20:05 +0000
> @@ -1288,6 +1288,14 @@ bool sys_var_thd_binlog_format::is_reado
>      my_error(ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT, MYF(0));
>      return 1;    
>    }
> +  /*
> +    Make the session variable 'binlog_format' read-only inside a transaction.
> +  */
> +  if (thd->active_transaction())
> +  {
> +    my_error(ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT, MYF(0));
> +    return 1;
> +  }
>    return sys_var_thd_enum::is_readonly();
>  }

(C5) This will make both the global and the session variable read-only. 
I think it would be enough to make the session variable read-only. I 
think we can do that by using the function

sys_var_thd_binlog_format::check(THD *thd, set_var *var)

in set_var.cc instead of is_readonly(). Then you can generate an error 
only if var->type==OPT_SESSION.

>  
> 
> === modified file 'sql/share/errmsg.txt'
> --- a/sql/share/errmsg.txt	2009-11-30 18:20:26 +0000
> +++ b/sql/share/errmsg.txt	2009-12-14 06:20:05 +0000
> @@ -6249,3 +6249,5 @@ ER_DEBUG_SYNC_TIMEOUT
>  ER_DEBUG_SYNC_HIT_LIMIT
>    eng "debug sync point hit limit reached"
>    ger "Debug Sync Point Hit Limit erreicht"
> +ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT
> +  eng "Cannot change binary logging format while a transaction is in progress"

(C6) I suggest a small change in wording: "Cannot modify 
@@session.binlog_format inside a transaction". 
("@@session.binlog_format" is more specific than "binary logging 
format". "inside a transaction" states more clearly that the transaction 
and the change of binlog format are in the same thread - "while a 
transaction is in progress" could mean that another client executes a 
transaction).


-- 
Sven Sandberg, Software Engineer
MySQL AB, www.mysql.com
Thread
bzr commit into mysql-5.1-rep+3 branch (Dao-Gang.Qu:3125) Bug#47863Dao-Gang.Qu14 Dec
  • Re: bzr commit into mysql-5.1-rep+3 branch (Dao-Gang.Qu:3125) Bug#47863Sven Sandberg16 Dec
    • Re: bzr commit into mysql-5.1-rep+3 branch (Dao-Gang.Qu:3125) Bug#47863Daogang Qu17 Dec