List:Commits« Previous MessageNext Message »
From:Alfranio Correia Date:December 4 2009 3:17pm
Subject:bzr commit into mysql-pe branch (alfranio.correia:3742)
View as plain text  
#At file:///home/acorreia/workspace.sun/repository.mysql/bzrwork/bug-45292/mysql-pe/ based on revid:gshchepa@stripped

 3742 Alfranio Correia	2009-12-04 [merge]
      merge mysql-5.1-bugteam --> mysql-pe

    modified:
      mysql-test/suite/binlog/r/binlog_index.result
      mysql-test/suite/binlog/t/binlog_index.test
      mysql-test/suite/rpl/r/rpl_bug41902.result
      mysql-test/suite/rpl/t/rpl_bug41902.test
      sql/log.cc
      sql/log.h
      sql/mysqld.cc
      sql/rpl_rli.cc
=== modified file 'mysql-test/suite/binlog/r/binlog_index.result'
--- a/mysql-test/suite/binlog/r/binlog_index.result	2009-09-25 14:15:30 +0000
+++ b/mysql-test/suite/binlog/r/binlog_index.result	2009-12-04 15:16:06 +0000
@@ -1,3 +1,8 @@
+call mtr.add_suppression('Attempting backtrace');
+call mtr.add_suppression('MSYQL_BIN_LOG::purge_logs failed to process registered files that would be purged.');
+call mtr.add_suppression('MSYQL_BIN_LOG::open failed to sync the index file');
+call mtr.add_suppression('Turning logging off for the whole duration of the MySQL server process.');
+call mtr.add_suppression('MSYQL_BIN_LOG::purge_logs failed to clean registers before purging logs.');
 flush logs;
 flush logs;
 flush logs;
@@ -21,7 +26,6 @@ flush logs;
 *** must be a warning master-bin.000001 was not found ***
 Warnings:
 Warning	1612	Being purged log master-bin.000001 was not found
-Warning	1612	Being purged log master-bin.000001 was not found
 *** must show one record, of the active binlog, left in the index file after PURGE ***
 show binary logs;
 Log_name	File_size
@@ -31,10 +35,117 @@ flush logs;
 flush logs;
 flush logs;
 purge binary logs TO 'master-bin.000002';
-ERROR HY000: Fatal error during log purge
+ERROR HY000: a problem with deleting ./master-bin.000001; consider examining correspondence of your binlog index file to the actual binlog files
 show warnings;
 Level	Code	Message
-Warning	1377	a problem with deleting master-bin.000001; consider examining correspondence of your binlog index file to the actual binlog files
+Error	1377	a problem with deleting master-bin.000001; consider examining correspondence of your binlog index file to the actual binlog files
 Error	1377	Fatal error during log purge
 reset master;
+# crash_purge_before_update_index
+flush logs;
+SET SESSION debug="+d,crash_purge_before_update_index";
+purge binary logs TO 'master-bin.000002';
+ERROR HY000: Lost connection to MySQL server during query
+SET @index=LOAD_FILE('MYSQLTEST_VARDIR/mysqld.1/data//master-bin.index');
+SELECT @index;
+@index
+master-bin.000001
+master-bin.000002
+master-bin.000003
+
+# crash_purge_non_critical_after_update_index
+flush logs;
+SET SESSION debug="+d,crash_purge_non_critical_after_update_index";
+purge binary logs TO 'master-bin.000004';
+ERROR HY000: Lost connection to MySQL server during query
+SET @index=LOAD_FILE('MYSQLTEST_VARDIR/mysqld.1/data//master-bin.index');
+SELECT @index;
+@index
+master-bin.000004
+master-bin.000005
+
+# crash_purge_critical_after_update_index
+flush logs;
+SET SESSION debug="+d,crash_purge_critical_after_update_index";
+purge binary logs TO 'master-bin.000006';
+ERROR HY000: Lost connection to MySQL server during query
+SET @index=LOAD_FILE('MYSQLTEST_VARDIR/mysqld.1/data//master-bin.index');
+SELECT @index;
+@index
+master-bin.000006
+master-bin.000007
+
+# crash_create_non_critical_before_update_index
+SET SESSION debug="+d,crash_create_non_critical_before_update_index";
+flush logs;
+ERROR HY000: Lost connection to MySQL server during query
+SET @index=LOAD_FILE('MYSQLTEST_VARDIR/mysqld.1/data//master-bin.index');
+SELECT @index;
+@index
+master-bin.000006
+master-bin.000007
+master-bin.000008
+
+# crash_create_critical_before_update_index
+SET SESSION debug="+d,crash_create_critical_before_update_index";
+flush logs;
+ERROR HY000: Lost connection to MySQL server during query
+SET @index=LOAD_FILE('MYSQLTEST_VARDIR/mysqld.1/data//master-bin.index');
+SELECT @index;
+@index
+master-bin.000006
+master-bin.000007
+master-bin.000008
+master-bin.000009
+
+# crash_create_after_update_index
+SET SESSION debug="+d,crash_create_after_update_index";
+flush logs;
+ERROR HY000: Lost connection to MySQL server during query
+SET @index=LOAD_FILE('MYSQLTEST_VARDIR/mysqld.1/data//master-bin.index');
+SELECT @index;
+@index
+master-bin.000006
+master-bin.000007
+master-bin.000008
+master-bin.000009
+master-bin.000010
+master-bin.000011
+
+#
+# This should put the server in unsafe state and stop
+# accepting any command. If we inject a fault at this
+# point and continue the execution the server crashes.
+# Besides the flush command does not report an error.
+#
+# fault_injection_registering_index
+SET SESSION debug="+d,fault_injection_registering_index";
+flush logs;
+SET @index=LOAD_FILE('MYSQLTEST_VARDIR/mysqld.1/data//master-bin.index');
+SELECT @index;
+@index
+master-bin.000006
+master-bin.000007
+master-bin.000008
+master-bin.000009
+master-bin.000010
+master-bin.000011
+master-bin.000012
+
+# fault_injection_updating_index
+SET SESSION debug="+d,fault_injection_updating_index";
+flush logs;
+SET @index=LOAD_FILE('MYSQLTEST_VARDIR/mysqld.1/data//master-bin.index');
+SELECT @index;
+@index
+master-bin.000006
+master-bin.000007
+master-bin.000008
+master-bin.000009
+master-bin.000010
+master-bin.000011
+master-bin.000012
+master-bin.000013
+
+SET SESSION debug="";
 End of tests

