From 4f0f021c3387d621a3334c97ff3a0ee394ced040 Mon Sep 17 00:00:00 2001 From: Guangbao Ni Date: Thu, 5 Mar 2009 18:39:02 +0000 Subject: [PATCH] BUG#41980 SBL, INSERT .. SELECT .. LIMIT = ERROR, even when @@SQL_LOG_BIN is 0 ! When binlog_format is STATEMENT and the statement is unsafe before, the unsafe warning/error message was issued without checking whether the SQL_LOG_BIN was turned on or not. Fixed with adding a sql_log_bin_toplevel flag in THD to check whether SQL_LOG_BIN is ON in current session whatever the current is in sp or not. mysql-test/suite/binlog/r/binlog_unsafe.result: Test case result for unsafe warning/error message mysql-test/suite/binlog/t/binlog_unsafe.test: Test case for unsafe message warning/error sql/set_var.cc: Adding a function set_option_log_bin_bit() which specailly handles to the change of SQL_LOG_BIN bit in order to set sql_log_bin_toplevel according to SQL_LOG_BIN current value at the same time. sql/sql_class.cc: Initialize the flag sql_log_bin_toplevel in THD::init(), and add the condition to check whether unsafe ror message was issued. sql/sql_class.h: Add a sql_log_bin_toplevel flag in THD to indicate whether the toplevel SQL_LOG_BIN is --- .../suite/binlog/r/binlog_unsafe.result | 83 ++++++++++++++ mysql-test/suite/binlog/t/binlog_unsafe.test | 103 +++++++++++++++++- sql/set_var.cc | 13 ++- sql/sql_class.cc | 4 +- sql/sql_class.h | 2 + 5 files changed, 201 insertions(+), 4 deletions(-) diff --git a/mysql-test/suite/binlog/r/binlog_unsafe.result b/mysql-test/suite/binlog/r/binlog_unsafe.result index 7c0980ba77c..a0809c79fa2 100644 --- a/mysql-test/suite/binlog/r/binlog_unsafe.result +++ b/mysql-test/suite/binlog/r/binlog_unsafe.result @@ -227,3 +227,86 @@ UPDATE t1 SET b = '%s%s%s%s%s%s%s%s%s%s%s%s%s%s' WHERE a = 'a' LIMIT 1; Warnings: Warning 1592 Statement is not safe to log in statement format. DROP TABLE t1; +DROP TABLE IF EXISTS t1, t2; +CREATE TABLE t1(i INT PRIMARY KEY); +CREATE TABLE t2(i INT PRIMARY KEY); +CREATE TABLE t3(i INT, ch CHAR(50)); +"Should issue message Statement is not safe to log in statement format." +INSERT INTO t1 SELECT * FROM t2 LIMIT 1; +Warnings: +Warning 1592 Statement is not safe to log in statement format. +CREATE FUNCTION func6() +RETURNS INT +BEGIN +INSERT INTO t1 VALUES (10); +INSERT INTO t1 VALUES (11); +INSERT INTO t1 VALUES (12); +RETURN 0; +END| +"Should issue message Statement is not safe to log in statement format only once" +INSERT INTO t3 VALUES(func6(), UUID()); +Warnings: +Warning 1592 Statement is not safe to log in statement format. +"Check whether SET @@SQL_LOG_BIN = 0/1 doesn't work in substatements" +CREATE FUNCTION fun_check_log_bin() RETURNS INT +BEGIN +SET @@SQL_LOG_BIN = 0; +INSERT INTO t1 VALUES(@@global.sync_binlog); +RETURN 100; +END| +"One unsafe warning should be issued in the following statement" +SELECT fun_check_log_bin(); +fun_check_log_bin() +100 +Warnings: +Warning 1592 Statement is not safe to log in statement format. +"SQL_LOG_BIN should be ON still" +SHOW VARIABLES LIKE "SQL_LOG_BIN"; +Variable_name Value +sql_log_bin ON +set @save_log_bin = @@SESSION.SQL_LOG_BIN; +set @@SESSION.SQL_LOG_BIN = 0; +"Should NOT have any warning message issued in the following statements" +INSERT INTO t1 SELECT * FROM t2 LIMIT 1; +DROP TABLE t1,t2; +"Should NOT have any warning message issued in the following func7() and trig" +CREATE TABLE t1 (a INT); +CREATE TABLE t2 (a CHAR(40)); +CREATE TABLE trigger_table (a CHAR(7)); +CREATE FUNCTION func7() +RETURNS INT +BEGIN +INSERT INTO t1 VALUES (@@global.sync_binlog); +INSERT INTO t1 VALUES (@@session.insert_id); +INSERT INTO t2 SELECT UUID(); +INSERT INTO t2 VALUES (@@session.sql_mode); +INSERT INTO t2 VALUES (@@global.init_slave); +RETURN 0; +END| +SHOW VARIABLES LIKE "SQL_LOG_BIN"; +Variable_name Value +sql_log_bin OFF +SELECT func7(); +func7() +0 +---- Insert from trigger ---- +CREATE TRIGGER trig +BEFORE INSERT ON trigger_table +FOR EACH ROW +BEGIN +INSERT INTO t1 VALUES (@@global.sync_binlog); +INSERT INTO t1 VALUES (@@session.insert_id); +INSERT INTO t1 VALUES (@@global.auto_increment_increment); +INSERT INTO t2 SELECT UUID(); +INSERT INTO t2 VALUES (@@session.sql_mode); +INSERT INTO t2 VALUES (@@global.init_slave); +INSERT INTO t2 VALUES (@@hostname); +END| +INSERT INTO trigger_table VALUES ('bye.'); +DROP FUNCTION fun_check_log_bin; +DROP FUNCTION func6; +DROP FUNCTION func7; +DROP TRIGGER trig; +DROP TABLE t1, t2, t3, trigger_table; +set @@SESSION.SQL_LOG_BIN = @save_log_bin; +"End of tests" diff --git a/mysql-test/suite/binlog/t/binlog_unsafe.test b/mysql-test/suite/binlog/t/binlog_unsafe.test index f58233d4fef..642dc3a46f7 100644 --- a/mysql-test/suite/binlog/t/binlog_unsafe.test +++ b/mysql-test/suite/binlog/t/binlog_unsafe.test @@ -8,6 +8,7 @@ # executed cannot be determined (e.g., INSERT DELAYED). Such # statements should be marked unsafe. All unsafe statements should # give a warning. +# Yet the warning/error message isn't issued when SQL_LOG_BIN is turned off. # # This test verifies that a warning is generated for statements that # should be unsafe, when they are executed under statement mode @@ -32,14 +33,19 @@ # We try to insert the variables that should not be unsafe into a # table, and verify that *no* warning is issued. # -# +# Execute a unsafe statement calling a trigger or stored function +# or neither when SQL_LOG_BIN is turned ON, a warning/error should be issued +# Execute a unsafe statement calling a trigger or stored function +# or neither when @@SQL_LOG_BIN is turned OFF, +# no warning/error is issued + # ==== Related bugs and worklogs ==== # # WL#3339: Issue warnings when statement-based replication may fail # BUG#31168: @@hostname does not replicate # BUG#34732: mysqlbinlog does not print default values for auto_increment variables # BUG#34768: nondeterministic INSERT using LIMIT logged in stmt mode if binlog_format=mixed -# +# BUG#41980, SBL, INSERT .. SELECT .. LIMIT = ERROR, even when @@SQL_LOG_BIN is 0 # # ==== Related test cases ==== # @@ -271,3 +277,96 @@ INSERT INTO t1 VALUES ('a','b'); UPDATE t1 SET b = '%s%s%s%s%s%s%s%s%s%s%s%s%s%s' WHERE a = 'a' LIMIT 1; DROP TABLE t1; +# +#For bug#41980, SBL, INSERT .. SELECT .. LIMIT = ERROR, even when @@SQL_LOG_BIN is 0 +# + +--disable_warnings +DROP TABLE IF EXISTS t1, t2; +--enable_warnings +CREATE TABLE t1(i INT PRIMARY KEY); +CREATE TABLE t2(i INT PRIMARY KEY); +CREATE TABLE t3(i INT, ch CHAR(50)); + +--echo "Should issue message Statement is not safe to log in statement format." +INSERT INTO t1 SELECT * FROM t2 LIMIT 1; + +DELIMITER |; +CREATE FUNCTION func6() +RETURNS INT +BEGIN + INSERT INTO t1 VALUES (10); + INSERT INTO t1 VALUES (11); + INSERT INTO t1 VALUES (12); + RETURN 0; +END| +DELIMITER ;| +--echo "Should issue message Statement is not safe to log in statement format only once" +INSERT INTO t3 VALUES(func6(), UUID()); + +--echo "Check whether SET @@SQL_LOG_BIN = 0/1 doesn't work in substatements" +DELIMITER |; +CREATE FUNCTION fun_check_log_bin() RETURNS INT +BEGIN + SET @@SQL_LOG_BIN = 0; + INSERT INTO t1 VALUES(@@global.sync_binlog); + RETURN 100; +END| +DELIMITER ;| +--echo "One unsafe warning should be issued in the following statement" +SELECT fun_check_log_bin(); +--echo "SQL_LOG_BIN should be ON still" +SHOW VARIABLES LIKE "SQL_LOG_BIN"; + +set @save_log_bin = @@SESSION.SQL_LOG_BIN; +set @@SESSION.SQL_LOG_BIN = 0; +--echo "Should NOT have any warning message issued in the following statements" +INSERT INTO t1 SELECT * FROM t2 LIMIT 1; +DROP TABLE t1,t2; + +--echo "Should NOT have any warning message issued in the following func7() and trig" +CREATE TABLE t1 (a INT); +CREATE TABLE t2 (a CHAR(40)); +CREATE TABLE trigger_table (a CHAR(7)); +DELIMITER |; +CREATE FUNCTION func7() +RETURNS INT +BEGIN + INSERT INTO t1 VALUES (@@global.sync_binlog); + INSERT INTO t1 VALUES (@@session.insert_id); + INSERT INTO t2 SELECT UUID(); + INSERT INTO t2 VALUES (@@session.sql_mode); + INSERT INTO t2 VALUES (@@global.init_slave); + RETURN 0; +END| +DELIMITER ;| +SHOW VARIABLES LIKE "SQL_LOG_BIN"; +SELECT func7(); + +--echo ---- Insert from trigger ---- + +DELIMITER |; +CREATE TRIGGER trig +BEFORE INSERT ON trigger_table +FOR EACH ROW +BEGIN + INSERT INTO t1 VALUES (@@global.sync_binlog); + INSERT INTO t1 VALUES (@@session.insert_id); + INSERT INTO t1 VALUES (@@global.auto_increment_increment); + INSERT INTO t2 SELECT UUID(); + INSERT INTO t2 VALUES (@@session.sql_mode); + INSERT INTO t2 VALUES (@@global.init_slave); + INSERT INTO t2 VALUES (@@hostname); +END| +DELIMITER ;| + +INSERT INTO trigger_table VALUES ('bye.'); + +#clean up +DROP FUNCTION fun_check_log_bin; +DROP FUNCTION func6; +DROP FUNCTION func7; +DROP TRIGGER trig; +DROP TABLE t1, t2, t3, trigger_table; +set @@SESSION.SQL_LOG_BIN = @save_log_bin; +--echo "End of tests" diff --git a/sql/set_var.cc b/sql/set_var.cc index 7e2efd2d580..fbbb6a3f529 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -110,6 +110,7 @@ static void sys_default_init_connect(THD*, enum_var_type type); static bool sys_update_init_slave(THD*, set_var*); static void sys_default_init_slave(THD*, enum_var_type type); static bool set_option_bit(THD *thd, set_var *var); +static bool set_option_log_bin_bit(THD *thd, set_var *var); static bool set_option_autocommit(THD *thd, set_var *var); static int check_log_update(THD *thd, set_var *var); static bool set_log_update(THD *thd, set_var *var); @@ -731,7 +732,7 @@ static sys_var_thd_bit sys_log_update(&vars, "sql_log_update", OPTION_BIN_LOG); static sys_var_thd_bit sys_log_binlog(&vars, "sql_log_bin", check_log_update, - set_option_bit, + set_option_log_bin_bit, OPTION_BIN_LOG); static sys_var_thd_bit sys_sql_warnings(&vars, "sql_warnings", 0, set_option_bit, @@ -2963,6 +2964,16 @@ static bool set_option_bit(THD *thd, set_var *var) return 0; } +/* + Functions to be only used to update thd->options OPTION_BIN_LOG bit +*/ +static bool set_option_log_bin_bit(THD *thd, set_var *var) +{ + set_option_bit(thd, var); + if (!thd->in_sub_stmt) + thd->sql_log_bin_toplevel= thd->options & OPTION_BIN_LOG; + return 0; +} static bool set_option_autocommit(THD *thd, set_var *var) { diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 597478933d6..cd00d0e20f1 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -538,6 +538,7 @@ THD::THD() Open_tables_state(refresh_version), rli_fake(0), lock_id(&main_lock_id), user_time(0), in_sub_stmt(0), + sql_log_bin_toplevel(false), binlog_table_maps(0), binlog_flags(0UL), table_map_for_update(0), arg_of_last_insert_id_function(FALSE), @@ -787,6 +788,7 @@ void THD::init(void) update_charset(); reset_current_stmt_binlog_row_based(); bzero((char *) &status_var, sizeof(status_var)); + sql_log_bin_toplevel= options & OPTION_BIN_LOG; } @@ -3662,7 +3664,7 @@ int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg, If we are in statement mode and trying to log an unsafe statement, we should print a warning. */ - if (lex->is_stmt_unsafe() && + if (sql_log_bin_toplevel && lex->is_stmt_unsafe() && variables.binlog_format == BINLOG_FORMAT_STMT) { push_warning(this, MYSQL_ERROR::WARN_LEVEL_WARN, diff --git a/sql/sql_class.h b/sql/sql_class.h index 3439e5b4f74..0a33d2767f5 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1350,6 +1350,8 @@ public: /* <> 0 if we are inside of trigger or stored function. */ uint in_sub_stmt; + /* TRUE when the current top has SQL_LOG_BIN ON */ + bool sql_log_bin_toplevel; /* container for handler's private per-connection data */ Ha_data ha_data[MAX_HA];