#At file:///D:/source/bzr/mysql-6.0-bug-39780/
2746 Chuck Bell 2009-01-06
BUG#39780 Allow restore to skip gap event during restore on master
This patch adds a new option to the restore command: skip_gap_event.
It is used to skip writing the incident event when a restore is
run on a master in an active replication topology (gap event).
modified:
mysql-test/suite/rpl/r/rpl_backup.result
mysql-test/suite/rpl/t/rpl_backup.test
sql/backup/backup_kernel.h
sql/backup/kernel.cc
sql/lex.h
sql/sql_parse.cc
sql/sql_yacc.yy
per-file messages:
mysql-test/suite/rpl/r/rpl_backup.result
Corrected result file.
mysql-test/suite/rpl/t/rpl_backup.test
Added test case for skipping gap event.
sql/backup/backup_kernel.h
Added parameter to pass on value of skip_gap_event check.
sql/backup/kernel.cc
Added parameter to pass on the result of the skip_gap_event check.
Added code to skip gap event when triggered.
sql/lex.h
Added new symbol.
sql/sql_parse.cc
Added code to pass the skip_gap_event signal to the proper locations.
Simplified option check code.
sql/sql_yacc.yy
Extended restore syntax to include new option.
=== modified file 'mysql-test/suite/rpl/r/rpl_backup.result'
--- a/mysql-test/suite/rpl/r/rpl_backup.result 2008-12-16 20:54:07 +0000
+++ b/mysql-test/suite/rpl/r/rpl_backup.result 2009-01-06 20:28:28 +0000
@@ -60,7 +60,7 @@ Replicate_Do_DB
Replicate_Ignore_DB
Replicate_Do_Table
Replicate_Ignore_Table
-Replicate_Wild_Do_Table
+Replicate_Wild_Do_Table rpl_backup.%
Replicate_Wild_Ignore_Table
Last_Errno 0
Last_Error
@@ -119,7 +119,7 @@ Replicate_Do_DB
Replicate_Ignore_DB
Replicate_Do_Table
Replicate_Ignore_Table
-Replicate_Wild_Do_Table
+Replicate_Wild_Do_Table rpl_backup.%
Replicate_Wild_Ignore_Table
Last_Errno 0
Last_Error
@@ -206,7 +206,7 @@ Replicate_Do_DB
Replicate_Ignore_DB
Replicate_Do_Table
Replicate_Ignore_Table
-Replicate_Wild_Do_Table
+Replicate_Wild_Do_Table rpl_backup.%
Replicate_Wild_Ignore_Table
Last_Errno 1590
Last_Error The incident RESTORE_ON_MASTER occured on the master. Message: A restore
operation was initiated on the master.
@@ -354,7 +354,7 @@ Replicate_Do_DB
Replicate_Ignore_DB
Replicate_Do_Table
Replicate_Ignore_Table
-Replicate_Wild_Do_Table
+Replicate_Wild_Do_Table rpl_backup.%
Replicate_Wild_Ignore_Table
Last_Errno 0
Last_Error
@@ -407,7 +407,7 @@ Replicate_Do_DB
Replicate_Ignore_DB
Replicate_Do_Table
Replicate_Ignore_Table
-Replicate_Wild_Do_Table
+Replicate_Wild_Do_Table rpl_backup.%
Replicate_Wild_Ignore_Table
Last_Errno 0
Last_Error
@@ -431,9 +431,52 @@ Last_SQL_Errno 0
Last_SQL_Error
Now stop the slave.
SLAVE STOP;
+First, reset replication.
+RESET MASTER;
+RESET SLAVE;
+SET DEBUG_SYNC = 'reset';
+START SLAVE;
+Create a new database on the master.
+CREATE DATABASE not_replicated;
+CREATE TABLE not_replicated.t1 (a int);
+INSERT INTO not_replicated.t1 VALUES (200), (300), (400);
+SHOW DATABASES;
+Database
+information_schema
+mysql
+not_replicated
+rpl_backup
+test
+Now let's see if it is replicated on the slave (shouldn't be).
+SHOW DATABASES;
+Database
+information_schema
+mysql
+rpl_backup
+test
+Now back to the master to make a backup of the new database.
+Backup_id = 505.
+BACKUP DATABASE not_replicated TO 'rpl_bup_m4.bak';
+backup_id
+505
+Now let's run the restore and see if the slave stops.
+Backup_id = 506.
+RESTORE FROM 'rpl_bup_m4.bak' OVERWRITE SKIP_GAP_EVENT;
+backup_id
+506
+Let's ensure replication is still running.
+INSERT INTO rpl_backup.t1 VALUES (901), (902), (903);
+SELECT * FROM rpl_backup.t1 WHERE a > 900;
+a
+901
+902
+903
+Now stop the slave.
+SLAVE STOP;
FLUSH BACKUP LOGS;
PURGE BACKUP LOGS;
DROP DATABASE rpl_backup;
+DROP DATABASE not_replicated;
FLUSH BACKUP LOGS;
PURGE BACKUP LOGS;
DROP DATABASE rpl_backup;
=== modified file 'mysql-test/suite/rpl/t/rpl_backup.test'
--- a/mysql-test/suite/rpl/t/rpl_backup.test 2008-12-16 20:54:07 +0000
+++ b/mysql-test/suite/rpl/t/rpl_backup.test 2009-01-06 20:28:28 +0000
@@ -432,6 +432,65 @@ SLAVE STOP;
--source include/wait_for_slave_to_stop.inc
#
+# Test case for BUG#39780 - Skip gap event on restore.
+#
+# The new SKIP_GAP_EVENT option for the RESTORE command
+# allows users to pruposefully skip writing the gap event
+# when the restore is run on a master and the databases
+# being restored do not exist on the slave nor are they
+# intended to be on the slave.
+#
+
+--echo First, reset replication.
+connection master;
+RESET MASTER;
+
+connection slave;
+RESET SLAVE;
+SET DEBUG_SYNC = 'reset';
+
+connection slave;
+START SLAVE;
+--source include/wait_for_slave_to_start.inc
+
+--echo Create a new database on the master.
+connection master;
+
+CREATE DATABASE not_replicated;
+CREATE TABLE not_replicated.t1 (a int);
+INSERT INTO not_replicated.t1 VALUES (200), (300), (400);
+
+SHOW DATABASES;
+
+--echo Now let's see if it is replicated on the slave (shouldn't be).
+sync_slave_with_master;
+connection slave;
+
+SHOW DATABASES;
+
+--echo Now back to the master to make a backup of the new database.
+connection master;
+
+--echo Backup_id = 505.
+BACKUP DATABASE not_replicated TO 'rpl_bup_m4.bak';
+
+--echo Now let's run the restore and see if the slave stops.
+--echo Backup_id = 506.
+RESTORE FROM 'rpl_bup_m4.bak' OVERWRITE SKIP_GAP_EVENT;
+
+--echo Let's ensure replication is still running.
+INSERT INTO rpl_backup.t1 VALUES (901), (902), (903);
+
+sync_slave_with_master;
+connection slave;
+
+SELECT * FROM rpl_backup.t1 WHERE a > 900;
+
+--echo Now stop the slave.
+SLAVE STOP;
+--source include/wait_for_slave_to_stop.inc
+
+#
# Cleanup
#
connection master;
@@ -439,10 +498,12 @@ connection master;
FLUSH BACKUP LOGS;
PURGE BACKUP LOGS;
DROP DATABASE rpl_backup;
+DROP DATABASE not_replicated;
--remove_file $MYSQLTEST_VARDIR/master-data/rpl_bup_m1.bak
--remove_file $MYSQLTEST_VARDIR/master-data/rpl_bup_m2.bak
--remove_file $MYSQLTEST_VARDIR/master-data/rpl_bup_m3.bak
+--remove_file $MYSQLTEST_VARDIR/master-data/rpl_bup_m4.bak
connection slave;
=== modified file 'sql/backup/backup_kernel.h'
--- a/sql/backup/backup_kernel.h 2008-12-18 21:46:36 +0000
+++ b/sql/backup/backup_kernel.h 2009-01-06 20:28:28 +0000
@@ -30,7 +30,7 @@ void backup_shutdown();
Called from the big switch in mysql_execute_command() to execute
backup related statement
*/
-int execute_backup_command(THD*, LEX*, String*, bool);
+int execute_backup_command(THD*, LEX*, String*, bool, bool);
// forward declarations
@@ -73,7 +73,7 @@ public:
const char*, bool);
Restore_info* prepare_for_restore(String *location,
LEX_STRING orig_loc,
- const char*);
+ const char*, bool);
int do_backup();
int do_restore(bool overwrite);
=== modified file 'sql/backup/kernel.cc'
--- a/sql/backup/kernel.cc 2008-12-18 21:46:36 +0000
+++ b/sql/backup/kernel.cc 2009-01-06 20:28:28 +0000
@@ -123,6 +123,9 @@ static int send_reply(Backup_restore_ctx
@param[in] backupdir value of the backupdir variable from server.
@param[in] overwrite whether or not restore should overwrite existing
DB with same name as in backup image
+ @param[in] skip_gap_event whether or not restore should skip writing
+ the gap event if run on a master in an active
+ replication scenario
@note This function sends response to the client (ok, result set or error).
@@ -134,7 +137,11 @@ static int send_reply(Backup_restore_ctx
*/
int
-execute_backup_command(THD *thd, LEX *lex, String *backupdir, bool overwrite)
+execute_backup_command(THD *thd,
+ LEX *lex,
+ String *backupdir,
+ bool overwrite,
+ bool skip_gap_event)
{
int res= 0;
@@ -209,7 +216,7 @@ execute_backup_command(THD *thd, LEX *le
{
Restore_info *info= context.prepare_for_restore(backupdir, lex->backup_dir,
- thd->query);
+ thd->query, skip_gap_event);
if (!info || !info->is_valid())
DBUG_RETURN(send_error(context, ER_BACKUP_RESTORE_PREPARE));
@@ -682,6 +689,7 @@ Backup_restore_ctx::prepare_for_backup(S
@param[in] backupdir path to the file where backup image is stored
@param[in] orig_loc path as specified on command line for backup image
@param[in] query RESTORE query starting the operation
+ @param[in] skip_gap_event TRUE means do not write gap event
@returns Pointer to a @c Restore_info instance containing catalogue of the
backup image (read from the image). NULL if errors were detected.
@@ -691,7 +699,8 @@ Backup_restore_ctx::prepare_for_backup(S
Restore_info*
Backup_restore_ctx::prepare_for_restore(String *backupdir,
LEX_STRING orig_loc,
- const char *query)
+ const char *query,
+ bool skip_gap_event)
{
using namespace backup;
@@ -822,7 +831,8 @@ Backup_restore_ctx::prepare_for_restore(
DEBUG_SYNC(m_thd, "after_disable_slave_connections");
- obs::write_incident_event(m_thd, obs::RESTORE_EVENT);
+ if (!skip_gap_event)
+ obs::write_incident_event(m_thd, obs::RESTORE_EVENT);
m_engage_binlog= TRUE;
obs::engage_binlog(FALSE);
}
=== modified file 'sql/lex.h'
--- a/sql/lex.h 2008-11-17 09:57:51 +0000
+++ b/sql/lex.h 2009-01-06 20:28:28 +0000
@@ -485,6 +485,7 @@ static SYMBOL symbols[] = {
{ "SHUTDOWN", SYM(SHUTDOWN)},
{ "SIGNED", SYM(SIGNED_SYM)},
{ "SIMPLE", SYM(SIMPLE_SYM)},
+ { "SKIP_GAP_EVENT", SYM(SKIP_GAP_EVENT_SYM)},
{ "SLAVE", SYM(SLAVE)},
{ "SNAPSHOT", SYM(SNAPSHOT_SYM)},
{ "SMALLINT", SYM(SMALLINT)},
=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc 2008-12-04 23:14:30 +0000
+++ b/sql/sql_parse.cc 2009-01-06 20:28:28 +0000
@@ -44,7 +44,7 @@
@defgroup Runtime_Environment Runtime Environment
@{
*/
-int execute_backup_command(THD*, LEX*, String*, bool);
+int execute_backup_command(THD*, LEX*, String*, bool, bool);
/* Used in error handling only */
#define SP_TYPE_STRING(LP) \
@@ -2316,26 +2316,43 @@ mysql_execute_command(THD *thd)
/* Used to specify if RESTORE should overwrite existing db with same name */
bool overwrite_restore= false;
- Item *it= (Item *)lex->value_list.head();
+ /* Used to specify if RESTORE should skup writing the gap event. */
+ bool skip_gap_event= false;
+
+ List<Item> lit= lex->value_list;
+ Item *it= 0;
- // Item only set for RESTORE in sql_yacc.yy, no error checking of
- // item necessary
- if (it)
+ // value list only set for RESTORE in sql_yacc.yy, no error checking of
+ // item necessary for backup
+ while (lit.elements)
{
+ it= lit.pop();
/*
it is OK to only emulate fix_fields, because we need only
value of constant
*/
it->quick_fix_field();
-
- if ((int8)it->val_int() == 1)
- overwrite_restore= true;
+ int8 val= (int8)it->val_int();
+ /*
+ Check options.
+ */
+ switch (val) {
+ /* OVERWRITE option */
+ case 1:
+ overwrite_restore= true;
+ break;
+ /* SKIP GAP EVENT option */
+ case 2:
+ skip_gap_event= true;
+ break;
+ }
}
/*
Note: execute_backup_command() sends a correct response to the client
(either ok, result set or error message).
*/
- if (execute_backup_command(thd, lex, &backupdir, overwrite_restore))
+ if (execute_backup_command(thd, lex, &backupdir, overwrite_restore,
+ skip_gap_event))
goto error;
break;
}
=== modified file 'sql/sql_yacc.yy'
--- a/sql/sql_yacc.yy 2008-12-04 23:14:30 +0000
+++ b/sql/sql_yacc.yy 2009-01-06 20:28:28 +0000
@@ -1074,6 +1074,7 @@ bool my_yyoverflow(short **a, YYSTYPE **
%token SHUTDOWN
%token SIGNED_SYM
%token SIMPLE_SYM /* SQL-2003-N */
+%token SKIP_GAP_EVENT_SYM
%token SLAVE
%token SMALLINT /* SQL-2003-R */
%token SNAPSHOT_SYM
@@ -6435,7 +6436,7 @@ restore:
}
FROM
TEXT_STRING_sys
- opt_overwrite
+ opt_overwrite opt_skip_gap_event
{
Lex->backup_dir = $4;
}
@@ -6459,6 +6460,23 @@ opt_overwrite:
lex->value_list.push_front(it);
}
;
+
+opt_skip_gap_event:
+ /* empty */
+ {
+ LEX *lex= Lex;
+ Item *it= new Item_int((int8) 0);
+
+ lex->value_list.push_back(it);
+ }
+ | SKIP_GAP_EVENT_SYM
+ {
+ LEX *lex= Lex;
+ Item *it= new Item_int((int8) 2);
+
+ lex->value_list.push_back(it);
+ }
+ ;
backup:
BACKUP_SYM