diff options
author | unknown <knielsen@knielsen-hq.org> | 2011-04-01 15:07:10 +0200 |
---|---|---|
committer | unknown <knielsen@knielsen-hq.org> | 2011-04-01 15:07:10 +0200 |
commit | c677fea7d01ef1fab3357496b309a3e3b3c00dfe (patch) | |
tree | b6000be3447e5a41440cffee8b8d927d37a1dcde /sql/log.cc | |
parent | 09bd2894973fbdbacec9bfc31a3635165b120200 (diff) | |
parent | 9aa44bb4b3e0fc69306626b961c7d9309e6ab114 (diff) | |
download | mariadb-git-c677fea7d01ef1fab3357496b309a3e3b3c00dfe.tar.gz |
Merge MariaDB 5.2.5 release into MariaDB-5.2-rpl.
Diffstat (limited to 'sql/log.cc')
-rw-r--r-- | sql/log.cc | 309 |
1 files changed, 192 insertions, 117 deletions
diff --git a/sql/log.cc b/sql/log.cc index 755c71f8310..2e4fa44128b 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1,4 +1,4 @@ -/* Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc. +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1799,7 +1799,7 @@ static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv) log_query.append("`")) DBUG_RETURN(1); int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); - Query_log_event qinfo(thd, log_query.c_ptr_safe(), log_query.length(), + Query_log_event qinfo(thd, log_query.ptr(), log_query.length(), TRUE, TRUE, errcode); DBUG_RETURN(mysql_bin_log.write(&qinfo)); } @@ -1823,7 +1823,7 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv) log_query.append("`")) DBUG_RETURN(1); int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); - Query_log_event qinfo(thd, log_query.c_ptr_safe(), log_query.length(), + Query_log_event qinfo(thd, log_query.ptr(), log_query.length(), TRUE, TRUE, errcode); DBUG_RETURN(mysql_bin_log.write(&qinfo)); } @@ -1953,10 +1953,11 @@ static int find_uniq_filename(char *name) *end='.'; length= (size_t) (end-start+1); - if (!(dir_info = my_dir(buff,MYF(MY_DONT_SORT)))) + if ((DBUG_EVALUATE_IF("error_unique_log_filename", 1, + !(dir_info = my_dir(buff,MYF(MY_DONT_SORT)))))) { // This shouldn't happen strmov(end,".1"); // use name+1 - DBUG_RETURN(0); + DBUG_RETURN(1); } file_info= dir_info->dir_entry; for (i=dir_info->number_off_files ; i-- ; file_info++) @@ -1970,8 +1971,7 @@ static int find_uniq_filename(char *name) my_dirend(dir_info); *end++='.'; - sprintf(end,"%06ld",max_found+1); - DBUG_RETURN(0); + DBUG_RETURN((sprintf(end,"%06ld",max_found+1) < 0)); } @@ -2183,6 +2183,8 @@ int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name) { if (find_uniq_filename(new_name)) { + my_printf_error(ER_NO_UNIQUE_LOGFILE, ER(ER_NO_UNIQUE_LOGFILE), + MYF(ME_FATALERROR), log_name); sql_print_error(ER(ER_NO_UNIQUE_LOGFILE), log_name); return 1; } @@ -2705,6 +2707,23 @@ bool MYSQL_BIN_LOG::open(const char *log_name, sync_purge_index_file() || DBUG_EVALUATE_IF("fault_injection_registering_index", 1, 0)) { + /** + TODO: although this was introduced to appease valgrind + when injecting emulated faults using fault_injection_registering_index + it may be good to consider what actually happens when + open_purge_index_file succeeds but register or sync fails. + + Perhaps we might need the code below in MYSQL_LOG_BIN::cleanup + for "real life" purposes as well? + */ + DBUG_EXECUTE_IF("fault_injection_registering_index", { + if (my_b_inited(&purge_index_file)) + { + end_io_cache(&purge_index_file); + my_close(purge_index_file.file, MYF(0)); + } + }); + sql_print_error("MSYQL_BIN_LOG::open failed to sync the index file."); DBUG_RETURN(1); } @@ -3150,7 +3169,7 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd) } /* Start logging with a new file */ - close(LOG_CLOSE_INDEX); + close(LOG_CLOSE_INDEX | LOG_CLOSE_TO_BE_OPENED); if ((error= my_delete_allow_opened(index_file_name, MYF(0)))) // Reset (open will update) { if (my_errno == ENOENT) @@ -3179,7 +3198,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, FALSE)) - open(save_name, log_type, 0, io_cache_type, no_auto_events, max_size, 0, FALSE); + if ((error= open(save_name, log_type, 0, io_cache_type, no_auto_events, max_size, 0, FALSE))) + goto err; my_free((uchar*) save_name, MYF(0)); err: @@ -3841,17 +3861,23 @@ bool MYSQL_BIN_LOG::is_active(const char *log_file_name_arg) incapsulation 3) allows external access to the class without a lock (which is not possible with private new_file_without_locking method). + + @retval + nonzero - error */ -void MYSQL_BIN_LOG::new_file() +int MYSQL_BIN_LOG::new_file() { - new_file_impl(1); + return new_file_impl(1); } - -void MYSQL_BIN_LOG::new_file_without_locking() +/* + @retval + nonzero - error + */ +int MYSQL_BIN_LOG::new_file_without_locking() { - new_file_impl(0); + return new_file_impl(0); } @@ -3860,19 +3886,23 @@ void MYSQL_BIN_LOG::new_file_without_locking() @param need_lock Set to 1 if caller has not locked LOCK_log + @retval + nonzero - error + @note The new file name is stored last in the index file */ -void MYSQL_BIN_LOG::new_file_impl(bool need_lock) +int MYSQL_BIN_LOG::new_file_impl(bool need_lock) { - char new_name[FN_REFLEN], *new_name_ptr, *old_name; + int error= 0, close_on_error= FALSE; + char new_name[FN_REFLEN], *new_name_ptr, *old_name, *file_to_open; DBUG_ENTER("MYSQL_BIN_LOG::new_file_impl"); if (!is_open()) { DBUG_PRINT("info",("log is closed")); - DBUG_VOID_RETURN; + DBUG_RETURN(error); } if (need_lock) @@ -3910,7 +3940,7 @@ void MYSQL_BIN_LOG::new_file_impl(bool need_lock) 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 ((error= generate_new_name(new_name, name))) goto end; new_name_ptr=new_name; @@ -3924,7 +3954,14 @@ void MYSQL_BIN_LOG::new_file_impl(bool need_lock) */ Rotate_log_event r(new_name+dirname_length(new_name), 0, LOG_EVENT_OFFSET, is_relay_log ? Rotate_log_event::RELAY_LOG : 0); - r.write(&log_file); + if(DBUG_EVALUATE_IF("fault_injection_new_file_rotate_event", (error=close_on_error=TRUE), FALSE) || + (error= r.write(&log_file))) + { + DBUG_EXECUTE_IF("fault_injection_new_file_rotate_event", errno=2;); + close_on_error= TRUE; + my_printf_error(ER_ERROR_ON_WRITE, ER(ER_CANT_OPEN_FILE), MYF(ME_FATALERROR), name, errno); + goto end; + } bytes_written += r.data_written; } /* @@ -3952,17 +3989,56 @@ void MYSQL_BIN_LOG::new_file_impl(bool need_lock) */ /* reopen index binlog file, BUG#34582 */ - 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); + file_to_open= index_file_name; + error= open_index_file(index_file_name, 0, FALSE); + if (!error) + { + /* reopen the binary log file. */ + file_to_open= new_name_ptr; + error= open(old_name, log_type, new_name_ptr, io_cache_type, + no_auto_events, max_size, 1, FALSE); + } + + /* handle reopening errors */ + if (error) + { + my_printf_error(ER_CANT_OPEN_FILE, ER(ER_CANT_OPEN_FILE), + MYF(ME_FATALERROR), file_to_open, error); + close_on_error= TRUE; + } + my_free(old_name,MYF(0)); end: + + if (error && close_on_error /* rotate or reopen failed */) + { + /* + Close whatever was left opened. + + We are keeping the behavior as it exists today, ie, + we disable logging and move on (see: BUG#51014). + + TODO: as part of WL#1790 consider other approaches: + - kill mysql (safety); + - try multiple locations for opening a log file; + - switch server to protected/readonly mode + - ... + */ + close(LOG_CLOSE_INDEX); + sql_print_error("Could not open %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, shutdown the MySQL " + "server and restart it.", + new_name_ptr, errno); + } + if (need_lock) pthread_mutex_unlock(&LOCK_log); pthread_mutex_unlock(&LOCK_index); - DBUG_VOID_RETURN; + DBUG_RETURN(error); } @@ -3985,8 +4061,7 @@ bool MYSQL_BIN_LOG::append(Log_event* ev) bytes_written+= ev->data_written; DBUG_PRINT("info",("max_size: %lu",max_size)); if ((uint) my_b_append_tell(&log_file) > max_size) - new_file_without_locking(); - + error= new_file_without_locking(); err: pthread_mutex_unlock(&LOCK_log); signal_update(); // Safe as we don't call close @@ -4015,8 +4090,7 @@ bool MYSQL_BIN_LOG::appendv(const char* buf, uint len,...) } while ((buf=va_arg(args,const char*)) && (len=va_arg(args,uint))); DBUG_PRINT("info",("max_size: %lu",max_size)); if ((uint) my_b_append_tell(&log_file) > max_size) - new_file_without_locking(); - + error= new_file_without_locking(); err: if (!error) signal_update(); @@ -4385,7 +4459,7 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd, if (!error) { signal_update(); - rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED); + error= rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED); } /* @@ -4604,7 +4678,9 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) if (flush_and_sync()) goto err_unlock; signal_update(); - rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED); + if ((error= rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED))) + goto err_unlock; + } error=0; @@ -4700,8 +4776,19 @@ bool general_log_write(THD *thd, enum enum_server_command command, return FALSE; } -void MYSQL_BIN_LOG::rotate_and_purge(uint flags) +/** + @note + If rotation fails, for instance the server was unable + to create a new log file, we still try to write an + incident event to the current log. + + @retval + nonzero - error +*/ +int MYSQL_BIN_LOG::rotate_and_purge(uint flags) { + int error= 0; + DBUG_ENTER("MYSQL_BIN_LOG::rotate_and_purge"); #ifdef HAVE_REPLICATION bool check_purge= false; #endif @@ -4710,26 +4797,38 @@ void MYSQL_BIN_LOG::rotate_and_purge(uint flags) if ((flags & RP_FORCE_ROTATE) || (my_b_tell(&log_file) >= (my_off_t) max_size)) { - new_file_without_locking(); + if ((error= new_file_without_locking())) + /** + Be conservative... There are possible lost events (eg, + failing to log the Execute_load_query_log_event + on a LOAD DATA while using a non-transactional + table)! + + We give it a shot and try to write an incident event anyway + to the current log. + */ + if (!write_incident_already_locked(current_thd)) + flush_and_sync(); + #ifdef HAVE_REPLICATION check_purge= true; #endif } if (!(flags & RP_LOCK_LOG_IS_ALREADY_LOCKED)) pthread_mutex_unlock(&LOCK_log); - #ifdef HAVE_REPLICATION /* NOTE: Run purge_logs wo/ holding LOCK_log as it otherwise will deadlock in ndbcluster_binlog_index_purge_file */ - if (check_purge && expire_logs_days) + if (!error && check_purge && expire_logs_days) { time_t purge_time= my_time(0) - expire_logs_days*24*60*60; if (purge_time >= 0) purge_logs_before_date(purge_time); } #endif + DBUG_RETURN(error); } uint MYSQL_BIN_LOG::next_file_id() @@ -4912,33 +5011,48 @@ int query_error_code(THD *thd, bool not_killed) return error; } -bool MYSQL_BIN_LOG::write_incident(THD *thd) + +bool MYSQL_BIN_LOG::write_incident_already_locked(THD *thd) { uint error= 0; - my_off_t offset; - DBUG_ENTER("MYSQL_BIN_LOG::write_incident"); + DBUG_ENTER("MYSQL_BIN_LOG::write_incident_already_locked"); Incident incident= INCIDENT_LOST_EVENTS; Incident_log_event ev(thd, incident, write_error_msg); - pthread_mutex_lock(&LOCK_log); if (likely(is_open())) { error= ev.write(&log_file); status_var_add(thd->status_var.binlog_bytes_written, ev.data_written); - if (!error && !(error= flush_and_sync())) + } + + DBUG_RETURN(error); +} + + +bool MYSQL_BIN_LOG::write_incident(THD *thd) +{ + uint error= 0; + my_off_t offset; + DBUG_ENTER("MYSQL_BIN_LOG::write_incident"); + + pthread_mutex_lock(&LOCK_log); + if (likely(is_open())) + { + if (!(error= write_incident_already_locked(thd)) && + !(error= flush_and_sync())) { signal_update(); - rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED); + error= rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED); } + offset= my_b_tell(&log_file); + /* + Take mutex to protect against a reader seeing partial writes of 64-bit + offset on 32-bit CPUs. + */ + pthread_mutex_lock(&LOCK_commit_ordered); + last_commit_pos_offset= offset; + pthread_mutex_unlock(&LOCK_commit_ordered); } - offset= my_b_tell(&log_file); - /* - Take mutex to protect against a reader seeing partial writes of 64-bit - offset on 32-bit CPUs. - */ - pthread_mutex_lock(&LOCK_commit_ordered); - last_commit_pos_offset= offset; - pthread_mutex_unlock(&LOCK_commit_ordered); pthread_mutex_unlock(&LOCK_log); DBUG_RETURN(error); @@ -5091,6 +5205,8 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader) uint xid_count= 0; uint write_count= 0; my_off_t commit_offset; + group_commit_entry *current; + group_commit_entry *last_in_queue; DBUG_ENTER("MYSQL_BIN_LOG::trx_group_commit_leader"); LINT_INIT(commit_offset); @@ -5102,12 +5218,13 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader) DEBUG_SYNC(leader->thd, "commit_after_get_LOCK_log"); pthread_mutex_lock(&LOCK_prepare_ordered); - group_commit_entry *current= group_commit_queue; + current= group_commit_queue; group_commit_queue= NULL; pthread_mutex_unlock(&LOCK_prepare_ordered); /* As the queue is in reverse order of entering, reverse it. */ group_commit_entry *queue= NULL; + last_in_queue= current; while (current) { group_commit_entry *next= current->next; @@ -5188,7 +5305,18 @@ MYSQL_BIN_LOG::trx_group_commit_leader(group_commit_entry *leader) mark_xids_active(xid_count); } else - rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED); + { + if (rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED)) + { + /* + If we fail to rotate, which thread should get the error? + We give the error to the *last* transaction thread; that seems to + make the most sense, as it was the last to write to the log. + */ + last_in_queue->error= ER_ERROR_ON_WRITE; + last_in_queue->commit_errno= errno; + } + } } DEBUG_SYNC(leader->thd, "commit_before_get_LOCK_commit_ordered"); @@ -5451,80 +5579,26 @@ void sql_perror(const char *message) } -#ifdef __WIN__ +/* + Change the file associated with two output streams. Used to + redirect stdout and stderr to a file. The streams are reopened + only for appending (writing at end of file). +*/ extern "C" my_bool reopen_fstreams(const char *filename, FILE *outstream, FILE *errstream) { - int handle_fd; - int err_fd, out_fd; - HANDLE osfh; - - DBUG_ASSERT(filename && errstream); - - // Services don't have stdout/stderr on Windows, so _fileno returns -1. - err_fd= _fileno(errstream); - if (err_fd < 0) - { - if (!freopen(filename, "a+", errstream)) - return TRUE; - - setbuf(errstream, NULL); - err_fd= _fileno(errstream); - } - - if (outstream) - { - out_fd= _fileno(outstream); - if (out_fd < 0) - { - if (!freopen(filename, "a+", outstream)) - return TRUE; - out_fd= _fileno(outstream); - } - } - - if ((osfh= CreateFile(filename, GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE | - FILE_SHARE_DELETE, NULL, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, - NULL)) == INVALID_HANDLE_VALUE) - return TRUE; - - if ((handle_fd= _open_osfhandle((intptr_t)osfh, - _O_APPEND | _O_TEXT)) == -1) - { - CloseHandle(osfh); - return TRUE; - } - - if (_dup2(handle_fd, err_fd) < 0) - { - CloseHandle(osfh); - return TRUE; - } - - if (outstream && _dup2(handle_fd, out_fd) < 0) - { - CloseHandle(osfh); + if (outstream && !my_freopen(filename, "a", outstream)) return TRUE; - } - _close(handle_fd); - return FALSE; -} -#else -extern "C" my_bool reopen_fstreams(const char *filename, - FILE *outstream, FILE *errstream) -{ - if (outstream && !freopen(filename, "a+", outstream)) + if (errstream && !my_freopen(filename, "a", errstream)) return TRUE; - if (errstream && !freopen(filename, "a+", errstream)) - return TRUE; + /* The error stream must be unbuffered. */ + if (errstream) + setbuf(errstream, NULL); return FALSE; } -#endif /* @@ -6259,7 +6333,7 @@ int TC_LOG_MMAP::sync() cookie points directly to the memory where xid was logged. */ -void TC_LOG_MMAP::unlog(ulong cookie, my_xid xid) +int TC_LOG_MMAP::unlog(ulong cookie, my_xid xid) { PAGE *p=pages+(cookie/tc_log_page_size); my_xid *x=(my_xid *)(data+cookie); @@ -6277,6 +6351,7 @@ void TC_LOG_MMAP::unlog(ulong cookie, my_xid xid) if (p->waiters == 0) // the page is in pool and ready to rock pthread_cond_signal(&COND_pool); // ping ... for overflow() pthread_mutex_unlock(&p->lock); + return 0; } void TC_LOG_MMAP::close() @@ -6583,13 +6658,13 @@ TC_LOG_BINLOG::mark_xid_done() DBUG_VOID_RETURN; } -void TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid) +int TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid) { DBUG_ENTER("TC_LOG_BINLOG::unlog"); if (xid) mark_xid_done(); - rotate_and_purge(0); // as ::write_transaction_to_binlog() did not rotate - DBUG_VOID_RETURN; + /* As ::write_transaction_to_binlog() did not rotate, do it here. */ + DBUG_RETURN(rotate_and_purge(0)); } int TC_LOG_BINLOG::recover(IO_CACHE *log, Format_description_log_event *fdle) |