diff options
author | unknown <cbell/Chuck@mysql_cab_desk.> | 2007-02-26 14:33:58 -0500 |
---|---|---|
committer | unknown <cbell/Chuck@mysql_cab_desk.> | 2007-02-26 14:33:58 -0500 |
commit | f3377aa8947836e355c780ae767ab3573e976afe (patch) | |
tree | fa283e662a33cb4654cb034379a7dc978b28b04a | |
parent | c140b92cfb1de9d0e9374bef96dc2d24ffb6ae8a (diff) | |
parent | 9f957f1429df804b35eafe468e20b219e8070d91 (diff) | |
download | mariadb-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.result | 174 | ||||
-rw-r--r-- | mysql-test/t/rpl_user_variables.test | 297 | ||||
-rw-r--r-- | sql/item_func.cc | 9 | ||||
-rw-r--r-- | sql/log.cc | 4 | ||||
-rw-r--r-- | sql/log.h | 2 | ||||
-rw-r--r-- | sql/sp_head.cc | 18 | ||||
-rw-r--r-- | sql/sql_class.cc | 9 |
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 |