=== modified file 'mysql-test/suite/binlog/t/binlog_index.test'
--- a/mysql-test/suite/binlog/t/binlog_index.test	2008-04-05 11:09:53 +0000
+++ b/mysql-test/suite/binlog/t/binlog_index.test	2009-12-04 14:40:42 +0000
@@ -3,6 +3,15 @@
 #
 source include/have_log_bin.inc;
 source include/not_embedded.inc;
+call mtr.add_suppression('Attempting backtrace');
+call mtr.add_suppression('MSYQL_BIN_LOG::purge_logs failed to process registered files that would be purged.');
+call mtr.add_suppression('MSYQL_BIN_LOG::open failed to sync the index file');
+call mtr.add_suppression('Turning logging off for the whole duration of the MySQL server process.');
+call mtr.add_suppression('MSYQL_BIN_LOG::purge_logs failed to clean registers before purging logs.');
+let $old=`select @@debug`;
+
+let $MYSQLD_DATADIR= `select @@datadir`;
+let $INDEX=$MYSQLD_DATADIR/master-bin.index;
 
 #
 # testing purge binary logs TO
@@ -13,7 +22,6 @@ flush logs;
 flush logs;
 
 source include/show_binary_logs.inc;
-let $MYSQLD_DATADIR= `select @@datadir`;
 remove_file $MYSQLD_DATADIR/master-bin.000001;
 
 # there must be a warning with file names
@@ -66,4 +74,159 @@ rmdir $MYSQLD_DATADIR/master-bin.000001;
 --disable_warnings
 reset master;
 --enable_warnings
+
+--echo # crash_purge_before_update_index
+flush logs;
+
+--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+SET SESSION debug="+d,crash_purge_before_update_index";
+--error 2013
+purge binary logs TO 'master-bin.000002';
+
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+file_exists $MYSQLD_DATADIR/master-bin.000001;
+file_exists $MYSQLD_DATADIR/master-bin.000002;
+file_exists $MYSQLD_DATADIR/master-bin.000003;
+--chmod 0644 $INDEX
+-- replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+-- eval SET @index=LOAD_FILE('$index')
+-- replace_regex /\.[\\\/]master/master/
+SELECT @index;
+
+--echo # crash_purge_non_critical_after_update_index
+flush logs;
+
+--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+SET SESSION debug="+d,crash_purge_non_critical_after_update_index";
+--error 2013
+purge binary logs TO 'master-bin.000004';
+
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+--error 1
+file_exists $MYSQLD_DATADIR/master-bin.000001;
+--error 1
+file_exists $MYSQLD_DATADIR/master-bin.000002;
+--error 1
+file_exists $MYSQLD_DATADIR/master-bin.000003;
+--chmod 0644 $INDEX
+-- replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+-- eval SET @index=LOAD_FILE('$index')
+-- replace_regex /\.[\\\/]master/master/
+SELECT @index;
+
+--echo # crash_purge_critical_after_update_index
+flush logs;
+
+--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+SET SESSION debug="+d,crash_purge_critical_after_update_index";
+--error 2013
+purge binary logs TO 'master-bin.000006';
+
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+--error 1
+file_exists $MYSQLD_DATADIR/master-bin.000004;
+--error 1
+file_exists $MYSQLD_DATADIR/master-bin.000005;
+file_exists $MYSQLD_DATADIR/master-bin.000006;
+file_exists $MYSQLD_DATADIR/master-bin.000007;
+--error 1
+file_exists $MYSQLD_DATADIR/master-bin.000008;
+--chmod 0644 $INDEX
+-- replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+-- eval SET @index=LOAD_FILE('$index')
+-- replace_regex /\.[\\\/]master/master/
+SELECT @index;
+
+--echo # crash_create_non_critical_before_update_index
+--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+SET SESSION debug="+d,crash_create_non_critical_before_update_index";
+--error 2013
+flush logs;
+
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+file_exists $MYSQLD_DATADIR/master-bin.000008;
+--error 1
+file_exists $MYSQLD_DATADIR/master-bin.000009;
+--chmod 0644 $INDEX
+-- replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+-- eval SET @index=LOAD_FILE('$index')
+-- replace_regex /\.[\\\/]master/master/
+SELECT @index;
+
+--echo # crash_create_critical_before_update_index
+--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+SET SESSION debug="+d,crash_create_critical_before_update_index";
+--error 2013
+flush logs;
+
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+file_exists $MYSQLD_DATADIR/master-bin.000009;
+--error 1
+file_exists $MYSQLD_DATADIR/master-bin.000010;
+--error 1
+file_exists $MYSQLD_DATADIR/master-bin.000011;
+--chmod 0644 $INDEX
+-- replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+-- eval SET @index=LOAD_FILE('$index')
+-- replace_regex /\.[\\\/]master/master/
+SELECT @index;
+
+--echo # crash_create_after_update_index
+--exec echo "restart" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+SET SESSION debug="+d,crash_create_after_update_index";
+--error 2013
+flush logs;
+
+--enable_reconnect
+--source include/wait_until_connected_again.inc
+
+file_exists $MYSQLD_DATADIR/master-bin.000010;
+file_exists $MYSQLD_DATADIR/master-bin.000011;
+--chmod 0644 $INDEX
+-- replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+-- eval SET @index=LOAD_FILE('$index')
+-- replace_regex /\.[\\\/]master/master/
+SELECT @index;
+
+--echo #
+--echo # This should put the server in unsafe state and stop
+--echo # accepting any command. If we inject a fault at this
+--echo # point and continue the execution the server crashes.
+--echo # Besides the flush command does not report an error.
+--echo #
+
+--echo # fault_injection_registering_index
+SET SESSION debug="+d,fault_injection_registering_index";
+flush logs;
+--source include/restart_mysqld.inc
+
+--chmod 0644 $INDEX
+-- replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+-- eval SET @index=LOAD_FILE('$index')
+-- replace_regex /\.[\\\/]master/master/
+SELECT @index;
+
+--echo # fault_injection_updating_index
+SET SESSION debug="+d,fault_injection_updating_index";
+flush logs;
+--source include/restart_mysqld.inc
+
+--chmod 0644 $INDEX
+-- replace_result $MYSQLTEST_VARDIR MYSQLTEST_VARDIR
+-- eval SET @index=LOAD_FILE('$index')
+-- replace_regex /\.[\\\/]master/master/
+SELECT @index;
+
+eval SET SESSION debug="$old";
+
 --echo End of tests

