List:Commits« Previous MessageNext Message »
From:Sven Sandberg Date:May 18 2010 1:12pm
Subject:bzr push into mysql-5.1 branch (sven.sandberg:3447 to 3448) Bug#28760
WL#344
View as plain text  
 3448 Sven Sandberg	2010-05-18
      WL#344: Time-delayed replication
      BUG#28760: Simulating a replication lag
      Implemented a new "CHANGE MASTER TO MASTER_DELAY = X" option, which causes
      the replication slave to be X seconds behind the master.
      
      This entails the following changes:
      - New syntax: CHANGE MASTER TO MASTER_DELAY = <int>
      - The SQL thread sleeps as needed.
      - SHOW SLAVE STATUS has three new fields with information about the
        delay and the SQL thread status.
      - When the slave SQL thread is sleeping, SHOW PROCESSLIST indicates this.
      - RESET SLAVE resets the delay.
      - The option is saved to the relay_log.info file.
      
      In addition, some small refactorings and bug fixes were needed to make
      this work. See file-specific commit messages for details.
     @ mysql-test/extra/rpl_tests/delayed_slave_wait_on_query.inc
        Auxiliary file used by rpl_delayed_slave.
     @ mysql-test/include/save_master_pos.inc
        Added source file equivalent to save_master_pos. This can now be used
        together with sync_slave_io_with_master.inc and sync_with_master.inc.
        It has the advantage over the built-in command that it supports all
        the features and flexibility of wait_for_slave_param.inc.
     @ mysql-test/include/show_delayed_slave_state.inc
        Auxiliary test file that displays the state of the delayed slave in a
        deterministic manner.
     @ mysql-test/include/sync_slave_io_with_master.inc
        Renamed internal variables to avoid clobbering namespace too much.
     @ mysql-test/include/sync_with_master.inc
        Added source file corresponding to the build-in command
        sync_with_master. This has the advantage that it supports all the
        features and flexibility of wait_for_slave_param.inc.
     @ mysql-test/std_data/old-format_relay-log.info
        A relay-log.info file using the old (pre-WL#344) format.
     @ mysql-test/suite/rpl/r/rpl_delayed_slave.result
        new result file
     @ mysql-test/suite/rpl/r/rpl_read_old_relay_log_info.result
        new result file
     @ mysql-test/suite/rpl/t/rpl_delayed_slave.test
        New test case for time-delayed replication slaves.
     @ mysql-test/suite/rpl/t/rpl_read_old_relay_log_info.test
        Test case that ensures that the pre-WL#344 format of relay-log.info
        can still be parsed.
     @ sql/lex.h
        Added MASTER_DELAY parser symbol.
     @ sql/log.cc
        Clarified comments and added assertion.
     @ sql/log_event.cc
        Clarified comments and added assertion.
     @ sql/rpl_mi.cc
        Moved forward declarations of functions defined in slave.cc to slave.h
     @ sql/rpl_rli.cc
         - Added delay parameter to relay_log.info file. The file format is now
           changed: first line is the number of lines in the file, then follows
           the actual lines. The code now understands both the new and the old
           format.
         - Removed is_fake and no_storage (they meant the same thing) and replaced
           them by the inline function belongs_to_client(). We now need
           belongs_to_client() in apply_event_and_update_pos() [slave.cc]
         - Moved forward declarations of functions defined in slave.cc to slave.h
         - Added Relay_log_info::state_delaying_string.
         - Moved the body of init_relay_log_info into the new member function
           Relay_log_info::init and made init_relay_log_info a wrapper around the
           new member function.
         - Added debug printouts.
     @ sql/rpl_rli.h
         - Removed is_fake and no_storage (they meant the same thing) and replaced
           them with the inline member function belongs_to_client().
         - Added private delay variables, and public functions to get and set them.
         - Replaced flush_relay_log_info and init_relay_log_info by the new member
           function Relay_log_info::flush and Relay_log_info::init.
         - Added comments clarifying how to use group_[relay|master]_log_[pos|name].
     @ sql/share/errmsg.txt
        Added new error codes.
     @ sql/slave.cc
         - Added three new fields to SHOW SLAVE STATUS: Slave_SQL_Delay (as
           specified by CHANGE MASTER), Slave_SQL_Running_State (this just
           exposes thd_proc_info; it is to the SQL thread what Slave_IO_State
           is to the IO thread), and Slave_SQL_Remaining_Delay (NULL when the
           SQL thread is not sleeping; the remaining time in seconds when
           the SQL thread is sleeping).
         - Updated apply_event_and_update_pos so that it takes sleeping into
           account. It now calls the new function sql_delay_event, which
           handles the delay.
         - Cleaned up some debug printout code in apply_event_and_update_pos
           (since it is related to rli->is_fake, which has been replaced by
           rli->belongs_to_client_thread(), which is available on all builds)
         - Improved documentation.
         - Made the IO thread take appropriate lock while setting
           mi->clock_diff_with_master and while setting mi->slave_running.
         - Replaced flush_relay_log_info by the new member function
           Relay_log_info::flush, and made flush_relay_log_info a wrapper
           around Relay_log_info::flush.
         - Made Relay_log_info::flush write the new delay parameter. The
           format has changed: see commit message and source comments in
           rpl_rli.cc.
     @ sql/slave.h
         - Added MASTER_DELAY_MAX.
         - Clarified comments.
         - Moved forward declarations of functions defined in slave.cc to slave.h
     @ sql/sql_binlog.cc
         - Added check that BINLOG statements only contain row events and
           Format_description_log_events. This avoids possible strange side
           effects and deadlocks that might happen if users feed strange
           events like rotate event to the BINLOG command.
         - With the new check, the code becomes more readable if refactored
           so that the event type is checked in a separate function. This
           means that the handling of Format_description_log_events has been
           refactored too.
         - Replaced the two member fields rli->is_fake and rli->no_storage by
           one single member function, rli->belongs_to_client().
     @ sql/sql_lex.cc
        Added implementation of new function
        st_lex_master_info::set_unspecified()
     @ sql/sql_lex.h
         - Added delay to st_lex_master_info.
         - Added set_unspecified()
         - Added explicit initialization of enumeration value SSL_UNCHANGED to 0,
           to make more clear that the value is not allowed to change (it is written
           to relay_log.info).
     @ sql/sql_repl.cc
         - Made RESET SLAVE reset the delay.
         - Initialize Relay_log_info::delay from Lex_master_info::delay.
     @ sql/sql_yacc.yy
         - Added code to parse "CHANGE MASTER TO MASTER_DELAY = X"
         - Use new function st_lex_master_info::set_unspecified to clear
           st_lex_master_info structure, instead of bzero.

    added:
      mysql-test/extra/rpl_tests/delayed_slave_wait_on_query.inc
      mysql-test/include/save_master_pos.inc
      mysql-test/include/show_delayed_slave_state.inc
      mysql-test/include/sync_with_master.inc
      mysql-test/std_data/old-format_relay-log.info
      mysql-test/suite/rpl/r/rpl_delayed_slave.result
      mysql-test/suite/rpl/r/rpl_read_old_relay_log_info.result
      mysql-test/suite/rpl/t/rpl_delayed_slave.test
      mysql-test/suite/rpl/t/rpl_read_old_relay_log_info.test
    modified:
      mysql-test/include/sync_slave_io_with_master.inc
      sql/lex.h
      sql/log.cc
      sql/log_event.cc
      sql/rpl_mi.cc
      sql/rpl_rli.cc
      sql/rpl_rli.h
      sql/share/errmsg.txt
      sql/slave.cc
      sql/slave.h
      sql/sql_binlog.cc
      sql/sql_lex.cc
      sql/sql_lex.h
      sql/sql_repl.cc
      sql/sql_yacc.yy
 3447 Sven Sandberg	2010-04-28
      pre-WL#344 test fixes: Remove show slave status from output of all tests.
      SHOW SLAVE STATUS is lengthy, makes result files hard to read, and
      makes it less clear what the purpose of the test is. The output
      also changes frequently, in particular after WL#344. To make it
      easier to implement WL#344 and other fixes, and to make it easier to
      maintain tests in the future, we replace all calls to show slave status
      in all tests by something more specific.
      In the future, no result file should contain the full output of
      show slave status; instead the tested property should be checked
      directly or only specific columns from the output of show slave status
      should be printed. See comment in show_slave_status.inc for details.
     @ mysql-test/extra/rpl_tests/rpl_deadlock.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to check that the slave is running,
        so we source include/check_slave_is_running.inc instead.
     @ mysql-test/extra/rpl_tests/rpl_extraMaster_Col.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to check that the slave is running
        in some places and to check the error message in some places,
        so we replace show slave status by
        source include/check_slave_is_running.inc and wait_for_slave_sql_error.inc,
        respectively.
     @ mysql-test/extra/rpl_tests/rpl_extraSlave_Col.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to check the error message,
        so we replace show slave status by wait_for_slave_sql_error.inc.
     @ mysql-test/extra/rpl_tests/rpl_log.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to check that the slave is running,
        so we source include/check_slave_is_running.inc instead.
     @ mysql-test/extra/rpl_tests/rpl_max_relay_size.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to check that the slave is running,
        so we source include/check_slave_is_running.inc instead.
        In one place, show slave status didn't do anything useful at all, so was removed.
     @ mysql-test/extra/rpl_tests/rpl_reset_slave.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to check the Master_User
        and Master_Host columns, so we replace show slave status
        by printouts of these.
     @ mysql-test/extra/rpl_tests/rpl_row_tabledefs.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to check that the slave is running
        in some places and to check the error message in some places,
        so we replace show slave status by
        source include/check_slave_is_running.inc and wait_for_slave_sql_error.inc,
        respectively.
     @ mysql-test/include/check_slave_is_running.inc
        Added file that asserts that both slave threads are running.
     @ mysql-test/include/show_binlog_events.inc
        Added filter to remove version from output of SHOW BINLOG EVENTS.
        This can occur if Format_description_log_events are listed.
     @ mysql-test/include/show_rpl_debug_info.inc
        Added printouts of NOW(). This is sometimes useful in debugging,
        particularly to compare current time with event time, and when
        testing time parameters like delayed slave and Seconds_Behind_Master.
     @ mysql-test/include/show_slave_ssl_status.inc
        Added file to show ssl-related fields of SHOW SLAVE STATUS.
     @ mysql-test/include/show_slave_status.inc
        Added check to prevent over-use of this file again.
        Filter out new columns from show slave status.
     @ mysql-test/include/show_slave_status2.inc
        Added check to prevent over-use of this file again.
        Filter out new columns from show slave status.
     @ mysql-test/include/test_fieldsize.inc
        Replaced show slave status by more specific checks.
        In this test, the purpose was to check the error message,
        so we replace show slave status by wait_for_slave_sql_error.inc.
     @ mysql-test/include/wait_for_binlog_event.inc
        Added more debug info on failure.
     @ mysql-test/include/wait_for_slave_param.inc
        Use die to fail, not exit.
     @ mysql-test/include/wait_for_slave_sql_error.inc
        Use die to fail, not exit.
        Moved $show_slave_sql_error from wait_for_slave_sql_error_and_skip.inc
        into wait_for_slave_sql_error.inc. Since the former sources the latter,
        both files now support $show_slave_sql_error.
     @ mysql-test/include/wait_for_slave_sql_error_and_skip.inc
        Allow custom value for slave_skip_counter.
        Moved $show_sql_error check to wait_for_slave_sql_error.inc.
     @ mysql-test/include/wait_for_status_var.inc
        Use die to fail, not exit.
     @ mysql-test/include/wait_until_count_sessions.inc
        Use die to fail, not exit.
     @ mysql-test/suite/parts/r/rpl_partition.result
        updated result file
     @ mysql-test/suite/parts/t/rpl_partition.test
        updated result file
     @ mysql-test/suite/rpl/r/rpl_000015.result
        removed useless test
     @ mysql-test/suite/rpl/r/rpl_bug33931.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_change_master.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_deadlock_innodb.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_empty_master_crash.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_extraCol_innodb.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_extraCol_myisam.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_extraColmaster_innodb.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_extraColmaster_myisam.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_flushlog_loop.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_grant.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_incident.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_known_bugs_detection.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_loaddata_fatal.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_log_pos.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_rbr_to_sbr.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_replicate_do.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_rotate_logs.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_row_colSize.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_row_log.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_row_log_innodb.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_row_max_relay_size.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_row_reset_slave.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_row_tabledefs_2myisam.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_row_tabledefs_3innodb.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_row_until.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_skip_error.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_slave_load_remove_tmpfile.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_slave_skip.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_ssl.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_ssl1.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_stm_log.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_stm_max_relay_size.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_stm_reset_slave.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_stm_until.result
        updated result file
     @ mysql-test/suite/rpl/r/rpl_temporary_errors.result
        updated result file
     @ mysql-test/suite/rpl/t/rpl_000015-slave.opt
        This test didn't do anything useful. Removed.
     @ mysql-test/suite/rpl/t/rpl_000015.cnf
        This test didn't do anything useful. Removed.
     @ mysql-test/suite/rpl/t/rpl_000015.test
        This test didn't do anything useful. Removed.
     @ mysql-test/suite/rpl/t/rpl_bug33931.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to check the error message,
        so we replace show slave status by wait_for_slave_sql_error.inc.
     @ mysql-test/suite/rpl/t/rpl_change_master.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to check that the slave stopped
        after a call to stop slave. Use include/stop_slave.inc instead.
     @ mysql-test/suite/rpl/t/rpl_do_grant.test
        Remove race from test (causing sporadic warnings from check-testcase).
     @ mysql-test/suite/rpl/t/rpl_empty_master_crash.test
        Replaced show slave status by more specific checks.
        In this test, show slave status didn't do anything useful at all, so was removed.
     @ mysql-test/suite/rpl/t/rpl_filter_tables_not_exist.test
        $show_sql_error has been renamed to $show_slave_sql_error.
     @ mysql-test/suite/rpl/t/rpl_flushlog_loop.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to check that the slave is running,
        so we source include/check_slave_is_running.inc instead.
        Also, use sync_slave_with_master instead of equivalent ad-hoc code.
        Also, the test used disable_query_log for apparently no reason, so we turn on the query log.
     @ mysql-test/suite/rpl/t/rpl_grant.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to check that the slave is running,
        so we source include/check_slave_is_running.inc instead.
     @ mysql-test/suite/rpl/t/rpl_incident.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to check that the slave is running,
        so we source include/check_slave_is_running.inc instead.
        In one place, show slave status was not needed at all, so was removed.
        Also, added $show_slave_sql_error=1 so that wait_for_slave_sql_error prints the error message.
     @ mysql-test/suite/rpl/t/rpl_known_bugs_detection.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to show Slave_SQL_Error, so we source
        include/wait_for_slave_sql_error.inc instead.
     @ mysql-test/suite/rpl/t/rpl_loaddata_fatal.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to show Slave_SQL_Error respectively
        check that the slaves is running, so we source
        include/wait_for_slave_sql_error.inc respectively
        include/check_slave_is_running.inc instead.
     @ mysql-test/suite/rpl/t/rpl_log_pos.test
        Replaced show slave status by more specific checks.
        In this test, show slave status was not needed at all, so was removed.
        (In one place, the purpose was to verify that the slave has stopped
        after stop slave. However, that is already guaranteed by the file
        include/stop_slave.inc, so show slave status was superfluous).
     @ mysql-test/suite/rpl/t/rpl_rbr_to_sbr.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to check that the slave is running,
        so we source include/check_slave_is_running.inc instead.
        Also replaced ad-hoc call to SHOW BINLOG EVENTS by source
        include/show_binlog_events.inc
     @ mysql-test/suite/rpl/t/rpl_replicate_do.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to display the Replicate_Do_Table column,
        so this was printed instead of the full output.
     @ mysql-test/suite/rpl/t/rpl_rotate_logs.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to display the Master_Log_File column and
        to ensure that the slave is still running. Hence, we source
        include/check_slave_is_running.inc and print only Master_Log_File.
     @ mysql-test/suite/rpl/t/rpl_row_until.test
        Replaced show slave status by more specific checks.
        In this test, show slave status was not needed at all, so was removed.
        (The purpose was to check that the event executed by the master has been
        copied to slave, but that is ensured by the SELECT statement so
        show slave status was superfluous.)
     @ mysql-test/suite/rpl/t/rpl_skip_error.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to check that the slave is running,
        so we source include/check_slave_is_running.inc instead.
     @ mysql-test/suite/rpl/t/rpl_slave_load_remove_tmpfile.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to show Slave_SQL_Error, so we source
        include/wait_for_slave_sql_error.inc instead.
     @ mysql-test/suite/rpl/t/rpl_slave_skip.test
        Replaced show slave status by more specific checks.
        In the first place in this test, the purpose was to check that the slave
        has stopped. That is already checked by wait_for_slave_sql_to_stop.inc, so
        the call was superfluous and has been removed.
        In the second place, the purpose was to show that the slave is running,
        so we source include/check_slave_is_running.inc instead.
     @ mysql-test/suite/rpl/t/rpl_ssl.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to show that the slave is still running
        so we source include/check_slave_is_running.inc instead.
        Also added debug printouts and 'die' in a case where the test fails.
     @ mysql-test/suite/rpl/t/rpl_ssl1.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to show that the slave is still running
        and to show the values of the various SSL-related columns. Hence, we
        source include/check_slave_is_running.inc and
        include/show_slave_ssl_status.inc instead.
     @ mysql-test/suite/rpl/t/rpl_stm_until.test
        Replaced show slave status by more specific checks.
        In this test, show slave status was not needed at all, so was removed.
        (The purpose was to show that the slave SQL thread has stopped.
        However, that is already guaranteed by wait_for_slave_sql_to_stop.inc,
        so the test was superfluous.)
     @ mysql-test/suite/rpl/t/rpl_temporary_errors.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to show that the slave is still running,
        so we source include/check_slave_is_running.inc instead.
     @ mysql-test/suite/rpl_ndb/r/rpl_ndb_basic.result
        updated result file
     @ mysql-test/suite/rpl_ndb/r/rpl_ndb_circular.result
        updated result file
     @ mysql-test/suite/rpl_ndb/r/rpl_ndb_circular_simplex.result
        updated result file
     @ mysql-test/suite/rpl_ndb/r/rpl_ndb_extraCol.result
        updated result file
     @ mysql-test/suite/rpl_ndb/r/rpl_ndb_idempotent.result
        updated result file
     @ mysql-test/suite/rpl_ndb/r/rpl_ndb_log.result
        updated result file
     @ mysql-test/suite/rpl_ndb/r/rpl_ndb_sync.result
        updated result file
     @ mysql-test/suite/rpl_ndb/t/rpl_ndb_basic.test
        Replaced show slave status by more specific checks.
        In this test, show slave status was not needed, so was removed.
        The purpose was to show that the slave is not running; however,
        that has already been assured by wait_for_slave_sql_to_stop.inc.
        Also removed unnecessary 'connection slave'.
     @ mysql-test/suite/rpl_ndb/t/rpl_ndb_circular.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to show that the slave
        was still running, so it was replaced by source
        include/check_slave_is_running.inc.
     @ mysql-test/suite/rpl_ndb/t/rpl_ndb_circular_simplex.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to show that the slave
        was still running, so it was replaced by source
        include/check_slave_is_running.inc.
     @ mysql-test/suite/rpl_ndb/t/rpl_ndb_idempotent.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to show that the slave
        was still running, so it was replaced by source
        include/check_slave_is_running.inc.
        In one place, show slave status was not needed at all,
        so was removed.
     @ mysql-test/suite/rpl_ndb/t/rpl_ndb_sync.test
        Replaced show slave status by more specific checks.
        In this test, the purpose was to show that the slave
        was still running, so it was replaced by source
        include/check_slave_is_running.inc.

    removed:
      mysql-test/suite/rpl/r/rpl_000015.result
      mysql-test/suite/rpl/t/rpl_000015-slave.opt
      mysql-test/suite/rpl/t/rpl_000015.cnf
      mysql-test/suite/rpl/t/rpl_000015.test
    added:
      mysql-test/include/check_slave_is_running.inc
      mysql-test/include/show_slave_ssl_status.inc
    modified:
      mysql-test/extra/rpl_tests/rpl_deadlock.test
      mysql-test/extra/rpl_tests/rpl_extraMaster_Col.test
      mysql-test/extra/rpl_tests/rpl_extraSlave_Col.test
      mysql-test/extra/rpl_tests/rpl_log.test
      mysql-test/extra/rpl_tests/rpl_max_relay_size.test
      mysql-test/extra/rpl_tests/rpl_reset_slave.test
      mysql-test/extra/rpl_tests/rpl_row_tabledefs.test
      mysql-test/include/show_binlog_events.inc
      mysql-test/include/show_rpl_debug_info.inc
      mysql-test/include/show_slave_status.inc
      mysql-test/include/show_slave_status2.inc
      mysql-test/include/test_fieldsize.inc
      mysql-test/include/wait_for_binlog_event.inc
      mysql-test/include/wait_for_slave_param.inc
      mysql-test/include/wait_for_slave_sql_error.inc
      mysql-test/include/wait_for_slave_sql_error_and_skip.inc
      mysql-test/include/wait_for_status_var.inc
      mysql-test/include/wait_until_count_sessions.inc
      mysql-test/suite/parts/r/rpl_partition.result
      mysql-test/suite/parts/t/rpl_partition.test
      mysql-test/suite/rpl/r/rpl_bug33931.result
      mysql-test/suite/rpl/r/rpl_change_master.result
      mysql-test/suite/rpl/r/rpl_deadlock_innodb.result
      mysql-test/suite/rpl/r/rpl_empty_master_crash.result
      mysql-test/suite/rpl/r/rpl_extraCol_innodb.result
      mysql-test/suite/rpl/r/rpl_extraCol_myisam.result
      mysql-test/suite/rpl/r/rpl_extraColmaster_innodb.result
      mysql-test/suite/rpl/r/rpl_extraColmaster_myisam.result
      mysql-test/suite/rpl/r/rpl_flushlog_loop.result
      mysql-test/suite/rpl/r/rpl_grant.result
      mysql-test/suite/rpl/r/rpl_incident.result
      mysql-test/suite/rpl/r/rpl_known_bugs_detection.result
      mysql-test/suite/rpl/r/rpl_loaddata_fatal.result
      mysql-test/suite/rpl/r/rpl_log_pos.result
      mysql-test/suite/rpl/r/rpl_rbr_to_sbr.result
      mysql-test/suite/rpl/r/rpl_replicate_do.result
      mysql-test/suite/rpl/r/rpl_rotate_logs.result
      mysql-test/suite/rpl/r/rpl_row_colSize.result
      mysql-test/suite/rpl/r/rpl_row_log.result
      mysql-test/suite/rpl/r/rpl_row_log_innodb.result
      mysql-test/suite/rpl/r/rpl_row_max_relay_size.result
      mysql-test/suite/rpl/r/rpl_row_reset_slave.result
      mysql-test/suite/rpl/r/rpl_row_tabledefs_2myisam.result
      mysql-test/suite/rpl/r/rpl_row_tabledefs_3innodb.result
      mysql-test/suite/rpl/r/rpl_row_until.result
      mysql-test/suite/rpl/r/rpl_skip_error.result
      mysql-test/suite/rpl/r/rpl_slave_load_remove_tmpfile.result
      mysql-test/suite/rpl/r/rpl_slave_skip.result
      mysql-test/suite/rpl/r/rpl_ssl.result
      mysql-test/suite/rpl/r/rpl_ssl1.result
      mysql-test/suite/rpl/r/rpl_stm_log.result
      mysql-test/suite/rpl/r/rpl_stm_max_relay_size.result
      mysql-test/suite/rpl/r/rpl_stm_reset_slave.result
      mysql-test/suite/rpl/r/rpl_stm_until.result
      mysql-test/suite/rpl/r/rpl_temporary_errors.result
      mysql-test/suite/rpl/t/rpl_bug33931.test
      mysql-test/suite/rpl/t/rpl_change_master.test
      mysql-test/suite/rpl/t/rpl_do_grant.test
      mysql-test/suite/rpl/t/rpl_empty_master_crash.test
      mysql-test/suite/rpl/t/rpl_filter_tables_not_exist.test
      mysql-test/suite/rpl/t/rpl_flushlog_loop.test
      mysql-test/suite/rpl/t/rpl_grant.test
      mysql-test/suite/rpl/t/rpl_incident.test
      mysql-test/suite/rpl/t/rpl_known_bugs_detection.test
      mysql-test/suite/rpl/t/rpl_loaddata_fatal.test
      mysql-test/suite/rpl/t/rpl_log_pos.test
      mysql-test/suite/rpl/t/rpl_rbr_to_sbr.test
      mysql-test/suite/rpl/t/rpl_replicate_do.test
      mysql-test/suite/rpl/t/rpl_rotate_logs.test
      mysql-test/suite/rpl/t/rpl_row_until.test
      mysql-test/suite/rpl/t/rpl_skip_error.test
      mysql-test/suite/rpl/t/rpl_slave_load_remove_tmpfile.test
      mysql-test/suite/rpl/t/rpl_slave_skip.test
      mysql-test/suite/rpl/t/rpl_ssl.test
      mysql-test/suite/rpl/t/rpl_ssl1.test
      mysql-test/suite/rpl/t/rpl_stm_until.test
      mysql-test/suite/rpl/t/rpl_temporary_errors.test
      mysql-test/suite/rpl_ndb/r/rpl_ndb_basic.result
      mysql-test/suite/rpl_ndb/r/rpl_ndb_circular.result
      mysql-test/suite/rpl_ndb/r/rpl_ndb_circular_simplex.result
      mysql-test/suite/rpl_ndb/r/rpl_ndb_extraCol.result
      mysql-test/suite/rpl_ndb/r/rpl_ndb_idempotent.result
      mysql-test/suite/rpl_ndb/r/rpl_ndb_log.result
      mysql-test/suite/rpl_ndb/r/rpl_ndb_sync.result
      mysql-test/suite/rpl_ndb/t/rpl_ndb_basic.test
      mysql-test/suite/rpl_ndb/t/rpl_ndb_circular.test
      mysql-test/suite/rpl_ndb/t/rpl_ndb_circular_simplex.test
      mysql-test/suite/rpl_ndb/t/rpl_ndb_idempotent.test
      mysql-test/suite/rpl_ndb/t/rpl_ndb_sync.test
=== added file 'mysql-test/extra/rpl_tests/delayed_slave_wait_on_query.inc'
--- a/mysql-test/extra/rpl_tests/delayed_slave_wait_on_query.inc	1970-01-01 00:00:00 +0000
+++ b/mysql-test/extra/rpl_tests/delayed_slave_wait_on_query.inc	2010-05-18 13:09:07 +0000
@@ -0,0 +1,39 @@
+# ==== Purpose ====
+#
+# Auxiliary file used by rpl_delayed_slave.test.  This assumes that an
+# 'INSERT INTO t1...' query has been executed on the master.  It does
+# this:
+#
+# - After half the delay, check the status. It should be delaying and
+#   the query should not have executed.
+#
+# - After one and a half delay, check the status. It should not be
+#   delaying and the query should be executed.
+#
+# ==== Usage ====
+#
+# --source extra/rpl_tests/delayed_slave_wait_on_query.inc
+
+connection master;
+--echo [on slave]
+--let $slave_timeout= $time1
+
+--source include/sync_slave_io_with_master.inc
+--echo # sleep 1*T
+--sleep $time1
+
+--echo # Expect query not executed and status is 'Waiting until MASTER_DELAY...'
+SELECT * FROM t1 ORDER BY b DESC LIMIT 1;
+--source include/show_delayed_slave_state.inc
+
+--echo # sleep 1*T
+--sleep $time1
+
+--echo # sync with master (with timeout 1*T)
+--source include/sync_with_master.inc
+
+--echo # Expect query executed and status is 'Has read all relay log...'
+SELECT * FROM t1 ORDER BY b DESC LIMIT 1;
+--source include/show_delayed_slave_state.inc
+
+--source include/check_slave_is_running.inc

=== added file 'mysql-test/include/save_master_pos.inc'
--- a/mysql-test/include/save_master_pos.inc	1970-01-01 00:00:00 +0000
+++ b/mysql-test/include/save_master_pos.inc	2010-05-18 13:09:07 +0000
@@ -0,0 +1,15 @@
+# ==== Purpose ====
+#
+# Save the master file and position of the current connection in
+# internal variables. This can later be used together with
+# sync_with_master.inc
+#
+# ==== Usage ====
+#
+# --source include/save_master_pos.inc
+#
+# This will save the binlog position of the current connection in the
+# mysqltest variables $_saved_master_file and $_saved_master_pos.
+
+let $_saved_master_file= query_get_value("SHOW MASTER STATUS", File, 1);
+let $_saved_master_pos= query_get_value("SHOW MASTER STATUS", Position, 1);

=== added file 'mysql-test/include/show_delayed_slave_state.inc'
--- a/mysql-test/include/show_delayed_slave_state.inc	1970-01-01 00:00:00 +0000
+++ b/mysql-test/include/show_delayed_slave_state.inc	2010-05-18 13:09:07 +0000
@@ -0,0 +1,28 @@
+# ==== Purpose ====
+#
+# Display the delay state of the SQL thread.
+#
+# ==== Usage ====
+#
+# --let $verbose_delayed_slave_state= [0|1]
+# --source extra/rpl_tests/show_delayed_slave_state.inc
+#
+# By default, the output is normalized so that it does not depend on
+# exact timing or exact binlog positions. If
+# $verbose_delayed_slave_state is set, then it outputs exact times and
+# binlog positions. This can be useful for debugging.
+
+--let $_delayed_slave_status= query_get_value(SHOW SLAVE STATUS, Slave_SQL_Running_State, 1)
+
+--let $_delayed_slave_remaining_delay= query_get_value(SHOW SLAVE STATUS, SQL_Remaining_Delay, 1)
+--let $_delayed_slave_qualitative_delay= `SELECT CASE WHEN "$_delayed_slave_remaining_delay" = "NULL" THEN "NULL" WHEN "$_delayed_slave_remaining_delay" = "0" THEN "0" ELSE "greater than zero" END`
+
+--let $_delayed_slave_io_pos= query_get_value(SHOW SLAVE STATUS, Read_Master_Log_Pos, 1)
+--let $_delayed_slave_sql_pos= query_get_value(SHOW SLAVE STATUS, Exec_Master_Log_Pos, 1)
+--let $_delayed_slave_qualitative_log_pos= `SELECT IF($_delayed_slave_io_pos > $_delayed_slave_sql_pos, "behind", "in sync with")`
+
+--echo Slave_SQL_Running_State='$_delayed_slave_status'; SQL_Remaining_Delay is $_delayed_slave_qualitative_delay; SQL thread is $_delayed_slave_qualitative_log_pos IO thread
+
+if ($verbose_delayed_slave_state) {
+  --echo SQL_Remaining_Delay='$_delayed_slave_remaining_delay'; Read_master_log_pos='$_delayed_slave_io_pos'; Exec_Master_Log_Pos='$_delayed_slave_sql_pos'
+}

=== modified file 'mysql-test/include/sync_slave_io_with_master.inc'
--- a/mysql-test/include/sync_slave_io_with_master.inc	2009-01-09 14:12:31 +0000
+++ b/mysql-test/include/sync_slave_io_with_master.inc	2010-05-18 13:09:07 +0000
@@ -18,19 +18,18 @@
 # $master_connection. See wait_for_slave_param.inc for
 # descriptions.
 
-let $_master_file= query_get_value("SHOW MASTER STATUS", File, 1);
-let $_master_pos= query_get_value("SHOW MASTER STATUS", Position, 1);
+--source include/save_master_pos.inc
 
 connection slave;
 
 let $slave_error_message= Failed while waiting for slave IO thread to sync;
 
 let $slave_param= Master_Log_File;
-let $slave_param_value= $_master_file;
+let $slave_param_value= $_saved_master_file;
 source include/wait_for_slave_param.inc;
 
 let $slave_param= Read_Master_Log_Pos;
-let $slave_param_value= $_master_pos;
+let $slave_param_value= $_saved_master_pos;
 source include/wait_for_slave_param.inc;
 
 let $slave_error_message= ;

=== added file 'mysql-test/include/sync_with_master.inc'
--- a/mysql-test/include/sync_with_master.inc	1970-01-01 00:00:00 +0000
+++ b/mysql-test/include/sync_with_master.inc	2010-05-18 13:09:07 +0000
@@ -0,0 +1,26 @@
+# ==== Purpose ====
+#
+# This file does the same as the built-in command sync_with_master,
+# but can be configured to use a custom timeout.  This has the benefit
+# that it accepts the same $slave_timeout and $master_connection
+# parameters as wait_for_slave_param.inc
+#
+#
+# ==== Usage ====
+#
+# --connection master
+# --source include/save_master_pos.inc
+# --connection slave
+# --source include/sync_with_master.inc
+#
+# Parameters to this macro are $slave_timeout and
+# $master_connection. See wait_for_slave_param.inc for
+# descriptions.
+
+--let $slave_param= Relay_Master_Log_File
+--let $slave_param_value= $_saved_master_file
+--source include/wait_for_slave_param.inc
+
+--let $slave_param= Exec_Master_Log_Pos
+--let $slave_param_value= $_saved_master_pos
+--source include/wait_for_slave_param.inc

=== added file 'mysql-test/std_data/old-format_relay-log.info'
--- a/mysql-test/std_data/old-format_relay-log.info	1970-01-01 00:00:00 +0000
+++ b/mysql-test/std_data/old-format_relay-log.info	2010-05-18 13:09:07 +0000
@@ -0,0 +1,4 @@
+./slave-relay-bin.000001
+4
+
+0

=== added file 'mysql-test/suite/rpl/r/rpl_delayed_slave.result'
--- a/mysql-test/suite/rpl/r/rpl_delayed_slave.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/r/rpl_delayed_slave.result	2010-05-18 13:09:07 +0000
@@ -0,0 +1,162 @@
+stop slave;
+drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
+reset master;
+reset slave;
+drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
+start slave;
+[on master]
+CREATE TABLE t1 (a VARCHAR(100), b INT AUTO_INCREMENT PRIMARY KEY);
+==== Normal setup ====
+[on slave]
+include/stop_slave.inc
+# CHANGE MASTER TO MASTER_DELAY = 2*T
+# Checking that delay is what we set it to
+# Expect status to be ''
+SELECT STATE FROM INFORMATION_SCHEMA.PROCESSLIST ORDER BY ID DESC LIMIT 1;
+STATE
+
+include/start_slave.inc
+[on master]
+INSERT INTO t1(a) VALUES ('normal setup');
+[on slave]
+# sleep 1*T
+# Expect query not executed and status is 'Waiting until MASTER_DELAY...'
+SELECT * FROM t1 ORDER BY b DESC LIMIT 1;
+a	b
+Slave_SQL_Running_State='Waiting until MASTER_DELAY seconds after master executed event'; SQL_Remaining_Delay is greater than zero; SQL thread is behind IO thread
+# sleep 1*T
+# sync with master (with timeout 1*T)
+# Expect query executed and status is 'Has read all relay log...'
+SELECT * FROM t1 ORDER BY b DESC LIMIT 1;
+a	b
+normal setup	1
+Slave_SQL_Running_State='Has read all relay log; waiting for the slave I/O thread to update it'; SQL_Remaining_Delay is NULL; SQL thread is in sync with IO thread
+Checking that both slave threads are running.
+==== Slave lags "naturally" after master ====
+[on master]
+# CREATE FUNCTION delay_on_slave(time_units INT) RETURNS INT BEGIN IF @@server_id = 2 THEN RETURN SLEEP(time_units * T); ELSE RETURN 0; END IF; END
+INSERT INTO t1(a) SELECT delay_on_slave(3);
+Warnings:
+Note	1592	Statement may not be safe to log in statement format.
+INSERT INTO t1(a) VALUES ('slave is already lagging: this statement should execute immediately');
+INSERT INTO t1(a) SELECT delay_on_slave(2);
+Warnings:
+Note	1592	Statement may not be safe to log in statement format.
+[on slave]
+# sleep 1*T
+# Expect no query executed and status is 'Waiting until MASTER_DELAY...'
+SELECT * FROM t1 ORDER BY b DESC LIMIT 1;
+a	b
+normal setup	1
+Slave_SQL_Running_State='Waiting until MASTER_DELAY seconds after master executed event'; SQL_Remaining_Delay is greater than zero; SQL thread is behind IO thread
+# wait for first query to execute
+# sleep 1*T
+# Expect second query executed and status is executing third query (i.e., 'User sleep')
+SELECT * FROM t1 ORDER BY b DESC LIMIT 1;
+a	b
+slave is already lagging: this statement should execute immediately	3
+Slave_SQL_Running_State='User sleep'; SQL_Remaining_Delay is NULL; SQL thread is behind IO thread
+# sleep 2*T
+# Expect query executed and status is 'Has read all relay log...'
+SELECT * FROM t1 ORDER BY b DESC LIMIT 1;
+a	b
+0	4
+Slave_SQL_Running_State='Has read all relay log; waiting for the slave I/O thread to update it'; SQL_Remaining_Delay is NULL; SQL thread is in sync with IO thread
+==== Seconds_Behind_Master ====
+# Bring slave to sync.
+include/stop_slave.inc
+CHANGE MASTER TO MASTER_DELAY = 0;
+include/start_slave.inc
+INSERT INTO t1(a) VALUES ('Syncing slave');
+include/stop_slave.inc
+# CHANGE MASTER TO MASTER_DELAY = 2*T
+include/start_slave.inc
+INSERT INTO t1(a) VALUES (delay_on_slave(1));
+Warnings:
+Note	1592	Statement may not be safe to log in statement format.
+# sleep 1*T
+# sleep 1*T
+==== STOP SLAVE and START SLAVE ====
+include/stop_slave.inc
+# CHANGE MASTER TO MASTER_DELAY = 3*T
+include/start_slave.inc
+# Checking that delay is what we set it to
+[on master]
+INSERT INTO t1(a) VALUES ('stop slave and start slave');
+[on slave]
+# sleep 1*T
+SET @before_stop_slave= UNIX_TIMESTAMP();
+include/stop_slave.inc
+# STOP SLAVE finished in time.
+# Expect query not executed and status is ''
+SELECT * FROM t1 ORDER BY b DESC LIMIT 1;
+a	b
+0	6
+Slave_SQL_Running_State=''; SQL_Remaining_Delay is NULL; SQL thread is behind IO thread
+include/start_slave.inc
+# START SLAVE finished in time.
+[on slave]
+# sleep 1*T
+# Expect query not executed and status is 'Waiting until MASTER_DELAY...'
+SELECT * FROM t1 ORDER BY b DESC LIMIT 1;
+a	b
+0	6
+Slave_SQL_Running_State='Waiting until MASTER_DELAY seconds after master executed event'; SQL_Remaining_Delay is greater than zero; SQL thread is behind IO thread
+# sleep 1*T
+# sync with master (with timeout 1*T)
+# Expect query executed and status is 'Has read all relay log...'
+SELECT * FROM t1 ORDER BY b DESC LIMIT 1;
+a	b
+stop slave and start slave	7
+Slave_SQL_Running_State='Has read all relay log; waiting for the slave I/O thread to update it'; SQL_Remaining_Delay is NULL; SQL thread is in sync with IO thread
+Checking that both slave threads are running.
+==== Change back to no delay ====
+[on slave]
+include/stop_slave.inc
+CHANGE MASTER TO MASTER_DELAY = 0;
+# Expect delay is 0.
+SQL_Delay='0'
+include/start_slave.inc
+[on master]
+INSERT INTO t1(a) VALUES ('change back to no delay');
+[on slave]
+# sleep 1*T
+# Expect query executed and status is 'Has read all relay log...'
+SELECT * FROM t1 ORDER BY b DESC LIMIT 1;
+a	b
+change back to no delay	8
+Slave_SQL_Running_State='Has read all relay log; waiting for the slave I/O thread to update it'; SQL_Remaining_Delay is NULL; SQL thread is in sync with IO thread
+==== Reset delay with RESET SLAVE ====
+include/stop_slave.inc
+CHANGE MASTER TO MASTER_DELAY = 71;
+include/start_slave.inc
+# Expect delay is 71
+SQL_Delay='71'
+include/stop_slave.inc
+RESET SLAVE;
+[on master]
+RESET MASTER;
+[on slave]
+include/start_slave.inc
+# Expect delay is 0
+SQL_Delay='0'
+==== Set a bad value for the delay ====
+include/stop_slave.inc
+# Expect error for setting negative delay
+CHANGE MASTER TO MASTER_DELAY = -1;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '-1' at line 1
+# Expect that it's ok to set delay of 2^31-1
+CHANGE MASTER TO MASTER_DELAY = 2147483647;
+# Expect error for setting delay between 2^31 and 2^32-1
+CHANGE MASTER TO MASTER_DELAY = 2147483648;
+ERROR HY000: The requested value 2147483648 for the master delay exceeds the maximum 2147483647
+# Expect error for setting delay to nonsense
+CHANGE MASTER TO MASTER_DELAY = blah;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'blah' at line 1
+CHANGE MASTER TO MASTER_DELAY = 0;
+include/start_slave.inc
+==== Clean up ====
+[on master]
+DROP TABLE t1;
+DROP FUNCTION delay_on_slave;
+[on slave]

=== added file 'mysql-test/suite/rpl/r/rpl_read_old_relay_log_info.result'
--- a/mysql-test/suite/rpl/r/rpl_read_old_relay_log_info.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/r/rpl_read_old_relay_log_info.result	2010-05-18 13:09:07 +0000
@@ -0,0 +1,16 @@
+stop slave;
+drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
+reset master;
+reset slave;
+drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
+start slave;
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1);
+DROP TABLE t1;
+==== Check that we still understand the old format of relay-log.info ====
+include/stop_slave.inc
+RESET SLAVE;
+# Read relay-log.info
+START SLAVE IO_THREAD;
+# Check that relay log coordinates are equal to those we saved in old-format_relay-log.info
+= , 0, slave-relay-bin.000001, 4

