diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:00:34 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:00:34 +0000 |
commit | 3f619478f796eddbba6e39502fe941b285dd97b1 (patch) | |
tree | e2c7b5777f728320e5b5542b6213fd3591ba51e2 /mysql-test/suite/binlog/t/binlog_unsafe.test | |
parent | Initial commit. (diff) | |
download | mariadb-upstream.tar.xz mariadb-upstream.zip |
Adding upstream version 1:10.11.6.upstream/1%10.11.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mysql-test/suite/binlog/t/binlog_unsafe.test')
-rw-r--r-- | mysql-test/suite/binlog/t/binlog_unsafe.test | 762 |
1 files changed, 762 insertions, 0 deletions
diff --git a/mysql-test/suite/binlog/t/binlog_unsafe.test b/mysql-test/suite/binlog/t/binlog_unsafe.test new file mode 100644 index 00000000..b637a980 --- /dev/null +++ b/mysql-test/suite/binlog/t/binlog_unsafe.test @@ -0,0 +1,762 @@ +# ==== Background ==== +# +# Some statements may execute differently on master and slave when +# logged in statement format. Such statements are called unsafe. +# Unsafe statements include: +# +# - statements using @@variables (with a small number of exceptions; +# see below); +# - statements using certain functions, e.g., UUID(); +# - statements using LIMIT; +# - INSERT DELAYED; +# - insert into two autoinc columns; +# - statements using UDF's. +# - statements reading from log tables in the mysql database. +# - INSERT ... SELECT ... ON DUPLICATE KEY UPDATE +# - REPLACE ... SELECT +# - CREATE TABLE [IGNORE/REPLACE] SELECT +# - INSERT IGNORE...SELECT +# - UPDATE IGNORE +# - INSERT... ON DUPLICATE KEY UPDATE on a table with two UNIQUE KEYS +# +# Note that statements that use stored functions, stored procedures, +# triggers, views, or prepared statements that invoke unsafe +# statements shall also be unsafe. +# +# Unsafeness of a statement shall have the following consequences: +# +# 1. If the binlogging is on and the unsafe statement is logged: +# - If binlog_format=STATEMENT, the statement shall give a warning. +# - If binlog_format=MIXED or binlog_format=ROW, the statement shall +# be logged in row format. +# +# 2. If binlogging is off or the statement is not logged (e.g. SELECT +# UUID()), no warning shall be issued and the statement shall not +# be logged. +# +# Moreover, when a sub-statement of a recursive construct (i.e., +# stored function, stored procedure, trigger, view, or prepared +# statement) is unsafe and binlog_format=STATEMENT, then a warning +# shall be issued for every recursive construct. In effect, this +# creates a stack trace from the top-level statement to the unsafe +# statement. +# +# +# ==== Purpose ==== +# +# This test verifies that a warning is generated when it should, +# according to the rules above. +# +# All @@variables should be unsafe, with some exceptions. Therefore, +# this test also verifies that the exceptions do *not* generate a +# warning. +# +# +# ==== Method ==== +# +# 1. Each type of statements listed above is executed. +# +# 2. Each unsafe statement is wrapped in each type of recursive +# construct (stored function, stored procedure, trigger, view, or +# prepared statement). +# +# 3. Each unsafe statement is wrapped in two levels of recursive +# constructs (function invoking trigger invoking UUID(), etc). +# +# 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 +# BUG#42640: mysqld crashes when unsafe statements are executed (STRICT_TRANS_TABLES mode) +# BUG#45825: INSERT DELAYED is not unsafe: logged in statement format +# BUG#45785: LIMIT in SP does not cause RBL if binlog_format=MIXED +# BUG#47995: Mark user functions as unsafe +# BUG#49222: Mark RAND() unsafe +# BUG#11758262: MARK INSERT...SEL...ON DUP KEY UPD,REPLACE...SEL,CREATE...[IGN|REPL] SEL +# +# ==== Related test cases ==== +# +# rpl.rpl_variables verifies that variables which cannot be replicated +# safely in statement mode are replicated correctly in mixed or row +# mode. +# +# rpl.rpl_variables_stm tests the small subset of variables that +# actually can be replicated safely in statement mode. +# +--source include/have_udf.inc +--source include/have_log_bin.inc +--source include/have_binlog_format_statement.inc + +--disable_query_log +call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT"); +--enable_query_log + +--echo #### Setup tables #### + +CREATE TABLE t0 (a CHAR(200)); +CREATE TABLE t1 (a CHAR(200)); +CREATE TABLE t2 (a CHAR(200)); +CREATE TABLE t3 (a CHAR(200)); +CREATE TABLE ta0 (a CHAR(200)); +CREATE TABLE ta1 (a CHAR(200)); +CREATE TABLE ta2 (a CHAR(200)); +CREATE TABLE ta3 (a CHAR(200)); +CREATE TABLE autoinc_table (a INT PRIMARY KEY AUTO_INCREMENT); +CREATE TABLE data_table (a CHAR(200)); +INSERT INTO data_table VALUES ('foo'); +CREATE TABLE trigger_table_1 (a INT); +CREATE TABLE trigger_table_2 (a INT); +CREATE TABLE trigger_table_3 (a INT); +CREATE TABLE double_autoinc_table (a INT PRIMARY KEY AUTO_INCREMENT); + +--DELIMITER | +CREATE TRIGGER double_autoinc_trig +BEFORE INSERT ON double_autoinc_table FOR EACH ROW +BEGIN + INSERT INTO autoinc_table VALUES (NULL); +END| + +CREATE FUNCTION multi_unsafe_func() RETURNS INT +BEGIN + INSERT INTO t0 VALUES(CONCAT(@@hostname, @@hostname)); + INSERT INTO t0 VALUES(0); + INSERT INTO t0 VALUES(CONCAT(UUID(), @@hostname)); + RETURN 1; +END| +--DELIMITER ; + +--replace_result $UDF_EXAMPLE_SO UDF_EXAMPLE_LIB +--eval CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "$UDF_EXAMPLE_SO" + +# In each iteration of this loop, we select one method to make the +# statement unsafe. +--let $unsafe_type= 0 +while ($unsafe_type < 9) { + + --echo + + if ($unsafe_type == 0) { + --echo ==== Testing UUID() unsafeness ==== + --let $desc_0= unsafe UUID() function + --let $stmt_sidef_0= INSERT INTO t0 VALUES (UUID()) + --let $value_0= UUID() + --let $sel_sidef_0= + --let $sel_retval_0= SELECT UUID() + --let $CRC_ARG_expected_number_of_warnings= 1 + } + + if ($unsafe_type == 1) { + --echo ==== Testing @@hostname unsafeness ==== + --let $desc_0= unsafe @@hostname variable + --let $stmt_sidef_0= INSERT INTO t0 VALUES (@@hostname) + --let $value_0= @@hostname + --let $sel_sidef_0= + # $sel_retval is going to be used in views. Views cannot execute + # statements that refer to @@variables. Hence, we set $set_retval + # to empty instead of SELECT @@hostname. + --let $sel_retval_0= + --let $CRC_ARG_expected_number_of_warnings= 1 + } + + if ($unsafe_type == 2) { + --echo ==== Testing SELECT...LIMIT unsafeness ==== + --let $desc_0= unsafe SELECT...LIMIT statement + --let $stmt_sidef_0= INSERT INTO t0 SELECT * FROM data_table LIMIT 1 + --let $value_0= + --let $sel_sidef_0= + --let $sel_retval_0= SELECT * FROM data_table LIMIT 1 + --let $CRC_ARG_expected_number_of_warnings= 1 + } + + if ($unsafe_type == 3) { + --echo ==== Testing INSERT DELAYED safeness after BUG#54579 is fixed ==== + --let $desc_0= unsafe INSERT DELAYED statement + --let $stmt_sidef_0= INSERT DELAYED INTO t0 VALUES (1), (2) + --let $value_0= + --let $sel_sidef_0= + --let $sel_retval_0= + --let $CRC_ARG_expected_number_of_warnings= 0 + } + + if ($unsafe_type == 4) { + --echo ==== Testing unsafeness of insert of two autoinc values ==== + --let $desc_0= unsafe update of two autoinc columns + --let $stmt_sidef_0= INSERT INTO double_autoinc_table VALUES (NULL) + --let $value_0= + --let $sel_sidef_0= + --let $sel_retval_0= + --let $CRC_ARG_expected_number_of_warnings= 1 + } + + if ($unsafe_type == 5) { + --echo ==== Testing unsafeness of UDF's ==== + --let $desc_0= unsafe UDF + --let $stmt_sidef_0= INSERT INTO t0 VALUES (myfunc_int(10)) + --let $value_0= myfunc_int(10) + --let $sel_sidef_0= SELECT myfunc_int(10) + --let $sel_retval_0= + --let $CRC_ARG_expected_number_of_warnings= 1 + } + + if ($unsafe_type == 6) { + --echo ==== Testing unsafeness of access to mysql.general_log ==== + --let $desc_0= unsafe use of mysql.general_log + --let $stmt_sidef_0= INSERT INTO t0 SELECT COUNT(*) FROM mysql.general_log + --let $value_0= + --let $sel_sidef_0= + --let $sel_retval_0= SELECT COUNT(*) FROM mysql.general_log + --let $CRC_ARG_expected_number_of_warnings= 1 + } + + if ($unsafe_type == 7) { + --echo ==== Testing a statement that is unsafe in many ways ==== + --let $desc_0= statement that is unsafe in many ways + # Concatenate three unsafe values, and then concatenate NULL to + # that so that the result is NULL and we instead use autoinc. + --let $stmt_sidef_0= INSERT DELAYED INTO double_autoinc_table SELECT CONCAT(UUID(), @@hostname, myfunc_int(), NULL) FROM mysql.general_log LIMIT 1 + --let $value_0= + --let $sel_sidef_0= + --let $sel_retval_0= + --let $CRC_ARG_expected_number_of_warnings= 7 + } + + if ($unsafe_type == 8) { + --echo ==== Testing a statement that is unsafe several times ==== + --let $desc_0= statement that is unsafe several times + --let $stmt_sidef_0= INSERT INTO ta0 VALUES (multi_unsafe_func()) + --let $value_0= + --let $sel_sidef_0= SELECT multi_unsafe_func() + --let $sel_retval_0= + --let $CRC_ARG_expected_number_of_warnings= 2 + } + + # In each iteration of the following loop, we select one way to + # enclose the unsafe statement as a sub-statement of a recursive + # construct (i.e., a function, procedure, trigger, view, or prepared + # statement). + # + # In the last iteration, $call_type_1=7, we don't create a recursive + # construct. Instead, we just invoke the unsafe statement directly. + + --let $call_type_1= 0 + while ($call_type_1 < 8) { + #--echo debug: level 1, types $call_type_1 -> $unsafe_type + --let $CRC_ARG_level= 1 + --let $CRC_ARG_type= $call_type_1 + --let $CRC_ARG_stmt_sidef= $stmt_sidef_0 + --let $CRC_ARG_value= $value_0 + --let $CRC_ARG_sel_sidef= $sel_sidef_0 + --let $CRC_ARG_sel_retval= $sel_retval_0 + --let $CRC_ARG_desc= $desc_0 + --source suite/rpl/include/create_recursive_construct.inc + --let $stmt_sidef_1= $CRC_RET_stmt_sidef + --let $value_1= $CRC_RET_value + --let $sel_sidef_1= $CRC_RET_sel_sidef + --let $sel_retval_1= $CRC_RET_sel_retval + --let $is_toplevel_1= $CRC_RET_is_toplevel + --let $drop_1= $CRC_RET_drop + --let $desc_1= $CRC_RET_desc + + # Some statements must be top-level statements, i.e., cannot be + # called as a sub-statement of any recursive construct. (One + # example is 'EXECUTE prepared_stmt'). When + # create_recursive_construct.inc creates a top-level statement, it + # sets $CRC_RET_is_toplevel=1. + + if (!$is_toplevel_1) { + + # In each iteration of this loop, we select one way to enclose + # the previous recursive construct in another recursive + # construct. + + --let $call_type_2= 0 + while ($call_type_2 < 7) { + #--echo debug: level 2, types $call_type_2 -> $call_type_1 -> $unsafe_type + --let $CRC_ARG_level= 2 + --let $CRC_ARG_type= $call_type_2 + --let $CRC_ARG_stmt_sidef= $stmt_sidef_1 + --let $CRC_ARG_value= $value_1 + --let $CRC_ARG_sel_sidef= $sel_sidef_1 + --let $CRC_ARG_sel_retval= $sel_retval_1 + --let $CRC_ARG_desc= $desc_1 + --source suite/rpl/include/create_recursive_construct.inc + --let $stmt_sidef_2= $CRC_RET_stmt_sidef + --let $value_2= $CRC_RET_value + --let $sel_sidef_2= $CRC_RET_sel_sidef + --let $sel_retval_2= $CRC_RET_sel_retval + --let $is_toplevel_2= $CRC_RET_is_toplevel + --let $drop_2= $CRC_RET_drop + --let $desc_2= $CRC_RET_desc + + if (!$is_toplevel_2) { + + # Conditioned out since it makes result file really big. + + if (0) { + + # In each iteration of this loop, we select one way to enclose + # the previous recursive construct in another recursive + # construct. + + --let $call_type_3= 0 + while ($call_type_3 < 7) { + #--echo debug: level 3, types $call_type_2 -> $call_type_2 -> $call_type_1 -> $unsafe_type + --let $CRC_ARG_level= 3 + --let $CRC_ARG_type= $call_type_3 + --let $CRC_ARG_stmt_sidef= $stmt_sidef_2 + --let $CRC_ARG_value= $value_2 + --let $CRC_ARG_sel_sidef= $sel_sidef_2 + --let $CRC_ARG_sel_retval= $sel_retval_2 + --let $CRC_ARG_desc= $desc_2 + --source suite/rpl/include/create_recursive_construct.inc + + # Drop created object. + if ($drop_3) { + --eval $drop_3 + } + --inc $call_type_3 + } # while (call_type_3) + } # if (0) + } # if (!is_toplevel_2) + + # Drop created object. + if ($drop_2) { + --eval $drop_2 + } + --inc $call_type_2 + } # while (call_type_2) + } # if (!is_toplevel_1) + + # Drop created object. + if ($drop_1) { + --eval $drop_1 + } + --inc $call_type_1 + } # while (call_type_1) + + --inc $unsafe_type +} # while (unsafe_type) + +DROP TRIGGER double_autoinc_trig; +DROP TABLE t0, t1, t2, t3, ta0, ta1, ta2, ta3, + autoinc_table, double_autoinc_table, + data_table, + trigger_table_1, trigger_table_2, trigger_table_3; +DROP FUNCTION myfunc_int; +DROP FUNCTION multi_unsafe_func; + + +--echo ==== Special system variables that should *not* be unsafe ==== + +CREATE TABLE t1 (a VARCHAR(1000)); +CREATE TABLE autoinc_table (a INT PRIMARY KEY AUTO_INCREMENT); + +INSERT INTO t1 VALUES (@@session.auto_increment_increment); +INSERT INTO t1 VALUES (@@session.auto_increment_offset); +INSERT INTO t1 VALUES (@@session.character_set_client); +INSERT INTO t1 VALUES (@@session.character_set_connection); +INSERT INTO t1 VALUES (@@session.character_set_database); +INSERT INTO t1 VALUES (@@session.character_set_server); +INSERT INTO t1 VALUES (@@session.collation_connection); +INSERT INTO t1 VALUES (@@session.collation_database); +INSERT INTO t1 VALUES (@@session.collation_server); +INSERT INTO t1 VALUES (@@session.foreign_key_checks); +INSERT INTO t1 VALUES (@@session.identity); +INSERT INTO t1 VALUES (@@session.last_insert_id); +INSERT INTO t1 VALUES (@@session.lc_time_names); +INSERT INTO t1 VALUES (@@session.pseudo_thread_id); +INSERT INTO t1 VALUES (@@session.sql_auto_is_null); +INSERT INTO t1 VALUES (@@session.timestamp); +INSERT INTO t1 VALUES (@@session.time_zone); +INSERT INTO t1 VALUES (@@session.unique_checks); + +SET @my_var= 4711; +INSERT INTO t1 VALUES (@my_var); + +# using insert_id implicitly should be ok. +SET insert_id= 12; +INSERT INTO autoinc_table VALUES (NULL); + +# See set_var.cc for explanation. +--echo The following variables *should* give a warning, despite they are replicated. +INSERT INTO t1 VALUES (@@session.sql_mode); +INSERT INTO t1 VALUES (@@session.insert_id); + + +DROP TABLE t1, autoinc_table; + + +# +# BUG#34768 - nondeterministic INSERT using LIMIT logged in stmt mode if +# binlog_format=mixed +# +CREATE TABLE t1(a INT, b INT, KEY(a), PRIMARY KEY(b)); +INSERT INTO t1 SELECT * FROM t1 LIMIT 1; +REPLACE INTO t1 SELECT * FROM t1 LIMIT 1; +UPDATE t1 SET a=1 LIMIT 1; +DELETE FROM t1 LIMIT 1; +delimiter |; +CREATE PROCEDURE p1() +BEGIN + INSERT INTO t1 SELECT * FROM t1 LIMIT 1; + REPLACE INTO t1 SELECT * FROM t1 LIMIT 1; + UPDATE t1 SET a=1 LIMIT 1; + DELETE FROM t1 LIMIT 1; +END| +delimiter ;| +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + +# +# Bug#42634: % character in query can cause mysqld signal 11 segfault +# + +--disable_warnings +DROP TABLE IF EXISTS t1; +--enable_warnings + +CREATE TABLE t1 (a VARCHAR(200), b VARCHAR(200)); +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 may not be 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 may not be 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 200; +END| +DELIMITER ;| +--echo "One unsafe warning should be issued in the following statement" +--error ER_STORED_FUNCTION_PREVENTS_SWITCH_SQL_LOG_BIN +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 TEXT); +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; + +# +# For BUG#42640: mysqld crashes when unsafe statements are executed (STRICT_TRANS_TABLES mode) +# +SET @save_sql_mode = @@SESSION.SQL_MODE; +SET @@SESSION.SQL_MODE = STRICT_ALL_TABLES; + +CREATE TABLE t1(i INT PRIMARY KEY); +CREATE TABLE t2(i INT PRIMARY KEY); + +INSERT INTO t1 SELECT * FROM t2 LIMIT 1; +INSERT INTO t1 VALUES(@@global.sync_binlog); + +UPDATE t1 SET i = 999 LIMIT 1; +DELETE FROM t1 LIMIT 1; + +DROP TABLE t1, t2; +SET @@SESSION.SQL_MODE = @save_sql_mode; + +# +# BUG#45825: INSERT DELAYED is not unsafe: logged in statement format +# BUG#45785: LIMIT in SP does not cause RBL if binlog_format=MIXED +# +SET @old_binlog_format = @@session.binlog_format; +SET binlog_format = MIXED; + +CREATE TABLE t1 (a INT); +CREATE TABLE t2 (a INT); +INSERT INTO t2 VALUES (1), (2); + +--DELIMITER | +CREATE PROCEDURE proc_insert_delayed () +BEGIN + INSERT DELAYED INTO t1 VALUES (1), (2); +END| + +CREATE FUNCTION func_limit () +RETURNS INT +BEGIN + INSERT INTO t1 SELECT * FROM t2 LIMIT 1; + RETURN 1; +END| +--DELIMITER ; + +RESET MASTER; +CALL proc_insert_delayed(); +--disable_ps2_protocol +SELECT func_limit(); +--enable_ps2_protocol +source include/show_binlog_events.inc; + +SET @@session.binlog_format = @old_binlog_format; +DROP TABLE t1, t2; +DROP PROCEDURE proc_insert_delayed; +DROP FUNCTION func_limit; + +# +# BUG#45827 +# The test verifies if stmt that have more than one +# different tables to update with autoinc columns +# will produce unsafe warning +# + +# Test case1: stmt that have more than one different tables +# to update with autoinc columns should produce +# unsafe warning when calling a function +CREATE TABLE t1 (a INT, b INT PRIMARY KEY AUTO_INCREMENT); +CREATE TABLE t2 (a INT, b INT PRIMARY KEY AUTO_INCREMENT); + +# The purpose of this function is to insert into t1 so that the second +# column is auto_increment'ed. +DELIMITER |; +CREATE FUNCTION func_modify_t1 () +RETURNS INT +BEGIN + INSERT INTO t1 SET a = 1; + RETURN 0; +END| +DELIMITER ;| +--echo # The following statement causes auto-incrementation +--echo # of both t1 and t2. It is logged in statement format, +--echo # so it should produce unsafe warning. +INSERT INTO t2 SET a = func_modify_t1(); + +SET SESSION binlog_format = MIXED; +--echo # Check if the statement is logged in row format. +let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); +INSERT INTO t2 SET a = func_modify_t1(); +--source include/show_binlog_events.inc + +# clean up +DROP TABLE t1,t2; +DROP FUNCTION func_modify_t1; +# +# Test case2: stmt that have more than one different tables +# to update with autoinc columns should produce +# unsafe warning when invoking a trigger +SET SESSION binlog_format = STATEMENT; +CREATE TABLE t1 (a INT); +CREATE TABLE t2 (a INT, b INT PRIMARY KEY AUTO_INCREMENT); +CREATE TABLE t3 (a INT, b INT PRIMARY KEY AUTO_INCREMENT); + +# The purpose of this function is to insert into t1 so that the second +# column is auto_increment'ed. +delimiter |; +create trigger tri_modify_two_tables before insert on t1 for each row begin + insert into t2(a) values(new.a); + insert into t3(a) values(new.a); +end | +delimiter ;| +--echo # The following statement causes auto-incrementation +--echo # of both t2 and t3. It is logged in statement format, +--echo # so it should produce unsafe warning +INSERT INTO t1 SET a = 1; + +SET SESSION binlog_format = MIXED; +--echo # Check if the statement is logged in row format. +let $binlog_start= query_get_value(SHOW MASTER STATUS, Position, 1); +INSERT INTO t1 SET a = 2; +--source include/show_binlog_events.inc + +# clean up +DROP TABLE t1,t2,t3; + +# +# BUG#47995: Mark user functions as unsafe +# BUG#49222: Mark RAND() unsafe +# +# Test that the system functions that are supposed to be marked unsafe +# generate a warning. Each INSERT statement below should generate a +# warning. +# +SET SESSION binlog_format = STATEMENT; + +CREATE TABLE t1 (a VARCHAR(1000)); +INSERT INTO t1 VALUES (CURRENT_USER()); #marked unsafe before BUG#47995 +INSERT INTO t1 VALUES (FOUND_ROWS()); #marked unsafe before BUG#47995 +INSERT INTO t1 VALUES (GET_LOCK('tmp', 1)); #marked unsafe in BUG#47995 +INSERT INTO t1 VALUES (IS_FREE_LOCK('tmp')); #marked unsafe in BUG#47995 +INSERT INTO t1 VALUES (IS_USED_LOCK('tmp')); #marked unsafe in BUG#47995 +INSERT INTO t1 VALUES (LOAD_FILE('../../std_data/words2.dat')); #marked unsafe in BUG#39701 +INSERT INTO t1 VALUES (MASTER_POS_WAIT('dummy arg', 4711, 1)); +INSERT INTO t1 VALUES (RELEASE_LOCK('tmp')); #marked unsafe in BUG#47995 +INSERT INTO t1 VALUES (ROW_COUNT()); #marked unsafe before BUG#47995 +INSERT INTO t1 VALUES (SESSION_USER()); #marked unsafe before BUG#47995 +INSERT INTO t1 VALUES (SLEEP(1)); #marked unsafe in BUG#47995 +INSERT INTO t1 VALUES (SYSDATE()); #marked unsafe in BUG#47995 +INSERT INTO t1 VALUES (SYSTEM_USER()); #marked unsafe before BUG#47995 +INSERT INTO t1 VALUES (USER()); #marked unsafe before BUG#47995 +INSERT INTO t1 VALUES (UUID()); #marked unsafe before BUG#47995 +INSERT INTO t1 VALUES (UUID_SHORT()); #marked unsafe before BUG#47995 +INSERT INTO t1 VALUES (VERSION()); #marked unsafe in BUG#47995 +INSERT INTO t1 VALUES (RAND()); #marked unsafe in BUG#49222 +INSERT INTO t1 VALUES (RANDOM_BYTES(1000)); #marked unsafe in MDEV-25704 +DELETE FROM t1; + +# Since we replicate the TIMESTAMP variable, functions affected by the +# TIMESTAMP variable are safe to replicate. So we check that the +# following following functions that depend on the TIMESTAMP variable +# are not unsafe and don't generate a warning. + +SET TIME_ZONE= '+03:00'; +SET TIMESTAMP=1000000; +INSERT INTO t1 VALUES + (CURDATE()), + (CURRENT_DATE()), + (CURRENT_TIME()), + (CURRENT_TIMESTAMP()), + (CURTIME()), + (LOCALTIME()), + (LOCALTIMESTAMP()), + (NOW()), + (UNIX_TIMESTAMP()), + (UTC_DATE()), + (UTC_TIME()), + (UTC_TIMESTAMP()); +SELECT * FROM t1; + +DROP TABLE t1; +# +#BUG#11758262-50439: MARK INSERT...SEL...ON DUP KEY UPD,REPLACE.. +#The following statement may be unsafe when logged in statement format. +#INSERT IGNORE...SELECT +#INSERT ... SELECT ... ON DUPLICATE KEY UPDATE +#REPLACE ... SELECT +#UPDATE IGNORE +#CREATE TABLE... IGNORE SELECT +#CREATE TABLE... REPLACE SELECT +# +###BUG 11765650 - 58637: MARK UPDATES THAT DEPEND ON ORDER OF TWO KEYS UNSAFE +#INSERT.... ON DUP KEY UPDATE on a table with more than one UNIQUE KEY + +#setup tables +CREATE TABLE filler_table (a INT, b INT); +INSERT INTO filler_table values (1,1),(1,2); +CREATE TABLE insert_table (a INT, b INT, PRIMARY KEY(a)); +CREATE TABLE replace_table (a INT, b INT, PRIMARY KEY(a)); +INSERT INTO replace_table values (1,1),(2,2); +CREATE TABLE update_table (a INT, b INT, PRIMARY KEY(a)); +INSERT INTO update_table values (1,1),(2,2); +CREATE TABLE insert_2_keys (a INT UNIQUE KEY, b INT UNIQUE KEY); +INSERT INTO insert_2_keys values (1, 1); + +#INSERT IGNORE... SELECT +INSERT IGNORE INTO insert_table SELECT * FROM filler_table; +TRUNCATE TABLE insert_table; +#INSERT ... SELECT ... ON DUPLICATE KEY UPDATE +INSERT INTO insert_table SELECT * FROM filler_table ON DUPLICATE KEY UPDATE a = 1; +TRUNCATE TABLE insert_table; +#REPLACE...SELECT +REPLACE INTO replace_table SELECT * FROM filler_table; +#UPDATE IGNORE +UPDATE IGNORE update_table SET a=2; +#CREATE TABLE [IGNORE/REPLACE] SELECT +CREATE TABLE create_ignore_test (a INT, b INT, PRIMARY KEY(b)) IGNORE SELECT * FROM filler_table; +CREATE TABLE create_replace_test (a INT, b INT, PRIMARY KEY(b)) REPLACE SELECT * FROM filler_table; +#temporary tables should not throw the warning. +CREATE TEMPORARY TABLE temp1 (a INT, b INT, PRIMARY KEY(b)) REPLACE SELECT * FROM filler_table; + +#INSERT.... ON DUP KEY UPDATE on a table with more than one UNIQUE KEY +INSERT INTO insert_2_keys VALUES (1, 2) + ON DUPLICATE KEY UPDATE a=VALUES(a)+10, b=VALUES(b)+10; + +###clean up +DROP TABLE filler_table; +DROP TABLE insert_table; +DROP TABLE update_table; +DROP TABLE replace_table; +DROP TABLE create_ignore_test; +DROP TABLE create_replace_test; +DROP TABLE insert_2_keys; + +--echo "End of tests" |