# ==== 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.
#
# rpl_ndb.rpl_ndb_binlog_format_errors tests all errors and warnings
# related to logging format (not just 'Unsafe statement written to the
# binary log using statement format since BINLOG_FORMAT = STATEMENT').

--source include/have_udf.inc
--source include/have_log_bin.inc
--source include/have_binlog_format_statement.inc
--source include/not_gtid_enabled.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(100));
CREATE TABLE t1 (a CHAR(100));
CREATE TABLE t2 (a CHAR(100));
CREATE TABLE t3 (a CHAR(100));
CREATE TABLE ta0 (a CHAR(100));
CREATE TABLE ta1 (a CHAR(100));
CREATE TABLE ta2 (a CHAR(100));
CREATE TABLE ta3 (a CHAR(100));
CREATE TABLE autoinc_table (a INT PRIMARY KEY AUTO_INCREMENT);
CREATE TABLE data_table (a CHAR(100));
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_LIB UDF_EXAMPLE_LIB
--eval CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "$UDF_EXAMPLE_LIB"

# 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
    --let $CRC_ARG_expected_number_of_deprecation_warnings= 0
  }

  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
    --let $CRC_ARG_expected_number_of_deprecation_warnings= 0
  }

  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
    --let $CRC_ARG_expected_number_of_deprecation_warnings= 0
  }

  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
    --let $CRC_ARG_expected_number_of_deprecation_warnings= 1
  }

  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
    --let $CRC_ARG_expected_number_of_deprecation_warnings= 0
  }

  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
    --let $CRC_ARG_expected_number_of_deprecation_warnings= 0
  }

  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
    --let $CRC_ARG_expected_number_of_deprecation_warnings= 0
  }

  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
    --let $CRC_ARG_expected_number_of_deprecation_warnings= 1
  }

  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
    --let $CRC_ARG_expected_number_of_deprecation_warnings= 0
  }

  # 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 extra/rpl_tests/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 extra/rpl_tests/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 extra/rpl_tests/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(100), b VARCHAR(100));
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 100;
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();
SELECT func_limit();
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: Mare 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(16));     #marked unsafe in BUG#18221667
INSERT INTO t1 VALUES (AES_ENCRYPT('a', 'a')); #marked unsafe in BUG#18221667
INSERT INTO t1 VALUES (AES_DECRYPT('a', 'a')); #marked unsafe in BUG#18221667
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 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) ENGINE = InnoDB;
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"