=== added file 'mysql-test/suite/rpl/t/rpl_delayed_slave.test'
--- a/mysql-test/suite/rpl/t/rpl_delayed_slave.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/t/rpl_delayed_slave.test	2010-05-18 13:09:07 +0000
@@ -0,0 +1,342 @@
+# ==== Purpose ====
+#
+# Test the time-delayed replication feature, i.e.,
+# CHANGE MASTER TO MASTER_DELAY=X:
+#
+#  - Verify that slave has executed the events after but not before the
+#    delay timeout.
+#
+#  - Verify that delay is correct works when slave is already lagging
+#    due to slow queries.
+#
+#  - Verify that Seconds_Behind_Master is greater than or equal to the
+#    delay if the slave still has unprocessed events in the relay log
+#    and more time than the delay has elapsed since the last event was
+#    executed on the master.
+#
+#  - Verify that STOP SLAVE works instantly even during a delay, and
+#    that it does not cause the waited-for event to be executed too
+#    early on slave.
+#
+#  - Verify that changing back to no delay works.
+#
+#  - Verify that RESET SLAVE sets the delay to 0.
+#
+#  - Verify that setting a bad value for the delay gives an error.
+#
+# ==== Implementation ====
+#
+# 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 that a query has been executed after this
+# time, we wait 10 seconds more.
+#
+# To simulate that the slave lags due to slow queries, we invoke a
+# stored function that executes SLEEP if @@server_id==2. This requires
+# that we run with binlog_format=STATEMENT.
+#
+# ==== 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 ====
+#
+# The test is inherently timing-sensitive (i.e., contains races) and
+# is likely to fail sporadically on a loaded host.
+#
+# The test takes a long time; it sleeps for around 20*10 seconds.
+
+--source include/master-slave.inc
+# Needed so that sleeps get executed in the slave SQL thread.
+--source include/have_binlog_format_statement.inc
+
+
+# We assume that any simple operation takes zero time, with an error
+# margin of $time1 seconds. Hence, if we run with a delay of $time2
+# seconds, we expect that:
+#  - If we execute a query on master and wait $time1 seconds, then the
+#    query has been copied to slave but not yet executed.
+#  - If we execute a query on master and wait $time3 seconds, then the
+#    query has been executed.
+--let $time1= 10
+if (`SELECT '$max_query_execution_time' > 0`) {
+  --let $time1= $max_query_execution_time
+}
+--let $time2= `SELECT 2 * $time1`
+--let $time3= `SELECT 3 * $time1`
+
+
+--echo [on master]
+CREATE TABLE t1 (a VARCHAR(100), b INT AUTO_INCREMENT PRIMARY KEY);
+
+
+--echo ==== Normal setup ====
+
+--echo [on slave]
+--sync_slave_with_master
+
+--source include/stop_slave.inc
+
+--echo # CHANGE MASTER TO MASTER_DELAY = 2*T
+--disable_query_log
+eval CHANGE MASTER TO MASTER_DELAY = $time2;
+--enable_query_log
+
+--echo # Checking that delay is what we set it to
+--let $delay= query_get_value(SHOW SLAVE STATUS, SQL_Delay, 1)
+if (`SELECT $delay != $time2`) {
+  --echo Delay is wrong! Expected $time2, got $delay
+  --source include/show_rpl_debug_info.inc
+  --die wrong delay
+}
+
+--echo # Expect status to be ''
+SELECT STATE FROM INFORMATION_SCHEMA.PROCESSLIST ORDER BY ID DESC LIMIT 1;
+
+--source include/start_slave.inc
+
+--echo [on master]
+--connection master
+INSERT INTO t1(a) VALUES ('normal setup');
+
+--source extra/rpl_tests/delayed_slave_wait_on_query.inc
+
+
+--echo ==== Slave lags "naturally" after master ====
+
+--echo [on master]
+--connection master
+
+--disable_query_log
+--echo # CREATE FUNCTION delay_on_slave(time_units INT) RETURNS INT BEGIN IF @@server_id = 2 THEN RETURN SLEEP(time_units * T); ELSE RETURN 0; END IF; END
+--eval CREATE FUNCTION delay_on_slave(time_units INT) RETURNS INT BEGIN IF @@server_id = 2 THEN RETURN SLEEP(time_units * $time1); ELSE RETURN 0; END IF; END
+--enable_query_log
+
+INSERT INTO t1(a) SELECT delay_on_slave(3);
+
+--save_master_pos
+INSERT INTO t1(a) VALUES ('slave is already lagging: this statement should execute immediately');
+INSERT INTO t1(a) SELECT delay_on_slave(2);
+
+--echo [on slave]
+--source include/sync_slave_io_with_master.inc
+--echo # sleep 1*T
+--sleep $time1
+
+--echo # Expect no query executed and status is 'Waiting until MASTER_DELAY...'
+SELECT * FROM t1 ORDER BY b DESC LIMIT 1;
+--source include/show_delayed_slave_state.inc
+
+--echo # wait for first query to execute
+--sync_with_master
+
+--echo # sleep 1*T
+--sleep $time1
+
+--echo # Expect second query executed and status is executing third query (i.e., 'User sleep')
+SELECT * FROM t1 ORDER BY b DESC LIMIT 1;
+--source include/show_delayed_slave_state.inc
+
+--echo # sleep 2*T
+--sleep $time2
+
+--echo # Expect query executed and status is 'Has read all relay log...'
+SELECT * FROM t1 ORDER BY b DESC LIMIT 1;
+--source include/show_delayed_slave_state.inc
+
+
+--echo ==== Seconds_Behind_Master ====
+
+--echo # Bring slave to sync.
+--source include/stop_slave.inc
+eval CHANGE MASTER TO MASTER_DELAY = 0;
+--source include/start_slave.inc
+
+--connection master
+INSERT INTO t1(a) VALUES ('Syncing slave');
+--sync_slave_with_master
+
+--source include/stop_slave.inc
+--echo # CHANGE MASTER TO MASTER_DELAY = 2*T
+--disable_query_log
+eval CHANGE MASTER TO MASTER_DELAY = $time2;
+--enable_query_log
+--source include/start_slave.inc
+
+--connection master
+INSERT INTO t1(a) VALUES (delay_on_slave(1));
+--save_master_pos
+--connection slave
+
+--echo # sleep 1*T
+--sleep $time1
+
+if ($bug_53167_is_fixed) {
+
+--let $seconds_behind_master= query_get_value(SHOW SLAVE STATUS, Seconds_Behind_Master, 1)
+if (`SELECT $seconds_behind_master <= 0 OR $seconds_behind_master >= $time2`) {
+  --echo Seconds_Behind_Master was $seconds_behind_master. Expected that 0 < Seconds_Behind_Master < SQL_Delay = $time2
+  --source include/show_rpl_debug_info.inc
+  --die Seconds_Behind_Master was wrong
+}
+
+}
+
+--echo # sleep 1*T
+--sleep $time1
+
+--let $seconds_behind_master= query_get_value(SHOW SLAVE STATUS, Seconds_Behind_Master, 1)
+if (`SELECT $seconds_behind_master < $time2`) {
+  --echo Seconds_Behind_Master was $seconds_behind_master. Expected it to be >= SQL_Delay = $time2
+  --source include/show_rpl_debug_info.inc
+  --die Seconds_Behind_Master was < SQL_Delay
+}
+
+--sync_with_master
+
+
+--echo ==== STOP SLAVE and START SLAVE ====
+
+# Set up a longer delay.
+--source include/stop_slave.inc
+
+--echo # CHANGE MASTER TO MASTER_DELAY = 3*T
+--disable_query_log
+eval CHANGE MASTER TO MASTER_DELAY = $time3;
+--enable_query_log
+
+--source include/start_slave.inc
+
+--echo # Checking that delay is what we set it to
+--let $delay= query_get_value(SHOW SLAVE STATUS, SQL_Delay, 1)
+if (`SELECT $delay != $time3`) {
+  --echo Delay is wrong! Expected $time2, got $delay
+  --source include/show_rpl_debug_info.inc
+  --die wrong delay
+}
+
+--echo [on master]
+--connection master
+INSERT INTO t1(a) VALUES ('stop slave and start slave');
+
+--echo [on slave]
+--connection slave
+--echo # sleep 1*T
+--sleep $time1
+SET @before_stop_slave= UNIX_TIMESTAMP();
+--source include/stop_slave.inc
+if (`SELECT UNIX_TIMESTAMP() - @before_stop_slave >= $time1`)
+{
+  --source include/show_rpl_debug_info.inc
+  --die STOP SLAVE did not finish in time
+}
+--echo # STOP SLAVE finished in time.
+
+--echo # Expect query not executed and status is ''
+SELECT * FROM t1 ORDER BY b DESC LIMIT 1;
+--source include/show_delayed_slave_state.inc
+
+--source include/start_slave.inc
+if (`SELECT UNIX_TIMESTAMP() - @before_stop_slave >= $time1`)
+{
+  --source include/show_rpl_debug_info.inc
+  --die START SLAVE did not finish in time
+}
+--echo # START SLAVE finished in time.
+
+--source extra/rpl_tests/delayed_slave_wait_on_query.inc
+
+
+--echo ==== Change back to no delay ====
+
+--echo [on slave]
+--connection slave
+--source include/stop_slave.inc
+eval CHANGE MASTER TO MASTER_DELAY = 0;
+
+--echo # Expect delay is 0.
+--let $delay= query_get_value(SHOW SLAVE STATUS, SQL_Delay, 1)
+--echo SQL_Delay='$delay'
+
+--source include/start_slave.inc
+
+--echo [on master]
+--connection master
+INSERT INTO t1(a) VALUES ('change back to no delay');
+
+--echo [on slave]
+--source include/sync_slave_io_with_master.inc
+--echo # sleep 1*T
+--sleep $time1
+
+--echo # Expect query executed and status is 'Has read all relay log...'
+SELECT * FROM t1 ORDER BY b DESC LIMIT 1;
+--source include/show_delayed_slave_state.inc
+
+
+--echo ==== Reset delay with RESET SLAVE ====
+
+--source include/stop_slave.inc
+CHANGE MASTER TO MASTER_DELAY = 71;
+--source include/start_slave.inc
+
+--echo # Expect delay is 71
+--let $delay= query_get_value(SHOW SLAVE STATUS, SQL_Delay, 1)
+--echo SQL_Delay='$delay'
+
+--source include/stop_slave.inc
+RESET SLAVE;
+--echo [on master]
+--connection master
+RESET MASTER;
+--echo [on slave]
+--connection slave
+--source include/start_slave.inc
+
+--echo # Expect delay is 0
+--let $delay= query_get_value(SHOW SLAVE STATUS, SQL_Delay, 1)
+--echo SQL_Delay='$delay'
+
+
+--echo ==== Set a bad value for the delay ====
+
+--source include/stop_slave.inc
+
+--echo # Expect error for setting negative delay
+--error ER_PARSE_ERROR
+CHANGE MASTER TO MASTER_DELAY = -1;
+
+--echo # Expect that it's ok to set delay of 2^31-1
+CHANGE MASTER TO MASTER_DELAY = 2147483647;
+--echo # Expect error for setting delay between 2^31 and 2^32-1
+--error ER_MASTER_DELAY_VALUE_OUT_OF_RANGE
+CHANGE MASTER TO MASTER_DELAY = 2147483648;
+
+--echo # Expect error for setting delay to nonsense
+--error ER_PARSE_ERROR
+CHANGE MASTER TO MASTER_DELAY = blah;
+
+# todo: CHANGE MASTER TO MASTER_DELAY = 999999999999999999999999999
+# should give error
+
+CHANGE MASTER TO MASTER_DELAY = 0;
+--source include/start_slave.inc
+
+
+--echo ==== Clean up ====
+
+--echo [on master]
+--connection master
+DROP TABLE t1;
+DROP FUNCTION delay_on_slave;
+
+--echo [on slave]
+--sync_slave_with_master
+
+--source include/master-slave-end.inc

