Bug #25543 Replication of wrong values if using rand() in stored procedure
When rand() is called multiple times inside a stored procedure, the server does not binlog the correct random seed values. This patch corrects the problem by resetting rand_used= 0 in THD::cleanup_after_query() allowing the system to save the random seeds if needed for each command in a stored procedure body. However, rand_used is not reset if executing in a stored function or trigger because these operations are binlogged by call and thus only the calling statement need detect the call to rand() made by its substatements. These substatements must not set rand_used to 0 because it would remove the detection of rand() by the calling statement.
This commit is contained in:
parent
e9481608c3
commit
d44eb9f0c9
@ -18,6 +18,29 @@ create table t2 like t1;
|
|||||||
load data local infile 'MYSQLTEST_VARDIR/master-data/test/rpl_misc_functions.outfile' into table t2;
|
load data local infile 'MYSQLTEST_VARDIR/master-data/test/rpl_misc_functions.outfile' into table t2;
|
||||||
select * from t1, t2 where (t1.id=t2.id) and not(t1.i=t2.i and t1.r1=t2.r1 and t1.r2=t2.r2 and t1.p=t2.p);
|
select * from t1, t2 where (t1.id=t2.id) and not(t1.i=t2.i and t1.r1=t2.r1 and t1.r2=t2.r2 and t1.p=t2.p);
|
||||||
id i r1 r2 p id i r1 r2 p
|
id i r1 r2 p id i r1 r2 p
|
||||||
stop slave;
|
|
||||||
drop table t1;
|
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
DROP TABLE IF EXISTS t1;
|
||||||
|
CREATE TABLE t1 (col_a double default NULL);
|
||||||
|
CREATE PROCEDURE test_replication_sp1()
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO t1 VALUES (rand()), (rand());
|
||||||
|
INSERT INTO t1 VALUES (rand());
|
||||||
|
END|
|
||||||
|
CREATE PROCEDURE test_replication_sp2()
|
||||||
|
BEGIN
|
||||||
|
CALL test_replication_sp1();
|
||||||
|
CALL test_replication_sp1();
|
||||||
|
END|
|
||||||
|
CREATE FUNCTION test_replication_sf() RETURNS DOUBLE DETERMINISTIC
|
||||||
|
BEGIN
|
||||||
|
RETURN (rand() + rand());
|
||||||
|
END|
|
||||||
|
CALL test_replication_sp1();
|
||||||
|
CALL test_replication_sp2();
|
||||||
|
INSERT INTO t1 VALUES (test_replication_sf());
|
||||||
|
INSERT INTO t1 VALUES (test_replication_sf());
|
||||||
|
INSERT INTO t1 VALUES (test_replication_sf());
|
||||||
|
DROP PROCEDURE IF EXISTS test_replication_sp1;
|
||||||
|
DROP PROCEDURE IF EXISTS test_replication_sp2;
|
||||||
|
DROP FUNCTION IF EXISTS test_replication_sf;
|
||||||
|
DROP TABLE IF EXISTS t1;
|
||||||
|
@ -28,10 +28,74 @@ create table t2 like t1;
|
|||||||
eval load data local infile '$MYSQLTEST_VARDIR/master-data/test/rpl_misc_functions.outfile' into table t2;
|
eval load data local infile '$MYSQLTEST_VARDIR/master-data/test/rpl_misc_functions.outfile' into table t2;
|
||||||
# compare them with the replica; the SELECT below should return no row
|
# compare them with the replica; the SELECT below should return no row
|
||||||
select * from t1, t2 where (t1.id=t2.id) and not(t1.i=t2.i and t1.r1=t2.r1 and t1.r2=t2.r2 and t1.p=t2.p);
|
select * from t1, t2 where (t1.id=t2.id) and not(t1.i=t2.i and t1.r1=t2.r1 and t1.r2=t2.r2 and t1.p=t2.p);
|
||||||
stop slave;
|
|
||||||
drop table t1;
|
|
||||||
|
|
||||||
connection master;
|
connection master;
|
||||||
drop table t1;
|
drop table t1;
|
||||||
|
|
||||||
# End of 4.1 tests
|
# End of 4.1 tests
|
||||||
|
|
||||||
|
#
|
||||||
|
# BUG#25543 test calling rand() multiple times on the master in
|
||||||
|
# a stored procedure.
|
||||||
|
#
|
||||||
|
|
||||||
|
--disable_warnings
|
||||||
|
DROP TABLE IF EXISTS t1;
|
||||||
|
--enable_warnings
|
||||||
|
|
||||||
|
CREATE TABLE t1 (col_a double default NULL);
|
||||||
|
|
||||||
|
DELIMITER |;
|
||||||
|
|
||||||
|
# Use a SP that calls rand() multiple times
|
||||||
|
CREATE PROCEDURE test_replication_sp1()
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO t1 VALUES (rand()), (rand());
|
||||||
|
INSERT INTO t1 VALUES (rand());
|
||||||
|
END|
|
||||||
|
|
||||||
|
# Use a SP that calls another SP to call rand() multiple times
|
||||||
|
CREATE PROCEDURE test_replication_sp2()
|
||||||
|
BEGIN
|
||||||
|
CALL test_replication_sp1();
|
||||||
|
CALL test_replication_sp1();
|
||||||
|
END|
|
||||||
|
|
||||||
|
# Use a SF that calls rand() multiple times
|
||||||
|
CREATE FUNCTION test_replication_sf() RETURNS DOUBLE DETERMINISTIC
|
||||||
|
BEGIN
|
||||||
|
RETURN (rand() + rand());
|
||||||
|
END|
|
||||||
|
|
||||||
|
DELIMITER ;|
|
||||||
|
|
||||||
|
# Exercise the functions and procedures then compare the results on
|
||||||
|
# the master to those on the slave.
|
||||||
|
CALL test_replication_sp1();
|
||||||
|
CALL test_replication_sp2();
|
||||||
|
INSERT INTO t1 VALUES (test_replication_sf());
|
||||||
|
INSERT INTO t1 VALUES (test_replication_sf());
|
||||||
|
INSERT INTO t1 VALUES (test_replication_sf());
|
||||||
|
|
||||||
|
# Record the results of the query on the master
|
||||||
|
--exec $MYSQL --port=$MASTER_MYPORT test -e "SELECT * FROM test.t1" > $MYSQLTEST_VARDIR/tmp/rpl_rand_master.sql
|
||||||
|
|
||||||
|
--sync_slave_with_master
|
||||||
|
|
||||||
|
# Record the results of the query on the slave
|
||||||
|
--exec $MYSQL --port=$SLAVE_MYPORT test -e "SELECT * FROM test.t1" > $MYSQLTEST_VARDIR/tmp/rpl_rand_slave.sql
|
||||||
|
|
||||||
|
# Compare the results from the master to the slave.
|
||||||
|
--exec diff $MYSQLTEST_VARDIR/tmp/rpl_rand_master.sql $MYSQLTEST_VARDIR/tmp/rpl_rand_slave.sql
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
--disable_warnings
|
||||||
|
DROP PROCEDURE IF EXISTS test_replication_sp1;
|
||||||
|
DROP PROCEDURE IF EXISTS test_replication_sp2;
|
||||||
|
DROP FUNCTION IF EXISTS test_replication_sf;
|
||||||
|
DROP TABLE IF EXISTS t1;
|
||||||
|
--enable_warnings
|
||||||
|
|
||||||
|
# If all is good, when can cleanup our dump files.
|
||||||
|
--system rm $MYSQLTEST_VARDIR/tmp/rpl_rand_master.sql
|
||||||
|
--system rm $MYSQLTEST_VARDIR/tmp/rpl_rand_slave.sql
|
||||||
|
@ -575,6 +575,18 @@ void THD::cleanup_after_query()
|
|||||||
clear_next_insert_id= 0;
|
clear_next_insert_id= 0;
|
||||||
next_insert_id= 0;
|
next_insert_id= 0;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
Reset rand_used so that detection of calls to rand() will save random
|
||||||
|
seeds if needed by the slave.
|
||||||
|
|
||||||
|
Do not reset rand_used if inside a stored function or trigger because
|
||||||
|
only the call to these operations is logged. Thus only the calling
|
||||||
|
statement needs to detect rand() calls made by its substatements. These
|
||||||
|
substatements must not set rand_used to 0 because it would remove the
|
||||||
|
detection of rand() by the calling statement.
|
||||||
|
*/
|
||||||
|
if (!in_sub_stmt)
|
||||||
|
rand_used= 0;
|
||||||
/* Free Items that were created during this execution */
|
/* Free Items that were created during this execution */
|
||||||
free_items();
|
free_items();
|
||||||
/* Reset where. */
|
/* Reset where. */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user