summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <Dao-Gang.Qu@sun.com>2009-12-23 18:43:45 +0800
committerunknown <Dao-Gang.Qu@sun.com>2009-12-23 18:43:45 +0800
commitf87816b450055e9815ced19d47a224bc58e9bfcc (patch)
treeb5f1b15e2d418254319f1d6862d4fc4e085a0666
parent03324f6a0a177d5e31f58533ab773970f2b2f747 (diff)
downloadmariadb-git-f87816b450055e9815ced19d47a224bc58e9bfcc.tar.gz
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/binlog/r/binlog_format_switch_inside_trans.result: Test result for bug#47863. mysql-test/suite/binlog/t/binlog_format_switch_inside_trans.test: Added 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.
-rw-r--r--mysql-test/suite/binlog/r/binlog_format_switch_inside_trans.result90
-rw-r--r--mysql-test/suite/binlog/t/binlog_format_switch_inside_trans.test98
-rw-r--r--sql/set_var.cc8
-rw-r--r--sql/share/errmsg.txt2
4 files changed, 198 insertions, 0 deletions
diff --git a/mysql-test/suite/binlog/r/binlog_format_switch_inside_trans.result b/mysql-test/suite/binlog/r/binlog_format_switch_inside_trans.result
new file mode 100644
index 00000000000..c1e900053e4
--- /dev/null
+++ b/mysql-test/suite/binlog/r/binlog_format_switch_inside_trans.result
@@ -0,0 +1,90 @@
+set @save_binlog_format= @@global.binlog_format;
+create table t1 (a int) engine= myisam;
+create table t2 (a int) engine= innodb;
+SELECT @@session.binlog_format;
+@@session.binlog_format
+ROW
+SET AUTOCOMMIT=1;
+# Test that the session variable 'binlog_format'
+# is writable outside a transaction.
+set @@session.binlog_format= statement;
+SELECT @@session.binlog_format;
+@@session.binlog_format
+STATEMENT
+begin;
+# Test that the session variable 'binlog_format' is read-only
+# inside a transaction with no preceding updates.
+set @@session.binlog_format= mixed;
+ERROR HY000: Cannot modify @@session.binlog_format inside a transaction
+insert into t2 values (1);
+# Test that the session variable 'binlog_format' is read-only
+# inside a transaction with preceding transactional updates.
+set @@session.binlog_format= row;
+ERROR HY000: Cannot modify @@session.binlog_format inside a transaction
+commit;
+begin;
+insert into t1 values (2);
+# Test that the session variable 'binlog_format' is read-only
+# inside a transaction with preceding non-transactional updates.
+set @@session.binlog_format= statement;
+ERROR HY000: Cannot modify @@session.binlog_format inside a transaction
+commit;
+# Test that the session variable 'binlog_format' is writable
+# when AUTOCOMMIT=0, before a transaction has started.
+set AUTOCOMMIT=0;
+set @@session.binlog_format= row;
+SELECT @@session.binlog_format;
+@@session.binlog_format
+ROW
+insert into t1 values (4);
+# Test that the session variable 'binlog_format' is read-only inside an
+# AUTOCOMMIT=0 transaction with preceding non-transactional updates.
+set @@session.binlog_format= statement;
+ERROR HY000: Cannot modify @@session.binlog_format inside a transaction
+SELECT @@session.binlog_format;
+@@session.binlog_format
+ROW
+commit;
+insert into t2 values (5);
+# Test that the session variable 'binlog_format' is read-only inside an
+# AUTOCOMMIT=0 transaction with preceding transactional updates.
+set @@session.binlog_format= row;
+ERROR HY000: Cannot modify @@session.binlog_format inside a transaction
+SELECT @@session.binlog_format;
+@@session.binlog_format
+ROW
+commit;
+begin;
+insert into t2 values (6);
+# Test that the global variable 'binlog_format' is writable
+# inside a transaction.
+SELECT @@global.binlog_format;
+@@global.binlog_format
+ROW
+set @@global.binlog_format= statement;
+SELECT @@global.binlog_format;
+@@global.binlog_format
+STATEMENT
+commit;
+set @@global.binlog_format= @save_binlog_format;
+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= statement;
+insert into t4(a) values(2);
+insert into t5(a) values(3);
+end |
+# Test that 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
+SELECT @@session.binlog_format;
+@@session.binlog_format
+ROW
+drop table t1;
+drop table t2;
+drop table t3;
+drop table t4;
+drop table t5;
diff --git a/mysql-test/suite/binlog/t/binlog_format_switch_inside_trans.test b/mysql-test/suite/binlog/t/binlog_format_switch_inside_trans.test
new file mode 100644
index 00000000000..8ae87d13a12
--- /dev/null
+++ b/mysql-test/suite/binlog/t/binlog_format_switch_inside_trans.test
@@ -0,0 +1,98 @@
+#
+# BUG#47863
+# This test verifies if the session variable 'binlog_format'
+# is read-only inside a transaction and in sub-statements.
+#
+
+source include/have_innodb.inc;
+source include/have_binlog_format_row.inc;
+
+set @save_binlog_format= @@global.binlog_format;
+create table t1 (a int) engine= myisam;
+create table t2 (a int) engine= innodb;
+
+SELECT @@session.binlog_format;
+SET AUTOCOMMIT=1;
+--echo # Test that the session variable 'binlog_format'
+--echo # is writable outside a transaction.
+set @@session.binlog_format= statement;
+SELECT @@session.binlog_format;
+
+begin;
+--echo # Test that the session variable 'binlog_format' is read-only
+--echo # inside a transaction with no preceding updates.
+--error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT
+ set @@session.binlog_format= mixed;
+
+ insert into t2 values (1);
+--echo # Test that the session variable 'binlog_format' is read-only
+--echo # inside a transaction with preceding transactional updates.
+--error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT
+ set @@session.binlog_format= row;
+commit;
+
+begin;
+ insert into t1 values (2);
+--echo # Test that the session variable 'binlog_format' is read-only
+--echo # inside a transaction with preceding non-transactional updates.
+--error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT
+ set @@session.binlog_format= statement;
+commit;
+
+--echo # Test that the session variable 'binlog_format' is writable
+--echo # when AUTOCOMMIT=0, before a transaction has started.
+set AUTOCOMMIT=0;
+set @@session.binlog_format= row;
+SELECT @@session.binlog_format;
+
+insert into t1 values (4);
+--echo # Test that the session variable 'binlog_format' is read-only inside an
+--echo # AUTOCOMMIT=0 transaction with preceding non-transactional updates.
+--error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT
+set @@session.binlog_format= statement;
+SELECT @@session.binlog_format;
+commit;
+
+insert into t2 values (5);
+--echo # Test that the session variable 'binlog_format' is read-only inside an
+--echo # AUTOCOMMIT=0 transaction with preceding transactional updates.
+--error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT
+set @@session.binlog_format= row;
+SELECT @@session.binlog_format;
+commit;
+
+begin;
+ insert into t2 values (6);
+--echo # Test that the global variable 'binlog_format' is writable
+--echo # inside a transaction.
+ SELECT @@global.binlog_format;
+ set @@global.binlog_format= statement;
+ SELECT @@global.binlog_format;
+commit;
+
+set @@global.binlog_format= @save_binlog_format;
+
+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= statement;
+ insert into t4(a) values(2);
+ insert into t5(a) values(3);
+end |
+delimiter ;|
+
+--echo # Test that the session variable 'binlog_format' is read-only
+--echo # in sub-statements.
+--error ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT
+insert into t3(a,b) values(1,1);
+SELECT @@session.binlog_format;
+
+drop table t1;
+drop table t2;
+drop table t3;
+drop table t4;
+drop table t5;
+
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 16772fee3c3..b1d1c89cfe6 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -1245,6 +1245,14 @@ void fix_slave_exec_mode(enum_var_type type)
bool sys_var_thd_binlog_format::check(THD *thd, set_var *var) {
/*
+ Make the session variable 'binlog_format' read-only inside a transaction.
+ */
+ if (thd->active_transaction() && (var->type == OPT_SESSION))
+ {
+ my_error(ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT, MYF(0));
+ return 1;
+ }
+ /*
All variables that affect writing to binary log (either format or
turning logging on and off) use the same checking. We call the
superclass ::check function to assign the variable correctly, and
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index 4262aa82cfa..1ce306bb35e 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -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 modify @@session.binlog_format inside a transaction"