=== added file 'mysql-test/suite/rpl/t/rpl_read_old_relay_log_info.test'
--- a/mysql-test/suite/rpl/t/rpl_read_old_relay_log_info.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/t/rpl_read_old_relay_log_info.test	2010-05-18 13:09:07 +0000
@@ -0,0 +1,36 @@
+# ==== Purpose ====
+#
+#  - Verify that the pre-WL#344 format of relay_log.info can still be
+#    parsed.
+
+--source include/master-slave.inc
+
+CREATE TABLE t1 (a INT);
+INSERT INTO t1 VALUES (1);
+DROP TABLE t1;
+--sync_slave_with_master
+
+--echo ==== Check that we still understand the old format of relay-log.info ====
+--source include/stop_slave.inc
+
+RESET SLAVE;
+let $MYSQLD_DATADIR= `select @@datadir`;
+--copy_file $MYSQL_TEST_DIR/std_data/old-format_relay-log.info $MYSQLD_DATADIR/relay-log.info
+
+--echo # Read relay-log.info
+START SLAVE IO_THREAD;
+--source include/wait_for_slave_io_to_start.inc
+--echo # Check that relay log coordinates are equal to those we saved in old-format_relay-log.info
+--let $master_file= query_get_value(SHOW SLAVE STATUS, Relay_Master_Log_File, 1)
+--let $master_pos= query_get_value(SHOW SLAVE STATUS, Exec_Master_Log_Pos, 1)
+--let $relay_log_file= query_get_value(SHOW SLAVE STATUS, Relay_Log_File, 1)
+--let $relay_log_pos= query_get_value(SHOW SLAVE STATUS, Relay_Log_Pos, 1)
+--echo $master_file= $master_file, $master_pos, $relay_log_file, $relay_log_pos
+if (`SELECT "$master_file" != "" OR
+            "$master_pos" != "0" OR
+            "$relay_log_file" != "slave-relay-bin.000001" OR
+            "$relay_log_pos" != "4"`) {
+  --echo ERROR: log coordinates changed
+  --die log coordinates changed
+}
+

