diff options
Diffstat (limited to 'sql/log.cc')
-rw-r--r-- | sql/log.cc | 165 |
1 files changed, 126 insertions, 39 deletions
diff --git a/sql/log.cc b/sql/log.cc index 4873cfd49c3..234fbae6961 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2185,7 +2185,8 @@ 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(1); @@ -2212,7 +2213,11 @@ updating the index files.", max_found); } next= max_found + 1; - sprintf(ext_buf, "%06lu", next); + if (sprintf(ext_buf, "%06lu", next)<0) + { + error= 1; + goto end; + } *end++='.'; /* @@ -2229,7 +2234,11 @@ index files.", name, ext_buf, (strlen(ext_buf) + (end - name))); goto end; } - sprintf(end, "%06lu", next); + if (sprintf(end, "%06lu", next)<0) + { + error= 1; + goto end; + } /* print warning if reaching the end of available extensions. */ if ((next > (MAX_LOG_UNIQUE_FN_EXT - LOG_WARN_UNIQUE_FN_EXT_LEFT))) @@ -2462,13 +2471,8 @@ int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name) { if (find_uniq_filename(new_name)) { - /* - This should be treated as error once propagation of error further - up in the stack gets proper handling. - */ - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_NO_UNIQUE_LOGFILE, ER(ER_NO_UNIQUE_LOGFILE), - log_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; } @@ -3450,7 +3454,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((void *) save_name); err: @@ -4115,17 +4120,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); } @@ -4134,19 +4145,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) @@ -4184,7 +4199,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; @@ -4198,7 +4213,13 @@ 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))) + { + 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; } /* @@ -4226,17 +4247,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); 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) mysql_mutex_unlock(&LOCK_log); mysql_mutex_unlock(&LOCK_index); - DBUG_VOID_RETURN; + DBUG_RETURN(error); } @@ -4261,8 +4321,7 @@ bool MYSQL_BIN_LOG::append(Log_event* ev) if (flush_and_sync(0)) goto err; if ((uint) my_b_append_tell(&log_file) > max_size) - new_file_without_locking(); - + error= new_file_without_locking(); err: mysql_mutex_unlock(&LOCK_log); signal_update(); // Safe as we don't call close @@ -4293,8 +4352,7 @@ bool MYSQL_BIN_LOG::appendv(const char* buf, uint len,...) if (flush_and_sync(0)) goto err; if ((uint) my_b_append_tell(&log_file) > max_size) - new_file_without_locking(); - + error= new_file_without_locking(); err: if (!error) signal_update(); @@ -4917,7 +4975,6 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) goto err; error= 0; - err: if (event_info->use_direct_logging()) { @@ -5022,8 +5079,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 @@ -5032,26 +5100,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(current_thd, FALSE)) + flush_and_sync(); + #ifdef HAVE_REPLICATION check_purge= true; #endif } if (!(flags & RP_LOCK_LOG_IS_ALREADY_LOCKED)) mysql_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() @@ -5237,6 +5317,10 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd, bool lock) { uint error= 0; DBUG_ENTER("MYSQL_BIN_LOG::write_incident"); + + if (!is_open()) + DBUG_RETURN(error); + LEX_STRING const write_error_msg= { C_STRING_WITH_LEN("error writing to the binary log") }; Incident incident= INCIDENT_LOST_EVENTS; @@ -5249,7 +5333,7 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd, bool lock) if (!error && !(error= flush_and_sync(0))) { signal_update(); - rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED); + error= rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED); } mysql_mutex_unlock(&LOCK_log); } @@ -5358,7 +5442,8 @@ bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event, mysql_mutex_unlock(&LOCK_prep_xids); } else - rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED); + if (rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED)) + goto err; } mysql_mutex_unlock(&LOCK_log); @@ -6191,7 +6276,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); @@ -6209,6 +6294,7 @@ void TC_LOG_MMAP::unlog(ulong cookie, my_xid xid) if (p->waiters == 0) // the page is in pool and ready to rock mysql_cond_signal(&COND_pool); // ping ... for overflow() mysql_mutex_unlock(&p->lock); + return 0; } void TC_LOG_MMAP::close() @@ -6451,8 +6537,9 @@ int TC_LOG_BINLOG::log_xid(THD *thd, my_xid xid) !binlog_commit_flush_trx_cache(thd, cache_mngr, xid)); } -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"); mysql_mutex_lock(&LOCK_prep_xids); DBUG_ASSERT(prepared_xids > 0); if (--prepared_xids == 0) { @@ -6460,7 +6547,7 @@ void TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid) mysql_cond_signal(&COND_prep_xids); } mysql_mutex_unlock(&LOCK_prep_xids); - rotate_and_purge(0); // as ::write() did not rotate + DBUG_RETURN(rotate_and_purge(0)); // as ::write() did not rotate } int TC_LOG_BINLOG::recover(IO_CACHE *log, Format_description_log_event *fdle) |