List:Commits« Previous MessageNext Message »
From:Sven Sandberg Date:April 11 2008 5:56am
Subject:bk commit into 6.0 tree (sven:1.2627) WL#344
View as plain text  
Below is the list of changes that have just been committed into a local
6.0 repository of sven.  When sven 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, 2008-04-11 12:56:27+07:00, sven@riska.(none) +17 -0
  WL#344: Time-delayed Replication
  Implement new CHANGE MASTER TO MASTER_DELAY=X" feature, which causes the
  replication slave to be X seconds behind master.
  
  This is a special pre-release commit before my holiday. It is not
  thoroughly tested. See email to dev-replication@

  mysql-test/include/save_master_pos.inc@stripped, 2008-04-11 12:56:25+07:00, sven@riska.(none) +16 -0
    Like the test language's built-in save_master_pos, but saves it in a test
    language variable so it's accessible to tests. This was needed to
    implement wait_for_slave_io_to_sync.inc.

  mysql-test/include/save_master_pos.inc@stripped, 2008-04-11 12:56:25+07:00, sven@riska.(none) +0 -0

  mysql-test/include/show_slave_status3.inc@stripped, 2008-04-11 12:56:25+07:00, sven@riska.(none) +9 -0
    Like show_slave_status2.inc, but shows Slave_IO_State.

  mysql-test/include/show_slave_status3.inc@stripped, 2008-04-11 12:56:25+07:00, sven@riska.(none) +0 -0

  mysql-test/include/wait_for_slave_io_to_sync.inc@stripped, 2008-04-11 12:56:25+07:00, sven@riska.(none) +41 -0
    Like sync_slave_with_master, but only waits until the IO thread has\
    synced; the SQL thread may still be behind.

  mysql-test/include/wait_for_slave_io_to_sync.inc@stripped, 2008-04-11 12:56:25+07:00, sven@riska.(none) +0 -0

  mysql-test/suite/rpl/t/rpl_delayed_slave.test@stripped, 2008-04-11 12:56:26+07:00, sven@riska.(none) +275 -0
    Test to check the new CHANGE MASTER TO MASTER_DELAY = X feature.

  mysql-test/suite/rpl/t/rpl_delayed_slave.test@stripped, 2008-04-11 12:56:26+07:00, sven@riska.(none) +0 -0

  sql/lex.h@stripped, 2008-04-11 12:56:24+07:00, sven@riska.(none) +1 -0
    Add MASTER_DELAY parser symbol.

  sql/log.cc@stripped, 2008-04-11 12:56:24+07:00, sven@riska.(none) +7 -1
    Clarified (through comments and assert) that rli->data_lock must be held
    while executing MYSQL_BIN_LOG::purge_first_log.

  sql/log_event.cc@stripped, 2008-04-11 12:56:24+07:00, sven@riska.(none) +6 -0
    Clarified the use of some functions, to avoid race conditions.

  sql/rpl_rli.cc@stripped, 2008-04-11 12:56:24+07:00, sven@riska.(none) +26 -14
    - Make is_fake compiled in unconditionally. is_fake is needed in several
    places of this patch, e.g., apply_event_and_update_pos().
    - Added textual descriptions of the state of the SQL thread.
    - Made rli->delay be read from relay_log.info
    - Only call flush_relay_log_info from the slave SQL thread, not from a
    client executing a BINLOG statement.

  sql/rpl_rli.h@stripped, 2008-04-11 12:56:25+07:00, sven@riska.(none) +66 -4
    Make is_fake compiled in unconditionally, add document it.
    Add delay, delay_end, run_state, and run_state_names fields to
    Relay_log_info.

  sql/share/errmsg.txt@stripped, 2008-04-11 12:56:25+07:00, sven@riska.(none) +4 -0
    Added error messages.

  sql/slave.cc@stripped, 2008-04-11 12:56:25+07:00, sven@riska.(none) +206 -47
    - Fix bug where clock_diff_with_master was modified without a lock.
    - Document some key functions that were not documented.
    - Add Delay, Remaining_Delay, and Slave_SQL_Running_State to
    SHOW SLAVE STATUS.
    - Make sql thread sleep as needed according to the delay.
    - Make sql thread update it run_status as needed.
    - Change prototype of apply_event_and_update_pos() (we now use
    rli->is_fake to determine if we should test if events should be skipped,
    so the skip parameter is not needed any more).
    - Clean up some debug printouts (related to rli->is_fake, which is now
    compiled in unconditionally).
    - Fix bug where Master_info->slave_running was modified without a lock.

  sql/slave.h@stripped, 2008-04-11 12:56:25+07:00, sven@riska.(none) +9 -3
    - Add max value for MASTER_DELAY.
    - Update documentation of locks.
    - Change prototype for apply_event_and_update_pos.

  sql/sql_binlog.cc@stripped, 2008-04-11 12:56:25+07:00, sven@riska.(none) +26 -5
    - changed prototype for apply_event_and_update_pos()
    - made rli->is_fake compiled in unconditionally.
    - Allow only FD and RBR events in BINLOG statements.
    - removed initialization of ev->thd from myqsl_client_binlog_statement,
    since that is done in apply_event_and_update_pos().

  sql/sql_lex.cc@stripped, 2008-04-11 12:56:25+07:00, sven@riska.(none) +9 -0
    Added method to zero st_lex_master_info.

  sql/sql_lex.h@stripped, 2008-04-11 12:56:25+07:00, sven@riska.(none) +16 -3
    - Added delay to st_lex_master_info.
    - Added explicit value to enumeration, since we depend on
    LEX_MI_UNCHANGED=0 when resetting st_lex_master_info using bzero.
    - Added set_unspecified() to st_lex_master_info.

  sql/sql_repl.cc@stripped, 2008-04-11 12:56:25+07:00, sven@riska.(none) +31 -0
    - Copy the delay that we parsed from the query to the rli structure.
    - Document some functions that were undocumented.

  sql/sql_yacc.yy@stripped, 2008-04-11 12:56:25+07:00, sven@riska.(none) +17 -3
    - Parse "CHANGE MASTER TO MASTER_DELAY = X"
    - Use lex->mi.set_unspecified() instead of bzero() to reset the rli->mi
    structure.

