List:Commits« Previous MessageNext Message »
From:Rafal Somla Date:January 8 2009 2:57pm
Subject:bzr commit into mysql-6.0-backup branch (Rafal.Somla:2748) Bug#40305
View as plain text  
#At file:///ext/mysql/bzr/backup/bug40305/

 2748 Rafal Somla	2009-01-08
      Bug #40305 - Backup: Bad feedback when BACKUP/RESTORE operation aborted.
      
      This patch refactors the code reporting termination of BACKUP/RESTORE statement. 
      After the changes "Backup/Restore completed" is shown only if operation has been 
      completed. In case of abnormal termination (error, interruption) a warning 
      "Operation aborted" or "Operation aborted - data might be corrupted" is logged. 
      The latter is in the case of RESTORE operation which has modified some data in 
      the server.
      
      The main changes are:
      - Function backup::Logger::report_stop() is replaced by 2 new functions: 
        report_completed() and report_aborted() to differentiate the feedback given in 
        the two situations.
      - Make sure that a call to report_completed() or report_aborted() always matches 
        a previous call to report_start(). This is done in Backup_restore_ctx::close() 
        which is called from the destructor.
      - Change the logic for removing backup file (in case BACKUP operation is not 
        completed). Before this was guarded by explicit m_remove_loc flag. Now we can 
        directly check if BACKUP operation has been completed successfully (i.e. 
        report_completed() was called) or not.
      
      Note: the new functionality is covered by existing test cases.
modified:
  mysql-test/lib/mtr_report.pl
  mysql-test/suite/backup/r/backup_errors.result
  mysql-test/suite/backup/r/backup_logs_purge.result
  mysql-test/suite/backup/t/backup_errors.test
  mysql-test/suite/backup/t/backup_logs_purge.test
  sql/backup/backup_kernel.h
  sql/backup/image_info.h
  sql/backup/kernel.cc
  sql/backup/logger.h
  sql/backup/restore_info.h
  sql/share/errmsg.txt

per-file messages:
  mysql-test/lib/mtr_report.pl
    Filter out the abort warnings.
  mysql-test/suite/backup/r/backup_errors.result
    Result change - now additional warning about statement interruption is pushed on 
    the error stack.
  mysql-test/suite/backup/t/backup_errors.test
    - Echo what is tested to improve readability of result file.
    - Move repeated database test after the results of previous
      test are examined.
  mysql-test/suite/backup/t/backup_logs_purge.test
    Update debug synchronization points - BACKUP/RESTORE must be stopped before 
    state change to "Completed" is reported in backup progress log. This state 
    change is reported by report_completed().
  sql/backup/backup_kernel.h
    - Document the states of Backup_restore_ctx class.
    - Remove m_remove_loc member which is no longer used.
    - Report the state change when moving to error state.
  sql/backup/image_info.h
    Make is_valid() method visible from base class Image_info.
  sql/backup/kernel.cc
    - Update Backup_restore_ctx::close() (which is called from Backup_restore_ctx
      destructor) so that it calls either report_completed() or report_aborted() to 
      match the report_start() call made in Backuo_restor_ctx::prepare_for...() 
      method.
    - Also, Backup_restore_ctx::close() moves the context to error state in case the
      catalogue got corrupted. This writes an entry into backup logs.
    - Change the logic for removing backup file: instead of using explicit 
      m_remove_loc flag, decide based on whether BACKUP operation has completed 
      successfully or not (as now this information is available).
    - Call Backup_restore_ctx::close() in do_backup() and do_restore(), when 
      operation is completed - no need to wait any longer.
    - Report data changes when objects are created in bcat_create_item() and  
      restore_triggers_and_events().
  sql/backup/logger.h
    Replace backup::Logger::report_stop() with two new methods: report_completed() 
    and report_aborted().
  sql/backup/restore_info.h
    Add flag for recording the fact that data has been modified during
    restore operation.
  sql/share/errmsg.txt
    Add messages for reporting early termination of a statement.