=== modified file 'sql/lex.h'
--- a/sql/lex.h	2009-07-29 08:54:20 +0000
+++ b/sql/lex.h	2010-05-18 13:09:07 +0000
@@ -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)},

=== modified file 'sql/log.cc'
--- a/sql/log.cc	2010-03-28 11:57:33 +0000
+++ b/sql/log.cc	2010-05-18 13:09:07 +0000
@@ -3084,6 +3084,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
@@ -3097,7 +3101,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
@@ -3126,6 +3130,8 @@ int MYSQL_BIN_LOG::purge_first_log(Relay
   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);
   to_purge_if_included= my_strdup(rli->group_relay_log_name, MYF(0));
 

=== modified file 'sql/log_event.cc'
--- a/sql/log_event.cc	2010-03-28 11:57:33 +0000
+++ b/sql/log_event.cc	2010-05-18 13:09:07 +0000
@@ -767,6 +767,7 @@ Log_event::Log_event(const char* buf,
 
 int Log_event::do_update_pos(Relay_log_info *rli)
 {
+  DBUG_ASSERT(!rli->belongs_to_client());
   /*
     rli is null when (as far as I (Guilhem) know) the caller is
     Load_log_event::do_apply_event *and* that one is called from
@@ -4932,6 +4933,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
 */
@@ -5944,6 +5948,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)
 {

=== modified file 'sql/rpl_mi.cc'
--- a/sql/rpl_mi.cc	2007-12-14 13:21:37 +0000
+++ b/sql/rpl_mi.cc	2010-05-18 13:09:07 +0000
@@ -18,15 +18,11 @@
 #include <my_dir.h>
 
 #include "rpl_mi.h"
+#include "slave.h"
 
 #ifdef HAVE_REPLICATION
 
 
-// Defined in slave.cc
-int init_intvar_from_file(int* var, IO_CACHE* f, int default_val);
-int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
-			  const char *default_val);
-
 Master_info::Master_info()
   :Slave_reporting_capability("I/O"),
    ssl(0), ssl_verify_server_cert(0), fd(-1), io_thd(0), inited(0),

=== modified file 'sql/rpl_rli.cc'
--- a/sql/rpl_rli.cc	2009-12-14 16:50:22 +0000
+++ b/sql/rpl_rli.cc	2010-05-18 13:09:07 +0000
@@ -20,30 +20,27 @@
 #include <my_dir.h>    // For MY_STAT
 #include "sql_repl.h"  // For check_binlog_magic
 #include "rpl_utility.h"
+#include "slave.h"
 
 static int count_relay_log_space(Relay_log_info* rli);
 
-// Defined in slave.cc
-int init_intvar_from_file(int* var, IO_CACHE* f, int default_val);
-int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
-			  const char *default_val);
+const char *const Relay_log_info::state_delaying_string = "Waiting until MASTER_DELAY seconds after master executed event";
 
 
 Relay_log_info::Relay_log_info()
   :Slave_reporting_capability("SQL"),
