Below is the list of changes that have just been committed into a local
5.0 repository of cbell. When cbell does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet@stripped, 2007-01-10 19:10:09-05:00, cbell@mysql_cab. +3 -0
BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode.
User-defined variables used inside of SF/SP/Triggers are not replicated. This can
cause errors in data if the user-defined variables are used in generating data.
This patch permits user-defined variables to be replicated if they are used in
SF/SP/Triggers. This permits the slave to produce the same data as the master
when user-defined variables are used within SF/SP/Triggers.
This patch also fixes BUG#14914 and BUG#25167.
mysql-test/r/rpl_user_variables.result@stripped, 2007-01-10 19:10:03-05:00, cbell@mysql_cab. +145 -0
BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode.
This patch modifies the test to ensure the test covers the use of user-defined
variables in SF/SP/Triggers.
mysql-test/t/rpl_user_variables.test@stripped, 2007-01-10 19:10:04-05:00, cbell@mysql_cab. +252 -2
BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode.
This patch modifies the test to ensure the test covers the use of user-defined
variables in SF/SP/Triggers.
sql/item_func.cc@stripped, 2007-01-10 19:10:04-05:00, cbell@mysql_cab. +37 -1
BUG#20141 - User-defined variables are not replicated properly for SF/Triggers in SBR mode.
User-defined variables used inside of SF/SP/Triggers are not replicated. This can
cause errors in data if the user-defined variables are used in generating data.
This patch permits user-defined variables to be replicated if they are used in
SF/SP/Triggers. This permits the slave to produce the same data as the master
when user-defined variables are used within SF/SP/Triggers.
# This is a BitKeeper patch. What follows are the unified diffs for the
# set of deltas contained in the patch. The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User: cbell
# Host: mysql_cab.
# Root: C:/source/c++/mysql-5.0_BUG_20141
--- 1.317/sql/item_func.cc 2007-01-10 19:10:27 -05:00
+++ 1.318/sql/item_func.cc 2007-01-10 19:10:27 -05:00
@@ -4120,10 +4120,46 @@ int get_var_with_binlog(THD *thd, enum_s
user_var_entry *var_entry;
var_entry= get_variable(&thd->user_vars, name, 0);
- if (!(opt_bin_log && is_update_query(sql_command)))
+ /*
+ Log accesses to user-defined variables for any statements used in
+ sp/trigger. The @var must be added to the list thd->user_var_events
+ to generate replication events. This is identified by thd->in_sub_stmt.
+ */
+ if (!(opt_bin_log &&
+ (is_update_query(sql_command) || thd->in_sub_stmt)))
{
*out_entry= var_entry;
return 0;
+ }
+ /*
+ If the execution is in a SP/Func/Trigger (in_sub_stmt != 0),
+ check to see if the @var has already been used. If it has, don't
+ overwrite any @var SET commands that are used inside the
+ SP/Func/Trigger. This is necessary because the evaluation of the
+ @vars created in this function are evaluated on the slave before
+ the execution of the SP/Func/Trigger.
+ */
+ if (thd->in_sub_stmt)
+ {
+ bool found = false;
+ if (thd->user_var_events.elements)
+ {
+ BINLOG_USER_VAR_EVENT *uve;
+ for (uint i= 0; i < thd->user_var_events.elements; i++)
+ {
+ get_dynamic(&thd->user_var_events,(gptr) &uve, i);
+ if (stricmp(uve->user_var_event->name.str, var_entry->name.str) == 0)
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (found) //if already used, don't overwrite
+ {
+ *out_entry= var_entry;
+ return 0;
+ }
}
if (!var_entry)
--- 1.17/mysql-test/r/rpl_user_variables.result 2007-01-10 19:10:27 -05:00
+++ 1.18/mysql-test/r/rpl_user_variables.result 2007-01-10 19:10:27 -05:00
@@ -108,4 +108,149 @@ slave-bin.000001 # User var 2 # @`a`=NUL
slave-bin.000001 # Query 1 # use `test`; insert into t1 values (@a),(@a),(@a*5)
insert into t1 select * FROM (select @var1 union select @var2) AS t2;
drop table t1;
+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|
+stop slave;
+start slave;
+SET @VAR=0;
+CALL test.insert();
+SET @VAR=1;
+CALL test.insert();
+SELECT * FROM t20;
+a
+SP_FALSE
+SP_TRUE
+SELECT * FROM t21;
+a
+TRIG_FALSE
+TRIG_TRUE
+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);
+stop slave;
+start slave;
+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());
+SELECT * FROM t1;
+i
+1
+4
+9
+16
+25
+SELECT * FROM t1;
+i
+1
+4
+9
+16
+25
+DROP TABLE t1;
+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 |
+stop slave;
+start slave;
+SET @a=500;
+INSERT INTO t1 values(f1());
+SET @b = 125;
+SET @c = 1;
+INSERT INTO t1 values(f2());
+SELECT * from t1;
+a
+500
+625
+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 |
+stop slave;
+start slave;
+SET @a:=100;
+INSERT INTO t1 values (5);
+SELECT * from t1;
+i
+5
+SELECT * from t2;
+k
+100
+42
+SELECT * from t1;
+i
+5
+SELECT * from t2;
+k
+100
+42
+DROP TABLE t1;
+DROP TABLE t2;
stop slave;
--- 1.14/mysql-test/t/rpl_user_variables.test 2007-01-10 19:10:27 -05:00
+++ 1.15/mysql-test/t/rpl_user_variables.test 2007-01-10 19:10:27 -05:00
@@ -56,9 +56,259 @@ connection master;
insert into t1 select * FROM (select @var1 union select @var2) AS t2;
drop table t1;
save_master_pos;
+# 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.
+#
+# 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;
+stop slave;
+start slave;
+connection master;
+
+# Set @var and call the procedure, repeat with different values
+
+SET @VAR=0;
+CALL test.insert();
+SET @VAR=1;
+CALL test.insert();
+
+# On master: Check the tables for correct data
+
+SELECT * FROM t20;
+SELECT * FROM t21;
+
+sync_slave_with_master;
connection slave;
-sync_with_master;
+
+# 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);
+
+sync_slave_with_master;
stop slave;
+start slave;
+connection master;
-# End of 4.1 tests
+# 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());
+
+# On master: Retrieve the values from the table
+
+SELECT * FROM t1;
+
+sync_slave_with_master;
+connection slave;
+
+# 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;
+
+# 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;
+stop slave;
+start slave;
+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());
+
+sync_slave_with_master;
+connection slave;
+
+# On master: Retrieve the values from the table
+
+SELECT * from t1;
+
+# 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;
+stop slave;
+start slave;
+connection master;
+
+# Set the @var to a value then insert data into first table.
+
+SET @a:=100;
+INSERT INTO t1 values (5);
+
+# On master: Check to see that data was inserted correctly in both tables
+
+SELECT * from t1;
+SELECT * from t2;
+
+sync_slave_with_master;
+connection slave;
+
+# On slave: Check the tables for correct data and it matches master
+
+SELECT * from t1;
+SELECT * from t2;
+
+connection master;
+
+# Cleanup
+
+DROP TABLE t1;
+DROP TABLE t2;
+
+sync_slave_with_master;
+stop slave;
| Thread |
|---|
| • bk commit into 5.0 tree (cbell:1.2347) BUG#20141 | cbell | 11 Jan |