=== modified file 'mysql-test/suite/rpl/r/rpl_bug41902.result'
--- a/mysql-test/suite/rpl/r/rpl_bug41902.result	2009-09-01 12:31:10 +0000
+++ b/mysql-test/suite/rpl/r/rpl_bug41902.result	2009-12-04 15:16:06 +0000
@@ -4,6 +4,7 @@ reset master;
 reset slave;
 drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
 start slave;
+call mtr.add_suppression('MYSQL_BIN_LOG::purge_logs was called with file ./master-bin.000001 not listed in the index.');
 stop slave;
 SET @@debug="d,simulate_find_log_pos_error";
 reset slave;

=== modified file 'mysql-test/suite/rpl/t/rpl_bug41902.test'
--- a/mysql-test/suite/rpl/t/rpl_bug41902.test	2009-09-01 12:31:10 +0000
+++ b/mysql-test/suite/rpl/t/rpl_bug41902.test	2009-12-04 15:16:06 +0000
@@ -4,6 +4,8 @@
 
 source include/have_debug.inc;
 source include/master-slave.inc;
+call mtr.add_suppression('MYSQL_BIN_LOG::purge_logs was called with file ./master-bin.000001 not listed in the index.');
+
 
 #
 # test checks that 
@@ -17,7 +19,6 @@ connection slave;
 stop slave;
 
 SET @@debug="d,simulate_find_log_pos_error";
-
 --error ER_UNKNOWN_TARGET_BINLOG
 reset slave;
 show warnings;

=== modified file 'sql/log.cc'
--- a/sql/log.cc	2009-11-27 14:56:49 +0000
+++ b/sql/log.cc	2009-12-04 15:16:06 +0000
@@ -3193,6 +3193,22 @@ void MYSQL_LOG::init(enum_log_type log_t
 }
 
 
+bool MYSQL_LOG::init_and_set_log_file_name(const char *log_name,
+                                           const char *new_name,
+                                           enum_log_type log_type_arg,
+                                           enum cache_type io_cache_type_arg)
+{
+  init(log_type_arg, io_cache_type_arg);
+
+  if (new_name && !strmov(log_file_name, new_name))
+    return TRUE;
+  else if (!new_name && generate_new_name(log_file_name, log_name))
+    return TRUE;
+
+  return FALSE;
+}
+
+
 /*
   Open a (new) log file.
 
@@ -3225,17 +3241,14 @@ bool MYSQL_LOG::open(const char *log_nam
 
   write_error= 0;
 
-  init(log_type_arg, io_cache_type_arg);
-
   if (!(name= my_strdup(log_name, MYF(MY_WME))))
   {
     name= (char *)log_name; // for the error message
     goto err;
   }
 
-  if (new_name)
-    strmov(log_file_name, new_name);
-  else if (generate_new_name(log_file_name, name))
+  if (init_and_set_log_file_name(name, new_name,
+                                 log_type_arg, io_cache_type_arg))
     goto err;
 
   if (io_cache_type == SEQ_READ_APPEND)
@@ -4322,7 +4335,7 @@ MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_
   */
   index_file_name[0] = 0;
   bzero((char*) &index_file, sizeof(index_file));
-  bzero((char*) &purge_temp, sizeof(purge_temp));
+  bzero((char*) &purge_index_file, sizeof(purge_index_file));
 }
 
 /* this is called only once */
@@ -4371,7 +4384,7 @@ void MYSQL_BIN_LOG::init_pthread_objects
 
 
 bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