-   no_storage(FALSE), replicate_same_server_id(::replicate_same_server_id),
+   replicate_same_server_id(::replicate_same_server_id),
    info_fd(-1), cur_log_fd(-1), save_temporary_tables(0),
    cur_log_old_open_count(0), group_relay_log_pos(0), event_relay_log_pos(0),
-#if HAVE_purify
-   is_fake(FALSE),
-#endif
    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),
+   sql_delay(0), sql_delay_end(0),
+   m_flags(0)
 {
   DBUG_ENTER("Relay_log_info::Relay_log_info");
 
@@ -81,15 +78,49 @@ Relay_log_info::~Relay_log_info()
 }
 
 
+/**
+  Wrapper around Relay_log_info::init(const char *).
+
+  @todo Remove this and replace all calls to it by calls to
+  Relay_log_info::init(const char *). /SVEN
+*/
 int init_relay_log_info(Relay_log_info* rli,
 			const char* info_fname)
 {
+  return rli->init(info_fname);
+}
+
+
+/**
+  Read the relay_log.info file.
+
+  @param info_fname The name of the file to read from.
+  @retval 0 success
+  @retval 1 failure
+*/
+int Relay_log_info::init(const char* info_fname)
+{
   char fname[FN_REFLEN+128];
   int info_fd;
   const char* msg = 0;
   int error = 0;
   DBUG_ENTER("init_relay_log_info");
-  DBUG_ASSERT(!rli->no_storage);         // Don't init if there is no storage
+  Relay_log_info *rli= this; // todo: remove useless local copy of 'this' pointer
+
+  /*
+    Should not read RLI from file in client threads. Client threads
+    only use RLI to execute BINLOG statements.
+
+    @todo Uncomment the following assertion. Currently,
+    Relay_log_info::init() is called from init_master_info() before
+    the THD object Relay_log_info::sql_thd is created. That means we
+    cannot call belongs_to_client() since belongs_to_client()
+    dereferences Relay_log_info::sql_thd. So we need to refactor
+    slightly: the THD object should be created by Relay_log_info
+    constructor (or passed to it), so that we are guaranteed that it
+    exists at this point. /Sven
+  */
+  // DBUG_ASSERT(!rli->belongs_to_client());
 
   if (rli->inited)                       // Set if this function called
     DBUG_RETURN(0);
@@ -263,20 +294,72 @@ Failed to open the existing relay log in
     }
 
     rli->info_fd = info_fd;
