summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <cbell/Chuck@mysql_cab_desk.>2007-02-26 14:33:58 -0500
committerunknown <cbell/Chuck@mysql_cab_desk.>2007-02-26 14:33:58 -0500
commitf3377aa8947836e355c780ae767ab3573e976afe (patch)
treefa283e662a33cb4654cb034379a7dc978b28b04a
parentc140b92cfb1de9d0e9374bef96dc2d24ffb6ae8a (diff)
parent9f957f1429df804b35eafe468e20b219e8070d91 (diff)
downloadmariadb-git-f3377aa8947836e355c780ae767ab3573e976afe.tar.gz
Merge cbell@bk-internal.mysql.com:/home/bk/mysql-5.1-new-rpl
into mysql_cab_desk.:C:/source/C++/mysql-5.1-new-rpl sql/item_func.cc: Auto merged sql/log.cc: Auto merged sql/log.h: Auto merged sql/sp_head.cc: Auto merged sql/sql_class.cc: Auto merged
-rw-r--r--mysql-test/r/rpl_user_variables.result174
-rw-r--r--mysql-test/t/rpl_user_variables.test297
-rw-r--r--sql/item_func.cc9
-rw-r--r--sql/log.cc4
-rw-r--r--sql/log.h2
-rw-r--r--sql/sp_head.cc18
-rw-r--r--sql/sql_class.cc9
7 files changed, 508 insertions, 5 deletions
diff --git a/mysql-test/r/rpl_user_variables.result b/mysql-test/r/rpl_user_variables.result
index ed0d2782394..3b6002a5d2d 100644
--- a/mysql-test/r/rpl_user_variables.result
+++ b/mysql-test/r/rpl_user_variables.result
@@ -80,4 +80,178 @@ abc\def
This is a test
insert into t1 select * FROM (select @var1 union select @var2) AS t2;
drop table t1;
+End of 4.1 tests.
+DROP TABLE IF EXISTS t20;
+DROP TABLE IF EXISTS t21;
+DROP PROCEDURE IF EXISTS test.insert;
+CREATE TABLE t20 (a VARCHAR(20));
+CREATE TABLE t21 (a VARCHAR(20));
+CREATE PROCEDURE test.insert()
+BEGIN
+IF (@VAR)
+THEN
+INSERT INTO test.t20 VALUES ('SP_TRUE');
+ELSE
+INSERT INTO test.t20 VALUES ('SP_FALSE');
+END IF;
+END|
+CREATE TRIGGER test.insert_bi BEFORE INSERT
+ON test.t20 FOR EACH ROW
+BEGIN
+IF (@VAR)
+THEN
+INSERT INTO test.t21 VALUES ('TRIG_TRUE');
+ELSE
+INSERT INTO test.t21 VALUES ('TRIG_FALSE');
+END IF;
+END|
+SET @VAR=0;
+CALL test.insert();
+SET @VAR=1;
+CALL test.insert();
+On master: Check the tables for correct data
+SELECT * FROM t20;
+a
+SP_FALSE
+SP_TRUE
+SELECT * FROM t21;
+a
+TRIG_FALSE
+TRIG_TRUE
+On slave: Check the tables for correct data and it matches master
+SELECT * FROM t20;
+a
+SP_FALSE
+SP_TRUE
+SELECT * FROM t21;
+a
+TRIG_FALSE
+TRIG_TRUE
+DROP TABLE t20;
+DROP TABLE t21;
+DROP PROCEDURE test.insert;
+DROP TABLE IF EXISTS t1;
+DROP FUNCTION IF EXISTS test.square;
+CREATE TABLE t1 (i INT);
+CREATE FUNCTION test.square() RETURNS INTEGER DETERMINISTIC RETURN
+(@var * @var);
+SET @var = 1;
+INSERT INTO t1 VALUES (square());
+SET @var = 2;
+INSERT INTO t1 VALUES (square());
+SET @var = 3;
+INSERT INTO t1 VALUES (square());
+SET @var = 4;
+INSERT INTO t1 VALUES (square());
+SET @var = 5;
+INSERT INTO t1 VALUES (square());
+On master: Retrieve the values from the table
+SELECT * FROM t1;
+i
+1
+4
+9
+16
+25
+On slave: Retrieve the values from the table and verify they are the same as on master
+SELECT * FROM t1;
+i
+1
+4
+9
+16
+25
+DROP TABLE t1;
+DROP FUNCTION test.square;
+DROP TABLE IF EXISTS t1;
+DROP FUNCTION IF EXISTS f1;
+DROP FUNCTION IF EXISTS f2;
+CREATE TABLE t1(a int);
+CREATE FUNCTION f1() returns int deterministic BEGIN
+return @a;
+END |
+CREATE FUNCTION f2() returns int deterministic BEGIN
+IF (@b > 0) then
+SET @c = (@a + @b);
+else
+SET @c = (@a - 1);
+END if;
+return @c;
+END |
+SET @a=500;
+INSERT INTO t1 values(f1());
+SET @b = 125;
+SET @c = 1;
+INSERT INTO t1 values(f2());
+On master: Retrieve the values from the table
+SELECT * from t1;
+a
+500
+625
+On slave: Check the tables for correct data and it matches master
+SELECT * from t1;
+a
+500
+625
+DROP TABLE t1;
+DROP FUNCTION f1;
+DROP FUNCTION f2;
+DROP TABLE IF EXISTS t1;
+DROP TABLE IF EXISTS t2;
+CREATE TABLE t1 (i int);
+CREATE TABLE t2 (k int);
+CREATE trigger t1_bi before INSERT on t1 for each row BEGIN
+INSERT INTO t2 values (@a);
+SET @a:=42;
+INSERT INTO t2 values (@a);
+END |
+SET @a:=100;
+INSERT INTO t1 values (5);
+On master: Check to see that data was inserted correctly in both tables
+SELECT * from t1;
+i
+5
+SELECT * from t2;
+k
+100
+42
+On slave: Check the tables for correct data and it matches master
+SELECT * from t1;
+i
+5
+SELECT * from t2;
+k
+100
+42
+End of 5.0 tests.
+DROP TABLE t1;
+DROP TABLE t2;
+DROP TABLE IF EXISTS t1;
+DROP FUNCTION IF EXISTS f1;
+DROP FUNCTION IF EXISTS f2;
+CREATE TABLE t1 (i INT);
+CREATE FUNCTION f1() RETURNS INT RETURN @a;
+CREATE
+FUNCTION f2() RETURNS INT BEGIN
+INSERT INTO t1 VALUES (10 + @a);
+RETURN 0;
+END|
+SET @a:=123;
+SELECT f1(), f2();
+f1() f2()
+123 0
+On master: Check to see that data was inserted correctly
+INSERT INTO t1 VALUES(f1());
+SELECT * FROM t1;
+i
+133
+123
+On slave: Check the table for correct data and it matches master
+SELECT * FROM t1;
+i
+133
+123
+DROP FUNCTION f1;
+DROP FUNCTION f2;
+DROP TABLE t1;
stop slave;
diff --git a/mysql-test/t/rpl_user_variables.test b/mysql-test/t/rpl_user_variables.test
index 08717fce114..226591bd13f 100644
--- a/mysql-test/t/rpl_user_variables.test
+++ b/mysql-test/t/rpl_user_variables.test
@@ -53,5 +53,302 @@ SELECT * FROM t1 ORDER BY n;
connection master;
insert into t1 select * FROM (select @var1 union select @var2) AS t2;
drop table t1;
+--echo End of 4.1 tests.
+
+# BUG#20141
+# The following tests ensure that if user-defined variables are used in SF/Triggers
+# that they are replicated correctly. These tests should be run in both SBR and RBR
+# modes.
+
+# This test uses a procedure that inserts data values based on the value of a
+# user-defined variable. It also has a trigger that inserts data based on the
+# same variable. Successful test runs show that the @var is replicated
+# properly and that the procedure and trigger insert the correct data on the
+# slave.
+#
+# The test of stored procedure was included for completeness. Replication of stored
+# procedures was not directly affected by BUG#20141.
+#
+# This test was constructed for BUG#20141
+
+--disable_warnings
+DROP TABLE IF EXISTS t20;
+DROP TABLE IF EXISTS t21;
+DROP PROCEDURE IF EXISTS test.insert;
+--enable_warnings
+
+CREATE TABLE t20 (a VARCHAR(20));
+CREATE TABLE t21 (a VARCHAR(20));
+DELIMITER |;
+
+# Create a procedure that uses the @var for flow control
+
+CREATE PROCEDURE test.insert()
+BEGIN
+ IF (@VAR)
+ THEN
+ INSERT INTO test.t20 VALUES ('SP_TRUE');
+ ELSE
+ INSERT INTO test.t20 VALUES ('SP_FALSE');
+ END IF;
+END|
+
+# Create a trigger that uses the @var for flow control
+
+CREATE TRIGGER test.insert_bi BEFORE INSERT
+ ON test.t20 FOR EACH ROW
+ BEGIN
+ IF (@VAR)
+ THEN
+ INSERT INTO test.t21 VALUES ('TRIG_TRUE');
+ ELSE
+ INSERT INTO test.t21 VALUES ('TRIG_FALSE');
+ END IF;
+ END|
+DELIMITER ;|
+
+sync_slave_with_master;
+connection master;
+
+# Set @var and call the procedure, repeat with different values
+
+SET @VAR=0;
+CALL test.insert();
+SET @VAR=1;
+CALL test.insert();
+
+--echo On master: Check the tables for correct data
+
+SELECT * FROM t20;
+SELECT * FROM t21;
+
+sync_slave_with_master;
+
+--echo On slave: Check the tables for correct data and it matches master
+
+SELECT * FROM t20;
+SELECT * FROM t21;
+connection master;
+
+# Cleanup
+
+DROP TABLE t20;
+DROP TABLE t21;
+DROP PROCEDURE test.insert;
+
+# This test uses a stored function that uses user-defined variables to return data
+# This test was constructed for BUG#20141
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+DROP FUNCTION IF EXISTS test.square;
+--enable_warnings
+
+CREATE TABLE t1 (i INT);
+
+# Create function that returns a value from @var. In this case, the square function
+
+CREATE FUNCTION test.square() RETURNS INTEGER DETERMINISTIC RETURN
+(@var * @var);
+
+# Set the @var to different values and insert them into a table
+
+SET @var = 1;
+INSERT INTO t1 VALUES (square());
+SET @var = 2;
+INSERT INTO t1 VALUES (square());
+SET @var = 3;
+INSERT INTO t1 VALUES (square());
+SET @var = 4;
+INSERT INTO t1 VALUES (square());
+SET @var = 5;
+INSERT INTO t1 VALUES (square());
+
+--echo On master: Retrieve the values from the table
+
+SELECT * FROM t1;
+
+sync_slave_with_master;
+
+--echo On slave: Retrieve the values from the table and verify they are the same as on master
+
+SELECT * FROM t1;
+
+connection master;
+
+# Cleanup
+
+DROP TABLE t1;
+DROP FUNCTION test.square;
+
+# This test uses stored functions that uses user-defined variables to return data
+# based on the use of @vars inside a function body.
+# This test was constructed for BUG#14914
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+DROP FUNCTION IF EXISTS f1;
+DROP FUNCTION IF EXISTS f2;
+--enable_warnings
+
+CREATE TABLE t1(a int);
+DELIMITER |;
+
+# Create a function that simply returns the value of an @var.
+# Create a function that uses an @var for flow control, creates and uses another
+# @var and sets its value to a value based on another @var.
+
+CREATE FUNCTION f1() returns int deterministic BEGIN
+ return @a;
+END |
+
+CREATE FUNCTION f2() returns int deterministic BEGIN
+ IF (@b > 0) then
+ SET @c = (@a + @b);
+ else
+ SET @c = (@a - 1);
+ END if;
+ return @c;
+END |
+DELIMITER ;|
+
+sync_slave_with_master;
+connection master;
+
+# Set an @var to a value and insert data into a table using the first function.
+# Set two more @vars to some values and insert data into a table using the second function.
+
+SET @a=500;
+INSERT INTO t1 values(f1());
+SET @b = 125;
+SET @c = 1;
+INSERT INTO t1 values(f2());
+
+--echo On master: Retrieve the values from the table
+
+sync_slave_with_master;
+connection master;
+
+SELECT * from t1;
+
+connection slave;
+
+--echo On slave: Check the tables for correct data and it matches master
+
+SELECT * from t1;
+
+connection master;
+
+# Cleanup
+
+DROP TABLE t1;
+DROP FUNCTION f1;
+DROP FUNCTION f2;
+
+# This test uses a function that changes a user-defined variable in its body. This test
+# will ensure the @vars are replicated when needed and not interrupt the normal execution
+# of the function on the slave. This also applies to procedures and triggers.
+
+# This test was constructed for BUG#25167
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+DROP TABLE IF EXISTS t2;
+--enable_warnings
+CREATE TABLE t1 (i int);
+CREATE TABLE t2 (k int);
+DELIMITER |;
+
+# Create a trigger that inserts data into another table, changes the @var then inserts
+# another row with the modified value.
+
+CREATE trigger t1_bi before INSERT on t1 for each row BEGIN
+ INSERT INTO t2 values (@a);
+ SET @a:=42;
+ INSERT INTO t2 values (@a);
+END |
+DELIMITER ;|
+
+sync_slave_with_master;
+connection master;
+
+# Set the @var to a value then insert data into first table.
+
+SET @a:=100;
+INSERT INTO t1 values (5);
+
+--echo On master: Check to see that data was inserted correctly in both tables
+
+SELECT * from t1;
+SELECT * from t2;
+
+sync_slave_with_master;
+
+--echo On slave: Check the tables for correct data and it matches master
+
+SELECT * from t1;
+SELECT * from t2;
+
+connection master;
+
+--echo End of 5.0 tests.
+
+# Cleanup
+
+DROP TABLE t1;
+DROP TABLE t2;
+
+# This test uses a stored function that uses user-defined variables to return data
+# The test ensures the value of the user-defined variable is replicated correctly
+# and in the correct order of assignment.
+
+# This test was constructed for BUG#20141
+
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+DROP FUNCTION IF EXISTS f1;
+DROP FUNCTION IF EXISTS f2;
+--enable_warnings
+
+CREATE TABLE t1 (i INT);
+
+# Create two functions. One simply returns the user-defined variable. The other
+# returns a value based on the user-defined variable.
+
+CREATE FUNCTION f1() RETURNS INT RETURN @a; DELIMITER |; CREATE
+FUNCTION f2() RETURNS INT BEGIN
+ INSERT INTO t1 VALUES (10 + @a);
+ RETURN 0;
+END|
+DELIMITER ;|
+
+sync_slave_with_master;
+connection master;
+
+# Set the variable and execute the functions.
+
+SET @a:=123;
+SELECT f1(), f2();
+
+--echo On master: Check to see that data was inserted correctly
+
+INSERT INTO t1 VALUES(f1());
+SELECT * FROM t1;
+
+sync_slave_with_master;
+
+--echo On slave: Check the table for correct data and it matches master
+
+SELECT * FROM t1;
+
+connection master;
+
+# Cleanup
+
+DROP FUNCTION f1;
+DROP FUNCTION f2;
+DROP TABLE t1;
+
sync_slave_with_master;
stop slave;
+
diff --git a/sql/item_func.cc b/sql/item_func.cc
index aa344a343de..5a1ba66e123 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -4220,7 +4220,14 @@ int get_var_with_binlog(THD *thd, enum_sql_command sql_command,
user_var_entry *var_entry;
var_entry= get_variable(&thd->user_vars, name, 0);
- if (!(opt_bin_log && is_update_query(sql_command)))
+ /*
+ Any reference to user-defined variable which is done from stored
+ function or trigger affects their execution and execution of calling
+ statement. Hence we want to log all accesses to such variables and
+ not only those that happen from table-updating statement.
+ */
+ if (!(opt_bin_log &&
+ (is_update_query(sql_command) || thd->in_sub_stmt)))
{
*out_entry= var_entry;
return 0;
diff --git a/sql/log.cc b/sql/log.cc
index 15cbff16339..f952fbd0d4c 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -3345,13 +3345,13 @@ bool MYSQL_BIN_LOG::flush_and_sync()
return err;
}
-void MYSQL_BIN_LOG::start_union_events(THD *thd)
+void MYSQL_BIN_LOG::start_union_events(THD *thd, query_id_t query_id_param)
{
DBUG_ASSERT(!thd->binlog_evt_union.do_union);
thd->binlog_evt_union.do_union= TRUE;
thd->binlog_evt_union.unioned_events= FALSE;
thd->binlog_evt_union.unioned_events_trans= FALSE;
- thd->binlog_evt_union.first_query_id= thd->query_id;
+ thd->binlog_evt_union.first_query_id= query_id_param;
}
void MYSQL_BIN_LOG::stop_union_events(THD *thd)
diff --git a/sql/log.h b/sql/log.h
index 80aa4b20ee6..970823dcd4a 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -341,7 +341,7 @@ public:
int write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync);
- void start_union_events(THD *thd);
+ void start_union_events(THD *thd, query_id_t query_id_param);
void stop_union_events(THD *thd);
bool is_query_in_union(THD *thd, query_id_t query_id_param);
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index b77d0cc9a0c..3d3e5179ad8 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1482,8 +1482,24 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
if (need_binlog_call)
{
+ query_id_t q;
reset_dynamic(&thd->user_var_events);
- mysql_bin_log.start_union_events(thd);
+ /*
+ In case of artificially constructed events for function calls
+ we have separate union for each such event and hence can't use
+ query_id of real calling statement as the start of all these
+ unions (this will break logic of replication of user-defined
+ variables). So we use artifical value which is guaranteed to
+ be greater than all query_id's of all statements belonging
+ to previous events/unions.
+ Possible alternative to this is logging of all function invocations
+ as one select and not resetting THD::user_var_events before
+ each invocation.
+ */
+ VOID(pthread_mutex_lock(&LOCK_thread_count));
+ q= ::query_id;
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ mysql_bin_log.start_union_events(thd, q + 1);
binlog_save_options= thd->options;
thd->options&= ~OPTION_BIN_LOG;
}
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 039fd71d670..fe03bea08db 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -2158,6 +2158,11 @@ void THD::reset_sub_statement_state(Sub_statement_state *backup,
{
options&= ~OPTION_BIN_LOG;
}
+
+ if ((backup->options & OPTION_BIN_LOG) && is_update_query(lex->sql_command)&&
+ !current_stmt_binlog_row_based)
+ mysql_bin_log.start_union_events(this, this->query_id);
+
/* Disable result sets */
client_capabilities &= ~CLIENT_MULTI_RESULTS;
in_sub_stmt|= new_state;
@@ -2201,6 +2206,10 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
sent_row_count= backup->sent_row_count;
client_capabilities= backup->client_capabilities;
+ if ((options & OPTION_BIN_LOG) && is_update_query(lex->sql_command) &&
+ !current_stmt_binlog_row_based)
+ mysql_bin_log.stop_union_events(this);
+
/*
The following is added to the old values as we are interested in the
total complexity of the query