-                                const char *log_name)
+                                    const char *log_name, bool need_mutex)
 {
   File index_file_nr= -1;
   DBUG_ASSERT(!my_b_inited(&index_file));
@@ -4396,7 +4409,8 @@ bool MYSQL_BIN_LOG::open_index_file(cons
        init_io_cache(&index_file, index_file_nr,
                      IO_SIZE, WRITE_CACHE,
                      my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)),
-			0, MYF(MY_WME | MY_WAIT_IF_FULL)))
+			0, MYF(MY_WME | MY_WAIT_IF_FULL)) ||
+      DBUG_EVALUATE_IF("fault_injection_openning_index", 1, 0))
   {
     /*
       TODO: all operations creating/deleting the index file or a log, should
@@ -4407,6 +4421,28 @@ bool MYSQL_BIN_LOG::open_index_file(cons
       my_close(index_file_nr,MYF(0));
     return TRUE;
   }
+
+#ifdef HAVE_REPLICATION
+  /*
+    Sync the index by purging any binary log file that is not registered.
+    In other words, either purge binary log files that were removed from
+    the index but not purged from the file system due to a crash or purge
+    any binary log file that was created but not register in the index
+    due to a crash.
+  */
+
+  if (set_purge_index_file_name(index_file_name_arg) ||
+      open_purge_index_file(FALSE) ||
+      purge_index_entry(NULL, NULL, need_mutex) ||
+      close_purge_index_file() ||
+      DBUG_EVALUATE_IF("fault_injection_recovering_index", 1, 0))
+  {
+    sql_print_error("MYSQL_BIN_LOG::open_index_file failed to sync the index "
+                    "file.");
+    return TRUE;
+  }
+#endif
+
   return FALSE;
 }
 
@@ -4431,17 +4467,44 @@ bool MYSQL_BIN_LOG::open(const char *log
                          enum cache_type io_cache_type_arg,
                          bool no_auto_events_arg,
                          ulong max_size_arg,
-                         bool null_created_arg)
+                         bool null_created_arg,
+                         bool need_mutex)
 {
   File file= -1;
+
   DBUG_ENTER("MYSQL_BIN_LOG::open");
   DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg));
 
-  write_error=0;
+  if (init_and_set_log_file_name(log_name, new_name, log_type_arg,
+                                 io_cache_type_arg))
+  {
+    sql_print_error("MSYQL_BIN_LOG::open failed to generate new file name.");
+    DBUG_RETURN(1);
+  }
+
+#ifdef HAVE_REPLICATION
+  if (open_purge_index_file(TRUE) ||
+      register_create_index_entry(log_file_name) ||
+      sync_purge_index_file() ||
+      DBUG_EVALUATE_IF("fault_injection_registering_index", 1, 0))
+  {
+    sql_print_error("MSYQL_BIN_LOG::open failed to sync the index file.");
+    DBUG_RETURN(1);
+  }
+  DBUG_EXECUTE_IF("crash_create_non_critical_before_update_index", abort(););
+#endif
+
+  write_error= 0;
 
   /* open the main log file */
-  if (MYSQL_LOG::open(log_name, log_type_arg, new_name, io_cache_type_arg))
+  if (MYSQL_LOG::open(log_name, log_type_arg, new_name,
+                      io_cache_type_arg))
+  {
+#ifdef HAVE_REPLICATION
+    close_purge_index_file();
+#endif
     DBUG_RETURN(1);                            /* all warnings issued */
+  }
 
   init(no_auto_events_arg, max_size_arg);
 
@@ -4467,9 +4530,6 @@ bool MYSQL_BIN_LOG::open(const char *log
       write_file_name_to_index_file= 1;
     }
 
-    DBUG_ASSERT(my_b_inited(&index_file) != 0);
-    reinit_io_cache(&index_file, WRITE_CACHE,
-                    my_b_filelength(&index_file), 0, 0);
     if (need_start_event && !no_auto_events)
     {
       /*
@@ -4527,23 +4587,44 @@ bool MYSQL_BIN_LOG::open(const char *log
 
     if (write_file_name_to_index_file)
     {
+#ifdef HAVE_REPLICATION
+      DBUG_EXECUTE_IF("crash_create_critical_before_update_index", abort(););
+#endif
+
+      DBUG_ASSERT(my_b_inited(&index_file) != 0);
+      reinit_io_cache(&index_file, WRITE_CACHE,
+                      my_b_filelength(&index_file), 0, 0);
       /*
         As this is a new log file, we write the file name to the index
         file. As every time we write to the index file, we sync it.
       */
-      if (my_b_write(&index_file, (uchar*) log_file_name,
-		     strlen(log_file_name)) ||
-	  my_b_write(&index_file, (uchar*) "\n", 1) ||
-	  flush_io_cache(&index_file) ||
+      if (DBUG_EVALUATE_IF("fault_injection_updating_index", 1, 0) ||
+          my_b_write(&index_file, (uchar*) log_file_name,
+                     strlen(log_file_name)) ||
+          my_b_write(&index_file, (uchar*) "\n", 1) ||
+          flush_io_cache(&index_file) ||
           my_sync(index_file.file, MYF(MY_WME)))
-	goto err;
+        goto err;
+
+#ifdef HAVE_REPLICATION
+      DBUG_EXECUTE_IF("crash_create_after_update_index", abort(););
+#endif
     }
   }
   log_state= LOG_OPENED;
 
+#ifdef HAVE_REPLICATION
+  close_purge_index_file();
+#endif
+
   DBUG_RETURN(0);
 
 err:
