summaryrefslogtreecommitdiff
path: root/sql/log.cc
diff options
context:
space:
mode:
authorunknown <knielsen@knielsen-hq.org>2011-04-01 15:07:10 +0200
committerunknown <knielsen@knielsen-hq.org>2011-04-01 15:07:10 +0200
commitc677fea7d01ef1fab3357496b309a3e3b3c00dfe (patch)
treeb6000be3447e5a41440cffee8b8d927d37a1dcde /sql/log.cc
parent09bd2894973fbdbacec9bfc31a3635165b120200 (diff)
parent9aa44bb4b3e0fc69306626b961c7d9309e6ab114 (diff)
downloadmariadb-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.cc309
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)