diff -Naur mysql-5.0.37/sql/log.cc mysql-5.0.37.fixed/sql/log.cc --- mysql-5.0.37/sql/log.cc 2008-09-29 12:57:44.000000000 -0700 +++ mysql-5.0.37.fixed/sql/log.cc 2008-09-29 13:00:42.000000000 -0700 @@ -379,6 +379,7 @@ index_file_name[0] = 0; bzero((char*) &log_file,sizeof(log_file)); bzero((char*) &index_file, sizeof(index_file)); + bzero((char*) &purge_temp, sizeof(purge_temp)); } /* this is called only once */ @@ -395,6 +396,8 @@ (void) pthread_mutex_destroy(&LOCK_log); (void) pthread_mutex_destroy(&LOCK_index); (void) pthread_cond_destroy(&update_cond); + if (my_b_inited(&purge_temp)) + close_cached_file(&purge_temp); } DBUG_VOID_RETURN; } @@ -1158,8 +1161,38 @@ if (need_mutex) pthread_mutex_lock(&LOCK_index); + + /* Check that requested file exists in 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 " + "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=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; + } + } /* File name exists in index file; delete until we find this file @@ -1170,12 +1203,65 @@ while ((strcmp(to_log,log_info.log_file_name) || (exit_loop=included)) && !log_in_use(log_info.log_file_name)) { + if ((error=my_b_write(&purge_temp, (byte*)log_info.log_file_name, + strlen(log_info.log_file_name))) || + (error=my_b_write(&purge_temp, (byte*)"\n", 1))) + { + sql_print_error("MYSQL_LOG::purge_logs failed to copy %s to purge_temp", + log_info.log_file_name); + goto err; + } + + if (find_next_log(&log_info, 0) || exit_loop) + break; + } + + /* 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"); + goto err; + } + + DBUG_EXECUTE_IF("crash_after_update_index", abort();); + + /* Switch purge_temp for read. */ + if (error=reinit_io_cache(&purge_temp, READ_CACHE, 0, 0, 0)) + { + sql_print_error("MSYQL_LOG::purge_logs failed to reinit purge_temp " + "for read"); + goto err; + } + + /* Read each entry from purge_temp and delete the file. */ + for (;;) + { ulong file_size= 0; + uint length; + + if ((length=my_b_gets(&purge_temp, log_info.log_file_name, + FN_REFLEN)) <= 1) + { + if (purge_temp.error) + { + error= purge_temp.error; + sql_print_error("MSYQL_LOG::purge_logs error %d reading from " + "purge_temp", error); + goto err; + } + + /* Reached EOF */ + break; + } + + /* Get rid of the trailing '\n' */ + log_info.log_file_name[length-1]= 0; + if (decrease_log_space) //stat the file we want to delete { MY_STAT s; - /* + /* If we could not stat, we can't know the amount of space that deletion will free. In most cases, deletion won't work either, so it's not a problem. @@ -1191,18 +1277,20 @@ if we could delete it, take its size into account */ DBUG_PRINT("info",("purging %s",log_info.log_file_name)); - if (!my_delete(log_info.log_file_name, MYF(0)) && decrease_log_space) + if (my_delete(log_info.log_file_name, MYF(0))) + { + /* + The downside of failing to delete a file is the lost disk space. + It won't cause any functional problems because find_uniq_filename + ensures that a given log name isn't reused. + */ + sql_print_error("MYSQL_LOG::purge_logs failed to delete %s", + log_info.log_file_name); + } + else if (decrease_log_space) *decrease_log_space-= file_size; - if (find_next_log(&log_info, 0) || exit_loop) - break; } - /* - If we get killed -9 here, the sysadmin would have to edit - the log index file after restart - otherwise, this should be safe - */ - error= update_log_index(&log_info, need_update_threads); - err: if (need_mutex) pthread_mutex_unlock(&LOCK_index); diff -Naur mysql-5.0.37/sql/sql_class.h mysql-5.0.37.fixed/sql/sql_class.h --- mysql-5.0.37/sql/sql_class.h 2008-09-29 12:57:44.000000000 -0700 +++ mysql-5.0.37.fixed/sql/sql_class.h 2008-09-29 13:00:42.000000000 -0700 @@ -198,6 +198,13 @@ time_t last_time,query_start; IO_CACHE log_file; IO_CACHE index_file; + /* + purge_temp 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 *name; char time_buff[20],db[NAME_LEN+1]; char log_file_name[FN_REFLEN],index_file_name[FN_REFLEN];