+#ifdef HAVE_REPLICATION
+  if (is_inited_purge_index_file())
+    purge_index_entry(NULL, NULL, need_mutex);
+  close_purge_index_file();
+#endif
   sql_print_error("Could not use %s for logging (error %d). \
 Turning logging off for the whole duration of the MySQL server process. \
 To turn it on again: fix the cause, \
@@ -4807,8 +4888,15 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
   name=0;					// Protect against free
   close(LOG_CLOSE_TO_BE_OPENED);
 
-  /* First delete all old log files */
+  /*
+    First delete all old log files and then update the index file.
+    As we first delete the log files and do not use sort of logging,
+    a crash may lead to an inconsistent state where the index has
+    references to non-existent files.
 
+    We need to invert the steps and use the purge_index_file methods
+    in order to make the operation safe.
+  */
   if ((err= find_log_pos(&linfo, NullS, 0)) != 0)
   {
     uint errcode= purge_log_get_error_code(err);
@@ -4878,8 +4966,8 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd)
   }
   if (!thd->slave_thread)
     need_start_event=1;
-  if (!open_index_file(index_file_name, 0))
-    open(save_name, log_type, 0, io_cache_type, no_auto_events, max_size, 0);
+  if (!open_index_file(index_file_name, 0, FALSE))
+    open(save_name, log_type, 0, io_cache_type, no_auto_events, max_size, 0, FALSE);
   my_free((uchar*) save_name, MYF(0));
 
 err:
