From 2173e61833013f2973719d77d0f2a1141f9bb2e6 Mon Sep 17 00:00:00 2001 From: Alfranio Correia Date: Wed, 24 Feb 2010 12:45:15 +0000 Subject: [PATCH] BUG#51277 SUPER_ACL should be checked unconditionally (binlog_format and binlog_direct) SUPER_ACL should be checked unconditionally while verifying if the binlog_format or the binlog_direct_non_transactional_updates might be changed. Roughly speaking, both session values cannot be changed in the context of a transaction or a stored function. Note that changing the global value does not cause any effect until a new connection is created. So, we fixed the problem by first checking the permissions and right after further verifications are ignored if the global value is being updated. In this patch, we also re-structure the test case to make it more readable. --- .../r/binlog_switch_inside_trans.result | 76 ++++++++++++------ .../binlog/t/binlog_switch_inside_trans.test | 79 ++++++++++++------- sql/sys_vars.cc | 47 ++++++----- 3 files changed, 124 insertions(+), 78 deletions(-) diff --git a/mysql-test/suite/binlog/r/binlog_switch_inside_trans.result b/mysql-test/suite/binlog/r/binlog_switch_inside_trans.result index 57feb3f50f0..de224a190c2 100644 --- a/mysql-test/suite/binlog/r/binlog_switch_inside_trans.result +++ b/mysql-test/suite/binlog/r/binlog_switch_inside_trans.result @@ -5,9 +5,14 @@ create table t2 (a int) engine= innodb; SELECT @@session.binlog_format; @@session.binlog_format ROW +SELECT @@session.binlog_direct_non_transactional_updates; +@@session.binlog_direct_non_transactional_updates +1 SET AUTOCOMMIT=1; -# Test that the session variable 'binlog_format' -# is writable outside a transaction. +# Test that the session variable 'binlog_format' and +# 'binlog_direct_non_transactional_updates' are +# writable outside a transaction. +# Current session values are ROW and FALSE, respectively. set @@session.binlog_format= statement; set @@session.binlog_direct_non_transactional_updates= TRUE; SELECT @@session.binlog_format; @@ -17,15 +22,19 @@ SELECT @@session.binlog_direct_non_transactional_updates; @@session.binlog_direct_non_transactional_updates 1 begin; -# Test that the session variable 'binlog_format' is read-only -# inside a transaction with no preceding updates. +# Test that the session variable 'binlog_format' and +# 'binlog_direct_non_transactional_updates' are +# read-only inside a transaction with no preceding updates. +# Current session values are STATEMENT and TRUE, respectively. set @@session.binlog_format= mixed; ERROR HY000: Cannot modify @@session.binlog_format inside a transaction set @@session.binlog_direct_non_transactional_updates= FALSE; ERROR HY000: Cannot modify @@session.binlog_direct_non_transactional_updates 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. +# Test that the session variable 'binlog_format' and +# 'binlog_direct_non_transactional_updates' are +# read-only inside a transaction with preceding transactional updates. +# Current session values are STATEMENT and TRUE, respectively. set @@session.binlog_format= row; ERROR HY000: Cannot modify @@session.binlog_format inside a transaction set @@session.binlog_direct_non_transactional_updates= FALSE; @@ -33,15 +42,19 @@ ERROR HY000: Cannot modify @@session.binlog_direct_non_transactional_updates ins 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; +# Test that the session variable 'binlog_format' and +# 'binlog_direct_non_transactional_updates' are +# read-only inside a transaction with preceding non-transactional updates. +# Current session values are STATEMENT and TRUE, respectively. +set @@session.binlog_format= mixed; ERROR HY000: Cannot modify @@session.binlog_format inside a transaction set @@session.binlog_direct_non_transactional_updates= FALSE; ERROR HY000: Cannot modify @@session.binlog_direct_non_transactional_updates inside a transaction commit; -# Test that the session variable 'binlog_format' is writable -# when AUTOCOMMIT=0, before a transaction has started. +# Test that the session variable 'binlog_format' and +# 'binlog_direct_non_transactional_updates' are +# writable when AUTOCOMMIT=0, before a transaction has started. +# Current session values are STATEMENT and TRUE, respectively. set AUTOCOMMIT=0; set @@session.binlog_format= row; set @@session.binlog_direct_non_transactional_updates= FALSE; @@ -51,9 +64,12 @@ ROW SELECT @@session.binlog_direct_non_transactional_updates; @@session.binlog_direct_non_transactional_updates 0 -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. +insert into t1 values (3); +# Test that the session variable 'binlog_format' and +# 'binlog_direct_non_transactional_updates' are +# read-only inside an AUTOCOMMIT=0 transaction +# with preceding non-transactional updates. +# Current session values are ROW and FALSE, respectively. set @@session.binlog_format= statement; ERROR HY000: Cannot modify @@session.binlog_format inside a transaction set @@session.binlog_direct_non_transactional_updates= TRUE; @@ -65,10 +81,13 @@ SELECT @@session.binlog_direct_non_transactional_updates; @@session.binlog_direct_non_transactional_updates 0 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; +insert into t2 values (4); +# Test that the session variable 'binlog_format' and +# 'binlog_direct_non_transactional_updates' are +# read-only inside an AUTOCOMMIT=0 transaction with +# preceding transactional updates. +# Current session values are ROW and FALSE, respectively. +set @@session.binlog_format= statement; ERROR HY000: Cannot modify @@session.binlog_format inside a transaction set @@session.binlog_direct_non_transactional_updates= TRUE; ERROR HY000: Cannot modify @@session.binlog_direct_non_transactional_updates inside a transaction @@ -80,9 +99,11 @@ SELECT @@session.binlog_direct_non_transactional_updates; 0 commit; begin; -insert into t2 values (6); -# Test that the global variable 'binlog_format' is writable -# inside a transaction. +insert into t2 values (5); +# Test that the global variable 'binlog_format' and +# 'binlog_direct_non_transactional_updates' are +# writable inside a transaction. +# Current session values are ROW and FALSE, respectively. SELECT @@global.binlog_format; @@global.binlog_format ROW @@ -96,18 +117,19 @@ SELECT @@global.binlog_direct_non_transactional_updates; 1 commit; set @@global.binlog_format= @save_binlog_format; -set @@global.binlog_direct_non_transactional_updates= @save_binlog_dirct; +set @@global.binlog_direct_non_transactional_updates= @save_binlog_dirct; 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 tr1 after insert on t3 for each row begin insert into t4(a) values(1); -set @@session.binlog_format= statement; +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. +# Current session value is ROW. 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; @@ -118,12 +140,14 @@ create table t7(a int) engine= innodb; create table t8(a int) engine= innodb; create trigger tr2 after insert on t6 for each row begin insert into t7(a) values(1); -set @@global.binlog_direct_non_transactional_updates= FALSE; +set @@session.binlog_direct_non_transactional_updates= TRUE; insert into t7(a) values(2); insert into t8(a) values(3); end | -# Test that the session variable 'binlog_format' is read-only -# in sub-statements. +# Test that the session variable +# 'binlog_direct_non_transactional_updates' is +# read-only in sub-statements. +# Current session value is FALSE. insert into t6(a,b) values(1,1); ERROR HY000: Cannot change the binlog direct flag inside a stored function or trigger SELECT @@session.binlog_direct_non_transactional_updates; diff --git a/mysql-test/suite/binlog/t/binlog_switch_inside_trans.test b/mysql-test/suite/binlog/t/binlog_switch_inside_trans.test index 7b98b5cd0d5..cdc94198933 100644 --- a/mysql-test/suite/binlog/t/binlog_switch_inside_trans.test +++ b/mysql-test/suite/binlog/t/binlog_switch_inside_trans.test @@ -1,7 +1,8 @@ # # BUG#47863 -# This test verifies if the session variable 'binlog_format' -# is read-only inside a transaction and in sub-statements. +# This test verifies if the session variable 'binlog_format' and +# 'binlog_direct_non_transactional_updates' are read-only inside +# a transaction and in sub-statements. # source include/have_innodb.inc; @@ -13,25 +14,32 @@ create table t1 (a int) engine= myisam; create table t2 (a int) engine= innodb; SELECT @@session.binlog_format; +SELECT @@session.binlog_direct_non_transactional_updates; SET AUTOCOMMIT=1; ---echo # Test that the session variable 'binlog_format' ---echo # is writable outside a transaction. +--echo # Test that the session variable 'binlog_format' and +--echo # 'binlog_direct_non_transactional_updates' are +--echo # writable outside a transaction. +--echo # Current session values are ROW and FALSE, respectively. set @@session.binlog_format= statement; set @@session.binlog_direct_non_transactional_updates= TRUE; SELECT @@session.binlog_format; SELECT @@session.binlog_direct_non_transactional_updates; begin; ---echo # Test that the session variable 'binlog_format' is read-only ---echo # inside a transaction with no preceding updates. +--echo # Test that the session variable 'binlog_format' and +--echo # 'binlog_direct_non_transactional_updates' are +--echo # read-only inside a transaction with no preceding updates. +--echo # Current session values are STATEMENT and TRUE, respectively. --error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT set @@session.binlog_format= mixed; --error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT set @@session.binlog_direct_non_transactional_updates= FALSE; insert into t2 values (1); ---echo # Test that the session variable 'binlog_format' is read-only ---echo # inside a transaction with preceding transactional updates. +--echo # Test that the session variable 'binlog_format' and +--echo # 'binlog_direct_non_transactional_updates' are +--echo # read-only inside a transaction with preceding transactional updates. +--echo # Current session values are STATEMENT and TRUE, respectively. --error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT set @@session.binlog_format= row; --error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT @@ -40,25 +48,32 @@ 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. +--echo # Test that the session variable 'binlog_format' and +--echo # 'binlog_direct_non_transactional_updates' are +--echo # read-only inside a transaction with preceding non-transactional updates. +--echo # Current session values are STATEMENT and TRUE, respectively. --error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT - set @@session.binlog_format= statement; + set @@session.binlog_format= mixed; --error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT set @@session.binlog_direct_non_transactional_updates= FALSE; commit; ---echo # Test that the session variable 'binlog_format' is writable ---echo # when AUTOCOMMIT=0, before a transaction has started. +--echo # Test that the session variable 'binlog_format' and +--echo # 'binlog_direct_non_transactional_updates' are +--echo # writable when AUTOCOMMIT=0, before a transaction has started. +--echo # Current session values are STATEMENT and TRUE, respectively. set AUTOCOMMIT=0; set @@session.binlog_format= row; set @@session.binlog_direct_non_transactional_updates= FALSE; SELECT @@session.binlog_format; SELECT @@session.binlog_direct_non_transactional_updates; -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. +insert into t1 values (3); +--echo # Test that the session variable 'binlog_format' and +--echo # 'binlog_direct_non_transactional_updates' are +--echo # read-only inside an AUTOCOMMIT=0 transaction +--echo # with preceding non-transactional updates. +--echo # Current session values are ROW and FALSE, respectively. --error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT set @@session.binlog_format= statement; --error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT @@ -67,11 +82,14 @@ SELECT @@session.binlog_format; SELECT @@session.binlog_direct_non_transactional_updates; 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. +insert into t2 values (4); +--echo # Test that the session variable 'binlog_format' and +--echo # 'binlog_direct_non_transactional_updates' are +--echo # read-only inside an AUTOCOMMIT=0 transaction with +--echo # preceding transactional updates. +--echo # Current session values are ROW and FALSE, respectively. --error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT -set @@session.binlog_format= row; +set @@session.binlog_format= statement; --error ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT set @@session.binlog_direct_non_transactional_updates= TRUE; SELECT @@session.binlog_format; @@ -79,9 +97,11 @@ SELECT @@session.binlog_direct_non_transactional_updates; commit; begin; - insert into t2 values (6); ---echo # Test that the global variable 'binlog_format' is writable ---echo # inside a transaction. + insert into t2 values (5); +--echo # Test that the global variable 'binlog_format' and +--echo # 'binlog_direct_non_transactional_updates' are +--echo # writable inside a transaction. +--echo # Current session values are ROW and FALSE, respectively. SELECT @@global.binlog_format; set @@global.binlog_format= statement; set @@global.binlog_direct_non_transactional_updates= TRUE; @@ -90,7 +110,7 @@ begin; commit; set @@global.binlog_format= @save_binlog_format; -set @@global.binlog_direct_non_transactional_updates= @save_binlog_dirct; +set @@global.binlog_direct_non_transactional_updates= @save_binlog_dirct; create table t3(a int, b int) engine= innodb; create table t4(a int) engine= innodb; @@ -98,7 +118,7 @@ create table t5(a int) engine= innodb; delimiter |; eval create trigger tr1 after insert on t3 for each row begin insert into t4(a) values(1); - set @@session.binlog_format= statement; + set @@session.binlog_format= statement; insert into t4(a) values(2); insert into t5(a) values(3); end | @@ -106,6 +126,7 @@ delimiter ;| --echo # Test that the session variable 'binlog_format' is read-only --echo # in sub-statements. +--echo # Current session value is ROW. --error ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_FORMAT insert into t3(a,b) values(1,1); SELECT @@session.binlog_format; @@ -116,14 +137,16 @@ create table t8(a int) engine= innodb; delimiter |; eval create trigger tr2 after insert on t6 for each row begin insert into t7(a) values(1); - set @@global.binlog_direct_non_transactional_updates= FALSE; + set @@session.binlog_direct_non_transactional_updates= TRUE; insert into t7(a) values(2); insert into t8(a) values(3); end | delimiter ;| ---echo # Test that the session variable 'binlog_format' is read-only ---echo # in sub-statements. +--echo # Test that the session variable +--echo # 'binlog_direct_non_transactional_updates' is +--echo # read-only in sub-statements. +--echo # Current session value is FALSE. --error ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_DIRECT insert into t6(a,b) values(1,1); SELECT @@session.binlog_direct_non_transactional_updates; diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 8f71d27e8d4..d8bdf35fd51 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -240,6 +240,12 @@ static bool check_has_super(sys_var *self, THD *thd, set_var *var) } static bool binlog_format_check(sys_var *self, THD *thd, set_var *var) { + if (check_has_super(self, thd, var)) + return true; + + if (var->type == OPT_GLOBAL) + return false; + /* If RBR and open temporary tables, their CREATE TABLE may not be in the binlog, so we can't toggle to SBR in this connection. @@ -271,18 +277,12 @@ static bool binlog_format_check(sys_var *self, THD *thd, set_var *var) /* Make the session variable 'binlog_format' read-only inside a transaction. */ - if (thd->active_transaction() && (var->type == OPT_SESSION)) + if (thd->active_transaction()) { my_error(ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_FORMAT, MYF(0)); return true; } - if (check_has_super(self, thd, var)) - return true; - if (var->type == OPT_GLOBAL || - (thd->variables.binlog_format == var->save_result.ulonglong_value)) - return false; - return false; } @@ -311,15 +311,12 @@ static Sys_var_enum Sys_binlog_format( static bool binlog_direct_check(sys_var *self, THD *thd, set_var *var) { - /* - Makes the session variable 'binlog_direct_non_transactional_updates' - read-only inside a transaction. - */ - if (thd->active_transaction() && (var->type == OPT_SESSION)) - { - my_error(ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT, MYF(0)); - return 1; - } + if (check_has_super(self, thd, var)) + return true; + + if (var->type == OPT_GLOBAL) + return false; + /* Makes the session variable 'binlog_direct_non_transactional_updates' read-only if within a procedure, trigger or function. @@ -327,15 +324,17 @@ static bool binlog_direct_check(sys_var *self, THD *thd, set_var *var) if (thd->in_sub_stmt) { my_error(ER_STORED_FUNCTION_PREVENTS_SWITCH_BINLOG_DIRECT, MYF(0)); - return 1; + return true; + } + /* + Makes the session variable 'binlog_direct_non_transactional_updates' + read-only inside a transaction. + */ + if (thd->active_transaction()) + { + my_error(ER_INSIDE_TRANSACTION_PREVENTS_SWITCH_BINLOG_DIRECT, MYF(0)); + return true; } - - if (check_has_super(self, thd, var)) - return true; - if (var->type == OPT_GLOBAL || - (thd->variables.binlog_direct_non_trans_update == - static_cast(var->save_result.ulonglong_value))) - return false; return false; }