-    int relay_log_pos, master_log_pos;
+    int relay_log_pos, master_log_pos, lines;
+    char *first_non_digit;
+
+    /*
+      Starting from 5.1.x, relay-log.info has a new format. Now, its
+      first line contains the number of lines in the file. By reading
+      this number we can determine which version our master.info comes
+      from. We can't simply count the lines in the file, since
+      versions before 5.1.x could generate files with more lines than
+      needed. If first line doesn't contain a number, or if it
+      contains a number less than LINES_IN_RELAY_LOG_INFO_WITH_DELAY,
+      then the file is treated like a file from pre-5.1.x version.
+      There is no ambiguity when reading an old master.info: before
+      5.1.x, the first line contained the binlog's name, which is
+      either empty or has an extension (contains a '.'), so can't be
+      confused with an integer.
+
+      So we're just reading first line and trying to figure which
+      version is this.
+    */
+
+    /*
+      The first row is temporarily stored in mi->master_log_name, if
+      it is line count and not binlog name (new format) it will be
+      overwritten by the second row later.
+    */
     if (init_strvar_from_file(rli->group_relay_log_name,
                               sizeof(rli->group_relay_log_name),
+                              &rli->info_file, ""))
+    {
+      msg="Error reading slave log configuration";
+      goto err;
+    }
+
+    lines= strtoul(rli->group_relay_log_name, &first_non_digit, 10);
+
+    if (rli->group_relay_log_name[0] != '\0' &&
+        *first_non_digit == '\0' &&
+        lines >= LINES_IN_RELAY_LOG_INFO_WITH_DELAY)
+    {
+      DBUG_PRINT("info", ("relay_log_info file is in new format."));
+      /* Seems to be new format => read relay log name from next line */
+      if (init_strvar_from_file(rli->group_relay_log_name,
+                                sizeof(rli->group_relay_log_name),
+                                &rli->info_file, ""))
+      {
+        msg="Error reading slave log configuration";
+        goto err;
+      }
+    }
+    else
+      DBUG_PRINT("info", ("relay_log_info file is in old format."));
+
+    if (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(&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(&master_log_pos, &rli->info_file, 0) ||
+        (lines >= LINES_IN_RELAY_LOG_INFO_WITH_DELAY &&
+         init_intvar_from_file(&rli->sql_delay, &rli->info_file, 0)))
     {
       msg="Error reading slave log configuration";
       goto err;
     }
+
     strmake(rli->event_relay_log_name,rli->group_relay_log_name,
             sizeof(rli->event_relay_log_name)-1);
     rli->group_relay_log_pos= rli->event_relay_log_pos= relay_log_pos;
@@ -611,6 +694,8 @@ err:
   if (!rli->relay_log.description_event_for_exec->is_valid() && !*errmsg)
     *errmsg= "Invalid Format_description log event; could be out of memory";
 
+  DBUG_PRINT("info", ("Returning %d from init_relay_log_pos", (*errmsg)?1:0));
+
   DBUG_RETURN ((*errmsg) ? 1 : 0);
 }
 
