diff options
Diffstat (limited to 'sql/log.cc')
-rw-r--r-- | sql/log.cc | 1443 |
1 files changed, 985 insertions, 458 deletions
diff --git a/sql/log.cc b/sql/log.cc index 5e5d5b9368e..073b7f691e8 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -29,10 +29,10 @@ #include <my_dir.h> #include <stdarg.h> #include <m_ctype.h> // For test_if_number +#include <assert.h> MYSQL_LOG mysql_log,mysql_update_log,mysql_slow_log,mysql_bin_log; extern I_List<i_string> binlog_do_db, binlog_ignore_db; -extern ulong max_binlog_size; static bool test_if_number(const char *str, long *res, bool allow_wildcards); @@ -79,9 +79,11 @@ static int find_uniq_filename(char *name) DBUG_RETURN(0); } -MYSQL_LOG::MYSQL_LOG(): last_time(0), query_start(0),index_file(-1), - name(0), log_type(LOG_CLOSED),write_error(0), - inited(0), no_rotate(0) + +MYSQL_LOG::MYSQL_LOG() + :bytes_written(0), last_time(0), query_start(0), name(0), + file_id(1), open_count(1), log_type(LOG_CLOSED), write_error(0), inited(0), + no_rotate(0), need_start_event(1) { /* We don't want to intialize LOCK_Log here as the thread system may @@ -89,34 +91,33 @@ MYSQL_LOG::MYSQL_LOG(): last_time(0), query_start(0),index_file(-1), */ index_file_name[0] = 0; bzero((char*) &log_file,sizeof(log_file)); + bzero((char*) &index_file, sizeof(index_file)); } + MYSQL_LOG::~MYSQL_LOG() { + cleanup(); +} + +void MYSQL_LOG::cleanup() +{ if (inited) { + close(1); + inited= 0; (void) pthread_mutex_destroy(&LOCK_log); (void) pthread_mutex_destroy(&LOCK_index); + (void) pthread_cond_destroy(&update_cond); } } -void MYSQL_LOG::set_index_file_name(const char* index_file_name) -{ - if (index_file_name) - fn_format(this->index_file_name,index_file_name,mysql_data_home,".index", - 4); - else - this->index_file_name[0] = 0; -} - int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name) { - if (log_type == LOG_NORMAL) - fn_format(new_name,log_name,mysql_data_home,"",4); - else + fn_format(new_name,log_name,mysql_data_home,"",4); + if (log_type != LOG_NORMAL) { - fn_format(new_name,log_name,mysql_data_home,"",4); if (!fn_ext(log_name)[0]) { if (find_uniq_filename(new_name)) @@ -129,44 +130,55 @@ int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name) return 0; } -bool MYSQL_LOG::open_index( int options) -{ - return (index_file < 0 && - (index_file = my_open(index_file_name, options | O_BINARY , - MYF(MY_WME))) < 0); -} -void MYSQL_LOG::init(enum_log_type log_type_arg) +void MYSQL_LOG::init(enum_log_type log_type_arg, + enum cache_type io_cache_type_arg, + bool no_auto_events_arg) { log_type = log_type_arg; + io_cache_type = io_cache_type_arg; + no_auto_events = no_auto_events_arg; if (!inited) { - inited=1; + inited= 1; (void) pthread_mutex_init(&LOCK_log,MY_MUTEX_INIT_SLOW); (void) pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW); + (void) pthread_cond_init(&update_cond, 0); } } -void MYSQL_LOG::close_index() -{ - if (index_file >= 0) - { - my_close(index_file, MYF(0)); - index_file = -1; - } -} -void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, - const char *new_name) +/* + Open a (new) log file. + + DESCRIPTION + - If binary logs, also open the index file and register the new + file name in it + - When calling this when the file is in use, you must have a locks + on LOCK_log and LOCK_index. + + RETURN VALUES + 0 ok + 1 error +*/ + +bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, + const char *new_name, const char *index_file_name_arg, + enum cache_type io_cache_type_arg, + bool no_auto_events_arg) { - MY_STAT tmp_stat; char buff[512]; - File file= -1; - bool do_magic; - + File file= -1, index_file_nr= -1; + int open_flags = O_CREAT | O_APPEND | O_BINARY; + DBUG_ENTER("MYSQL_LOG::open"); + DBUG_PRINT("enter",("log_type: %d",(int) log_type)); + + last_time=query_start=0; + write_error=0; + if (!inited && log_type_arg == LOG_BIN && *fn_ext(log_name)) - no_rotate = 1; - init(log_type_arg); + no_rotate = 1; + init(log_type_arg,io_cache_type_arg,no_auto_events_arg); if (!(name=my_strdup(log_name,MYF(MY_WME)))) goto err; @@ -174,21 +186,22 @@ void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, strmov(log_file_name,new_name); else if (generate_new_name(log_file_name, name)) goto err; - - if (log_type == LOG_BIN && !index_file_name[0]) - fn_format(index_file_name, name, mysql_data_home, ".index", 6); - db[0]=0; - do_magic = ((log_type == LOG_BIN) && !my_stat(log_file_name, - &tmp_stat, MYF(0))); + if (io_cache_type == SEQ_READ_APPEND) + open_flags |= O_RDWR; + else + open_flags |= O_WRONLY; - if ((file=my_open(log_file_name,O_CREAT | O_APPEND | O_WRONLY | O_BINARY, + db[0]=0; + open_count++; + if ((file=my_open(log_file_name,open_flags, MYF(MY_WME | ME_WAITTANG))) < 0 || - init_io_cache(&log_file, file, IO_SIZE, WRITE_CACHE, + init_io_cache(&log_file, file, IO_SIZE, io_cache_type, my_tell(file,MYF(MY_WME)), 0, MYF(MY_WME | MY_NABP))) goto err; - if (log_type == LOG_NORMAL) + switch (log_type) { + case LOG_NORMAL: { char *end; #ifdef __NT__ @@ -200,8 +213,9 @@ void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, if (my_b_write(&log_file, (byte*) buff,(uint) (end-buff)) || flush_io_cache(&log_file)) goto err; + break; } - else if (log_type == LOG_NEW) + case LOG_NEW: { time_t skr=time(NULL); struct tm tm_tmp; @@ -217,48 +231,98 @@ void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, if (my_b_write(&log_file, (byte*) buff,(uint) strlen(buff)) || flush_io_cache(&log_file)) goto err; + break; } - else if (log_type == LOG_BIN) + case LOG_BIN: { - /* - Explanation of the boolean black magic: - if we are supposed to write magic number try write - clean up if failed - then if index_file has not been previously opened, try to open it - clean up if failed - */ - if ((do_magic && my_b_write(&log_file, (byte*) BINLOG_MAGIC, 4)) || - open_index(O_APPEND | O_RDWR | O_CREAT)) - goto err; - Start_log_event s; - bool error; - s.write(&log_file); - flush_io_cache(&log_file); - pthread_mutex_lock(&LOCK_index); - error=(my_write(index_file, (byte*) log_file_name, strlen(log_file_name), - MYF(MY_NABP | MY_WME)) || - my_write(index_file, (byte*) "\n", 1, MYF(MY_NABP | MY_WME))); - pthread_mutex_unlock(&LOCK_index); - if (error) + bool write_file_name_to_index_file=0; + + myf opt= MY_UNPACK_FILENAME; + if (!index_file_name_arg) { - close_index(); + index_file_name_arg= name; // Use same basename for index file + opt= MY_UNPACK_FILENAME | MY_REPLACE_EXT; + } + + if (!my_b_filelength(&log_file)) + { + /* + The binary log file was empty (probably newly created) + This is the normal case and happens when the user doesn't specify + an extension for the binary log files. + In this case we write a standard header to it. + */ + if (my_b_write(&log_file, (byte*) BINLOG_MAGIC, BIN_LOG_HEADER_SIZE)) + goto err; + bytes_written += BIN_LOG_HEADER_SIZE; + write_file_name_to_index_file=1; + } + + if (!my_b_inited(&index_file)) + { + /* + First open of this class instance + Create an index file that will hold all file names uses for logging. + Add new entries to the end of it. + */ + fn_format(index_file_name, index_file_name_arg, mysql_data_home, + ".index", opt); + if ((index_file_nr= my_open(index_file_name, + O_RDWR | O_CREAT | O_BINARY , + MYF(MY_WME))) < 0 || + 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))) + goto err; + } + else + { + safe_mutex_assert_owner(&LOCK_index); + reinit_io_cache(&index_file, WRITE_CACHE, my_b_filelength(&index_file), + 0, 0); + } + if (need_start_event && !no_auto_events) + { + need_start_event=0; + Start_log_event s; + s.set_log_pos(this); + s.write(&log_file); + } + if (flush_io_cache(&log_file)) goto err; + + if (write_file_name_to_index_file) + { + /* As this is a new log file, we write the file name to the index file */ + if (my_b_write(&index_file, (byte*) log_file_name, + strlen(log_file_name)) || + my_b_write(&index_file, (byte*) "\n", 1) || + flush_io_cache(&index_file)) + goto err; } + break; } - return; + case LOG_CLOSED: // Impossible + DBUG_ASSERT(1); + break; + } + DBUG_RETURN(0); err: - sql_print_error("Could not use %s for logging (error %d)", log_name,errno); + sql_print_error("Could not use %s for logging (error %d)", log_name, errno); if (file >= 0) my_close(file,MYF(0)); + if (index_file_nr >= 0) + my_close(index_file_nr,MYF(0)); end_io_cache(&log_file); - x_free(name); name=0; + end_io_cache(&index_file); + safeFree(name); log_type=LOG_CLOSED; - - return; - + DBUG_RETURN(1); } + int MYSQL_LOG::get_current_log(LOG_INFO* linfo) { pthread_mutex_lock(&LOCK_log); @@ -268,296 +332,616 @@ int MYSQL_LOG::get_current_log(LOG_INFO* linfo) return 0; } -// if log_name is "" we stop at the first entry -int MYSQL_LOG::find_first_log(LOG_INFO* linfo, const char* log_name) + +/* + Move all data up in a file in an filename index file + + SYNOPSIS + copy_up_file_and_fill() + index_file File to move + offset Move everything from here to beginning + + NOTE + File will be truncated to be 'offset' shorter or filled up with + newlines + + IMPLEMENTATION + We do the copy outside of the IO_CACHE as the cache buffers would just + make things slower and more complicated. + In most cases the copy loop should only do one read. + + RETURN VALUES + 0 ok +*/ + +static bool copy_up_file_and_fill(IO_CACHE *index_file, my_off_t offset) { - if (index_file < 0) - return LOG_INFO_INVALID; - int error = 0; - char* fname = linfo->log_file_name; - uint log_name_len = (uint) strlen(log_name); - IO_CACHE io_cache; - - // mutex needed because we need to make sure the file pointer does not move - // from under our feet - pthread_mutex_lock(&LOCK_index); - if (init_io_cache(&io_cache, index_file, IO_SIZE, READ_CACHE, (my_off_t) 0, - 0, MYF(MY_WME))) + int bytes_read; + my_off_t init_offset= offset; + File file= index_file->file; + byte io_buf[IO_SIZE*2]; + DBUG_ENTER("copy_up_file_and_fill"); + + for (;; offset+= bytes_read) { - error = LOG_INFO_SEEK; - goto err; + (void) my_seek(file, offset, MY_SEEK_SET, MYF(0)); + if ((bytes_read= (int) my_read(file, io_buf, sizeof(io_buf), MYF(MY_WME))) + < 0) + goto err; + if (!bytes_read) + break; // end of file + (void) my_seek(file, offset-init_offset, MY_SEEK_SET, MYF(0)); + if (my_write(file, (byte*) io_buf, bytes_read, MYF(MY_WME | MY_NABP))) + goto err; } - for(;;) + /* The following will either truncate the file or fill the end with \n' */ + if (my_chsize(file, offset - init_offset, '\n', MYF(MY_WME))) + goto err; + + /* Reset data in old index cache */ + reinit_io_cache(index_file, READ_CACHE, (my_off_t) 0, 0, 1); + DBUG_RETURN(0); + +err: + DBUG_RETURN(1); +} + + +/* + Find the position in the log-index-file for the given log name + + SYNOPSIS + find_log_pos() + linfo Store here the found log file name and position to + the NEXT log file name in the index file. + log_name Filename to find in the index file. + Is a null pointer if we want to read the first entry + need_lock Set this to 1 if the parent doesn't already have a + lock on LOCK_index + + NOTE + On systems without the truncate function the file will end with one or + more empty lines. These will be ignored when reading the file. + + RETURN VALUES + 0 ok + LOG_INFO_EOF End of log-index-file found + LOG_INFO_IO Got IO error while reading file +*/ + +int MYSQL_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name, + bool need_lock) +{ + int error= 0; + char *fname= linfo->log_file_name; + uint log_name_len= log_name ? (uint) strlen(log_name) : 0; + DBUG_ENTER("find_log_pos"); + DBUG_PRINT("enter",("log_name: %s", log_name ? log_name : "NULL")); + + /* + Mutex needed because we need to make sure the file pointer does not move + from under our feet + */ + if (need_lock) + pthread_mutex_lock(&LOCK_index); + safe_mutex_assert_owner(&LOCK_index); + + /* As the file is flushed, we can't get an error here */ + (void) reinit_io_cache(&index_file, READ_CACHE, (my_off_t) 0, 0, 0); + + for (;;) { uint length; - if (!(length=my_b_gets(&io_cache, fname, FN_REFLEN-1))) + my_off_t offset= my_b_tell(&index_file); + /* If we get 0 or 1 characters, this is the end of the file */ + + if ((length= my_b_gets(&index_file, fname, FN_REFLEN)) <= 1) { - error = !io_cache.error ? LOG_INFO_EOF : LOG_INFO_IO; - goto err; + /* Did not find the given entry; Return not found or error */ + error= !index_file.error ? LOG_INFO_EOF : LOG_INFO_IO; + break; } - // if the log entry matches, empty string matching anything - if (!log_name_len || + // if the log entry matches, null string matching anything + if (!log_name || (log_name_len == length-1 && fname[log_name_len] == '\n' && !memcmp(fname, log_name, log_name_len))) { + DBUG_PRINT("info",("Found log file entry")); fname[length-1]=0; // remove last \n - linfo->index_file_offset = my_b_tell(&io_cache); + linfo->index_file_start_offset= offset; + linfo->index_file_offset = my_b_tell(&index_file); break; } } - error = 0; -err: - pthread_mutex_unlock(&LOCK_index); - end_io_cache(&io_cache); - return error; - + if (need_lock) + pthread_mutex_unlock(&LOCK_index); + DBUG_RETURN(error); } -int MYSQL_LOG::find_next_log(LOG_INFO* linfo) +/* + Find the position in the log-index-file for the given log name + + SYNOPSIS + find_next_log() + linfo Store here the next log file name and position to + the file name after that. + need_lock Set this to 1 if the parent doesn't already have a + lock on LOCK_index + + NOTE + - Before calling this function, one has to call find_log_pos() + to set up 'linfo' + - Mutex needed because we need to make sure the file pointer does not move + from under our feet + + RETURN VALUES + 0 ok + LOG_INFO_EOF End of log-index-file found + LOG_INFO_SEEK Could not allocate IO cache + LOG_INFO_IO Got IO error while reading file +*/ + +int MYSQL_LOG::find_next_log(LOG_INFO* linfo, bool need_lock) { - // mutex needed because we need to make sure the file pointer does not move - // from under our feet - if (index_file < 0) return LOG_INFO_INVALID; - int error = 0; - char* fname = linfo->log_file_name; - IO_CACHE io_cache; + int error= 0; uint length; + char *fname= linfo->log_file_name; - pthread_mutex_lock(&LOCK_index); - if (init_io_cache(&io_cache, index_file, IO_SIZE, - READ_CACHE, (my_off_t) linfo->index_file_offset, 0, - MYF(MY_WME))) - { - error = LOG_INFO_SEEK; - goto err; - } - if (!(length=my_b_gets(&io_cache, fname, FN_REFLEN))) + if (need_lock) + pthread_mutex_lock(&LOCK_index); + safe_mutex_assert_owner(&LOCK_index); + + /* As the file is flushed, we can't get an error here */ + (void) reinit_io_cache(&index_file, READ_CACHE, linfo->index_file_offset, 0, + 0); + + linfo->index_file_start_offset= linfo->index_file_offset; + if ((length=my_b_gets(&index_file, fname, FN_REFLEN)) <= 1) { - error = !io_cache.error ? LOG_INFO_EOF : LOG_INFO_IO; + error = !index_file.error ? LOG_INFO_EOF : LOG_INFO_IO; goto err; } fname[length-1]=0; // kill /n - linfo->index_file_offset = my_b_tell(&io_cache); - error = 0; + linfo->index_file_offset = my_b_tell(&index_file); err: - pthread_mutex_unlock(&LOCK_index); - end_io_cache(&io_cache); + if (need_lock) + pthread_mutex_unlock(&LOCK_index); return error; } - -int MYSQL_LOG::purge_logs(THD* thd, const char* to_log) + +/* + Delete all logs refered to in the index file + Start writing to a new log file. The new index file will only contain + this file. + + SYNOPSIS + reset_logs() + thd Thread + + NOTE + If not called from slave thread, write start event to new log + + + RETURN VALUES + 0 ok + 1 error +*/ + +bool MYSQL_LOG::reset_logs(THD* thd) { - if (index_file < 0) return LOG_INFO_INVALID; - if (no_rotate) return LOG_INFO_PURGE_NO_ROTATE; - int error; - char fname[FN_REFLEN]; - char *p; - uint fname_len, i; - bool logs_to_purge_inited = 0, logs_to_keep_inited = 0, found_log = 0; - DYNAMIC_ARRAY logs_to_purge, logs_to_keep; - my_off_t purge_offset ; - LINT_INIT(purge_offset); - IO_CACHE io_cache; - + LOG_INFO linfo; + bool error=0; + const char* save_name; + enum_log_type save_log_type; + DBUG_ENTER("reset_logs"); + + /* + We need to get both locks to be sure that no one is trying to + write to the index log file. + */ + pthread_mutex_lock(&LOCK_log); pthread_mutex_lock(&LOCK_index); - - if (init_io_cache(&io_cache,index_file, IO_SIZE*2, READ_CACHE, (my_off_t) 0, - 0, MYF(MY_WME))) - { - error = LOG_INFO_MEM; - goto err; - } - if (my_init_dynamic_array(&logs_to_purge, sizeof(char*), 1024, 1024)) + + /* Save variables so that we can reopen the log */ + save_name=name; + name=0; // Protect against free + save_log_type=log_type; + close(0); // Don't close the index file + + /* First delete all old log files */ + + if (find_log_pos(&linfo, NullS, 0)) { - error = LOG_INFO_MEM; + error=1; goto err; } - logs_to_purge_inited = 1; - if (my_init_dynamic_array(&logs_to_keep, sizeof(char*), 1024, 1024)) + for (;;) { - error = LOG_INFO_MEM; - goto err; + my_delete(linfo.log_file_name, MYF(MY_WME)); + if (find_next_log(&linfo, 0)) + break; } - logs_to_keep_inited = 1; - - for(;;) - { - my_off_t init_purge_offset= my_b_tell(&io_cache); - if (!(fname_len=my_b_gets(&io_cache, fname, FN_REFLEN))) - { - if(!io_cache.error) - break; - error = LOG_INFO_IO; - goto err; - } + /* Start logging with a new file */ + close(1); // Close index file + my_delete(index_file_name, MYF(MY_WME)); // Reset (open will update) + if (!thd->slave_thread) + need_start_event=1; + open(save_name, save_log_type, 0, index_file_name, + io_cache_type, no_auto_events); + my_free((gptr) save_name, MYF(0)); - fname[--fname_len]=0; // kill \n - if(!memcmp(fname, to_log, fname_len + 1 )) - { - found_log = 1; - purge_offset = init_purge_offset; - } - - // if one of the logs before the target is in use - if(!found_log && log_in_use(fname)) - { - error = LOG_INFO_IN_USE; - goto err; - } - - if (!(p = sql_memdup(fname, fname_len+1)) || - insert_dynamic(found_log ? &logs_to_keep : &logs_to_purge, - (gptr) &p)) - { - error = LOG_INFO_MEM; - goto err; - } - } - - end_io_cache(&io_cache); - if(!found_log) +err: + pthread_mutex_unlock(&LOCK_index); + pthread_mutex_unlock(&LOCK_log); + DBUG_RETURN(error); +} + + +/* + Delete the current log file, remove it from index file and start on next + + SYNOPSIS + purge_first_log() + rli Relay log information + + NOTE + - This is only called from the slave-execute thread when it has read + all commands from a log and want to switch to a new log. + - When this happens, we should never be in an active transaction as + a transaction is always written as a single block to the binary log. + + IMPLEMENTATION + - Protects index file with LOCK_index + - Delete first log file, + - Copy all file names after this one to the front of the index file + - If the OS has truncate, truncate the file, else fill it with \n' + - Read the first file name from the index file and store in rli->linfo + + RETURN VALUES + 0 ok + LOG_INFO_EOF End of log-index-file found + LOG_INFO_SEEK Could not allocate IO cache + LOG_INFO_IO Got IO error while reading file +*/ + +int MYSQL_LOG::purge_first_log(struct st_relay_log_info* rli) +{ + int error; + DBUG_ENTER("purge_first_log"); + + /* + Test pre-conditions. + + Assume that we have previously read the first log and + stored it in rli->relay_log_name + */ + DBUG_ASSERT(is_open()); + DBUG_ASSERT(rli->slave_running == 1); + DBUG_ASSERT(!strcmp(rli->linfo.log_file_name,rli->relay_log_name)); + DBUG_ASSERT(rli->linfo.index_file_offset == + strlen(rli->relay_log_name) + 1); + + /* We have already processed the relay log, so it's safe to delete it */ + my_delete(rli->relay_log_name, MYF(0)); + pthread_mutex_lock(&LOCK_index); + if (copy_up_file_and_fill(&index_file, rli->linfo.index_file_offset)) { - error = LOG_INFO_EOF; + error= LOG_INFO_IO; goto err; } + + /* + Update the space counter used by all relay logs + Ok to broadcast after the critical region as there is no risk of + the mutex being destroyed by this thread later - this helps save + context switches + */ + pthread_mutex_lock(&rli->log_space_lock); + rli->log_space_total -= rli->relay_log_pos; + pthread_mutex_unlock(&rli->log_space_lock); + pthread_cond_broadcast(&rli->log_space_cond); - for(i = 0; i < logs_to_purge.elements; i++) - { - char* l; - get_dynamic(&logs_to_purge, (gptr)&l, i); - if (my_delete(l, MYF(MY_WME))) - sql_print_error("Error deleting %s during purge", l); - } - - // if we get killed -9 here, the sysadmin would have to do a small - // vi job on the log index file after restart - otherwise, this should - // be safe -#ifdef HAVE_FTRUNCATE - if (ftruncate(index_file,0)) + /* + Read the next log file name from the index file and pass it back to + the caller + */ + if ((error=find_log_pos(&rli->linfo, NullS, 0 /*no mutex*/))) { - sql_print_error( -"Could not truncate the binlog index file during log purge for write"); - error = LOG_INFO_FATAL; + char buff[22]; + sql_print_error("next log error: %d offset: %s log: %s", + error, + llstr(rli->linfo.index_file_offset,buff), + rli->linfo.log_file_name); goto err; } - my_seek(index_file, 0, MY_SEEK_CUR,MYF(MY_WME)); -#else - my_close(index_file, MYF(MY_WME)); - my_delete(index_file_name, MYF(MY_WME)); - if(!(index_file = my_open(index_file_name, - O_CREAT | O_BINARY | O_RDWR | O_APPEND, - MYF(MY_WME)))) - { - sql_print_error( -"Could not re-open the binlog index file during log purge for write"); - error = LOG_INFO_FATAL; + /* + Reset position to current log. This involves setting both of the + position variables: + */ + rli->relay_log_pos = BIN_LOG_HEADER_SIZE; + rli->pending = 0; + strmake(rli->relay_log_name,rli->linfo.log_file_name, + sizeof(rli->relay_log_name)-1); + + /* Store where we are in the new file for the execution thread */ + flush_relay_log_info(rli); + +err: + pthread_mutex_unlock(&LOCK_index); + DBUG_RETURN(error); +} + + +/* + Remove all logs before the given log from disk and from the index file. + + SYNOPSIS + purge_logs() + thd Thread pointer + to_log Delete all log file name before this file. This file is not + deleted + + NOTES + If any of the logs before the deleted one is in use, + only purge logs up to this one. + + RETURN VALUES + 0 ok + LOG_INFO_PURGE_NO_ROTATE Binary file that can't be rotated + LOG_INFO_EOF to_log not found +*/ + +int MYSQL_LOG::purge_logs(THD* thd, const char* to_log) +{ + int error; + LOG_INFO log_info; + DBUG_ENTER("purge_logs"); + + if (no_rotate) + DBUG_RETURN(LOG_INFO_PURGE_NO_ROTATE); + + pthread_mutex_lock(&LOCK_index); + if ((error=find_log_pos(&log_info, to_log, 0 /*no mutex*/))) goto err; + + /* + File name exists in index file; Delete until we find this file + or a file that is used. + */ + if ((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/))) + goto err; + while (strcmp(to_log,log_info.log_file_name) && + !log_in_use(log_info.log_file_name)) + { + /* It's not fatal even if we can't delete a log file */ + my_delete(log_info.log_file_name, MYF(0)); + if (find_next_log(&log_info, 0)) + break; } -#endif - - for(i = 0; i < logs_to_keep.elements; i++) + + /* + If we get killed -9 here, the sysadmin would have to edit + the log index file after restart - otherwise, this should be safe + */ + + if (copy_up_file_and_fill(&index_file, log_info.index_file_start_offset)) { - char* l; - get_dynamic(&logs_to_keep, (gptr)&l, i); - if (my_write(index_file, (byte*) l, strlen(l), MYF(MY_WME|MY_NABP)) || - my_write(index_file, (byte*) "\n", 1, MYF(MY_WME|MY_NABP))) - { - error = LOG_INFO_FATAL; - goto err; - } + error= LOG_INFO_IO; + goto err; } - // now update offsets - adjust_linfo_offsets(purge_offset); - error = 0; + // now update offsets in index file for running threads + adjust_linfo_offsets(log_info.index_file_start_offset); err: pthread_mutex_unlock(&LOCK_index); - if(logs_to_purge_inited) - delete_dynamic(&logs_to_purge); - if(logs_to_keep_inited) - delete_dynamic(&logs_to_keep); - end_io_cache(&io_cache); - return error; + DBUG_RETURN(error); } -// we assume that buf has at least FN_REFLEN bytes alloced +/* + Create a new log file name + + SYNOPSIS + make_log_name() + buf buf of at least FN_REFLEN where new name is stored + + NOTE + If file name will be longer then FN_REFLEN it will be truncated +*/ + void MYSQL_LOG::make_log_name(char* buf, const char* log_ident) { - buf[0] = 0; // In case of error - if (inited) + if (inited) // QQ When is this not true ? { - int dir_len = dirname_length(log_file_name); - int ident_len = (uint) strlen(log_ident); - if (dir_len + ident_len + 1 > FN_REFLEN) - return; // protection agains malicious buffer overflow - - memcpy(buf, log_file_name, dir_len); - // copy filename + end null - memcpy(buf + dir_len, log_ident, ident_len + 1); + uint dir_len = dirname_length(log_file_name); + if (dir_len > FN_REFLEN) + dir_len=FN_REFLEN-1; + strnmov(buf, log_file_name, dir_len); + strmake(buf+dir_len, log_ident, FN_REFLEN - dir_len); } } -bool MYSQL_LOG::is_active(const char* log_file_name) + +/* + Check if we are writing/reading to the given log file +*/ + +bool MYSQL_LOG::is_active(const char *log_file_name_arg) { - return inited && !strcmp(log_file_name, this->log_file_name); + return inited && !strcmp(log_file_name, log_file_name_arg); } -void MYSQL_LOG::new_file(bool inside_mutex) + +/* + Start writing to a new log file or reopen the old file + + SYNOPSIS + new_file() + need_lock Set to 1 (default) if caller has not locked + LOCK_log and LOCK_index + + NOTE + The new file name is stored last in the index file +*/ + +void MYSQL_LOG::new_file(bool need_lock) { + char new_name[FN_REFLEN], *new_name_ptr, *old_name; + enum_log_type save_log_type; + if (!is_open()) - return; + return; // Should never happen - if (!inside_mutex) - VOID(pthread_mutex_lock(&LOCK_log)); + if (need_lock) + { + pthread_mutex_lock(&LOCK_log); + pthread_mutex_lock(&LOCK_index); + } + safe_mutex_assert_owner(&LOCK_log); + safe_mutex_assert_owner(&LOCK_index); - char new_name[FN_REFLEN], *old_name = name; - + // Reuse old name if not binlog and not update log + new_name_ptr= name; + + /* + Only rotate open logs that are marked non-rotatable + (binlog with constant name are non-rotatable) + */ if (!no_rotate) { /* - only rotate open logs that are marked non-rotatable - (binlog with constant name are non-rotatable) + If user hasn't specified an extension, generate a new log name + We have to do this here and not in open as we want to store the + new file name in the current binary log file. */ if (generate_new_name(new_name, name)) - { - if (!inside_mutex) - VOID(pthread_mutex_unlock(&LOCK_log)); - return; // Something went wrong - } + goto end; + new_name_ptr=new_name; + if (log_type == LOG_BIN) { + if (!no_auto_events) + { + /* + We log the whole file name for log file as the user may decide + to change base names at some point. + */ + THD* thd = current_thd; + Rotate_log_event r(thd,new_name+dirname_length(new_name)); + r.set_log_pos(this); + + /* + Because this log rotation could have been initiated by a master of + the slave running with log-bin, we set the flag on rotate + event to prevent infinite log rotation loop + */ + if (thd->slave_thread) + r.flags|= LOG_EVENT_FORCED_ROTATE_F; + r.write(&log_file); + bytes_written += r.get_event_len(); + } /* - We log the whole file name for log file as the user may decide - to change base names at some point. + Update needs to be signalled even if there is no rotate event + log rotation should give the waiting thread a signal to + discover EOF and move on to the next log. */ - Rotate_log_event r(new_name+dirname_length(new_name)); - r.write(&log_file); - VOID(pthread_cond_broadcast(&COND_binlog_update)); + signal_update(); } } - else - strmov(new_name, old_name); // Reopen old file name - name=0; + old_name=name; + save_log_type=log_type; + name=0; // Don't free name close(); - open(old_name, log_type, new_name); + open(old_name, save_log_type, new_name_ptr, index_file_name, io_cache_type, + no_auto_events); my_free(old_name,MYF(0)); - last_time=query_start=0; - write_error=0; - if (!inside_mutex) - VOID(pthread_mutex_unlock(&LOCK_log)); +end: + if (need_lock) + { + pthread_mutex_unlock(&LOCK_index); + pthread_mutex_unlock(&LOCK_log); + } } +bool MYSQL_LOG::append(Log_event* ev) +{ + bool error = 0; + pthread_mutex_lock(&LOCK_log); + + DBUG_ASSERT(log_file.type == SEQ_READ_APPEND); + /* + Log_event::write() is smart enough to use my_b_write() or + my_b_append() depending on the kind of cache we have. + */ + if (ev->write(&log_file)) + { + error=1; + goto err; + } + bytes_written += ev->get_event_len(); + if ((uint) my_b_append_tell(&log_file) > max_binlog_size) + { + pthread_mutex_lock(&LOCK_index); + new_file(0); + pthread_mutex_unlock(&LOCK_index); + } + +err: + pthread_mutex_unlock(&LOCK_log); + signal_update(); // Safe as we don't call close + return error; +} + + +bool MYSQL_LOG::appendv(const char* buf, uint len,...) +{ + bool error= 0; + va_list(args); + va_start(args,len); + + DBUG_ASSERT(log_file.type == SEQ_READ_APPEND); + + pthread_mutex_lock(&LOCK_log); + do + { + if (my_b_append(&log_file,(byte*) buf,len)) + { + error= 1; + goto err; + } + bytes_written += len; + } while ((buf=va_arg(args,const char*)) && (len=va_arg(args,uint))); + + if ((uint) my_b_append_tell(&log_file) > max_binlog_size) + { + pthread_mutex_lock(&LOCK_index); + new_file(0); + pthread_mutex_unlock(&LOCK_index); + } + +err: + pthread_mutex_unlock(&LOCK_log); + if (!error) + signal_update(); + return error; +} + + +/* + Write to normal (not rotable) log + This is the format for the 'normal', 'slow' and 'update' logs. +*/ + bool MYSQL_LOG::write(THD *thd,enum enum_server_command command, const char *format,...) { @@ -578,7 +962,7 @@ bool MYSQL_LOG::write(THD *thd,enum enum_server_command command, if (thd) { // Normal thread if ((thd->options & OPTION_LOG_OFF) && - (thd->master_access & PROCESS_ACL)) + (thd->master_access & SUPER_ACL)) { VOID(pthread_mutex_unlock(&LOCK_log)); return 0; // No logging @@ -637,110 +1021,122 @@ bool MYSQL_LOG::write(THD *thd,enum enum_server_command command, return 0; } -/* Write to binary log in a format to be used for replication */ -bool MYSQL_LOG::write(Query_log_event* event_info) +/* + Write an event to the binary log +*/ + +bool MYSQL_LOG::write(Log_event* event_info) { - /* In most cases this is only called if 'is_open()' is true */ bool error=0; - bool should_rotate = 0; + DBUG_ENTER("MYSQL_LOG::write(event)"); if (!inited) // Can't use mutex if not init - return 0; - VOID(pthread_mutex_lock(&LOCK_log)); + { + DBUG_PRINT("error",("not initied")); + DBUG_RETURN(0); + } + pthread_mutex_lock(&LOCK_log); + + /* In most cases this is only called if 'is_open()' is true */ if (is_open()) { + bool should_rotate = 0; THD *thd=event_info->thd; - IO_CACHE *file = (event_info->cache_stmt ? &thd->transaction.trans_log : + const char *local_db = event_info->get_db(); +#ifdef USING_TRANSACTIONS + IO_CACHE *file = ((event_info->get_cache_stmt()) ? + &thd->transaction.trans_log : &log_file); - if ((!(thd->options & OPTION_BIN_LOG) && - (thd->master_access & PROCESS_ACL)) || - !db_ok(event_info->db, binlog_do_db, binlog_ignore_db)) +#else + IO_CACHE *file = &log_file; +#endif + if ((thd && !(thd->options & OPTION_BIN_LOG) && + (thd->master_access & SUPER_ACL)) || + (local_db && !db_ok(local_db, binlog_do_db, binlog_ignore_db))) { VOID(pthread_mutex_unlock(&LOCK_log)); - return 0; + DBUG_PRINT("error",("!db_ok")); + DBUG_RETURN(0); } error=1; - - if (file == &thd->transaction.trans_log - && !my_b_tell(&thd->transaction.trans_log)) { - - /* Add the "BEGIN" and "COMMIT" in the binlog around transactions - which may contain more than 1 SQL statement. If we run with - AUTOCOMMIT=1, then MySQL immediately writes each SQL statement to - the binlog when the statement has been completed. No need to add - "BEGIN" ... "COMMIT" around such statements. Otherwise, MySQL uses - thd->transaction.trans_log to cache the SQL statements until the - explicit commit, and at the commit writes the contents in .trans_log - to the binlog. - - We write the "BEGIN" mark first in the buffer (.trans_log) where we - store the SQL statements for a transaction. At the transaction commit - we will add the "COMMIT mark and write the buffer to the binlog. - The function my_b_tell above returns != 0 if there already is data - in the buffer. */ - - int save_query_length = thd->query_length; - - thd->query_length = 5; /* length of string BEGIN */ - - Query_log_event qinfo(thd, "BEGIN", TRUE); - - error = ((&qinfo)->write(file)); - - thd->query_length = save_query_length; - - if (error) - goto err; - } - - if (thd->last_insert_id_used) - { - Intvar_log_event e((uchar)LAST_INSERT_ID_EVENT, thd->current_insert_id); - if(thd->server_id) - e.server_id = thd->server_id; - if (e.write(file)) - goto err; - } - if (thd->insert_id_used) - { - Intvar_log_event e((uchar)INSERT_ID_EVENT, thd->last_insert_id); - if(thd->server_id) - e.server_id = thd->server_id; - if (e.write(file)) - goto err; - } - if (thd->convert_set) + /* + No check for auto events flag here - this write method should + never be called if auto-events are enabled + */ + if (thd) { - char buf[1024] = "SET CHARACTER SET "; - char* p = strend(buf); - p = strmov(p, thd->convert_set->name); - int save_query_length = thd->query_length; - // just in case somebody wants it later - thd->query_length = (uint)(p - buf); - Query_log_event e(thd, buf); - if (e.write(file)) - goto err; - thd->query_length = save_query_length; // clean up + if (thd->last_insert_id_used) + { + Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT, + thd->current_insert_id); + e.set_log_pos(this); + if (thd->server_id) + e.server_id = thd->server_id; + if (e.write(file)) + goto err; + } + if (thd->insert_id_used) + { + Intvar_log_event e(thd,(uchar) INSERT_ID_EVENT,thd->last_insert_id); + e.set_log_pos(this); + if (thd->server_id) + e.server_id = thd->server_id; + if (e.write(file)) + goto err; + } + if (thd->rand_used) + { + Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2); + e.set_log_pos(this); + if (e.write(file)) + goto err; + } + if (thd->variables.convert_set) + { + char buf[256], *p; + p= strmov(strmov(buf, "SET CHARACTER SET "), + thd->variables.convert_set->name); + Query_log_event e(thd, buf, (ulong) (p - buf), 0); + e.set_log_pos(this); + if (e.write(file)) + goto err; + } } + event_info->set_log_pos(this); if (event_info->write(file) || file == &log_file && flush_io_cache(file)) goto err; error=0; - should_rotate = (file == &log_file && my_b_tell(file) >= max_binlog_size); - - /* Tell for transactional table handlers up to which position in the - binlog file we wrote. The table handler can store this info, and - after crash recovery print for the user the offset of the last - transactions which were recovered. Actually, we must also call - the table handler commit here, protected by the LOCK_log mutex, - because otherwise the transactions may end up in a different order - in the table handler log! */ - - if (file == &log_file) { - error = ha_report_binlog_offset_and_commit(thd, log_file_name, - file->pos_in_file); + + /* + Tell for transactional table handlers up to which position in the + binlog file we wrote. The table handler can store this info, and + after crash recovery print for the user the offset of the last + transactions which were recovered. Actually, we must also call + the table handler commit here, protected by the LOCK_log mutex, + because otherwise the transactions may end up in a different order + in the table handler log! + */ + + if (file == &log_file) + { + /* + LOAD DATA INFILE in AUTOCOMMIT=1 mode writes to the binlog + chunks also before it is successfully completed. We only report + the binlog write and do the commit inside the transactional table + handler if the log event type is appropriate. + */ + + if (event_info->get_type_code() == QUERY_EVENT + || event_info->get_type_code() == EXEC_LOAD_EVENT) + { + error = ha_report_binlog_offset_and_commit(thd, log_file_name, + file->pos_in_file); + } + + should_rotate= (my_b_tell(file) >= (my_off_t) max_binlog_size); } err: @@ -753,118 +1149,136 @@ err: write_error=1; } if (file == &log_file) - VOID(pthread_cond_broadcast(&COND_binlog_update)); + signal_update(); + if (should_rotate) + { + pthread_mutex_lock(&LOCK_index); + new_file(0); // inside mutex + pthread_mutex_unlock(&LOCK_index); + } } - if (should_rotate) - new_file(1); // inside mutex - VOID(pthread_mutex_unlock(&LOCK_log)); - return error; + + pthread_mutex_unlock(&LOCK_log); + DBUG_RETURN(error); } + +uint MYSQL_LOG::next_file_id() +{ + uint res; + pthread_mutex_lock(&LOCK_log); + res = file_id++; + pthread_mutex_unlock(&LOCK_log); + return res; +} + + /* Write a cached log entry to the binary log - We only come here if there is something in the cache. - 'cache' needs to be reinitialized after this functions returns. + + NOTE + - We only come here if there is something in the cache. + - The thing in the cache is always a complete transaction + - 'cache' needs to be reinitialized after this functions returns. + + IMPLEMENTATION + - To support transaction over replication, we wrap the transaction + with BEGIN/COMMIT in the binary log. */ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache) { VOID(pthread_mutex_lock(&LOCK_log)); - bool error=1; + DBUG_ENTER("MYSQL_LOG::write(cache"); - if (is_open()) + if (is_open()) // Should always be true { uint length; - if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0)) + /* + Add the "BEGIN" and "COMMIT" in the binlog around transactions + which may contain more than 1 SQL statement. If we run with + AUTOCOMMIT=1, then MySQL immediately writes each SQL statement to + the binlog when the statement has been completed. No need to add + "BEGIN" ... "COMMIT" around such statements. Otherwise, MySQL uses + thd->transaction.trans_log to cache the SQL statements until the + explicit commit, and at the commit writes the contents in .trans_log + to the binlog. + + We write the "BEGIN" mark first in the buffer (.trans_log) where we + store the SQL statements for a transaction. At the transaction commit + we will add the "COMMIT mark and write the buffer to the binlog. + */ { - sql_print_error(ER(ER_ERROR_ON_WRITE), cache->file_name, errno); - goto err; + Query_log_event qinfo(thd, "BEGIN", 5, TRUE); + if (qinfo.write(&log_file)) + goto err; } + /* Read from the file used to cache the queries .*/ + if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0)) + goto err; length=my_b_bytes_in_cache(cache); do { - if (my_b_write(&log_file, cache->rc_pos, length)) - { - if (!write_error) - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); + /* Write data to the binary log file */ + if (my_b_write(&log_file, cache->read_pos, length)) goto err; - } - cache->rc_pos=cache->rc_end; // Mark buffer used up + cache->read_pos=cache->read_end; // Mark buffer used up } while ((length=my_b_fill(cache))); - if (flush_io_cache(&log_file)) + + /* + We write the command "COMMIT" as the last SQL command in the + binlog segment cached for this transaction + */ + { - if (!write_error) - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); - goto err; + Query_log_event qinfo(thd, "COMMIT", 6, TRUE); + if (qinfo.write(&log_file) || flush_io_cache(&log_file)) + goto err; } if (cache->error) // Error on read { sql_print_error(ER(ER_ERROR_ON_READ), cache->file_name, errno); + write_error=1; // Don't give more errors goto err; } - error = ha_report_binlog_offset_and_commit(thd, log_file_name, - log_file.pos_in_file); - if (error) + if ((ha_report_binlog_offset_and_commit(thd, log_file_name, + log_file.pos_in_file))) goto err; - + signal_update(); if (my_b_tell(&log_file) >= (my_off_t) max_binlog_size) - new_file(1); // inside mutex + { + pthread_mutex_lock(&LOCK_index); + new_file(0); // inside mutex + pthread_mutex_unlock(&LOCK_index); + } + } - error=0; + VOID(pthread_mutex_unlock(&LOCK_log)); + DBUG_RETURN(0); err: - if (error) - write_error=1; - else - VOID(pthread_cond_broadcast(&COND_binlog_update)); - + if (!write_error) + { + write_error= 1; + sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); + } VOID(pthread_mutex_unlock(&LOCK_log)); - - return error; + DBUG_RETURN(1); } -bool MYSQL_LOG::write(Load_log_event* event_info) -{ - bool error=0; - bool should_rotate = 0; - - if (inited) - { - VOID(pthread_mutex_lock(&LOCK_log)); - if (is_open()) - { - THD *thd=event_info->thd; - if ((thd->options & OPTION_BIN_LOG) || - !(thd->master_access & PROCESS_ACL)) - { - if (event_info->write(&log_file) || flush_io_cache(&log_file)) - { - if (!write_error) - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); - error=write_error=1; - } - should_rotate = (my_b_tell(&log_file) >= max_binlog_size); - VOID(pthread_cond_broadcast(&COND_binlog_update)); - } - } - - if(should_rotate) - new_file(1); // inside mutex - - VOID(pthread_mutex_unlock(&LOCK_log)); - } - - - return error; -} +/* + Write update log in a format suitable for incremental backup + NOTE + - This code should be deleted in MySQL 5,0 as the binary log + is a full replacement for the update log. -/* Write update log in a format suitable for incremental backup */ +*/ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, - time_t query_start) + time_t query_start_arg) { bool error=0; if (is_open()) @@ -877,12 +1291,12 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, char buff[80],*end; end=buff; if (!(thd->options & OPTION_UPDATE_LOG) && - (thd->master_access & PROCESS_ACL)) + (thd->master_access & SUPER_ACL)) { VOID(pthread_mutex_unlock(&LOCK_log)); return 0; } - if ((specialflag & SPECIAL_LONG_LOG_FORMAT) || query_start) + if ((specialflag & SPECIAL_LONG_LOG_FORMAT) || query_start_arg) { current_time=time(NULL); if (current_time != last_time) @@ -910,13 +1324,13 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, thd->ip ? thd->ip : "") == (uint) -1) tmp_errno=errno; } - if (query_start) + if (query_start_arg) { /* For slow query log */ if (my_b_printf(&log_file, "# Query_time: %lu Lock_time: %lu Rows_sent: %lu Rows_examined: %lu\n", - (ulong) (current_time - query_start), - (ulong) (thd->time_after_lock - query_start), + (ulong) (current_time - query_start_arg), + (ulong) (thd->time_after_lock - query_start_arg), (ulong) thd->sent_row_count, (ulong) thd->examined_row_count) == (uint) -1) tmp_errno=errno; @@ -943,11 +1357,11 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, } if (thd->query_start_used) { - if (query_start != thd->query_start()) + if (query_start_arg != thd->query_start()) { - query_start=thd->query_start(); + query_start_arg=thd->query_start(); end=strmov(end,",timestamp="); - end=int10_to_str((long) query_start,end,10); + end=int10_to_str((long) query_start_arg,end,10); } } if (end != buff) @@ -984,41 +1398,109 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, return error; } +/* + Wait until we get a signal that the binary log has been updated + + SYNOPSIS + wait_for_update() + thd Thread variable + + NOTES + One must have a lock on LOCK_log before calling this function. + This lock will be freed before return! + + The reason for the above is that for enter_cond() / exit_cond() to + work the mutex must be got before enter_cond() but releases before + exit_cond(). + If you don't do it this way, you will get a deadlock in THD::awake() +*/ + + +void MYSQL_LOG:: wait_for_update(THD* thd) +{ + safe_mutex_assert_owner(&LOCK_log); + const char* old_msg = thd->enter_cond(&update_cond, &LOCK_log, + "Slave: waiting for binlog update"); + pthread_cond_wait(&update_cond, &LOCK_log); + pthread_mutex_unlock(&LOCK_log); // See NOTES + thd->exit_cond(old_msg); +} + + +/* + Close the log file + + SYNOPSIS + close() + exiting Set to 1 if we should also close the index file + This can be set to 0 if we are going to do call open + at once after close, in which case we don't want to + close the index file. + We only write a 'stop' event to the log if exiting is set + + NOTES + One can do an open on the object at once after doing a close. + The internal structures are not freed until cleanup() is called +*/ void MYSQL_LOG::close(bool exiting) { // One can't set log_type here! + DBUG_ENTER("MYSQL_LOG::close"); + DBUG_PRINT("enter",("exiting: %d", (int) exiting)); if (is_open()) { - File file=log_file.file; - if (log_type == LOG_BIN) + if (log_type == LOG_BIN && !no_auto_events && exiting) { Stop_log_event s; + s.set_log_pos(this); s.write(&log_file); - VOID(pthread_cond_broadcast(&COND_binlog_update)); + signal_update(); } end_io_cache(&log_file); - if (my_close(file,MYF(0)) < 0 && ! write_error) + if (my_close(log_file.file,MYF(0)) < 0 && ! write_error) { write_error=1; - sql_print_error(ER(ER_ERROR_ON_WRITE),name,errno); + sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); } } - if (exiting && index_file >= 0) + + /* + The following test is needed even if is_open() is not set, as we may have + called a not complete close earlier and the index file is still open. + */ + + if (exiting && my_b_inited(&index_file)) { - if (my_close(index_file,MYF(0)) < 0 && ! write_error) + end_io_cache(&index_file); + if (my_close(index_file.file, MYF(0)) < 0 && ! write_error) { - write_error=1; - sql_print_error(ER(ER_ERROR_ON_WRITE),name,errno); + write_error= 1; + sql_print_error(ER(ER_ERROR_ON_WRITE), index_file_name, errno); } - index_file=-1; - log_type=LOG_CLOSED; } + log_type= LOG_CLOSED; safeFree(name); + DBUG_VOID_RETURN; } - /* Check if a string is a valid number */ - /* Output: TRUE -> number */ +/* + Check if a string is a valid number + + SYNOPSIS + test_if_number() + str String to test + res Store value here + allow_wildcards Set to 1 if we should ignore '%' and '_' + + NOTE + For the moment the allow_wildcards argument is not used + Should be move to some other file. + + RETURN VALUES + 1 String is a number + 0 Error +*/ static bool test_if_number(register const char *str, long *res, bool allow_wildcards) @@ -1089,7 +1571,6 @@ void sql_print_error(const char *format,...) } - void sql_perror(const char *message) { #ifdef HAVE_STRERROR @@ -1098,3 +1579,49 @@ void sql_perror(const char *message) perror(message); #endif } + +bool flush_error_log() +{ + bool result=0; + if (opt_error_log) + { + char err_renamed[FN_REFLEN], *end; + end= strmake(err_renamed,log_error_file,FN_REFLEN-4); + strmov(end, "-old"); +#ifdef __WIN__ + char err_temp[FN_REFLEN+4]; + /* + On Windows is necessary a temporary file for to rename + the current error file. + */ + strmov(strmov(err_temp, err_renamed),"-tmp"); + (void) my_delete(err_temp, MYF(0)); + if (freopen(err_temp,"a+",stdout)) + { + freopen(err_temp,"a+",stderr); + (void) my_delete(err_renamed, MYF(0)); + my_rename(log_error_file,err_renamed,MYF(0)); + if (freopen(log_error_file,"a+",stdout)) + freopen(log_error_file,"a+",stderr); + int fd, bytes; + char buf[IO_SIZE]; + if ((fd = my_open(err_temp, O_RDONLY, MYF(0))) >= 0) + { + while ((bytes = (int) my_read(fd, (byte*) buf, IO_SIZE, MYF(0))) > 0) + my_fwrite(stderr, (byte*) buf, (uint) strlen(buf),MYF(0)); + my_close(fd, MYF(0)); + } + (void) my_delete(err_temp, MYF(0)); + } + else + result= 1; +#else + my_rename(log_error_file,err_renamed,MYF(0)); + if (freopen(log_error_file,"a+",stdout)) + freopen(log_error_file,"a+",stderr); + else + result= 1; +#endif + } + return result; +} |