=== modified file 'mysql-test/lib/mtr_report.pl'
--- a/mysql-test/lib/mtr_report.pl	2008-12-16 20:54:07 +0000
+++ b/mysql-test/lib/mtr_report.pl	2009-01-08 14:57:41 +0000
@@ -328,6 +328,9 @@ sub mtr_report_stats ($) {
 		/Slave: Can't drop database.* database doesn't exist/ or
                 /Slave SQL:.*(?:Error_code: \d+|Query:.*)/ or
 		
+		# Ignore warnings issued when backup/restore operation is aborted.
+		/\[Warning\] (Backup|Restore): Operation aborted/ or
+		
 		# backup_errors test is supposed to trigger lots of backup related errors
 		($testname eq 'backup.backup_errors') and
 		(

=== modified file 'mysql-test/suite/backup/r/backup_errors.result'
--- a/mysql-test/suite/backup/r/backup_errors.result	2009-01-06 09:53:50 +0000
+++ b/mysql-test/suite/backup/r/backup_errors.result	2009-01-08 14:57:41 +0000
@@ -1,5 +1,6 @@
 DROP DATABASE IF EXISTS adb;
 DROP DATABASE IF EXISTS bdb;
+# non-existent backup archive
 RESTORE FROM 'test.bak';
 ERROR HY000: File 'MYSQLTEST_VARDIR/master-data/test.bak' not found (Errcode: #)
 Get last backup_id
@@ -20,11 +21,13 @@ error
 CREATE DATABASE adb;
 CREATE DATABASE bdb;
 CREATE TABLE bdb.t1(a int) ENGINE=MEMORY;
+# invalid location
 BACKUP DATABASE adb TO '';
 ERROR HY000: Malformed file path ''
 SHOW WARNINGS;
 Level	Code	Message
 Error	#	Malformed file path ''
+Warning	#	Operation aborted
 Get last backup_id
 SELECT MAX(backup_id) INTO @bup_id FROM mysql.backup_history
 WHERE command LIKE "BACKUP DATABASE adb TO%";
@@ -40,23 +43,27 @@ starting
 running
 Malformed file path ''
 error
+# don't overwrite existing files
 BACKUP DATABASE adb TO "bdb/t1.frm";
 ERROR HY000: Can't create/write to file 'MYSQLTEST_VARDIR/master-data/bdb/t1.frm' (Errcode: #)
 SHOW WARNINGS;
 Level	Code	Message
 Error	#	Can't create/write to file 'MYSQLTEST_VARDIR/master-data/bdb/t1.frm' (Errcode: #)
 Error	#	Can't write to backup location 'bdb/t1.frm'
+Warning	#	Operation aborted
 BACKUP DATABASE adb TO "test.bak";
 backup_id
 #
 SHOW WARNINGS;
 Level	Code	Message
+# don't overwrite existing backup image
 BACKUP DATABASE adb TO "test.bak";
 ERROR HY000: Can't create/write to file 'MYSQLTEST_VARDIR/master-data/test.bak' (Errcode: #)
 SHOW WARNINGS;
 Level	Code	Message
 Error	#	Can't create/write to file 'MYSQLTEST_VARDIR/master-data/test.bak' (Errcode: #)
 Error	#	Can't write to backup location 'test.bak'
+Warning	#	Operation aborted
 Get last backup_id
 SELECT MAX(backup_id) INTO @bup_id FROM mysql.backup_history
 WHERE command LIKE "BACKUP DATABASE adb TO%";
@@ -72,6 +79,7 @@ starting
 running
 Can't write to backup location 'test.bak'
 error
+# non-existent database
 DROP DATABASE IF EXISTS foo;
 DROP DATABASE IF EXISTS bar;
 BACKUP DATABASE foo TO 'test.bak';
@@ -79,13 +87,13 @@ ERROR 42000: Unknown database 'foo'
 SHOW WARNINGS;
 Level	Code	Message
 Error	#	Unknown database 'foo'
+Warning	#	Operation aborted
 BACKUP DATABASE test,foo,bdb,bar TO 'test.bak';
 ERROR 42000: Unknown database 'foo,bar'
 SHOW WARNINGS;
 Level	Code	Message
 Error	#	Unknown database 'foo,bar'
-BACKUP DATABASE foo,test,bar,foo TO 'test.bak';
-ERROR 42000: Not unique database: 'foo'
+Warning	#	Operation aborted
 Get last backup_id
 SELECT MAX(backup_id) INTO @bup_id FROM mysql.backup_history
 WHERE command LIKE "BACKUP DATABASE test,foo,bdb,bar TO%";
@@ -101,6 +109,12 @@ starting
 running
 Unknown database 'foo,bar'
 error
+# repeated database
+BACKUP DATABASE foo,test,bar,foo TO 'test.bak';
+ERROR 42000: Not unique database: 'foo'
+SHOW WARNINGS;
+Level	Code	Message
+Error	#	Not unique database: 'foo'
 use adb;
 create table t1 (a int);
 create procedure p1() backup database test to 'test.bak';
@@ -131,6 +145,7 @@ ERROR HY000: Database 'mysql' cannot be 
 SHOW WARNINGS;
 Level	Code	Message
 Error	#	Database 'mysql' cannot be included in a backup
+Warning	#	Operation aborted
 Get last backup_id
 SELECT MAX(backup_id) INTO @bup_id FROM mysql.backup_history
 WHERE command LIKE "BACKUP DATABASE mysql TO%";
@@ -152,6 +167,7 @@ ERROR HY000: Database 'information_schem
 SHOW WARNINGS;
 Level	Code	Message
 Error	#	Database 'information_schema' cannot be included in a backup
+Warning	#	Operation aborted
 Get last backup_id
 SELECT MAX(backup_id) INTO @bup_id FROM mysql.backup_history
 WHERE command LIKE "BACKUP DATABASE information_schema TO%";
@@ -173,6 +189,7 @@ ERROR HY000: Database 'mysql' cannot be 
 SHOW WARNINGS;
 Level	Code	Message
 Error	#	Database 'mysql' cannot be included in a backup
+Warning	#	Operation aborted
 Get last backup_id
 SELECT MAX(backup_id) INTO @bup_id FROM mysql.backup_history
 WHERE command LIKE "BACKUP DATABASE mysql, information_schema TO%";
@@ -194,6 +211,7 @@ ERROR HY000: Database 'mysql' cannot be 
 SHOW WARNINGS;
 Level	Code	Message
 Error	#	Database 'mysql' cannot be included in a backup
+Warning	#	Operation aborted
 Get last backup_id
 SELECT MAX(backup_id) INTO @bup_id FROM mysql.backup_history
 WHERE command LIKE "BACKUP DATABASE mysql, test TO%";
@@ -215,12 +233,14 @@ ERROR HY000: Database 'information_schem
 SHOW WARNINGS;
 Level	Code	Message
 Error	#	Database 'information_schema' cannot be included in a backup
+Warning	#	Operation aborted
 Backup of mysql, information_schema scenario 6
 BACKUP DATABASE mysql, information_schema, test TO 't.bak';
 ERROR HY000: Database 'mysql' cannot be included in a backup
 SHOW WARNINGS;
 Level	Code	Message
 Error	#	Database 'mysql' cannot be included in a backup
+Warning	#	Operation aborted
 Get last backup_id
 SELECT MAX(backup_id) INTO @bup_id FROM mysql.backup_history
 WHERE command LIKE "BACKUP DATABASE mysql, information_schema, test TO%";
@@ -393,6 +413,7 @@ SHOW WARNINGS;
 Level	Code	Message
 Error	#	Can't create/write to file 'MYSQLTEST_VARDIR/master-data/test.bak' (Errcode: #)
 Error	#	Can't write to backup location 'test.bak'
+Warning	#	Operation aborted
 
 # Test that there are no warnings after successful BACKUP
 BACKUP DATABASE db1 TO 'newtest.bak';
@@ -408,6 +429,7 @@ SHOW WARNINGS;
 Level	Code	Message
 Error	#	Can't create/write to file 'MYSQLTEST_VARDIR/master-data/test.bak' (Errcode: #)
 Error	#	Can't write to backup location 'test.bak'
+Warning	#	Operation aborted
 
 # Test that there are no warnings after successful RESTORE
 RESTORE FROM 'newtest.bak' OVERWRITE;

=== modified file 'mysql-test/suite/backup/r/backup_logs_purge.result'
--- a/mysql-test/suite/backup/r/backup_logs_purge.result	2008-11-17 09:57:51 +0000
+++ b/mysql-test/suite/backup/r/backup_logs_purge.result	2009-01-08 14:57:41 +0000
@@ -252,7 +252,7 @@ count(*)
 24
 SET SESSION debug="+d,set_backup_id";
 con2: Activate sync points for the backup statement.
-SET DEBUG_SYNC= 'before_backup_done SIGNAL ready WAIT_FOR proceed';
+SET DEBUG_SYNC= 'before_backup_completed SIGNAL ready WAIT_FOR proceed';
 BACKUP DATABASE backup_logs to 'backup5.bak';
 con1: Wait for the backup to be ready.
 SET DEBUG_SYNC= 'now WAIT_FOR ready';
@@ -305,7 +305,7 @@ SELECT count(*) FROM mysql.backup_progre
 count(*)
 13
 con2: Activate sync points for the backup statement.
-SET DEBUG_SYNC= 'before_restore_done SIGNAL ready WAIT_FOR proceed';
+SET DEBUG_SYNC= 'before_restore_completed SIGNAL ready WAIT_FOR proceed';
 RESTORE FROM 'backup5.bak' OVERWRITE;
 con1: Wait for the backup to be ready.
 SET DEBUG_SYNC= 'now WAIT_FOR ready';

=== modified file 'mysql-test/suite/backup/t/backup_errors.test'
--- a/mysql-test/suite/backup/t/backup_errors.test	2009-01-06 09:53:50 +0000
+++ b/mysql-test/suite/backup/t/backup_errors.test	2009-01-08 14:57:41 +0000
@@ -12,7 +12,7 @@ DROP DATABASE IF EXISTS bdb;
 --remove_file $MYSQLTEST_VARDIR/master-data/test.bak
 --enable_warnings
 
-# non-existent backup archive
+--echo # non-existent backup archive
 --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
 --replace_regex /Errcode: [0-9]+/Errcode: #/
 --error 29
@@ -32,7 +32,7 @@ CREATE DATABASE adb;
 CREATE DATABASE bdb;
 CREATE TABLE bdb.t1(a int) ENGINE=MEMORY;
 
-# invalid location
+--echo # invalid location
 --error ER_BAD_PATH
 BACKUP DATABASE adb TO '';
 --replace_column 2 #
@@ -48,7 +48,7 @@ SELECT backup_state,operation, backup_fi
 SELECT notes FROM mysql.backup_progress
      WHERE backup_id=@bup_id;
 
-# don't overwrite existing files
+--echo # don't overwrite existing files
 --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
 --replace_regex /Errcode: [0-9]+/Errcode: #/
 --error 1
@@ -63,7 +63,7 @@ BACKUP DATABASE adb TO "test.bak";
 # There should be no warnings after a successful backup
 SHOW WARNINGS;
 
-# don't overwrite existing backup image
+--echo # don't overwrite existing backup image
 --replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
 --replace_regex /Errcode: [0-9]+/Errcode: #/
 --error 1
@@ -85,7 +85,7 @@ SELECT notes FROM mysql.backup_progress
 
 --remove_file $MYSQLTEST_VARDIR/master-data/test.bak
 
-# non-existent database
+--echo # non-existent database
 --disable_warnings
 DROP DATABASE IF EXISTS foo;
 DROP DATABASE IF EXISTS bar;
@@ -101,10 +101,6 @@ BACKUP DATABASE test,foo,bdb,bar TO 'tes
 --replace_column 2 #
 SHOW WARNINGS;
 
-# repeated database
--- error ER_NONUNIQ_DB
-BACKUP DATABASE foo,test,bar,foo TO 'test.bak';
-
 --echo Get last backup_id
 SELECT MAX(backup_id) INTO @bup_id FROM mysql.backup_history
 WHERE command LIKE "BACKUP DATABASE test,foo,bdb,bar TO%";
@@ -115,6 +111,16 @@ SELECT backup_state,operation,backup_fil
 SELECT notes FROM mysql.backup_progress
      WHERE backup_id=@bup_id;
 
+#
+# Note: the following error is detected on parser level. Thus no 
+# backup_id is assigned and nothing is written to backup logs.
+#
+--echo # repeated database
+-- error ER_NONUNIQ_DB
+BACKUP DATABASE foo,test,bar,foo TO 'test.bak';
+--replace_column 2 #
+SHOW WARNINGS;
+
 # Test that BACKUP/RESTORE statements are disable inside stored routines,
 # triggers and events.
 

=== modified file 'mysql-test/suite/backup/t/backup_logs_purge.test'
--- a/mysql-test/suite/backup/t/backup_logs_purge.test	2008-11-17 09:57:51 +0000
+++ b/mysql-test/suite/backup/t/backup_logs_purge.test	2009-01-08 14:57:41 +0000
@@ -274,7 +274,7 @@ SET SESSION debug="+d,set_backup_id";
 # it writes the history log and last complete log entry.
 #
 --echo con2: Activate sync points for the backup statement.
-SET DEBUG_SYNC= 'before_backup_done SIGNAL ready WAIT_FOR proceed';
+SET DEBUG_SYNC= 'before_backup_completed SIGNAL ready WAIT_FOR proceed';
 SEND BACKUP DATABASE backup_logs to 'backup5.bak';
 
 connection con1;
@@ -328,7 +328,7 @@ connection con2;
 # it writes the history log and last complete log entry.
 #
 --echo con2: Activate sync points for the backup statement.
-SET DEBUG_SYNC= 'before_restore_done SIGNAL ready WAIT_FOR proceed';
+SET DEBUG_SYNC= 'before_restore_completed SIGNAL ready WAIT_FOR proceed';
 SEND RESTORE FROM 'backup5.bak' OVERWRITE;
 
 connection con1;

=== 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-08 14:57:41 +0000
@@ -102,9 +102,31 @@ private:
 
   /** 
     @brief State of a context object. 
-    
-    Backup/restore can be performed only if object is prepared for that 
-    operation.
+
+    The following diagram illustrates the states in which a context object
+    can be and how the state changes as a result of calling public methods.
+    Methods which are not listed are forbidden in a given state.
+    @verbatim
+    CREATED
+        prepare_for_backup()   -> PREPARED_FOR_BACKUP
+        prepare_for_restore()  -> PREPARED_FOR_RESTORE
+        close()                -> CLOSED
+
+    PREPARED_FOR_BACKUP
+        do_backup()            -> CLOSED
+        close()                -> CLOSED
+
+    PREPARED_FOR_RESTORE
+        do_restore()           -> CLOSED
+        close()                -> CLOSED
+
+    CLOSED
+        close()                -> CLOSED
+    @endverbatim
+
+    @note An instance of the context class can be used only once -- when it 
+    moves to CLOSED state no methods can be called except for close() which does
+    nothing in that case.
    */
   enum { CREATED,
          PREPARED_FOR_BACKUP,
@@ -132,9 +154,6 @@ private:
   
   ::String  m_path;   ///< Path to where the backup image file is located.
 
-  /** If true, the backup image file is deleted at clean-up time. */
-  bool m_remove_loc;
-
   backup::Stream *m_stream; ///< Pointer to the backup stream object, if opened.
   backup::Image_info *m_catalog;  ///< Pointer to the image catalogue object.
 
@@ -163,6 +182,9 @@ private:
   
   int report_stream_open_failure(int open_error, const LEX_STRING *location);
 
+  /// Indicates if the operation has been successfully completed.  
+  bool m_completed;  
+
   friend int backup_init();
   friend void backup_shutdown();
   friend bstream_byte* bstream_alloc(unsigned long int);
@@ -210,12 +232,11 @@ void Backup_restore_ctx::disable_fkey_co
 inline
 int Backup_restore_ctx::fatal_error(int error_code)
 {
-  m_remove_loc= TRUE;
-
   if (m_error)
     return m_error;
 
   m_error= error_code;
+  report_state(BUP_ERRORS);
 
   return error_code;
 }

=== modified file 'sql/backup/image_info.h'
--- a/sql/backup/image_info.h	2008-12-18 21:46:36 +0000
+++ b/sql/backup/image_info.h	2009-01-08 14:57:41 +0000
@@ -85,6 +85,8 @@ public: // public interface
  
   // info about image (most of it is in the st_bstream_image_header base
 
+  virtual bool is_valid() =0;  ///< Is the structure valid?
+
   size_t     data_size;      ///< How much of table data is saved in the image.
   st_bstream_binlog_pos  master_pos; ///< To store master position info.
 

=== 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-08 14:57:41 +0000
@@ -270,8 +270,6 @@ int send_error(Backup_restore_ctx &conte
     va_end(args);
   }
 
-  if (context.backup::Logger::m_state == backup::Logger::RUNNING)
-    context.report_stop(my_time(0), FALSE); // FASLE = no success
   return error_code;
 }
 
@@ -376,9 +374,9 @@ pthread_mutex_t Backup_restore_ctx::run_
 
 Backup_restore_ctx::Backup_restore_ctx(THD *thd)
  :Logger(thd), m_state(CREATED), m_thd_options(thd->options),
-  m_error(0), m_remove_loc(FALSE), m_stream(NULL),
+  m_error(0), m_stream(NULL),
   m_catalog(NULL), mem_alloc(NULL), m_tables_locked(FALSE),
-  m_engage_binlog(FALSE)
+  m_engage_binlog(FALSE), m_completed(FALSE)
 {
   /*
     Check for progress tables.
@@ -625,9 +623,6 @@ Backup_restore_ctx::prepare_for_backup(S
     return NULL;
   }
 
-  // Mark that the file should be removed unless operation completes successfuly
-  m_remove_loc= TRUE;
-
   int my_open_status= s->open();
   if (my_open_status != 0)
   {
@@ -936,6 +931,35 @@ int Backup_restore_ctx::close()
 
   using namespace backup;
 
+  // Move context to error state if the catalog became corrupted.
+  if (m_catalog && !m_catalog->is_valid())
+    fatal_error(m_type == BACKUP ? ER_BACKUP_BACKUP : ER_BACKUP_RESTORE);
+
+  /*
+    Report end of the operation which has started if it has not been done 
+    before (Logger is in RUNNING state). 
+  */ 
+  if (Logger::m_state == RUNNING)
+  {
+    time_t  now= my_time(0);
+    if (m_catalog)
+      m_catalog->save_end_time(now);
+
+    // Report either completion or interruption depending on m_completed flag.
+    if (m_completed)
+      report_completed(now);
+    else
+    {
+      /*
+        If this is restore operation then m_data_changed flag in the 
+        Restore_info object tells if data has been modified or not.
+       */ 
+      const bool data_changed= m_type==RESTORE && m_catalog && 
+                         static_cast<Restore_info*>(m_catalog)->m_data_changed;
+      report_aborted(now, data_changed);
+    }
+  }
+
   /*
     Allow slaves connect after restore is complete.
   */
@@ -952,8 +976,6 @@ int Backup_restore_ctx::close()
   if (m_engage_binlog)
     obs::engage_binlog(TRUE);
 
-  time_t when= my_time(0);
-
   // unlock tables if they are still locked
   unlock_tables();                              // Never errors
 
@@ -964,7 +986,7 @@ int Backup_restore_ctx::close()
 
   m_thd->options= m_thd_options;
 
-  // close stream
+  // close stream if not closed already (in which case m_steam is NULL)
 
   if (m_stream && !m_stream->close())
   {
@@ -972,16 +994,13 @@ int Backup_restore_ctx::close()
     fatal_error(report_error(ER_BACKUP_CLOSE));
   }
 
-  if (m_catalog)
-    m_catalog->save_end_time(when); // Note: no errors.
-
   /* 
-    Remove the location, if asked for.
+    Remove the location if it is BACKUP operation and it has not completed
+    successfully.
     
-    Important: This is done only for backup operation - RESTORE should never
-    remove the specified backup image!
+    Important: RESTORE should never remove the specified backup image!
    */
-  if (m_remove_loc && m_state == PREPARED_FOR_BACKUP)
+  if (m_state == PREPARED_FOR_BACKUP && !m_completed)
   {
     int ret= my_delete(m_path.c_ptr(), MYF(0));
 
@@ -989,17 +1008,8 @@ int Backup_restore_ctx::close()
       Ignore ENOENT error since it is ok if the file doesn't exist.
      */
     if (ret && my_errno != ENOENT)
-      fatal_error(report_error(ER_CANT_DELETE_FILE, m_path.c_ptr(), my_errno));
-  }
-
-  /* We report completion of the operation only if no errors were detected,
-     and logger has been initialized.
-  */
-  if (!m_error)
-  {
-    if (backup::Logger::m_state == backup::Logger::RUNNING)
     {
-      report_stop(when, TRUE);
+      fatal_error(report_error(ER_CANT_DELETE_FILE, m_path.c_ptr(), my_errno));
     }
   }
 
@@ -1078,17 +1088,14 @@ int Backup_restore_ctx::do_backup()
   if (ret)
     DBUG_RETURN(fatal_error(report_error(ER_BACKUP_WRITE_SUMMARY)));
 
-  /*
-    Now backup image has been written. Set m_remove_loc to FALSE, so that the
-    backup file is not removed in Backup_restore_ctx::close().
-  */
-  m_remove_loc= FALSE;
+  DEBUG_SYNC(m_thd, "before_backup_completed");
+  m_completed= TRUE;
   report_stats_post(info);                      // Never errors
 
   DBUG_PRINT("backup",("Backup done."));
   DEBUG_SYNC(m_thd, "before_backup_done");
 
-  DBUG_RETURN(0);
+  DBUG_RETURN(close());
 }
 
 /**
@@ -1113,11 +1120,13 @@ int Backup_restore_ctx::restore_triggers
 
   DBUG_ENTER("restore_triggers_and_events");
 
+  DBUG_ASSERT(m_type == RESTORE);
+  Restore_info *info= static_cast<Restore_info*>(m_catalog);
   Image_info::Obj *obj;
   List<Image_info::Obj> events;
   Image_info::Obj::describe_buf buf;
 
-  Image_info::Iterator *dbit= m_catalog->get_dbs();
+  Image_info::Iterator *dbit= info->get_dbs();
   if (!dbit)
     DBUG_RETURN(fatal_error(report_error(ER_OUT_OF_RESOURCES)));
 
@@ -1126,7 +1135,7 @@ int Backup_restore_ctx::restore_triggers
   while ((obj= (*dbit)++)) 
   {
     Image_info::Iterator *it=
-                    m_catalog->get_db_objects(*static_cast<Image_info::Db*>(obj));
+                       info->get_db_objects(*static_cast<Image_info::Db*>(obj));
     if (!it)
       DBUG_RETURN(fatal_error(report_error(ER_OUT_OF_RESOURCES)));
 
@@ -1144,6 +1153,8 @@ int Backup_restore_ctx::restore_triggers
       
       case BSTREAM_IT_TRIGGER:
         DBUG_ASSERT(obj->m_obj_ptr);
+        // Mark that data is being changed.
+        info->m_data_changed= TRUE;
         if (obj->m_obj_ptr->create(m_thd))
         {
           delete it;
@@ -1168,11 +1179,15 @@ int Backup_restore_ctx::restore_triggers
   Image_info::Obj *ev;
 
   while ((ev= it++)) 
+  {
+    // Mark that data is being changed.
+    info->m_data_changed= TRUE;
     if (ev->m_obj_ptr->create(m_thd))
     {
       int ret= report_error(ER_BACKUP_CANT_RESTORE_EVENT,ev->describe(buf));
       DBUG_RETURN(fatal_error(ret));
     };
+  }
 
   DBUG_RETURN(0);
 }
@@ -1260,7 +1275,11 @@ int Backup_restore_ctx::do_restore(bool 
   if (err)
     DBUG_RETURN(fatal_error(err));
 
-  // Here restore drivers are created to restore table data
+  /* 
+   Here restore drivers are created to restore table data. Data is being
+   (potentially) changed so we set m_data_changed flag.
+  */
+  info.m_data_changed= TRUE;
   err= restore_table_data(m_thd, info, s);      // logs errors
 
   unlock_tables();                              // Never errors
@@ -1290,6 +1309,9 @@ int Backup_restore_ctx::do_restore(bool 
 
   DBUG_PRINT("restore",("Done."));
 
+  DEBUG_SYNC(m_thd, "before_restore_completed");
+  m_completed= TRUE;
+
   err= read_summary(info, s);
   if (err)
     DBUG_RETURN(fatal_error(report_error(ER_BACKUP_READ_SUMMARY)));
@@ -1307,7 +1329,7 @@ int Backup_restore_ctx::do_restore(bool 
 
   DEBUG_SYNC(m_thd, "before_restore_done");
 
-  DBUG_RETURN(0);
+  DBUG_RETURN(close());
 }
 
 /**
@@ -2040,6 +2062,9 @@ int bcat_create_item(st_bstream_image_he
     }
   }
 
+  // Mark that data is being changed.
+  info->m_data_changed= TRUE;
+
   if (sobj->create(thd))
   {
     log.report_error(create_err, desc);

=== modified file 'sql/backup/logger.h'
--- a/sql/backup/logger.h	2008-12-18 21:46:36 +0000
+++ b/sql/backup/logger.h	2009-01-08 14:57:41 +0000
@@ -58,7 +58,8 @@ public:
    int log_error(int error_code, ...);
 
    void report_start(time_t);
-   void report_stop(time_t, bool);
+   void report_completed(time_t);
+   void report_aborted(time_t, bool data_changed);
    void report_state(enum_backup_state);
    void report_vp_time(time_t, bool);
    void report_binlog_pos(const st_bstream_binlog_pos&);
@@ -67,6 +68,7 @@ public:
    void report_backup_file(char * path);
    void report_stats_pre(const Image_info&);
    void report_stats_post(const Image_info&);
+
    /// Return the Backup_id of the current operation
    ulonglong get_op_id() const 
    {
@@ -192,27 +194,73 @@ void Logger::report_start(time_t when)
   backup_log->state(BUP_RUNNING);
 }
 
+/** 
+  Report that the operation has completed successfully.
+
+  @param[in] when       the time when operation has completed.
+
+  @note This method can be called only after @c report_start(). It can not be
+  called after end of operation has been logged with either this method or
+  @c report_aborted().
+*/
+inline
+void Logger::report_completed(time_t when)
+{
+   DBUG_ASSERT(m_state == RUNNING);
+   DBUG_ASSERT(backup_log);
+
+   report_error(log_level::INFO, m_type == BACKUP ? ER_BACKUP_BACKUP_DONE
+                                                  : ER_BACKUP_RESTORE_DONE);  
+   report_state(BUP_COMPLETE);
+  
+  // Report stop time to backup logs.
+  backup_log->stop(when);
+  /* 
+    Since the operation has completed, we can now write the backup history log
+    entry describing it.
+  */
+  backup_log->write_history();
+}
+
 /**
-  Report end of the operation.
+  Report that backup/restore operation has been aborted.
+
+  @param[in] when  time when the operation has ended.
+  @param[in] data_changed  tells if data has been already modified in case
+                           this is restore operation.
+
+  This method should be called when backup/restore operation has been aborted
+  before its completion, e.g., because of an error or user interruption.
   
-  @param[in] when    time when report stopped 
-  @param[in] success indicates if the operation ended successfully
+  The method will log the stop time and ER_OPERATION_ABORTED warning.
+  However, if a restore operation has been interrupted and @c data_changed flag
+  is true, ER_OPERATION_ABORTED_CORRUPTED warning will be logged, to warn the 
+  user about the possibility of data corruption.
+  
+  @note This method must be called after @c report_start(). It can not be
+  called after end of operation has been logged with either this method or
+  @c report_completed().
  */
 inline
-void Logger::report_stop(time_t when, bool success)
+void Logger::report_aborted(time_t when, bool data_changed)
 {
-  if (m_state == DONE)
-    return;
-
   DBUG_ASSERT(m_state == RUNNING);
   DBUG_ASSERT(backup_log);
 
-  report_error(log_level::INFO, m_type == BACKUP ? ER_BACKUP_BACKUP_DONE
-                                                 : ER_BACKUP_RESTORE_DONE);  
+ if (m_type == RESTORE && data_changed)
+   report_error(log_level::WARNING, ER_OPERATION_ABORTED_CORRUPTED);
+ else
+   report_error(log_level::WARNING, ER_OPERATION_ABORTED);
+
+  // Report stop time to backup logs.
 
   backup_log->stop(when);
-  backup_log->state(success ? BUP_COMPLETE : BUP_ERRORS);
+  /* 
+    Since the operation has ended, we can now write the backup history log
+    entry describing it.
+  */
   backup_log->write_history();
+
   m_state= DONE;
 }
 

=== modified file 'sql/backup/restore_info.h'
--- a/sql/backup/restore_info.h	2008-12-18 21:46:36 +0000
+++ b/sql/backup/restore_info.h	2009-01-08 14:57:41 +0000
@@ -38,7 +38,7 @@ public:
   ~Restore_info();
 
   /// Determine of information class is valid.
-  bool is_valid() const;
+  bool is_valid();
 
   Image_info::Ts* add_ts(const ::String&, uint);
   Image_info::Db* add_db(const ::String&, uint);
@@ -59,6 +59,9 @@ private:
 
   THD *m_thd;
 
+  /// A flag to indicate that data has been modified during restore operation.
+  bool m_data_changed;
+
   friend int backup::restore_table_data(THD*, Restore_info&, 
                                         backup::Input_stream&);
   friend int ::bcat_add_item(st_bstream_image_header*,
@@ -72,7 +75,7 @@ private:
 
 inline
 Restore_info::Restore_info(backup::Logger &log, THD *thd)
-  :m_log(log), m_thd(thd)
+  :m_log(log), m_thd(thd), m_data_changed(FALSE)
 {}
 
 inline
@@ -86,7 +89,7 @@ Restore_info::~Restore_info()
 }
 
 inline
-bool Restore_info::is_valid() const
+bool Restore_info::is_valid()
 {
   return TRUE; 
 }

=== modified file 'sql/share/errmsg.txt'
--- a/sql/share/errmsg.txt	2008-12-16 20:54:07 +0000
+++ b/sql/share/errmsg.txt	2009-01-08 14:57:41 +0000
@@ -6449,3 +6449,7 @@ ER_BACKUP_SYNCHRONIZE
   eng "Backup failed to synchronize table images."
 ER_RESTORE_CANNOT_START_SLAVE
   eng "Cannot start slave. SLAVE START is blocked by %-.64s."
+ER_OPERATION_ABORTED
+  eng "Operation aborted"
+ER_OPERATION_ABORTED_CORRUPTED
+  eng "Operation aborted - data might be corrupted"

Thread
bzr commit into mysql-6.0-backup branch (Rafal.Somla:2748) Bug#40305Rafal Somla8 Jan