Hi Zhenxing,
I am mostly ok with the incremental changes.
I was a bit skeptical about moving the
decide_logging_format into binlog.cc, but I guess it
makes sense to keep it a bit disconnected from the rest
of the server core.
I was also a bit skeptical about splitting the
mysql_show_binlog_events, but I guess there is a need
for it.
I did not build this, I guess that your changes are good
for both: cmake and autotools...
Patch approved.
Now we "just" need to break down the inter-module
dependencies...
Regards,
Luís
On Thu, 2010-05-13 at 05:54 +0000, He Zhenxing wrote:
> #At file:///media/sdb2/hezx/work/mysql/bzrwork/w3662/trunk-bugfixing/ based on
> revid:zhenxing.he@stripped
>
> 3037 He Zhenxing 2010-05-13
> WL#5385 Refactoring: Split replication into libraries
>
> Incremental patch based on previous patch.
>
> - orgnized functions in binlog.cc
> - removed sql_repl.h, added master.h move code to slave.h and
> master.h correspondly
> - split mysql_show_binlog_events, add mysql_show_relaylog_events
> and show_binlog_events.
> @ sql/Makefile.am
> Added master.h
> @ sql/binlog.cc
> moved binlog related code from sql_class.cc to binlog.cc
> @ sql/master.h
> Added master.h
> @ sql/rpl_rli.cc
> added mysql_show_relaylog_events
> @ sql/rpl_rli.h
> moved some relay log related functions delaration here from slave.h
> @ sql/slave.h
> moved slave related declarations from sql_repl.h
> @ sql/sql_class.cc
> Moved binlog related code to binlog.cc
> Moved definition of Discrete_intervals_list::append to structs.h
> @ sql/sql_parse.cc
> use mysql_show_relaylog_events for SHOW RELAYLOG EVENTS command
> @ sql/structs.h
> Moved definition of Discrete_intervals_list::append from sql_class.cc
>
> removed:
> sql/sql_repl.h
> added:
> sql/master.h
> modified:
> sql/Makefile.am
> sql/binlog.cc
> sql/binlog.h
> sql/log.h
> sql/master.cc
> sql/mysqld.cc
> sql/repl_failsafe.cc
> sql/rpl_handler.cc
> sql/rpl_rli.cc
> sql/rpl_rli.h
> sql/slave.cc
> sql/slave.h
> sql/sql_class.cc
> sql/sql_load.cc
> sql/sql_parse.cc
> sql/structs.h
> === modified file 'sql/Makefile.am'
> --- a/sql/Makefile.am 2010-05-08 02:34:04 +0000
> +++ b/sql/Makefile.am 2010-05-13 05:54:26 +0000
> @@ -109,7 +109,7 @@ noinst_HEADERS = item.h item_func.h item
> sql_derived.h sql_load.h sql_handler.h init.h \
> derror.h sql_union.h des_key_file.h sql_binlog.h \
> discover.h sql_manager.h sql_do.h \
> - sql_repl.h slave.h rpl_filter.h rpl_injector.h \
> + sql_repl.h slave.h master.h rpl_filter.h rpl_injector.h \
> log_event.h rpl_record.h sql_const.h \
> log_event_old.h rpl_record_old.h \
> sql_sort.h sql_cache.h set_var.h sys_vars_shared.h \
>
> === modified file 'sql/binlog.cc'
> --- a/sql/binlog.cc 2010-05-08 02:34:04 +0000
> +++ b/sql/binlog.cc 2010-05-13 05:54:26 +0000
> @@ -20,7 +20,6 @@
> #include "log_event.h"
> #include "rpl_filter.h"
> #include "rpl_rli.h"
> -#include "sql_repl.h"
> #include "sql_plugin.h"
> #include "rpl_handler.h"
>
> @@ -31,6 +30,10 @@ handlerton *binlog_hton;
>
> MYSQL_BIN_LOG mysql_bin_log(&sync_binlog_period);
>
> +static bool purge_error_message(THD* thd, int res);
> +static void adjust_linfo_offsets(my_off_t purge_offset);
> +static bool log_in_use(const char* log_name);
> +
> static int binlog_init(void *p);
> static int binlog_close_connection(handlerton *hton, THD *thd);
> static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv);
> @@ -295,7 +298,7 @@ binlog_trans_log_truncate(THD *thd, my_o
> should be moved here.
> */
>
> -int binlog_init(void *p)
> +static int binlog_init(void *p)
> {
> binlog_hton= (handlerton *)p;
> binlog_hton->state=opt_bin_log ? SHOW_OPTION_YES : SHOW_OPTION_NO;
> @@ -655,23 +658,6 @@ static int binlog_rollback(handlerton *h
> DBUG_RETURN(error);
> }
>
> -void MYSQL_BIN_LOG::set_write_error(THD *thd)
> -{
> - DBUG_ENTER("MYSQL_BIN_LOG::set_write_error");
> -
> - write_error= 1;
> -
> - if (check_write_error(thd))
> - DBUG_VOID_RETURN;
> -
> - if (my_errno == EFBIG)
> - my_message(ER_TRANS_CACHE_FULL, ER(ER_TRANS_CACHE_FULL), MYF(MY_WME));
> - else
> - my_error(ER_ERROR_ON_WRITE, MYF(MY_WME), name, errno);
> -
> - DBUG_VOID_RETURN;
> -}
> -
> /**
> @note
> How do we handle this (unlikely but legal) case:
> @@ -735,6 +721,97 @@ static int binlog_savepoint_rollback(han
> }
>
>
> +/*
> + Adjust the position pointer in the binary log file for all running slaves
> +
> + SYNOPSIS
> + adjust_linfo_offsets()
> + purge_offset Number of bytes removed from start of log index file
> +
> + NOTES
> + - This is called when doing a PURGE when we delete lines from the
> + index log file
> +
> + REQUIREMENTS
> + - Before calling this function, we have to ensure that no threads are
> + using any binary log file before purge_offset.a
> +
> + TODO
> + - Inform the slave threads that they should sync the position
> + in the binary log file with flush_relay_log_info.
> + Now they sync is done for next read.
> +*/
> +
> +static void adjust_linfo_offsets(my_off_t purge_offset)
> +{
> + THD *tmp;
> +
> + mysql_mutex_lock(&LOCK_thread_count);
> + I_List_iterator<THD> it(threads);
> +
> + while ((tmp=it++))
> + {
> + LOG_INFO* linfo;
> + if ((linfo = tmp->current_linfo))
> + {
> + mysql_mutex_lock(&linfo->lock);
> + /*
> + Index file offset can be less that purge offset only if
> + we just started reading the index file. In that case
> + we have nothing to adjust
> + */
> + if (linfo->index_file_offset < purge_offset)
> + linfo->fatal = (linfo->index_file_offset != 0);
> + else
> + linfo->index_file_offset -= purge_offset;
> + mysql_mutex_unlock(&linfo->lock);
> + }
> + }
> + mysql_mutex_unlock(&LOCK_thread_count);
> +}
> +
> +
> +static bool log_in_use(const char* log_name)
> +{
> + size_t log_name_len = strlen(log_name) + 1;
> + THD *tmp;
> + bool result = 0;
> +
> + mysql_mutex_lock(&LOCK_thread_count);
> + I_List_iterator<THD> it(threads);
> +
> + while ((tmp=it++))
> + {
> + LOG_INFO* linfo;
> + if ((linfo = tmp->current_linfo))
> + {
> + mysql_mutex_lock(&linfo->lock);
> + result = !bcmp((uchar*) log_name, (uchar*) linfo->log_file_name,
> + log_name_len);
> + mysql_mutex_unlock(&linfo->lock);
> + if (result)
> + break;
> + }
> + }
> +
> + mysql_mutex_unlock(&LOCK_thread_count);
> + return result;
> +}
> +
> +static bool purge_error_message(THD* thd, int res)
> +{
> + uint errcode;
> +
> + if ((errcode= purge_log_get_error_code(res)) != 0)
> + {
> + my_message(errcode, ER(errcode), MYF(0));
> + return TRUE;
> + }
> + my_ok(thd);
> + return FALSE;
> +}
> +
> +
> int check_binlog_magic(IO_CACHE* log, const char** errmsg)
> {
> char magic[4];
> @@ -791,3000 +868,4229 @@ err:
> DBUG_RETURN(-1);
> }
>
> -bool MYSQL_BIN_LOG::check_write_error(THD *thd)
> +/**
> + This function checks if a transactional table was updated by the
> + current transaction.
> +
> + @param thd The client thread that executed the current statement.
> + @return
> + @c true if a transactional table was updated, @c false otherwise.
> +*/
> +bool
> +trans_has_updated_trans_table(const THD* thd)
> {
> - DBUG_ENTER("MYSQL_BIN_LOG::check_write_error");
> + binlog_cache_mngr *const cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
>
> - bool checked= FALSE;
> + return (cache_mngr ? !cache_mngr->trx_cache.empty() : 0);
> +}
>
> - if (!thd->is_error())
> - DBUG_RETURN(checked);
> +/**
> + This function checks if a transactional table was updated by the
> + current statement.
>
> - switch (thd->stmt_da->sql_errno())
> + @param thd The client thread that executed the current statement.
> + @return
> + @c true if a transactional table was updated, @c false otherwise.
> +*/
> +bool
> +stmt_has_updated_trans_table(const THD *thd)
> +{
> + Ha_trx_info *ha_info;
> +
> + for (ha_info= thd->transaction.stmt.ha_list; ha_info;
> + ha_info= ha_info->next())
> {
> - case ER_TRANS_CACHE_FULL:
> - case ER_ERROR_ON_WRITE:
> - case ER_BINLOG_LOGGING_IMPOSSIBLE:
> - checked= TRUE;
> - break;
> + if (ha_info->is_trx_read_write() && ha_info->ht() != binlog_hton)
> + return (TRUE);
> }
> -
> - DBUG_RETURN(checked);
> + return (FALSE);
> }
>
> -MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
> - :bytes_written(0), prepared_xids(0), file_id(1), open_count(1),
> - need_start_event(TRUE), m_table_map_version(0),
> - sync_period_ptr(sync_period),
> - is_relay_log(0), signal_cnt(0),
> - description_event_for_exec(0), description_event_for_queue(0)
> -{
> - /*
> - We don't want to initialize locks here as such initialization depends on
> - safe_mutex (when using safe_mutex) which depends on MY_INIT(), which is
> - called only in main(). Doing initialization here would make it happen
> - before main().
> - */
> - index_file_name[0] = 0;
> - bzero((char*) &index_file, sizeof(index_file));
> - bzero((char*) &purge_index_file, sizeof(purge_index_file));
> -}
> +/**
> + This function checks if either a trx-cache or a non-trx-cache should
> + be used. If @c bin_log_direct_non_trans_update is active or the format
> + is either MIXED or ROW, the cache to be used depends on the flag @c
> + is_transactional.
>
> -/* this is called only once */
> + On the other hand, if binlog_format is STMT or direct option is
> + OFF, the trx-cache should be used if and only if the statement is
> + transactional or the trx-cache is not empty. Otherwise, the
> + non-trx-cache should be used.
>
> -void MYSQL_BIN_LOG::cleanup()
> + @param thd The client thread.
> + @param is_transactional The changes are related to a trx-table.
> + @return
> + @c true if a trx-cache should be used, @c false otherwise.
> +*/
> +bool use_trans_cache(const THD* thd, bool is_transactional)
> {
> - DBUG_ENTER("cleanup");
> - if (inited)
> - {
> - inited= 0;
> - close(LOG_CLOSE_INDEX|LOG_CLOSE_STOP_EVENT);
> - delete description_event_for_queue;
> - delete description_event_for_exec;
> - mysql_mutex_destroy(&LOCK_log);
> - mysql_mutex_destroy(&LOCK_index);
> - mysql_cond_destroy(&update_cond);
> - }
> - DBUG_VOID_RETURN;
> + binlog_cache_mngr *const cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> +
> + return
> + ((thd->variables.binlog_format != BINLOG_FORMAT_STMT ||
> + thd->variables.binlog_direct_non_trans_update) ? is_transactional :
> + (is_transactional || !cache_mngr->trx_cache.empty()));
> }
>
> +/**
> + This function checks if a transaction, either a multi-statement
> + or a single statement transaction is about to commit or not.
>
> -/* Init binlog-specific vars */
> -void MYSQL_BIN_LOG::init(bool no_auto_events_arg, ulong max_size_arg)
> + @param thd The client thread that executed the current statement.
> + @param all Committing a transaction (i.e. TRUE) or a statement
> + (i.e. FALSE).
> + @return
> + @c true if committing a transaction, otherwise @c false.
> +*/
> +bool ending_trans(THD* thd, const bool all)
> {
> - DBUG_ENTER("MYSQL_BIN_LOG::init");
> - no_auto_events= no_auto_events_arg;
> - max_size= max_size_arg;
> - DBUG_PRINT("info",("max_size: %lu", max_size));
> - DBUG_VOID_RETURN;
> + return (all || (!all && !thd->in_multi_stmt_transaction()));
> }
>
> +/**
> + This function checks if a non-transactional table was updated by
> + the current transaction.
>
> -void MYSQL_BIN_LOG::init_pthread_objects()
> + @param thd The client thread that executed the current statement.
> + @return
> + @c true if a non-transactional table was updated, @c false
> + otherwise.
> +*/
> +bool trans_has_updated_non_trans_table(const THD* thd)
> {
> - DBUG_ASSERT(inited == 0);
> - inited= 1;
> - mysql_mutex_init(key_LOG_LOCK_log, &LOCK_log, MY_MUTEX_INIT_SLOW);
> - mysql_mutex_init(key_BINLOG_LOCK_index, &LOCK_index, MY_MUTEX_INIT_SLOW);
> - mysql_cond_init(key_BINLOG_update_cond, &update_cond, 0);
> + return (thd->transaction.all.modified_non_trans_table ||
> + thd->transaction.stmt.modified_non_trans_table);
> }
>
> +/**
> + This function checks if a non-transactional table was updated by the
> + current statement.
>
> -bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
> - const char *log_name, bool need_mutex)
> + @param thd The client thread that executed the current statement.
> + @return
> + @c true if a non-transactional table was updated, @c false otherwise.
> +*/
> +bool stmt_has_updated_non_trans_table(const THD* thd)
> {
> - File index_file_nr= -1;
> - DBUG_ASSERT(!my_b_inited(&index_file));
> + return (thd->transaction.stmt.modified_non_trans_table);
> +}
>
> - /*
> - 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.
> - */
> - myf opt= MY_UNPACK_FILENAME;
> - if (!index_file_name_arg)
> +#ifndef EMBEDDED_LIBRARY
> +/**
> + Execute a PURGE BINARY LOGS TO <log> command.
> +
> + @param thd Pointer to THD object for the client thread executing the
> + statement.
> +
> + @param to_log Name of the last log to purge.
> +
> + @retval FALSE success
> + @retval TRUE failure
> +*/
> +bool purge_master_logs(THD* thd, const char* to_log)
> +{
> + char search_file_name[FN_REFLEN];
> + if (!mysql_bin_log.is_open())
> {
> - index_file_name_arg= log_name; // Use same basename for index file
> - opt= MY_UNPACK_FILENAME | MY_REPLACE_EXT;
> + my_ok(thd);
> + return FALSE;
> }
> - fn_format(index_file_name, index_file_name_arg, mysql_data_home,
> - ".index", opt);
> - if ((index_file_nr= mysql_file_open(key_file_binlog_index,
> - index_file_name,
> - O_RDWR | O_CREAT | O_BINARY,
> - MYF(MY_WME))) < 0 ||
> - mysql_file_sync(index_file_nr, MYF(MY_WME)) ||
> - init_io_cache(&index_file, index_file_nr,
> - IO_SIZE, WRITE_CACHE,
> - mysql_file_seek(index_file_nr, 0L, MY_SEEK_END, MYF(0)),
> - 0, MYF(MY_WME | MY_WAIT_IF_FULL)) ||
> - DBUG_EVALUATE_IF("fault_injection_openning_index", 1, 0))
> +
> + mysql_bin_log.make_log_name(search_file_name, to_log);
> + return purge_error_message(thd,
> + mysql_bin_log.purge_logs(search_file_name, 0, 1,
> + 1, NULL));
> +}
> +
> +
> +/**
> + Execute a PURGE BINARY LOGS BEFORE <date> command.
> +
> + @param thd Pointer to THD object for the client thread executing the
> + statement.
> +
> + @param purge_time Date before which logs should be purged.
> +
> + @retval FALSE success
> + @retval TRUE failure
> +*/
> +bool purge_master_logs_before_date(THD* thd, time_t purge_time)
> +{
> + if (!mysql_bin_log.is_open())
> {
> - /*
> - TODO: all operations creating/deleting the index file or a log, should
> - call my_sync_dir() or my_sync_dir_by_file() to be durable.
> - TODO: file creation should be done with mysql_file_create()
> - not mysql_file_open().
> - */
> - if (index_file_nr >= 0)
> - mysql_file_close(index_file_nr, MYF(0));
> - return TRUE;
> + my_ok(thd);
> + return 0;
> }
> + return purge_error_message(thd,
> + mysql_bin_log.purge_logs_before_date(purge_time));
> +}
> +#endif /* EMBEDDED_LIBRARY */
>
> -#ifdef HAVE_REPLICATION
> - /*
> - Sync the index by purging any binary log file that is not registered.
> - In other words, either purge binary log files that were removed from
> - the index but not purged from the file system due to a crash or purge
> - any binary log file that was created but not register in the index
> - due to a crash.
> - */
> +/*
> + Helper function to get the error code of the query to be binlogged.
> + */
> +int query_error_code(THD *thd, bool not_killed)
> +{
> + int error;
> +
> + if (not_killed || (thd->killed == THD::KILL_BAD_DATA))
> + {
> + error= thd->is_error() ? thd->stmt_da->sql_errno() : 0;
>
> - if (set_purge_index_file_name(index_file_name_arg) ||
> - open_purge_index_file(FALSE) ||
> - purge_index_entry(NULL, NULL, need_mutex) ||
> - close_purge_index_file() ||
> - DBUG_EVALUATE_IF("fault_injection_recovering_index", 1, 0))
> + /* thd->stmt_da->sql_errno() might be ER_SERVER_SHUTDOWN or
> + ER_QUERY_INTERRUPTED, So here we need to make sure that error
> + is not set to these errors when specified not_killed by the
> + caller.
> + */
> + if (error == ER_SERVER_SHUTDOWN || error == ER_QUERY_INTERRUPTED)
> + error= 0;
> + }
> + else
> {
> - sql_print_error("MYSQL_BIN_LOG::open_index_file failed to sync the index "
> - "file.");
> - return TRUE;
> + /* killed status for DELAYED INSERT thread should never be used */
> + DBUG_ASSERT(!(thd->system_thread & SYSTEM_THREAD_DELAYED_INSERT));
> + error= thd->killed_errno();
> }
> -#endif
>
> - return FALSE;
> + return error;
> }
>
>
> /**
> - Open a (new) binlog file.
> + Move all data up in a file in an filename index file.
>
> - - Open the log file and the index file. 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.
> + 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.
> +
> + @param index_file File to move
> + @param offset Move everything from here to beginning
> +
> + @note
> + File will be truncated to be 'offset' shorter or filled up with newlines
>
> @retval
> 0 ok
> - @retval
> - 1 error
> */
>
> -bool MYSQL_BIN_LOG::open(const char *log_name,
> - enum_log_type log_type_arg,
> - const char *new_name,
> - enum cache_type io_cache_type_arg,
> - bool no_auto_events_arg,
> - ulong max_size_arg,
> - bool null_created_arg,
> - bool need_mutex)
> -{
> - File file= -1;
> +#ifdef HAVE_REPLICATION
>
> - DBUG_ENTER("MYSQL_BIN_LOG::open");
> - DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg));
> +static bool copy_up_file_and_fill(IO_CACHE *index_file, my_off_t offset)
> +{
> + int bytes_read;
> + my_off_t init_offset= offset;
> + File file= index_file->file;
> + uchar io_buf[IO_SIZE*2];
> + DBUG_ENTER("copy_up_file_and_fill");
>
> - if (init_and_set_log_file_name(log_name, new_name, log_type_arg,
> - io_cache_type_arg))
> + for (;; offset+= bytes_read)
> {
> - sql_print_error("MSYQL_BIN_LOG::open failed to generate new file name.");
> - DBUG_RETURN(1);
> + mysql_file_seek(file, offset, MY_SEEK_SET, MYF(0));
> + if ((bytes_read= (int) mysql_file_read(file, io_buf, sizeof(io_buf),
> + MYF(MY_WME)))
> + < 0)
> + goto err;
> + if (!bytes_read)
> + break; // end of file
> + mysql_file_seek(file, offset-init_offset, MY_SEEK_SET, MYF(0));
> + if (mysql_file_write(file, io_buf, bytes_read, MYF(MY_WME | MY_NABP)))
> + goto err;
> }
> + /* The following will either truncate the file or fill the end with \n' */
> + if (mysql_file_chsize(file, offset - init_offset, '\n', MYF(MY_WME)) ||
> + mysql_file_sync(file, MYF(MY_WME)))
> + goto err;
>
> -#ifdef HAVE_REPLICATION
> - if (open_purge_index_file(TRUE) ||
> - register_create_index_entry(log_file_name) ||
> - sync_purge_index_file() ||
> - DBUG_EVALUATE_IF("fault_injection_registering_index", 1, 0))
> - {
> - sql_print_error("MSYQL_BIN_LOG::open failed to sync the index file.");
> - DBUG_RETURN(1);
> - }
> - DBUG_EXECUTE_IF("crash_create_non_critical_before_update_index", DBUG_ABORT(););
> -#endif
> + /* Reset data in old index cache */
> + reinit_io_cache(index_file, READ_CACHE, (my_off_t) 0, 0, 1);
> + DBUG_RETURN(0);
>
> - write_error= 0;
> +err:
> + DBUG_RETURN(1);
> +}
>
> - /* open the main log file */
> - if (MYSQL_LOG::open(log_name, log_type_arg, new_name,
> - io_cache_type_arg))
> - {
> -#ifdef HAVE_REPLICATION
> - close_purge_index_file();
> -#endif
> - DBUG_RETURN(1); /* all warnings issued */
> +/**
> + Load data's io cache specific hook to be executed
> + before a chunk of data is being read into the cache's buffer
> + The fuction instantianates and writes into the binlog
> + replication events along LOAD DATA processing.
> +
> + @param file pointer to io-cache
> + @retval 0 success
> + @retval 1 failure
> +*/
> +int log_loaded_block(IO_CACHE* file)
> +{
> + DBUG_ENTER("log_loaded_block");
> + LOAD_FILE_INFO *lf_info;
> + uint block_len;
> + /* buffer contains position where we started last read */
> + uchar* buffer= (uchar*) my_b_get_buffer_start(file);
> + uint max_event_size= current_thd->variables.max_allowed_packet;
> + lf_info= (LOAD_FILE_INFO*) file->arg;
> + if (lf_info->thd->is_current_stmt_binlog_format_row())
> + DBUG_RETURN(0);
> + if (lf_info->last_pos_in_file != HA_POS_ERROR &&
> + lf_info->last_pos_in_file >= my_b_get_pos_in_file(file))
> + DBUG_RETURN(0);
> +
> + for (block_len= (uint) (my_b_get_bytes_in_buffer(file)); block_len > 0;
> + buffer += min(block_len, max_event_size),
> + block_len -= min(block_len, max_event_size))
> + {
> + lf_info->last_pos_in_file= my_b_get_pos_in_file(file);
> + if (lf_info->wrote_create_file)
> + {
> + Append_block_log_event a(lf_info->thd, lf_info->thd->db, buffer,
> + min(block_len, max_event_size),
> + lf_info->log_delayed);
> + if (mysql_bin_log.write(&a))
> + DBUG_RETURN(1);
> + }
> + else
> + {
> + Begin_load_query_log_event b(lf_info->thd, lf_info->thd->db,
> + buffer,
> + min(block_len, max_event_size),
> + lf_info->log_delayed);
> + if (mysql_bin_log.write(&b))
> + DBUG_RETURN(1);
> + lf_info->wrote_create_file= 1;
> + }
> }
> + DBUG_RETURN(0);
> +}
>
> - init(no_auto_events_arg, max_size_arg);
> +/* Helper function for SHOW BINLOG/RELAYLOG EVENTS */
> +bool show_binlog_events(THD *thd, MYSQL_BIN_LOG *binary_log)
> +{
> + Protocol *protocol= thd->protocol;
> + List<Item> field_list;
> + const char *errmsg = 0;
> + bool ret = TRUE;
> + IO_CACHE log;
> + File file = -1;
> + DBUG_ENTER("show_binlog_events");
> +
> + DBUG_ASSERT(thd->lex->sql_command == SQLCOM_SHOW_BINLOG_EVENTS ||
> + thd->lex->sql_command == SQLCOM_SHOW_RELAYLOG_EVENTS);
> +
> + Format_description_log_event *description_event= new
> + Format_description_log_event(3); /* MySQL 4.0 by default */
> +
> + if (binary_log->is_open())
> + {
> + LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
> + SELECT_LEX_UNIT *unit= &thd->lex->unit;
> + ha_rows event_count, limit_start, limit_end;
> + my_off_t pos = max(BIN_LOG_HEADER_SIZE, lex_mi->pos); // user-friendly
> + char search_file_name[FN_REFLEN], *name;
> + const char *log_file_name = lex_mi->log_file_name;
> + mysql_mutex_t *log_lock = binary_log->get_log_lock();
> + LOG_INFO linfo;
> + Log_event* ev;
> +
> + unit->set_limit(thd->lex->current_select);
> + limit_start= unit->offset_limit_cnt;
> + limit_end= unit->select_limit_cnt;
> +
> + name= search_file_name;
> + if (log_file_name)
> + binary_log->make_log_name(search_file_name, log_file_name);
> + else
> + name=0; // Find first log
>
> - open_count++;
> + linfo.index_file_offset = 0;
>
> - DBUG_ASSERT(log_type == LOG_BIN);
> + if (binary_log->find_log_pos(&linfo, name, 1))
> + {
> + errmsg = "Could not find target log";
> + goto err;
> + }
>
> - {
> - bool write_file_name_to_index_file=0;
> + mysql_mutex_lock(&LOCK_thread_count);
> + thd->current_linfo = &linfo;
> + mysql_mutex_unlock(&LOCK_thread_count);
>
> - if (!my_b_filelength(&log_file))
> + if ((file=open_binlog(&log, linfo.log_file_name, &errmsg)) < 0)
> + goto err;
> +
> + /*
> + to account binlog event header size
> + */
> + thd->variables.max_allowed_packet += MAX_LOG_EVENT_HEADER;
> +
> + mysql_mutex_lock(log_lock);
> +
> + /*
> + open_binlog() sought to position 4.
> + Read the first event in case it's a Format_description_log_event, to
> + know the format. If there's no such event, we are 3.23 or 4.x. This
> + code, like before, can't read 3.23 binlogs.
> + This code will fail on a mixed relay log (one which has Format_desc then
> + Rotate then Format_desc).
> + */
> + ev= Log_event::read_log_event(&log, (mysql_mutex_t*)0, description_event);
> + if (ev)
> {
> - /*
> - 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_safe_write(&log_file, (uchar*) BINLOG_MAGIC,
> - BIN_LOG_HEADER_SIZE))
> - goto err;
> - bytes_written+= BIN_LOG_HEADER_SIZE;
> - write_file_name_to_index_file= 1;
> + if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
> + {
> + delete description_event;
> + description_event= (Format_description_log_event*) ev;
> + }
> + else
> + delete ev;
> }
>
> - if (need_start_event && !no_auto_events)
> + my_b_seek(&log, pos);
> +
> + if (!description_event->is_valid())
> {
> - /*
> - In 4.x we set need_start_event=0 here, but in 5.0 we want a Start event
> - even if this is not the very first binlog.
> - */
> - Format_description_log_event s(BINLOG_VERSION);
> - /*
> - don't set LOG_EVENT_BINLOG_IN_USE_F for SEQ_READ_APPEND io_cache
> - as we won't be able to reset it later
> - */
> - if (io_cache_type == WRITE_CACHE)
> - s.flags|= LOG_EVENT_BINLOG_IN_USE_F;
> - if (!s.is_valid())
> - goto err;
> - s.dont_set_created= null_created_arg;
> - if (s.write(&log_file))
> - goto err;
> - bytes_written+= s.data_written;
> + errmsg="Invalid Format_description event; could be out of memory";
> + goto err;
> }
> - if (description_event_for_queue &&
> - description_event_for_queue->binlog_version>=4)
> +
> + for (event_count = 0;
> + (ev = Log_event::read_log_event(&log, (mysql_mutex_t*) 0,
> + description_event)); )
> {
> - /*
> - This is a relay log written to by the I/O slave thread.
> - Write the event so that others can later know the format of this relay
> - log.
> - Note that this event is very close to the original event from the
> - master (it has binlog version of the master, event types of the
> - master), so this is suitable to parse the next relay log's event. It
> - has been produced by
> - Format_description_log_event::Format_description_log_event(char* buf,).
> - Why don't we want to write the description_event_for_queue if this
> - event is for format<4 (3.23 or 4.x): this is because in that case, the
> - description_event_for_queue describes the data received from the
> - master, but not the data written to the relay log (*conversion*),
> - which is in format 4 (slave's).
> - */
> - /*
> - Set 'created' to 0, so that in next relay logs this event does not
> - trigger cleaning actions on the slave in
> - Format_description_log_event::apply_event_impl().
> - */
> - description_event_for_queue->created= 0;
> - /* Don't set log_pos in event header */
> - description_event_for_queue->set_artificial_event();
> + if (event_count >= limit_start &&
> + ev->net_send(protocol, linfo.log_file_name, pos))
> + {
> + errmsg = "Net error";
> + delete ev;
> + mysql_mutex_unlock(log_lock);
> + goto err;
> + }
>
> - if (description_event_for_queue->write(&log_file))
> - goto err;
> - bytes_written+= description_event_for_queue->data_written;
> + pos = my_b_tell(&log);
> + delete ev;
> +
> + if (++event_count >= limit_end)
> + break;
> }
> - if (flush_io_cache(&log_file) ||
> - mysql_file_sync(log_file.file, MYF(MY_WME)))
> - goto err;
>
> - if (write_file_name_to_index_file)
> + if (event_count < limit_end && log.error)
> {
> -#ifdef HAVE_REPLICATION
> - DBUG_EXECUTE_IF("crash_create_critical_before_update_index", DBUG_ABORT(););
> -#endif
> -
> - DBUG_ASSERT(my_b_inited(&index_file) != 0);
> - reinit_io_cache(&index_file, WRITE_CACHE,
> - my_b_filelength(&index_file), 0, 0);
> - /*
> - As this is a new log file, we write the file name to the index
> - file. As every time we write to the index file, we sync it.
> - */
> - if (DBUG_EVALUATE_IF("fault_injection_updating_index", 1, 0) ||
> - my_b_write(&index_file, (uchar*) log_file_name,
> - strlen(log_file_name)) ||
> - my_b_write(&index_file, (uchar*) "\n", 1) ||
> - flush_io_cache(&index_file) ||
> - mysql_file_sync(index_file.file, MYF(MY_WME)))
> - goto err;
> -
> -#ifdef HAVE_REPLICATION
> - DBUG_EXECUTE_IF("crash_create_after_update_index", DBUG_ABORT(););
> -#endif
> + errmsg = "Wrong offset or I/O error";
> + mysql_mutex_unlock(log_lock);
> + goto err;
> }
> - }
> - log_state= LOG_OPENED;
>
> -#ifdef HAVE_REPLICATION
> - close_purge_index_file();
> -#endif
> + mysql_mutex_unlock(log_lock);
> + }
>
> - DBUG_RETURN(0);
> + ret= FALSE;
>
> err:
> -#ifdef HAVE_REPLICATION
> - if (is_inited_purge_index_file())
> - purge_index_entry(NULL, NULL, need_mutex);
> - close_purge_index_file();
> -#endif
> - sql_print_error("Could not use %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.", name, errno);
> + delete description_event;
> if (file >= 0)
> - mysql_file_close(file, MYF(0));
> - end_io_cache(&log_file);
> - end_io_cache(&index_file);
> - safeFree(name);
> - log_state= LOG_CLOSED;
> - DBUG_RETURN(1);
> -}
> -
> + {
> + end_io_cache(&log);
> + mysql_file_close(file, MYF(MY_WME));
> + }
>
> -int MYSQL_BIN_LOG::get_current_log(LOG_INFO* linfo)
> -{
> - mysql_mutex_lock(&LOCK_log);
> - int ret = raw_get_current_log(linfo);
> - mysql_mutex_unlock(&LOCK_log);
> - return ret;
> -}
> + if (errmsg)
> + my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0),
> + "SHOW BINLOG EVENTS", errmsg);
> + else
> + my_eof(thd);
>
> -int MYSQL_BIN_LOG::raw_get_current_log(LOG_INFO* linfo)
> -{
> - strmake(linfo->log_file_name, log_file_name,
> sizeof(linfo->log_file_name)-1);
> - linfo->pos = my_b_tell(&log_file);
> - return 0;
> + mysql_mutex_lock(&LOCK_thread_count);
> + thd->current_linfo = 0;
> + mysql_mutex_unlock(&LOCK_thread_count);
> + DBUG_RETURN(ret);
> }
>
> /**
> - Move all data up in a file in an filename index file.
> -
> - 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.
> -
> - @param index_file File to move
> - @param offset Move everything from here to beginning
> + Execute a SHOW BINLOG EVENTS statement.
>
> - @note
> - File will be truncated to be 'offset' shorter or filled up with newlines
> + @param thd Pointer to THD object for the client thread executing the
> + statement.
>
> - @retval
> - 0 ok
> + @retval FALSE success
> + @retval TRUE failure
> */
> -
> -#ifdef HAVE_REPLICATION
> -
> -static bool copy_up_file_and_fill(IO_CACHE *index_file, my_off_t offset)
> +bool mysql_show_binlog_events(THD* thd)
> {
> - int bytes_read;
> - my_off_t init_offset= offset;
> - File file= index_file->file;
> - uchar io_buf[IO_SIZE*2];
> - DBUG_ENTER("copy_up_file_and_fill");
> + Protocol *protocol= thd->protocol;
> + List<Item> field_list;
> + DBUG_ENTER("mysql_show_binlog_events");
>
> - for (;; offset+= bytes_read)
> - {
> - mysql_file_seek(file, offset, MY_SEEK_SET, MYF(0));
> - if ((bytes_read= (int) mysql_file_read(file, io_buf, sizeof(io_buf),
> - MYF(MY_WME)))
> - < 0)
> - goto err;
> - if (!bytes_read)
> - break; // end of file
> - mysql_file_seek(file, offset-init_offset, MY_SEEK_SET, MYF(0));
> - if (mysql_file_write(file, io_buf, bytes_read, MYF(MY_WME | MY_NABP)))
> - goto err;
> - }
> - /* The following will either truncate the file or fill the end with \n' */
> - if (mysql_file_chsize(file, offset - init_offset, '\n', MYF(MY_WME)) ||
> - mysql_file_sync(file, MYF(MY_WME)))
> - goto err;
> + DBUG_ASSERT(thd->lex->sql_command == SQLCOM_SHOW_BINLOG_EVENTS);
>
> - /* Reset data in old index cache */
> - reinit_io_cache(index_file, READ_CACHE, (my_off_t) 0, 0, 1);
> - DBUG_RETURN(0);
> + Log_event::init_show_field_list(&field_list);
> + if (protocol->send_result_set_metadata(&field_list,
> + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
> + DBUG_RETURN(TRUE);
>
> -err:
> - DBUG_RETURN(1);
> + /*
> + Wait for handlers to insert any pending information
> + into the binlog. For e.g. ndb which updates the binlog asynchronously
> + this is needed so that the uses sees all its own commands in the binlog
> + */
> + ha_binlog_wait(thd);
> +
> + DBUG_RETURN(show_binlog_events(thd, &mysql_bin_log));
> }
>
> #endif /* HAVE_REPLICATION */
>
> -/**
> - Find the position in the log-index-file for the given log name.
> -
> - @param linfo Store here the found log file name and position to
> - the NEXT log file name in the index file.
> - @param log_name Filename to find in the index file.
> - Is a null pointer if we want to read the first entry
> - @param 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.
> -
> - @retval
> - 0 ok
> - @retval
> - LOG_INFO_EOF End of log-index-file found
> - @retval
> - LOG_INFO_IO Got IO error while reading file
> -*/
>
> -int MYSQL_BIN_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name,
> - bool need_lock)
> +MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
> + :bytes_written(0), prepared_xids(0), file_id(1), open_count(1),
> + need_start_event(TRUE), m_table_map_version(0),
> + sync_period_ptr(sync_period),
> + is_relay_log(0), signal_cnt(0),
> + description_event_for_exec(0), description_event_for_queue(0)
> {
> - 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
> + We don't want to initialize locks here as such initialization depends on
> + safe_mutex (when using safe_mutex) which depends on MY_INIT(), which is
> + called only in main(). Doing initialization here would make it happen
> + before main().
> */
> - if (need_lock)
> - mysql_mutex_lock(&LOCK_index);
> - mysql_mutex_assert_owner(&LOCK_index);
> + index_file_name[0] = 0;
> + bzero((char*) &index_file, sizeof(index_file));
> + bzero((char*) &purge_index_file, sizeof(purge_index_file));
> +}
>
> - /* 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);
> +/* this is called only once */
>
> - for (;;)
> +void MYSQL_BIN_LOG::cleanup()
> +{
> + DBUG_ENTER("cleanup");
> + if (inited)
> {
> - uint length;
> - my_off_t offset= my_b_tell(&index_file);
> -
> - DBUG_EXECUTE_IF("simulate_find_log_pos_error",
> - error= LOG_INFO_EOF; break;);
> - /* 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)
> - {
> - /* 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, 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_start_offset= offset;
> - linfo->index_file_offset = my_b_tell(&index_file);
> - break;
> - }
> + inited= 0;
> + close(LOG_CLOSE_INDEX|LOG_CLOSE_STOP_EVENT);
> + delete description_event_for_queue;
> + delete description_event_for_exec;
> + mysql_mutex_destroy(&LOCK_log);
> + mysql_mutex_destroy(&LOCK_index);
> + mysql_cond_destroy(&update_cond);
> }
> -
> - if (need_lock)
> - mysql_mutex_unlock(&LOCK_index);
> - DBUG_RETURN(error);
> + DBUG_VOID_RETURN;
> }
>
>
> -/**
> - Find the position in the log-index-file for the given log name.
> -
> - @param
> - linfo Store here the next log file name and position to
> - the file name after that.
> - @param
> - need_lock Set this to 1 if the parent doesn't already have a
> - lock on LOCK_index
> +/* Init binlog-specific vars */
> +void MYSQL_BIN_LOG::init(bool no_auto_events_arg, ulong max_size_arg)
> +{
> + DBUG_ENTER("MYSQL_BIN_LOG::init");
> + no_auto_events= no_auto_events_arg;
> + max_size= max_size_arg;
> + DBUG_PRINT("info",("max_size: %lu", max_size));
> + DBUG_VOID_RETURN;
> +}
>
> - @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
>
> - @retval
> - 0 ok
> - @retval
> - LOG_INFO_EOF End of log-index-file found
> - @retval
> - LOG_INFO_IO Got IO error while reading file
> -*/
> +void MYSQL_BIN_LOG::init_pthread_objects()
> +{
> + DBUG_ASSERT(inited == 0);
> + inited= 1;
> + mysql_mutex_init(key_LOG_LOCK_log, &LOCK_log, MY_MUTEX_INIT_SLOW);
> + mysql_mutex_init(key_BINLOG_LOCK_index, &LOCK_index, MY_MUTEX_INIT_SLOW);
> + mysql_cond_init(key_BINLOG_update_cond, &update_cond, 0);
> +}
>
> -int MYSQL_BIN_LOG::find_next_log(LOG_INFO* linfo, bool need_lock)
> +
> +bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
> + const char *log_name, bool need_mutex)
> {
> - int error= 0;
> - uint length;
> - char *fname= linfo->log_file_name;
> + File index_file_nr= -1;
> + DBUG_ASSERT(!my_b_inited(&index_file));
>
> - if (need_lock)
> - mysql_mutex_lock(&LOCK_index);
> - mysql_mutex_assert_owner(&LOCK_index);
> + /*
> + 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.
> + */
> + myf opt= MY_UNPACK_FILENAME;
> + if (!index_file_name_arg)
> + {
> + index_file_name_arg= log_name; // Use same basename for index file
> + opt= MY_UNPACK_FILENAME | MY_REPLACE_EXT;
> + }
> + fn_format(index_file_name, index_file_name_arg, mysql_data_home,
> + ".index", opt);
> + if ((index_file_nr= mysql_file_open(key_file_binlog_index,
> + index_file_name,
> + O_RDWR | O_CREAT | O_BINARY,
> + MYF(MY_WME))) < 0 ||
> + mysql_file_sync(index_file_nr, MYF(MY_WME)) ||
> + init_io_cache(&index_file, index_file_nr,
> + IO_SIZE, WRITE_CACHE,
> + mysql_file_seek(index_file_nr, 0L, MY_SEEK_END, MYF(0)),
> + 0, MYF(MY_WME | MY_WAIT_IF_FULL)) ||
> + DBUG_EVALUATE_IF("fault_injection_openning_index", 1, 0))
> + {
> + /*
> + TODO: all operations creating/deleting the index file or a log, should
> + call my_sync_dir() or my_sync_dir_by_file() to be durable.
> + TODO: file creation should be done with mysql_file_create()
> + not mysql_file_open().
> + */
> + if (index_file_nr >= 0)
> + mysql_file_close(index_file_nr, MYF(0));
> + return TRUE;
> + }
>
> - /* 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);
> +#ifdef HAVE_REPLICATION
> + /*
> + Sync the index by purging any binary log file that is not registered.
> + In other words, either purge binary log files that were removed from
> + the index but not purged from the file system due to a crash or purge
> + any binary log file that was created but not register in the index
> + due to a crash.
> + */
>
> - linfo->index_file_start_offset= linfo->index_file_offset;
> - if ((length=my_b_gets(&index_file, fname, FN_REFLEN)) <= 1)
> + if (set_purge_index_file_name(index_file_name_arg) ||
> + open_purge_index_file(FALSE) ||
> + purge_index_entry(NULL, NULL, need_mutex) ||
> + close_purge_index_file() ||
> + DBUG_EVALUATE_IF("fault_injection_recovering_index", 1, 0))
> {
> - error = !index_file.error ? LOG_INFO_EOF : LOG_INFO_IO;
> - goto err;
> + sql_print_error("MYSQL_BIN_LOG::open_index_file failed to sync the index "
> + "file.");
> + return TRUE;
> }
> - fname[length-1]=0; // kill \n
> - linfo->index_file_offset = my_b_tell(&index_file);
> +#endif
>
> -err:
> - if (need_lock)
> - mysql_mutex_unlock(&LOCK_index);
> - return error;
> + return FALSE;
> }
>
>
> /**
> - 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.
> -
> - @param thd Thread
> + Open a (new) binlog file.
>
> - @note
> - If not called from slave thread, write start event to new log
> + - Open the log file and the index file. 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.
>
> @retval
> 0 ok
> @retval
> - 1 error
> + 1 error
> */
>
> -bool MYSQL_BIN_LOG::reset_logs(THD* thd)
> +bool MYSQL_BIN_LOG::open(const char *log_name,
> + enum_log_type log_type_arg,
> + const char *new_name,
> + enum cache_type io_cache_type_arg,
> + bool no_auto_events_arg,
> + ulong max_size_arg,
> + bool null_created_arg,
> + bool need_mutex)
> {
> - LOG_INFO linfo;
> - bool error=0;
> - int err;
> - const char* save_name;
> - DBUG_ENTER("reset_logs");
> -
> - ha_reset_logs(thd);
> - /*
> - We need to get both locks to be sure that no one is trying to
> - write to the index log file.
> - */
> - mysql_mutex_lock(&LOCK_log);
> - mysql_mutex_lock(&LOCK_index);
> + File file= -1;
>
> - /*
> - The following mutex is needed to ensure that no threads call
> - 'delete thd' as we would then risk missing a 'rollback' from this
> - thread. If the transaction involved MyISAM tables, it should go
> - into binlog even on rollback.
> - */
> - mysql_mutex_lock(&LOCK_thread_count);
> + DBUG_ENTER("MYSQL_BIN_LOG::open");
> + DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg));
>
> - /* Save variables so that we can reopen the log */
> - save_name=name;
> - name=0; // Protect against free
> - close(LOG_CLOSE_TO_BE_OPENED);
> + if (init_and_set_log_file_name(log_name, new_name, log_type_arg,
> + io_cache_type_arg))
> + {
> + sql_print_error("MSYQL_BIN_LOG::open failed to generate new file name.");
> + DBUG_RETURN(1);
> + }
>
> - /*
> - First delete all old log files and then update the index file.
> - As we first delete the log files and do not use sort of logging,
> - a crash may lead to an inconsistent state where the index has
> - references to non-existent files.
> +#ifdef HAVE_REPLICATION
> + if (open_purge_index_file(TRUE) ||
> + register_create_index_entry(log_file_name) ||
> + sync_purge_index_file() ||
> + DBUG_EVALUATE_IF("fault_injection_registering_index", 1, 0))
> + {
> + sql_print_error("MSYQL_BIN_LOG::open failed to sync the index file.");
> + DBUG_RETURN(1);
> + }
> + DBUG_EXECUTE_IF("crash_create_non_critical_before_update_index", DBUG_ABORT(););
> +#endif
>
> - We need to invert the steps and use the purge_index_file methods
> - in order to make the operation safe.
> - */
> + write_error= 0;
>
> - if ((err= find_log_pos(&linfo, NullS, 0)) != 0)
> + /* open the main log file */
> + if (MYSQL_LOG::open(log_name, log_type_arg, new_name,
> + io_cache_type_arg))
> {
> - uint errcode= purge_log_get_error_code(err);
> - sql_print_error("Failed to locate old binlog or relay log files");
> - my_message(errcode, ER(errcode), MYF(0));
> - error= 1;
> - goto err;
> +#ifdef HAVE_REPLICATION
> + close_purge_index_file();
> +#endif
> + DBUG_RETURN(1); /* all warnings issued */
> }
>
> - for (;;)
> + init(no_auto_events_arg, max_size_arg);
> +
> + open_count++;
> +
> + DBUG_ASSERT(log_type == LOG_BIN);
> +
> {
> - if ((error= my_delete_allow_opened(linfo.log_file_name, MYF(0))) != 0)
> + bool write_file_name_to_index_file=0;
> +
> + if (!my_b_filelength(&log_file))
> {
> - if (my_errno == ENOENT)
> - {
> - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> - ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
> - linfo.log_file_name);
> - sql_print_information("Failed to delete file '%s'",
> - linfo.log_file_name);
> - my_errno= 0;
> - error= 0;
> - }
> - else
> - {
> - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> - ER_BINLOG_PURGE_FATAL_ERR,
> - "a problem with deleting %s; "
> - "consider examining correspondence "
> - "of your binlog index file "
> - "to the actual binlog files",
> - linfo.log_file_name);
> - error= 1;
> + /*
> + 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_safe_write(&log_file, (uchar*) BINLOG_MAGIC,
> + BIN_LOG_HEADER_SIZE))
> goto err;
> - }
> + bytes_written+= BIN_LOG_HEADER_SIZE;
> + write_file_name_to_index_file= 1;
> }
> - if (find_next_log(&linfo, 0))
> - break;
> - }
>
> - /* Start logging with a new file */
> - close(LOG_CLOSE_INDEX);
> - if ((error= my_delete_allow_opened(index_file_name, MYF(0)))) // Reset (open will
> update)
> - {
> - if (my_errno == ENOENT)
> + if (need_start_event && !no_auto_events)
> {
> - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> - ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
> - index_file_name);
> - sql_print_information("Failed to delete file '%s'",
> - index_file_name);
> - my_errno= 0;
> - error= 0;
> - }
> - else
> + /*
> + In 4.x we set need_start_event=0 here, but in 5.0 we want a Start event
> + even if this is not the very first binlog.
> + */
> + Format_description_log_event s(BINLOG_VERSION);
> + /*
> + don't set LOG_EVENT_BINLOG_IN_USE_F for SEQ_READ_APPEND io_cache
> + as we won't be able to reset it later
> + */
> + if (io_cache_type == WRITE_CACHE)
> + s.flags|= LOG_EVENT_BINLOG_IN_USE_F;
> + if (!s.is_valid())
> + goto err;
> + s.dont_set_created= null_created_arg;
> + if (s.write(&log_file))
> + goto err;
> + bytes_written+= s.data_written;
> + }
> + if (description_event_for_queue &&
> + description_event_for_queue->binlog_version>=4)
> {
> - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> - ER_BINLOG_PURGE_FATAL_ERR,
> - "a problem with deleting %s; "
> - "consider examining correspondence "
> - "of your binlog index file "
> - "to the actual binlog files",
> - index_file_name);
> - error= 1;
> + /*
> + This is a relay log written to by the I/O slave thread.
> + Write the event so that others can later know the format of this relay
> + log.
> + Note that this event is very close to the original event from the
> + master (it has binlog version of the master, event types of the
> + master), so this is suitable to parse the next relay log's event. It
> + has been produced by
> + Format_description_log_event::Format_description_log_event(char* buf,).
> + Why don't we want to write the description_event_for_queue if this
> + event is for format<4 (3.23 or 4.x): this is because in that case, the
> + description_event_for_queue describes the data received from the
> + master, but not the data written to the relay log (*conversion*),
> + which is in format 4 (slave's).
> + */
> + /*
> + Set 'created' to 0, so that in next relay logs this event does not
> + trigger cleaning actions on the slave in
> + Format_description_log_event::apply_event_impl().
> + */
> + description_event_for_queue->created= 0;
> + /* Don't set log_pos in event header */
> + description_event_for_queue->set_artificial_event();
> +
> + if (description_event_for_queue->write(&log_file))
> + goto err;
> + bytes_written+= description_event_for_queue->data_written;
> + }
> + if (flush_io_cache(&log_file) ||
> + mysql_file_sync(log_file.file, MYF(MY_WME)))
> goto err;
> +
> + if (write_file_name_to_index_file)
> + {
> +#ifdef HAVE_REPLICATION
> + DBUG_EXECUTE_IF("crash_create_critical_before_update_index", DBUG_ABORT(););
> +#endif
> +
> + DBUG_ASSERT(my_b_inited(&index_file) != 0);
> + reinit_io_cache(&index_file, WRITE_CACHE,
> + my_b_filelength(&index_file), 0, 0);
> + /*
> + As this is a new log file, we write the file name to the index
> + file. As every time we write to the index file, we sync it.
> + */
> + if (DBUG_EVALUATE_IF("fault_injection_updating_index", 1, 0) ||
> + my_b_write(&index_file, (uchar*) log_file_name,
> + strlen(log_file_name)) ||
> + my_b_write(&index_file, (uchar*) "\n", 1) ||
> + flush_io_cache(&index_file) ||
> + mysql_file_sync(index_file.file, MYF(MY_WME)))
> + goto err;
> +
> +#ifdef HAVE_REPLICATION
> + DBUG_EXECUTE_IF("crash_create_after_update_index", DBUG_ABORT(););
> +#endif
> }
> }
> - 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);
> - my_free((uchar*) save_name, MYF(0));
> + log_state= LOG_OPENED;
> +
> +#ifdef HAVE_REPLICATION
> + close_purge_index_file();
> +#endif
> +
> + DBUG_RETURN(0);
>
> err:
> - if (error == 1)
> - name= const_cast<char*>(save_name);
> - mysql_mutex_unlock(&LOCK_thread_count);
> - mysql_mutex_unlock(&LOCK_index);
> +#ifdef HAVE_REPLICATION
> + if (is_inited_purge_index_file())
> + purge_index_entry(NULL, NULL, need_mutex);
> + close_purge_index_file();
> +#endif
> + sql_print_error("Could not use %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.", name, errno);
> + if (file >= 0)
> + mysql_file_close(file, MYF(0));
> + end_io_cache(&log_file);
> + end_io_cache(&index_file);
> + safeFree(name);
> + log_state= LOG_CLOSED;
> + DBUG_RETURN(1);
> +}
> +
> +
> +int MYSQL_BIN_LOG::get_current_log(LOG_INFO* linfo)
> +{
> + mysql_mutex_lock(&LOCK_log);
> + int ret = raw_get_current_log(linfo);
> mysql_mutex_unlock(&LOCK_log);
> - DBUG_RETURN(error);
> + return ret;
> +}
> +
> +int MYSQL_BIN_LOG::raw_get_current_log(LOG_INFO* linfo)
> +{
> + strmake(linfo->log_file_name, log_file_name,
> sizeof(linfo->log_file_name)-1);
> + linfo->pos = my_b_tell(&log_file);
> + return 0;
> }
>
> +bool MYSQL_BIN_LOG::check_write_error(THD *thd)
> +{
> + DBUG_ENTER("MYSQL_BIN_LOG::check_write_error");
>
> -/**
> - Delete relay log files prior to rli->group_relay_log_name
> - (i.e. all logs which are not involved in a non-finished group
> - (transaction)), remove them from the index file and start on next
> - relay log.
> + bool checked= FALSE;
>
> - IMPLEMENTATION
> - - Protects index file with LOCK_index
> - - Delete relevant relay log files
> - - Copy all file names after these ones to the front of the index file
> - - If the OS has truncate, truncate the file, else fill it with \n'
> - - Read the next file name from the index file and store in rli->linfo
> + if (!thd->is_error())
> + DBUG_RETURN(checked);
>
> - @param rli Relay log information
> - @param included If false, all relay logs that are strictly before
> - rli->group_relay_log_name are deleted ; if true, the
> - latter is deleted too (i.e. all relay logs
> - read by the SQL slave thread are deleted).
> + switch (thd->stmt_da->sql_errno())
> + {
> + case ER_TRANS_CACHE_FULL:
> + case ER_ERROR_ON_WRITE:
> + case ER_BINLOG_LOGGING_IMPOSSIBLE:
> + checked= TRUE;
> + break;
> + }
> +
> + DBUG_RETURN(checked);
> +}
> +
> +void MYSQL_BIN_LOG::set_write_error(THD *thd)
> +{
> + DBUG_ENTER("MYSQL_BIN_LOG::set_write_error");
> +
> + write_error= 1;
> +
> + if (check_write_error(thd))
> + DBUG_VOID_RETURN;
> +
> + if (my_errno == EFBIG)
> + my_message(ER_TRANS_CACHE_FULL, ER(ER_TRANS_CACHE_FULL), MYF(MY_WME));
> + else
> + my_error(ER_ERROR_ON_WRITE, MYF(MY_WME), name, errno);
> +
> + DBUG_VOID_RETURN;
> +}
> +
> +/**
> + Find the position in the log-index-file for the given log name.
> +
> + @param linfo Store here the found log file name and position to
> + the NEXT log file name in the index file.
> + @param log_name Filename to find in the index file.
> + Is a null pointer if we want to read the first entry
> + @param need_lock Set this to 1 if the parent doesn't already have a
> + lock on LOCK_index
>
> @note
> - - This is only called from the slave-execute thread when it has read
> - all commands from a relay log and want to switch to a new relay log.
> - - When this happens, we can be in an active transaction as
> - a transaction can span over two relay logs
> - (although it is always written as a single block to the master's binary
> - log, hence cannot span over two master's binary logs).
> + On systems without the truncate function the file will end with one or
> + more empty lines. These will be ignored when reading the file.
>
> @retval
> 0 ok
> @retval
> LOG_INFO_EOF End of log-index-file found
> @retval
> - LOG_INFO_SEEK Could not allocate IO cache
> - @retval
> LOG_INFO_IO Got IO error while reading file
> */
>
> -#ifdef HAVE_REPLICATION
> -
> -int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
> +int MYSQL_BIN_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name,
> + bool need_lock)
> {
> - int error;
> - char *to_purge_if_included= NULL;
> - DBUG_ENTER("purge_first_log");
> -
> - DBUG_ASSERT(is_open());
> - DBUG_ASSERT(rli->slave_running == 1);
> - DBUG_ASSERT(!strcmp(rli->linfo.log_file_name,rli->event_relay_log_name));
> -
> - mysql_mutex_lock(&LOCK_index);
> - to_purge_if_included= my_strdup(rli->group_relay_log_name, MYF(0));
> + 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"));
>
> /*
> - Read the next log file name from the index file and pass it back to
> - the caller.
> + Mutex needed because we need to make sure the file pointer does not
> + move from under our feet
> */
> - if((error=find_log_pos(&rli->linfo, rli->event_relay_log_name, 0)) ||
> - (error=find_next_log(&rli->linfo, 0)))
> - {
> - char buff[22];
> - sql_print_error("next log error: %d offset: %s log: %s included: %d",
> - error,
> - llstr(rli->linfo.index_file_offset,buff),
> - rli->event_relay_log_name,
> - included);
> - goto err;
> - }
> + if (need_lock)
> + mysql_mutex_lock(&LOCK_index);
> + mysql_mutex_assert_owner(&LOCK_index);
>
> - /*
> - Reset rli's coordinates to the current log.
> - */
> - rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
> - strmake(rli->event_relay_log_name,rli->linfo.log_file_name,
> - sizeof(rli->event_relay_log_name)-1);
> + /* 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);
>
> - /*
> - If we removed the rli->group_relay_log_name file,
> - we must update the rli->group* coordinates, otherwise do not touch it as the
> - group's execution is not finished (e.g. COMMIT not executed)
> - */
> - if (included)
> + for (;;)
> {
> - rli->group_relay_log_pos = BIN_LOG_HEADER_SIZE;
> - strmake(rli->group_relay_log_name,rli->linfo.log_file_name,
> - sizeof(rli->group_relay_log_name)-1);
> - rli->notify_group_relay_log_name_update();
> - }
> -
> - /* Store where we are in the new file for the execution thread */
> - flush_relay_log_info(rli);
> -
> - DBUG_EXECUTE_IF("crash_before_purge_logs", DBUG_ABORT(););
> -
> - mysql_mutex_lock(&rli->log_space_lock);
> - rli->relay_log.purge_logs(to_purge_if_included, included,
> - 0, 0, &rli->log_space_total);
> - // Tell the I/O thread to take the relay_log_space_limit into account
> - rli->ignore_log_space_limit= 0;
> - mysql_mutex_unlock(&rli->log_space_lock);
> + uint length;
> + my_off_t offset= my_b_tell(&index_file);
>
> - /*
> - 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
> - */
> - mysql_cond_broadcast(&rli->log_space_cond);
> + DBUG_EXECUTE_IF("simulate_find_log_pos_error",
> + error= LOG_INFO_EOF; break;);
> + /* 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)
> + {
> + /* Did not find the given entry; Return not found or error */
> + error= !index_file.error ? LOG_INFO_EOF : LOG_INFO_IO;
> + break;
> + }
>
> - /*
> - * Need to update the log pos because purge logs has been called
> - * after fetching initially the log pos at the begining of the method.
> - */
> - if((error=find_log_pos(&rli->linfo, rli->event_relay_log_name, 0)))
> - {
> - char buff[22];
> - sql_print_error("next log error: %d offset: %s log: %s included: %d",
> - error,
> - llstr(rli->linfo.index_file_offset,buff),
> - rli->group_relay_log_name,
> - included);
> - goto err;
> + // 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_start_offset= offset;
> + linfo->index_file_offset = my_b_tell(&index_file);
> + break;
> + }
> }
>
> - /* If included was passed, rli->linfo should be the first entry. */
> - DBUG_ASSERT(!included || rli->linfo.index_file_start_offset == 0);
> -
> -err:
> - my_free(to_purge_if_included, MYF(0));
> - mysql_mutex_unlock(&LOCK_index);
> + if (need_lock)
> + mysql_mutex_unlock(&LOCK_index);
> DBUG_RETURN(error);
> }
>
> -/**
> - Update log index_file.
> -*/
> -
> -int MYSQL_BIN_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads)
> -{
> - if (copy_up_file_and_fill(&index_file, log_info->index_file_start_offset))
> - return LOG_INFO_IO;
> -
> - // now update offsets in index file for running threads
> - if (need_update_threads)
> - adjust_linfo_offsets(log_info->index_file_start_offset);
> - return 0;
> -}
>
> /**
> - Remove all logs before the given log from disk and from the index file.
> + Find the position in the log-index-file for the given log name.
>
> - @param to_log Delete all log file name before this file.
> - @param included If true, to_log is deleted too.
> - @param need_mutex
> - @param need_update_threads If we want to update the log coordinates of
> - all threads. False for relay logs, true otherwise.
> - @param freed_log_space If not null, decrement this variable of
> - the amount of log space freed
> + @param
> + linfo Store here the next log file name and position to
> + the file name after that.
> + @param
> + need_lock Set this to 1 if the parent doesn't already have a
> + lock on LOCK_index
>
> @note
> - If any of the logs before the deleted one is in use,
> - only purge logs up to this one.
> + - 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
>
> @retval
> 0 ok
> @retval
> - LOG_INFO_EOF to_log not found
> - LOG_INFO_EMFILE too many files opened
> - LOG_INFO_FATAL if any other than ENOENT error from
> - mysql_file_stat() or mysql_file_delete()
> + LOG_INFO_EOF End of log-index-file found
> + @retval
> + LOG_INFO_IO Got IO error while reading file
> */
>
> -int MYSQL_BIN_LOG::purge_logs(const char *to_log,
> - bool included,
> - bool need_mutex,
> - bool need_update_threads,
> - ulonglong *decrease_log_space)
> +int MYSQL_BIN_LOG::find_next_log(LOG_INFO* linfo, bool need_lock)
> {
> int error= 0;
> - bool exit_loop= 0;
> - LOG_INFO log_info;
> - THD *thd= current_thd;
> - DBUG_ENTER("purge_logs");
> - DBUG_PRINT("info",("to_log= %s",to_log));
> + uint length;
> + char *fname= linfo->log_file_name;
>
> - if (need_mutex)
> + if (need_lock)
> mysql_mutex_lock(&LOCK_index);
> - if ((error=find_log_pos(&log_info, to_log, 0 /*no mutex*/)))
> - {
> - sql_print_error("MYSQL_BIN_LOG::purge_logs was called with file %s not "
> - "listed in the index.", to_log);
> - goto err;
> - }
> -
> - if ((error= open_purge_index_file(TRUE)))
> - {
> - sql_print_error("MYSQL_BIN_LOG::purge_logs failed to sync the index file.");
> - 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) || (exit_loop=included)) &&
> - !is_active(log_info.log_file_name) &&
> - !log_in_use(log_info.log_file_name))
> - {
> - if ((error= register_purge_index_entry(log_info.log_file_name)))
> - {
> - sql_print_error("MYSQL_BIN_LOG::purge_logs failed to copy %s to register
> file.",
> - log_info.log_file_name);
> - goto err;
> - }
> -
> - if (find_next_log(&log_info, 0) || exit_loop)
> - break;
> - }
> -
> - DBUG_EXECUTE_IF("crash_purge_before_update_index", DBUG_ABORT(););
> + mysql_mutex_assert_owner(&LOCK_index);
>
> - if ((error= sync_purge_index_file()))
> - {
> - sql_print_error("MSYQL_BIN_LOG::purge_logs failed to flush register file.");
> - goto err;
> - }
> + /* 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);
>
> - /* We know how many files to delete. Update index file. */
> - if ((error=update_log_index(&log_info, need_update_threads)))
> + linfo->index_file_start_offset= linfo->index_file_offset;
> + if ((length=my_b_gets(&index_file, fname, FN_REFLEN)) <= 1)
> {
> - sql_print_error("MSYQL_BIN_LOG::purge_logs failed to update the index file");
> + error = !index_file.error ? LOG_INFO_EOF : LOG_INFO_IO;
> goto err;
> }
> -
> - DBUG_EXECUTE_IF("crash_purge_critical_after_update_index", DBUG_ABORT(););
> + fname[length-1]=0; // kill \n
> + linfo->index_file_offset = my_b_tell(&index_file);
>
> err:
> - /* Read each entry from purge_index_file and delete the file. */
> - if (is_inited_purge_index_file() &&
> - (error= purge_index_entry(thd, decrease_log_space, FALSE)))
> - sql_print_error("MSYQL_BIN_LOG::purge_logs failed to process registered files"
> - " that would be purged.");
> - close_purge_index_file();
> -
> - DBUG_EXECUTE_IF("crash_purge_non_critical_after_update_index", DBUG_ABORT(););
> -
> - if (need_mutex)
> + if (need_lock)
> mysql_mutex_unlock(&LOCK_index);
> - DBUG_RETURN(error);
> + return error;
> }
>
> -int MYSQL_BIN_LOG::set_purge_index_file_name(const char *base_file_name)
> -{
> - int error= 0;
> - DBUG_ENTER("MYSQL_BIN_LOG::set_purge_index_file_name");
> - if (fn_format(purge_index_file_name, base_file_name, mysql_data_home,
> - ".~rec~", MYF(MY_UNPACK_FILENAME | MY_SAFE_PATH |
> - MY_REPLACE_EXT)) == NULL)
> - {
> - error= 1;
> - sql_print_error("MYSQL_BIN_LOG::set_purge_index_file_name failed to set "
> - "file name.");
> - }
> - DBUG_RETURN(error);
> -}
>
> -int MYSQL_BIN_LOG::open_purge_index_file(bool destroy)
> -{
> - int error= 0;
> - File file= -1;
> +/**
> + Delete all logs refered to in the index file.
> + Start writing to a new log file.
>
> - DBUG_ENTER("MYSQL_BIN_LOG::open_purge_index_file");
> + The new index file will only contain this file.
>
> - if (destroy)
> - close_purge_index_file();
> + @param thd Thread
>
> - if (!my_b_inited(&purge_index_file))
> - {
> - if ((file= my_open(purge_index_file_name, O_RDWR | O_CREAT | O_BINARY,
> - MYF(MY_WME | ME_WAITTANG))) < 0 ||
> - init_io_cache(&purge_index_file, file, IO_SIZE,
> - (destroy ? WRITE_CACHE : READ_CACHE),
> - 0, 0, MYF(MY_WME | MY_NABP | MY_WAIT_IF_FULL)))
> - {
> - error= 1;
> - sql_print_error("MYSQL_BIN_LOG::open_purge_index_file failed to open register
> "
> - " file.");
> - }
> - }
> - DBUG_RETURN(error);
> -}
> + @note
> + If not called from slave thread, write start event to new log
>
> -int MYSQL_BIN_LOG::close_purge_index_file()
> + @retval
> + 0 ok
> + @retval
> + 1 error
> +*/
> +
> +bool MYSQL_BIN_LOG::reset_logs(THD* thd)
> {
> - int error= 0;
> + LOG_INFO linfo;
> + bool error=0;
> + int err;
> + const char* save_name;
> + DBUG_ENTER("reset_logs");
>
> - DBUG_ENTER("MYSQL_BIN_LOG::close_purge_index_file");
> + ha_reset_logs(thd);
> + /*
> + We need to get both locks to be sure that no one is trying to
> + write to the index log file.
> + */
> + mysql_mutex_lock(&LOCK_log);
> + mysql_mutex_lock(&LOCK_index);
>
> - if (my_b_inited(&purge_index_file))
> - {
> + /*
> + The following mutex is needed to ensure that no threads call
> + 'delete thd' as we would then risk missing a 'rollback' from this
> + thread. If the transaction involved MyISAM tables, it should go
> + into binlog even on rollback.
> + */
> + mysql_mutex_lock(&LOCK_thread_count);
> +
> + /* Save variables so that we can reopen the log */
> + save_name=name;
> + name=0; // Protect against free
> + close(LOG_CLOSE_TO_BE_OPENED);
> +
> + /*
> + First delete all old log files and then update the index file.
> + As we first delete the log files and do not use sort of logging,
> + a crash may lead to an inconsistent state where the index has
> + references to non-existent files.
> +
> + We need to invert the steps and use the purge_index_file methods
> + in order to make the operation safe.
> + */
> +
> + if ((err= find_log_pos(&linfo, NullS, 0)) != 0)
> + {
> + uint errcode= purge_log_get_error_code(err);
> + sql_print_error("Failed to locate old binlog or relay log files");
> + my_message(errcode, ER(errcode), MYF(0));
> + error= 1;
> + goto err;
> + }
> +
> + for (;;)
> + {
> + if ((error= my_delete_allow_opened(linfo.log_file_name, MYF(0))) != 0)
> + {
> + if (my_errno == ENOENT)
> + {
> + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> + ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
> + linfo.log_file_name);
> + sql_print_information("Failed to delete file '%s'",
> + linfo.log_file_name);
> + my_errno= 0;
> + error= 0;
> + }
> + else
> + {
> + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> + ER_BINLOG_PURGE_FATAL_ERR,
> + "a problem with deleting %s; "
> + "consider examining correspondence "
> + "of your binlog index file "
> + "to the actual binlog files",
> + linfo.log_file_name);
> + error= 1;
> + goto err;
> + }
> + }
> + if (find_next_log(&linfo, 0))
> + break;
> + }
> +
> + /* Start logging with a new file */
> + close(LOG_CLOSE_INDEX);
> + if ((error= my_delete_allow_opened(index_file_name, MYF(0)))) // Reset (open will
> update)
> + {
> + if (my_errno == ENOENT)
> + {
> + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> + ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
> + index_file_name);
> + sql_print_information("Failed to delete file '%s'",
> + index_file_name);
> + my_errno= 0;
> + error= 0;
> + }
> + else
> + {
> + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> + ER_BINLOG_PURGE_FATAL_ERR,
> + "a problem with deleting %s; "
> + "consider examining correspondence "
> + "of your binlog index file "
> + "to the actual binlog files",
> + index_file_name);
> + error= 1;
> + goto err;
> + }
> + }
> + 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);
> + my_free((uchar*) save_name, MYF(0));
> +
> +err:
> + if (error == 1)
> + name= const_cast<char*>(save_name);
> + mysql_mutex_unlock(&LOCK_thread_count);
> + mysql_mutex_unlock(&LOCK_index);
> + mysql_mutex_unlock(&LOCK_log);
> + DBUG_RETURN(error);
> +}
> +
> +
> +/**
> + Delete relay log files prior to rli->group_relay_log_name
> + (i.e. all logs which are not involved in a non-finished group
> + (transaction)), remove them from the index file and start on next
> + relay log.
> +
> + IMPLEMENTATION
> + - Protects index file with LOCK_index
> + - Delete relevant relay log files
> + - Copy all file names after these ones to the front of the index file
> + - If the OS has truncate, truncate the file, else fill it with \n'
> + - Read the next file name from the index file and store in rli->linfo
> +
> + @param rli Relay log information
> + @param included If false, all relay logs that are strictly before
> + rli->group_relay_log_name are deleted ; if true, the
> + latter is deleted too (i.e. all relay logs
> + read by the SQL slave thread are deleted).
> +
> + @note
> + - This is only called from the slave-execute thread when it has read
> + all commands from a relay log and want to switch to a new relay log.
> + - When this happens, we can be in an active transaction as
> + a transaction can span over two relay logs
> + (although it is always written as a single block to the master's binary
> + log, hence cannot span over two master's binary logs).
> +
> + @retval
> + 0 ok
> + @retval
> + LOG_INFO_EOF End of log-index-file found
> + @retval
> + LOG_INFO_SEEK Could not allocate IO cache
> + @retval
> + LOG_INFO_IO Got IO error while reading file
> +*/
> +
> +#ifdef HAVE_REPLICATION
> +
> +int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
> +{
> + int error;
> + char *to_purge_if_included= NULL;
> + DBUG_ENTER("purge_first_log");
> +
> + DBUG_ASSERT(is_open());
> + DBUG_ASSERT(rli->slave_running == 1);
> + DBUG_ASSERT(!strcmp(rli->linfo.log_file_name,rli->event_relay_log_name));
> +
> + mysql_mutex_lock(&LOCK_index);
> + to_purge_if_included= my_strdup(rli->group_relay_log_name, MYF(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, rli->event_relay_log_name, 0)) ||
> + (error=find_next_log(&rli->linfo, 0)))
> + {
> + char buff[22];
> + sql_print_error("next log error: %d offset: %s log: %s included: %d",
> + error,
> + llstr(rli->linfo.index_file_offset,buff),
> + rli->event_relay_log_name,
> + included);
> + goto err;
> + }
> +
> + /*
> + Reset rli's coordinates to the current log.
> + */
> + rli->event_relay_log_pos= BIN_LOG_HEADER_SIZE;
> + strmake(rli->event_relay_log_name,rli->linfo.log_file_name,
> + sizeof(rli->event_relay_log_name)-1);
> +
> + /*
> + If we removed the rli->group_relay_log_name file,
> + we must update the rli->group* coordinates, otherwise do not touch it as the
> + group's execution is not finished (e.g. COMMIT not executed)
> + */
> + if (included)
> + {
> + rli->group_relay_log_pos = BIN_LOG_HEADER_SIZE;
> + strmake(rli->group_relay_log_name,rli->linfo.log_file_name,
> + sizeof(rli->group_relay_log_name)-1);
> + rli->notify_group_relay_log_name_update();
> + }
> +
> + /* Store where we are in the new file for the execution thread */
> + flush_relay_log_info(rli);
> +
> + DBUG_EXECUTE_IF("crash_before_purge_logs", DBUG_ABORT(););
> +
> + mysql_mutex_lock(&rli->log_space_lock);
> + rli->relay_log.purge_logs(to_purge_if_included, included,
> + 0, 0, &rli->log_space_total);
> + // Tell the I/O thread to take the relay_log_space_limit into account
> + rli->ignore_log_space_limit= 0;
> + mysql_mutex_unlock(&rli->log_space_lock);
> +
> + /*
> + 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
> + */
> + mysql_cond_broadcast(&rli->log_space_cond);
> +
> + /*
> + * Need to update the log pos because purge logs has been called
> + * after fetching initially the log pos at the begining of the method.
> + */
> + if((error=find_log_pos(&rli->linfo, rli->event_relay_log_name, 0)))
> + {
> + char buff[22];
> + sql_print_error("next log error: %d offset: %s log: %s included: %d",
> + error,
> + llstr(rli->linfo.index_file_offset,buff),
> + rli->group_relay_log_name,
> + included);
> + goto err;
> + }
> +
> + /* If included was passed, rli->linfo should be the first entry. */
> + DBUG_ASSERT(!included || rli->linfo.index_file_start_offset == 0);
> +
> +err:
> + my_free(to_purge_if_included, MYF(0));
> + mysql_mutex_unlock(&LOCK_index);
> + DBUG_RETURN(error);
> +}
> +
> +/**
> + Update log index_file.
> +*/
> +
> +int MYSQL_BIN_LOG::update_log_index(LOG_INFO* log_info, bool need_update_threads)
> +{
> + if (copy_up_file_and_fill(&index_file, log_info->index_file_start_offset))
> + return LOG_INFO_IO;
> +
> + // now update offsets in index file for running threads
> + if (need_update_threads)
> + adjust_linfo_offsets(log_info->index_file_start_offset);
> + return 0;
> +}
> +
> +/**
> + Remove all logs before the given log from disk and from the index file.
> +
> + @param to_log Delete all log file name before this file.
> + @param included If true, to_log is deleted too.
> + @param need_mutex
> + @param need_update_threads If we want to update the log coordinates of
> + all threads. False for relay logs, true otherwise.
> + @param freed_log_space If not null, decrement this variable of
> + the amount of log space freed
> +
> + @note
> + If any of the logs before the deleted one is in use,
> + only purge logs up to this one.
> +
> + @retval
> + 0 ok
> + @retval
> + LOG_INFO_EOF to_log not found
> + LOG_INFO_EMFILE too many files opened
> + LOG_INFO_FATAL if any other than ENOENT error from
> + mysql_file_stat() or mysql_file_delete()
> +*/
> +
> +int MYSQL_BIN_LOG::purge_logs(const char *to_log,
> + bool included,
> + bool need_mutex,
> + bool need_update_threads,
> + ulonglong *decrease_log_space)
> +{
> + int error= 0;
> + bool exit_loop= 0;
> + LOG_INFO log_info;
> + THD *thd= current_thd;
> + DBUG_ENTER("purge_logs");
> + DBUG_PRINT("info",("to_log= %s",to_log));
> +
> + if (need_mutex)
> + mysql_mutex_lock(&LOCK_index);
> + if ((error=find_log_pos(&log_info, to_log, 0 /*no mutex*/)))
> + {
> + sql_print_error("MYSQL_BIN_LOG::purge_logs was called with file %s not "
> + "listed in the index.", to_log);
> + goto err;
> + }
> +
> + if ((error= open_purge_index_file(TRUE)))
> + {
> + sql_print_error("MYSQL_BIN_LOG::purge_logs failed to sync the index file.");
> + 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) || (exit_loop=included)) &&
> + !is_active(log_info.log_file_name) &&
> + !log_in_use(log_info.log_file_name))
> + {
> + if ((error= register_purge_index_entry(log_info.log_file_name)))
> + {
> + sql_print_error("MYSQL_BIN_LOG::purge_logs failed to copy %s to register
> file.",
> + log_info.log_file_name);
> + goto err;
> + }
> +
> + if (find_next_log(&log_info, 0) || exit_loop)
> + break;
> + }
> +
> + DBUG_EXECUTE_IF("crash_purge_before_update_index", DBUG_ABORT(););
> +
> + if ((error= sync_purge_index_file()))
> + {
> + sql_print_error("MSYQL_BIN_LOG::purge_logs failed to flush register file.");
> + goto err;
> + }
> +
> + /* We know how many files to delete. Update index file. */
> + if ((error=update_log_index(&log_info, need_update_threads)))
> + {
> + sql_print_error("MSYQL_BIN_LOG::purge_logs failed to update the index file");
> + goto err;
> + }
> +
> + DBUG_EXECUTE_IF("crash_purge_critical_after_update_index", DBUG_ABORT(););
> +
> +err:
> + /* Read each entry from purge_index_file and delete the file. */
> + if (is_inited_purge_index_file() &&
> + (error= purge_index_entry(thd, decrease_log_space, FALSE)))
> + sql_print_error("MSYQL_BIN_LOG::purge_logs failed to process registered files"
> + " that would be purged.");
> + close_purge_index_file();
> +
> + DBUG_EXECUTE_IF("crash_purge_non_critical_after_update_index", DBUG_ABORT(););
> +
> + if (need_mutex)
> + mysql_mutex_unlock(&LOCK_index);
> + DBUG_RETURN(error);
> +}
> +
> +int MYSQL_BIN_LOG::set_purge_index_file_name(const char *base_file_name)
> +{
> + int error= 0;
> + DBUG_ENTER("MYSQL_BIN_LOG::set_purge_index_file_name");
> + if (fn_format(purge_index_file_name, base_file_name, mysql_data_home,
> + ".~rec~", MYF(MY_UNPACK_FILENAME | MY_SAFE_PATH |
> + MY_REPLACE_EXT)) == NULL)
> + {
> + error= 1;
> + sql_print_error("MYSQL_BIN_LOG::set_purge_index_file_name failed to set "
> + "file name.");
> + }
> + DBUG_RETURN(error);
> +}
> +
> +int MYSQL_BIN_LOG::open_purge_index_file(bool destroy)
> +{
> + int error= 0;
> + File file= -1;
> +
> + DBUG_ENTER("MYSQL_BIN_LOG::open_purge_index_file");
> +
> + if (destroy)
> + close_purge_index_file();
> +
> + if (!my_b_inited(&purge_index_file))
> + {
> + if ((file= my_open(purge_index_file_name, O_RDWR | O_CREAT | O_BINARY,
> + MYF(MY_WME | ME_WAITTANG))) < 0 ||
> + init_io_cache(&purge_index_file, file, IO_SIZE,
> + (destroy ? WRITE_CACHE : READ_CACHE),
> + 0, 0, MYF(MY_WME | MY_NABP | MY_WAIT_IF_FULL)))
> + {
> + error= 1;
> + sql_print_error("MYSQL_BIN_LOG::open_purge_index_file failed to open register
> "
> + " file.");
> + }
> + }
> + DBUG_RETURN(error);
> +}
> +
> +int MYSQL_BIN_LOG::close_purge_index_file()
> +{
> + int error= 0;
> +
> + DBUG_ENTER("MYSQL_BIN_LOG::close_purge_index_file");
> +
> + if (my_b_inited(&purge_index_file))
> + {
> end_io_cache(&purge_index_file);
> error= my_close(purge_index_file.file, MYF(0));
> }
> - my_delete(purge_index_file_name, MYF(0));
> - bzero((char*) &purge_index_file, sizeof(purge_index_file));
> + my_delete(purge_index_file_name, MYF(0));
> + bzero((char*) &purge_index_file, sizeof(purge_index_file));
> +
> + DBUG_RETURN(error);
> +}
> +
> +bool MYSQL_BIN_LOG::is_inited_purge_index_file()
> +{
> + DBUG_ENTER("MYSQL_BIN_LOG::is_inited_purge_index_file");
> + DBUG_RETURN (my_b_inited(&purge_index_file));
> +}
> +
> +int MYSQL_BIN_LOG::sync_purge_index_file()
> +{
> + int error= 0;
> + DBUG_ENTER("MYSQL_BIN_LOG::sync_purge_index_file");
> +
> + if ((error= flush_io_cache(&purge_index_file)) ||
> + (error= my_sync(purge_index_file.file, MYF(MY_WME))))
> + DBUG_RETURN(error);
> +
> + DBUG_RETURN(error);
> +}
> +
> +int MYSQL_BIN_LOG::register_purge_index_entry(const char *entry)
> +{
> + int error= 0;
> + DBUG_ENTER("MYSQL_BIN_LOG::register_purge_index_entry");
> +
> + if ((error=my_b_write(&purge_index_file, (const uchar*)entry, strlen(entry)))
> ||
> + (error=my_b_write(&purge_index_file, (const uchar*)"\n", 1)))
> + DBUG_RETURN (error);
> +
> + DBUG_RETURN(error);
> +}
> +
> +int MYSQL_BIN_LOG::register_create_index_entry(const char *entry)
> +{
> + DBUG_ENTER("MYSQL_BIN_LOG::register_create_index_entry");
> + DBUG_RETURN(register_purge_index_entry(entry));
> +}
> +
> +int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *decrease_log_space,
> + bool need_mutex)
> +{
> + MY_STAT s;
> + int error= 0;
> + LOG_INFO log_info;
> + LOG_INFO check_log_info;
> +
> + DBUG_ENTER("MYSQL_BIN_LOG:purge_index_entry");
> +
> + DBUG_ASSERT(my_b_inited(&purge_index_file));
> +
> + if ((error=reinit_io_cache(&purge_index_file, READ_CACHE, 0, 0, 0)))
> + {
> + sql_print_error("MSYQL_BIN_LOG::purge_index_entry failed to reinit register file
> "
> + "for read");
> + goto err;
> + }
> +
> + for (;;)
> + {
> + uint length;
> +
> + if ((length=my_b_gets(&purge_index_file, log_info.log_file_name,
> + FN_REFLEN)) <= 1)
> + {
> + if (purge_index_file.error)
> + {
> + error= purge_index_file.error;
> + sql_print_error("MSYQL_BIN_LOG::purge_index_entry error %d reading from "
> + "register file.", error);
> + goto err;
> + }
> +
> + /* Reached EOF */
> + break;
> + }
> +
> + /* Get rid of the trailing '\n' */
> + log_info.log_file_name[length-1]= 0;
> +
> + if (!mysql_file_stat(key_file_binlog, log_info.log_file_name, &s, MYF(0)))
> + {
> + if (my_errno == ENOENT)
> + {
> + /*
> + It's not fatal if we can't stat a log file that does not exist;
> + If we could not stat, we won't delete.
> + */
> + if (thd)
> + {
> + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> + ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
> + log_info.log_file_name);
> + }
> + sql_print_information("Failed to execute mysql_file_stat on file '%s'",
> + log_info.log_file_name);
> + my_errno= 0;
> + }
> + else
> + {
> + /*
> + Other than ENOENT are fatal
> + */
> + if (thd)
> + {
> + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> + ER_BINLOG_PURGE_FATAL_ERR,
> + "a problem with getting info on being purged %s; "
> + "consider examining correspondence "
> + "of your binlog index file "
> + "to the actual binlog files",
> + log_info.log_file_name);
> + }
> + else
> + {
> + sql_print_information("Failed to delete log file '%s'; "
> + "consider examining correspondence "
> + "of your binlog index file "
> + "to the actual binlog files",
> + log_info.log_file_name);
> + }
> + error= LOG_INFO_FATAL;
> + goto err;
> + }
> + }
> + else
> + {
> + if ((error= find_log_pos(&check_log_info, log_info.log_file_name,
> need_mutex)))
> + {
> + if (error != LOG_INFO_EOF)
> + {
> + if (thd)
> + {
> + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> + ER_BINLOG_PURGE_FATAL_ERR,
> + "a problem with deleting %s and "
> + "reading the binlog index file",
> + log_info.log_file_name);
> + }
> + else
> + {
> + sql_print_information("Failed to delete file '%s' and "
> + "read the binlog index file",
> + log_info.log_file_name);
> + }
> + goto err;
> + }
> +
> + error= 0;
> + if (!need_mutex)
> + {
> + /*
> + This is to avoid triggering an error in NDB.
> + */
> + ha_binlog_index_purge_file(current_thd, log_info.log_file_name);
> + }
> +
> + DBUG_PRINT("info",("purging %s",log_info.log_file_name));
> + if (!my_delete(log_info.log_file_name, MYF(0)))
> + {
> + if (decrease_log_space)
> + *decrease_log_space-= s.st_size;
> + }
> + else
> + {
> + if (my_errno == ENOENT)
> + {
> + if (thd)
> + {
> + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> + ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
> + log_info.log_file_name);
> + }
> + sql_print_information("Failed to delete file '%s'",
> + log_info.log_file_name);
> + my_errno= 0;
> + }
> + else
> + {
> + if (thd)
> + {
> + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> + ER_BINLOG_PURGE_FATAL_ERR,
> + "a problem with deleting %s; "
> + "consider examining correspondence "
> + "of your binlog index file "
> + "to the actual binlog files",
> + log_info.log_file_name);
> + }
> + else
> + {
> + sql_print_information("Failed to delete file '%s'; "
> + "consider examining correspondence "
> + "of your binlog index file "
> + "to the actual binlog files",
> + log_info.log_file_name);
> + }
> + if (my_errno == EMFILE)
> + {
> + DBUG_PRINT("info",
> + ("my_errno: %d, set ret = LOG_INFO_EMFILE", my_errno));
> + error= LOG_INFO_EMFILE;
> + goto err;
> + }
> + error= LOG_INFO_FATAL;
> + goto err;
> + }
> + }
> + }
> + }
> + }
> +
> +err:
> + DBUG_RETURN(error);
> +}
> +
> +/**
> + Remove all logs before the given file date from disk and from the
> + index file.
> +
> + @param thd Thread pointer
> + @param purge_time Delete all log files before given date.
> +
> + @note
> + If any of the logs before the deleted one is in use,
> + only purge logs up to this one.
> +
> + @retval
> + 0 ok
> + @retval
> + LOG_INFO_PURGE_NO_ROTATE Binary file that can't be rotated
> + LOG_INFO_FATAL if any other than ENOENT error from
> + mysql_file_stat() or mysql_file_delete()
> +*/
> +
> +int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time)
> +{
> + int error;
> + char to_log[FN_REFLEN];
> + LOG_INFO log_info;
> + MY_STAT stat_area;
> + THD *thd= current_thd;
> +
> + DBUG_ENTER("purge_logs_before_date");
> +
> + mysql_mutex_lock(&LOCK_index);
> + to_log[0]= 0;
> +
> + if ((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/)))
> + goto err;
> +
> + while (strcmp(log_file_name, log_info.log_file_name) &&
> + !is_active(log_info.log_file_name) &&
> + !log_in_use(log_info.log_file_name))
> + {
> + if (!mysql_file_stat(key_file_binlog,
> + log_info.log_file_name, &stat_area, MYF(0)))
> + {
> + if (my_errno == ENOENT)
> + {
> + /*
> + It's not fatal if we can't stat a log file that does not exist.
> + */
> + my_errno= 0;
> + }
> + else
> + {
> + /*
> + Other than ENOENT are fatal
> + */
> + if (thd)
> + {
> + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> + ER_BINLOG_PURGE_FATAL_ERR,
> + "a problem with getting info on being purged %s; "
> + "consider examining correspondence "
> + "of your binlog index file "
> + "to the actual binlog files",
> + log_info.log_file_name);
> + }
> + else
> + {
> + sql_print_information("Failed to delete log file '%s'",
> + log_info.log_file_name);
> + }
> + error= LOG_INFO_FATAL;
> + goto err;
> + }
> + }
> + else
> + {
> + if (stat_area.st_mtime < purge_time)
> + strmake(to_log,
> + log_info.log_file_name,
> + sizeof(log_info.log_file_name) - 1);
> + else
> + break;
> + }
> + if (find_next_log(&log_info, 0))
> + break;
> + }
> +
> + error= (to_log[0] ? purge_logs(to_log, 1, 0, 1, (ulonglong *) 0) : 0);
> +
> +err:
> + mysql_mutex_unlock(&LOCK_index);
> + DBUG_RETURN(error);
> +}
> +#endif /* HAVE_REPLICATION */
> +
> +
> +/**
> + Create a new log file name.
> +
> + @param 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_BIN_LOG::make_log_name(char* buf, const char* log_ident)
> +{
> + 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 -1);
> +}
> +
> +
> +/**
> + Check if we are writing/reading to the given log file.
> +*/
> +
> +bool MYSQL_BIN_LOG::is_active(const char *log_file_name_arg)
> +{
> + return !strcmp(log_file_name, log_file_name_arg);
> +}
> +
> +
> +/*
> + Wrappers around new_file_impl to avoid using argument
> + to control locking. The argument 1) less readable 2) breaks
> + incapsulation 3) allows external access to the class without
> + a lock (which is not possible with private new_file_without_locking
> + method).
> +*/
> +
> +void MYSQL_BIN_LOG::new_file()
> +{
> + new_file_impl(1);
> +}
> +
> +
> +void MYSQL_BIN_LOG::new_file_without_locking()
> +{
> + new_file_impl(0);
> +}
> +
> +
> +/**
> + Start writing to a new log file or reopen the old file.
> +
> + @param need_lock Set to 1 if caller has not locked LOCK_log
> +
> + @note
> + The new file name is stored last in the index file
> +*/
> +
> +void MYSQL_BIN_LOG::new_file_impl(bool need_lock)
> +{
> + char new_name[FN_REFLEN], *new_name_ptr, *old_name;
> +
> + DBUG_ENTER("MYSQL_BIN_LOG::new_file_impl");
> + if (!is_open())
> + {
> + DBUG_PRINT("info",("log is closed"));
> + DBUG_VOID_RETURN;
> + }
> +
> + if (need_lock)
> + mysql_mutex_lock(&LOCK_log);
> + mysql_mutex_lock(&LOCK_index);
> +
> + mysql_mutex_assert_owner(&LOCK_log);
> + mysql_mutex_assert_owner(&LOCK_index);
> +
> + /*
> + if binlog is used as tc log, be sure all xids are "unlogged",
> + so that on recover we only need to scan one - latest - binlog file
> + for prepared xids. As this is expected to be a rare event,
> + simple wait strategy is enough. We're locking LOCK_log to be sure no
> + new Xid_log_event's are added to the log (and prepared_xids is not
> + increased), and waiting on COND_prep_xids for late threads to
> + catch up.
> + */
> + if (prepared_xids)
> + {
> + tc_log_page_waits++;
> + mysql_mutex_lock(&LOCK_prep_xids);
> + while (prepared_xids) {
> + DBUG_PRINT("info", ("prepared_xids=%lu", prepared_xids));
> + mysql_cond_wait(&COND_prep_xids, &LOCK_prep_xids);
> + }
> + mysql_mutex_unlock(&LOCK_prep_xids);
> + }
> +
> + /* Reuse old name if not binlog and not update log */
> + new_name_ptr= name;
> +
> + /*
> + 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))
> + 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.
> + */
> + 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);
> + bytes_written += r.data_written;
> + }
> + /*
> + 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.
> + */
> + signal_update();
> + }
> + old_name=name;
> + name=0; // Don't free name
> + close(LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX);
> +
> + /*
> + Note that at this point, log_state != LOG_CLOSED (important for is_open()).
> + */
> +
> + /*
> + new_file() is only used for rotation (in FLUSH LOGS or because size >
> + max_binlog_size or max_relay_log_size).
> + If this is a binary log, the Format_description_log_event at the beginning of
> + the new file should have created=0 (to distinguish with the
> + Format_description_log_event written at server startup, which should
> + trigger temp tables deletion on slaves.
> + */
> +
> + /* 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);
> + my_free(old_name,MYF(0));
> +
> +end:
> + if (need_lock)
> + mysql_mutex_unlock(&LOCK_log);
> + mysql_mutex_unlock(&LOCK_index);
> +
> + DBUG_VOID_RETURN;
> +}
> +
> +
> +bool MYSQL_BIN_LOG::append(Log_event* ev)
> +{
> + bool error = 0;
> + mysql_mutex_lock(&LOCK_log);
> + DBUG_ENTER("MYSQL_BIN_LOG::append");
> +
> + 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->data_written;
> + DBUG_PRINT("info",("max_size: %lu",max_size));
> + if (flush_and_sync(0))
> + goto err;
> + if ((uint) my_b_append_tell(&log_file) > max_size)
> + new_file_without_locking();
> +
> +err:
> + mysql_mutex_unlock(&LOCK_log);
> + signal_update(); // Safe as we don't call close
> + DBUG_RETURN(error);
> +}
> +
> +
> +bool MYSQL_BIN_LOG::appendv(const char* buf, uint len,...)
> +{
> + bool error= 0;
> + DBUG_ENTER("MYSQL_BIN_LOG::appendv");
> + va_list(args);
> + va_start(args,len);
> +
> + DBUG_ASSERT(log_file.type == SEQ_READ_APPEND);
> +
> + mysql_mutex_assert_owner(&LOCK_log);
> + do
> + {
> + if (my_b_append(&log_file,(uchar*) buf,len))
> + {
> + error= 1;
> + goto err;
> + }
> + bytes_written += len;
> + } while ((buf=va_arg(args,const char*)) && (len=va_arg(args,uint)));
> + DBUG_PRINT("info",("max_size: %lu",max_size));
> + if (flush_and_sync(0))
> + goto err;
> + if ((uint) my_b_append_tell(&log_file) > max_size)
> + new_file_without_locking();
>
> +err:
> + if (!error)
> + signal_update();
> DBUG_RETURN(error);
> }
>
> -bool MYSQL_BIN_LOG::is_inited_purge_index_file()
> +bool MYSQL_BIN_LOG::flush_and_sync(bool *synced)
> {
> - DBUG_ENTER("MYSQL_BIN_LOG::is_inited_purge_index_file");
> - DBUG_RETURN (my_b_inited(&purge_index_file));
> + int err=0, fd=log_file.file;
> + if (synced)
> + *synced= 0;
> + mysql_mutex_assert_owner(&LOCK_log);
> + if (flush_io_cache(&log_file))
> + return 1;
> + uint sync_period= get_sync_period();
> + if (sync_period && ++sync_counter >= sync_period)
> + {
> + sync_counter= 0;
> + err= mysql_file_sync(fd, MYF(MY_WME));
> + if (synced)
> + *synced= 1;
> + }
> + return err;
> }
>
> -int MYSQL_BIN_LOG::sync_purge_index_file()
> +void MYSQL_BIN_LOG::start_union_events(THD *thd, query_id_t query_id_param)
> {
> - int error= 0;
> - DBUG_ENTER("MYSQL_BIN_LOG::sync_purge_index_file");
> -
> - if ((error= flush_io_cache(&purge_index_file)) ||
> - (error= my_sync(purge_index_file.file, MYF(MY_WME))))
> - DBUG_RETURN(error);
> + DBUG_ASSERT(!thd->binlog_evt_union.do_union);
> + thd->binlog_evt_union.do_union= TRUE;
> + thd->binlog_evt_union.unioned_events= FALSE;
> + thd->binlog_evt_union.unioned_events_trans= FALSE;
> + thd->binlog_evt_union.first_query_id= query_id_param;
> +}
>
> - DBUG_RETURN(error);
> +void MYSQL_BIN_LOG::stop_union_events(THD *thd)
> +{
> + DBUG_ASSERT(thd->binlog_evt_union.do_union);
> + thd->binlog_evt_union.do_union= FALSE;
> }
>
> -int MYSQL_BIN_LOG::register_purge_index_entry(const char *entry)
> +bool MYSQL_BIN_LOG::is_query_in_union(THD *thd, query_id_t query_id_param)
> {
> - int error= 0;
> - DBUG_ENTER("MYSQL_BIN_LOG::register_purge_index_entry");
> + return (thd->binlog_evt_union.do_union &&
> + query_id_param >= thd->binlog_evt_union.first_query_id);
> +}
>
> - if ((error=my_b_write(&purge_index_file, (const uchar*)entry, strlen(entry)))
> ||
> - (error=my_b_write(&purge_index_file, (const uchar*)"\n", 1)))
> - DBUG_RETURN (error);
>
> - DBUG_RETURN(error);
> -}
> +/**
> + This function removes the pending rows event, discarding any outstanding
> + rows. If there is no pending rows event available, this is effectively a
> + no-op.
>
> -int MYSQL_BIN_LOG::register_create_index_entry(const char *entry)
> + @param thd a pointer to the user thread.
> + @param is_transactional @c true indicates a transactional cache,
> + otherwise @c false a non-transactional.
> +*/
> +int
> +MYSQL_BIN_LOG::remove_pending_rows_event(THD *thd, bool is_transactional)
> {
> - DBUG_ENTER("MYSQL_BIN_LOG::register_create_index_entry");
> - DBUG_RETURN(register_purge_index_entry(entry));
> + DBUG_ENTER("MYSQL_BIN_LOG::remove_pending_rows_event");
> +
> + binlog_cache_mngr *const cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> +
> + DBUG_ASSERT(cache_mngr);
> +
> + binlog_cache_data *cache_data=
> + cache_mngr->get_binlog_cache_data(use_trans_cache(thd, is_transactional));
> +
> + if (Rows_log_event* pending= cache_data->pending())
> + {
> + delete pending;
> + cache_data->set_pending(NULL);
> + }
> +
> + DBUG_RETURN(0);
> }
>
> -int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *decrease_log_space,
> - bool need_mutex)
> +/*
> + Moves the last bunch of rows from the pending Rows event to a cache (either
> + transactional cache if is_transaction is @c true, or the non-transactional
> + cache otherwise. Sets a new pending event.
> +
> + @param thd a pointer to the user thread.
> + @param evt a pointer to the row event.
> + @param is_transactional @c true indicates a transactional cache,
> + otherwise @c false a non-transactional.
> +*/
> +int
> +MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd,
> + Rows_log_event* event,
> + bool is_transactional)
> {
> - MY_STAT s;
> + DBUG_ENTER("MYSQL_BIN_LOG::flush_and_set_pending_rows_event(event)");
> + DBUG_ASSERT(mysql_bin_log.is_open());
> + DBUG_PRINT("enter", ("event: 0x%lx", (long) event));
> +
> int error= 0;
> - LOG_INFO log_info;
> - LOG_INFO check_log_info;
> + binlog_cache_mngr *const cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
>
> - DBUG_ENTER("MYSQL_BIN_LOG:purge_index_entry");
> + DBUG_ASSERT(cache_mngr);
>
> - DBUG_ASSERT(my_b_inited(&purge_index_file));
> + binlog_cache_data *cache_data=
> + cache_mngr->get_binlog_cache_data(use_trans_cache(thd, is_transactional));
>
> - if ((error=reinit_io_cache(&purge_index_file, READ_CACHE, 0, 0, 0)))
> - {
> - sql_print_error("MSYQL_BIN_LOG::purge_index_entry failed to reinit register file
> "
> - "for read");
> - goto err;
> - }
> + DBUG_PRINT("info", ("cache_mngr->pending(): 0x%lx", (long)
> cache_data->pending()));
>
> - for (;;)
> + if (Rows_log_event* pending= cache_data->pending())
> {
> - uint length;
> + IO_CACHE *file= &cache_data->cache_log;
>
> - if ((length=my_b_gets(&purge_index_file, log_info.log_file_name,
> - FN_REFLEN)) <= 1)
> + /*
> + Write pending event to the cache.
> + */
> + if (pending->write(file))
> {
> - if (purge_index_file.error)
> - {
> - error= purge_index_file.error;
> - sql_print_error("MSYQL_BIN_LOG::purge_index_entry error %d reading from "
> - "register file.", error);
> - goto err;
> - }
> -
> - /* Reached EOF */
> - break;
> + set_write_error(thd);
> + if (check_write_error(thd) && cache_data &&
> + stmt_has_updated_non_trans_table(thd))
> + cache_data->set_incident();
> + DBUG_RETURN(1);
> }
>
> - /* Get rid of the trailing '\n' */
> - log_info.log_file_name[length-1]= 0;
> + /*
> + We step the table map version if we are writing an event
> + representing the end of a statement.
>
> - if (!mysql_file_stat(key_file_binlog, log_info.log_file_name, &s, MYF(0)))
> - {
> - if (my_errno == ENOENT)
> - {
> - /*
> - It's not fatal if we can't stat a log file that does not exist;
> - If we could not stat, we won't delete.
> - */
> - if (thd)
> - {
> - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> - ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
> - log_info.log_file_name);
> - }
> - sql_print_information("Failed to execute mysql_file_stat on file '%s'",
> - log_info.log_file_name);
> - my_errno= 0;
> - }
> - else
> - {
> - /*
> - Other than ENOENT are fatal
> - */
> - if (thd)
> - {
> - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> - ER_BINLOG_PURGE_FATAL_ERR,
> - "a problem with getting info on being purged %s; "
> - "consider examining correspondence "
> - "of your binlog index file "
> - "to the actual binlog files",
> - log_info.log_file_name);
> - }
> - else
> - {
> - sql_print_information("Failed to delete log file '%s'; "
> - "consider examining correspondence "
> - "of your binlog index file "
> - "to the actual binlog files",
> - log_info.log_file_name);
> - }
> - error= LOG_INFO_FATAL;
> - goto err;
> - }
> + In an ideal world, we could avoid stepping the table map version,
> + since we could then reuse the table map that was written earlier
> + in the cache. This does not work since STMT_END_F implies closing
> + all table mappings on the slave side.
> +
> + TODO: Find a solution so that table maps does not have to be
> + written several times within a transaction.
> + */
> + if (pending->get_flags(Rows_log_event::STMT_END_F))
> + ++m_table_map_version;
> +
> + delete pending;
> + }
> +
> + thd->binlog_set_pending_rows_event(event, is_transactional);
> +
> + DBUG_RETURN(error);
> +}
> +
> +/**
> + Write an event to the binary log.
> +*/
> +
> +bool MYSQL_BIN_LOG::write(Log_event *event_info)
> +{
> + THD *thd= event_info->thd;
> + bool error= 1;
> + DBUG_ENTER("MYSQL_BIN_LOG::write(Log_event *)");
> + binlog_cache_data *cache_data= 0;
> +
> + if (thd->binlog_evt_union.do_union)
> + {
> + /*
> + In Stored function; Remember that function call caused an update.
> + We will log the function call to the binary log on function exit
> + */
> + thd->binlog_evt_union.unioned_events= TRUE;
> + thd->binlog_evt_union.unioned_events_trans |=
> + event_info->use_trans_cache();
> + DBUG_RETURN(0);
> + }
> +
> + /*
> + We only end the statement if we are in a top-level statement. If
> + we are inside a stored function, we do not end the statement since
> + this will close all tables on the slave.
> + */
> + bool const end_stmt=
> + thd->locked_tables_mode && thd->lex->requires_prelocking();
> + if (thd->binlog_flush_pending_rows_event(end_stmt,
> + event_info->use_trans_cache()))
> + DBUG_RETURN(error);
> +
> + /*
> + In most cases this is only called if 'is_open()' is true; in fact this is
> + mostly called if is_open() *was* true a few instructions before, but it
> + could have changed since.
> + */
> + if (likely(is_open()))
> + {
> +#ifdef HAVE_REPLICATION
> + /*
> + In the future we need to add to the following if tests like
> + "do the involved tables match (to be implemented)
> + binlog_[wild_]{do|ignore}_table?" (WL#1049)"
> + */
> + const char *local_db= event_info->get_db();
> + if ((thd && !(thd->variables.option_bits & OPTION_BIN_LOG)) ||
> + !binlog_filter->db_ok(local_db))
> + DBUG_RETURN(0);
> +#endif /* HAVE_REPLICATION */
> +
> + IO_CACHE *file= NULL;
> +
> + if (event_info->use_direct_logging())
> + {
> + file= &log_file;
> + mysql_mutex_lock(&LOCK_log);
> }
> else
> {
> - if ((error= find_log_pos(&check_log_info, log_info.log_file_name,
> need_mutex)))
> + if (thd->binlog_setup_trx_data())
> + goto err;
> +
> + binlog_cache_mngr *const cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> +
> + bool is_trans_cache= use_trans_cache(thd, event_info->use_trans_cache());
> + file= cache_mngr->get_binlog_cache_log(is_trans_cache);
> + cache_data= cache_mngr->get_binlog_cache_data(is_trans_cache);
> +
> + thd->binlog_start_trans_and_stmt();
> + }
> + DBUG_PRINT("info",("event type: %d",event_info->get_type_code()));
> +
> + /*
> + No check for auto events flag here - this write method should
> + never be called if auto-events are enabled.
> +
> + Write first log events which describe the 'run environment'
> + of the SQL command. If row-based binlogging, Insert_id, Rand
> + and other kind of "setting context" events are not needed.
> + */
> + if (thd)
> + {
> + if (!thd->is_current_stmt_binlog_format_row())
> {
> - if (error != LOG_INFO_EOF)
> + if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt)
> {
> - if (thd)
> - {
> - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> - ER_BINLOG_PURGE_FATAL_ERR,
> - "a problem with deleting %s and "
> - "reading the binlog index file",
> - log_info.log_file_name);
> - }
> - else
> - {
> - sql_print_information("Failed to delete file '%s' and "
> - "read the binlog index file",
> - log_info.log_file_name);
> - }
> - goto err;
> + Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT,
> +
> thd->first_successful_insert_id_in_prev_stmt_for_binlog);
> + if (e.write(file))
> + goto err;
> }
> -
> - error= 0;
> - if (!need_mutex)
> + if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
> {
> - /*
> - This is to avoid triggering an error in NDB.
> - */
> - ha_binlog_index_purge_file(current_thd, log_info.log_file_name);
> + DBUG_PRINT("info",("number of auto_inc intervals: %u",
> + thd->auto_inc_intervals_in_cur_stmt_for_binlog.
> + nb_elements()));
> + Intvar_log_event e(thd, (uchar) INSERT_ID_EVENT,
> + thd->auto_inc_intervals_in_cur_stmt_for_binlog.
> + minimum());
> + if (e.write(file))
> + goto err;
> }
> -
> - DBUG_PRINT("info",("purging %s",log_info.log_file_name));
> - if (!my_delete(log_info.log_file_name, MYF(0)))
> + if (thd->rand_used)
> {
> - if (decrease_log_space)
> - *decrease_log_space-= s.st_size;
> + Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2);
> + if (e.write(file))
> + goto err;
> }
> - else
> + if (thd->user_var_events.elements)
> {
> - if (my_errno == ENOENT)
> - {
> - if (thd)
> - {
> - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> - ER_LOG_PURGE_NO_FILE, ER(ER_LOG_PURGE_NO_FILE),
> - log_info.log_file_name);
> - }
> - sql_print_information("Failed to delete file '%s'",
> - log_info.log_file_name);
> - my_errno= 0;
> - }
> - else
> + for (uint i= 0; i < thd->user_var_events.elements; i++)
> {
> - if (thd)
> - {
> - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> - ER_BINLOG_PURGE_FATAL_ERR,
> - "a problem with deleting %s; "
> - "consider examining correspondence "
> - "of your binlog index file "
> - "to the actual binlog files",
> - log_info.log_file_name);
> - }
> - else
> - {
> - sql_print_information("Failed to delete file '%s'; "
> - "consider examining correspondence "
> - "of your binlog index file "
> - "to the actual binlog files",
> - log_info.log_file_name);
> - }
> - if (my_errno == EMFILE)
> - {
> - DBUG_PRINT("info",
> - ("my_errno: %d, set ret = LOG_INFO_EMFILE", my_errno));
> - error= LOG_INFO_EMFILE;
> + BINLOG_USER_VAR_EVENT *user_var_event;
> + get_dynamic(&thd->user_var_events,(uchar*) &user_var_event,
> i);
> +
> + /* setting flags for user var log event */
> + uchar flags= User_var_log_event::UNDEF_F;
> + if (user_var_event->unsigned_flag)
> + flags|= User_var_log_event::UNSIGNED_F;
> +
> + User_var_log_event e(thd,
> user_var_event->user_var_event->name.str,
> + user_var_event->user_var_event->name.length,
> + user_var_event->value,
> + user_var_event->length,
> + user_var_event->type,
> + user_var_event->charset_number,
> + flags);
> + if (e.write(file))
> goto err;
> - }
> - error= LOG_INFO_FATAL;
> - goto err;
> }
> }
> }
> }
> - }
> -
> -err:
> - DBUG_RETURN(error);
> -}
>
> -/**
> - Remove all logs before the given file date from disk and from the
> - index file.
> -
> - @param thd Thread pointer
> - @param purge_time Delete all log files before given date.
> -
> - @note
> - If any of the logs before the deleted one is in use,
> - only purge logs up to this one.
> -
> - @retval
> - 0 ok
> - @retval
> - LOG_INFO_PURGE_NO_ROTATE Binary file that can't be rotated
> - LOG_INFO_FATAL if any other than ENOENT error from
> - mysql_file_stat() or mysql_file_delete()
> -*/
> -
> -int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time)
> -{
> - int error;
> - char to_log[FN_REFLEN];
> - LOG_INFO log_info;
> - MY_STAT stat_area;
> - THD *thd= current_thd;
> -
> - DBUG_ENTER("purge_logs_before_date");
> -
> - mysql_mutex_lock(&LOCK_index);
> - to_log[0]= 0;
> + /*
> + Write the event.
> + */
> + if (event_info->write(file) ||
> + DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0))
> + goto err;
>
> - if ((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/)))
> - goto err;
> + error= 0;
>
> - while (strcmp(log_file_name, log_info.log_file_name) &&
> - !is_active(log_info.log_file_name) &&
> - !log_in_use(log_info.log_file_name))
> - {
> - if (!mysql_file_stat(key_file_binlog,
> - log_info.log_file_name, &stat_area, MYF(0)))
> +err:
> + if (event_info->use_direct_logging())
> {
> - if (my_errno == ENOENT)
> - {
> - /*
> - It's not fatal if we can't stat a log file that does not exist.
> - */
> - my_errno= 0;
> - }
> - else
> + if (!error)
> {
> - /*
> - Other than ENOENT are fatal
> - */
> - if (thd)
> - {
> - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> - ER_BINLOG_PURGE_FATAL_ERR,
> - "a problem with getting info on being purged %s; "
> - "consider examining correspondence "
> - "of your binlog index file "
> - "to the actual binlog files",
> - log_info.log_file_name);
> - }
> - else
> + bool synced;
> + if ((error= flush_and_sync(&synced)))
> + goto unlock;
> +
> + if ((error= RUN_HOOK(binlog_storage, after_flush,
> + (thd, log_file_name, file->pos_in_file, synced))))
> {
> - sql_print_information("Failed to delete log file '%s'",
> - log_info.log_file_name);
> + sql_print_error("Failed to run 'after_flush' hooks");
> + goto unlock;
> }
> - error= LOG_INFO_FATAL;
> - goto err;
> + signal_update();
> + rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED);
> }
> +unlock:
> + mysql_mutex_unlock(&LOCK_log);
> }
> - else
> +
> + if (error)
> {
> - if (stat_area.st_mtime < purge_time)
> - strmake(to_log,
> - log_info.log_file_name,
> - sizeof(log_info.log_file_name) - 1);
> - else
> - break;
> + set_write_error(thd);
> + if (check_write_error(thd) && cache_data &&
> + stmt_has_updated_non_trans_table(thd))
> + cache_data->set_incident();
> }
> - if (find_next_log(&log_info, 0))
> - break;
> }
>
> - error= (to_log[0] ? purge_logs(to_log, 1, 0, 1, (ulonglong *) 0) : 0);
> + if (event_info->flags & LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F)
> + ++m_table_map_version;
>
> -err:
> - mysql_mutex_unlock(&LOCK_index);
> DBUG_RETURN(error);
> }
> -#endif /* HAVE_REPLICATION */
> -
> -
> -/**
> - Create a new log file name.
> -
> - @param 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_BIN_LOG::make_log_name(char* buf, const char* log_ident)
> -{
> - 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 -1);
> -}
> -
>
> -/**
> - Check if we are writing/reading to the given log file.
> -*/
>
> -bool MYSQL_BIN_LOG::is_active(const char *log_file_name_arg)
> +void MYSQL_BIN_LOG::rotate_and_purge(uint flags)
> {
> - return !strcmp(log_file_name, log_file_name_arg);
> -}
> -
> -
> -/*
> - Wrappers around new_file_impl to avoid using argument
> - to control locking. The argument 1) less readable 2) breaks
> - incapsulation 3) allows external access to the class without
> - a lock (which is not possible with private new_file_without_locking
> - method).
> -*/
> +#ifdef HAVE_REPLICATION
> + bool check_purge= false;
> +#endif
> + if (!(flags & RP_LOCK_LOG_IS_ALREADY_LOCKED))
> + mysql_mutex_lock(&LOCK_log);
> + if ((flags & RP_FORCE_ROTATE) ||
> + (my_b_tell(&log_file) >= (my_off_t) max_size))
> + {
> + new_file_without_locking();
> +#ifdef HAVE_REPLICATION
> + check_purge= true;
> +#endif
> + }
> + if (!(flags & RP_LOCK_LOG_IS_ALREADY_LOCKED))
> + mysql_mutex_unlock(&LOCK_log);
>
> -void MYSQL_BIN_LOG::new_file()
> -{
> - new_file_impl(1);
> +#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)
> + {
> + time_t purge_time= my_time(0) - expire_logs_days*24*60*60;
> + if (purge_time >= 0)
> + purge_logs_before_date(purge_time);
> + }
> +#endif
> }
>
> -
> -void MYSQL_BIN_LOG::new_file_without_locking()
> +uint MYSQL_BIN_LOG::next_file_id()
> {
> - new_file_impl(0);
> + uint res;
> + mysql_mutex_lock(&LOCK_log);
> + res = file_id++;
> + mysql_mutex_unlock(&LOCK_log);
> + return res;
> }
>
>
> -/**
> - Start writing to a new log file or reopen the old file.
> +/*
> + Write the contents of a cache to the binary log.
>
> - @param need_lock Set to 1 if caller has not locked LOCK_log
> + SYNOPSIS
> + write_cache()
> + cache Cache to write to the binary log
> + lock_log True if the LOCK_log mutex should be aquired, false otherwise
> + sync_log True if the log should be flushed and synced
>
> - @note
> - The new file name is stored last in the index file
> -*/
> + DESCRIPTION
> + Write the contents of the cache to the binary log. The cache will
> + be reset as a READ_CACHE to be able to read the contents from it.
> + */
>
> -void MYSQL_BIN_LOG::new_file_impl(bool need_lock)
> +int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log)
> {
> - char new_name[FN_REFLEN], *new_name_ptr, *old_name;
> -
> - DBUG_ENTER("MYSQL_BIN_LOG::new_file_impl");
> - if (!is_open())
> - {
> - DBUG_PRINT("info",("log is closed"));
> - DBUG_VOID_RETURN;
> - }
> -
> - if (need_lock)
> - mysql_mutex_lock(&LOCK_log);
> - mysql_mutex_lock(&LOCK_index);
> + Mutex_sentry sentry(lock_log ? &LOCK_log : NULL);
>
> - mysql_mutex_assert_owner(&LOCK_log);
> - mysql_mutex_assert_owner(&LOCK_index);
> + if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
> + return ER_ERROR_ON_WRITE;
> + uint length= my_b_bytes_in_cache(cache), group, carry, hdr_offs;
> + long val;
> + uchar header[LOG_EVENT_HEADER_LEN];
>
> /*
> - if binlog is used as tc log, be sure all xids are "unlogged",
> - so that on recover we only need to scan one - latest - binlog file
> - for prepared xids. As this is expected to be a rare event,
> - simple wait strategy is enough. We're locking LOCK_log to be sure no
> - new Xid_log_event's are added to the log (and prepared_xids is not
> - increased), and waiting on COND_prep_xids for late threads to
> - catch up.
> + The events in the buffer have incorrect end_log_pos data
> + (relative to beginning of group rather than absolute),
> + so we'll recalculate them in situ so the binlog is always
> + correct, even in the middle of a group. This is possible
> + because we now know the start position of the group (the
> + offset of this cache in the log, if you will); all we need
> + to do is to find all event-headers, and add the position of
> + the group to the end_log_pos of each event. This is pretty
> + straight forward, except that we read the cache in segments,
> + so an event-header might end up on the cache-border and get
> + split.
> */
> - if (prepared_xids)
> - {
> - tc_log_page_waits++;
> - mysql_mutex_lock(&LOCK_prep_xids);
> - while (prepared_xids) {
> - DBUG_PRINT("info", ("prepared_xids=%lu", prepared_xids));
> - mysql_cond_wait(&COND_prep_xids, &LOCK_prep_xids);
> - }
> - mysql_mutex_unlock(&LOCK_prep_xids);
> - }
> -
> - /* Reuse old name if not binlog and not update log */
> - new_name_ptr= name;
>
> - /*
> - 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))
> - goto end;
> - new_name_ptr=new_name;
> + group= (uint)my_b_tell(&log_file);
> + hdr_offs= carry= 0;
>
> - if (log_type == LOG_BIN)
> + do
> {
> - 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.
> - */
> - 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);
> - bytes_written += r.data_written;
> - }
> /*
> - 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.
> + if we only got a partial header in the last iteration,
> + get the other half now and process a full header.
> */
> - signal_update();
> - }
> - old_name=name;
> - name=0; // Don't free name
> - close(LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX);
> -
> - /*
> - Note that at this point, log_state != LOG_CLOSED (important for is_open()).
> - */
> -
> - /*
> - new_file() is only used for rotation (in FLUSH LOGS or because size >
> - max_binlog_size or max_relay_log_size).
> - If this is a binary log, the Format_description_log_event at the beginning of
> - the new file should have created=0 (to distinguish with the
> - Format_description_log_event written at server startup, which should
> - trigger temp tables deletion on slaves.
> - */
> -
> - /* 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);
> - my_free(old_name,MYF(0));
> -
> -end:
> - if (need_lock)
> - mysql_mutex_unlock(&LOCK_log);
> - mysql_mutex_unlock(&LOCK_index);
> -
> - DBUG_VOID_RETURN;
> -}
> -
> -
> -bool MYSQL_BIN_LOG::append(Log_event* ev)
> -{
> - bool error = 0;
> - mysql_mutex_lock(&LOCK_log);
> - DBUG_ENTER("MYSQL_BIN_LOG::append");
> + if (unlikely(carry > 0))
> + {
> + DBUG_ASSERT(carry < LOG_EVENT_HEADER_LEN);
>
> - 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->data_written;
> - DBUG_PRINT("info",("max_size: %lu",max_size));
> - if (flush_and_sync(0))
> - goto err;
> - if ((uint) my_b_append_tell(&log_file) > max_size)
> - new_file_without_locking();
> + /* assemble both halves */
> + memcpy(&header[carry], (char *)cache->read_pos, LOG_EVENT_HEADER_LEN -
> carry);
>
> -err:
> - mysql_mutex_unlock(&LOCK_log);
> - signal_update(); // Safe as we don't call close
> - DBUG_RETURN(error);
> -}
> + /* fix end_log_pos */
> + val= uint4korr(&header[LOG_POS_OFFSET]) + group;
> + int4store(&header[LOG_POS_OFFSET], val);
>
> + /* write the first half of the split header */
> + if (my_b_write(&log_file, header, carry))
> + return ER_ERROR_ON_WRITE;
>
> -bool MYSQL_BIN_LOG::appendv(const char* buf, uint len,...)
> -{
> - bool error= 0;
> - DBUG_ENTER("MYSQL_BIN_LOG::appendv");
> - va_list(args);
> - va_start(args,len);
> + /*
> + copy fixed second half of header to cache so the correct
> + version will be written later.
> + */
> + memcpy((char *)cache->read_pos, &header[carry], LOG_EVENT_HEADER_LEN -
> carry);
>
> - DBUG_ASSERT(log_file.type == SEQ_READ_APPEND);
> + /* next event header at ... */
> + hdr_offs = uint4korr(&header[EVENT_LEN_OFFSET]) - carry;
>
> - mysql_mutex_assert_owner(&LOCK_log);
> - do
> - {
> - if (my_b_append(&log_file,(uchar*) buf,len))
> - {
> - error= 1;
> - goto err;
> + carry= 0;
> }
> - bytes_written += len;
> - } while ((buf=va_arg(args,const char*)) && (len=va_arg(args,uint)));
> - DBUG_PRINT("info",("max_size: %lu",max_size));
> - if (flush_and_sync(0))
> - goto err;
> - if ((uint) my_b_append_tell(&log_file) > max_size)
> - new_file_without_locking();
>
> -err:
> - if (!error)
> - signal_update();
> - DBUG_RETURN(error);
> -}
> + /* if there is anything to write, process it. */
>
> -bool MYSQL_BIN_LOG::flush_and_sync(bool *synced)
> -{
> - int err=0, fd=log_file.file;
> - if (synced)
> - *synced= 0;
> - mysql_mutex_assert_owner(&LOCK_log);
> - if (flush_io_cache(&log_file))
> - return 1;
> - uint sync_period= get_sync_period();
> - if (sync_period && ++sync_counter >= sync_period)
> - {
> - sync_counter= 0;
> - err= mysql_file_sync(fd, MYF(MY_WME));
> - if (synced)
> - *synced= 1;
> - }
> - return err;
> -}
> + if (likely(length > 0))
> + {
> + /*
> + process all event-headers in this (partial) cache.
> + if next header is beyond current read-buffer,
> + we'll get it later (though not necessarily in the
> + very next iteration, just "eventually").
> + */
>
> -void MYSQL_BIN_LOG::start_union_events(THD *thd, query_id_t query_id_param)
> -{
> - DBUG_ASSERT(!thd->binlog_evt_union.do_union);
> - thd->binlog_evt_union.do_union= TRUE;
> - thd->binlog_evt_union.unioned_events= FALSE;
> - thd->binlog_evt_union.unioned_events_trans= FALSE;
> - thd->binlog_evt_union.first_query_id= query_id_param;
> -}
> + while (hdr_offs < length)
> + {
> + /*
> + partial header only? save what we can get, process once
> + we get the rest.
> + */
>
> -void MYSQL_BIN_LOG::stop_union_events(THD *thd)
> -{
> - DBUG_ASSERT(thd->binlog_evt_union.do_union);
> - thd->binlog_evt_union.do_union= FALSE;
> -}
> + if (hdr_offs + LOG_EVENT_HEADER_LEN > length)
> + {
> + carry= length - hdr_offs;
> + memcpy(header, (char *)cache->read_pos + hdr_offs, carry);
> + length= hdr_offs;
> + }
> + else
> + {
> + /* we've got a full event-header, and it came in one piece */
>
> -bool MYSQL_BIN_LOG::is_query_in_union(THD *thd, query_id_t query_id_param)
> -{
> - return (thd->binlog_evt_union.do_union &&
> - query_id_param >= thd->binlog_evt_union.first_query_id);
> -}
> + uchar *log_pos= (uchar *)cache->read_pos + hdr_offs + LOG_POS_OFFSET;
>
> -/**
> - This function checks if a transactional table was updated by the
> - current transaction.
> + /* fix end_log_pos */
> + val= uint4korr(log_pos) + group;
> + int4store(log_pos, val);
>
> - @param thd The client thread that executed the current statement.
> - @return
> - @c true if a transactional table was updated, @c false otherwise.
> -*/
> -bool
> -trans_has_updated_trans_table(const THD* thd)
> -{
> - binlog_cache_mngr *const cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> + /* next event header at ... */
> + log_pos= (uchar *)cache->read_pos + hdr_offs + EVENT_LEN_OFFSET;
> + hdr_offs += uint4korr(log_pos);
>
> - return (cache_mngr ? !cache_mngr->trx_cache.empty() : 0);
> -}
> + }
> + }
>
> -/**
> - This function checks if a transactional table was updated by the
> - current statement.
> + /*
> + Adjust hdr_offs. Note that it may still point beyond the segment
> + read in the next iteration; if the current event is very long,
> + it may take a couple of read-iterations (and subsequent adjustments
> + of hdr_offs) for it to point into the then-current segment.
> + If we have a split header (!carry), hdr_offs will be set at the
> + beginning of the next iteration, overwriting the value we set here:
> + */
> + hdr_offs -= length;
> + }
>
> - @param thd The client thread that executed the current statement.
> - @return
> - @c true if a transactional table was updated, @c false otherwise.
> -*/
> -bool
> -stmt_has_updated_trans_table(const THD *thd)
> -{
> - Ha_trx_info *ha_info;
> + /* Write data to the binary log file */
> + if (my_b_write(&log_file, cache->read_pos, length))
> + return ER_ERROR_ON_WRITE;
> + cache->read_pos=cache->read_end; // Mark buffer used up
> + } while ((length= my_b_fill(cache)));
>
> - for (ha_info= thd->transaction.stmt.ha_list; ha_info;
> - ha_info= ha_info->next())
> - {
> - if (ha_info->is_trx_read_write() && ha_info->ht() != binlog_hton)
> - return (TRUE);
> - }
> - return (FALSE);
> -}
> + DBUG_ASSERT(carry == 0);
>
> -/**
> - This function checks if either a trx-cache or a non-trx-cache should
> - be used. If @c bin_log_direct_non_trans_update is active or the format
> - is either MIXED or ROW, the cache to be used depends on the flag @c
> - is_transactional.
> + if (sync_log)
> + return flush_and_sync(0);
>
> - On the other hand, if binlog_format is STMT or direct option is
> - OFF, the trx-cache should be used if and only if the statement is
> - transactional or the trx-cache is not empty. Otherwise, the
> - non-trx-cache should be used.
> + return 0; // All OK
> +}
>
> - @param thd The client thread.
> - @param is_transactional The changes are related to a trx-table.
> - @return
> - @c true if a trx-cache should be used, @c false otherwise.
> -*/
> -bool use_trans_cache(const THD* thd, bool is_transactional)
> +bool MYSQL_BIN_LOG::write_incident(THD *thd, bool lock)
> {
> - binlog_cache_mngr *const cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> -
> - return
> - ((thd->variables.binlog_format != BINLOG_FORMAT_STMT ||
> - thd->variables.binlog_direct_non_trans_update) ? is_transactional :
> - (is_transactional || !cache_mngr->trx_cache.empty()));
> + uint error= 0;
> + DBUG_ENTER("MYSQL_BIN_LOG::write_incident");
> + LEX_STRING const write_error_msg=
> + { C_STRING_WITH_LEN("error writing to the binary log") };
> + Incident incident= INCIDENT_LOST_EVENTS;
> + Incident_log_event ev(thd, incident, write_error_msg);
> + if (lock)
> + mysql_mutex_lock(&LOCK_log);
> + error= ev.write(&log_file);
> + if (lock)
> + {
> + if (!error && !(error= flush_and_sync(0)))
> + {
> + signal_update();
> + rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED);
> + }
> + mysql_mutex_unlock(&LOCK_log);
> + }
> + DBUG_RETURN(error);
> }
>
> /**
> - This function checks if a transaction, either a multi-statement
> - or a single statement transaction is about to commit or not.
> + Write a cached log entry to the binary log.
> + - To support transaction over replication, we wrap the transaction
> + with BEGIN/COMMIT or BEGIN/ROLLBACK in the binary log.
> + We want to write a BEGIN/ROLLBACK block when a non-transactional table
> + was updated in a transaction which was rolled back. This is to ensure
> + that the same updates are run on the slave.
>
> - @param thd The client thread that executed the current statement.
> - @param all Committing a transaction (i.e. TRUE) or a statement
> - (i.e. FALSE).
> - @return
> - @c true if committing a transaction, otherwise @c false.
> + @param thd
> + @param cache The cache to copy to the binlog
> + @param commit_event The commit event to print after writing the
> + contents of the cache.
> + @param incident Defines if an incident event should be created to
> + notify that some non-transactional changes did
> + not get into the binlog.
> +
> + @note
> + We only come here if there is something in the cache.
> + @note
> + The thing in the cache is always a complete transaction.
> + @note
> + 'cache' needs to be reinitialized after this functions returns.
> */
> -bool ending_trans(THD* thd, const bool all)
> +
> +bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event,
> + bool incident)
> {
> - return (all || (!all && !thd->in_multi_stmt_transaction()));
> -}
> + DBUG_ENTER("MYSQL_BIN_LOG::write(THD *, IO_CACHE *, Log_event *)");
> + mysql_mutex_lock(&LOCK_log);
>
> -/**
> - This function checks if a non-transactional table was updated by
> - the current transaction.
> + DBUG_ASSERT(is_open());
> + if (likely(is_open())) // Should always be true
> + {
> + /*
> + We only bother to write to the binary log if there is anything
> + to write.
> + */
> + if (my_b_tell(cache) > 0)
> + {
> + /*
> + Log "BEGIN" at the beginning of every transaction. Here, a
> + transaction is either a BEGIN..COMMIT block or a single
> + statement in autocommit mode.
> + */
> + Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), TRUE, FALSE, TRUE, 0);
> + if (qinfo.write(&log_file))
> + goto err;
> + DBUG_EXECUTE_IF("crash_before_writing_xid",
> + {
> + if ((write_error= write_cache(cache, false, true)))
> + DBUG_PRINT("info", ("error writing binlog cache: %d",
> + write_error));
> + DBUG_PRINT("info", ("crashing before writing xid"));
> + DBUG_ABORT();
> + });
>
> - @param thd The client thread that executed the current statement.
> - @return
> - @c true if a non-transactional table was updated, @c false
> - otherwise.
> -*/
> -bool trans_has_updated_non_trans_table(const THD* thd)
> -{
> - return (thd->transaction.all.modified_non_trans_table ||
> - thd->transaction.stmt.modified_non_trans_table);
> -}
> + if ((write_error= write_cache(cache, false, false)))
> + goto err;
>
> -/**
> - This function checks if a non-transactional table was updated by the
> - current statement.
> + if (commit_event && commit_event->write(&log_file))
> + goto err;
>
> - @param thd The client thread that executed the current statement.
> - @return
> - @c true if a non-transactional table was updated, @c false otherwise.
> -*/
> -bool stmt_has_updated_non_trans_table(const THD* thd)
> -{
> - return (thd->transaction.stmt.modified_non_trans_table);
> -}
> + if (incident && write_incident(thd, FALSE))
> + goto err;
>
> -/*
> - These functions are placed in this file since they need access to
> - binlog_hton, which has internal linkage.
> -*/
> + bool synced= 0;
> + if (flush_and_sync(&synced))
> + goto err;
> + DBUG_EXECUTE_IF("half_binlogged_transaction", DBUG_ABORT(););
> + 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;
> + }
>
> -int THD::binlog_setup_trx_data()
> -{
> - DBUG_ENTER("THD::binlog_setup_trx_data");
> - binlog_cache_mngr *cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
> + if (RUN_HOOK(binlog_storage, after_flush,
> + (thd, log_file_name, log_file.pos_in_file, synced)))
> + {
> + sql_print_error("Failed to run 'after_flush' hooks");
> + write_error=1;
> + goto err;
> + }
>
> - if (cache_mngr)
> - DBUG_RETURN(0); // Already set up
> + signal_update();
> + }
>
> - cache_mngr= (binlog_cache_mngr*) my_malloc(sizeof(binlog_cache_mngr),
> MYF(MY_ZEROFILL));
> - if (!cache_mngr ||
> - open_cached_file(&cache_mngr->stmt_cache.cache_log, mysql_tmpdir,
> - LOG_PREFIX, binlog_cache_size, MYF(MY_WME)) ||
> - open_cached_file(&cache_mngr->trx_cache.cache_log, mysql_tmpdir,
> - LOG_PREFIX, binlog_cache_size, MYF(MY_WME)))
> - {
> - my_free((uchar*)cache_mngr, MYF(MY_ALLOW_ZERO_PTR));
> - DBUG_RETURN(1); // Didn't manage to set it up
> + /*
> + if commit_event is Xid_log_event, increase the number of
> + prepared_xids (it's decreasd in ::unlog()). Binlog cannot be rotated
> + if there're prepared xids in it - see the comment in new_file() for
> + an explanation.
> + If the commit_event is not Xid_log_event (then it's a Query_log_event)
> + rotate binlog, if necessary.
> + */
> + if (commit_event && commit_event->get_type_code() == XID_EVENT)
> + {
> + mysql_mutex_lock(&LOCK_prep_xids);
> + prepared_xids++;
> + mysql_mutex_unlock(&LOCK_prep_xids);
> + }
> + else
> + rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED);
> }
> - thd_set_ha_data(this, binlog_hton, cache_mngr);
> -
> - cache_mngr= new (thd_get_ha_data(this, binlog_hton)) binlog_cache_mngr;
> + mysql_mutex_unlock(&LOCK_log);
>
> DBUG_RETURN(0);
> -}
> -
> -/*
> - Function to start a statement and optionally a transaction for the
> - binary log.
>
> - SYNOPSIS
> - binlog_start_trans_and_stmt()
> -
> - DESCRIPTION
> +err:
> + if (!write_error)
> + {
> + write_error= 1;
> + sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
> + }
> + mysql_mutex_unlock(&LOCK_log);
> + DBUG_RETURN(1);
> +}
>
> - This function does three things:
> - - Start a transaction if not in autocommit mode or if a BEGIN
> - statement has been seen.
>
> - - Start a statement transaction to allow us to truncate the cache.
> +/**
> + Wait until we get a signal that the relay log has been updated.
>
> - - Save the currrent binlog position so that we can roll back the
> - statement by truncating the cache.
> + @param thd Thread variable
>
> - We only update the saved position if the old one was undefined,
> - the reason is that there are some cases (e.g., for CREATE-SELECT)
> - where the position is saved twice (e.g., both in
> - select_create::prepare() and THD::binlog_write_table_map()) , but
> - we should use the first. This means that calls to this function
> - can be used to start the statement before the first table map
> - event, to include some extra events.
> - */
> + @note
> + One must have a lock on LOCK_log before calling this function.
> + This lock will be released before return! That's required by
> + THD::enter_cond() (see NOTES in sql_class.h).
> +*/
>
> -void
> -THD::binlog_start_trans_and_stmt()
> +void MYSQL_BIN_LOG::wait_for_update_relay_log(THD* thd)
> {
> - binlog_cache_mngr *cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(this,
> binlog_hton);
> - DBUG_ENTER("binlog_start_trans_and_stmt");
> - DBUG_PRINT("enter", ("cache_mngr: %p
> cache_mngr->trx_cache.get_prev_position(): %lu",
> - cache_mngr,
> - (cache_mngr ? (ulong)
> cache_mngr->trx_cache.get_prev_position() :
> - (ulong) 0)));
> + const char *old_msg;
> + DBUG_ENTER("wait_for_update_relay_log");
>
> - if (cache_mngr == NULL ||
> - cache_mngr->trx_cache.get_prev_position() == MY_OFF_T_UNDEF)
> - {
> - this->binlog_set_stmt_begin();
> - if (in_multi_stmt_transaction())
> - trans_register_ha(this, TRUE, binlog_hton);
> - trans_register_ha(this, FALSE, binlog_hton);
> - /*
> - Mark statement transaction as read/write. We never start
> - a binary log transaction and keep it read-only,
> - therefore it's best to mark the transaction read/write just
> - at the same time we start it.
> - Not necessary to mark the normal transaction read/write
> - since the statement-level flag will be propagated automatically
> - inside ha_commit_trans.
> - */
> - ha_data[binlog_hton->slot].ha_info[0].set_trx_read_write();
> - }
> + old_msg= thd->enter_cond(&update_cond, &LOCK_log,
> + "Slave has read all relay log; "
> + "waiting for the slave I/O "
> + "thread to update it" );
> + mysql_cond_wait(&update_cond, &LOCK_log);
> + thd->exit_cond(old_msg);
> DBUG_VOID_RETURN;
> }
>
> -void THD::binlog_set_stmt_begin() {
> - binlog_cache_mngr *cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
> +/**
> + Wait until we get a signal that the binary log has been updated.
> + Applies to master only.
> +
> + NOTES
> + @param[in] thd a THD struct
> + @param[in] timeout a pointer to a timespec;
> + NULL means to wait w/o timeout.
> + @retval 0 if got signalled on update
> + @retval non-0 if wait timeout elapsed
> + @note
> + LOCK_log must be taken before calling this function.
> + LOCK_log is being released while the thread is waiting.
> + LOCK_log is released by the caller.
> +*/
>
> - /*
> - The call to binlog_trans_log_savepos() might create the cache_mngr
> - structure, if it didn't exist before, so we save the position
> - into an auto variable and then write it into the transaction
> - data for the binary log (i.e., cache_mngr).
> - */
> - my_off_t pos= 0;
> - binlog_trans_log_savepos(this, &pos);
> - cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
> - cache_mngr->trx_cache.set_prev_position(pos);
> +int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd,
> + const struct timespec *timeout)
> +{
> + int ret= 0;
> + const char* old_msg = thd->proc_info;
> + DBUG_ENTER("wait_for_update_bin_log");
> + old_msg= thd->enter_cond(&update_cond, &LOCK_log,
> + "Master has sent all binlog to slave; "
> + "waiting for binlog to be updated");
> + if (!timeout)
> + mysql_cond_wait(&update_cond, &LOCK_log);
> + else
> + ret= mysql_cond_timedwait(&update_cond, &LOCK_log,
> + const_cast<struct timespec *>(timeout));
> + DBUG_RETURN(ret);
> }
>
>
> /**
> - This function writes a table map to the binary log.
> - Note that in order to keep the signature uniform with related methods,
> - we use a redundant parameter to indicate whether a transactional table
> - was changed or not.
> -
> - @param table a pointer to the table.
> - @param is_transactional @c true indicates a transactional table,
> - otherwise @c false a non-transactional.
> - @return
> - nonzero if an error pops up when writing the table map event.
> -*/
> -int THD::binlog_write_table_map(TABLE *table, bool is_transactional)
> -{
> - int error;
> - DBUG_ENTER("THD::binlog_write_table_map");
> - DBUG_PRINT("enter", ("table: 0x%lx (%s: #%lu)",
> - (long) table, table->s->table_name.str,
> - table->s->table_map_id));
> + Close the log file.
>
> - /* Pre-conditions */
> - DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
> mysql_bin_log.is_open());
> - DBUG_ASSERT(table->s->table_map_id != ULONG_MAX);
> + @param exiting Bitmask for one or more of the following bits:
> + - LOG_CLOSE_INDEX : if we should close the index file
> + - LOG_CLOSE_TO_BE_OPENED : if we intend to call open
> + at once after close.
> + - LOG_CLOSE_STOP_EVENT : write a 'stop' event to the log
>
> - Table_map_log_event
> - the_event(this, table, table->s->table_map_id, is_transactional);
> + @note
> + 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_BIN_LOG::close(uint exiting)
> +{ // One can't set log_type here!
> + DBUG_ENTER("MYSQL_BIN_LOG::close");
> + DBUG_PRINT("enter",("exiting: %d", (int) exiting));
> + if (log_state == LOG_OPENED)
> + {
> +#ifdef HAVE_REPLICATION
> + if (log_type == LOG_BIN && !no_auto_events &&
> + (exiting & LOG_CLOSE_STOP_EVENT))
> + {
> + Stop_log_event s;
> + s.write(&log_file);
> + bytes_written+= s.data_written;
> + signal_update();
> + }
> +#endif /* HAVE_REPLICATION */
>
> - if (binlog_table_maps == 0)
> - binlog_start_trans_and_stmt();
> + /* don't pwrite in a file opened with O_APPEND - it doesn't work */
> + if (log_file.type == WRITE_CACHE && log_type == LOG_BIN)
> + {
> + my_off_t offset= BIN_LOG_HEADER_SIZE + FLAGS_OFFSET;
> + my_off_t org_position= mysql_file_tell(log_file.file, MYF(0));
> + uchar flags= 0; // clearing LOG_EVENT_BINLOG_IN_USE_F
> + mysql_file_pwrite(log_file.file, &flags, 1, offset, MYF(0));
> + /*
> + Restore position so that anything we have in the IO_cache is written
> + to the correct position.
> + We need the seek here, as mysql_file_pwrite() is not guaranteed to keep the
> + original position on system that doesn't support pwrite().
> + */
> + mysql_file_seek(log_file.file, org_position, MY_SEEK_SET, MYF(0));
> + }
>
> - binlog_cache_mngr *const cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
> + /* this will cleanup IO_CACHE, sync and close the file */
> + MYSQL_LOG::close(exiting);
> + }
>
> - IO_CACHE *file=
> - cache_mngr->get_binlog_cache_log(use_trans_cache(this, is_transactional));
> - if ((error= the_event.write(file)))
> - DBUG_RETURN(error);
> + /*
> + 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.
> + */
>
> - binlog_table_maps++;
> - table->s->table_map_version= mysql_bin_log.table_map_version();
> - DBUG_RETURN(0);
> + if ((exiting & LOG_CLOSE_INDEX) && my_b_inited(&index_file))
> + {
> + end_io_cache(&index_file);
> + if (mysql_file_close(index_file.file, MYF(0)) < 0 && ! write_error)
> + {
> + write_error= 1;
> + sql_print_error(ER(ER_ERROR_ON_WRITE), index_file_name, errno);
> + }
> + }
> + log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED :
> LOG_CLOSED;
> + safeFree(name);
> + DBUG_VOID_RETURN;
> }
>
> -/**
> - This function retrieves a pending row event from a cache which is
> - specified through the parameter @c is_transactional. Respectively, when it
> - is @c true, the pending event is returned from the transactional cache.
> - Otherwise from the non-transactional cache.
>
> - @param is_transactional @c true indicates a transactional cache,
> - otherwise @c false a non-transactional.
> - @return
> - The row event if any.
> -*/
> -Rows_log_event*
> -THD::binlog_get_pending_rows_event(bool is_transactional) const
> +void MYSQL_BIN_LOG::set_max_size(ulong max_size_arg)
> {
> - Rows_log_event* rows= NULL;
> - binlog_cache_mngr *const cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
> -
> /*
> - This is less than ideal, but here's the story: If there is no cache_mngr,
> - prepare_pending_rows_event() has never been called (since the cache_mngr
> - is set up there). In that case, we just return NULL.
> - */
> - if (cache_mngr)
> - {
> - binlog_cache_data *cache_data=
> - cache_mngr->get_binlog_cache_data(use_trans_cache(this,
> is_transactional));
> -
> - rows= cache_data->pending();
> - }
> - return (rows);
> + We need to take locks, otherwise this may happen:
> + new_file() is called, calls open(old_max_size), then before open() starts,
> + set_max_size() sets max_size to max_size_arg, then open() starts and
> + uses the old_max_size argument, so max_size_arg has been overwritten and
> + it's like if the SET command was never run.
> + */
> + DBUG_ENTER("MYSQL_BIN_LOG::set_max_size");
> + mysql_mutex_lock(&LOCK_log);
> + if (is_open())
> + max_size= max_size_arg;
> + mysql_mutex_unlock(&LOCK_log);
> + DBUG_VOID_RETURN;
> }
>
> -/**
> - This function stores a pending row event into a cache which is specified
> - through the parameter @c is_transactional. Respectively, when it is @c
> - true, the pending event is stored into the transactional cache. Otherwise
> - into the non-transactional cache.
>
> - @param evt a pointer to the row event.
> - @param is_transactional @c true indicates a transactional cache,
> - otherwise @c false a non-transactional.
> -*/
> -void
> -THD::binlog_set_pending_rows_event(Rows_log_event* ev, bool is_transactional)
> +void MYSQL_BIN_LOG::signal_update()
> {
> - if (thd_get_ha_data(this, binlog_hton) == NULL)
> - binlog_setup_trx_data();
> -
> - binlog_cache_mngr *const cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
> -
> - DBUG_ASSERT(cache_mngr);
> -
> - binlog_cache_data *cache_data=
> - cache_mngr->get_binlog_cache_data(use_trans_cache(this, is_transactional));
> -
> - cache_data->set_pending(ev);
> + DBUG_ENTER("MYSQL_BIN_LOG::signal_update");
> + signal_cnt++;
> + mysql_cond_broadcast(&update_cond);
> + DBUG_VOID_RETURN;
> }
>
> +/****** transaction coordinator log for 2pc - binlog() based solution ******/
>
> /**
> - This function removes the pending rows event, discarding any outstanding
> - rows. If there is no pending rows event available, this is effectively a
> - no-op.
> -
> - @param thd a pointer to the user thread.
> - @param is_transactional @c true indicates a transactional cache,
> - otherwise @c false a non-transactional.
> + @todo
> + keep in-memory list of prepared transactions
> + (add to list in log(), remove on unlog())
> + and copy it to the new binlog if rotated
> + but let's check the behaviour of tc_log_page_waits first!
> */
> -int
> -MYSQL_BIN_LOG::remove_pending_rows_event(THD *thd, bool is_transactional)
> -{
> - DBUG_ENTER("MYSQL_BIN_LOG::remove_pending_rows_event");
>
> - binlog_cache_mngr *const cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> +int MYSQL_BIN_LOG::open(const char *opt_name)
> +{
> + LOG_INFO log_info;
> + int error= 1;
>
> - DBUG_ASSERT(cache_mngr);
> + DBUG_ASSERT(total_ha_2pc > 1);
> + DBUG_ASSERT(opt_name && opt_name[0]);
>
> - binlog_cache_data *cache_data=
> - cache_mngr->get_binlog_cache_data(use_trans_cache(thd, is_transactional));
> + mysql_mutex_init(key_BINLOG_LOCK_prep_xids,
> + &LOCK_prep_xids, MY_MUTEX_INIT_FAST);
> + mysql_cond_init(key_BINLOG_COND_prep_xids, &COND_prep_xids, 0);
>
> - if (Rows_log_event* pending= cache_data->pending())
> + if (!my_b_inited(&index_file))
> {
> - delete pending;
> - cache_data->set_pending(NULL);
> + /* There was a failure to open the index file, can't open the binlog */
> + cleanup();
> + return 1;
> }
>
> - DBUG_RETURN(0);
> -}
> -
> -/*
> - Moves the last bunch of rows from the pending Rows event to a cache (either
> - transactional cache if is_transaction is @c true, or the non-transactional
> - cache otherwise. Sets a new pending event.
> -
> - @param thd a pointer to the user thread.
> - @param evt a pointer to the row event.
> - @param is_transactional @c true indicates a transactional cache,
> - otherwise @c false a non-transactional.
> -*/
> -int
> -MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd,
> - Rows_log_event* event,
> - bool is_transactional)
> -{
> - DBUG_ENTER("MYSQL_BIN_LOG::flush_and_set_pending_rows_event(event)");
> - DBUG_ASSERT(mysql_bin_log.is_open());
> - DBUG_PRINT("enter", ("event: 0x%lx", (long) event));
> + if (using_heuristic_recover())
> + {
> + /* generate a new binlog to mask a corrupted one */
> + open(opt_name, LOG_BIN, 0, WRITE_CACHE, 0, max_binlog_size, 0, TRUE);
> + cleanup();
> + return 1;
> + }
>
> - int error= 0;
> - binlog_cache_mngr *const cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> + if ((error= find_log_pos(&log_info, NullS, 1)))
> + {
> + if (error != LOG_INFO_EOF)
> + sql_print_error("find_log_pos() failed (error: %d)", error);
> + else
> + error= 0;
> + goto err;
> + }
>
> - DBUG_ASSERT(cache_mngr);
> + {
> + const char *errmsg;
> + IO_CACHE log;
> + File file;
> + Log_event *ev=0;
> + Format_description_log_event fdle(BINLOG_VERSION);
> + char log_name[FN_REFLEN];
>
> - binlog_cache_data *cache_data=
> - cache_mngr->get_binlog_cache_data(use_trans_cache(thd, is_transactional));
> + if (! fdle.is_valid())
> + goto err;
>
> - DBUG_PRINT("info", ("cache_mngr->pending(): 0x%lx", (long)
> cache_data->pending()));
> + do
> + {
> + strmake(log_name, log_info.log_file_name, sizeof(log_name)-1);
> + } while (!(error= find_next_log(&log_info, 1)));
>
> - if (Rows_log_event* pending= cache_data->pending())
> - {
> - IO_CACHE *file= &cache_data->cache_log;
> + if (error != LOG_INFO_EOF)
> + {
> + sql_print_error("find_log_pos() failed (error: %d)", error);
> + goto err;
> + }
>
> - /*
> - Write pending event to the cache.
> - */
> - if (pending->write(file))
> + if ((file= open_binlog(&log, log_name, &errmsg)) < 0)
> {
> - set_write_error(thd);
> - if (check_write_error(thd) && cache_data &&
> - stmt_has_updated_non_trans_table(thd))
> - cache_data->set_incident();
> - DBUG_RETURN(1);
> + sql_print_error("%s", errmsg);
> + goto err;
> }
>
> - /*
> - We step the table map version if we are writing an event
> - representing the end of a statement.
> + if ((ev= Log_event::read_log_event(&log, 0, &fdle)) &&
> + ev->get_type_code() == FORMAT_DESCRIPTION_EVENT &&
> + ev->flags & LOG_EVENT_BINLOG_IN_USE_F)
> + {
> + sql_print_information("Recovering after a crash using %s", opt_name);
> + error= recover(&log, (Format_description_log_event *)ev);
> + }
> + else
> + error=0;
>
> - In an ideal world, we could avoid stepping the table map version,
> - since we could then reuse the table map that was written earlier
> - in the cache. This does not work since STMT_END_F implies closing
> - all table mappings on the slave side.
> -
> - TODO: Find a solution so that table maps does not have to be
> - written several times within a transaction.
> - */
> - if (pending->get_flags(Rows_log_event::STMT_END_F))
> - ++m_table_map_version;
> + delete ev;
> + end_io_cache(&log);
> + mysql_file_close(file, MYF(MY_WME));
>
> - delete pending;
> + if (error)
> + goto err;
> }
>
> - thd->binlog_set_pending_rows_event(event, is_transactional);
> +err:
> + return error;
> +}
>
> - DBUG_RETURN(error);
> +/** This is called on shutdown, after ha_panic. */
> +void MYSQL_BIN_LOG::close()
> +{
> + DBUG_ASSERT(prepared_xids==0);
> + mysql_mutex_destroy(&LOCK_prep_xids);
> + mysql_cond_destroy(&COND_prep_xids);
> }
>
> /**
> - Write an event to the binary log.
> -*/
> + @todo
> + group commit
>
> -bool MYSQL_BIN_LOG::write(Log_event *event_info)
> + @retval
> + 0 error
> + @retval
> + 1 success
> +*/
> +int MYSQL_BIN_LOG::log_xid(THD *thd, my_xid xid)
> {
> - THD *thd= event_info->thd;
> - bool error= 1;
> - DBUG_ENTER("MYSQL_BIN_LOG::write(Log_event *)");
> - binlog_cache_data *cache_data= 0;
> + DBUG_ENTER("MYSQL_BIN_LOG::log_xid");
> + Xid_log_event xle(thd, xid);
> + binlog_cache_mngr *cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> + /*
> + We always commit the entire transaction when writing an XID. Also
> + note that the return value is inverted.
> + */
> + DBUG_RETURN(!binlog_flush_stmt_cache(thd, cache_mngr) &&
> + !binlog_flush_trx_cache(thd, cache_mngr, &xle));
> +}
>
> - if (thd->binlog_evt_union.do_union)
> - {
> - /*
> - In Stored function; Remember that function call caused an update.
> - We will log the function call to the binary log on function exit
> - */
> - thd->binlog_evt_union.unioned_events= TRUE;
> - thd->binlog_evt_union.unioned_events_trans |=
> - event_info->use_trans_cache();
> - DBUG_RETURN(0);
> +void MYSQL_BIN_LOG::unlog(ulong cookie, my_xid xid)
> +{
> + mysql_mutex_lock(&LOCK_prep_xids);
> + DBUG_ASSERT(prepared_xids > 0);
> + if (--prepared_xids == 0) {
> + DBUG_PRINT("info", ("prepared_xids=%lu", prepared_xids));
> + mysql_cond_signal(&COND_prep_xids);
> }
> + mysql_mutex_unlock(&LOCK_prep_xids);
> + rotate_and_purge(0); // as ::write() did not rotate
> +}
>
> - /*
> - We only end the statement if we are in a top-level statement. If
> - we are inside a stored function, we do not end the statement since
> - this will close all tables on the slave.
> - */
> - bool const end_stmt=
> - thd->locked_tables_mode && thd->lex->requires_prelocking();
> - if (thd->binlog_flush_pending_rows_event(end_stmt,
> - event_info->use_trans_cache()))
> - DBUG_RETURN(error);
> +int MYSQL_BIN_LOG::recover(IO_CACHE *log, Format_description_log_event *fdle)
> +{
> + Log_event *ev;
> + HASH xids;
> + MEM_ROOT mem_root;
>
> - /*
> - In most cases this is only called if 'is_open()' is true; in fact this is
> - mostly called if is_open() *was* true a few instructions before, but it
> - could have changed since.
> - */
> - if (likely(is_open()))
> - {
> -#ifdef HAVE_REPLICATION
> - /*
> - In the future we need to add to the following if tests like
> - "do the involved tables match (to be implemented)
> - binlog_[wild_]{do|ignore}_table?" (WL#1049)"
> - */
> - const char *local_db= event_info->get_db();
> - if ((thd && !(thd->variables.option_bits & OPTION_BIN_LOG)) ||
> - !binlog_filter->db_ok(local_db))
> - DBUG_RETURN(0);
> -#endif /* HAVE_REPLICATION */
> + if (! fdle->is_valid() ||
> + my_hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0,
> + sizeof(my_xid), 0, 0, MYF(0)))
> + goto err1;
>
> - IO_CACHE *file= NULL;
> + init_alloc_root(&mem_root, TC_LOG_PAGE_SIZE, TC_LOG_PAGE_SIZE);
>
> - if (event_info->use_direct_logging())
> + fdle->flags&= ~LOG_EVENT_BINLOG_IN_USE_F; // abort on the first error
> +
> + while ((ev= Log_event::read_log_event(log,0,fdle)) && ev->is_valid())
> + {
> + if (ev->get_type_code() == XID_EVENT)
> {
> - file= &log_file;
> - mysql_mutex_lock(&LOCK_log);
> + Xid_log_event *xev=(Xid_log_event *)ev;
> + uchar *x= (uchar *) memdup_root(&mem_root, (uchar*) &xev->xid,
> + sizeof(xev->xid));
> + if (!x || my_hash_insert(&xids, x))
> + goto err2;
> }
> - else
> - {
> - if (thd->binlog_setup_trx_data())
> - goto err;
> + delete ev;
> + }
>
> - binlog_cache_mngr *const cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> + if (ha_recover(&xids))
> + goto err2;
>
> - bool is_trans_cache= use_trans_cache(thd, event_info->use_trans_cache());
> - file= cache_mngr->get_binlog_cache_log(is_trans_cache);
> - cache_data= cache_mngr->get_binlog_cache_data(is_trans_cache);
> + free_root(&mem_root, MYF(0));
> + my_hash_free(&xids);
> + return 0;
>
> - thd->binlog_start_trans_and_stmt();
> - }
> - DBUG_PRINT("info",("event type: %d",event_info->get_type_code()));
> +err2:
> + free_root(&mem_root, MYF(0));
> + my_hash_free(&xids);
> +err1:
> + sql_print_error("Crash recovery failed. Either correct the problem "
> + "(if it's, for example, out of memory error) and restart, "
> + "or delete (or rename) binary log and start mysqld with "
> + "--tc-heuristic-recover={commit|rollback}");
> + return 1;
> +}
>
> - /*
> - No check for auto events flag here - this write method should
> - never be called if auto-events are enabled.
>
> - Write first log events which describe the 'run environment'
> - of the SQL command. If row-based binlogging, Insert_id, Rand
> - and other kind of "setting context" events are not needed.
> - */
> - if (thd)
> - {
> - if (!thd->is_current_stmt_binlog_format_row())
> - {
> - if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt)
> - {
> - Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT,
> -
> thd->first_successful_insert_id_in_prev_stmt_for_binlog);
> - if (e.write(file))
> - goto err;
> - }
> - if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
> - {
> - DBUG_PRINT("info",("number of auto_inc intervals: %u",
> - thd->auto_inc_intervals_in_cur_stmt_for_binlog.
> - nb_elements()));
> - Intvar_log_event e(thd, (uchar) INSERT_ID_EVENT,
> - thd->auto_inc_intervals_in_cur_stmt_for_binlog.
> - minimum());
> - if (e.write(file))
> - goto err;
> - }
> - if (thd->rand_used)
> - {
> - Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2);
> - if (e.write(file))
> - goto err;
> - }
> - if (thd->user_var_events.elements)
> - {
> - for (uint i= 0; i < thd->user_var_events.elements; i++)
> - {
> - BINLOG_USER_VAR_EVENT *user_var_event;
> - get_dynamic(&thd->user_var_events,(uchar*) &user_var_event,
> i);
> +/*
> + These functions are placed in this file since they need access to
> + binlog_hton, which has internal linkage.
> +*/
>
> - /* setting flags for user var log event */
> - uchar flags= User_var_log_event::UNDEF_F;
> - if (user_var_event->unsigned_flag)
> - flags|= User_var_log_event::UNSIGNED_F;
> +int THD::binlog_setup_trx_data()
> +{
> + DBUG_ENTER("THD::binlog_setup_trx_data");
> + binlog_cache_mngr *cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
>
> - User_var_log_event e(thd,
> user_var_event->user_var_event->name.str,
> - user_var_event->user_var_event->name.length,
> - user_var_event->value,
> - user_var_event->length,
> - user_var_event->type,
> - user_var_event->charset_number,
> - flags);
> - if (e.write(file))
> - goto err;
> - }
> - }
> - }
> - }
> + if (cache_mngr)
> + DBUG_RETURN(0); // Already set up
>
> - /*
> - Write the event.
> - */
> - if (event_info->write(file) ||
> - DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0))
> - goto err;
> + cache_mngr= (binlog_cache_mngr*) my_malloc(sizeof(binlog_cache_mngr),
> MYF(MY_ZEROFILL));
> + if (!cache_mngr ||
> + open_cached_file(&cache_mngr->stmt_cache.cache_log, mysql_tmpdir,
> + LOG_PREFIX, binlog_cache_size, MYF(MY_WME)) ||
> + open_cached_file(&cache_mngr->trx_cache.cache_log, mysql_tmpdir,
> + LOG_PREFIX, binlog_cache_size, MYF(MY_WME)))
> + {
> + my_free((uchar*)cache_mngr, MYF(MY_ALLOW_ZERO_PTR));
> + DBUG_RETURN(1); // Didn't manage to set it up
> + }
> + thd_set_ha_data(this, binlog_hton, cache_mngr);
>
> - error= 0;
> + cache_mngr= new (thd_get_ha_data(this, binlog_hton)) binlog_cache_mngr;
>
> -err:
> - if (event_info->use_direct_logging())
> - {
> - if (!error)
> - {
> - bool synced;
> - if ((error= flush_and_sync(&synced)))
> - goto unlock;
> + DBUG_RETURN(0);
> +}
>
> - if ((error= RUN_HOOK(binlog_storage, after_flush,
> - (thd, log_file_name, file->pos_in_file, synced))))
> - {
> - sql_print_error("Failed to run 'after_flush' hooks");
> - goto unlock;
> - }
> - signal_update();
> - rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED);
> - }
> -unlock:
> - mysql_mutex_unlock(&LOCK_log);
> - }
> +/*
> + Function to start a statement and optionally a transaction for the
> + binary log.
>
> - if (error)
> - {
> - set_write_error(thd);
> - if (check_write_error(thd) && cache_data &&
> - stmt_has_updated_non_trans_table(thd))
> - cache_data->set_incident();
> - }
> - }
> + SYNOPSIS
> + binlog_start_trans_and_stmt()
>
> - if (event_info->flags & LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F)
> - ++m_table_map_version;
> + DESCRIPTION
>
> - DBUG_RETURN(error);
> -}
> + This function does three things:
> + - Start a transaction if not in autocommit mode or if a BEGIN
> + statement has been seen.
>
> + - Start a statement transaction to allow us to truncate the cache.
>
> -void MYSQL_BIN_LOG::rotate_and_purge(uint flags)
> + - Save the currrent binlog position so that we can roll back the
> + statement by truncating the cache.
> +
> + We only update the saved position if the old one was undefined,
> + the reason is that there are some cases (e.g., for CREATE-SELECT)
> + where the position is saved twice (e.g., both in
> + select_create::prepare() and THD::binlog_write_table_map()) , but
> + we should use the first. This means that calls to this function
> + can be used to start the statement before the first table map
> + event, to include some extra events.
> + */
> +
> +void
> +THD::binlog_start_trans_and_stmt()
> {
> -#ifdef HAVE_REPLICATION
> - bool check_purge= false;
> -#endif
> - if (!(flags & RP_LOCK_LOG_IS_ALREADY_LOCKED))
> - mysql_mutex_lock(&LOCK_log);
> - if ((flags & RP_FORCE_ROTATE) ||
> - (my_b_tell(&log_file) >= (my_off_t) max_size))
> + binlog_cache_mngr *cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(this,
> binlog_hton);
> + DBUG_ENTER("binlog_start_trans_and_stmt");
> + DBUG_PRINT("enter", ("cache_mngr: %p
> cache_mngr->trx_cache.get_prev_position(): %lu",
> + cache_mngr,
> + (cache_mngr ? (ulong)
> cache_mngr->trx_cache.get_prev_position() :
> + (ulong) 0)));
> +
> + if (cache_mngr == NULL ||
> + cache_mngr->trx_cache.get_prev_position() == MY_OFF_T_UNDEF)
> {
> - new_file_without_locking();
> -#ifdef HAVE_REPLICATION
> - check_purge= true;
> -#endif
> + this->binlog_set_stmt_begin();
> + if (in_multi_stmt_transaction())
> + trans_register_ha(this, TRUE, binlog_hton);
> + trans_register_ha(this, FALSE, binlog_hton);
> + /*
> + Mark statement transaction as read/write. We never start
> + a binary log transaction and keep it read-only,
> + therefore it's best to mark the transaction read/write just
> + at the same time we start it.
> + Not necessary to mark the normal transaction read/write
> + since the statement-level flag will be propagated automatically
> + inside ha_commit_trans.
> + */
> + ha_data[binlog_hton->slot].ha_info[0].set_trx_read_write();
> }
> - if (!(flags & RP_LOCK_LOG_IS_ALREADY_LOCKED))
> - mysql_mutex_unlock(&LOCK_log);
> + DBUG_VOID_RETURN;
> +}
> +
> +void THD::binlog_set_stmt_begin() {
> + binlog_cache_mngr *cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
>
> -#ifdef HAVE_REPLICATION
> /*
> - NOTE: Run purge_logs wo/ holding LOCK_log
> - as it otherwise will deadlock in ndbcluster_binlog_index_purge_file
> + The call to binlog_trans_log_savepos() might create the cache_mngr
> + structure, if it didn't exist before, so we save the position
> + into an auto variable and then write it into the transaction
> + data for the binary log (i.e., cache_mngr).
> */
> - if (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
> + my_off_t pos= 0;
> + binlog_trans_log_savepos(this, &pos);
> + cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
> + cache_mngr->trx_cache.set_prev_position(pos);
> }
>
> -uint MYSQL_BIN_LOG::next_file_id()
> +
> +/**
> + This function writes a table map to the binary log.
> + Note that in order to keep the signature uniform with related methods,
> + we use a redundant parameter to indicate whether a transactional table
> + was changed or not.
> +
> + @param table a pointer to the table.
> + @param is_transactional @c true indicates a transactional table,
> + otherwise @c false a non-transactional.
> + @return
> + nonzero if an error pops up when writing the table map event.
> +*/
> +int THD::binlog_write_table_map(TABLE *table, bool is_transactional)
> {
> - uint res;
> - mysql_mutex_lock(&LOCK_log);
> - res = file_id++;
> - mysql_mutex_unlock(&LOCK_log);
> - return res;
> -}
> + int error;
> + DBUG_ENTER("THD::binlog_write_table_map");
> + DBUG_PRINT("enter", ("table: 0x%lx (%s: #%lu)",
> + (long) table, table->s->table_name.str,
> + table->s->table_map_id));
>
> + /* Pre-conditions */
> + DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
> mysql_bin_log.is_open());
> + DBUG_ASSERT(table->s->table_map_id != ULONG_MAX);
>
> -/*
> - Write the contents of a cache to the binary log.
> + Table_map_log_event
> + the_event(this, table, table->s->table_map_id, is_transactional);
>
> - SYNOPSIS
> - write_cache()
> - cache Cache to write to the binary log
> - lock_log True if the LOCK_log mutex should be aquired, false otherwise
> - sync_log True if the log should be flushed and synced
> + if (binlog_table_maps == 0)
> + binlog_start_trans_and_stmt();
>
> - DESCRIPTION
> - Write the contents of the cache to the binary log. The cache will
> - be reset as a READ_CACHE to be able to read the contents from it.
> - */
> + binlog_cache_mngr *const cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
>
> -int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log)
> -{
> - Mutex_sentry sentry(lock_log ? &LOCK_log : NULL);
> + IO_CACHE *file=
> + cache_mngr->get_binlog_cache_log(use_trans_cache(this, is_transactional));
> + if ((error= the_event.write(file)))
> + DBUG_RETURN(error);
>
> - if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
> - return ER_ERROR_ON_WRITE;
> - uint length= my_b_bytes_in_cache(cache), group, carry, hdr_offs;
> - long val;
> - uchar header[LOG_EVENT_HEADER_LEN];
> + binlog_table_maps++;
> + table->s->table_map_version= mysql_bin_log.table_map_version();
> + DBUG_RETURN(0);
> +}
>
> - /*
> - The events in the buffer have incorrect end_log_pos data
> - (relative to beginning of group rather than absolute),
> - so we'll recalculate them in situ so the binlog is always
> - correct, even in the middle of a group. This is possible
> - because we now know the start position of the group (the
> - offset of this cache in the log, if you will); all we need
> - to do is to find all event-headers, and add the position of
> - the group to the end_log_pos of each event. This is pretty
> - straight forward, except that we read the cache in segments,
> - so an event-header might end up on the cache-border and get
> - split.
> - */
> +/**
> + This function retrieves a pending row event from a cache which is
> + specified through the parameter @c is_transactional. Respectively, when it
> + is @c true, the pending event is returned from the transactional cache.
> + Otherwise from the non-transactional cache.
>
> - group= (uint)my_b_tell(&log_file);
> - hdr_offs= carry= 0;
> + @param is_transactional @c true indicates a transactional cache,
> + otherwise @c false a non-transactional.
> + @return
> + The row event if any.
> +*/
> +Rows_log_event*
> +THD::binlog_get_pending_rows_event(bool is_transactional) const
> +{
> + Rows_log_event* rows= NULL;
> + binlog_cache_mngr *const cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
>
> - do
> + /*
> + This is less than ideal, but here's the story: If there is no cache_mngr,
> + prepare_pending_rows_event() has never been called (since the cache_mngr
> + is set up there). In that case, we just return NULL.
> + */
> + if (cache_mngr)
> {
> - /*
> - if we only got a partial header in the last iteration,
> - get the other half now and process a full header.
> - */
> - if (unlikely(carry > 0))
> - {
> - DBUG_ASSERT(carry < LOG_EVENT_HEADER_LEN);
> + binlog_cache_data *cache_data=
> + cache_mngr->get_binlog_cache_data(use_trans_cache(this,
> is_transactional));
>
> - /* assemble both halves */
> - memcpy(&header[carry], (char *)cache->read_pos, LOG_EVENT_HEADER_LEN -
> carry);
> + rows= cache_data->pending();
> + }
> + return (rows);
> +}
>
> - /* fix end_log_pos */
> - val= uint4korr(&header[LOG_POS_OFFSET]) + group;
> - int4store(&header[LOG_POS_OFFSET], val);
> +/**
> + This function stores a pending row event into a cache which is specified
> + through the parameter @c is_transactional. Respectively, when it is @c
> + true, the pending event is stored into the transactional cache. Otherwise
> + into the non-transactional cache.
>
> - /* write the first half of the split header */
> - if (my_b_write(&log_file, header, carry))
> - return ER_ERROR_ON_WRITE;
> + @param evt a pointer to the row event.
> + @param is_transactional @c true indicates a transactional cache,
> + otherwise @c false a non-transactional.
> +*/
> +void
> +THD::binlog_set_pending_rows_event(Rows_log_event* ev, bool is_transactional)
> +{
> + if (thd_get_ha_data(this, binlog_hton) == NULL)
> + binlog_setup_trx_data();
>
> - /*
> - copy fixed second half of header to cache so the correct
> - version will be written later.
> - */
> - memcpy((char *)cache->read_pos, &header[carry], LOG_EVENT_HEADER_LEN -
> carry);
> + binlog_cache_mngr *const cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
>
> - /* next event header at ... */
> - hdr_offs = uint4korr(&header[EVENT_LEN_OFFSET]) - carry;
> + DBUG_ASSERT(cache_mngr);
>
> - carry= 0;
> - }
> + binlog_cache_data *cache_data=
> + cache_mngr->get_binlog_cache_data(use_trans_cache(this, is_transactional));
>
> - /* if there is anything to write, process it. */
> + cache_data->set_pending(ev);
> +}
>
> - if (likely(length > 0))
> - {
> - /*
> - process all event-headers in this (partial) cache.
> - if next header is beyond current read-buffer,
> - we'll get it later (though not necessarily in the
> - very next iteration, just "eventually").
> - */
> +/**
> + Decide on logging format to use for the statement and issue errors
> + or warnings as needed. The decision depends on the following
> + parameters:
>
> - while (hdr_offs < length)
> - {
> - /*
> - partial header only? save what we can get, process once
> - we get the rest.
> - */
> + - The logging mode, i.e., the value of binlog_format. Can be
> + statement, mixed, or row.
> +
> + - The type of statement. There are three types of statements:
> + "normal" safe statements; unsafe statements; and row injections.
> + An unsafe statement is one that, if logged in statement format,
> + might produce different results when replayed on the slave (e.g.,
> + INSERT DELAYED). A row injection is either a BINLOG statement, or
> + a row event executed by the slave's SQL thread.
> +
> + - The capabilities of tables modified by the statement. The
> + *capabilities vector* for a table is a set of flags associated
> + with the table. Currently, it only includes two flags: *row
> + capability flag* and *statement capability flag*.
> +
> + The row capability flag is set if and only if the engine can
> + handle row-based logging. The statement capability flag is set if
> + and only if the table can handle statement-based logging.
>
> - if (hdr_offs + LOG_EVENT_HEADER_LEN > length)
> - {
> - carry= length - hdr_offs;
> - memcpy(header, (char *)cache->read_pos + hdr_offs, carry);
> - length= hdr_offs;
> - }
> - else
> - {
> - /* we've got a full event-header, and it came in one piece */
> + Decision table for logging format
> + ---------------------------------
>
> - uchar *log_pos= (uchar *)cache->read_pos + hdr_offs + LOG_POS_OFFSET;
> + The following table summarizes how the format and generated
> + warning/error depends on the tables' capabilities, the statement
> + type, and the current binlog_format.
>
> - /* fix end_log_pos */
> - val= uint4korr(log_pos) + group;
> - int4store(log_pos, val);
> + Row capable N NNNNNNNNN YYYYYYYYY YYYYYYYYY
> + Statement capable N YYYYYYYYY NNNNNNNNN YYYYYYYYY
>
> - /* next event header at ... */
> - log_pos= (uchar *)cache->read_pos + hdr_offs + EVENT_LEN_OFFSET;
> - hdr_offs += uint4korr(log_pos);
> + Statement type * SSSUUUIII SSSUUUIII SSSUUUIII
>
> - }
> - }
> + binlog_format * SMRSMRSMR SMRSMRSMR SMRSMRSMR
>
> - /*
> - Adjust hdr_offs. Note that it may still point beyond the segment
> - read in the next iteration; if the current event is very long,
> - it may take a couple of read-iterations (and subsequent adjustments
> - of hdr_offs) for it to point into the then-current segment.
> - If we have a split header (!carry), hdr_offs will be set at the
> - beginning of the next iteration, overwriting the value we set here:
> - */
> - hdr_offs -= length;
> - }
> + Logged format - SS-S----- -RR-RR-RR SRRSRR-RR
> + Warning/Error 1 --2732444 5--5--6-- ---7--6--
>
> - /* Write data to the binary log file */
> - if (my_b_write(&log_file, cache->read_pos, length))
> - return ER_ERROR_ON_WRITE;
> - cache->read_pos=cache->read_end; // Mark buffer used up
> - } while ((length= my_b_fill(cache)));
> + Legend
> + ------
>
> - DBUG_ASSERT(carry == 0);
> + Row capable: N - Some table not row-capable, Y - All tables row-capable
> + Stmt capable: N - Some table not stmt-capable, Y - All tables stmt-capable
> + Statement type: (S)afe, (U)nsafe, or Row (I)njection
> + binlog_format: (S)TATEMENT, (M)IXED, or (R)OW
> + Logged format: (S)tatement or (R)ow
> + Warning/Error: Warnings and error messages are as follows:
>
> - if (sync_log)
> - return flush_and_sync(0);
> + 1. Error: Cannot execute statement: binlogging impossible since both
> + row-incapable engines and statement-incapable engines are
> + involved.
>
> - return 0; // All OK
> -}
> + 2. Error: Cannot execute statement: binlogging impossible since
> + BINLOG_FORMAT = ROW and at least one table uses a storage engine
> + limited to statement-logging.
>
> -/*
> - Helper function to get the error code of the query to be binlogged.
> - */
> -int query_error_code(THD *thd, bool not_killed)
> -{
> - int error;
> -
> - if (not_killed || (thd->killed == THD::KILL_BAD_DATA))
> - {
> - error= thd->is_error() ? thd->stmt_da->sql_errno() : 0;
> + 3. Error: Cannot execute statement: binlogging of unsafe statement
> + is impossible when storage engine is limited to statement-logging
> + and BINLOG_FORMAT = MIXED.
>
> - /* thd->stmt_da->sql_errno() might be ER_SERVER_SHUTDOWN or
> - ER_QUERY_INTERRUPTED, So here we need to make sure that error
> - is not set to these errors when specified not_killed by the
> - caller.
> - */
> - if (error == ER_SERVER_SHUTDOWN || error == ER_QUERY_INTERRUPTED)
> - error= 0;
> - }
> - else
> - {
> - /* killed status for DELAYED INSERT thread should never be used */
> - DBUG_ASSERT(!(thd->system_thread & SYSTEM_THREAD_DELAYED_INSERT));
> - error= thd->killed_errno();
> - }
> + 4. Error: Cannot execute row injection: binlogging impossible since
> + at least one table uses a storage engine limited to
> + statement-logging.
>
> - return error;
> -}
> + 5. Error: Cannot execute statement: binlogging impossible since
> + BINLOG_FORMAT = STATEMENT and at least one table uses a storage
> + engine limited to row-logging.
>
> -bool MYSQL_BIN_LOG::write_incident(THD *thd, bool lock)
> -{
> - uint error= 0;
> - DBUG_ENTER("MYSQL_BIN_LOG::write_incident");
> - LEX_STRING const write_error_msg=
> - { C_STRING_WITH_LEN("error writing to the binary log") };
> - Incident incident= INCIDENT_LOST_EVENTS;
> - Incident_log_event ev(thd, incident, write_error_msg);
> - if (lock)
> - mysql_mutex_lock(&LOCK_log);
> - error= ev.write(&log_file);
> - if (lock)
> - {
> - if (!error && !(error= flush_and_sync(0)))
> - {
> - signal_update();
> - rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED);
> - }
> - mysql_mutex_unlock(&LOCK_log);
> - }
> - DBUG_RETURN(error);
> -}
> + 6. Error: Cannot execute row injection: binlogging impossible since
> + BINLOG_FORMAT = STATEMENT.
>
> -/**
> - Write a cached log entry to the binary log.
> - - To support transaction over replication, we wrap the transaction
> - with BEGIN/COMMIT or BEGIN/ROLLBACK in the binary log.
> - We want to write a BEGIN/ROLLBACK block when a non-transactional table
> - was updated in a transaction which was rolled back. This is to ensure
> - that the same updates are run on the slave.
> + 7. Warning: Unsafe statement binlogged in statement format since
> + BINLOG_FORMAT = STATEMENT.
>
> - @param thd
> - @param cache The cache to copy to the binlog
> - @param commit_event The commit event to print after writing the
> - contents of the cache.
> - @param incident Defines if an incident event should be created to
> - notify that some non-transactional changes did
> - not get into the binlog.
> + In addition, we can produce the following error (not depending on
> + the variables of the decision diagram):
>
> - @note
> - We only come here if there is something in the cache.
> - @note
> - The thing in the cache is always a complete transaction.
> - @note
> - 'cache' needs to be reinitialized after this functions returns.
> + 8. Error: Cannot execute statement: binlogging impossible since more
> + than one engine is involved and at least one engine is
> + self-logging.
> +
> + For each error case above, the statement is prevented from being
> + logged, we report an error, and roll back the statement. For
> + warnings, we set the thd->binlog_flags variable: the warning will be
> + printed only if the statement is successfully logged.
> +
> + @see THD::binlog_query
> +
> + @param[in] thd Client thread
> + @param[in] tables Tables involved in the query
> +
> + @retval 0 No error; statement can be logged.
> + @retval -1 One of the error conditions above applies (1, 2, 4, 5, or 6).
> */
>
> -bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event,
> - bool incident)
> +int THD::decide_logging_format(TABLE_LIST *tables)
> {
> - DBUG_ENTER("MYSQL_BIN_LOG::write(THD *, IO_CACHE *, Log_event *)");
> - mysql_mutex_lock(&LOCK_log);
> + DBUG_ENTER("THD::decide_logging_format");
> + DBUG_PRINT("info", ("query: %s", query()));
> + DBUG_PRINT("info", ("variables.binlog_format: %u",
> + variables.binlog_format));
> + DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags(): 0x%x",
> + lex->get_stmt_unsafe_flags()));
>
> - DBUG_ASSERT(is_open());
> - if (likely(is_open())) // Should always be true
> + /*
> + We should not decide logging format if the binlog is closed or
> + binlogging is off, or if the statement is filtered out from the
> + binlog by filtering rules.
> + */
> + if (mysql_bin_log.is_open() && (variables.option_bits &
> OPTION_BIN_LOG) &&
> + !(variables.binlog_format == BINLOG_FORMAT_STMT &&
> + !binlog_filter->db_ok(db)))
> {
> /*
> - We only bother to write to the binary log if there is anything
> - to write.
> - */
> - if (my_b_tell(cache) > 0)
> + Compute one bit field with the union of all the engine
> + capabilities, and one with the intersection of all the engine
> + capabilities.
> + */
> + handler::Table_flags flags_write_some_set= 0;
> + handler::Table_flags flags_some_set= 0;
> + handler::Table_flags flags_write_all_set=
> + HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE;
> +
> + /*
> + If different types of engines are about to be updated.
> + For example: Innodb and Falcon; Innodb and MyIsam.
> + */
> + my_bool multi_write_engine= FALSE;
> + /*
> + If different types of engines are about to be accessed
> + and any of them is about to be updated. For example:
> + Innodb and Falcon; Innodb and MyIsam.
> + */
> + my_bool multi_access_engine= FALSE;
> + /*
> + If non-transactional and transactional engines are about
> + to be accessed and any of them is about to be updated.
> + For example: Innodb and MyIsam.
> + */
> + my_bool trans_non_trans_access_engines= FALSE;
> + /*
> + If all engines that are about to be updated are
> + transactional.
> + */
> + my_bool all_trans_write_engines= TRUE;
> + TABLE* prev_write_table= NULL;
> + TABLE* prev_access_table= NULL;
> +
> +#ifndef DBUG_OFF
> + {
> + static const char *prelocked_mode_name[] = {
> + "NON_PRELOCKED",
> + "PRELOCKED",
> + "PRELOCKED_UNDER_LOCK_TABLES",
> + };
> + DBUG_PRINT("debug", ("prelocked_mode: %s",
> + prelocked_mode_name[locked_tables_mode]));
> + }
> +#endif
> +
> + /*
> + Get the capabilities vector for all involved storage engines and
> + mask out the flags for the binary log.
> + */
> + for (TABLE_LIST *table= tables; table; table= table->next_global)
> {
> + if (table->placeholder())
> + continue;
> + if (table->table->s->table_category == TABLE_CATEGORY_PERFORMANCE)
> + lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_TABLE);
> + handler::Table_flags const flags=
> table->table->file->ha_table_flags();
> + DBUG_PRINT("info", ("table: %s; ha_table_flags: 0x%llx",
> + table->table_name, flags));
> + if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
> + {
> + if (prev_write_table && prev_write_table->file->ht !=
> + table->table->file->ht)
> + multi_write_engine= TRUE;
> + /*
> + Every temporary table must be always written to the binary
> + log in transaction boundaries and as such we artificially
> + classify them as transactional.
> +
> + Indirectly, this avoids classifying a temporary table created
> + on a non-transactional engine as unsafe when it is modified
> + after any transactional table:
> +
> + BEGIN;
> + INSERT INTO innodb_t VALUES (1);
> + INSERT INTO myisam_t_temp VALUES (1);
> + COMMIT;
> +
> + BINARY LOG:
> +
> + BEGIN;
> + INSERT INTO innodb_t VALUES (1);
> + INSERT INTO myisam_t_temp VALUES (1);
> + COMMIT;
> + */
> + all_trans_write_engines= all_trans_write_engines &&
> + (table->table->file->has_transactions()
> ||
> + table->table->s->tmp_table);
> + prev_write_table= table->table;
> + flags_write_all_set &= flags;
> + flags_write_some_set |= flags;
> + }
> + flags_some_set |= flags;
> /*
> - Log "BEGIN" at the beginning of every transaction. Here, a
> - transaction is either a BEGIN..COMMIT block or a single
> - statement in autocommit mode.
> - */
> - Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), TRUE, FALSE, TRUE, 0);
> - if (qinfo.write(&log_file))
> - goto err;
> - DBUG_EXECUTE_IF("crash_before_writing_xid",
> - {
> - if ((write_error= write_cache(cache, false, true)))
> - DBUG_PRINT("info", ("error writing binlog cache: %d",
> - write_error));
> - DBUG_PRINT("info", ("crashing before writing xid"));
> - DBUG_ABORT();
> - });
> + The mixture of non-transactional and transactional tables must
> + identified and classified as unsafe. However, a temporary table
> + must be always handled as a transactional table. Based on that,
> + we have the following statements classified as mixed and by
> + consequence as unsafe:
>
> - if ((write_error= write_cache(cache, false, false)))
> - goto err;
> + 1: INSERT INTO myisam_t SELECT * FROM innodb_t;
>
> - if (commit_event && commit_event->write(&log_file))
> - goto err;
> + 2: INSERT INTO innodb_t SELECT * FROM myisam_t;
>
> - if (incident && write_incident(thd, FALSE))
> - goto err;
> + 3: INSERT INTO myisam_t SELECT * FROM myisam_t_temp;
>
> - bool synced= 0;
> - if (flush_and_sync(&synced))
> - goto err;
> - DBUG_EXECUTE_IF("half_binlogged_transaction", DBUG_ABORT(););
> - 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;
> - }
> + 4: INSERT INTO myisam_t_temp SELECT * FROM myisam_t;
>
> - if (RUN_HOOK(binlog_storage, after_flush,
> - (thd, log_file_name, log_file.pos_in_file, synced)))
> + 5: CREATE TEMPORARY TABLE myisam_t_temp SELECT * FROM mysiam_t;
> +
> + The following statements are not considered mixed and as such
> + are safe:
> +
> + 1: INSERT INTO innodb_t SELECT * FROM myisam_t_temp;
> +
> + 2: INSERT INTO myisam_t_temp SELECT * FROM innodb_t_temp;
> + */
> + if (!trans_non_trans_access_engines && prev_access_table &&
> + (lex->sql_command != SQLCOM_CREATE_TABLE ||
> + (lex->sql_command == SQLCOM_CREATE_TABLE &&
> + (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))))
> {
> - sql_print_error("Failed to run 'after_flush' hooks");
> - write_error=1;
> - goto err;
> + my_bool prev_trans;
> + my_bool act_trans;
> + if (prev_access_table->s->tmp_table ||
> table->table->s->tmp_table)
> + {
> + prev_trans= prev_access_table->s->tmp_table ? TRUE :
> + prev_access_table->file->has_transactions();
> + act_trans= table->table->s->tmp_table ? TRUE :
> + table->table->file->has_transactions();
> + }
> + else
> + {
> + prev_trans= prev_access_table->file->has_transactions();
> + act_trans= table->table->file->has_transactions();
> + }
> + trans_non_trans_access_engines= (prev_trans != act_trans);
> + multi_access_engine= TRUE;
> }
> -
> - signal_update();
> + thread_temporary_used |= table->table->s->tmp_table;
> + prev_access_table= table->table;
> }
>
> + DBUG_PRINT("info", ("flags_write_all_set: 0x%llx", flags_write_all_set));
> + DBUG_PRINT("info", ("flags_write_some_set: 0x%llx", flags_write_some_set));
> + DBUG_PRINT("info", ("flags_some_set: 0x%llx", flags_some_set));
> + DBUG_PRINT("info", ("multi_write_engine: %d", multi_write_engine));
> + DBUG_PRINT("info", ("multi_access_engine: %d", multi_access_engine));
> + DBUG_PRINT("info", ("trans_non_trans_access_engines: %d",
> + trans_non_trans_access_engines));
> +
> + int error= 0;
> + int unsafe_flags;
> +
> /*
> - if commit_event is Xid_log_event, increase the number of
> - prepared_xids (it's decreasd in ::unlog()). Binlog cannot be rotated
> - if there're prepared xids in it - see the comment in new_file() for
> - an explanation.
> - If the commit_event is not Xid_log_event (then it's a Query_log_event)
> - rotate binlog, if necessary.
> - */
> - if (commit_event && commit_event->get_type_code() == XID_EVENT)
> - {
> - mysql_mutex_lock(&LOCK_prep_xids);
> - prepared_xids++;
> - mysql_mutex_unlock(&LOCK_prep_xids);
> - }
> - else
> - rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED);
> - }
> - mysql_mutex_unlock(&LOCK_log);
> + Set the statement as unsafe if:
>
> - DBUG_RETURN(0);
> + . it is a mixed statement, i.e. access transactional and non-transactional
> + tables, and update any of them;
>
> -err:
> - if (!write_error)
> - {
> - write_error= 1;
> - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
> - }
> - mysql_mutex_unlock(&LOCK_log);
> - DBUG_RETURN(1);
> -}
> + or:
>
> + . an early statement updated a transactional table;
> + . and, the current statement updates a non-transactional table.
>
> -/**
> - Wait until we get a signal that the relay log has been updated.
> + Any mixed statement is classified as unsafe to ensure that mixed mode is
> + completely safe. Consider the following example to understand why we
> + decided to do this:
>
> - @param thd Thread variable
> + Note that mixed statements such as
>
> - @note
> - One must have a lock on LOCK_log before calling this function.
> - This lock will be released before return! That's required by
> - THD::enter_cond() (see NOTES in sql_class.h).
> -*/
> + 1: INSERT INTO myisam_t SELECT * FROM innodb_t;
>
> -void MYSQL_BIN_LOG::wait_for_update_relay_log(THD* thd)
> -{
> - const char *old_msg;
> - DBUG_ENTER("wait_for_update_relay_log");
> + 2: INSERT INTO innodb_t SELECT * FROM myisam_t;
>
> - old_msg= thd->enter_cond(&update_cond, &LOCK_log,
> - "Slave has read all relay log; "
> - "waiting for the slave I/O "
> - "thread to update it" );
> - mysql_cond_wait(&update_cond, &LOCK_log);
> - thd->exit_cond(old_msg);
> - DBUG_VOID_RETURN;
> -}
> + 3: INSERT INTO myisam_t SELECT * FROM myisam_t_temp;
>
> -/**
> - Wait until we get a signal that the binary log has been updated.
> - Applies to master only.
> -
> - NOTES
> - @param[in] thd a THD struct
> - @param[in] timeout a pointer to a timespec;
> - NULL means to wait w/o timeout.
> - @retval 0 if got signalled on update
> - @retval non-0 if wait timeout elapsed
> - @note
> - LOCK_log must be taken before calling this function.
> - LOCK_log is being released while the thread is waiting.
> - LOCK_log is released by the caller.
> -*/
> + 4: INSERT INTO myisam_t_temp SELECT * FROM myisam_t;
> +
> + 5: CREATE TEMPORARY TABLE myisam_t_temp SELECT * FROM mysiam_t;
>
> -int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd,
> - const struct timespec *timeout)
> -{
> - int ret= 0;
> - const char* old_msg = thd->proc_info;
> - DBUG_ENTER("wait_for_update_bin_log");
> - old_msg= thd->enter_cond(&update_cond, &LOCK_log,
> - "Master has sent all binlog to slave; "
> - "waiting for binlog to be updated");
> - if (!timeout)
> - mysql_cond_wait(&update_cond, &LOCK_log);
> - else
> - ret= mysql_cond_timedwait(&update_cond, &LOCK_log,
> - const_cast<struct timespec *>(timeout));
> - DBUG_RETURN(ret);
> -}
> + are classified as unsafe to ensure that in mixed mode the execution is
> + completely safe and equivalent to the row mode. Consider the following
> + statements and sessions (connections) to understand the reason:
>
> + con1: INSERT INTO innodb_t VALUES (1);
> + con1: INSERT INTO innodb_t VALUES (100);
>
> -/**
> - Close the log file.
> + con1: BEGIN
> + con2: INSERT INTO myisam_t SELECT * FROM innodb_t;
> + con1: INSERT INTO innodb_t VALUES (200);
> + con1: COMMIT;
>
> - @param exiting Bitmask for one or more of the following bits:
> - - LOG_CLOSE_INDEX : if we should close the index file
> - - LOG_CLOSE_TO_BE_OPENED : if we intend to call open
> - at once after close.
> - - LOG_CLOSE_STOP_EVENT : write a 'stop' event to the log
> + The point is that the concurrent statements may be written into the binary
> log
> + in a way different from the execution. For example,
>
> - @note
> - One can do an open on the object at once after doing a close.
> - The internal structures are not freed until cleanup() is called
> -*/
> + BINARY LOG:
>
> -void MYSQL_BIN_LOG::close(uint exiting)
> -{ // One can't set log_type here!
> - DBUG_ENTER("MYSQL_BIN_LOG::close");
> - DBUG_PRINT("enter",("exiting: %d", (int) exiting));
> - if (log_state == LOG_OPENED)
> - {
> -#ifdef HAVE_REPLICATION
> - if (log_type == LOG_BIN && !no_auto_events &&
> - (exiting & LOG_CLOSE_STOP_EVENT))
> - {
> - Stop_log_event s;
> - s.write(&log_file);
> - bytes_written+= s.data_written;
> - signal_update();
> - }
> -#endif /* HAVE_REPLICATION */
> + con2: BEGIN;
> + con2: INSERT INTO myisam_t SELECT * FROM innodb_t;
> + con2: COMMIT;
> + con1: BEGIN
> + con1: INSERT INTO innodb_t VALUES (200);
> + con1: COMMIT;
>
> - /* don't pwrite in a file opened with O_APPEND - it doesn't work */
> - if (log_file.type == WRITE_CACHE && log_type == LOG_BIN)
> + ....
> +
> + or
> +
> + BINARY LOG:
> +
> + con1: BEGIN
> + con1: INSERT INTO innodb_t VALUES (200);
> + con1: COMMIT;
> + con2: BEGIN;
> + con2: INSERT INTO myisam_t SELECT * FROM innodb_t;
> + con2: COMMIT;
> +
> + Clearly, this may become a problem in STMT mode and setting the statement
> + as unsafe will make rows to be written into the binary log in MIXED mode
> + and as such the problem will not stand.
> +
> + In STMT mode, although such statement is classified as unsafe, i.e.
> +
> + INSERT INTO myisam_t SELECT * FROM innodb_t;
> +
> + there is no enough information to avoid writing it outside the boundaries
> + of a transaction. This is not a problem if we are considering snapshot
> + isolation level but if we have pure repeatable read or serializable the
> + lock history on the slave will be different from the master.
> + */
> + if (trans_non_trans_access_engines)
> + lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_MIXED_STATEMENT);
> + else if (trans_has_updated_trans_table(this) &&
> !all_trans_write_engines)
> + lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_NONTRANS_AFTER_TRANS);
> +
> + /*
> + If more than one engine is involved in the statement and at
> + least one is doing it's own logging (is *self-logging*), the
> + statement cannot be logged atomically, so we generate an error
> + rather than allowing the binlog to become corrupt.
> + */
> + if (multi_write_engine &&
> + (flags_write_some_set & HA_HAS_OWN_BINLOGGING))
> + my_error((error= ER_BINLOG_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE),
> + MYF(0));
> + else if (multi_access_engine && flags_some_set &
> HA_HAS_OWN_BINLOGGING)
> +
> lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE);
> +
> + /* both statement-only and row-only engines involved */
> + if ((flags_write_all_set & (HA_BINLOG_STMT_CAPABLE | HA_BINLOG_ROW_CAPABLE))
> == 0)
> {
> - my_off_t offset= BIN_LOG_HEADER_SIZE + FLAGS_OFFSET;
> - my_off_t org_position= mysql_file_tell(log_file.file, MYF(0));
> - uchar flags= 0; // clearing LOG_EVENT_BINLOG_IN_USE_F
> - mysql_file_pwrite(log_file.file, &flags, 1, offset, MYF(0));
> /*
> - Restore position so that anything we have in the IO_cache is written
> - to the correct position.
> - We need the seek here, as mysql_file_pwrite() is not guaranteed to keep the
> - original position on system that doesn't support pwrite().
> + 1. Error: Binary logging impossible since both row-incapable
> + engines and statement-incapable engines are involved
> */
> - mysql_file_seek(log_file.file, org_position, MY_SEEK_SET, MYF(0));
> + my_error((error= ER_BINLOG_ROW_ENGINE_AND_STMT_ENGINE), MYF(0));
> }
> -
> - /* this will cleanup IO_CACHE, sync and close the file */
> - MYSQL_LOG::close(exiting);
> - }
> -
> - /*
> - 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 & LOG_CLOSE_INDEX) && my_b_inited(&index_file))
> - {
> - end_io_cache(&index_file);
> - if (mysql_file_close(index_file.file, MYF(0)) < 0 && ! write_error)
> + /* statement-only engines involved */
> + else if ((flags_write_all_set & HA_BINLOG_ROW_CAPABLE) == 0)
> {
> - write_error= 1;
> - sql_print_error(ER(ER_ERROR_ON_WRITE), index_file_name, errno);
> + if (lex->is_stmt_row_injection())
> + {
> + /*
> + 4. Error: Cannot execute row injection since table uses
> + storage engine limited to statement-logging
> + */
> + my_error((error= ER_BINLOG_ROW_INJECTION_AND_STMT_ENGINE), MYF(0));
> + }
> + else if (variables.binlog_format == BINLOG_FORMAT_ROW)
> + {
> + /*
> + 2. Error: Cannot modify table that uses a storage engine
> + limited to statement-logging when BINLOG_FORMAT = ROW
> + */
> + my_error((error= ER_BINLOG_ROW_MODE_AND_STMT_ENGINE), MYF(0));
> + }
> + else if ((unsafe_flags= lex->get_stmt_unsafe_flags()) != 0)
> + {
> + /*
> + 3. Error: Cannot execute statement: binlogging of unsafe
> + statement is impossible when storage engine is limited to
> + statement-logging and BINLOG_FORMAT = MIXED.
> + */
> + for (int unsafe_type= 0;
> + unsafe_type < LEX::BINLOG_STMT_UNSAFE_COUNT;
> + unsafe_type++)
> + if (unsafe_flags & (1 << unsafe_type))
> + my_error((error= ER_BINLOG_UNSAFE_AND_STMT_ENGINE), MYF(0),
> + ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]));
> + }
> + /* log in statement format! */
> + }
> + /* no statement-only engines */
> + else
> + {
> + /* binlog_format = STATEMENT */
> + if (variables.binlog_format == BINLOG_FORMAT_STMT)
> + {
> + if (lex->is_stmt_row_injection())
> + {
> + /*
> + 6. Error: Cannot execute row injection since
> + BINLOG_FORMAT = STATEMENT
> + */
> + my_error((error= ER_BINLOG_ROW_INJECTION_AND_STMT_MODE), MYF(0));
> + }
> + else if ((flags_write_all_set & HA_BINLOG_STMT_CAPABLE) == 0)
> + {
> + /*
> + 5. Error: Cannot modify table that uses a storage engine
> + limited to row-logging when binlog_format = STATEMENT
> + */
> + my_error((error= ER_BINLOG_STMT_MODE_AND_ROW_ENGINE), MYF(0), "");
> + }
> + else if ((unsafe_flags= lex->get_stmt_unsafe_flags()) != 0)
> + {
> + /*
> + 7. Warning: Unsafe statement logged as statement due to
> + binlog_format = STATEMENT
> + */
> + binlog_unsafe_warning_flags|= unsafe_flags;
> + DBUG_PRINT("info", ("Scheduling warning to be issued by "
> + "binlog_query: '%s'",
> + ER(ER_BINLOG_UNSAFE_STATEMENT)));
> + DBUG_PRINT("info", ("binlog_unsafe_warning_flags: 0x%x",
> + binlog_unsafe_warning_flags));
> + }
> + /* log in statement format! */
> + }
> + /* No statement-only engines and binlog_format != STATEMENT.
> + I.e., nothing prevents us from row logging if needed. */
> + else
> + {
> + if (lex->is_stmt_unsafe() || lex->is_stmt_row_injection()
> + || (flags_write_all_set & HA_BINLOG_STMT_CAPABLE) == 0)
> + {
> + /* log in row format! */
> + set_current_stmt_binlog_format_row_if_mixed();
> + }
> + }
> }
> - }
> - log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED :
> LOG_CLOSED;
> - safeFree(name);
> - DBUG_VOID_RETURN;
> -}
>
> + if (error) {
> + DBUG_PRINT("info", ("decision: no logging since an error was generated"));
> + DBUG_RETURN(-1);
> + }
> + DBUG_PRINT("info", ("decision: logging in %s format",
> + is_current_stmt_binlog_format_row() ?
> + "ROW" : "STATEMENT"));
> + }
> +#ifndef DBUG_OFF
> + else
> + DBUG_PRINT("info", ("decision: no logging since "
> + "mysql_bin_log.is_open() = %d "
> + "and (options & OPTION_BIN_LOG) = 0x%llx "
> + "and binlog_format = %u "
> + "and binlog_filter->db_ok(db) = %d",
> + mysql_bin_log.is_open(),
> + (variables.option_bits & OPTION_BIN_LOG),
> + variables.binlog_format,
> + binlog_filter->db_ok(db)));
> +#endif
>
> -void MYSQL_BIN_LOG::set_max_size(ulong max_size_arg)
> -{
> - /*
> - We need to take locks, otherwise this may happen:
> - new_file() is called, calls open(old_max_size), then before open() starts,
> - set_max_size() sets max_size to max_size_arg, then open() starts and
> - uses the old_max_size argument, so max_size_arg has been overwritten and
> - it's like if the SET command was never run.
> - */
> - DBUG_ENTER("MYSQL_BIN_LOG::set_max_size");
> - mysql_mutex_lock(&LOCK_log);
> - if (is_open())
> - max_size= max_size_arg;
> - mysql_mutex_unlock(&LOCK_log);
> - DBUG_VOID_RETURN;
> + DBUG_RETURN(0);
> }
>
>
> -void MYSQL_BIN_LOG::signal_update()
> -{
> - DBUG_ENTER("MYSQL_BIN_LOG::signal_update");
> - signal_cnt++;
> - mysql_cond_broadcast(&update_cond);
> - DBUG_VOID_RETURN;
> -}
> +/*
> + Implementation of interface to write rows to the binary log through the
> + thread. The thread is responsible for writing the rows it has
> + inserted/updated/deleted.
> +*/
>
> -/****** transaction coordinator log for 2pc - binlog() based solution ******/
> -#define TC_LOG_BINLOG MYSQL_BIN_LOG
> +#ifndef MYSQL_CLIENT
>
> -/**
> - @todo
> - keep in-memory list of prepared transactions
> - (add to list in log(), remove on unlog())
> - and copy it to the new binlog if rotated
> - but let's check the behaviour of tc_log_page_waits first!
> -*/
> +/*
> + Template member function for ensuring that there is an rows log
> + event of the apropriate type before proceeding.
> +
> + PRE CONDITION:
> + - Events of type 'RowEventT' have the type code 'type_code'.
> +
> + POST CONDITION:
> + If a non-NULL pointer is returned, the pending event for thread 'thd' will
> + be an event of type 'RowEventT' (which have the type code 'type_code')
> + will either empty or have enough space to hold 'needed' bytes. In
> + addition, the columns bitmap will be correct for the row, meaning that
> + the pending event will be flushed if the columns in the event differ from
> + the columns suppled to the function.
> +
> + RETURNS
> + If no error, a non-NULL pending event (either one which already existed or
> + the newly created one).
> + If error, NULL.
> + */
>
> -int TC_LOG_BINLOG::open(const char *opt_name)
> +template <class RowsEventT> Rows_log_event*
> +THD::binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id,
> + MY_BITMAP const* cols,
> + size_t colcnt,
> + size_t needed,
> + bool is_transactional,
> + RowsEventT *hint __attribute__((unused)))
> {
> - LOG_INFO log_info;
> - int error= 1;
> + DBUG_ENTER("binlog_prepare_pending_rows_event");
> + /* Pre-conditions */
> + DBUG_ASSERT(table->s->table_map_id != ~0UL);
>
> - DBUG_ASSERT(total_ha_2pc > 1);
> - DBUG_ASSERT(opt_name && opt_name[0]);
> + /* Fetch the type code for the RowsEventT template parameter */
> + int const type_code= RowsEventT::TYPE_CODE;
>
> - mysql_mutex_init(key_BINLOG_LOCK_prep_xids,
> - &LOCK_prep_xids, MY_MUTEX_INIT_FAST);
> - mysql_cond_init(key_BINLOG_COND_prep_xids, &COND_prep_xids, 0);
> + /*
> + There is no good place to set up the transactional data, so we
> + have to do it here.
> + */
> + if (binlog_setup_trx_data())
> + DBUG_RETURN(NULL);
> +
> + Rows_log_event* pending= binlog_get_pending_rows_event(is_transactional);
> +
> + if (unlikely(pending && !pending->is_valid()))
> + DBUG_RETURN(NULL);
> +
> + /*
> + Check if the current event is non-NULL and a write-rows
> + event. Also check if the table provided is mapped: if it is not,
> + then we have switched to writing to a new table.
> + If there is no pending event, we need to create one. If there is a pending
> + event, but it's not about the same table id, or not of the same type
> + (between Write, Update and Delete), or not the same affected columns, or
> + going to be too big, flush this event to disk and create a new pending
> + event.
> + */
> + if (!pending ||
> + pending->server_id != serv_id ||
> + pending->get_table_id() != table->s->table_map_id ||
> + pending->get_type_code() != type_code ||
> + pending->get_data_size() + needed > opt_binlog_rows_event_max_size ||
> + pending->get_width() != colcnt ||
> + !bitmap_cmp(pending->get_cols(), cols))
> + {
> + /* Create a new RowsEventT... */
> + Rows_log_event* const
> + ev= new RowsEventT(this, table, table->s->table_map_id, cols,
> + is_transactional);
> + if (unlikely(!ev))
> + DBUG_RETURN(NULL);
> + ev->server_id= serv_id; // I don't like this, it's too easy to forget.
> + /*
> + flush the pending event and replace it with the newly created
> + event...
> + */
> + if (unlikely(
> + mysql_bin_log.flush_and_set_pending_rows_event(this, ev,
> + is_transactional)))
> + {
> + delete ev;
> + DBUG_RETURN(NULL);
> + }
>
> - if (!my_b_inited(&index_file))
> - {
> - /* There was a failure to open the index file, can't open the binlog */
> - cleanup();
> - return 1;
> + DBUG_RETURN(ev); /* This is the new pending event */
> }
> + DBUG_RETURN(pending); /* This is the current pending event */
> +}
>
> - if (using_heuristic_recover())
> - {
> - /* generate a new binlog to mask a corrupted one */
> - open(opt_name, LOG_BIN, 0, WRITE_CACHE, 0, max_binlog_size, 0, TRUE);
> - cleanup();
> - return 1;
> - }
> +#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
> +/*
> + Instantiate the versions we need, we have -fno-implicit-template as
> + compiling option.
> +*/
> +template Rows_log_event*
> +THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*,
> + size_t, size_t, bool,
> + Write_rows_log_event*);
> +
> +template Rows_log_event*
> +THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*,
> + size_t colcnt, size_t, bool,
> + Delete_rows_log_event *);
> +
> +template Rows_log_event*
> +THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*,
> + size_t colcnt, size_t, bool,
> + Update_rows_log_event *);
> +#endif
>
> - if ((error= find_log_pos(&log_info, NullS, 1)))
> - {
> - if (error != LOG_INFO_EOF)
> - sql_print_error("find_log_pos() failed (error: %d)", error);
> - else
> - error= 0;
> - goto err;
> - }
> +namespace {
> + /**
> + Class to handle temporary allocation of memory for row data.
> +
> + The responsibilities of the class is to provide memory for
> + packing one or two rows of packed data (depending on what
> + constructor is called).
> +
> + In order to make the allocation more efficient for "simple" rows,
> + i.e., rows that do not contain any blobs, a pointer to the
> + allocated memory is of memory is stored in the table structure
> + for simple rows. If memory for a table containing a blob field
> + is requested, only memory for that is allocated, and subsequently
> + released when the object is destroyed.
>
> - {
> - const char *errmsg;
> - IO_CACHE log;
> - File file;
> - Log_event *ev=0;
> - Format_description_log_event fdle(BINLOG_VERSION);
> - char log_name[FN_REFLEN];
> + */
> + class Row_data_memory {
> + public:
> + /**
> + Build an object to keep track of a block-local piece of memory
> + for storing a row of data.
>
> - if (! fdle.is_valid())
> - goto err;
> + @param table
> + Table where the pre-allocated memory is stored.
>
> - do
> + @param length
> + Length of data that is needed, if the record contain blobs.
> + */
> + Row_data_memory(TABLE *table, size_t const len1)
> + : m_memory(0)
> {
> - strmake(log_name, log_info.log_file_name, sizeof(log_name)-1);
> - } while (!(error= find_next_log(&log_info, 1)));
> +#ifndef DBUG_OFF
> + m_alloc_checked= FALSE;
> +#endif
> + allocate_memory(table, len1);
> + m_ptr[0]= has_memory() ? m_memory : 0;
> + m_ptr[1]= 0;
> + }
>
> - if (error != LOG_INFO_EOF)
> + Row_data_memory(TABLE *table, size_t const len1, size_t const len2)
> + : m_memory(0)
> {
> - sql_print_error("find_log_pos() failed (error: %d)", error);
> - goto err;
> +#ifndef DBUG_OFF
> + m_alloc_checked= FALSE;
> +#endif
> + allocate_memory(table, len1 + len2);
> + m_ptr[0]= has_memory() ? m_memory : 0;
> + m_ptr[1]= has_memory() ? m_memory + len1 : 0;
> }
>
> - if ((file= open_binlog(&log, log_name, &errmsg)) < 0)
> + ~Row_data_memory()
> {
> - sql_print_error("%s", errmsg);
> - goto err;
> + if (m_memory != 0 && m_release_memory_on_destruction)
> + my_free((uchar*) m_memory, MYF(MY_WME));
> + }
> +
> + /**
> + Is there memory allocated?
> +
> + @retval true There is memory allocated
> + @retval false Memory allocation failed
> + */
> + bool has_memory() const {
> +#ifndef DBUG_OFF
> + m_alloc_checked= TRUE;
> +#endif
> + return m_memory != 0;
> + }
> +
> + uchar *slot(uint s)
> + {
> + DBUG_ASSERT(s < sizeof(m_ptr)/sizeof(*m_ptr));
> + DBUG_ASSERT(m_ptr[s] != 0);
> + DBUG_ASSERT(m_alloc_checked == TRUE);
> + return m_ptr[s];
> + }
> +
> + private:
> + void allocate_memory(TABLE *const table, size_t const total_length)
> + {
> + if (table->s->blob_fields == 0)
> + {
> + /*
> + The maximum length of a packed record is less than this
> + length. We use this value instead of the supplied length
> + when allocating memory for records, since we don't know how
> + the memory will be used in future allocations.
> +
> + Since table->s->reclength is for unpacked records, we have
> + to add two bytes for each field, which can potentially be
> + added to hold the length of a packed field.
> + */
> + size_t const maxlen= table->s->reclength + 2 *
> table->s->fields;
> +
> + /*
> + Allocate memory for two records if memory hasn't been
> + allocated. We allocate memory for two records so that it can
> + be used when processing update rows as well.
> + */
> + if (table->write_row_record == 0)
> + table->write_row_record=
> + (uchar *) alloc_root(&table->mem_root, 2 * maxlen);
> + m_memory= table->write_row_record;
> + m_release_memory_on_destruction= FALSE;
> + }
> + else
> + {
> + m_memory= (uchar *) my_malloc(total_length, MYF(MY_WME));
> + m_release_memory_on_destruction= TRUE;
> + }
> }
>
> - if ((ev= Log_event::read_log_event(&log, 0, &fdle)) &&
> - ev->get_type_code() == FORMAT_DESCRIPTION_EVENT &&
> - ev->flags & LOG_EVENT_BINLOG_IN_USE_F)
> - {
> - sql_print_information("Recovering after a crash using %s", opt_name);
> - error= recover(&log, (Format_description_log_event *)ev);
> - }
> - else
> - error=0;
> +#ifndef DBUG_OFF
> + mutable bool m_alloc_checked;
> +#endif
> + bool m_release_memory_on_destruction;
> + uchar *m_memory;
> + uchar *m_ptr[2];
> + };
> +}
> +
> +
> +int THD::binlog_write_row(TABLE* table, bool is_trans,
> + MY_BITMAP const* cols, size_t colcnt,
> + uchar const *record)
> +{
> + DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
> mysql_bin_log.is_open());
> +
> + /*
> + Pack records into format for transfer. We are allocating more
> + memory than needed, but that doesn't matter.
> + */
> + Row_data_memory memory(table, max_row_length(table, record));
> + if (!memory.has_memory())
> + return HA_ERR_OUT_OF_MEM;
> +
> + uchar *row_data= memory.slot(0);
>
> - delete ev;
> - end_io_cache(&log);
> - mysql_file_close(file, MYF(MY_WME));
> + size_t const len= pack_row(table, cols, row_data, record);
>
> - if (error)
> - goto err;
> - }
> + Rows_log_event* const ev=
> + binlog_prepare_pending_rows_event(table, server_id, cols, colcnt,
> + len, is_trans,
> + static_cast<Write_rows_log_event*>(0));
>
> -err:
> - return error;
> -}
> + if (unlikely(ev == 0))
> + return HA_ERR_OUT_OF_MEM;
>
> -/** This is called on shutdown, after ha_panic. */
> -void TC_LOG_BINLOG::close()
> -{
> - DBUG_ASSERT(prepared_xids==0);
> - mysql_mutex_destroy(&LOCK_prep_xids);
> - mysql_cond_destroy(&COND_prep_xids);
> + return ev->add_row_data(row_data, len);
> }
>
> -/**
> - @todo
> - group commit
> +int THD::binlog_update_row(TABLE* table, bool is_trans,
> + MY_BITMAP const* cols, size_t colcnt,
> + const uchar *before_record,
> + const uchar *after_record)
> +{
> + DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
> mysql_bin_log.is_open());
> +
> + size_t const before_maxlen = max_row_length(table, before_record);
> + size_t const after_maxlen = max_row_length(table, after_record);
> +
> + Row_data_memory row_data(table, before_maxlen, after_maxlen);
> + if (!row_data.has_memory())
> + return HA_ERR_OUT_OF_MEM;
> +
> + uchar *before_row= row_data.slot(0);
> + uchar *after_row= row_data.slot(1);
> +
> + size_t const before_size= pack_row(table, cols, before_row,
> + before_record);
> + size_t const after_size= pack_row(table, cols, after_row,
> + after_record);
>
> - @retval
> - 0 error
> - @retval
> - 1 success
> -*/
> -int TC_LOG_BINLOG::log_xid(THD *thd, my_xid xid)
> -{
> - DBUG_ENTER("TC_LOG_BINLOG::log");
> - Xid_log_event xle(thd, xid);
> - binlog_cache_mngr *cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> /*
> - We always commit the entire transaction when writing an XID. Also
> - note that the return value is inverted.
> + Don't print debug messages when running valgrind since they can
> + trigger false warnings.
> */
> - DBUG_RETURN(!binlog_flush_stmt_cache(thd, cache_mngr) &&
> - !binlog_flush_trx_cache(thd, cache_mngr, &xle));
> -}
> +#ifndef HAVE_purify
> + DBUG_DUMP("before_record", before_record, table->s->reclength);
> + DBUG_DUMP("after_record", after_record, table->s->reclength);
> + DBUG_DUMP("before_row", before_row, before_size);
> + DBUG_DUMP("after_row", after_row, after_size);
> +#endif
>
> -void TC_LOG_BINLOG::unlog(ulong cookie, my_xid xid)
> -{
> - mysql_mutex_lock(&LOCK_prep_xids);
> - DBUG_ASSERT(prepared_xids > 0);
> - if (--prepared_xids == 0) {
> - DBUG_PRINT("info", ("prepared_xids=%lu", prepared_xids));
> - mysql_cond_signal(&COND_prep_xids);
> - }
> - mysql_mutex_unlock(&LOCK_prep_xids);
> - rotate_and_purge(0); // as ::write() did not rotate
> -}
> + Rows_log_event* const ev=
> + binlog_prepare_pending_rows_event(table, server_id, cols, colcnt,
> + before_size + after_size, is_trans,
> + static_cast<Update_rows_log_event*>(0));
>
> -int TC_LOG_BINLOG::recover(IO_CACHE *log, Format_description_log_event *fdle)
> -{
> - Log_event *ev;
> - HASH xids;
> - MEM_ROOT mem_root;
> + if (unlikely(ev == 0))
> + return HA_ERR_OUT_OF_MEM;
>
> - if (! fdle->is_valid() ||
> - my_hash_init(&xids, &my_charset_bin, TC_LOG_PAGE_SIZE/3, 0,
> - sizeof(my_xid), 0, 0, MYF(0)))
> - goto err1;
> + return
> + ev->add_row_data(before_row, before_size) ||
> + ev->add_row_data(after_row, after_size);
> +}
>
> - init_alloc_root(&mem_root, TC_LOG_PAGE_SIZE, TC_LOG_PAGE_SIZE);
> +int THD::binlog_delete_row(TABLE* table, bool is_trans,
> + MY_BITMAP const* cols, size_t colcnt,
> + uchar const *record)
> +{
> + DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
> mysql_bin_log.is_open());
>
> - fdle->flags&= ~LOG_EVENT_BINLOG_IN_USE_F; // abort on the first error
> + /*
> + Pack records into format for transfer. We are allocating more
> + memory than needed, but that doesn't matter.
> + */
> + Row_data_memory memory(table, max_row_length(table, record));
> + if (unlikely(!memory.has_memory()))
> + return HA_ERR_OUT_OF_MEM;
>
> - while ((ev= Log_event::read_log_event(log,0,fdle)) && ev->is_valid())
> - {
> - if (ev->get_type_code() == XID_EVENT)
> - {
> - Xid_log_event *xev=(Xid_log_event *)ev;
> - uchar *x= (uchar *) memdup_root(&mem_root, (uchar*) &xev->xid,
> - sizeof(xev->xid));
> - if (!x || my_hash_insert(&xids, x))
> - goto err2;
> - }
> - delete ev;
> - }
> + uchar *row_data= memory.slot(0);
>
> - if (ha_recover(&xids))
> - goto err2;
> + size_t const len= pack_row(table, cols, row_data, record);
>
> - free_root(&mem_root, MYF(0));
> - my_hash_free(&xids);
> - return 0;
> + Rows_log_event* const ev=
> + binlog_prepare_pending_rows_event(table, server_id, cols, colcnt,
> + len, is_trans,
> + static_cast<Delete_rows_log_event*>(0));
>
> -err2:
> - free_root(&mem_root, MYF(0));
> - my_hash_free(&xids);
> -err1:
> - sql_print_error("Crash recovery failed. Either correct the problem "
> - "(if it's, for example, out of memory error) and restart, "
> - "or delete (or rename) binary log and start mysqld with "
> - "--tc-heuristic-recover={commit|rollback}");
> - return 1;
> + if (unlikely(ev == 0))
> + return HA_ERR_OUT_OF_MEM;
> +
> + return ev->add_row_data(row_data, len);
> }
>
> -/*
> - Adjust the position pointer in the binary log file for all running slaves
>
> - SYNOPSIS
> - adjust_linfo_offsets()
> - purge_offset Number of bytes removed from start of log index file
> +int THD::binlog_remove_pending_rows_event(bool clear_maps,
> + bool is_transactional)
> +{
> + DBUG_ENTER("THD::binlog_remove_pending_rows_event");
>
> - NOTES
> - - This is called when doing a PURGE when we delete lines from the
> - index log file
> + if (!mysql_bin_log.is_open())
> + DBUG_RETURN(0);
>
> - REQUIREMENTS
> - - Before calling this function, we have to ensure that no threads are
> - using any binary log file before purge_offset.a
> + mysql_bin_log.remove_pending_rows_event(this, is_transactional);
>
> - TODO
> - - Inform the slave threads that they should sync the position
> - in the binary log file with flush_relay_log_info.
> - Now they sync is done for next read.
> -*/
> + if (clear_maps)
> + binlog_table_maps= 0;
>
> -void adjust_linfo_offsets(my_off_t purge_offset)
> -{
> - THD *tmp;
> + DBUG_RETURN(0);
> +}
>
> - mysql_mutex_lock(&LOCK_thread_count);
> - I_List_iterator<THD> it(threads);
> +int THD::binlog_flush_pending_rows_event(bool stmt_end, bool is_transactional)
> +{
> + DBUG_ENTER("THD::binlog_flush_pending_rows_event");
> + /*
> + We shall flush the pending event even if we are not in row-based
> + mode: it might be the case that we left row-based mode before
> + flushing anything (e.g., if we have explicitly locked tables).
> + */
> + if (!mysql_bin_log.is_open())
> + DBUG_RETURN(0);
>
> - while ((tmp=it++))
> + /*
> + Mark the event as the last event of a statement if the stmt_end
> + flag is set.
> + */
> + int error= 0;
> + if (Rows_log_event *pending= binlog_get_pending_rows_event(is_transactional))
> {
> - LOG_INFO* linfo;
> - if ((linfo = tmp->current_linfo))
> + if (stmt_end)
> {
> - mysql_mutex_lock(&linfo->lock);
> - /*
> - Index file offset can be less that purge offset only if
> - we just started reading the index file. In that case
> - we have nothing to adjust
> - */
> - if (linfo->index_file_offset < purge_offset)
> - linfo->fatal = (linfo->index_file_offset != 0);
> - else
> - linfo->index_file_offset -= purge_offset;
> - mysql_mutex_unlock(&linfo->lock);
> + pending->set_flags(Rows_log_event::STMT_END_F);
> + pending->flags|= LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F;
> + binlog_table_maps= 0;
> }
> +
> + error= mysql_bin_log.flush_and_set_pending_rows_event(this, 0,
> + is_transactional);
> }
> - mysql_mutex_unlock(&LOCK_thread_count);
> +
> + DBUG_RETURN(error);
> +}
> +
> +
> +#if !defined(DBUG_OFF) && !defined(_lint)
> +static const char *
> +show_query_type(THD::enum_binlog_query_type qtype)
> +{
> + switch (qtype) {
> + case THD::ROW_QUERY_TYPE:
> + return "ROW";
> + case THD::STMT_QUERY_TYPE:
> + return "STMT";
> + case THD::QUERY_TYPE_COUNT:
> + default:
> + DBUG_ASSERT(0 <= qtype && qtype < THD::QUERY_TYPE_COUNT);
> + }
> + static char buf[64];
> + sprintf(buf, "UNKNOWN#%d", qtype);
> + return buf;
> }
> +#endif
>
>
> -bool log_in_use(const char* log_name)
> +/**
> + Auxiliary method used by @c binlog_query() to raise warnings.
> +
> + The type of warning and the type of unsafeness is stored in
> + THD::binlog_unsafe_warning_flags.
> +*/
> +void THD::issue_unsafe_warnings()
> {
> - size_t log_name_len = strlen(log_name) + 1;
> - THD *tmp;
> - bool result = 0;
> + DBUG_ENTER("issue_unsafe_warnings");
> + /*
> + Ensure that binlog_unsafe_warning_flags is big enough to hold all
> + bits. This is actually a constant expression.
> + */
> + DBUG_ASSERT(2 * LEX::BINLOG_STMT_UNSAFE_COUNT <=
> + sizeof(binlog_unsafe_warning_flags) * CHAR_BIT);
>
> - mysql_mutex_lock(&LOCK_thread_count);
> - I_List_iterator<THD> it(threads);
> + uint32 unsafe_type_flags= binlog_unsafe_warning_flags;
>
> - while ((tmp=it++))
> + /*
> + Clear: (1) bits above BINLOG_STMT_UNSAFE_COUNT; (2) bits for
> + warnings that have been printed already.
> + */
> + unsafe_type_flags &= (LEX::BINLOG_STMT_UNSAFE_ALL_FLAGS ^
> + (unsafe_type_flags >>
> LEX::BINLOG_STMT_UNSAFE_COUNT));
> + /* If all warnings have been printed already, return. */
> + if (unsafe_type_flags == 0)
> + DBUG_VOID_RETURN;
> +
> + DBUG_PRINT("info", ("unsafe_type_flags: 0x%x", unsafe_type_flags));
> +
> + /*
> + For each unsafe_type, check if the statement is unsafe in this way
> + and issue a warning.
> + */
> + for (int unsafe_type=0;
> + unsafe_type < LEX::BINLOG_STMT_UNSAFE_COUNT;
> + unsafe_type++)
> {
> - LOG_INFO* linfo;
> - if ((linfo = tmp->current_linfo))
> + if ((unsafe_type_flags & (1 << unsafe_type)) != 0)
> {
> - mysql_mutex_lock(&linfo->lock);
> - result = !bcmp((uchar*) log_name, (uchar*) linfo->log_file_name,
> - log_name_len);
> - mysql_mutex_unlock(&linfo->lock);
> - if (result)
> - break;
> + push_warning_printf(this, MYSQL_ERROR::WARN_LEVEL_NOTE,
> + ER_BINLOG_UNSAFE_STATEMENT,
> + ER(ER_BINLOG_UNSAFE_STATEMENT),
> + ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]));
> + if (global_system_variables.log_warnings)
> + {
> + char buf[MYSQL_ERRMSG_SIZE * 2];
> + sprintf(buf, ER(ER_BINLOG_UNSAFE_STATEMENT),
> + ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]));
> + sql_print_warning(ER(ER_MESSAGE_AND_STATEMENT), buf, query());
> + }
> }
> }
> -
> - mysql_mutex_unlock(&LOCK_thread_count);
> - return result;
> + /*
> + Mark these unsafe types as already printed, to avoid printing
> + warnings for them again.
> + */
> + binlog_unsafe_warning_flags|=
> + unsafe_type_flags << LEX::BINLOG_STMT_UNSAFE_COUNT;
> + DBUG_VOID_RETURN;
> }
>
> -#ifndef EMBEDDED_LIBRARY
> -bool purge_error_message(THD* thd, int res)
> -{
> - uint errcode;
>
> - if ((errcode= purge_log_get_error_code(res)) != 0)
> - {
> - my_message(errcode, ER(errcode), MYF(0));
> - return TRUE;
> - }
> - my_ok(thd);
> - return FALSE;
> -}
> +/**
> + Log the current query.
>
> + The query will be logged in either row format or statement format
> + depending on the value of @c current_stmt_binlog_format_row field and
> + the value of the @c qtype parameter.
>
> -/**
> - Execute a PURGE BINARY LOGS TO <log> command.
> + This function must be called:
>
> - @param thd Pointer to THD object for the client thread executing the
> - statement.
> + - After the all calls to ha_*_row() functions have been issued.
>
> - @param to_log Name of the last log to purge.
> + - After any writes to system tables. Rationale: if system tables
> + were written after a call to this function, and the master crashes
> + after the call to this function and before writing the system
> + tables, then the master and slave get out of sync.
>
> - @retval FALSE success
> - @retval TRUE failure
> + - Before tables are unlocked and closed.
> +
> + @see decide_logging_format
> +
> + @retval 0 Success
> +
> + @retval nonzero If there is a failure when writing the query (e.g.,
> + write failure), then the error code is returned.
> */
> -bool purge_master_logs(THD* thd, const char* to_log)
> +int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
> + ulong query_len, bool is_trans, bool direct,
> + bool suppress_use, int errcode)
> {
> - char search_file_name[FN_REFLEN];
> - if (!mysql_bin_log.is_open())
> - {
> - my_ok(thd);
> - return FALSE;
> - }
> + DBUG_ENTER("THD::binlog_query");
> + DBUG_PRINT("enter", ("qtype: %s query: '%s'",
> + show_query_type(qtype), query_arg));
> + DBUG_ASSERT(query_arg && mysql_bin_log.is_open());
>
> - mysql_bin_log.make_log_name(search_file_name, to_log);
> - return purge_error_message(thd,
> - mysql_bin_log.purge_logs(search_file_name, 0, 1,
> - 1, NULL));
> -}
> + /*
> + If we are not in prelocked mode, mysql_unlock_tables() will be
> + called after this binlog_query(), so we have to flush the pending
> + rows event with the STMT_END_F set to unlock all tables at the
> + slave side as well.
>
> + If we are in prelocked mode, the flushing will be done inside the
> + top-most close_thread_tables().
> + */
> + if (this->locked_tables_mode <= LTM_LOCK_TABLES)
> + if (int error= binlog_flush_pending_rows_event(TRUE, is_trans))
> + DBUG_RETURN(error);
>
> -/**
> - Execute a PURGE BINARY LOGS BEFORE <date> command.
> + /*
> + Warnings for unsafe statements logged in statement format are
> + printed here instead of in decide_logging_format(). This is
> + because the warnings should be printed only if the statement is
> + actually logged. When executing decide_logging_format(), we cannot
> + know for sure if the statement will be logged.
> + */
> + if (sql_log_bin_toplevel)
> + issue_unsafe_warnings();
>
> - @param thd Pointer to THD object for the client thread executing the
> - statement.
> + switch (qtype) {
> + /*
> + ROW_QUERY_TYPE means that the statement may be logged either in
> + row format or in statement format. If
> + current_stmt_binlog_format is row, it means that the
> + statement has already been logged in row format and hence shall
> + not be logged again.
> + */
> + case THD::ROW_QUERY_TYPE:
> + DBUG_PRINT("debug",
> + ("is_current_stmt_binlog_format_row: %d",
> + is_current_stmt_binlog_format_row()));
> + if (is_current_stmt_binlog_format_row())
> + DBUG_RETURN(0);
> + /* Fall through */
>
> - @param purge_time Date before which logs should be purged.
> + /*
> + STMT_QUERY_TYPE means that the query must be logged in statement
> + format; it cannot be logged in row format. This is typically
> + used by DDL statements. It is an error to use this query type
> + if current_stmt_binlog_format_row is row.
> +
> + @todo Currently there are places that call this method with
> + STMT_QUERY_TYPE and current_stmt_binlog_format is row. Fix those
> + places and add assert to ensure correct behavior. /Sven
> + */
> + case THD::STMT_QUERY_TYPE:
> + /*
> + The MYSQL_LOG::write() function will set the STMT_END_F flag and
> + flush the pending rows event if necessary.
> + */
> + {
> + Query_log_event qinfo(this, query_arg, query_len, is_trans, direct,
> + suppress_use, errcode);
> + qinfo.flags|= LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F;
> + /*
> + Binlog table maps will be irrelevant after a Query_log_event
> + (they are just removed on the slave side) so after the query
> + log event is written to the binary log, we pretend that no
> + table maps were written.
> + */
> + int error= mysql_bin_log.write(&qinfo);
> + binlog_table_maps= 0;
> + DBUG_RETURN(error);
> + }
> + break;
>
> - @retval FALSE success
> - @retval TRUE failure
> -*/
> -bool purge_master_logs_before_date(THD* thd, time_t purge_time)
> -{
> - if (!mysql_bin_log.is_open())
> - {
> - my_ok(thd);
> - return 0;
> + case THD::QUERY_TYPE_COUNT:
> + default:
> + DBUG_ASSERT(0 <= qtype && qtype < QUERY_TYPE_COUNT);
> }
> - return purge_error_message(thd,
> - mysql_bin_log.purge_logs_before_date(purge_time));
> + DBUG_RETURN(0);
> }
> -#endif /* EMBEDDED_LIBRARY */
> +
> +#endif /* !defined(MYSQL_CLIENT) */
>
> #ifdef INNODB_COMPATIBILITY_HOOKS
> /**
>
> === modified file 'sql/binlog.h'
> --- a/sql/binlog.h 2010-05-08 02:34:04 +0000
> +++ b/sql/binlog.h 2010-05-13 05:54:26 +0000
> @@ -23,13 +23,6 @@ class Relay_log_info;
>
> class Format_description_log_event;
>
> -bool trans_has_updated_trans_table(const THD* thd);
> -bool stmt_has_updated_trans_table(const THD *thd);
> -bool use_trans_cache(const THD* thd, bool is_transactional);
> -bool ending_trans(THD* thd, const bool all);
> -bool trans_has_updated_non_trans_table(const THD* thd);
> -bool stmt_has_updated_non_trans_table(const THD* thd);
> -
> class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
> {
> private:
> @@ -253,6 +246,28 @@ public:
> inline uint32 get_open_count() { return open_count; }
> };
>
> +typedef struct st_load_file_info
> +{
> + THD* thd;
> + my_off_t last_pos_in_file;
> + bool wrote_create_file, log_delayed;
> +} LOAD_FILE_INFO;
> +
> extern MYSQL_PLUGIN_IMPORT MYSQL_BIN_LOG mysql_bin_log;
>
> +bool trans_has_updated_trans_table(const THD* thd);
> +bool stmt_has_updated_trans_table(const THD *thd);
> +bool use_trans_cache(const THD* thd, bool is_transactional);
> +bool ending_trans(THD* thd, const bool all);
> +bool trans_has_updated_non_trans_table(const THD* thd);
> +bool stmt_has_updated_non_trans_table(const THD* thd);
> +
> +int log_loaded_block(IO_CACHE* file);
> +File open_binlog(IO_CACHE *log, const char *log_file_name,
> + const char **errmsg);
> +int check_binlog_magic(IO_CACHE* log, const char** errmsg);
> +bool purge_master_logs(THD* thd, const char* to_log);
> +bool purge_master_logs_before_date(THD* thd, time_t purge_time);
> +bool show_binlog_events(THD *thd, MYSQL_BIN_LOG *binary_log);
> +
> #endif /* BINLOG_H_INCLUDED */
>
> === modified file 'sql/log.h'
> --- a/sql/log.h 2010-05-08 02:34:04 +0000
> +++ b/sql/log.h 2010-05-13 05:54:26 +0000
> @@ -436,9 +436,6 @@ bool general_log_write(THD *thd, enum en
> void sql_perror(const char *message);
> bool flush_error_log();
>
> -File open_binlog(IO_CACHE *log, const char *log_file_name,
> - const char **errmsg);
> -
> char *make_log_name(char *buff, const char *name, const char* log_ext);
>
> extern LOGGER logger;
>
> === modified file 'sql/master.cc'
> --- a/sql/master.cc 2010-05-08 02:34:04 +0000
> +++ b/sql/master.cc 2010-05-13 05:54:26 +0000
> @@ -19,7 +19,6 @@
> #include "sql_parse.h" // check_access
> #ifdef HAVE_REPLICATION
>
> -#include "sql_repl.h"
> #include "sql_acl.h" // SUPER_ACL
> #include "log_event.h"
> #include "rpl_filter.h"
> @@ -925,182 +924,6 @@ int cmp_master_pos(const char* log_file_
>
>
> /**
> - Execute a SHOW BINLOG EVENTS statement.
> -
> - @param thd Pointer to THD object for the client thread executing the
> - statement.
> -
> - @retval FALSE success
> - @retval TRUE failure
> -*/
> -bool mysql_show_binlog_events(THD* thd)
> -{
> - Protocol *protocol= thd->protocol;
> - List<Item> field_list;
> - const char *errmsg = 0;
> - bool ret = TRUE;
> - IO_CACHE log;
> - File file = -1;
> - MYSQL_BIN_LOG *binary_log= NULL;
> - DBUG_ENTER("mysql_show_binlog_events");
> -
> - Log_event::init_show_field_list(&field_list);
> - if (protocol->send_result_set_metadata(&field_list,
> - Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
> - DBUG_RETURN(TRUE);
> -
> - Format_description_log_event *description_event= new
> - Format_description_log_event(3); /* MySQL 4.0 by default */
> -
> - DBUG_ASSERT(thd->lex->sql_command == SQLCOM_SHOW_BINLOG_EVENTS ||
> - thd->lex->sql_command == SQLCOM_SHOW_RELAYLOG_EVENTS);
> -
> - /* select wich binary log to use: binlog or relay */
> - if ( thd->lex->sql_command == SQLCOM_SHOW_BINLOG_EVENTS )
> - {
> - /*
> - Wait for handlers to insert any pending information
> - into the binlog. For e.g. ndb which updates the binlog asynchronously
> - this is needed so that the uses sees all its own commands in the binlog
> - */
> - ha_binlog_wait(thd);
> -
> - binary_log= &mysql_bin_log;
> - }
> - else /* showing relay log contents */
> - {
> - if (!active_mi)
> - DBUG_RETURN(TRUE);
> -
> - binary_log= &(active_mi->rli.relay_log);
> - }
> -
> - if (binary_log->is_open())
> - {
> - LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
> - SELECT_LEX_UNIT *unit= &thd->lex->unit;
> - ha_rows event_count, limit_start, limit_end;
> - my_off_t pos = max(BIN_LOG_HEADER_SIZE, lex_mi->pos); // user-friendly
> - char search_file_name[FN_REFLEN], *name;
> - const char *log_file_name = lex_mi->log_file_name;
> - mysql_mutex_t *log_lock = binary_log->get_log_lock();
> - LOG_INFO linfo;
> - Log_event* ev;
> -
> - unit->set_limit(thd->lex->current_select);
> - limit_start= unit->offset_limit_cnt;
> - limit_end= unit->select_limit_cnt;
> -
> - name= search_file_name;
> - if (log_file_name)
> - binary_log->make_log_name(search_file_name, log_file_name);
> - else
> - name=0; // Find first log
> -
> - linfo.index_file_offset = 0;
> -
> - if (binary_log->find_log_pos(&linfo, name, 1))
> - {
> - errmsg = "Could not find target log";
> - goto err;
> - }
> -
> - mysql_mutex_lock(&LOCK_thread_count);
> - thd->current_linfo = &linfo;
> - mysql_mutex_unlock(&LOCK_thread_count);
> -
> - if ((file=open_binlog(&log, linfo.log_file_name, &errmsg)) < 0)
> - goto err;
> -
> - /*
> - to account binlog event header size
> - */
> - thd->variables.max_allowed_packet += MAX_LOG_EVENT_HEADER;
> -
> - mysql_mutex_lock(log_lock);
> -
> - /*
> - open_binlog() sought to position 4.
> - Read the first event in case it's a Format_description_log_event, to
> - know the format. If there's no such event, we are 3.23 or 4.x. This
> - code, like before, can't read 3.23 binlogs.
> - This code will fail on a mixed relay log (one which has Format_desc then
> - Rotate then Format_desc).
> - */
> - ev= Log_event::read_log_event(&log, (mysql_mutex_t*)0, description_event);
> - if (ev)
> - {
> - if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
> - {
> - delete description_event;
> - description_event= (Format_description_log_event*) ev;
> - }
> - else
> - delete ev;
> - }
> -
> - my_b_seek(&log, pos);
> -
> - if (!description_event->is_valid())
> - {
> - errmsg="Invalid Format_description event; could be out of memory";
> - goto err;
> - }
> -
> - for (event_count = 0;
> - (ev = Log_event::read_log_event(&log, (mysql_mutex_t*) 0,
> - description_event)); )
> - {
> - if (event_count >= limit_start &&
> - ev->net_send(protocol, linfo.log_file_name, pos))
> - {
> - errmsg = "Net error";
> - delete ev;
> - mysql_mutex_unlock(log_lock);
> - goto err;
> - }
> -
> - pos = my_b_tell(&log);
> - delete ev;
> -
> - if (++event_count >= limit_end)
> - break;
> - }
> -
> - if (event_count < limit_end && log.error)
> - {
> - errmsg = "Wrong offset or I/O error";
> - mysql_mutex_unlock(log_lock);
> - goto err;
> - }
> -
> - mysql_mutex_unlock(log_lock);
> - }
> -
> - ret= FALSE;
> -
> -err:
> - delete description_event;
> - if (file >= 0)
> - {
> - end_io_cache(&log);
> - mysql_file_close(file, MYF(MY_WME));
> - }
> -
> - if (errmsg)
> - my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0),
> - "SHOW BINLOG EVENTS", errmsg);
> - else
> - my_eof(thd);
> -
> - mysql_mutex_lock(&LOCK_thread_count);
> - thd->current_linfo = 0;
> - mysql_mutex_unlock(&LOCK_thread_count);
> - DBUG_RETURN(ret);
> -}
> -
> -
> -/**
> Execute a SHOW MASTER STATUS statement.
>
> @param thd Pointer to THD object for the client thread executing the
> @@ -1225,56 +1048,4 @@ err:
> DBUG_RETURN(TRUE);
> }
>
> -/**
> - Load data's io cache specific hook to be executed
> - before a chunk of data is being read into the cache's buffer
> - The fuction instantianates and writes into the binlog
> - replication events along LOAD DATA processing.
> -
> - @param file pointer to io-cache
> - @retval 0 success
> - @retval 1 failure
> -*/
> -int log_loaded_block(IO_CACHE* file)
> -{
> - DBUG_ENTER("log_loaded_block");
> - LOAD_FILE_INFO *lf_info;
> - uint block_len;
> - /* buffer contains position where we started last read */
> - uchar* buffer= (uchar*) my_b_get_buffer_start(file);
> - uint max_event_size= current_thd->variables.max_allowed_packet;
> - lf_info= (LOAD_FILE_INFO*) file->arg;
> - if (lf_info->thd->is_current_stmt_binlog_format_row())
> - DBUG_RETURN(0);
> - if (lf_info->last_pos_in_file != HA_POS_ERROR &&
> - lf_info->last_pos_in_file >= my_b_get_pos_in_file(file))
> - DBUG_RETURN(0);
> -
> - for (block_len= (uint) (my_b_get_bytes_in_buffer(file)); block_len > 0;
> - buffer += min(block_len, max_event_size),
> - block_len -= min(block_len, max_event_size))
> - {
> - lf_info->last_pos_in_file= my_b_get_pos_in_file(file);
> - if (lf_info->wrote_create_file)
> - {
> - Append_block_log_event a(lf_info->thd, lf_info->thd->db, buffer,
> - min(block_len, max_event_size),
> - lf_info->log_delayed);
> - if (mysql_bin_log.write(&a))
> - DBUG_RETURN(1);
> - }
> - else
> - {
> - Begin_load_query_log_event b(lf_info->thd, lf_info->thd->db,
> - buffer,
> - min(block_len, max_event_size),
> - lf_info->log_delayed);
> - if (mysql_bin_log.write(&b))
> - DBUG_RETURN(1);
> - lf_info->wrote_create_file= 1;
> - }
> - }
> - DBUG_RETURN(0);
> -}
> -
> #endif /* HAVE_REPLICATION */
>
> === added file 'sql/master.h'
> --- a/sql/master.h 1970-01-01 00:00:00 +0000
> +++ b/sql/master.h 2010-05-13 05:54:26 +0000
> @@ -0,0 +1,28 @@
> +#ifndef MASTER_H_INCLUDED
> +/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
> +
> + 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
> + the Free Software Foundation; version 2 of the License.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program; if not, write to the Free Software
> + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
> +
> +
> +#define MASTER_H_INCLUDED
> +extern bool server_id_supplied;
> +extern int max_binlog_dump_events;
> +extern my_bool opt_sporadic_binlog_dump_fail;
> +
> +bool mysql_show_binlog_events(THD* thd);
> +bool show_binlogs(THD* thd);
> +void kill_zombie_dump_threads(uint32 slave_server_id);
> +void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, ushort flags);
> +int reset_master(THD* thd);
> +#endif /* MASTER_H_INCLUDED */
>
> === modified file 'sql/mysqld.cc'
> --- a/sql/mysqld.cc 2010-04-19 08:27:46 +0000
> +++ b/sql/mysqld.cc 2010-05-13 05:54:26 +0000
> @@ -54,8 +54,8 @@
> #include <my_dir.h>
> #include <my_bit.h>
> #include "slave.h"
> +#include "master.h"
> #include "rpl_mi.h"
> -#include "sql_repl.h"
> #include "rpl_filter.h"
> #include "repl_failsafe.h"
> #include <my_stacktrace.h>
>
> === modified file 'sql/repl_failsafe.cc'
> --- a/sql/repl_failsafe.cc 2010-03-31 14:05:33 +0000
> +++ b/sql/repl_failsafe.cc 2010-05-13 05:54:26 +0000
> @@ -29,7 +29,6 @@
>
> #include "repl_failsafe.h"
> #include "sql_acl.h" // REPL_SLAVE_ACL
> -#include "sql_repl.h"
> #include "slave.h"
> #include "rpl_mi.h"
> #include "rpl_filter.h"
>
> === modified file 'sql/rpl_handler.cc'
> --- a/sql/rpl_handler.cc 2010-03-31 14:05:33 +0000
> +++ b/sql/rpl_handler.cc 2010-05-13 05:54:26 +0000
> @@ -17,7 +17,6 @@
> #include "unireg.h"
>
> #include "rpl_mi.h"
> -#include "sql_repl.h"
> #include "log_event.h"
> #include "rpl_filter.h"
> #include <my_dir.h>
>
> === modified file 'sql/rpl_rli.cc'
> --- a/sql/rpl_rli.cc 2010-03-31 14:05:33 +0000
> +++ b/sql/rpl_rli.cc 2010-05-13 05:54:26 +0000
> @@ -19,10 +19,10 @@
> #include "rpl_rli.h"
> #include "sql_base.h" // close_thread_tables
> #include <my_dir.h> // For MY_STAT
> -#include "sql_repl.h" // For check_binlog_magic
> #include "log_event.h" // Format_description_log_event, Log_event,
> // FORMAT_DESCRIPTION_LOG_EVENT, ROTATE_EVENT,
> // PREFIX_SQL_LOAD
> +#include "slave.h"
> #include "rpl_utility.h"
> #include "transaction.h"
> #include "sql_parse.h" // end_trans, ROLLBACK
> @@ -1261,4 +1261,32 @@ void Relay_log_info::slave_close_thread_
> close_thread_tables(thd);
> clear_tables_to_lock();
> }
> +/**
> + Execute a SHOW RELAYLOG EVENTS statement.
> +
> + @param thd Pointer to THD object for the client thread executing the
> + statement.
> +
> + @retval FALSE success
> + @retval TRUE failure
> +*/
> +bool mysql_show_relaylog_events(THD* thd)
> +{
> + Protocol *protocol= thd->protocol;
> + List<Item> field_list;
> + DBUG_ENTER("mysql_show_relaylog_events");
> +
> + DBUG_ASSERT(thd->lex->sql_command == SQLCOM_SHOW_RELAYLOG_EVENTS);
> +
> + Log_event::init_show_field_list(&field_list);
> + if (protocol->send_result_set_metadata(&field_list,
> + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
> + DBUG_RETURN(TRUE);
> +
> + if (!active_mi)
> + DBUG_RETURN(TRUE);
> +
> + DBUG_RETURN(show_binlog_events(thd, &active_mi->rli.relay_log));
> +}
> +
> #endif
>
> === modified file 'sql/rpl_rli.h'
> --- a/sql/rpl_rli.h 2010-05-08 02:34:04 +0000
> +++ b/sql/rpl_rli.h 2010-05-13 05:54:26 +0000
> @@ -450,6 +450,15 @@ private:
>
> // Defined in rpl_rli.cc
> int init_relay_log_info(Relay_log_info* rli, const char* info_fname);
> +bool flush_relay_log_info(Relay_log_info* rli);
> +void end_relay_log_info(Relay_log_info* rli);
> +int init_relay_log_pos(Relay_log_info* rli,const char* log,ulonglong pos,
> + bool need_data_lock, const char** errmsg,
> + bool look_for_description_event);
>
> +int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
> + const char** errmsg);
> +void rotate_relay_log(Master_info* mi);
> +bool mysql_show_relaylog_events(THD* thd);
>
> #endif /* RPL_RLI_H */
>
> === modified file 'sql/slave.cc'
> --- a/sql/slave.cc 2010-05-08 02:34:04 +0000
> +++ b/sql/slave.cc 2010-05-13 05:54:26 +0000
> @@ -31,7 +31,6 @@
> #include "sql_table.h" // mysql_rm_table
> #include "rpl_mi.h"
> #include "rpl_rli.h"
> -#include "sql_repl.h"
> #include "rpl_filter.h"
> #include "repl_failsafe.h"
> #include "transaction.h"
>
> === modified file 'sql/slave.h'
> --- a/sql/slave.h 2010-05-08 02:34:04 +0000
> +++ b/sql/slave.h 2010-05-13 05:54:26 +0000
> @@ -49,6 +49,20 @@
> class Relay_log_info;
> class Master_info;
>
> +extern bool server_id_supplied;
> +extern my_bool opt_show_slave_auth_info;
> +
> +typedef struct st_slave_info
> +{
> + uint32 server_id;
> + uint32 rpl_recovery_rank, master_id;
> + char host[HOSTNAME_LENGTH+1];
> + char user[USERNAME_LENGTH+1];
> + char password[MAX_PASSWORD_LENGTH+1];
> + uint16 port;
> + THD* thd;
> +} SLAVE_INFO;
> +
>
> /*****************************************************************************
>
> @@ -145,10 +159,15 @@ extern ulonglong relay_log_space_limit;
> */
> #define SLAVE_FORCE_ALL 4
>
> +int start_slave(THD* thd, Master_info* mi, bool net_report);
> +int stop_slave(THD* thd, Master_info* mi, bool net_report);
> +bool change_master(THD* thd, Master_info* mi);
> +int cmp_master_pos(const char* log_file_name1, ulonglong log_pos1,
> + const char* log_file_name2, ulonglong log_pos2);
> +int reset_slave(THD *thd, Master_info* mi);
> int init_slave();
> int init_recovery(Master_info* mi, const char** errmsg);
> void init_slave_skip_errors(const char* arg);
> -bool flush_relay_log_info(Relay_log_info* rli);
> int register_slave_on_master(MYSQL* mysql);
> int terminate_slave_threads(Master_info* mi, int thread_mask,
> bool skip_lock = 0);
> @@ -194,19 +213,11 @@ void end_slave(); /* release slave threa
> void close_active_mi(); /* clean up slave threads data */
> void clear_until_condition(Relay_log_info* rli);
> void clear_slave_error(Relay_log_info* rli);
> -void end_relay_log_info(Relay_log_info* rli);
> void lock_slave_threads(Master_info* mi);
> void unlock_slave_threads(Master_info* mi);
> void init_thread_mask(int* mask,Master_info* mi,bool inverse);
> -int init_relay_log_pos(Relay_log_info* rli,const char* log,ulonglong pos,
> - bool need_data_lock, const char** errmsg,
> - bool look_for_description_event);
> -
> -int purge_relay_logs(Relay_log_info* rli, THD *thd, bool just_reset,
> - const char** errmsg);
> void set_slave_thread_options(THD* thd);
> void set_slave_thread_default_charset(THD *thd, Relay_log_info const *rli);
> -void rotate_relay_log(Master_info* mi);
> int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli);
>
> pthread_handler_t handle_slave_io(void *arg);
>
> === modified file 'sql/sql_class.cc'
> --- a/sql/sql_class.cc 2010-04-29 10:43:54 +0000
> +++ b/sql/sql_class.cc 2010-05-13 05:54:26 +0000
> @@ -3480,610 +3480,6 @@ void xid_cache_delete(XID_STATE *xid_sta
> }
>
>
> -/**
> - Decide on logging format to use for the statement and issue errors
> - or warnings as needed. The decision depends on the following
> - parameters:
> -
> - - The logging mode, i.e., the value of binlog_format. Can be
> - statement, mixed, or row.
> -
> - - The type of statement. There are three types of statements:
> - "normal" safe statements; unsafe statements; and row injections.
> - An unsafe statement is one that, if logged in statement format,
> - might produce different results when replayed on the slave (e.g.,
> - INSERT DELAYED). A row injection is either a BINLOG statement, or
> - a row event executed by the slave's SQL thread.
> -
> - - The capabilities of tables modified by the statement. The
> - *capabilities vector* for a table is a set of flags associated
> - with the table. Currently, it only includes two flags: *row
> - capability flag* and *statement capability flag*.
> -
> - The row capability flag is set if and only if the engine can
> - handle row-based logging. The statement capability flag is set if
> - and only if the table can handle statement-based logging.
> -
> - Decision table for logging format
> - ---------------------------------
> -
> - The following table summarizes how the format and generated
> - warning/error depends on the tables' capabilities, the statement
> - type, and the current binlog_format.
> -
> - Row capable N NNNNNNNNN YYYYYYYYY YYYYYYYYY
> - Statement capable N YYYYYYYYY NNNNNNNNN YYYYYYYYY
> -
> - Statement type * SSSUUUIII SSSUUUIII SSSUUUIII
> -
> - binlog_format * SMRSMRSMR SMRSMRSMR SMRSMRSMR
> -
> - Logged format - SS-S----- -RR-RR-RR SRRSRR-RR
> - Warning/Error 1 --2732444 5--5--6-- ---7--6--
> -
> - Legend
> - ------
> -
> - Row capable: N - Some table not row-capable, Y - All tables row-capable
> - Stmt capable: N - Some table not stmt-capable, Y - All tables stmt-capable
> - Statement type: (S)afe, (U)nsafe, or Row (I)njection
> - binlog_format: (S)TATEMENT, (M)IXED, or (R)OW
> - Logged format: (S)tatement or (R)ow
> - Warning/Error: Warnings and error messages are as follows:
> -
> - 1. Error: Cannot execute statement: binlogging impossible since both
> - row-incapable engines and statement-incapable engines are
> - involved.
> -
> - 2. Error: Cannot execute statement: binlogging impossible since
> - BINLOG_FORMAT = ROW and at least one table uses a storage engine
> - limited to statement-logging.
> -
> - 3. Error: Cannot execute statement: binlogging of unsafe statement
> - is impossible when storage engine is limited to statement-logging
> - and BINLOG_FORMAT = MIXED.
> -
> - 4. Error: Cannot execute row injection: binlogging impossible since
> - at least one table uses a storage engine limited to
> - statement-logging.
> -
> - 5. Error: Cannot execute statement: binlogging impossible since
> - BINLOG_FORMAT = STATEMENT and at least one table uses a storage
> - engine limited to row-logging.
> -
> - 6. Error: Cannot execute row injection: binlogging impossible since
> - BINLOG_FORMAT = STATEMENT.
> -
> - 7. Warning: Unsafe statement binlogged in statement format since
> - BINLOG_FORMAT = STATEMENT.
> -
> - In addition, we can produce the following error (not depending on
> - the variables of the decision diagram):
> -
> - 8. Error: Cannot execute statement: binlogging impossible since more
> - than one engine is involved and at least one engine is
> - self-logging.
> -
> - For each error case above, the statement is prevented from being
> - logged, we report an error, and roll back the statement. For
> - warnings, we set the thd->binlog_flags variable: the warning will be
> - printed only if the statement is successfully logged.
> -
> - @see THD::binlog_query
> -
> - @param[in] thd Client thread
> - @param[in] tables Tables involved in the query
> -
> - @retval 0 No error; statement can be logged.
> - @retval -1 One of the error conditions above applies (1, 2, 4, 5, or 6).
> -*/
> -
> -int THD::decide_logging_format(TABLE_LIST *tables)
> -{
> - DBUG_ENTER("THD::decide_logging_format");
> - DBUG_PRINT("info", ("query: %s", query()));
> - DBUG_PRINT("info", ("variables.binlog_format: %u",
> - variables.binlog_format));
> - DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags(): 0x%x",
> - lex->get_stmt_unsafe_flags()));
> -
> - /*
> - We should not decide logging format if the binlog is closed or
> - binlogging is off, or if the statement is filtered out from the
> - binlog by filtering rules.
> - */
> - if (mysql_bin_log.is_open() && (variables.option_bits &
> OPTION_BIN_LOG) &&
> - !(variables.binlog_format == BINLOG_FORMAT_STMT &&
> - !binlog_filter->db_ok(db)))
> - {
> - /*
> - Compute one bit field with the union of all the engine
> - capabilities, and one with the intersection of all the engine
> - capabilities.
> - */
> - handler::Table_flags flags_write_some_set= 0;
> - handler::Table_flags flags_some_set= 0;
> - handler::Table_flags flags_write_all_set=
> - HA_BINLOG_ROW_CAPABLE | HA_BINLOG_STMT_CAPABLE;
> -
> - /*
> - If different types of engines are about to be updated.
> - For example: Innodb and Falcon; Innodb and MyIsam.
> - */
> - my_bool multi_write_engine= FALSE;
> - /*
> - If different types of engines are about to be accessed
> - and any of them is about to be updated. For example:
> - Innodb and Falcon; Innodb and MyIsam.
> - */
> - my_bool multi_access_engine= FALSE;
> - /*
> - If non-transactional and transactional engines are about
> - to be accessed and any of them is about to be updated.
> - For example: Innodb and MyIsam.
> - */
> - my_bool trans_non_trans_access_engines= FALSE;
> - /*
> - If all engines that are about to be updated are
> - transactional.
> - */
> - my_bool all_trans_write_engines= TRUE;
> - TABLE* prev_write_table= NULL;
> - TABLE* prev_access_table= NULL;
> -
> -#ifndef DBUG_OFF
> - {
> - static const char *prelocked_mode_name[] = {
> - "NON_PRELOCKED",
> - "PRELOCKED",
> - "PRELOCKED_UNDER_LOCK_TABLES",
> - };
> - DBUG_PRINT("debug", ("prelocked_mode: %s",
> - prelocked_mode_name[locked_tables_mode]));
> - }
> -#endif
> -
> - /*
> - Get the capabilities vector for all involved storage engines and
> - mask out the flags for the binary log.
> - */
> - for (TABLE_LIST *table= tables; table; table= table->next_global)
> - {
> - if (table->placeholder())
> - continue;
> - if (table->table->s->table_category == TABLE_CATEGORY_PERFORMANCE)
> - lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_TABLE);
> - handler::Table_flags const flags=
> table->table->file->ha_table_flags();
> - DBUG_PRINT("info", ("table: %s; ha_table_flags: 0x%llx",
> - table->table_name, flags));
> - if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
> - {
> - if (prev_write_table && prev_write_table->file->ht !=
> - table->table->file->ht)
> - multi_write_engine= TRUE;
> - /*
> - Every temporary table must be always written to the binary
> - log in transaction boundaries and as such we artificially
> - classify them as transactional.
> -
> - Indirectly, this avoids classifying a temporary table created
> - on a non-transactional engine as unsafe when it is modified
> - after any transactional table:
> -
> - BEGIN;
> - INSERT INTO innodb_t VALUES (1);
> - INSERT INTO myisam_t_temp VALUES (1);
> - COMMIT;
> -
> - BINARY LOG:
> -
> - BEGIN;
> - INSERT INTO innodb_t VALUES (1);
> - INSERT INTO myisam_t_temp VALUES (1);
> - COMMIT;
> - */
> - all_trans_write_engines= all_trans_write_engines &&
> - (table->table->file->has_transactions()
> ||
> - table->table->s->tmp_table);
> - prev_write_table= table->table;
> - flags_write_all_set &= flags;
> - flags_write_some_set |= flags;
> - }
> - flags_some_set |= flags;
> - /*
> - The mixture of non-transactional and transactional tables must
> - identified and classified as unsafe. However, a temporary table
> - must be always handled as a transactional table. Based on that,
> - we have the following statements classified as mixed and by
> - consequence as unsafe:
> -
> - 1: INSERT INTO myisam_t SELECT * FROM innodb_t;
> -
> - 2: INSERT INTO innodb_t SELECT * FROM myisam_t;
> -
> - 3: INSERT INTO myisam_t SELECT * FROM myisam_t_temp;
> -
> - 4: INSERT INTO myisam_t_temp SELECT * FROM myisam_t;
> -
> - 5: CREATE TEMPORARY TABLE myisam_t_temp SELECT * FROM mysiam_t;
> -
> - The following statements are not considered mixed and as such
> - are safe:
> -
> - 1: INSERT INTO innodb_t SELECT * FROM myisam_t_temp;
> -
> - 2: INSERT INTO myisam_t_temp SELECT * FROM innodb_t_temp;
> - */
> - if (!trans_non_trans_access_engines && prev_access_table &&
> - (lex->sql_command != SQLCOM_CREATE_TABLE ||
> - (lex->sql_command == SQLCOM_CREATE_TABLE &&
> - (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))))
> - {
> - my_bool prev_trans;
> - my_bool act_trans;
> - if (prev_access_table->s->tmp_table ||
> table->table->s->tmp_table)
> - {
> - prev_trans= prev_access_table->s->tmp_table ? TRUE :
> - prev_access_table->file->has_transactions();
> - act_trans= table->table->s->tmp_table ? TRUE :
> - table->table->file->has_transactions();
> - }
> - else
> - {
> - prev_trans= prev_access_table->file->has_transactions();
> - act_trans= table->table->file->has_transactions();
> - }
> - trans_non_trans_access_engines= (prev_trans != act_trans);
> - multi_access_engine= TRUE;
> - }
> - thread_temporary_used |= table->table->s->tmp_table;
> - prev_access_table= table->table;
> - }
> -
> - DBUG_PRINT("info", ("flags_write_all_set: 0x%llx", flags_write_all_set));
> - DBUG_PRINT("info", ("flags_write_some_set: 0x%llx", flags_write_some_set));
> - DBUG_PRINT("info", ("flags_some_set: 0x%llx", flags_some_set));
> - DBUG_PRINT("info", ("multi_write_engine: %d", multi_write_engine));
> - DBUG_PRINT("info", ("multi_access_engine: %d", multi_access_engine));
> - DBUG_PRINT("info", ("trans_non_trans_access_engines: %d",
> - trans_non_trans_access_engines));
> -
> - int error= 0;
> - int unsafe_flags;
> -
> - /*
> - Set the statement as unsafe if:
> -
> - . it is a mixed statement, i.e. access transactional and non-transactional
> - tables, and update any of them;
> -
> - or:
> -
> - . an early statement updated a transactional table;
> - . and, the current statement updates a non-transactional table.
> -
> - Any mixed statement is classified as unsafe to ensure that mixed mode is
> - completely safe. Consider the following example to understand why we
> - decided to do this:
> -
> - Note that mixed statements such as
> -
> - 1: INSERT INTO myisam_t SELECT * FROM innodb_t;
> -
> - 2: INSERT INTO innodb_t SELECT * FROM myisam_t;
> -
> - 3: INSERT INTO myisam_t SELECT * FROM myisam_t_temp;
> -
> - 4: INSERT INTO myisam_t_temp SELECT * FROM myisam_t;
> -
> - 5: CREATE TEMPORARY TABLE myisam_t_temp SELECT * FROM mysiam_t;
> -
> - are classified as unsafe to ensure that in mixed mode the execution is
> - completely safe and equivalent to the row mode. Consider the following
> - statements and sessions (connections) to understand the reason:
> -
> - con1: INSERT INTO innodb_t VALUES (1);
> - con1: INSERT INTO innodb_t VALUES (100);
> -
> - con1: BEGIN
> - con2: INSERT INTO myisam_t SELECT * FROM innodb_t;
> - con1: INSERT INTO innodb_t VALUES (200);
> - con1: COMMIT;
> -
> - The point is that the concurrent statements may be written into the binary
> log
> - in a way different from the execution. For example,
> -
> - BINARY LOG:
> -
> - con2: BEGIN;
> - con2: INSERT INTO myisam_t SELECT * FROM innodb_t;
> - con2: COMMIT;
> - con1: BEGIN
> - con1: INSERT INTO innodb_t VALUES (200);
> - con1: COMMIT;
> -
> - ....
> -
> - or
> -
> - BINARY LOG:
> -
> - con1: BEGIN
> - con1: INSERT INTO innodb_t VALUES (200);
> - con1: COMMIT;
> - con2: BEGIN;
> - con2: INSERT INTO myisam_t SELECT * FROM innodb_t;
> - con2: COMMIT;
> -
> - Clearly, this may become a problem in STMT mode and setting the statement
> - as unsafe will make rows to be written into the binary log in MIXED mode
> - and as such the problem will not stand.
> -
> - In STMT mode, although such statement is classified as unsafe, i.e.
> -
> - INSERT INTO myisam_t SELECT * FROM innodb_t;
> -
> - there is no enough information to avoid writing it outside the boundaries
> - of a transaction. This is not a problem if we are considering snapshot
> - isolation level but if we have pure repeatable read or serializable the
> - lock history on the slave will be different from the master.
> - */
> - if (trans_non_trans_access_engines)
> - lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_MIXED_STATEMENT);
> - else if (trans_has_updated_trans_table(this) &&
> !all_trans_write_engines)
> - lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_NONTRANS_AFTER_TRANS);
> -
> - /*
> - If more than one engine is involved in the statement and at
> - least one is doing it's own logging (is *self-logging*), the
> - statement cannot be logged atomically, so we generate an error
> - rather than allowing the binlog to become corrupt.
> - */
> - if (multi_write_engine &&
> - (flags_write_some_set & HA_HAS_OWN_BINLOGGING))
> - my_error((error= ER_BINLOG_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE),
> - MYF(0));
> - else if (multi_access_engine && flags_some_set &
> HA_HAS_OWN_BINLOGGING)
> -
> lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_MULTIPLE_ENGINES_AND_SELF_LOGGING_ENGINE);
> -
> - /* both statement-only and row-only engines involved */
> - if ((flags_write_all_set & (HA_BINLOG_STMT_CAPABLE | HA_BINLOG_ROW_CAPABLE))
> == 0)
> - {
> - /*
> - 1. Error: Binary logging impossible since both row-incapable
> - engines and statement-incapable engines are involved
> - */
> - my_error((error= ER_BINLOG_ROW_ENGINE_AND_STMT_ENGINE), MYF(0));
> - }
> - /* statement-only engines involved */
> - else if ((flags_write_all_set & HA_BINLOG_ROW_CAPABLE) == 0)
> - {
> - if (lex->is_stmt_row_injection())
> - {
> - /*
> - 4. Error: Cannot execute row injection since table uses
> - storage engine limited to statement-logging
> - */
> - my_error((error= ER_BINLOG_ROW_INJECTION_AND_STMT_ENGINE), MYF(0));
> - }
> - else if (variables.binlog_format == BINLOG_FORMAT_ROW)
> - {
> - /*
> - 2. Error: Cannot modify table that uses a storage engine
> - limited to statement-logging when BINLOG_FORMAT = ROW
> - */
> - my_error((error= ER_BINLOG_ROW_MODE_AND_STMT_ENGINE), MYF(0));
> - }
> - else if ((unsafe_flags= lex->get_stmt_unsafe_flags()) != 0)
> - {
> - /*
> - 3. Error: Cannot execute statement: binlogging of unsafe
> - statement is impossible when storage engine is limited to
> - statement-logging and BINLOG_FORMAT = MIXED.
> - */
> - for (int unsafe_type= 0;
> - unsafe_type < LEX::BINLOG_STMT_UNSAFE_COUNT;
> - unsafe_type++)
> - if (unsafe_flags & (1 << unsafe_type))
> - my_error((error= ER_BINLOG_UNSAFE_AND_STMT_ENGINE), MYF(0),
> - ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]));
> - }
> - /* log in statement format! */
> - }
> - /* no statement-only engines */
> - else
> - {
> - /* binlog_format = STATEMENT */
> - if (variables.binlog_format == BINLOG_FORMAT_STMT)
> - {
> - if (lex->is_stmt_row_injection())
> - {
> - /*
> - 6. Error: Cannot execute row injection since
> - BINLOG_FORMAT = STATEMENT
> - */
> - my_error((error= ER_BINLOG_ROW_INJECTION_AND_STMT_MODE), MYF(0));
> - }
> - else if ((flags_write_all_set & HA_BINLOG_STMT_CAPABLE) == 0)
> - {
> - /*
> - 5. Error: Cannot modify table that uses a storage engine
> - limited to row-logging when binlog_format = STATEMENT
> - */
> - my_error((error= ER_BINLOG_STMT_MODE_AND_ROW_ENGINE), MYF(0), "");
> - }
> - else if ((unsafe_flags= lex->get_stmt_unsafe_flags()) != 0)
> - {
> - /*
> - 7. Warning: Unsafe statement logged as statement due to
> - binlog_format = STATEMENT
> - */
> - binlog_unsafe_warning_flags|= unsafe_flags;
> - DBUG_PRINT("info", ("Scheduling warning to be issued by "
> - "binlog_query: '%s'",
> - ER(ER_BINLOG_UNSAFE_STATEMENT)));
> - DBUG_PRINT("info", ("binlog_unsafe_warning_flags: 0x%x",
> - binlog_unsafe_warning_flags));
> - }
> - /* log in statement format! */
> - }
> - /* No statement-only engines and binlog_format != STATEMENT.
> - I.e., nothing prevents us from row logging if needed. */
> - else
> - {
> - if (lex->is_stmt_unsafe() || lex->is_stmt_row_injection()
> - || (flags_write_all_set & HA_BINLOG_STMT_CAPABLE) == 0)
> - {
> - /* log in row format! */
> - set_current_stmt_binlog_format_row_if_mixed();
> - }
> - }
> - }
> -
> - if (error) {
> - DBUG_PRINT("info", ("decision: no logging since an error was generated"));
> - DBUG_RETURN(-1);
> - }
> - DBUG_PRINT("info", ("decision: logging in %s format",
> - is_current_stmt_binlog_format_row() ?
> - "ROW" : "STATEMENT"));
> - }
> -#ifndef DBUG_OFF
> - else
> - DBUG_PRINT("info", ("decision: no logging since "
> - "mysql_bin_log.is_open() = %d "
> - "and (options & OPTION_BIN_LOG) = 0x%llx "
> - "and binlog_format = %u "
> - "and binlog_filter->db_ok(db) = %d",
> - mysql_bin_log.is_open(),
> - (variables.option_bits & OPTION_BIN_LOG),
> - variables.binlog_format,
> - binlog_filter->db_ok(db)));
> -#endif
> -
> - DBUG_RETURN(0);
> -}
> -
> -
> -/*
> - Implementation of interface to write rows to the binary log through the
> - thread. The thread is responsible for writing the rows it has
> - inserted/updated/deleted.
> -*/
> -
> -#ifndef MYSQL_CLIENT
> -
> -/*
> - Template member function for ensuring that there is an rows log
> - event of the apropriate type before proceeding.
> -
> - PRE CONDITION:
> - - Events of type 'RowEventT' have the type code 'type_code'.
> -
> - POST CONDITION:
> - If a non-NULL pointer is returned, the pending event for thread 'thd' will
> - be an event of type 'RowEventT' (which have the type code 'type_code')
> - will either empty or have enough space to hold 'needed' bytes. In
> - addition, the columns bitmap will be correct for the row, meaning that
> - the pending event will be flushed if the columns in the event differ from
> - the columns suppled to the function.
> -
> - RETURNS
> - If no error, a non-NULL pending event (either one which already existed or
> - the newly created one).
> - If error, NULL.
> - */
> -
> -template <class RowsEventT> Rows_log_event*
> -THD::binlog_prepare_pending_rows_event(TABLE* table, uint32 serv_id,
> - MY_BITMAP const* cols,
> - size_t colcnt,
> - size_t needed,
> - bool is_transactional,
> - RowsEventT *hint __attribute__((unused)))
> -{
> - DBUG_ENTER("binlog_prepare_pending_rows_event");
> - /* Pre-conditions */
> - DBUG_ASSERT(table->s->table_map_id != ~0UL);
> -
> - /* Fetch the type code for the RowsEventT template parameter */
> - int const type_code= RowsEventT::TYPE_CODE;
> -
> - /*
> - There is no good place to set up the transactional data, so we
> - have to do it here.
> - */
> - if (binlog_setup_trx_data())
> - DBUG_RETURN(NULL);
> -
> - Rows_log_event* pending= binlog_get_pending_rows_event(is_transactional);
> -
> - if (unlikely(pending && !pending->is_valid()))
> - DBUG_RETURN(NULL);
> -
> - /*
> - Check if the current event is non-NULL and a write-rows
> - event. Also check if the table provided is mapped: if it is not,
> - then we have switched to writing to a new table.
> - If there is no pending event, we need to create one. If there is a pending
> - event, but it's not about the same table id, or not of the same type
> - (between Write, Update and Delete), or not the same affected columns, or
> - going to be too big, flush this event to disk and create a new pending
> - event.
> - */
> - if (!pending ||
> - pending->server_id != serv_id ||
> - pending->get_table_id() != table->s->table_map_id ||
> - pending->get_type_code() != type_code ||
> - pending->get_data_size() + needed > opt_binlog_rows_event_max_size ||
> - pending->get_width() != colcnt ||
> - !bitmap_cmp(pending->get_cols(), cols))
> - {
> - /* Create a new RowsEventT... */
> - Rows_log_event* const
> - ev= new RowsEventT(this, table, table->s->table_map_id, cols,
> - is_transactional);
> - if (unlikely(!ev))
> - DBUG_RETURN(NULL);
> - ev->server_id= serv_id; // I don't like this, it's too easy to forget.
> - /*
> - flush the pending event and replace it with the newly created
> - event...
> - */
> - if (unlikely(
> - mysql_bin_log.flush_and_set_pending_rows_event(this, ev,
> - is_transactional)))
> - {
> - delete ev;
> - DBUG_RETURN(NULL);
> - }
> -
> - DBUG_RETURN(ev); /* This is the new pending event */
> - }
> - DBUG_RETURN(pending); /* This is the current pending event */
> -}
> -
> -#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
> -/*
> - Instantiate the versions we need, we have -fno-implicit-template as
> - compiling option.
> -*/
> -template Rows_log_event*
> -THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*,
> - size_t, size_t, bool,
> - Write_rows_log_event*);
> -
> -template Rows_log_event*
> -THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*,
> - size_t colcnt, size_t, bool,
> - Delete_rows_log_event *);
> -
> -template Rows_log_event*
> -THD::binlog_prepare_pending_rows_event(TABLE*, uint32, MY_BITMAP const*,
> - size_t colcnt, size_t, bool,
> - Update_rows_log_event *);
> -#endif
> -
> #ifdef NOT_USED
> static char const*
> field_type_name(enum_field_types type)
> @@ -4147,505 +3543,3 @@ field_type_name(enum_field_types type)
> return "Unknown";
> }
> #endif
> -
> -
> -namespace {
> - /**
> - Class to handle temporary allocation of memory for row data.
> -
> - The responsibilities of the class is to provide memory for
> - packing one or two rows of packed data (depending on what
> - constructor is called).
> -
> - In order to make the allocation more efficient for "simple" rows,
> - i.e., rows that do not contain any blobs, a pointer to the
> - allocated memory is of memory is stored in the table structure
> - for simple rows. If memory for a table containing a blob field
> - is requested, only memory for that is allocated, and subsequently
> - released when the object is destroyed.
> -
> - */
> - class Row_data_memory {
> - public:
> - /**
> - Build an object to keep track of a block-local piece of memory
> - for storing a row of data.
> -
> - @param table
> - Table where the pre-allocated memory is stored.
> -
> - @param length
> - Length of data that is needed, if the record contain blobs.
> - */
> - Row_data_memory(TABLE *table, size_t const len1)
> - : m_memory(0)
> - {
> -#ifndef DBUG_OFF
> - m_alloc_checked= FALSE;
> -#endif
> - allocate_memory(table, len1);
> - m_ptr[0]= has_memory() ? m_memory : 0;
> - m_ptr[1]= 0;
> - }
> -
> - Row_data_memory(TABLE *table, size_t const len1, size_t const len2)
> - : m_memory(0)
> - {
> -#ifndef DBUG_OFF
> - m_alloc_checked= FALSE;
> -#endif
> - allocate_memory(table, len1 + len2);
> - m_ptr[0]= has_memory() ? m_memory : 0;
> - m_ptr[1]= has_memory() ? m_memory + len1 : 0;
> - }
> -
> - ~Row_data_memory()
> - {
> - if (m_memory != 0 && m_release_memory_on_destruction)
> - my_free((uchar*) m_memory, MYF(MY_WME));
> - }
> -
> - /**
> - Is there memory allocated?
> -
> - @retval true There is memory allocated
> - @retval false Memory allocation failed
> - */
> - bool has_memory() const {
> -#ifndef DBUG_OFF
> - m_alloc_checked= TRUE;
> -#endif
> - return m_memory != 0;
> - }
> -
> - uchar *slot(uint s)
> - {
> - DBUG_ASSERT(s < sizeof(m_ptr)/sizeof(*m_ptr));
> - DBUG_ASSERT(m_ptr[s] != 0);
> - DBUG_ASSERT(m_alloc_checked == TRUE);
> - return m_ptr[s];
> - }
> -
> - private:
> - void allocate_memory(TABLE *const table, size_t const total_length)
> - {
> - if (table->s->blob_fields == 0)
> - {
> - /*
> - The maximum length of a packed record is less than this
> - length. We use this value instead of the supplied length
> - when allocating memory for records, since we don't know how
> - the memory will be used in future allocations.
> -
> - Since table->s->reclength is for unpacked records, we have
> - to add two bytes for each field, which can potentially be
> - added to hold the length of a packed field.
> - */
> - size_t const maxlen= table->s->reclength + 2 *
> table->s->fields;
> -
> - /*
> - Allocate memory for two records if memory hasn't been
> - allocated. We allocate memory for two records so that it can
> - be used when processing update rows as well.
> - */
> - if (table->write_row_record == 0)
> - table->write_row_record=
> - (uchar *) alloc_root(&table->mem_root, 2 * maxlen);
> - m_memory= table->write_row_record;
> - m_release_memory_on_destruction= FALSE;
> - }
> - else
> - {
> - m_memory= (uchar *) my_malloc(total_length, MYF(MY_WME));
> - m_release_memory_on_destruction= TRUE;
> - }
> - }
> -
> -#ifndef DBUG_OFF
> - mutable bool m_alloc_checked;
> -#endif
> - bool m_release_memory_on_destruction;
> - uchar *m_memory;
> - uchar *m_ptr[2];
> - };
> -}
> -
> -
> -int THD::binlog_write_row(TABLE* table, bool is_trans,
> - MY_BITMAP const* cols, size_t colcnt,
> - uchar const *record)
> -{
> - DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
> mysql_bin_log.is_open());
> -
> - /*
> - Pack records into format for transfer. We are allocating more
> - memory than needed, but that doesn't matter.
> - */
> - Row_data_memory memory(table, max_row_length(table, record));
> - if (!memory.has_memory())
> - return HA_ERR_OUT_OF_MEM;
> -
> - uchar *row_data= memory.slot(0);
> -
> - size_t const len= pack_row(table, cols, row_data, record);
> -
> - Rows_log_event* const ev=
> - binlog_prepare_pending_rows_event(table, server_id, cols, colcnt,
> - len, is_trans,
> - static_cast<Write_rows_log_event*>(0));
> -
> - if (unlikely(ev == 0))
> - return HA_ERR_OUT_OF_MEM;
> -
> - return ev->add_row_data(row_data, len);
> -}
> -
> -int THD::binlog_update_row(TABLE* table, bool is_trans,
> - MY_BITMAP const* cols, size_t colcnt,
> - const uchar *before_record,
> - const uchar *after_record)
> -{
> - DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
> mysql_bin_log.is_open());
> -
> - size_t const before_maxlen = max_row_length(table, before_record);
> - size_t const after_maxlen = max_row_length(table, after_record);
> -
> - Row_data_memory row_data(table, before_maxlen, after_maxlen);
> - if (!row_data.has_memory())
> - return HA_ERR_OUT_OF_MEM;
> -
> - uchar *before_row= row_data.slot(0);
> - uchar *after_row= row_data.slot(1);
> -
> - size_t const before_size= pack_row(table, cols, before_row,
> - before_record);
> - size_t const after_size= pack_row(table, cols, after_row,
> - after_record);
> -
> - /*
> - Don't print debug messages when running valgrind since they can
> - trigger false warnings.
> - */
> -#ifndef HAVE_purify
> - DBUG_DUMP("before_record", before_record, table->s->reclength);
> - DBUG_DUMP("after_record", after_record, table->s->reclength);
> - DBUG_DUMP("before_row", before_row, before_size);
> - DBUG_DUMP("after_row", after_row, after_size);
> -#endif
> -
> - Rows_log_event* const ev=
> - binlog_prepare_pending_rows_event(table, server_id, cols, colcnt,
> - before_size + after_size, is_trans,
> - static_cast<Update_rows_log_event*>(0));
> -
> - if (unlikely(ev == 0))
> - return HA_ERR_OUT_OF_MEM;
> -
> - return
> - ev->add_row_data(before_row, before_size) ||
> - ev->add_row_data(after_row, after_size);
> -}
> -
> -int THD::binlog_delete_row(TABLE* table, bool is_trans,
> - MY_BITMAP const* cols, size_t colcnt,
> - uchar const *record)
> -{
> - DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
> mysql_bin_log.is_open());
> -
> - /*
> - Pack records into format for transfer. We are allocating more
> - memory than needed, but that doesn't matter.
> - */
> - Row_data_memory memory(table, max_row_length(table, record));
> - if (unlikely(!memory.has_memory()))
> - return HA_ERR_OUT_OF_MEM;
> -
> - uchar *row_data= memory.slot(0);
> -
> - size_t const len= pack_row(table, cols, row_data, record);
> -
> - Rows_log_event* const ev=
> - binlog_prepare_pending_rows_event(table, server_id, cols, colcnt,
> - len, is_trans,
> - static_cast<Delete_rows_log_event*>(0));
> -
> - if (unlikely(ev == 0))
> - return HA_ERR_OUT_OF_MEM;
> -
> - return ev->add_row_data(row_data, len);
> -}
> -
> -
> -int THD::binlog_remove_pending_rows_event(bool clear_maps,
> - bool is_transactional)
> -{
> - DBUG_ENTER("THD::binlog_remove_pending_rows_event");
> -
> - if (!mysql_bin_log.is_open())
> - DBUG_RETURN(0);
> -
> - mysql_bin_log.remove_pending_rows_event(this, is_transactional);
> -
> - if (clear_maps)
> - binlog_table_maps= 0;
> -
> - DBUG_RETURN(0);
> -}
> -
> -int THD::binlog_flush_pending_rows_event(bool stmt_end, bool is_transactional)
> -{
> - DBUG_ENTER("THD::binlog_flush_pending_rows_event");
> - /*
> - We shall flush the pending event even if we are not in row-based
> - mode: it might be the case that we left row-based mode before
> - flushing anything (e.g., if we have explicitly locked tables).
> - */
> - if (!mysql_bin_log.is_open())
> - DBUG_RETURN(0);
> -
> - /*
> - Mark the event as the last event of a statement if the stmt_end
> - flag is set.
> - */
> - int error= 0;
> - if (Rows_log_event *pending= binlog_get_pending_rows_event(is_transactional))
> - {
> - if (stmt_end)
> - {
> - pending->set_flags(Rows_log_event::STMT_END_F);
> - pending->flags|= LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F;
> - binlog_table_maps= 0;
> - }
> -
> - error= mysql_bin_log.flush_and_set_pending_rows_event(this, 0,
> - is_transactional);
> - }
> -
> - DBUG_RETURN(error);
> -}
> -
> -
> -#if !defined(DBUG_OFF) && !defined(_lint)
> -static const char *
> -show_query_type(THD::enum_binlog_query_type qtype)
> -{
> - switch (qtype) {
> - case THD::ROW_QUERY_TYPE:
> - return "ROW";
> - case THD::STMT_QUERY_TYPE:
> - return "STMT";
> - case THD::QUERY_TYPE_COUNT:
> - default:
> - DBUG_ASSERT(0 <= qtype && qtype < THD::QUERY_TYPE_COUNT);
> - }
> - static char buf[64];
> - sprintf(buf, "UNKNOWN#%d", qtype);
> - return buf;
> -}
> -#endif
> -
> -
> -/**
> - Auxiliary method used by @c binlog_query() to raise warnings.
> -
> - The type of warning and the type of unsafeness is stored in
> - THD::binlog_unsafe_warning_flags.
> -*/
> -void THD::issue_unsafe_warnings()
> -{
> - DBUG_ENTER("issue_unsafe_warnings");
> - /*
> - Ensure that binlog_unsafe_warning_flags is big enough to hold all
> - bits. This is actually a constant expression.
> - */
> - DBUG_ASSERT(2 * LEX::BINLOG_STMT_UNSAFE_COUNT <=
> - sizeof(binlog_unsafe_warning_flags) * CHAR_BIT);
> -
> - uint32 unsafe_type_flags= binlog_unsafe_warning_flags;
> -
> - /*
> - Clear: (1) bits above BINLOG_STMT_UNSAFE_COUNT; (2) bits for
> - warnings that have been printed already.
> - */
> - unsafe_type_flags &= (LEX::BINLOG_STMT_UNSAFE_ALL_FLAGS ^
> - (unsafe_type_flags >>
> LEX::BINLOG_STMT_UNSAFE_COUNT));
> - /* If all warnings have been printed already, return. */
> - if (unsafe_type_flags == 0)
> - DBUG_VOID_RETURN;
> -
> - DBUG_PRINT("info", ("unsafe_type_flags: 0x%x", unsafe_type_flags));
> -
> - /*
> - For each unsafe_type, check if the statement is unsafe in this way
> - and issue a warning.
> - */
> - for (int unsafe_type=0;
> - unsafe_type < LEX::BINLOG_STMT_UNSAFE_COUNT;
> - unsafe_type++)
> - {
> - if ((unsafe_type_flags & (1 << unsafe_type)) != 0)
> - {
> - push_warning_printf(this, MYSQL_ERROR::WARN_LEVEL_NOTE,
> - ER_BINLOG_UNSAFE_STATEMENT,
> - ER(ER_BINLOG_UNSAFE_STATEMENT),
> - ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]));
> - if (global_system_variables.log_warnings)
> - {
> - char buf[MYSQL_ERRMSG_SIZE * 2];
> - sprintf(buf, ER(ER_BINLOG_UNSAFE_STATEMENT),
> - ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]));
> - sql_print_warning(ER(ER_MESSAGE_AND_STATEMENT), buf, query());
> - }
> - }
> - }
> - /*
> - Mark these unsafe types as already printed, to avoid printing
> - warnings for them again.
> - */
> - binlog_unsafe_warning_flags|=
> - unsafe_type_flags << LEX::BINLOG_STMT_UNSAFE_COUNT;
> - DBUG_VOID_RETURN;
> -}
> -
> -
> -/**
> - Log the current query.
> -
> - The query will be logged in either row format or statement format
> - depending on the value of @c current_stmt_binlog_format_row field and
> - the value of the @c qtype parameter.
> -
> - This function must be called:
> -
> - - After the all calls to ha_*_row() functions have been issued.
> -
> - - After any writes to system tables. Rationale: if system tables
> - were written after a call to this function, and the master crashes
> - after the call to this function and before writing the system
> - tables, then the master and slave get out of sync.
> -
> - - Before tables are unlocked and closed.
> -
> - @see decide_logging_format
> -
> - @retval 0 Success
> -
> - @retval nonzero If there is a failure when writing the query (e.g.,
> - write failure), then the error code is returned.
> -*/
> -int THD::binlog_query(THD::enum_binlog_query_type qtype, char const *query_arg,
> - ulong query_len, bool is_trans, bool direct,
> - bool suppress_use, int errcode)
> -{
> - DBUG_ENTER("THD::binlog_query");
> - DBUG_PRINT("enter", ("qtype: %s query: '%s'",
> - show_query_type(qtype), query_arg));
> - DBUG_ASSERT(query_arg && mysql_bin_log.is_open());
> -
> - /*
> - If we are not in prelocked mode, mysql_unlock_tables() will be
> - called after this binlog_query(), so we have to flush the pending
> - rows event with the STMT_END_F set to unlock all tables at the
> - slave side as well.
> -
> - If we are in prelocked mode, the flushing will be done inside the
> - top-most close_thread_tables().
> - */
> - if (this->locked_tables_mode <= LTM_LOCK_TABLES)
> - if (int error= binlog_flush_pending_rows_event(TRUE, is_trans))
> - DBUG_RETURN(error);
> -
> - /*
> - Warnings for unsafe statements logged in statement format are
> - printed here instead of in decide_logging_format(). This is
> - because the warnings should be printed only if the statement is
> - actually logged. When executing decide_logging_format(), we cannot
> - know for sure if the statement will be logged.
> - */
> - if (sql_log_bin_toplevel)
> - issue_unsafe_warnings();
> -
> - switch (qtype) {
> - /*
> - ROW_QUERY_TYPE means that the statement may be logged either in
> - row format or in statement format. If
> - current_stmt_binlog_format is row, it means that the
> - statement has already been logged in row format and hence shall
> - not be logged again.
> - */
> - case THD::ROW_QUERY_TYPE:
> - DBUG_PRINT("debug",
> - ("is_current_stmt_binlog_format_row: %d",
> - is_current_stmt_binlog_format_row()));
> - if (is_current_stmt_binlog_format_row())
> - DBUG_RETURN(0);
> - /* Fall through */
> -
> - /*
> - STMT_QUERY_TYPE means that the query must be logged in statement
> - format; it cannot be logged in row format. This is typically
> - used by DDL statements. It is an error to use this query type
> - if current_stmt_binlog_format_row is row.
> -
> - @todo Currently there are places that call this method with
> - STMT_QUERY_TYPE and current_stmt_binlog_format is row. Fix those
> - places and add assert to ensure correct behavior. /Sven
> - */
> - case THD::STMT_QUERY_TYPE:
> - /*
> - The MYSQL_LOG::write() function will set the STMT_END_F flag and
> - flush the pending rows event if necessary.
> - */
> - {
> - Query_log_event qinfo(this, query_arg, query_len, is_trans, direct,
> - suppress_use, errcode);
> - qinfo.flags|= LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F;
> - /*
> - Binlog table maps will be irrelevant after a Query_log_event
> - (they are just removed on the slave side) so after the query
> - log event is written to the binary log, we pretend that no
> - table maps were written.
> - */
> - int error= mysql_bin_log.write(&qinfo);
> - binlog_table_maps= 0;
> - DBUG_RETURN(error);
> - }
> - break;
> -
> - case THD::QUERY_TYPE_COUNT:
> - default:
> - DBUG_ASSERT(0 <= qtype && qtype < QUERY_TYPE_COUNT);
> - }
> - DBUG_RETURN(0);
> -}
> -
> -bool Discrete_intervals_list::append(ulonglong start, ulonglong val,
> - ulonglong incr)
> -{
> - DBUG_ENTER("Discrete_intervals_list::append");
> - /* first, see if this can be merged with previous */
> - if ((head == NULL) || tail->merge_if_contiguous(start, val, incr))
> - {
> - /* it cannot, so need to add a new interval */
> - Discrete_interval *new_interval= new Discrete_interval(start, val, incr);
> - DBUG_RETURN(append(new_interval));
> - }
> - DBUG_RETURN(0);
> -}
> -
> -bool Discrete_intervals_list::append(Discrete_interval *new_interval)
> -{
> - DBUG_ENTER("Discrete_intervals_list::append");
> - if (unlikely(new_interval == NULL))
> - DBUG_RETURN(1);
> - DBUG_PRINT("info",("adding new auto_increment interval"));
> - if (head == NULL)
> - head= current= new_interval;
> - else
> - tail->next= new_interval;
> - tail= new_interval;
> - elements++;
> - DBUG_RETURN(0);
> -}
> -
> -#endif /* !defined(MYSQL_CLIENT) */
>
> === modified file 'sql/sql_load.cc'
> --- a/sql/sql_load.cc 2010-05-05 10:34:20 +0000
> +++ b/sql/sql_load.cc 2010-05-13 05:54:26 +0000
> @@ -34,7 +34,7 @@
> // LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F
> #include <m_ctype.h>
> #include "rpl_mi.h"
> -#include "sql_repl.h"
> +#include "slave.h"
> #include "sp_head.h"
> #include "sql_trigger.h"
>
>
> === modified file 'sql/sql_parse.cc'
> --- a/sql/sql_parse.cc 2010-04-26 09:02:29 +0000
> +++ b/sql/sql_parse.cc 2010-05-13 05:54:26 +0000
> @@ -76,7 +76,8 @@
> #include "sql_help.h" // mysqld_help
> #include "rpl_constants.h" // Incident, INCIDENT_LOST_EVENTS
> #include "log_event.h"
> -#include "sql_repl.h"
> +#include "slave.h"
> +#include "master.h"
> #include "rpl_filter.h"
> #include "repl_failsafe.h"
> #include <m_ctype.h>
> @@ -2374,7 +2375,13 @@ case SQLCOM_PREPARE:
> res = show_slave_hosts(thd);
> break;
> }
> - case SQLCOM_SHOW_RELAYLOG_EVENTS: /* fall through */
> + case SQLCOM_SHOW_RELAYLOG_EVENTS:
> + {
> + if (check_global_access(thd, REPL_SLAVE_ACL))
> + goto error;
> + res = mysql_show_relaylog_events(thd);
> + break;
> + }
> case SQLCOM_SHOW_BINLOG_EVENTS:
> {
> if (check_global_access(thd, REPL_SLAVE_ACL))
>
> === removed file 'sql/sql_repl.h'
> --- a/sql/sql_repl.h 2010-03-31 14:05:33 +0000
> +++ b/sql/sql_repl.h 1970-01-01 00:00:00 +0000
> @@ -1,72 +0,0 @@
> -/* Copyright (C) 2000-2006 MySQL AB & Sasha
> -
> - 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
> - the Free Software Foundation; version 2 of the License.
> -
> - This program is distributed in the hope that it will be useful,
> - but WITHOUT ANY WARRANTY; without even the implied warranty of
> - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> - GNU General Public License for more details.
> -
> - You should have received a copy of the GNU General Public License
> - along with this program; if not, write to the Free Software
> - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
> -
> -#ifndef SQL_REPL_INCLUDED
> -#define SQL_REPL_INCLUDED
> -
> -#include "rpl_filter.h"
> -
> -#ifdef HAVE_REPLICATION
> -#include "slave.h"
> -
> -typedef struct st_slave_info
> -{
> - uint32 server_id;
> - uint32 rpl_recovery_rank, master_id;
> - char host[HOSTNAME_LENGTH+1];
> - char user[USERNAME_LENGTH+1];
> - char password[MAX_PASSWORD_LENGTH+1];
> - uint16 port;
> - THD* thd;
> -} SLAVE_INFO;
> -
> -extern my_bool opt_show_slave_auth_info;
> -extern char *master_host, *master_info_file;
> -extern bool server_id_supplied;
> -
> -extern int max_binlog_dump_events;
> -extern my_bool opt_sporadic_binlog_dump_fail;
> -
> -int start_slave(THD* thd, Master_info* mi, bool net_report);
> -int stop_slave(THD* thd, Master_info* mi, bool net_report);
> -bool change_master(THD* thd, Master_info* mi);
> -bool mysql_show_binlog_events(THD* thd);
> -int cmp_master_pos(const char* log_file_name1, ulonglong log_pos1,
> - const char* log_file_name2, ulonglong log_pos2);
> -int reset_slave(THD *thd, Master_info* mi);
> -int reset_master(THD* thd);
> -bool purge_master_logs(THD* thd, const char* to_log);
> -bool purge_master_logs_before_date(THD* thd, time_t purge_time);
> -bool log_in_use(const char* log_name);
> -void adjust_linfo_offsets(my_off_t purge_offset);
> -bool show_binlogs(THD* thd);
> -extern int init_master_info(Master_info* mi);
> -void kill_zombie_dump_threads(uint32 slave_server_id);
> -int check_binlog_magic(IO_CACHE* log, const char** errmsg);
> -
> -typedef struct st_load_file_info
> -{
> - THD* thd;
> - my_off_t last_pos_in_file;
> - bool wrote_create_file, log_delayed;
> -} LOAD_FILE_INFO;
> -
> -int log_loaded_block(IO_CACHE* file);
> -int init_replication_sys_vars();
> -void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, ushort flags);
> -
> -#endif /* HAVE_REPLICATION */
> -
> -#endif /* SQL_REPL_INCLUDED */
>
> === modified file 'sql/structs.h'
> --- a/sql/structs.h 2010-03-31 14:05:33 +0000
> +++ b/sql/structs.h 2010-05-13 05:54:26 +0000
> @@ -358,8 +358,30 @@ public:
> return tmp;
> }
> ~Discrete_intervals_list() { empty(); };
> - bool append(ulonglong start, ulonglong val, ulonglong incr);
> - bool append(Discrete_interval *interval);
> + bool append(ulonglong start, ulonglong val, ulonglong incr)
> + {
> + /* first, see if this can be merged with previous */
> + if ((head == NULL) || tail->merge_if_contiguous(start, val, incr))
> + {
> + /* it cannot, so need to add a new interval */
> + Discrete_interval *new_interval= new Discrete_interval(start, val, incr);
> + return append(new_interval);
> + }
> + return 0;
> + }
> +
> + bool append(Discrete_interval *new_interval)
> + {
> + if (unlikely(new_interval == NULL))
> + return 1;
> + if (head == NULL)
> + head= current= new_interval;
> + else
> + tail->next= new_interval;
> + tail= new_interval;
> + elements++;
> + return 0;
> + }
> ulonglong minimum() const { return (head ? head->minimum() : 0); };
> ulonglong maximum() const { return (head ? tail->maximum() : 0); };
> uint nb_elements() const { return elements; }
>
> text/bzr-bundle type attachment
> (bzr/zhenxing.he@stripped)
> # Bazaar merge directive format 2 (Bazaar 0.90)
> # revision_id: zhenxing.he@stripped
> # target_branch: file:///media/sdb2/hezx/work/mysql/bzrwork/w3662\
> # /trunk-bugfixing/
> # testament_sha1: ad8bd39e334e1b8f5de5bbbb5cad371aa702c02c
> # timestamp: 2010-05-13 13:54:34 +0800
> # base_revision_id: zhenxing.he@stripped\
> # j8kzr62xlqln5c6f
> #
> # Begin bundle
> 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
>
>