@@ -825,7 +910,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;
@@ -1137,6 +1224,7 @@ void Relay_log_info::stmt_done(my_off_t 
   extern uint debug_not_change_ts_if_art_event;
 #endif
   clear_flag(IN_STMT);
+  DBUG_ASSERT(!belongs_to_client());
 
   /*
     If in a transaction, and if the slave supports transactions, just

=== modified file 'sql/rpl_rli.h'
--- a/sql/rpl_rli.h	2009-12-14 16:32:22 +0000
+++ b/sql/rpl_rli.h	2010-05-18 13:09:07 +0000
@@ -64,11 +64,17 @@ public:
   };
 
   /*
-    If flag set, then rli does not store its state in any info file.
-    This is the case only when we execute BINLOG SQL commands inside
-    a client, non-replication thread.
+    The SQL thread owns one Relay_log_info, and each client that has
+    executed a BINLOG statement owns one Relay_log_info. This function
+    returns zero for the Relay_log_info object that belongs to the SQL
+    thread and nonzero for Relay_log_info objects that belong to
+    clients.
   */
-  bool no_storage;
+  inline bool belongs_to_client()
+  {
+    DBUG_ASSERT(sql_thd);
+    return !sql_thd->slave_thread;
+  }
 
   /*
     If true, events with the same server id should be replicated. This
@@ -147,6 +153,11 @@ 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
+    !belongs_to_client(); client thread executing BINLOG statement if
+    belongs_to_client()).
   */
   char group_relay_log_name[FN_REFLEN];
   ulonglong group_relay_log_pos;
@@ -154,16 +165,17 @@ 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
-
   /* 
      Original log name and position of the group we're currently executing
      (whose coordinates are group_relay_log_name/pos in the relay log)
      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
+    !belongs_to_client(); client thread executing BINLOG statement if
+    belongs_to_client()).
   */
   char group_master_log_name[FN_REFLEN];
   volatile my_off_t group_master_log_pos;
@@ -192,6 +204,15 @@ public:
   time_t last_master_timestamp;
 
   void clear_until_condition();
+  /**
+    Reset the delay.
+    This is used by RESET SLAVE to clear the delay.
+  */
+  void clear_sql_delay()
+  {
+    sql_delay= 0;
+  }
+
 
   /*
     Needed for problems when slave stops and we want to restart it
@@ -296,7 +317,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);
@@ -413,12 +434,83 @@ public:
       (m_flags & (1UL << IN_STMT));
   }
 
+  /**
+    Text used in THD::proc_info when the slave SQL thread is delaying.
+  */
+  static const char *const state_delaying_string;
+
+  bool flush();
+
+  /**
+    Reads the relay_log.info file.
+  */
+  int init(const char* info_filename);
+
+  /**
+    Indicate that a delay starts.
+
+    This does not actually sleep; it only sets the state of this
+    Relay_log_info object to delaying so that the correct state can be
+    reported by SHOW SLAVE STATUS and SHOW PROCESSLIST.
+
+    Requires rli->data_lock.
+
+    @param delay_end The time when the delay shall end.
+  */
+  void start_sql_delay(time_t delay_end)
+  {
+    safe_mutex_assert_owner(&data_lock);
+    sql_delay_end= delay_end;
+    thd_proc_info(sql_thd, state_delaying_string);
+  }
+
+  int32 get_sql_delay() { return sql_delay; }
+  void set_sql_delay(time_t _sql_delay) { sql_delay= _sql_delay; }
+  time_t get_sql_delay_end() { return sql_delay_end; }
+
 private:
+
+  /**
+    Delay slave SQL thread 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 sql_delay;
+
+  /**
+    During a delay, specifies the point in time when the delay ends.
+
+    This is used for the SQL_Remaining_Delay column in SHOW SLAVE STATUS.
+
+    Guarded by data_lock. Written by the sql thread.  Read by client
+    threads executing SHOW SLAVE STATUS.
+  */
+  time_t sql_delay_end;
+
+  /*
+    Before the MASTER_DELAY parameter was added (WL#344),
+    relay_log.info had 4 lines. Now it has 5 lines.
+  */
+  static const int LINES_IN_RELAY_LOG_INFO_WITH_DELAY= 5;
+
   uint32 m_flags;
 };
 
 
-// Defined in rpl_rli.cc
+/**
+  Reads the relay_log.info file.
+
+  @todo This is a wrapper around Relay_log_info::init(). It's only
+  kept for historical reasons. It would be good if we removed this
+  function and replaced all calls to it by calls to
+  Relay_log_info::init(). /SVEN
+*/
 int init_relay_log_info(Relay_log_info* rli, const char* info_fname);
 
 

=== modified file 'sql/share/errmsg.txt'
--- a/sql/share/errmsg.txt	2010-02-09 10:30:50 +0000
+++ b/sql/share/errmsg.txt	2010-05-18 13:09:07 +0000
@@ -6213,3 +6213,8 @@ ER_DEBUG_SYNC_TIMEOUT
 ER_DEBUG_SYNC_HIT_LIMIT
   eng "debug sync point hit limit reached"
   ger "Debug Sync Point Hit Limit erreicht"
+
+ER_MASTER_DELAY_VALUE_OUT_OF_RANGE
+  eng "The requested value %u for the master delay 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)"