diff -Nrup a/mysql-test/include/save_master_pos.inc b/mysql-test/include/save_master_pos.inc
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/include/save_master_pos.inc	2008-04-11 12:56:25 +07:00
@@ -0,0 +1,16 @@
+# ==== Purpose ====
+#
+# Saves the master position in the variables
+# $saved_master_file and $saved_master_position.
+#
+# ==== Usage ====
+#
+# source include/save_master_pos.inc;
+#
+# Must be executed on a master.
+#
+# Usually followed by source include/wait_for_slave_io_to_sync.inc; or
+# some other script that uses the saved position.
+
+let $saved_master_file= query_get_value("SHOW MASTER STATUS", File, 1);
+let $saved_master_pos= query_get_value("SHOW MASTER STATUS", Position, 1);
diff -Nrup a/mysql-test/include/show_slave_status3.inc b/mysql-test/include/show_slave_status3.inc
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/include/show_slave_status3.inc	2008-04-11 12:56:25 +07:00
@@ -0,0 +1,9 @@
+# Include file to show the slave status, masking out some information
+# that varies depending on where the test is executed, as well as
+# binlog positions.  However, the Slave_IO_State and Slave_SQL_State
+# are kept, not masked out.
+
+--replace_result $MASTER_MYPORT MASTER_PORT
+--replace_column 7 # 8 # 9 # 16 # 22 # 23 # 33 # 35 # 36 #
+# add the following to the end of previous line: 40 #
+query_vertical SHOW SLAVE STATUS;
diff -Nrup a/mysql-test/include/wait_for_slave_io_to_sync.inc b/mysql-test/include/wait_for_slave_io_to_sync.inc
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/include/wait_for_slave_io_to_sync.inc	2008-04-11 12:56:25 +07:00
@@ -0,0 +1,41 @@
+# ==== Purpose ====
+#
+# Waits until the slave IO thread has been synced, i.e., all events
+# have been copied over to slave.  Does not care if the SQL thread is
+# in sync.
+#
+#
+# ==== Usage ====
+#
+# Syncs the slave to the position saved in the last call to
+# include/save_master_pos.inc.
+#
+# Must be called on the slave.
+
+let $error= `SELECT '$saved_master_pos' = '' OR '$saved_master_file' = ''`;
+if ($error) {
+  --echo ERROR IN TEST SCRIPT: you must source include/save_master_pos.inc before include/wait_for_slave_io_to_sync.inc
+  exit;
+}
+
+let $_counter= 300;
+
+let $_file= query_get_value("SHOW SLAVE STATUS", Master_Log_File, 1);
+let $_pos= query_get_value("SHOW SLAVE STATUS", Read_Master_Log_Pos, 1);
+let $not_done= `SELECT '$_file' != '$saved_master_file' OR '$_pos' != '$saved_master_pos'`;
+
+while ($not_done)
+{
+  dec $_counter;
+  if (!$_counter)
+  {
+    --echo ERROR: failed while waiting for slave io to sync. _file=$_file _pos=$_pos saved_master_file=$saved_master_file saved_master_pos=$saved_master_pos
+    query_vertical show slave status;
+    exit;
+  }
+  sleep 0.1;
+
+  let $_file= query_get_value("SHOW SLAVE STATUS", Master_Log_File, 1);
+  let $_pos= query_get_value("SHOW SLAVE STATUS", Read_Master_Log_Pos, 1);
+  let $not_done= `SELECT '$_file' != '$saved_master_file' OR '$_pos' != '$saved_master_pos'`;
+}
diff -Nrup a/mysql-test/suite/rpl/t/rpl_delayed_slave.test b/mysql-test/suite/rpl/t/rpl_delayed_slave.test
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/mysql-test/suite/rpl/t/rpl_delayed_slave.test	2008-04-11 12:56:26 +07:00
@@ -0,0 +1,275 @@
+# ==== Purpose ====
+#
+# Test the time-delayed replication feature, i.e.,
+# CHANGE MASTER TO MASTER_DELAY=X:
+#
+#  - Verify that slave really has not executed the events before the
+#    delay timeout, and that it has executed events after the timout.
+#
+#  - Verify that it works when master and slave have different
+#    timestamps.
+#
+#  - Verify that it works when slave is lagging behind master for
+#    "natural" reasons (i.e., slow queries).
+#
+#  - Verify that STOP SLAVE works instantly even during a delay, and
+#    that it does not cause events to be executed too early on slave.
+#
+#  - Verify that changing back to no delay works.
+#
+# ==== Method ====
+#
+# We run the slave with 10 seconds lag.
+#
+# In general, to test that a query has not been executed by the slave
+# before this time, we wait until the slave IO thread has received the
+# event, and then 5 seconds more, and check that the table has not
+# been updated.  To test taht a query has been executed after this
+# time, we wait 10 seconds more.
+#
+# To simulate that the slave lags "naturally", we do "select
+# sleep(15)".
+#
+# ==== Related Bugs and Worklogs ====
+#
+# WL#344: Time-delayed replication
+# BUG#28760: Simulating a replication lag
+# [duplicate] BUG#22072: configurable delayed replication
+# [duplicate] BUG#21639: Add Replication Delay parameter
+#
+# ==== Issues with this Test Case ====
+#
+# Relies on all its queries to be executed within 5 seconds.
+#
+# Takes a long time; it sleeps for 90 seconds.
+
+source include/master-slave.inc;
+source include/have_binlog_format_mixed_or_statement.inc;
+
+--echo [on master]
+create table t1 (a varchar(100));
+
+
+--echo ==== Normal setup ====
+
+--echo [on slave]
+sync_slave_with_master;
+STOP SLAVE;
+source include/wait_for_slave_to_stop.inc;
+CHANGE MASTER TO MASTER_DELAY = 10;
+# Delay should be 10.
+source include/show_slave_status.inc;
+START SLAVE;
+source include/wait_for_slave_to_start.inc;
+
+--echo [on master]
+connection master;
+INSERT INTO t1 VALUES ('normal setup');
+source include/save_master_pos.inc;
+
+--echo [on slave]
+connection slave;
+source include/wait_for_slave_io_to_sync.inc;
+sleep 5;
+
+# Query should not have been executed.
+SELECT * FROM t1;
+source include/show_slave_status3.inc;
+
+sleep 10;
+
+# Query should have been executed.
+SELECT * FROM t1;
+source include/show_slave_status3.inc;
+
+
+--echo ==== Slave clock ahead of master ====
+
+--echo [on master]
+connection master;
+SET TIMESTAMP = 100;
+
+--echo [on slave]
+connection slave;
+STOP SLAVE;
+source include/wait_for_slave_to_stop.inc;
+SET TIMESTAMP = 10100;
+START SLAVE;
+source include/wait_for_slave_to_start.inc;
+
+--echo [on master]
+connection master;
+INSERT INTO t1 VALUES ('slave clock ahead of master');
+source include/save_master_pos.inc;
+
+--echo [on slave]
+connection slave;
+source include/wait_for_slave_io_to_sync.inc;
+sleep 5;
+
+# Query should not have been executed.
+SELECT * FROM t1;
+source include/show_slave_status3.inc;
+
+sleep 10;
+
+# Query should have been executed.
+SELECT * FROM t1;
+source include/show_slave_status3.inc;
+
+
+--echo ==== Slave clock behind master ====
+
+--echo [on master]
+connection master;
+SET TIMESTAMP = 10100;
+
+--echo [on slave]
+connection slave;
+STOP SLAVE;
+source include/wait_for_slave_to_stop.inc;
+SET TIMESTAMP = 100;
+START SLAVE;
+source include/wait_for_slave_to_start.inc;
+
+--echo [on master]
+connection master;
+INSERT INTO t1 VALUES ('slave clock behind master');
+source include/save_master_pos.inc;
+
+--echo [on slave]
+connection slave;
+source include/wait_for_slave_io_to_sync.inc;
+sleep 5;
+
+# Query should not have been executed.
+SELECT * FROM t1;
+source include/show_slave_status3.inc;
+
+sleep 10;
+
+# Query should have been executed.
+SELECT * FROM t1;
+source include/show_slave_status3.inc;
+
+SET TIMESTAMP= 0;
+--echo [on master]
+connection master;
+SET TIMESTAMP= 0;
+
+
+--echo ==== Slave lags "naturally" after master ====
+
+# To make slave lag, we do a SELECT SLEEP(15) on master
+INSERT INTO t1 SELECT SLEEP(15);
+save_master_pos;
+INSERT INTO t1 VALUES ('slave lags naturally after master');
+source include/save_master_pos.inc;
+
+--echo [on slave]
+connection slave;
+sync_with_master;
+source include/wait_for_slave_io_to_sync.inc;
+sleep 5;
+
+# Query should have been executed.
+SELECT * FROM t1;
+source include/show_slave_status3.inc;
+
+
+--echo ==== STOP SLAVE and START SLAVE ====
+
+# Set up a longer delay.
+STOP SLAVE;
+source include/wait_for_slave_to_stop.inc;
+CHANGE MASTER TO MASTER_DELAY = 15;
+# Delay should be 15.
+source include/show_slave_status.inc;
+START SLAVE;
+source include/wait_for_slave_to_start.inc;
+
+--echo [on master]
+connection master;
+INSERT INTO t1 VALUES ('stop slave and start slave');
+
+--echo [on slave]
+connection slave;
+sleep 5;
+STOP SLAVE;
+source include/wait_for_slave_to_stop.inc;
+
+# Query should not have been executed.
+SELECT * FROM t1;
+source include/show_slave_status3.inc;
+
+# Start and stop again, to ensure that even after restarting the
+# slave, we do not execute the event too early.
+START SLAVE;
+source include/wait_for_slave_to_start.inc;
+sleep 5;
+STOP SLAVE;
+source include/wait_for_slave_to_stop.inc;
+
+# Query should not have been executed.
+SELECT * FROM t1;
+source include/show_slave_status3.inc;
+
+sleep 10;
+
+# Query should have been executed.
+SELECT * FROM t1;
+source include/show_slave_status3.inc;
+
+
+--echo ==== Change back to no delay ====
+
+--echo [on master]
+connection master;
+SET TIMESTAMP = 0;
+
+--echo [on slave]
+connection slave;
+SET TIMESTAMP = 0;
+STOP SLAVE;
+source include/wait_for_slave_to_stop.inc;
+CHANGE MASTER TO MASTER_DELAY = 0;
+# Delay should be 0.
+source include/show_slave_status.inc;
+START SLAVE;
+source include/wait_for_slave_to_start.inc;
+
+--echo [on master]
+connection master;
+INSERT INTO t1 VALUES ('change back to no delay');
+source include/save_master_pos.inc;
+
+--echo [on slave]
+connection slave;
+source include/wait_for_slave_io_to_sync.inc;
+sleep 5;
+
+# Query should have been executed.
+SELECT * FROM t1;
+source include/show_slave_status3.inc;
+
+
+--echo ==== Setting a bad value for the delay ====
+
+--error ER_PARSE_ERROR
+CHANGE MASTER TO MASTER_DELAY = -1;
+
+--error ER_MASTER_DELAY_VALUE_OUT_OF_RANGE
+CHANGE MASTER TO MASTER_DELAY = 3000000000;
+
+# todo: CHANGE MASTER TO MASTER_DELAY = 999999999999999999999999999
+# should give error
+
+
+--echo ==== Clean up ====
+
+--echo [on master]
+connection master;
+drop table t1;
+
+--echo [on slave]
+sync_slave_with_master;
diff -Nrup a/sql/lex.h b/sql/lex.h
--- a/sql/lex.h	2008-04-01 17:56:38 +07:00
+++ b/sql/lex.h	2008-04-11 12:56:24 +07:00
@@ -306,6 +306,7 @@ static SYMBOL symbols[] = {
   { "LOW_PRIORITY",	SYM(LOW_PRIORITY)},
   { "MASTER",           SYM(MASTER_SYM)},
   { "MASTER_CONNECT_RETRY",           SYM(MASTER_CONNECT_RETRY_SYM)},
+  { "MASTER_DELAY",     SYM(MASTER_DELAY_SYM)},
   { "MASTER_HOST",           SYM(MASTER_HOST_SYM)},
   { "MASTER_LOG_FILE",           SYM(MASTER_LOG_FILE_SYM)},
   { "MASTER_LOG_POS",           SYM(MASTER_LOG_POS_SYM)},
diff -Nrup a/sql/log.cc b/sql/log.cc
--- a/sql/log.cc	2008-04-01 17:56:39 +07:00
+++ b/sql/log.cc	2008-04-11 12:56:24 +07:00
@@ -2923,6 +2923,10 @@ err:
   relay log.
 
   IMPLEMENTATION
+
+  - You must hold rli->data_lock before calling this function, since
+    it writes group_relay_log_pos and similar fields of
+    Relay_log_info.
   - Protects index file with LOCK_index
   - Delete relevant relay log files
   - Copy all file names after these ones to the front of the index file
@@ -2936,7 +2940,7 @@ err:
                       read by the SQL slave thread are deleted).
 
   @note