@@ -5068,7 +5156,7 @@ int MYSQL_BIN_LOG::purge_logs(const char
                           bool need_update_threads, 
                           ulonglong *decrease_log_space)
 {
-  int error;
+  int error= 0;
   bool exit_loop= 0;
   LOG_INFO log_info;
   THD *thd= current_thd;
@@ -5079,33 +5167,15 @@ int MYSQL_BIN_LOG::purge_logs(const char
     pthread_mutex_lock(&LOCK_index);
   if ((error=find_log_pos(&log_info, to_log, 0 /*no mutex*/))) 
   {
-    sql_print_error("MYSQL_LOG::purge_logs was called with file %s not "
+    sql_print_error("MYSQL_BIN_LOG::purge_logs was called with file %s not "
                     "listed in the index.", to_log);
     goto err;
   }
 
-  /*
-    For crash recovery reasons the index needs to be updated before
-    any files are deleted. Move files to be deleted into a temp file
-    to be processed after the index is updated.
-  */
-  if (!my_b_inited(&purge_temp))
-  {
-    if ((error=open_cached_file(&purge_temp, mysql_tmpdir, TEMP_PREFIX,
-                                DISK_BUFFER_SIZE, MYF(MY_WME))))
-    {
-      sql_print_error("MYSQL_LOG::purge_logs failed to open purge_temp");
-      goto err;
-    }
-  }
-  else
+  if ((error= open_purge_index_file(TRUE)))
   {
-    if ((error=reinit_io_cache(&purge_temp, WRITE_CACHE, 0, 0, 1)))
-    {
-      sql_print_error("MYSQL_LOG::purge_logs failed to reinit purge_temp "
-                      "for write");
-      goto err;
-    }
+    sql_print_error("MYSQL_BIN_LOG::purge_logs failed to sync the index file.");
+    goto err;
   }
 
   /*
@@ -5115,51 +5185,181 @@ int MYSQL_BIN_LOG::purge_logs(const char
   if ((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/)))
     goto err;
   while ((strcmp(to_log,log_info.log_file_name) || (exit_loop=included)) &&
+         !is_active(log_info.log_file_name) &&
          !log_in_use(log_info.log_file_name))
   {
-    if ((error=my_b_write(&purge_temp, (const uchar*)log_info.log_file_name,
-                          strlen(log_info.log_file_name))) ||
-        (error=my_b_write(&purge_temp, (const uchar*)"\n", 1)))
+    if ((error= register_purge_index_entry(log_info.log_file_name)))
     {
-      sql_print_error("MYSQL_LOG::purge_logs failed to copy %s to purge_temp",
+      sql_print_error("MYSQL_BIN_LOG::purge_logs failed to copy %s to register file.",
                       log_info.log_file_name);
       goto err;
     }
 
     if (find_next_log(&log_info, 0) || exit_loop)
-      break;
- }
+       break;
+  }
+
+  DBUG_EXECUTE_IF("crash_purge_before_update_index", abort(););
+
+  if ((error= sync_purge_index_file()))
+  {
+    sql_print_error("MSYQL_BIN_LOG::purge_logs failed to flush register file.");
+    goto err;
+  }
 
   /* We know how many files to delete. Update index file. */
   if ((error=update_log_index(&log_info, need_update_threads)))
   {
-    sql_print_error("MSYQL_LOG::purge_logs failed to update the index file");
+    sql_print_error("MSYQL_BIN_LOG::purge_logs failed to update the index file");
     goto err;
   }
 
-  DBUG_EXECUTE_IF("crash_after_update_index", DBUG_ABORT(););
+  DBUG_EXECUTE_IF("crash_purge_critical_after_update_index", abort(););
+
+err:
+  /* Read each entry from purge_index_file and delete the file. */
+  if (is_inited_purge_index_file() && 
+      (error= purge_index_entry(thd, decrease_log_space, FALSE)))
+    sql_print_error("MSYQL_BIN_LOG::purge_logs failed to process registered files"
+                    " that would be purged.");
+  close_purge_index_file();
+
+  DBUG_EXECUTE_IF("crash_purge_non_critical_after_update_index", abort(););
+
+  if (need_mutex)
+    pthread_mutex_unlock(&LOCK_index);
+  DBUG_RETURN(error);
+}
+
+int MYSQL_BIN_LOG::set_purge_index_file_name(const char *base_file_name)
+{
+  int error= 0;
+  DBUG_ENTER("MYSQL_BIN_LOG::set_purge_index_file_name");
+  if (fn_format(purge_index_file_name, base_file_name, mysql_data_home,
+                ".~rec~", MYF(MY_UNPACK_FILENAME | MY_SAFE_PATH |
+                              MY_REPLACE_EXT)) == NULL)
+  {
+    error= 1;
+    sql_print_error("MYSQL_BIN_LOG::set_purge_index_file_name failed to set "
+                    "file name.");
+  }
+  DBUG_RETURN(error);
+}
+
+int MYSQL_BIN_LOG::open_purge_index_file(bool destroy)
+{
+  int error= 0;
+  File file= -1;
+
+  DBUG_ENTER("MYSQL_BIN_LOG::open_purge_index_file");
+
+  if (destroy)
+    close_purge_index_file();
+
+  if (!my_b_inited(&purge_index_file))
+  {
+    if ((file= my_open(purge_index_file_name, O_RDWR | O_CREAT | O_BINARY,
+                       MYF(MY_WME | ME_WAITTANG))) < 0  ||
+        init_io_cache(&purge_index_file, file, IO_SIZE,
+                      (destroy ? WRITE_CACHE : READ_CACHE),
+                      0, 0, MYF(MY_WME | MY_NABP | MY_WAIT_IF_FULL)))
+    {
+      error= 1;
+      sql_print_error("MYSQL_BIN_LOG::open_purge_index_file failed to open register "
+                      " file.");
+    }
+  }
+  DBUG_RETURN(error);
+}
+
+int MYSQL_BIN_LOG::close_purge_index_file()
+{
+  int error= 0;
+
+  DBUG_ENTER("MYSQL_BIN_LOG::close_purge_index_file");
+
+  if (my_b_inited(&purge_index_file))
+  {
+    end_io_cache(&purge_index_file);
+    error= my_close(purge_index_file.file, MYF(0));
+  }
+  my_delete(purge_index_file_name, MYF(0));
+  bzero((char*) &purge_index_file, sizeof(purge_index_file));
+
+  DBUG_RETURN(error);
+}
+
+bool MYSQL_BIN_LOG::is_inited_purge_index_file()
+{
+  DBUG_ENTER("MYSQL_BIN_LOG::is_inited_purge_index_file");
+  DBUG_RETURN (my_b_inited(&purge_index_file));
+}
+
+int MYSQL_BIN_LOG::sync_purge_index_file()
+{
+  int error= 0;
+  DBUG_ENTER("MYSQL_BIN_LOG::sync_purge_index_file");
+
+  if ((error= flush_io_cache(&purge_index_file)) ||
+      (error= my_sync(purge_index_file.file, MYF(MY_WME))))
+    DBUG_RETURN(error);
+
+  DBUG_RETURN(error);
+}
+
+int MYSQL_BIN_LOG::register_purge_index_entry(const char *entry)
+{
+  int error= 0;
+  DBUG_ENTER("MYSQL_BIN_LOG::register_purge_index_entry");
+
+  if ((error=my_b_write(&purge_index_file, (const uchar*)entry, strlen(entry))) ||
+      (error=my_b_write(&purge_index_file, (const uchar*)"\n", 1)))
+    DBUG_RETURN (error);
+
+  DBUG_RETURN(error);
+}
+
+int MYSQL_BIN_LOG::register_create_index_entry(const char *entry)
+{
+  DBUG_ENTER("MYSQL_BIN_LOG::register_create_index_entry");
+  DBUG_RETURN(register_purge_index_entry(entry));
+}
+
+int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *decrease_log_space,
+                                     bool need_mutex)
+{
+  MY_STAT s;
+  int error= 0;
+  LOG_INFO log_info;
+  LOG_INFO check_log_info;
+
+  DBUG_ENTER("MYSQL_BIN_LOG:purge_index_entry");
+
+  /* 
+    The index file must be initialized before we proceed with the purge
+    operation.
+  */
+  DBUG_ASSERT(my_b_inited(&purge_index_file));
 
-  /* Switch purge_temp for read. */
-  if ((error=reinit_io_cache(&purge_temp, READ_CACHE, 0, 0, 0)))
+  if ((error= reinit_io_cache(&purge_index_file, READ_CACHE, 0, 0, 0)))
   {
-    sql_print_error("MSYQL_LOG::purge_logs failed to reinit purge_temp "
-                    "for read");
+    sql_print_error("MSYQL_BIN_LOG::purge_index_entry failed to reinit register "
+                    "file for read");
     goto err;
   }
 
-  /* Read each entry from purge_temp and delete the file. */
   for (;;)
   {
     uint length;
 
-    if ((length=my_b_gets(&purge_temp, log_info.log_file_name,
+    if ((length=my_b_gets(&purge_index_file, log_info.log_file_name,
                           FN_REFLEN)) <= 1)
     {
-      if (purge_temp.error)
+      if (purge_index_file.error)
       {
-        error= purge_temp.error;
-        sql_print_error("MSYQL_LOG::purge_logs error %d reading from "
-                        "purge_temp", error);
+        error= purge_index_file.error;
+        sql_print_error("MSYQL_BIN_LOG::purge_index_entry error %d reading from "
+                        "register file.", error);
         goto err;
       }
 
@@ -5170,9 +5370,6 @@ int MYSQL_BIN_LOG::purge_logs(const char
     /* Get rid of the trailing '\n' */
     log_info.log_file_name[length-1]= 0;
 
-    ha_binlog_index_purge_file(current_thd, log_info.log_file_name);
-
-    MY_STAT s;
     if (!my_stat(log_info.log_file_name, &s, MYF(0)))
     {
       if (my_errno == ENOENT) 
@@ -5220,67 +5417,96 @@ int MYSQL_BIN_LOG::purge_logs(const char
     }
     else
     {
-      DBUG_PRINT("info",("purging %s",log_info.log_file_name));
-      if (!my_delete(log_info.log_file_name, MYF(0)))
+      if ((error= find_log_pos(&check_log_info, log_info.log_file_name, need_mutex)))
       {
-        if (decrease_log_space)
-          *decrease_log_space-= s.st_size;
-      }
-      else
-      {
-        if (my_errno == ENOENT) 
+        if (error != LOG_INFO_EOF)
         {
           if (thd)
           {
-            push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
-                                ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
+            push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+                                ER_BINLOG_PURGE_FATAL_ERR,
+                                "a problem with deleting %s and "
+                                "reading the binlog index file",
                                 log_info.log_file_name);
           }
-          sql_print_information("Failed to delete file '%s'",
-                                log_info.log_file_name);
-          my_errno= 0;
+          else
+          {
+            sql_print_information("Failed to delete file '%s' and "
+                                  "read the binlog index file",
+                                  log_info.log_file_name);
+          }
+          goto err;
+        }
+
+        error= 0;
+        if (!need_mutex)
+        {
+          /*
+            This is to avoid triggering an error in NDB.
+          */
+          ha_binlog_index_purge_file(current_thd, log_info.log_file_name);
+        }
+
+        DBUG_PRINT("info",("purging %s",log_info.log_file_name));
+        if (!my_delete(log_info.log_file_name, MYF(0)))
+        {
+          if (decrease_log_space)
+            *decrease_log_space-= s.st_size;
         }
         else
         {
-          if (thd)
+          if (my_errno == ENOENT)
           {
-            push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
-                                ER_BINLOG_PURGE_FATAL_ERR,
-                                "a problem with deleting %s; "
-                                "consider examining correspondence "
-                                "of your binlog index file "
-                                "to the actual binlog files",
-                                log_info.log_file_name);
+            if (thd)
+            {
+              push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+                                  ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
+                                  log_info.log_file_name);
+            }
+            sql_print_information("Failed to delete file '%s'",
+                                  log_info.log_file_name);
+            my_errno= 0;
           }
           else
           {
-            sql_print_information("Failed to delete file '%s'; "
+            if (thd)
+            {
+              push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+                                  ER_BINLOG_PURGE_FATAL_ERR,
+                                  "a problem with deleting %s; "
                                   "consider examining correspondence "
                                   "of your binlog index file "
                                   "to the actual binlog files",
                                   log_info.log_file_name);
-          }
-          if (my_errno == EMFILE)
-          {
-            DBUG_PRINT("info",
-                       ("my_errno: %d, set ret = LOG_INFO_EMFILE", my_errno));
-            error= LOG_INFO_EMFILE;
+            }
+            else
+            {
+              sql_print_information("Failed to delete file '%s'; "
+                                    "consider examining correspondence "
+                                    "of your binlog index file "
+                                    "to the actual binlog files",
+                                    log_info.log_file_name);
+            }
+            if (my_errno == EMFILE)
+            {
+              DBUG_PRINT("info",
+                         ("my_errno: %d, set ret = LOG_INFO_EMFILE", my_errno));
+              error= LOG_INFO_EMFILE;
+              goto err;
+            }
+            error= LOG_INFO_FATAL;
             goto err;
           }
-          error= LOG_INFO_FATAL;
-          goto err;
         }
       }
     }
   }
 
 err:
-  close_cached_file(&purge_temp);
-  if (need_mutex)
-    pthread_mutex_unlock(&LOCK_index);
   DBUG_RETURN(error);
 }
 
+
 /**
   Remove all logs before the given file date from disk and from the
   index file.
@@ -5317,7 +5543,8 @@ int MYSQL_BIN_LOG::purge_logs_before_dat
     goto err;
 
   while (strcmp(log_file_name, log_info.log_file_name) &&
-	 !log_in_use(log_info.log_file_name))
+	 !is_active(log_info.log_file_name) &&
+         !log_in_use(log_info.log_file_name))
   {
     if (!my_stat(log_info.log_file_name, &stat_area, MYF(0)))
     {
@@ -5326,14 +5553,6 @@ int MYSQL_BIN_LOG::purge_logs_before_dat
         /*
           It's not fatal if we can't stat a log file that does not exist.
         */
-        if (thd)
-        {
-          push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
-                              ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
-                              log_info.log_file_name);
-        }
-        sql_print_information("Failed to execute my_stat on file '%s'",
-                              log_info.log_file_name);
         my_errno= 0;
       }
       else
@@ -5528,9 +5747,9 @@ void MYSQL_BIN_LOG::new_file_impl(bool n
   */
 
   /* reopen index binlog file, BUG#34582 */
-  if (!open_index_file(index_file_name, 0))
-    open(old_name, log_type, new_name_ptr, 
-         io_cache_type, no_auto_events, max_size, 1);
+  if (!open_index_file(index_file_name, 0, FALSE))
+    open(old_name, log_type, new_name_ptr,
+         io_cache_type, no_auto_events, max_size, 1, FALSE);
   my_free(old_name,MYF(0));
 
 end:
@@ -7493,7 +7712,7 @@ int TC_LOG_BINLOG::open(const char *opt_
   if (using_heuristic_recover())
   {
     /* generate a new binlog to mask a corrupted one */
-    open(opt_name, LOG_BIN, 0, WRITE_CACHE, 0, max_binlog_size, 0);
+    open(opt_name, LOG_BIN, 0, WRITE_CACHE, 0, max_binlog_size, 0, TRUE);
     cleanup();
     return 1;
   }

=== modified file 'sql/log.h'
--- a/sql/log.h	2009-10-26 14:02:26 +0000
+++ b/sql/log.h	2009-12-04 15:16:06 +0000
@@ -226,6 +226,10 @@ public:
             enum_log_type log_type,
             const char *new_name,
             enum cache_type io_cache_type_arg);
+  bool init_and_set_log_file_name(const char *log_name,
+                                  const char *new_name,
+                                  enum_log_type log_type_arg,
+                                  enum cache_type io_cache_type_arg);
   void init(enum_log_type log_type_arg,
             enum cache_type io_cache_type_arg);
   void close(uint exiting);
@@ -347,14 +351,15 @@ class MYSQL_BIN_LOG: public TC_LOG, priv
   pthread_cond_t update_cond;
   ulonglong bytes_written;
   IO_CACHE index_file;
+  char index_file_name[FN_REFLEN];
   /*
-    purge_temp is a temp file used in purge_logs so that the index file
+    purge_file is a temp file used in purge_logs so that the index file
     can be updated before deleting files from disk, yielding better crash
     recovery. It is created on demand the first time purge_logs is called
     and then reused for subsequent calls. It is cleaned up in cleanup().
   */
-  IO_CACHE purge_temp;
-  char index_file_name[FN_REFLEN];
+  IO_CACHE purge_index_file;
+  char purge_index_file_name[FN_REFLEN];
   /*
      The max size before rotation (usable only if log_type == LOG_BIN: binary
      logs and relay logs).
@@ -476,9 +481,10 @@ public:
             const char *new_name,
 	    enum cache_type io_cache_type_arg,
 	    bool no_auto_events_arg, ulong max_size,
-            bool null_created);
+            bool null_created,
+            bool need_mutex);
   bool open_index_file(const char *index_file_name_arg,
-                       const char *log_name);
+                       const char *log_name, bool need_mutex);
   /* Use this to start writing a new log file */
   void new_file();
 
@@ -525,6 +531,16 @@ public:
                  ulonglong *decrease_log_space);
   int purge_logs_before_date(time_t purge_time);
   int purge_first_log(Relay_log_info* rli, bool included);
+  int set_purge_index_file_name(const char *base_file_name);
+  int open_purge_index_file(bool destroy);
+  bool is_inited_purge_index_file();
+  int close_purge_index_file();
+  int clean_purge_index_file();
+  int sync_purge_index_file();
+  int register_purge_index_entry(const char* entry);
+  int register_create_index_entry(const char* entry);
+  int purge_index_entry(THD *thd, ulonglong *decrease_log_space,
+                        bool need_mutex);
   bool reset_logs(THD* thd);
   void close(uint exiting);
 

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	2009-11-27 14:56:49 +0000
+++ b/sql/mysqld.cc	2009-12-04 15:16:06 +0000
@@ -4244,7 +4244,7 @@ a file name for --log-bin-index option",
       my_free(opt_bin_logname, MYF(MY_ALLOW_ZERO_PTR));
       opt_bin_logname=my_strdup(buf, MYF(0));
     }
-    if (mysql_bin_log.open_index_file(opt_binlog_index_name, ln))
+    if (mysql_bin_log.open_index_file(opt_binlog_index_name, ln, TRUE))
     {
       unireg_abort(1);
     }
@@ -4451,7 +4451,7 @@ a file name for --log-bin-index option",
   }
 
   if (opt_bin_log && mysql_bin_log.open(opt_bin_logname, LOG_BIN, 0,
-                                        WRITE_CACHE, 0, max_binlog_size, 0))
+                                        WRITE_CACHE, 0, max_binlog_size, 0, TRUE))
     unireg_abort(1);
 
 #ifdef HAVE_REPLICATION

=== modified file 'sql/rpl_rli.cc'
--- a/sql/rpl_rli.cc	2009-11-28 05:23:28 +0000
+++ b/sql/rpl_rli.cc	2009-12-04 15:16:06 +0000
@@ -1066,10 +1066,10 @@ a file name for --relay-log-index option
       note, that if open() fails, we'll still have index file open
       but a destructor will take care of that
     */
-    if (relay_log.open_index_file(opt_relaylog_index_name, ln) ||
+    if (relay_log.open_index_file(opt_relaylog_index_name, ln, TRUE) ||
         relay_log.open(ln, LOG_BIN, 0, SEQ_READ_APPEND, 0,
                             (max_relay_log_size ? max_relay_log_size :
-                            max_binlog_size), 1))
+                            max_binlog_size), 1, TRUE))
     {
       sql_print_error("Failed in open_log() called from Relay_log_info::init_info()");
       DBUG_RETURN(1);


Attachment: [text/bzr-bundle]
Thread
bzr commit into mysql-pe branch (alfranio.correia:3742)Alfranio Correia4 Dec