=== modified file 'sql/slave.cc'
--- a/sql/slave.cc	2010-03-19 09:06:40 +0000
+++ b/sql/slave.cc	2010-05-18 13:09:07 +0000
@@ -997,8 +997,10 @@ 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 (is_network_error(mysql_errno(mysql)))
   {
@@ -1008,7 +1010,9 @@ static int get_master_version_and_clock(
   }
   else 
   {
+    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)",
@@ -1623,6 +1627,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("SQL_Delay", 10, MYSQL_TYPE_LONG));
+  field_list.push_back(new Item_return_int("SQL_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))
@@ -1744,6 +1751,21 @@ 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);
+    // SQL_Delay
+    protocol->store((uint32) mi->rli.get_sql_delay());
+    // SQL_Remaining_Delay
+    // THD::proc_info is not protected by any lock, so we read it once
+    // to ensure that we use the same value throughout this function.
+    const char *slave_sql_running_state= mi->rli.sql_thd ? mi->rli.sql_thd->proc_info : "";
+    if (slave_sql_running_state == Relay_log_info::state_delaying_string)
+    {
+      time_t t= my_time(0), sql_delay_end= mi->rli.get_sql_delay_end();
+      protocol->store((uint32)(t < sql_delay_end ? sql_delay_end - t : 0));
+    }
+    else
+      protocol->store_null();
+    // Slave_SQL_Running_State
+    protocol->store(slave_sql_running_state, &my_charset_bin);
 
     pthread_mutex_unlock(&mi->rli.err_lock);
     pthread_mutex_unlock(&mi->err_lock);
@@ -2076,23 +2098,104 @@ 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 sql_delay_event(Log_event *ev, THD *thd, Relay_log_info *rli)
+{
+  long sql_delay= rli->get_sql_delay();
+
+  DBUG_ENTER("sql_delay_event");
+  safe_mutex_assert_owner(&rli->data_lock);
+  DBUG_ASSERT(!rli->belongs_to_client());
+
+  int type= ev->get_type_code();
+  if (sql_delay && type != ROTATE_EVENT &&
+      type != FORMAT_DESCRIPTION_EVENT && type != START_EVENT_V3)
+  {
+    // The time when we should execute the event.
+    time_t sql_delay_end=
+      ev->when + rli->mi->clock_diff_with_master + sql_delay;
+    // The current time.
+    time_t now= my_time(0);
+    // The time we will have to sleep before executing the event.
+    unsigned long nap_time= 0;
+    if (sql_delay_end > now)
+      nap_time= sql_delay_end - now;
+
+    DBUG_PRINT("info", ("sql_delay= %lu "
+                        "ev->when= %lu "
+                        "rli->mi->clock_diff_with_master= %lu "
+                        "now= %ld "
+                        "sql_delay_end= %lu "
+                        "nap_time= %ld",
+                        sql_delay, (long)ev->when,
+                        rli->mi->clock_diff_with_master,
+                        (long)now, sql_delay_end, (long)nap_time));
+
+    if (sql_delay_end > now)
+    {
+      DBUG_PRINT("info", ("delaying replication event %lu secs",
+                          nap_time));
+      rli->start_sql_delay(sql_delay_end);
+      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->belongs_to_client() (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 if required 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.
 
@@ -2149,9 +2252,15 @@ int apply_event_and_update_pos(Log_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 (reason == Log_event::EVENT_SKIP_NOT)
+  {
+    // Sleeps if needed, and unlocks rli->data_lock.
+    sql_delay_event(ev, thd, rli);
     exec_res= ev->apply_event(rli);
+  }
+  else
+    pthread_mutex_unlock(&rli->data_lock);
 
 #ifndef DBUG_OFF
   /*
@@ -2178,14 +2287,11 @@ int apply_event_and_update_pos(Log_event
   if (exec_res == 0)
   {
     int error= ev->update_pos(rli);
-#ifdef HAVE_purify
-    if (!rli->is_fake)
-#endif
-    {
 #ifndef DBUG_OFF
+    DBUG_PRINT("info", ("update_pos error = %d", error));
+    if (!rli->belongs_to_client())
+    {
       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));
@@ -2193,6 +2299,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.
@@ -2219,7 +2326,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.
+  This is called 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.
@@ -2584,9 +2692,10 @@ connected:
                       DBUG_ASSERT(!debug_sync_set_action(thd, 
                                                          STRING_WITH_LEN(act)));
                     };);
-
-  // 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");
   ret= get_master_version_and_clock(mysql, mi);
@@ -3935,55 +4044,75 @@ 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.)
 
-  RETURN VALUES
-    0   ok
-    1   write error
-*/
+  - 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.
+
+  @todo Change the log file information to a binary format to avoid
+  calling longlong2str.
+
+  @todo Move the member function into rpl_rli.cc and get rid of the
+  global function. /SVEN
 
+  @return 0 on success, 1 on error.
+*/
 bool flush_relay_log_info(Relay_log_info* rli)
 {
+  return rli->flush();
+}
+
+bool Relay_log_info::flush()
+{
   bool error=0;
-  DBUG_ENTER("flush_relay_log_info");
 
-  if (unlikely(rli->no_storage))
-    DBUG_RETURN(0);
+  DBUG_ENTER("Relay_log_info::flush()");
 
-  IO_CACHE *file = &rli->info_file;
-  char buff[FN_REFLEN*2+22*2+4], *pos;
+  /*
+    @todo Uncomment the following assertion. See todo in
+    Relay_log_info::init() for details. /Sven
+  */
+  //DBUG_ASSERT(!belongs_to_client());
 
+  IO_CACHE *file = &info_file;
+  // 2*file name, 2*long long, 2*unsigned long, 6*'\n'
+  char buff[FN_REFLEN * 2 + 22 * 2 + 10 * 2 + 6], *pos;
   my_b_seek(file, 0L);
-  pos=strmov(buff, rli->group_relay_log_name);
+  pos= longlong2str(LINES_IN_RELAY_LOG_INFO_WITH_DELAY, buff, 10);
+  *pos++='\n';
+  pos=strmov(pos, group_relay_log_name);
   *pos++='\n';
-  pos=longlong2str(rli->group_relay_log_pos, pos, 10);
+  pos=longlong2str(group_relay_log_pos, pos, 10);
   *pos++='\n';
-  pos=strmov(pos, rli->group_master_log_name);
+  pos=strmov(pos, group_master_log_name);
   *pos++='\n';
-  pos=longlong2str(rli->group_master_log_pos, pos, 10);
-  *pos='\n';
+  pos=longlong2str(group_master_log_pos, pos, 10);
+  *pos++= '\n';
+  pos= longlong2str(sql_delay, pos, 10);
+  *pos= '\n';
   if (my_b_write(file, (uchar*) buff, (size_t) (pos-buff)+1))
     error=1;
   if (flush_io_cache(file))

=== modified file 'sql/slave.h'
--- a/sql/slave.h	2009-12-14 16:32:22 +0000
+++ b/sql/slave.h	2010-05-18 13:09:07 +0000
@@ -17,6 +17,14 @@
 #define SLAVE_H
 
 /**
+  MASTER_DELAY can be at most (1 << 31) - 1.
+*/
+#define MASTER_DELAY_MAX (0x7FFFFFFF)
+#if INT_MAX < 0x7FFFFFFF
+#error "don't support platforms where INT_MAX < 0x7FFFFFFF"
+#endif
+
+/**
   @defgroup Replication Replication
   @{
 
@@ -81,12 +89,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.
@@ -192,6 +202,10 @@ void set_slave_thread_default_charset(TH
 void rotate_relay_log(Master_info* mi);
 int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli);
 
+int init_intvar_from_file(int* var, IO_CACHE* f, int default_val);
+int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
+                          const char *default_val);
+
 pthread_handler_t handle_slave_io(void *arg);
 pthread_handler_t handle_slave_sql(void *arg);
 extern bool volatile abort_loop;

=== modified file 'sql/sql_binlog.cc'
--- a/sql/sql_binlog.cc	2009-10-14 01:39:05 +0000
+++ b/sql/sql_binlog.cc	2010-05-18 13:09:07 +0000
@@ -17,6 +17,88 @@
 #include "rpl_rli.h"
 #include "base64.h"
 
+
+/**
+  Check if the event type is allowed in a BINLOG statement.
+
+  @retval 0 if the event type is ok.
+  @retval 1 if the event type is not ok.
+*/
+static int check_event_type(int type, Relay_log_info *rli)
+{
+  Format_description_log_event *fd_event=
+    rli->relay_log.description_event_for_exec;
+
+  /*
+    Convert event type id of certain old versions (see comment in
+    Format_description_log_event::Format_description_log_event(char*,...)).
+  */
+  if (fd_event && fd_event->event_type_permutation)
+  {
+    IF_DBUG({
+        int new_type= fd_event->event_type_permutation[type];
+        DBUG_PRINT("info",
+                   ("converting event type %d to %d (%s)",
+                    type, new_type,
+                    Log_event::get_type_str((Log_event_type)new_type)));
+      });
+    type= fd_event->event_type_permutation[type];
+  }
+
+  switch (type)
+  {
+  case START_EVENT_V3:
+  case FORMAT_DESCRIPTION_EVENT:
+    /*
+      We need a preliminary FD event in order to parse the FD event,
+      if we don't already have one.
+    */
+    if (!fd_event)
+      if (!(rli->relay_log.description_event_for_exec=
+            new Format_description_log_event(4)))
+      {
+        my_error(ER_OUTOFMEMORY, MYF(0), 1);
+        return 1;
+      }
+
+    /* It is always allowed to execute FD events. */
+    return 0;
+    
+  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:
+    /*
+      Row events are only allowed if a Format_description_event has
+      already been seen.
+    */
+    if (fd_event)
+      return 0;
+    else
+    {
+      my_error(ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT,
+               MYF(0), Log_event::get_type_str((Log_event_type)type));
+      return 1;
+    }
+    break;
+
+  default:
+    /*
+      It is not meaningful to execute other events than row-events and
+      FD events. It would even be dangerous to execute 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.
+    */
+    my_error(ER_ONLY_FD_AND_RBR_EVENTS_ALLOWED_IN_BINLOG_STATEMENT,
+             MYF(0), Log_event::get_type_str((Log_event_type)type));
+    return 1;
+  }
+}
+
 /**
   Execute a BINLOG statement.
 
@@ -50,29 +132,11 @@ void mysql_client_binlog_statement(THD* 
     Allocation
   */
 
-  /*
-    If we do not have a Format_description_event, we create a dummy
-    one here.  In this case, the first event we read must be a
-    Format_description_event.
-  */
-  my_bool have_fd_event= TRUE;
   int err;
   Relay_log_info *rli;
   rli= thd->rli_fake;
-  if (!rli)
-  {
-    rli= thd->rli_fake= new Relay_log_info;
-#ifdef HAVE_purify
-    rli->is_fake= TRUE;
-#endif
-    have_fd_event= FALSE;
-  }
-  if (rli && !rli->relay_log.description_event_for_exec)
-  {
-    rli->relay_log.description_event_for_exec=
-      new Format_description_log_event(4);
-    have_fd_event= FALSE;
-  }
+  if (!rli && (rli= thd->rli_fake= new Relay_log_info))
+    rli->sql_thd= thd;
 
   const char *error= 0;
   char *buf= (char *) my_malloc(decoded_len, MYF(MY_WME));
@@ -81,16 +145,13 @@ void mysql_client_binlog_statement(THD* 
   /*
     Out of memory check
   */
-  if (!(rli &&
-        rli->relay_log.description_event_for_exec &&
-        buf))
+  if (!(rli && buf))
   {
     my_error(ER_OUTOFMEMORY, MYF(0), 1);  /* needed 1 bytes */
     goto end;
   }
 
-  rli->sql_thd= thd;
-  rli->no_storage= TRUE;
+  DBUG_ASSERT(rli->belongs_to_client());
 
   for (char const *strptr= thd->lex->comment.str ;
        strptr < thd->lex->comment.str + thd->lex->comment.length ; )
@@ -154,23 +215,8 @@ void mysql_client_binlog_statement(THD* 
         goto end;
       }
 
-      /*
-        If we have not seen any Format_description_event, then we must
-        see one; it is the only statement that can be read in base64
-        without a prior Format_description_event.
-      */
-      if (!have_fd_event)
-      {
-        int type = bufptr[EVENT_TYPE_OFFSET];
-        if (type == FORMAT_DESCRIPTION_EVENT || type == START_EVENT_V3)
-          have_fd_event= TRUE;
-        else
-        {
-          my_error(ER_NO_FORMAT_DESCRIPTION_EVENT_BEFORE_BINLOG_STATEMENT,
-                   MYF(0), Log_event::get_type_str((Log_event_type)type));
-          goto end;
-        }
-      }
+      if (check_event_type(bufptr[EVENT_TYPE_OFFSET], rli))
+        goto end;
 
       ev= Log_event::read_log_event(bufptr, event_len, &error,
                                     rli->relay_log.description_event_for_exec);
@@ -180,7 +226,7 @@ void mysql_client_binlog_statement(THD* 
       {
         /*
           This could actually be an out-of-memory, but it is more likely
-          causes by a bad statement
+          caused by a bad statement
         */
         my_error(ER_SYNTAX_ERROR, MYF(0));
         goto end;

=== modified file 'sql/sql_lex.cc'
--- a/sql/sql_lex.cc	2010-02-06 19:54:30 +0000
+++ b/sql/sql_lex.cc	2010-05-18 13:09:07 +0000
@@ -3007,3 +3007,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));
+  sql_delay= -1;
+}

=== modified file 'sql/sql_lex.h'
--- a/sql/sql_lex.h	2010-03-28 08:37:47 +0000
+++ b/sql/sql_lex.h	2010-05-18 13:09:07 +0000
@@ -203,17 +203,20 @@ typedef struct st_lex_master_info
 {
   char *host, *user, *password, *log_file_name;
   uint port, connect_retry;
+  int sql_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 {SSL_UNCHANGED, SSL_DISABLE, SSL_ENABLE}
+  enum {SSL_UNCHANGED= 0, SSL_DISABLE, SSL_ENABLE}
     ssl, ssl_verify_server_cert;
   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;
 
 

=== modified file 'sql/sql_repl.cc'
--- a/sql/sql_repl.cc	2010-03-19 09:06:40 +0000
+++ b/sql/sql_repl.cc	2010-05-18 13:09:07 +0000
@@ -1043,6 +1043,7 @@ int reset_slave(THD *thd, Master_info* m
   mi->clear_error();
   mi->rli.clear_error();
   mi->rli.clear_until_condition();
+  mi->rli.clear_sql_delay();
 
   // close master_info_file, relay_log_info_file, set mi->inited=rli->inited=0
   end_master_info(mi);
@@ -1193,6 +1194,9 @@ bool change_master(THD* thd, Master_info
   if (lex_mi->connect_retry)
     mi->connect_retry = lex_mi->connect_retry;
 
+  if (lex_mi->sql_delay != -1)
+    mi->rli.set_sql_delay(lex_mi->sql_delay);
+
   if (lex_mi->ssl != LEX_MASTER_INFO::SSL_UNCHANGED)
     mi->ssl= (lex_mi->ssl == LEX_MASTER_INFO::SSL_ENABLE);
 

=== modified file 'sql/sql_yacc.yy'
--- a/sql/sql_yacc.yy	2010-03-14 16:01:45 +0000
+++ b/sql/sql_yacc.yy	2010-05-18 13:09:07 +0000
@@ -960,6 +960,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
@@ -1721,7 +1722,7 @@ change:
           {
             LEX *lex = Lex;
             lex->sql_command = SQLCOM_CHANGE_MASTER;
-            bzero((char*) &lex->mi, sizeof(lex->mi));
+            lex->mi.set_unspecified();
           }
           master_defs
           {}
@@ -1753,6 +1754,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.sql_delay = $3;
+          }
         | MASTER_SSL_SYM EQ ulong_num
           {
             Lex->mi.ssl= $3 ? 
@@ -6219,7 +6230,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
@@ -6237,7 +6248,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
           {}
@@ -6246,6 +6258,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 */
           }
         ;
 
@@ -11724,6 +11737,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    {}


Attachment: [text/bzr-bundle] bzr/sven.sandberg@sun.com-20100518130907-d7v86vzko23jy9qr.bundle
Thread
bzr push into mysql-5.1 branch (sven.sandberg:3447 to 3448) Bug#28760WL#344Sven Sandberg18 May