-    - This is only called from the slave-execute thread when it has read
+    - This is only called from the slave SQL thread when it has read
     all commands from a relay log and want to switch to a new relay log.
     - When this happens, we can be in an active transaction as
     a transaction can span over two relay logs
@@ -2963,6 +2967,8 @@ int MYSQL_BIN_LOG::purge_first_log(Relay
   DBUG_ASSERT(is_open());
   DBUG_ASSERT(rli->slave_running == 1);
   DBUG_ASSERT(!strcmp(rli->linfo.log_file_name,rli->event_relay_log_name));
+
+  safe_mutex_assert_owner(&rli->data_lock);
 
   pthread_mutex_lock(&LOCK_index);
   pthread_mutex_lock(&rli->log_space_lock);
diff -Nrup a/sql/log_event.cc b/sql/log_event.cc
--- a/sql/log_event.cc	2008-04-03 01:48:15 +07:00
+++ b/sql/log_event.cc	2008-04-11 12:56:24 +07:00
@@ -4101,6 +4101,9 @@ bool Rotate_log_event::write(IO_CACHE* f
   in a A -> B -> A setup.
   The NOTES below is a wrong comment which will disappear when 4.1 is merged.
 
+  This must only be called from the Slave SQL thread, since it calls
+  flush_relay_log_info().
+
   @retval
     0	ok
 */
@@ -5100,6 +5103,9 @@ void Stop_log_event::print(FILE* file, P
   were we must do this cleaning is in
   Start_log_event_v3::do_apply_event(), not here. Because if we come
   here, the master was sane.
+
+  This must only be called from the Slave SQL thread, since it calls
+  flush_relay_log_info().
 */
 int Stop_log_event::do_update_pos(Relay_log_info *rli)
 {
diff -Nrup a/sql/rpl_rli.cc b/sql/rpl_rli.cc
--- a/sql/rpl_rli.cc	2008-03-27 17:13:13 +07:00
+++ b/sql/rpl_rli.cc	2008-04-11 12:56:24 +07:00
@@ -29,21 +29,29 @@ int init_strvar_from_file(char *var, int
 			  const char *default_val);
 
 
+const char* Relay_log_info::run_state_names[]=
+{
+  "Not running",
+  "Waiting to read more events from relay log",
+  "Waiting until MASTER_DELAY seconds after master executed event",
+  "Executing event",
+};
+
+
 Relay_log_info::Relay_log_info()
   :Slave_reporting_capability("SQL"),
    no_storage(FALSE), replicate_same_server_id(::replicate_same_server_id),
    info_fd(-1), cur_log_fd(-1), save_temporary_tables(0),
-#if HAVE_purify
-   is_fake(FALSE),
-#endif
-   cur_log_old_open_count(0), group_relay_log_pos(0), event_relay_log_pos(0),
-   group_master_log_pos(0), log_space_total(0), ignore_log_space_limit(0),
+   cur_log_old_open_count(0), group_relay_log_pos(0),
+   event_relay_log_pos(0), is_fake(FALSE), group_master_log_pos(0),
+   log_space_total(0), ignore_log_space_limit(0),
    last_master_timestamp(0), slave_skip_counter(0),
    abort_pos_wait(0), slave_run_id(0), sql_thd(0),
    inited(0), abort_slave(0), slave_running(0), until_condition(UNTIL_NONE),
    until_log_pos(0), retried_trans(0),
    tables_to_lock(0), tables_to_lock_count(0),
-   last_event_start_time(0), m_flags(0)
+   last_event_start_time(0), delay(0), delay_end(0),
+   run_state(STATE_NOT_RUNNING), m_flags(0)
 {
   DBUG_ENTER("Relay_log_info::Relay_log_info");
 
@@ -230,12 +238,13 @@ Failed to open the existing relay log in
     if (init_strvar_from_file(rli->group_relay_log_name,
                               sizeof(rli->group_relay_log_name),
                               &rli->info_file, "") ||
-       init_intvar_from_file(&relay_log_pos,
-                             &rli->info_file, BIN_LOG_HEADER_SIZE) ||
-       init_strvar_from_file(rli->group_master_log_name,
-                             sizeof(rli->group_master_log_name),
-                             &rli->info_file, "") ||
-       init_intvar_from_file(&master_log_pos, &rli->info_file, 0))
+        init_intvar_from_file(&relay_log_pos,
+                              &rli->info_file, BIN_LOG_HEADER_SIZE) ||
+        init_strvar_from_file(rli->group_master_log_name,
+                              sizeof(rli->group_master_log_name),
+                              &rli->info_file, "") ||
+        init_intvar_from_file(&master_log_pos, &rli->info_file, 0) ||
+        init_intvar_from_file(&rli->delay, &rli->info_file, 0))
     {
       msg="Error reading slave log configuration";
       goto err;
@@ -788,7 +797,9 @@ void Relay_log_info::inc_group_relay_log
 {
   DBUG_ENTER("Relay_log_info::inc_group_relay_log_pos");
 
-  if (!skip_lock)
+  if (skip_lock)
+    safe_mutex_assert_owner(&data_lock);
+  else
     pthread_mutex_lock(&data_lock);
   inc_event_relay_log_pos();
   group_relay_log_pos= event_relay_log_pos;
@@ -1124,7 +1135,8 @@ void Relay_log_info::stmt_done(my_off_t 
   else
   {
     inc_group_relay_log_pos(event_master_log_pos);
-    flush_relay_log_info(this);
+    if (!is_fake)
+      flush_relay_log_info(this);
     /*
       Note that Rotate_log_event::do_apply_event() does not call this
       function, so there is no chance that a fake rotate event resets
diff -Nrup a/sql/rpl_rli.h b/sql/rpl_rli.h
--- a/sql/rpl_rli.h	2008-03-27 16:56:03 +07:00
+++ b/sql/rpl_rli.h	2008-04-11 12:56:25 +07:00
@@ -147,6 +147,10 @@ public:
     relay log and finishing (commiting) on another relay log. Case which can
     happen when, for example, the relay log gets rotated because of
     max_binlog_size.
+
+    Note: group_relay_log_name, group_relay_log_pos must only be
+    written from the thread owning the Relay_log_info (SQL thread if
+    !is_fake; client thread executing BINLOG statement if is_fake).
   */
   char group_relay_log_name[FN_REFLEN];
   ulonglong group_relay_log_pos;
@@ -154,9 +158,13 @@ public:
   ulonglong event_relay_log_pos;
   ulonglong future_event_relay_log_pos;
 
-#ifdef HAVE_purify
-  bool is_fake; /* Mark that this is a fake relay log info structure */
-#endif
+  /*
+    False for the Relay_log_info object that belongs to the sql
+    thread; true for the Relay_log_info that belongs to a client.  In
+    other words, non-fake rli's are used for replication, whereas fake
+    ones are used to execute BINLOG statements.
+  */
+  bool is_fake;
 
   /* 
      Original log name and position of the group we're currently executing
@@ -164,6 +172,10 @@ public:
      in the master's binlog. These concern the *group*, because in the master's
      binlog the log_pos that comes with each event is the position of the
      beginning of the group.
+
+    Note: group_master_log_name, group_master_log_pos must only be
+    written from the thread owning the Relay_log_info (SQL thread if
+    !is_fake; client thread executing BINLOG statement if is_fake).
   */
   char group_master_log_name[FN_REFLEN];
   volatile my_off_t group_master_log_pos;
@@ -289,7 +301,7 @@ public:
   }
 
   void inc_group_relay_log_pos(ulonglong log_pos,
-			       bool skip_lock=0);
+                              bool skip_lock=0);
 
   int wait_for_pos(THD* thd, String* log_name, longlong log_pos, 
 		   longlong timeout);
@@ -336,6 +348,56 @@ public:
     transaction).
    */
   time_t last_event_start_time;
+
+  /**
+    Delay replication slave by this amount, compared to master (in
+    seconds). This is set with CHANGE MASTER TO MASTER_DELAY=X.
+
+    Guarded by data_lock.  Initialized by the client thread executing
+    START SLAVE.  Written by client threads executing CHANGE MASTER TO
+    MASTER_DELAY=X.  Read by SQL thread and by client threads
+    executing SHOW SLAVE STATUS.  Note: must not be written while the
+    slave SQL thread is running, since the SQL thread reads it without
+    a lock when executing flush_relay_log_info().
+  */
+  int delay;
+  /**
+    During a delay, specifies the point in time when the delay ends.
+
+    This is used for the Remaining_Delay column in SHOW SLAVE STATUS.
+    Special values:
+
+    - 0 when the sql thread executes an event.
+
+    - 1 when the sql thread is waiting for events to show up in the
+      binlog.
+
+    Guarded by data_lock. Written by the sql thread.  Read by client
+    threads executing SHOW SLAVE STATUS.
+  */
+  time_t delay_end;
+  /**
+    The current state of the sql thread, used for the Slave_SQL_State
+    column in SHOW SLAVE STATUS.
+
+    Guarded by data_lock. Written by the sql thread.  Read by client
+    threads executing SHOW SLAVE STATUS.
+  */
+  enum enum_thread_state {
+    STATE_NOT_RUNNING= 0,
+    STATE_WAITING_FOR_EVENT= 1,
+    STATE_DELAYING= 2,
+    STATE_EXECUTING_EVENT= 3
+  };
+  /**
+    The SQL thread's current task, if it is running.
+  */
+  enum_thread_state run_state;
+  /**
+    Array of texts corresponding to each possible run_state, indexed
+    by enum_thread_state.
+  */
+  static const char* run_state_names[];
 
   /**
     Helper function to do after statement completion.
diff -Nrup a/sql/share/errmsg.txt b/sql/share/errmsg.txt
--- a/sql/share/errmsg.txt	2008-04-03 01:48:16 +07:00
+++ b/sql/share/errmsg.txt	2008-04-11 12:56:25 +07:00
@@ -6252,3 +6252,7 @@ ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE
   eng "The requested value for the heartbeat period %s %s"
 ER_LOG_PURGE_NO_FILE  
 	eng "Being purged log %s was not found"
+ER_MASTER_DELAY_VALUE_OUT_OF_RANGE
+        eng "The requested value for the master delay %u exceeds the maximum %u"
+ER_ONLY_FD_AND_RBR_EVENTS_ALLOWED_IN_BINLOG_STATEMENT
+        eng "Only Format_description_log_event and row events are allowed in BINLOG statements (but %s was provided)"
diff -Nrup a/sql/slave.cc b/sql/slave.cc
--- a/sql/slave.cc	2008-04-01 17:56:42 +07:00
+++ b/sql/slave.cc	2008-04-11 12:56:25 +07:00
@@ -878,12 +878,16 @@ static int get_master_version_and_clock(
       (master_res= mysql_store_result(mysql)) &&
       (master_row= mysql_fetch_row(master_res)))
   {
+    pthread_mutex_lock(&mi->data_lock);
     mi->clock_diff_with_master=
       (long) (time((time_t*) 0) - strtoul(master_row[0], 0, 10));
+    pthread_mutex_unlock(&mi->data_lock);
   }
   else if (!check_io_slave_killed(mi->io_thd, mi, NULL))
   {
+    pthread_mutex_lock(&mi->data_lock);
     mi->clock_diff_with_master= 0; /* The "most sensible" value */
+    pthread_mutex_unlock(&mi->data_lock);
     sql_print_warning("\"SELECT UNIX_TIMESTAMP()\" failed on master, "
                       "do not trust column Seconds_Behind_Master of SHOW "
                       "SLAVE STATUS. Error: %s (%d)",
@@ -1171,6 +1175,15 @@ int register_slave_on_master(MYSQL* mysq
 }
 
 
+/**
+  Executes SHOW SLAVE STATUS statements.
+
+  @param thd THD object for the client thread executing the statement.
+
+  @param mi Master_info object for the IO thread.
+
+  @return FALSE on success, TRUE on failure.
+*/
 bool show_master_info(THD* thd, Master_info* mi)
 {
   // TODO: fix this for multi-master
@@ -1238,6 +1251,9 @@ bool show_master_info(THD* thd, Master_i
   field_list.push_back(new Item_empty_string("Last_IO_Error", 20));
   field_list.push_back(new Item_return_int("Last_SQL_Errno", 4, MYSQL_TYPE_LONG));
   field_list.push_back(new Item_empty_string("Last_SQL_Error", 20));
+  field_list.push_back(new Item_return_int("Delay", 10, MYSQL_TYPE_LONG));
+  field_list.push_back(new Item_return_int("Remaining_Delay", 8, MYSQL_TYPE_LONG));
+  field_list.push_back(new Item_empty_string("Slave_SQL_Running_State", 20));
 
   if (protocol->send_fields(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
@@ -1357,6 +1373,17 @@ bool show_master_info(THD* thd, Master_i
     protocol->store(mi->rli.last_error().number);
     // Last_SQL_Error
     protocol->store(mi->rli.last_error().message, &my_charset_bin);
+    // Delay
+    protocol->store((uint32) mi->rli.delay);
+    // Remaining_Delay
+    if (mi->rli.run_state == Relay_log_info::STATE_DELAYING)
+      protocol->store((uint32) (mi->rli.delay_end - my_time(0)));
+    else if (mi->rli.run_state == Relay_log_info::STATE_EXECUTING_EVENT)
+      protocol->store((uint32) 0);
+    else
+      protocol->store_null();
+    // Slave_SQL_Running_State
+    protocol->store(mi->rli.run_state_names[(int)mi->rli.run_state], &my_charset_bin);
 
     pthread_mutex_unlock(&mi->rli.data_lock);
     pthread_mutex_unlock(&mi->data_lock);
@@ -1678,23 +1705,98 @@ static int has_temporary_error(THD *thd)
 
 
 /**
+  If this is a lagging slave (specified with CHANGE MASTER TO MASTER_DELAY = X), delays accordingly. Also unlocks rli->data_lock.
+
+  Design note: this is the place to unlock rli->data_lock here since
+  it should be held when reading delay info from rli, but it should
+  not be held while sleeping.
+
+  @param ev Event that is about to be executed.
+
+  @param thd The sql thread's THD object.
+
+  @param rli The sql thread's Relay_log_info structure.
+*/
+static void delay_event(Log_event *ev, THD *thd, Relay_log_info *rli)
+{
+  long delay= rli->delay;
+
+  DBUG_ENTER("delay_event");
+  safe_mutex_assert_owner(&rli->data_lock);
+  DBUG_ASSERT(!rli->is_fake);
+
+  if (delay)
+  {
+    time_t delay_end= rli->delay_end=
+      ev->when + rli->mi->clock_diff_with_master + delay;
+
+    time_t now= my_time(0);
+    long nap_time= delay_end - now;
+
+    DBUG_PRINT("info", ("delay= %lu "
+                        "ev->when= %lu "
+                        "rli->mi->clock_diff_with_master= %lu "
+                        "now= %lu "
+                        "delay_end= %lu "
+                        "nap_time= %lu",
+                        delay, (long)ev->when, rli->mi->clock_diff_with_master,
+                        (long)now, delay_end, nap_time));
+
+    if (nap_time > 0)
+    {
+      DBUG_PRINT("info", ("delaying replication event %lu secs",
+                          nap_time));
+      rli->run_state= Relay_log_info::STATE_DELAYING;
+      pthread_mutex_unlock(&rli->data_lock);
+      safe_sleep(thd, nap_time, (CHECK_KILLED_FUNC)sql_slave_killed,
+                 (void*)rli);
+      DBUG_VOID_RETURN;
+    }
+  }
+
+  pthread_mutex_unlock(&rli->data_lock);
+
+  DBUG_VOID_RETURN;
+}
+
+
+/**
   Applies the given event and advances the relay log position.
 
-  In essence, this function does:
+  This is needed by the sql thread to execute events from the binlog,
+  and by clients executing BINLOG statements.  Conceptually, this
+  function does:
 
   @code
     ev->apply_event(rli);
     ev->update_pos(rli);
   @endcode
 
-  But it also does some maintainance, such as skipping events if
-  needed and reporting errors.
+  It also does the following maintainance:
 
-  If the @c skip flag is set, then it is tested whether the event
-  should be skipped, by looking at the slave_skip_counter and the
-  server id.  The skip flag should be set when calling this from a
-  replication thread but not set when executing an explicit BINLOG
-  statement.
+   - Initializes the thread's server_id and time; and the event's
+     thread.
+
+   - If rli is not fake (i.e., if it belongs to the slave sql thread
+     instead of being used for executing BINLOG statements), it does
+     the following things: (1) skips events if it is needed according
+     to the server id or slave_skip_counter; (2) unlocks
+     rli->data_lock; (3) sleeps appropriately if this is a
+     time-delayed slave (specified by CHANGE MASTER TO
+     MASTER_DELAY=X); (4) maintains the running state of the sql
+     thread (rli->thread_state).
+
+   - Reports errors as needed.
+
+  @param ev The event to apply.
+
+  @param thd The client thread that executes the event (i.e., the
+  slave sql thread if called from a replication slave, or the client
+  thread if called to execute a BINLOG statement).
+
+  @param rli The relay log info (i.e., the slave's rli if called from
+  a replication slave, or the client's thd->rli_fake if called to
+  execute a BINLOG statement).
 
   @retval 0 OK.
 
@@ -1703,8 +1805,7 @@ static int has_temporary_error(THD *thd)
   @retval 2 No error calling ev->apply_event(), but error calling
   ev->update_pos().
 */
-int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli,
-                               bool skip)
+int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli)
 {
   int exec_res= 0;
 
@@ -1749,14 +1850,44 @@ int apply_event_and_update_pos(Log_event
     ev->when= my_time(0);
   ev->thd = thd; // because up to this point, ev->thd == 0
 
-  if (skip)
+  /*
+    There is one non-"fake" rli object for the slave sql thread, and
+    one "fake" rli object for executing explicit BINLOG statements.
+    Below, the slave does three things that should not be done by
+    BINLOG statements:
+
+     - checks if the event shall be skipped,
+
+     - honors the delay specified by CHANGE MASTER TO MASTER_DELAY=X,
+
+     - unlocks rli->data_lock.
+
+     - maintains the running state of the sql thread
+       (rli->thread_state).
+  */
+  if (!rli->is_fake)
   {
+    // The sql thread should hold the data_lock before calling this
+    // function (rli->is_fake is true for the sql thread).
+    safe_mutex_assert_owner(&rli->data_lock);
+
+    // Check if we need to skip the event.
     int reason= ev->shall_skip(rli);
     if (reason == Log_event::EVENT_SKIP_COUNT)
       --rli->slave_skip_counter;
-    pthread_mutex_unlock(&rli->data_lock);
+
+    // If event should be executed, check if slave needs to delay.
     if (reason == Log_event::EVENT_SKIP_NOT)
+    {
+      // Sleeps if needed, and unlocks rli->data_lock.
+      delay_event(ev, thd, rli);
+      rli->run_state= Relay_log_info::STATE_EXECUTING_EVENT;
       exec_res= ev->apply_event(rli);
+      rli->run_state= Relay_log_info::STATE_WAITING_FOR_EVENT;
+    }
+    else
+      pthread_mutex_unlock(&rli->data_lock);
+
 #ifndef DBUG_OFF
     /*
       This only prints information to the debug trace.
@@ -1785,14 +1916,11 @@ int apply_event_and_update_pos(Log_event
   if (exec_res == 0)
   {
     int error= ev->update_pos(rli);
-#ifdef HAVE_purify
+#ifndef DBUG_OFF
+    DBUG_PRINT("info", ("update_pos error = %d", error));
     if (!rli->is_fake)
-#endif
     {
-#ifndef DBUG_OFF
       char buf[22];
-#endif
-      DBUG_PRINT("info", ("update_pos error = %d", error));
       DBUG_PRINT("info", ("group %s %s",
                           llstr(rli->group_relay_log_pos, buf),
                           rli->group_relay_log_name));
@@ -1800,6 +1928,7 @@ int apply_event_and_update_pos(Log_event
                           llstr(rli->event_relay_log_pos, buf),
                           rli->event_relay_log_name));
     }
+#endif
     /*
       The update should not fail, so print an error message and
       return an error code.
@@ -1826,7 +1955,8 @@ int apply_event_and_update_pos(Log_event
 
 
 /**
-  Top-level function for executing the next event from the relay log.
+  Top-level function for executing the next event in the relay log
+  from the SQL thread.
 
   This function reads the event from the relay log, executes it, and
   advances the relay log position.  It also handles errors, etc.
@@ -1845,7 +1975,7 @@ int apply_event_and_update_pos(Log_event
      fewer times, 0 is returned.
 
    - init_master_info or init_relay_log_pos failed. (These are called
-     if a failure occurs when applying the event.)</li>
+     if a failure occurs when applying the event.)
 
    - An error occurred when updating the binlog position.
 
@@ -1899,7 +2029,7 @@ static int exec_relay_log_event(THD* thd
       delete ev;
       DBUG_RETURN(1);
     }
-    exec_res= apply_event_and_update_pos(ev, thd, rli, TRUE);
+    exec_res= apply_event_and_update_pos(ev, thd, rli);
 
     /*
       Format_description_log_event should not be deleted because it will be
@@ -2164,8 +2294,10 @@ pthread_handler_t handle_slave_io(void *
 
 connected:
 
-  // TODO: the assignment below should be under mutex (5.0)
+  pthread_mutex_lock(&mi->run_lock);
   mi->slave_running= MYSQL_SLAVE_RUN_CONNECT;
+  pthread_mutex_unlock(&mi->run_lock);
+
   thd->slave_net = &mysql->net;
   thd_proc_info(thd, "Checking master version");
   if (get_master_version_and_clock(mysql, mi))
@@ -2533,6 +2665,8 @@ Slave SQL thread aborted. Can't execute 
     pthread_mutex_unlock(&rli->data_lock);
     goto err;
   }
+
+  rli->run_state= Relay_log_info::STATE_WAITING_FOR_EVENT;
   pthread_mutex_unlock(&rli->data_lock);
 
   /* Read queries from the IO/THREAD until this thread is killed */
@@ -3452,45 +3586,60 @@ static int safe_reconnect(THD* thd, MYSQ
 }
 
 
-/*
-  Store the file and position where the execute-slave thread are in the
+/**
+  Store the file and position where the slave's SQL thread are in the
   relay log.
 
-  SYNOPSIS
-    flush_relay_log_info()
-    rli                 Relay log information
+  Notes:
 
-  NOTES
-    - As this is only called by the slave thread, we don't need to
-      have a lock on this.
-    - If there is an active transaction, then we don't update the position
-      in the relay log.  This is to ensure that we re-execute statements
-      if we die in the middle of an transaction that was rolled back.
-    - As a transaction never spans binary logs, we don't have to handle the
-      case where we do a relay-log-rotation in the middle of the transaction.
-      If this would not be the case, we would have to ensure that we
-      don't delete the relay log file where the transaction started when
-      we switch to a new relay log file.
-
-  TODO
-    - Change the log file information to a binary format to avoid calling
-      longlong2str.
+  - This function should be called either from the slave SQL thread,
+    or when the slave thread is not running.  (It reads the
+    group_{relay|master}_log_{pos|name} and delay fields in the rli
+    object.  These may only be modified by the slave SQL thread or by
+    a client thread when the slave SQL thread is not running.)
+
+  - If there is an active transaction, then we do not update the
+    position in the relay log.  This is to ensure that we re-execute
+    statements if we die in the middle of an transaction that was
+    rolled back.
+
+  - As a transaction never spans binary logs, we don't have to handle
+    the case where we do a relay-log-rotation in the middle of the
+    transaction.  If transactions could span several binlogs, we would
+    have to ensure that we do not delete the relay log file where the
+    transaction started before switching to a new relay log file.
+
+  - Error can happen if writing to file fails or if flushing the file
+    fails.
+
+  @param rli The object representing the Relay_log_info.
+
+  @param unlock_data_lock If set, rli->data_lock will be unlocked by
+  this function, otherwise it will be kept.  This is useful if the
+  lock is not needed after the call to this function, because this
+  function does expensive disk operations after it stopped depending
+  on the lock.
 
-  RETURN VALUES
-    0   ok
-    1   write error
-*/
+  @todo Change the log file information to a binary format to avoid
+  calling longlong2str.
+
+  @todo Refactor this to become a member function of Relay_log_info.
 
+  @return 0 on success, 1 on error.
+*/
 bool flush_relay_log_info(Relay_log_info* rli)
 {
   bool error=0;
   DBUG_ENTER("flush_relay_log_info");
 
+  DBUG_ASSERT(!rli->is_fake);
+
   if (unlikely(rli->no_storage))
     DBUG_RETURN(0);
 
   IO_CACHE *file = &rli->info_file;
-  char buff[FN_REFLEN*2+22*2+4], *pos;
+  // 2*file name, 2*long long, 1*unsigned long, 5*'\n'
+  char buff[FN_REFLEN*2+22*2+10+5], *pos;
 
   my_b_seek(file, 0L);
   pos=strmov(buff, rli->group_relay_log_name);
@@ -3500,7 +3649,9 @@ bool flush_relay_log_info(Relay_log_info
   pos=strmov(pos, rli->group_master_log_name);
   *pos++='\n';
   pos=longlong2str(rli->group_master_log_pos, pos, 10);
-  *pos='\n';
+  *pos++= '\n';
+  pos= longlong2str(rli->delay, pos, 10);
+  *pos= '\n';
   if (my_b_write(file, (uchar*) buff, (size_t) (pos-buff)+1))
     error=1;
   if (flush_io_cache(file))
@@ -3536,6 +3687,14 @@ static IO_CACHE *reopen_relay_log(Relay_
 }
 
 
+/**
+  Reads next event from the relay log.  Should only be called from the
+  slave SQL thread.
+
+  @param rli The Relay_log_info that belongs to the IO thread.
+
+  @return The event read, or NULL on error.
+*/
 static Log_event* next_event(Relay_log_info* rli)
 {
   Log_event* ev;
diff -Nrup a/sql/slave.h b/sql/slave.h
--- a/sql/slave.h	2008-03-30 14:17:07 +07:00
+++ b/sql/slave.h	2008-04-11 12:56:25 +07:00
@@ -33,6 +33,11 @@
 */
 #define SLAVE_MAX_HEARTBEAT_PERIOD 4294967
 
+/**
+  MASTER_DELAY can be at most INT32_MAX.
+*/
+#define MASTER_DELAY_MAX (0X7FFFFFFF)
+
 #ifdef HAVE_REPLICATION
 
 #include "log.h"
@@ -91,12 +96,14 @@ class Master_info;
 
   In Master_info: run_lock, data_lock
   run_lock protects all information about the run state: slave_running, thd
-  and the existence of the I/O thread to stop/start it, you need this mutex).
+  and the existence of the I/O thread (to stop/start it, you need this mutex).
   data_lock protects some moving members of the struct: counters (log name,
   position) and relay log (MYSQL_BIN_LOG object).
 
   In Relay_log_info: run_lock, data_lock
   see Master_info
+  However, note that run_lock does not protect
+  Relay_log_info.run_state; that is protected by data_lock.
   
   Order of acquisition: if you want to have LOCK_active_mi and a run_lock, you
   must acquire LOCK_active_mi first.
@@ -198,8 +205,7 @@ int purge_relay_logs(Relay_log_info* rli
 void set_slave_thread_options(THD* thd);
 void set_slave_thread_default_charset(THD *thd, Relay_log_info const *rli);
 void rotate_relay_log(Master_info* mi);
-int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli,
-                               bool skip);
+int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli);
 
 pthread_handler_t handle_slave_io(void *arg);
 pthread_handler_t handle_slave_sql(void *arg);
diff -Nrup a/sql/sql_binlog.cc b/sql/sql_binlog.cc
--- a/sql/sql_binlog.cc	2008-03-05 17:19:53 +07:00
+++ b/sql/sql_binlog.cc	2008-04-11 12:56:25 +07:00
@@ -56,9 +56,7 @@ void mysql_client_binlog_statement(THD* 
   if (!thd->rli_fake)
   {
     thd->rli_fake= new Relay_log_info;
-#ifdef HAVE_purify
     thd->rli_fake->is_fake= TRUE;
-#endif
     have_fd_event= FALSE;
   }
   if (thd->rli_fake && !thd->rli_fake->relay_log.description_event_for_exec)
@@ -153,9 +151,9 @@ void mysql_client_binlog_statement(THD* 
         see one; it is the only statement that can be read in base64
         without a prior Format_description_event.
       */
+      int type = bufptr[EVENT_TYPE_OFFSET];
       if (!have_fd_event)
       {
-        int type = bufptr[EVENT_TYPE_OFFSET];
         if (type == FORMAT_DESCRIPTION_EVENT || type == START_EVENT_V3)
           have_fd_event= TRUE;
         else
@@ -166,6 +164,29 @@ void mysql_client_binlog_statement(THD* 
         }
       }
 
+      /*
+        Do not execute other events than row-events and FD events.  It
+        is crucial to avoid executing Stop_log_event and
+        Rotate_log_event since they call flush_relay_log_info, which
+        is not allowed to call by other threads than the slave SQL
+        thread when the slave SQL thread is running.
+      */
+      switch (type) {
+      case FORMAT_DESCRIPTION_EVENT:
+      case TABLE_MAP_EVENT:
+      case WRITE_ROWS_EVENT:
+      case UPDATE_ROWS_EVENT:
+      case DELETE_ROWS_EVENT:
+      case PRE_GA_WRITE_ROWS_EVENT:
+      case PRE_GA_UPDATE_ROWS_EVENT:
+      case PRE_GA_DELETE_ROWS_EVENT:
+        /* only the above event types are allowed */
+        break;
+      default:
+        my_error(ER_ONLY_FD_AND_RBR_EVENTS_ALLOWED_IN_BINLOG_STATEMENT,
+                 MYF(0), Log_event::get_type_str((Log_event_type)type));
+      }
+
       ev= Log_event::read_log_event(bufptr, event_len, &error,
                                     thd->rli_fake->relay_log.
                                       description_event_for_exec);
@@ -196,7 +217,7 @@ void mysql_client_binlog_statement(THD* 
                           bytes_decoded, (long) bufptr,
                           (ulong) uint4korr(bufptr+EVENT_LEN_OFFSET)));
 #endif
-      ev->thd= thd;
+
       /*
         We go directly to the application phase, since we don't need
         to check if the event shall be skipped or not.
@@ -206,7 +227,7 @@ void mysql_client_binlog_statement(THD* 
         reporting.
       */
 #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
-      if (apply_event_and_update_pos(ev, thd, thd->rli_fake, FALSE))
+      if (apply_event_and_update_pos(ev, thd, thd->rli_fake))
       {
         /*
           TODO: Maybe a better error message since the BINLOG statement
diff -Nrup a/sql/sql_lex.cc b/sql/sql_lex.cc
--- a/sql/sql_lex.cc	2008-04-01 17:56:44 +07:00
+++ b/sql/sql_lex.cc	2008-04-11 12:56:25 +07:00
@@ -3028,3 +3028,12 @@ bool st_lex::is_partition_management() c
            alter_info.flags == ALTER_REORGANIZE_PARTITION));
 }
 
+
+/**
+  Set all fields to their "unspecified" value.
+*/
+void st_lex_master_info::set_unspecified()
+{
+  bzero((char*) this, sizeof(*this));
+  delay= -1;
+}
diff -Nrup a/sql/sql_lex.h b/sql/sql_lex.h
--- a/sql/sql_lex.h	2008-04-01 17:56:44 +07:00
+++ b/sql/sql_lex.h	2008-04-11 12:56:25 +07:00
@@ -195,22 +195,35 @@ typedef struct st_lex_server_options
   char *server_name, *host, *db, *username, *password, *scheme, *socket, *owner;
 } LEX_SERVER_OPTIONS;
 
+/**
+  Structure to hold parameters for CHANGE MASTER or START/STOP SLAVE
+  or SHOW NEW MASTER.
+
+  Remark: this should not be confused with Master_info (and perhaps
+  would better be renamed to st_lex_replication_info).  Some fields,
+  e.g., delay, are saved in Relay_log_info, not in Master_info.
+*/
 typedef struct st_lex_master_info
 {
   char *host, *user, *password, *log_file_name;
   uint port, connect_retry;
   float heartbeat_period;
+  int delay;
   ulonglong pos;
   ulong server_id;
   /*
     Enum is used for making it possible to detect if the user
-    changed variable or if it should be left at old value
-   */
-  enum {LEX_MI_UNCHANGED, LEX_MI_DISABLE, LEX_MI_ENABLE}
+    changed variable or if it should be left at old value.
+    LEX_MI_UNCHANGED must be 0 since we use bzero to reset these
+    variables in set_undefined().
+  */
+  enum {LEX_MI_UNCHANGED= 0, LEX_MI_DISABLE, LEX_MI_ENABLE}
     ssl, ssl_verify_server_cert, heartbeat_opt;
   char *ssl_key, *ssl_cert, *ssl_ca, *ssl_capath, *ssl_cipher;
   char *relay_log_name;
   ulong relay_log_pos;
+
+  void set_unspecified();
 } LEX_MASTER_INFO;
 
 
diff -Nrup a/sql/sql_repl.cc b/sql/sql_repl.cc
--- a/sql/sql_repl.cc	2008-03-08 17:14:45 +07:00
+++ b/sql/sql_repl.cc	2008-04-11 12:56:25 +07:00
@@ -891,6 +891,19 @@ err:
   DBUG_VOID_RETURN;
 }
 
+
+/**
+  Executes START SLAVE statements.
+
+  @param thd THD object for the client thread executing the statement.
+
+  @param mi Master_info object belonging to the slave's IO thread.
+
+  @param net_report If true, updates saves the exit status into
+  thd->main_da.
+
+  @return 0 on success, 1 on error.
+*/
 int start_slave(THD* thd , Master_info* mi,  bool net_report)
 {
   int slave_errno= 0;
@@ -1190,6 +1203,15 @@ void kill_zombie_dump_threads(uint32 sla
 }
 
 
+/**
+  Executes CHANGE MASTER statements.
+
+  @param thd THD object for the client thread executing the statement.
+
+  @param mi Master_info object belonging to the slave's IO thread.
+
+  @return FALSE on success, TRUE on error.
+*/
 bool change_master(THD* thd, Master_info* mi)
 {
   int thread_mask;
@@ -1259,6 +1281,8 @@ bool change_master(THD* thd, Master_info
     mi->heartbeat_period= (float) min(SLAVE_MAX_HEARTBEAT_PERIOD,
                                       (slave_net_timeout/2.0));
   mi->received_heartbeats= LL(0); // counter lives until master is CHANGEd
+  if (lex_mi->delay != -1)
+    mi->rli.delay= lex_mi->delay;
   if (lex_mi->ssl != LEX_MASTER_INFO::LEX_MI_UNCHANGED)
     mi->ssl= (lex_mi->ssl == LEX_MASTER_INFO::LEX_MI_ENABLE);
 
@@ -1587,6 +1611,13 @@ err:
 }
 
 
+/**
+  Executes SHOW MASTER STATUS statements.
+
+  @param thd THD object for the client thread executing the statement.
+
+  @return FALSE on success, TRUE on failure.
+*/
 bool show_binlog_info(THD* thd)
 {
   Protocol *protocol= thd->protocol;
diff -Nrup a/sql/sql_yacc.yy b/sql/sql_yacc.yy
--- a/sql/sql_yacc.yy	2008-04-01 20:19:00 +07:00
+++ b/sql/sql_yacc.yy	2008-04-11 12:56:25 +07:00
@@ -876,6 +876,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
 %token  LOW_PRIORITY
 %token  LT                            /* OPERATOR */
 %token  MASTER_CONNECT_RETRY_SYM
+%token  MASTER_DELAY_SYM
 %token  MASTER_HOST_SYM
 %token  MASTER_LOG_FILE_SYM
 %token  MASTER_LOG_POS_SYM
@@ -1635,7 +1636,7 @@ change:
           {
             LEX *lex = Lex;
             lex->sql_command = SQLCOM_CHANGE_MASTER;
-            bzero((char*) &lex->mi, sizeof(lex->mi));
+            lex->mi.set_unspecified();
           }
           master_defs
           {}
@@ -1667,6 +1668,16 @@ master_def:
           {
             Lex->mi.connect_retry = $3;
           }
+        | MASTER_DELAY_SYM EQ ulong_num
+          {
+            if ($3 > MASTER_DELAY_MAX)
+            {
+              my_error(ER_MASTER_DELAY_VALUE_OUT_OF_RANGE, MYF(0),
+                       $3, MASTER_DELAY_MAX);
+            }
+            else
+              Lex->mi.delay = $3;
+          }
         | MASTER_SSL_SYM EQ ulong_num
           {
             Lex->mi.ssl= $3 ? 
@@ -6198,7 +6209,7 @@ slave:
             lex->sql_command = SQLCOM_SLAVE_START;
             lex->type = 0;
             /* We'll use mi structure for UNTIL options */
-            bzero((char*) &lex->mi, sizeof(lex->mi));
+            lex->mi.set_unspecified();
             /* If you change this code don't forget to update SLAVE START too */
           }
           slave_until
@@ -6216,7 +6227,8 @@ slave:
             lex->sql_command = SQLCOM_SLAVE_START;
             lex->type = 0;
             /* We'll use mi structure for UNTIL options */
-            bzero((char*) &lex->mi, sizeof(lex->mi));
+            lex->mi.set_unspecified();
+            /* If you change this code don't forget to update START SLAVE too */
           }
           slave_until
           {}
@@ -6225,6 +6237,7 @@ slave:
             LEX *lex=Lex;
             lex->sql_command = SQLCOM_SLAVE_STOP;
             lex->type = 0;
+            /* If you change this code don't forget to update STOP SLAVE too */
           }
         ;
 
@@ -11018,6 +11031,7 @@ keyword_sp:
         | MASTER_PASSWORD_SYM      {}
         | MASTER_SERVER_ID_SYM     {}
         | MASTER_CONNECT_RETRY_SYM {}
+        | MASTER_DELAY_SYM         {}
         | MASTER_SSL_SYM           {}
         | MASTER_SSL_CA_SYM        {}
         | MASTER_SSL_CAPATH_SYM    {}
Thread
bk commit into 6.0 tree (sven:1.2627) WL#344Sven Sandberg11 Apr