Hi Zhenxing,
I have found some conflicts when merging the bundle. I guess
these are due to some recent merge from main into mysql-next-mr-rpl-merge.
Otherwise, the changes requested by Kostja (prefixing files with rpl_
and removing repl_failsafe.cc) seem good.
Approved.
Regards,
Luís
On 21/06/10 07:02, He Zhenxing wrote:
> #At file:///media/sdb2/hezx/work/mysql/bzr/w5385/next-mr-rpl-merge-new/ based on
> revid:zhenxing.he@stripped
>
> 2981 He Zhenxing 2010-06-21 [merge]
> Test commit
>
> removed:
> sql/repl_failsafe.cc
> sql/repl_failsafe.h
> sql/sql_repl.cc
> sql/sql_repl.h
> added:
> sql/binlog.cc
> sql/binlog.h
> sql/rpl_master.cc
> sql/rpl_master.h
> renamed:
> sql/slave.cc => sql/rpl_slave.cc
> sql/slave.h => sql/rpl_slave.h
> modified:
> libmysqld/CMakeLists.txt
> libmysqld/Makefile.am
> mysql-test/lib/v1/mysql-test-run.pl
> mysql-test/suite/rpl/rpl_1slave_base.cnf
> mysql-test/suite/rpl/t/rpl_ip_mix.cnf
> mysql-test/suite/rpl/t/rpl_ip_mix2.cnf
> mysql-test/suite/rpl/t/rpl_ipv4_as_ipv6.cnf
> mysql-test/suite/rpl/t/rpl_ipv6.cnf
> mysql-test/suite/rpl_ndb/my.cnf
> mysql-test/suite/rpl_ndb/t/rpl_ndb_circular_2ch.cnf
> sql/CMakeLists.txt
> sql/Makefile.am
> sql/field.cc
> sql/ha_ndbcluster_binlog.cc
> sql/ha_partition.cc
> sql/item_func.cc
> sql/log.cc
> sql/log.h
> sql/log_event.cc
> sql/log_event.h
> sql/mysqld.cc
> sql/mysqld.h
> sql/rpl_handler.cc
> sql/rpl_injector.cc
> sql/rpl_mi.cc
> sql/rpl_record.cc
> sql/rpl_rli.cc
> sql/rpl_rli.h
> sql/set_var.h
> sql/sql_binlog.cc
> sql/sql_class.cc
> sql/sql_db.cc
> sql/sql_insert.cc
> sql/sql_load.cc
> sql/sql_parse.cc
> sql/sql_servers.h
> sql/sql_show.cc
> sql/sql_yacc.yy
> sql/structs.h
> sql/sys_vars.cc
> sql/rpl_slave.cc
> sql/rpl_slave.h
> === modified file 'libmysqld/CMakeLists.txt'
> --- a/libmysqld/CMakeLists.txt 2010-05-12 11:51:23 +0000
> +++ b/libmysqld/CMakeLists.txt 2010-06-21 06:01:50 +0000
> @@ -54,7 +54,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc l
> ../sql/log_event.cc ../sql/mf_iocache.cc ../sql/my_decimal.cc
> ../sql/net_serv.cc ../sql/opt_range.cc ../sql/opt_sum.cc
> ../sql/parse_file.cc ../sql/procedure.cc ../sql/protocol.cc
> - ../sql/records.cc ../sql/repl_failsafe.cc ../sql/rpl_filter.cc
> + ../sql/records.cc ../sql/rpl_filter.cc
> ../sql/rpl_record.cc ../sql/sha2.cc ../sql/des_key_file.cc
> ../sql/rpl_injector.cc ../sql/set_var.cc ../sql/spatial.cc
> ../sql/sp_cache.cc ../sql/sp.cc ../sql/sp_head.cc
> @@ -69,7 +69,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc l
> ../sql/sql_binlog.cc ../sql/sql_manager.cc ../sql/sql_map.cc
> ../sql/sql_parse.cc ../sql/sql_partition.cc ../sql/sql_plugin.cc
> ../sql/debug_sync.cc
> - ../sql/sql_prepare.cc ../sql/sql_rename.cc ../sql/sql_repl.cc
> + ../sql/sql_prepare.cc ../sql/sql_rename.cc
> ../sql/sql_select.cc ../sql/sql_servers.cc
> ../sql/sql_show.cc ../sql/sql_state.c ../sql/sql_string.cc
> ../sql/sql_tablespace.cc ../sql/sql_table.cc ../sql/sql_test.cc
> @@ -81,7 +81,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc l
> ../sql/scheduler.cc ../sql/sql_audit.cc
> ../sql/event_parse_data.cc
> ../sql/sql_signal.cc ../sql/rpl_handler.cc
> - ../sql/rpl_utility.cc
> + ../sql/rpl_utility.cc ../sql/binlog.cc
> ../sql/sys_vars.cc
> ${CMAKE_BINARY_DIR}/sql/sql_builtin.cc
> ../sql/mdl.cc ../sql/transaction.cc
>
> === modified file 'libmysqld/Makefile.am'
> --- a/libmysqld/Makefile.am 2010-05-19 13:00:23 +0000
> +++ b/libmysqld/Makefile.am 2010-06-21 06:01:50 +0000
> @@ -80,7 +80,7 @@ sqlsources = derror.cc field.cc field_co
> debug_sync.cc sql_tablespace.cc transaction.cc \
> rpl_injector.cc my_user.c partition_info.cc \
> sql_servers.cc event_parse_data.cc sql_signal.cc \
> - rpl_handler.cc mdl.cc keycaches.cc sql_audit.cc
> + binlog.cc rpl_handler.cc mdl.cc keycaches.cc sql_audit.cc
>
> libmysqld_int_a_SOURCES= $(libmysqld_sources)
> nodist_libmysqld_int_a_SOURCES= $(libmysqlsources) $(sqlsources)
>
> === modified file 'mysql-test/lib/v1/mysql-test-run.pl'
> --- a/mysql-test/lib/v1/mysql-test-run.pl 2010-03-22 10:36:23 +0000
> +++ b/mysql-test/lib/v1/mysql-test-run.pl 2010-06-21 06:01:50 +0000
> @@ -3959,7 +3959,6 @@ sub mysqld_arguments ($$$$) {
> mtr_error("unknown mysqld type")
> unless $mysqld->{'type'} eq 'slave';
>
> - mtr_add_arg($args, "%s--init-rpl-role=slave", $prefix);
> if (! ( $opt_skip_slave_binlog || $skip_binlog ))
> {
> mtr_add_arg($args, "%s--log-bin=%s/log/slave%s-bin", $prefix,
> @@ -4016,9 +4015,7 @@ sub mysqld_arguments ($$$$) {
> # $master->[0]->{'port'}); # First master
> # }
> my $slave_server_id= 2 + $idx;
> - my $slave_rpl_rank= $slave_server_id;
> mtr_add_arg($args, "%s--server-id=%d", $prefix, $slave_server_id);
> - mtr_add_arg($args, "%s--rpl-recovery-rank=%d", $prefix, $slave_rpl_rank);
> }
>
> my $cluster= $clusters->[$mysqld->{'cluster'}];
> @@ -4094,12 +4091,7 @@ sub mysqld_arguments ($$$$) {
> mtr_add_arg($args, "%s%s", $prefix, "--core-file");
> }
>
> - if ( $opt_bench )
> - {
> - mtr_add_arg($args, "%s--rpl-recovery-rank=1", $prefix);
> - mtr_add_arg($args, "%s--init-rpl-role=master", $prefix);
> - }
> - elsif ( $mysqld->{'type'} eq 'master' )
> + if ( !$opt_bench and $mysqld->{'type'} eq 'master' )
> {
> mtr_add_arg($args, "%s--open-files-limit=1024", $prefix);
> }
>
> === modified file 'mysql-test/suite/rpl/rpl_1slave_base.cnf'
> --- a/mysql-test/suite/rpl/rpl_1slave_base.cnf 2009-10-21 12:59:47 +0000
> +++ b/mysql-test/suite/rpl/rpl_1slave_base.cnf 2010-06-21 05:56:56 +0000
> @@ -22,7 +22,6 @@ loose-innodb
> log-bin= slave-bin
> relay-log= slave-relay-bin
>
> -init-rpl-role= slave
> log-slave-updates
> master-retry-count= 10
>
>
> === modified file 'mysql-test/suite/rpl/t/rpl_ip_mix.cnf'
> --- a/mysql-test/suite/rpl/t/rpl_ip_mix.cnf 2009-11-23 16:38:42 +0000
> +++ b/mysql-test/suite/rpl/t/rpl_ip_mix.cnf 2010-06-21 05:56:56 +0000
> @@ -26,7 +26,6 @@ bind-address= ::
> log-bin= slave-bin
> relay-log= slave-relay-bin
>
> -init-rpl-role= slave
> log-slave-updates
> master-retry-count= 10
>
>
> === modified file 'mysql-test/suite/rpl/t/rpl_ip_mix2.cnf'
> --- a/mysql-test/suite/rpl/t/rpl_ip_mix2.cnf 2009-11-23 16:38:42 +0000
> +++ b/mysql-test/suite/rpl/t/rpl_ip_mix2.cnf 2010-06-21 05:56:56 +0000
> @@ -26,7 +26,6 @@ bind-address= 0.0.0.0
> log-bin= slave-bin
> relay-log= slave-relay-bin
>
> -init-rpl-role= slave
> log-slave-updates
> master-retry-count= 10
>
>
> === modified file 'mysql-test/suite/rpl/t/rpl_ipv4_as_ipv6.cnf'
> --- a/mysql-test/suite/rpl/t/rpl_ipv4_as_ipv6.cnf 2009-11-23 16:38:42 +0000
> +++ b/mysql-test/suite/rpl/t/rpl_ipv4_as_ipv6.cnf 2010-06-21 05:56:56 +0000
> @@ -26,7 +26,6 @@ bind-address= 0.0.0.0
> log-bin= slave-bin
> relay-log= slave-relay-bin
>
> -init-rpl-role= slave
> log-slave-updates
> master-retry-count= 10
>
>
> === modified file 'mysql-test/suite/rpl/t/rpl_ipv6.cnf'
> --- a/mysql-test/suite/rpl/t/rpl_ipv6.cnf 2009-11-23 16:38:42 +0000
> +++ b/mysql-test/suite/rpl/t/rpl_ipv6.cnf 2010-06-21 05:56:56 +0000
> @@ -26,7 +26,6 @@ bind-address= ::
> log-bin= slave-bin
> relay-log= slave-relay-bin
>
> -init-rpl-role= slave
> log-slave-updates
> master-retry-count= 10
>
>
> === modified file 'mysql-test/suite/rpl_ndb/my.cnf'
> --- a/mysql-test/suite/rpl_ndb/my.cnf 2009-12-01 19:07:18 +0000
> +++ b/mysql-test/suite/rpl_ndb/my.cnf 2010-06-21 05:56:56 +0000
> @@ -33,7 +33,6 @@ log-bin= master-bi
> log-bin= slave-bin
> relay-log= slave-relay-bin
>
> -init-rpl-role= slave
> log-slave-updates
> master-retry-count= 10
>
>
> === modified file 'mysql-test/suite/rpl_ndb/t/rpl_ndb_circular_2ch.cnf'
> --- a/mysql-test/suite/rpl_ndb/t/rpl_ndb_circular_2ch.cnf 2009-11-04 12:28:20 +0000
> +++ b/mysql-test/suite/rpl_ndb/t/rpl_ndb_circular_2ch.cnf 2010-06-21 05:56:56 +0000
> @@ -15,7 +15,6 @@ skip-slave-start
>
> [mysqld.2.slave]
> server-id= 2
> -init-rpl-role= slave
> log-bin
> skip-slave-start
> ndb_connectstring= @mysql_cluster.slave.ndb_connectstring
>
> === modified file 'sql/CMakeLists.txt'
> --- a/sql/CMakeLists.txt 2010-05-12 21:20:24 +0000
> +++ b/sql/CMakeLists.txt 2010-06-21 06:01:50 +0000
> @@ -48,14 +48,12 @@ SET (SQL_SOURCE
> item_create.cc item_func.cc item_geofunc.cc item_row.cc
> item_strfunc.cc item_subselect.cc item_sum.cc item_timefunc.cc
> key.cc log.cc lock.cc
> - log_event.cc rpl_record.cc rpl_reporting.cc
> - log_event_old.cc rpl_record_old.cc
> message.h mf_iocache.cc my_decimal.cc ../sql-common/my_time.c
> mysqld.cc net_serv.cc keycaches.cc
> opt_range.cc opt_range.h opt_sum.cc
> ../sql-common/pack.c parse_file.cc password.c procedure.cc
> - protocol.cc records.cc repl_failsafe.cc rpl_filter.cc set_var.cc
> - slave.cc sp.cc sp_cache.cc sp_head.cc sp_pcontext.cc
> + protocol.cc records.cc set_var.cc
> + sp.cc sp_cache.cc sp_head.cc sp_pcontext.cc
> sp_rcontext.cc spatial.cc sql_acl.cc sql_analyse.cc sql_base.cc
> sql_cache.cc sql_class.cc sql_client.cc sql_crypt.cc sql_crypt.h
> sql_cursor.cc sql_db.cc sql_delete.cc sql_derived.cc sql_do.cc
> @@ -63,18 +61,18 @@ SET (SQL_SOURCE
> sql_list.cc sql_load.cc sql_manager.cc sql_map.cc sql_parse.cc
> sql_partition.cc sql_plugin.cc sql_prepare.cc sql_rename.cc
> debug_sync.cc debug_sync.h
> - sql_repl.cc sql_select.cc sql_show.cc sql_state.c sql_string.cc
> + sql_select.cc sql_show.cc sql_state.c sql_string.cc
> sql_table.cc sql_test.cc sql_trigger.cc sql_udf.cc sql_union.cc
> sql_update.cc sql_view.cc strfunc.cc table.cc thr_malloc.cc
> sql_time.cc tztime.cc uniques.cc unireg.cc item_xmlfunc.cc
> - rpl_tblmap.cc sql_binlog.cc event_scheduler.cc event_data_objects.cc
> + event_scheduler.cc event_data_objects.cc
> event_queue.cc event_db_repository.cc
> sql_tablespace.cc events.cc ../sql-common/my_user.c
> - partition_info.cc rpl_utility.cc rpl_injector.cc sql_locale.cc
> - rpl_rli.cc rpl_mi.cc sql_servers.cc sql_audit.cc
> + partition_info.cc sql_locale.cc
> + sql_servers.cc sql_audit.cc
> sql_connect.cc scheduler.cc
> sql_profile.cc event_parse_data.cc
> - sql_signal.cc rpl_handler.cc mdl.cc
> + sql_signal.cc mdl.cc
> transaction.cc sys_vars.cc
> ${GEN_SOURCES}
> ${MYSYS_LIBWRAP_SOURCE})
> @@ -89,7 +87,16 @@ TARGET_LINK_LIBRARIES(sql ${MYSQLD_STATI
> ${LIBWRAP} ${LIBCRYPT} ${LIBDL}
> ${SSL_LIBRARIES})
>
> -
> +SET (BINLOG_SOURCE log_event.cc log_event_old.cc binlog.cc sql_binlog.cc
> + rpl_filter.cc rpl_record.cc rpl_record_old.cc rpl_utility.cc
> + sql_binlog.cc rpl_injector.cc)
> +ADD_LIBRARY(binlog ${BINLOG_SOURCE})
> +SET (RPL_SOURCE rpl_handler.cc rpl_tblmap.cc)
> +ADD_LIBRARY(rpl ${RPL_SOURCE})
> +SET (MASTER_SOURCE rpl_master.cc)
> +ADD_LIBRARY(master ${MASTER_SOURCE})
> +SET (SLAVE_SOURCE rpl_slave.cc rpl_reporting.cc rpl_mi.cc rpl_rli.cc)
> +ADD_LIBRARY(slave ${SLAVE_SOURCE})
>
> IF(WIN32)
> SET(MYSQLD_SOURCE main.cc nt_servc.cc nt_servc.h message.rc)
> @@ -131,7 +138,7 @@ IF(NOT WITHOUT_DYNAMIC_PLUGINS)
> ENDIF()
>
> SET_TARGET_PROPERTIES(mysqld PROPERTIES ENABLE_EXPORTS TRUE)
> -TARGET_LINK_LIBRARIES(mysqld sql)
> +TARGET_LINK_LIBRARIES(mysqld sql binlog rpl master slave)
>
> # Provide plugins with minimal set of libraries
> SET(INTERFACE_LIBS ${LIBRT})
>
> === modified file 'sql/Makefile.am'
> --- a/sql/Makefile.am 2010-05-19 13:00:23 +0000
> +++ b/sql/Makefile.am 2010-06-21 06:01:50 +0000
> @@ -62,6 +62,8 @@ DTRACEFILES_DEPEND = filesort.o \
> sql_select.o \
> sql_update.o
>
> +RPL_LIBS = libbinlog.la librpl.la libmaster.la libslave.la
> +pkglib_LTLIBRARIES= $(RPL_LIBS)
>
> noinst_LTLIBRARIES= libndb.la \
> udf_example.la
> @@ -71,9 +73,11 @@ SUPPORTING_LIBS = $(top_builddir)/vio/li
> $(top_builddir)/dbug/libdbug.a \
> $(top_builddir)/regex/libregex.a \
> $(top_builddir)/strings/libmystrings.a
> -mysqld_DEPENDENCIES= @mysql_plugin_libs@ $(SUPPORTING_LIBS) libndb.la
> +mysqld_DEPENDENCIES= @mysql_plugin_libs@ $(SUPPORTING_LIBS) libndb.la \
> + $(RPL_LIBS)
> LDADD = $(SUPPORTING_LIBS) @ZLIB_LIBS@ @NDB_SCI_LIBS@
> mysqld_LDADD = libndb.la \
> + $(RPL_LIBS) \
> @MYSQLD_EXTRA_LDFLAGS@ \
> @pstack_libs@ \
> @mysql_plugin_libs@ \
> @@ -105,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 \
> + rpl_slave.h rpl_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 \
> @@ -142,8 +146,6 @@ mysqld_SOURCES = sql_lex.cc sql_handler.
> procedure.cc sql_test.cc \
> log.cc init.cc derror.cc sql_acl.cc \
> unireg.cc des_key_file.cc \
> - log_event.cc rpl_record.cc \
> - log_event_old.cc rpl_record_old.cc \
> discover.cc sql_time.cc opt_range.cc opt_sum.cc \
> records.cc filesort.cc handler.cc \
> ha_partition.cc \
> @@ -151,12 +153,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.
> sql_db.cc sql_table.cc sql_rename.cc sql_crypt.cc \
> sql_load.cc mf_iocache.cc field_conv.cc sql_show.cc \
> sql_udf.cc sql_analyse.cc sql_analyse.h sql_cache.cc \
> - slave.cc sql_repl.cc rpl_filter.cc rpl_tblmap.cc \
> - rpl_utility.cc rpl_injector.cc rpl_rli.cc rpl_mi.cc \
> - rpl_reporting.cc \
> sql_union.cc sql_derived.cc \
> sql_client.cc \
> - repl_failsafe.h repl_failsafe.cc \
> sql_olap.cc sql_view.cc \
> gstream.cc spatial.cc sql_help.cc sql_cursor.cc \
> tztime.cc my_decimal.cc\
> @@ -164,14 +162,21 @@ mysqld_SOURCES = sql_lex.cc sql_handler.
> sp_cache.cc parse_file.cc sql_trigger.cc \
> event_scheduler.cc event_data_objects.cc \
> event_queue.cc event_db_repository.cc events.cc \
> - sql_plugin.cc sql_binlog.cc \
> + sql_plugin.cc \
> sql_builtin.cc sql_tablespace.cc partition_info.cc \
> sql_servers.cc event_parse_data.cc sql_signal.cc \
> - rpl_handler.cc mdl.cc transaction.cc sql_audit.cc \
> + mdl.cc transaction.cc sql_audit.cc \
> sha2.cc
>
> nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c
>
> +libbinlog_la_SOURCES = log_event.cc log_event_old.cc binlog.cc \
> + rpl_filter.cc rpl_record.cc rpl_record_old.cc \
> + rpl_utility.cc sql_binlog.cc rpl_injector.cc
> +librpl_la_SOURCES = rpl_handler.cc rpl_tblmap.cc
> +libmaster_la_SOURCES = rpl_master.cc
> +libslave_la_SOURCES = rpl_slave.cc rpl_reporting.cc rpl_rli.cc rpl_mi.cc
> +
> libndb_la_CPPFLAGS= @ndbcluster_includes@
> libndb_la_SOURCES= ha_ndbcluster.cc \
> ha_ndbcluster_binlog.cc \
>
> === added file 'sql/binlog.cc'
> --- a/sql/binlog.cc 1970-01-01 00:00:00 +0000
> +++ b/sql/binlog.cc 2010-06-21 05:56:56 +0000
> @@ -0,0 +1,5135 @@
> +/* 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 */
> +
> +
> +#include "my_global.h"
> +#include "log.h"
> +#include "binlog.h"
> +#include "log_event.h"
> +#include "rpl_filter.h"
> +#include "rpl_rli.h"
> +#include "sql_plugin.h"
> +#include "rpl_handler.h"
> +
> +#define MY_OFF_T_UNDEF (~(my_off_t)0UL)
> +#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
> +
> +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);
> +static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv);
> +static int binlog_commit(handlerton *hton, THD *thd, bool all);
> +static int binlog_rollback(handlerton *hton, THD *thd, bool all);
> +static int binlog_prepare(handlerton *hton, THD *thd, bool all);
> +
> +/*
> + Helper class to hold a mutex for the duration of the
> + block.
> +
> + Eliminates the need for explicit unlocking of mutexes on, e.g.,
> + error returns. On passing a null pointer, the sentry will not do
> + anything.
> + */
> +class Mutex_sentry
> +{
> +public:
> + Mutex_sentry(mysql_mutex_t *mutex)
> + : m_mutex(mutex)
> + {
> + if (m_mutex)
> + mysql_mutex_lock(mutex);
> + }
> +
> + ~Mutex_sentry()
> + {
> + if (m_mutex)
> + mysql_mutex_unlock(m_mutex);
> +#ifndef DBUG_OFF
> + m_mutex= 0;
> +#endif
> + }
> +
> +private:
> + mysql_mutex_t *m_mutex;
> +
> + // It's not allowed to copy this object in any way
> + Mutex_sentry(Mutex_sentry const&);
> + void operator=(Mutex_sentry const&);
> +};
> +
> +/*
> + Helper classes to store non-transactional and transactional data
> + before copying it to the binary log.
> +*/
> +class binlog_cache_data
> +{
> +public:
> + binlog_cache_data(): m_pending(0), before_stmt_pos(MY_OFF_T_UNDEF),
> + incident(FALSE)
> + {
> + cache_log.end_of_file= max_binlog_cache_size;
> + }
> +
> + ~binlog_cache_data()
> + {
> + DBUG_ASSERT(empty());
> + close_cached_file(&cache_log);
> + }
> +
> + bool empty() const
> + {
> + return pending() == NULL && my_b_tell(&cache_log) == 0;
> + }
> +
> + Rows_log_event *pending() const
> + {
> + return m_pending;
> + }
> +
> + void set_pending(Rows_log_event *const pending)
> + {
> + m_pending= pending;
> + }
> +
> + void set_incident(void)
> + {
> + incident= TRUE;
> + }
> +
> + bool has_incident(void)
> + {
> + return(incident);
> + }
> +
> + void reset()
> + {
> + truncate(0);
> + incident= FALSE;
> + before_stmt_pos= MY_OFF_T_UNDEF;
> + cache_log.end_of_file= max_binlog_cache_size;
> + DBUG_ASSERT(empty());
> + }
> +
> + my_off_t get_byte_position() const
> + {
> + return my_b_tell(&cache_log);
> + }
> +
> + my_off_t get_prev_position()
> + {
> + return(before_stmt_pos);
> + }
> +
> + void set_prev_position(my_off_t pos)
> + {
> + before_stmt_pos= pos;
> + }
> +
> + void restore_prev_position()
> + {
> + truncate(before_stmt_pos);
> + }
> +
> + void restore_savepoint(my_off_t pos)
> + {
> + truncate(pos);
> + if (pos < before_stmt_pos)
> + before_stmt_pos= MY_OFF_T_UNDEF;
> + }
> +
> + /*
> + Cache to store data before copying it to the binary log.
> + */
> + IO_CACHE cache_log;
> +
> +private:
> + /*
> + Pending binrows event. This event is the event where the rows are currently
> + written.
> + */
> + Rows_log_event *m_pending;
> +
> + /*
> + Binlog position before the start of the current statement.
> + */
> + my_off_t before_stmt_pos;
> +
> + /*
> + This indicates that some events did not get into the cache and most likely
> + it is corrupted.
> + */
> + bool incident;
> +
> + /*
> + It truncates the cache to a certain position. This includes deleting the
> + pending event.
> + */
> + void truncate(my_off_t pos)
> + {
> + DBUG_PRINT("info", ("truncating to position %lu", (ulong) pos));
> + if (pending())
> + {
> + delete pending();
> + set_pending(0);
> + }
> + reinit_io_cache(&cache_log, WRITE_CACHE, pos, 0, 0);
> + cache_log.end_of_file= max_binlog_cache_size;
> + }
> +
> + binlog_cache_data& operator=(const binlog_cache_data& info);
> + binlog_cache_data(const binlog_cache_data& info);
> +};
> +
> +class binlog_cache_mngr {
> +public:
> + binlog_cache_mngr() {}
> +
> + void reset_cache(binlog_cache_data* cache_data)
> + {
> + cache_data->reset();
> + }
> +
> + binlog_cache_data* get_binlog_cache_data(bool is_transactional)
> + {
> + return (is_transactional ? &trx_cache : &stmt_cache);
> + }
> +
> + IO_CACHE* get_binlog_cache_log(bool is_transactional)
> + {
> + return (is_transactional ? &trx_cache.cache_log :
> &stmt_cache.cache_log);
> + }
> +
> + binlog_cache_data stmt_cache;
> +
> + binlog_cache_data trx_cache;
> +
> +private:
> +
> + binlog_cache_mngr& operator=(const binlog_cache_mngr& info);
> + binlog_cache_mngr(const binlog_cache_mngr& info);
> +};
> +
> + /*
> + Save position of binary log transaction cache.
> +
> + SYNPOSIS
> + binlog_trans_log_savepos()
> +
> + thd The thread to take the binlog data from
> + pos Pointer to variable where the position will be stored
> +
> + DESCRIPTION
> +
> + Save the current position in the binary log transaction cache into
> + the variable pointed to by 'pos'
> + */
> +
> +static void
> +binlog_trans_log_savepos(THD *thd, my_off_t *pos)
> +{
> + DBUG_ENTER("binlog_trans_log_savepos");
> + DBUG_ASSERT(pos != NULL);
> + if (thd_get_ha_data(thd, binlog_hton) == NULL)
> + thd->binlog_setup_trx_data();
> + binlog_cache_mngr *const cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> + DBUG_ASSERT(mysql_bin_log.is_open());
> + *pos= cache_mngr->trx_cache.get_byte_position();
> + DBUG_PRINT("return", ("*pos: %lu", (ulong) *pos));
> + DBUG_VOID_RETURN;
> +}
> +
> +
> +/*
> + Truncate the binary log transaction cache.
> +
> + SYNPOSIS
> + binlog_trans_log_truncate()
> +
> + thd The thread to take the binlog data from
> + pos Position to truncate to
> +
> + DESCRIPTION
> +
> + Truncate the binary log to the given position. Will not change
> + anything else.
> +
> + */
> +static void
> +binlog_trans_log_truncate(THD *thd, my_off_t pos)
> +{
> + DBUG_ENTER("binlog_trans_log_truncate");
> + DBUG_PRINT("enter", ("pos: %lu", (ulong) pos));
> +
> + DBUG_ASSERT(thd_get_ha_data(thd, binlog_hton) != NULL);
> + /* Only true if binlog_trans_log_savepos() wasn't called before */
> + DBUG_ASSERT(pos != ~(my_off_t) 0);
> +
> + binlog_cache_mngr *const cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> + cache_mngr->trx_cache.restore_savepoint(pos);
> + DBUG_VOID_RETURN;
> +}
> +
> +
> +/*
> + this function is mostly a placeholder.
> + conceptually, binlog initialization (now mostly done in MYSQL_BIN_LOG::open)
> + should be moved here.
> +*/
> +
> +static int binlog_init(void *p)
> +{
> + binlog_hton= (handlerton *)p;
> + binlog_hton->state=opt_bin_log ? SHOW_OPTION_YES : SHOW_OPTION_NO;
> + binlog_hton->db_type=DB_TYPE_BINLOG;
> + binlog_hton->savepoint_offset= sizeof(my_off_t);
> + binlog_hton->close_connection= binlog_close_connection;
> + binlog_hton->savepoint_set= binlog_savepoint_set;
> + binlog_hton->savepoint_rollback= binlog_savepoint_rollback;
> + binlog_hton->commit= binlog_commit;
> + binlog_hton->rollback= binlog_rollback;
> + binlog_hton->prepare= binlog_prepare;
> + binlog_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN;
> + return 0;
> +}
> +
> +static int binlog_close_connection(handlerton *hton, THD *thd)
> +{
> + binlog_cache_mngr *const cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> + DBUG_ASSERT(cache_mngr->trx_cache.empty() &&
> cache_mngr->stmt_cache.empty());
> + thd_set_ha_data(thd, binlog_hton, NULL);
> + cache_mngr->~binlog_cache_mngr();
> + my_free((uchar*)cache_mngr, MYF(0));
> + return 0;
> +}
> +
> +/**
> + This function flushes a transactional cache upon commit/rollback.
> +
> + @param thd The thread whose transaction should be flushed
> + @param cache_mngr Pointer to the cache data to be flushed
> + @param end_ev The end event either commit/rollback.
> +
> + @return
> + nonzero if an error pops up when flushing the transactional cache.
> +*/
> +static int
> +binlog_flush_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr,
> + Log_event *end_ev)
> +{
> + DBUG_ENTER("binlog_flush_trx_cache");
> + int error=0;
> + IO_CACHE *cache_log= &cache_mngr->trx_cache.cache_log;
> +
> + /*
> + This function handles transactional changes and as such
> + this flag equals to true.
> + */
> + bool const is_transactional= TRUE;
> +
> + if (thd->binlog_flush_pending_rows_event(TRUE, is_transactional))
> + DBUG_RETURN(1);
> + /*
> + Doing a commit or a rollback including non-transactional tables,
> + i.e., ending a transaction where we might write the transaction
> + cache to the binary log.
> +
> + We can always end the statement when ending a transaction since
> + transactions are not allowed inside stored functions. If they
> + were, we would have to ensure that we're not ending a statement
> + inside a stored function.
> + */
> + error= mysql_bin_log.write(thd, &cache_mngr->trx_cache.cache_log, end_ev,
> + cache_mngr->trx_cache.has_incident());
> + cache_mngr->reset_cache(&cache_mngr->trx_cache);
> +
> + /*
> + We need to step the table map version after writing the
> + transaction cache to disk.
> + */
> + mysql_bin_log.update_table_map_version();
> + statistic_increment(binlog_cache_use, &LOCK_status);
> + if (cache_log->disk_writes != 0)
> + {
> + statistic_increment(binlog_cache_disk_use, &LOCK_status);
> + cache_log->disk_writes= 0;
> + }
> +
> + DBUG_ASSERT(cache_mngr->trx_cache.empty());
> + DBUG_RETURN(error);
> +}
> +
> +/**
> + This function truncates the transactional cache upon committing or rolling
> + back either a transaction or a statement.
> +
> + @param thd The thread whose transaction should be flushed
> + @param cache_mngr Pointer to the cache data to be flushed
> + @param all @c true means truncate the transaction, otherwise the
> + statement must be truncated.
> +
> + @return
> + nonzero if an error pops up when truncating the transactional cache.
> +*/
> +static int
> +binlog_truncate_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr, bool all)
> +{
> + DBUG_ENTER("binlog_truncate_trx_cache");
> + int error=0;
> + /*
> + This function handles transactional changes and as such this flag
> + equals to true.
> + */
> + bool const is_transactional= TRUE;
> +
> + DBUG_PRINT("info", ("thd->options={ %s%s}, transaction: %s",
> + FLAGSTR(thd->variables.option_bits,
> OPTION_NOT_AUTOCOMMIT),
> + FLAGSTR(thd->variables.option_bits, OPTION_BEGIN),
> + all ? "all" : "stmt"));
> + /*
> + If rolling back an entire transaction or a single statement not
> + inside a transaction, we reset the transaction cache.
> + */
> + thd->binlog_remove_pending_rows_event(TRUE, is_transactional);
> + if (ending_trans(thd, all))
> + {
> + if (cache_mngr->trx_cache.has_incident())
> + error= mysql_bin_log.write_incident(thd, TRUE);
> +
> + cache_mngr->reset_cache(&cache_mngr->trx_cache);
> +
> + thd->clear_binlog_table_maps();
> + }
> + /*
> + If rolling back a statement in a transaction, we truncate the
> + transaction cache to remove the statement.
> + */
> + else
> + cache_mngr->trx_cache.restore_prev_position();
> +
> + /*
> + We need to step the table map version on a rollback to ensure that a new
> + table map event is generated instead of the one that was written to the
> + thrown-away transaction cache.
> + */
> + mysql_bin_log.update_table_map_version();
> +
> + DBUG_ASSERT(thd->binlog_get_pending_rows_event(is_transactional) == NULL);
> + DBUG_RETURN(error);
> +}
> +
> +static int binlog_prepare(handlerton *hton, THD *thd, bool all)
> +{
> + /*
> + do nothing.
> + just pretend we can do 2pc, so that MySQL won't
> + switch to 1pc.
> + real work will be done in MYSQL_BIN_LOG::log_xid()
> + */
> + return 0;
> +}
> +
> +/**
> + This function flushes the non-transactional to the binary log upon
> + committing or rolling back a statement.
> +
> + @param thd The thread whose transaction should be flushed
> + @param cache_mngr Pointer to the cache data to be flushed
> +
> + @return
> + nonzero if an error pops up when flushing the non-transactional cache.
> +*/
> +static int
> +binlog_flush_stmt_cache(THD *thd, binlog_cache_mngr *cache_mngr)
> +{
> + int error= 0;
> + DBUG_ENTER("binlog_flush_stmt_cache");
> + /*
> + If we are flushing the statement cache, it means that the changes get
> + through otherwise the cache is empty and this routine should not be called.
> + */
> + DBUG_ASSERT(cache_mngr->stmt_cache.has_incident() == FALSE);
> + /*
> + This function handles non-transactional changes and as such this flag equals
> + to false.
> + */
> + bool const is_transactional= FALSE;
> + IO_CACHE *cache_log= &cache_mngr->stmt_cache.cache_log;
> +
> + if (thd->binlog_flush_pending_rows_event(TRUE, is_transactional))
> + DBUG_RETURN(1);
> +
> + Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE, TRUE, 0);
> + if ((error= mysql_bin_log.write(thd, cache_log, &qev,
> + cache_mngr->stmt_cache.has_incident())))
> + DBUG_RETURN(error);
> + cache_mngr->reset_cache(&cache_mngr->stmt_cache);
> +
> + /*
> + We need to step the table map version after writing the
> + transaction cache to disk.
> + */
> + mysql_bin_log.update_table_map_version();
> + statistic_increment(binlog_cache_use, &LOCK_status);
> + if (cache_log->disk_writes != 0)
> + {
> + statistic_increment(binlog_cache_disk_use, &LOCK_status);
> + cache_log->disk_writes= 0;
> + }
> + DBUG_RETURN(error);
> +}
> +
> +/**
> + This function is called once after each statement.
> +
> + It has the responsibility to flush the caches to the binary log on commits.
> +
> + @param hton The binlog handlerton.
> + @param thd The client thread that executes the transaction.
> + @param all This is @c true if this is a real transaction commit, and
> + @false otherwise.
> +
> + @see handlerton::commit
> +*/
> +static int binlog_commit(handlerton *hton, THD *thd, bool all)
> +{
> + int error= 0;
> + DBUG_ENTER("binlog_commit");
> + binlog_cache_mngr *const cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> +
> + DBUG_PRINT("debug",
> + ("all: %d, in_transaction: %s, all.modified_non_trans_table: %s,
> stmt.modified_non_trans_table: %s",
> + all,
> + YESNO(thd->in_multi_stmt_transaction()),
> + YESNO(thd->transaction.all.modified_non_trans_table),
> + YESNO(thd->transaction.stmt.modified_non_trans_table)));
> +
> + if (!cache_mngr->stmt_cache.empty())
> + {
> + binlog_flush_stmt_cache(thd, cache_mngr);
> + }
> +
> + if (cache_mngr->trx_cache.empty())
> + {
> + /*
> + we're here because cache_log was flushed in MYSQL_BIN_LOG::log_xid()
> + */
> + cache_mngr->reset_cache(&cache_mngr->trx_cache);
> + DBUG_RETURN(0);
> + }
> +
> + /*
> + We commit the transaction if:
> + - We are not in a transaction and committing a statement, or
> + - We are in a transaction and a full transaction is committed.
> + Otherwise, we accumulate the changes.
> + */
> + if (ending_trans(thd, all))
> + {
> + Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE, TRUE, 0);
> + error= binlog_flush_trx_cache(thd, cache_mngr, &qev);
> + }
> +
> + /*
> + This is part of the stmt rollback.
> + */
> + if (!all)
> + cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
> + DBUG_RETURN(error);
> +}
> +
> +/**
> + This function is called when a transaction or a statement is rolled back.
> +
> + @param hton The binlog handlerton.
> + @param thd The client thread that executes the transaction.
> + @param all This is @c true if this is a real transaction rollback, and
> + @false otherwise.
> +
> + @see handlerton::rollback
> +*/
> +static int binlog_rollback(handlerton *hton, THD *thd, bool all)
> +{
> + DBUG_ENTER("binlog_rollback");
> + int error=0;
> + binlog_cache_mngr *const cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> +
> + DBUG_PRINT("debug", ("all: %s, all.modified_non_trans_table: %s,
> stmt.modified_non_trans_table: %s",
> + YESNO(all),
> + YESNO(thd->transaction.all.modified_non_trans_table),
> + YESNO(thd->transaction.stmt.modified_non_trans_table)));
> +
> + /*
> + If an incident event is set we do not flush the content of the statement
> + cache because it may be corrupted.
> + */
> + if (cache_mngr->stmt_cache.has_incident())
> + {
> + error= mysql_bin_log.write_incident(thd, TRUE);
> + cache_mngr->reset_cache(&cache_mngr->stmt_cache);
> + }
> + else if (!cache_mngr->stmt_cache.empty())
> + {
> + binlog_flush_stmt_cache(thd, cache_mngr);
> + }
> +
> + if (cache_mngr->trx_cache.empty())
> + {
> + /*
> + we're here because cache_log was flushed in MYSQL_BIN_LOG::log_xid()
> + */
> + cache_mngr->reset_cache(&cache_mngr->trx_cache);
> + DBUG_RETURN(0);
> + }
> +
> + if (mysql_bin_log.check_write_error(thd))
> + {
> + /*
> + "all == true" means that a "rollback statement" triggered the error and
> + this function was called. However, this must not happen as a rollback
> + is written directly to the binary log. And in auto-commit mode, a single
> + statement that is rolled back has the flag all == false.
> + */
> + DBUG_ASSERT(!all);
> + /*
> + We reach this point if the effect of a statement did not properly get into
> + a cache and need to be rolled back.
> + */
> + error= binlog_truncate_trx_cache(thd, cache_mngr, all);
> + }
> + else
> + {
> + /*
> + We flush the cache wrapped in a beging/rollback if:
> + . aborting a single or multi-statement transaction and;
> + . the format is STMT and non-trans engines were updated or;
> + . the OPTION_KEEP_LOG is activate.
> + */
> + if (ending_trans(thd, all) &&
> + ((thd->variables.option_bits & OPTION_KEEP_LOG) ||
> + (trans_has_updated_non_trans_table(thd) &&
> + thd->variables.binlog_format == BINLOG_FORMAT_STMT)))
> + {
> + Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, FALSE, TRUE, 0);
> + error= binlog_flush_trx_cache(thd, cache_mngr, &qev);
> + }
> + /*
> + Truncate the cache if:
> + . aborting a single or multi-statement transaction or;
> + . the OPTION_KEEP_LOG is not activate and;
> + . the format is not STMT or no non-trans were updated.
> + */
> + else if (ending_trans(thd, all) ||
> + (!(thd->variables.option_bits & OPTION_KEEP_LOG) &&
> + ((!stmt_has_updated_non_trans_table(thd) ||
> + thd->variables.binlog_format != BINLOG_FORMAT_STMT))))
> + error= binlog_truncate_trx_cache(thd, cache_mngr, all);
> + }
> +
> + /*
> + This is part of the stmt rollback.
> + */
> + if (!all)
> + cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
> + DBUG_RETURN(error);
> +}
> +
> +/**
> + @note
> + How do we handle this (unlikely but legal) case:
> + @verbatim
> + [transaction] + [update to non-trans table] + [rollback to savepoint] ?
> + @endverbatim
> + The problem occurs when a savepoint is before the update to the
> + non-transactional table. Then when there's a rollback to the savepoint, if we
> + simply truncate the binlog cache, we lose the part of the binlog cache where
> + the update is. If we want to not lose it, we need to write the SAVEPOINT
> + command and the ROLLBACK TO SAVEPOINT command to the binlog cache. The latter
> + is easy: it's just write at the end of the binlog cache, but the former
> + should be *inserted* to the place where the user called SAVEPOINT. The
> + solution is that when the user calls SAVEPOINT, we write it to the binlog
> + cache (so no need to later insert it). As transactions are never intermixed
> + in the binary log (i.e. they are serialized), we won't have conflicts with
> + savepoint names when using mysqlbinlog or in the slave SQL thread.
> + Then when ROLLBACK TO SAVEPOINT is called, if we updated some
> + non-transactional table, we don't truncate the binlog cache but instead write
> + ROLLBACK TO SAVEPOINT to it; otherwise we truncate the binlog cache (which
> + will chop the SAVEPOINT command from the binlog cache, which is good as in
> + that case there is no need to have it in the binlog).
> +*/
> +
> +static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv)
> +{
> + DBUG_ENTER("binlog_savepoint_set");
> +
> + binlog_trans_log_savepos(thd, (my_off_t*) sv);
> + /* Write it to the binary log */
> +
> + int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
> + int const error=
> + thd->binlog_query(THD::STMT_QUERY_TYPE,
> + thd->query(), thd->query_length(), TRUE, FALSE, FALSE,
> + errcode);
> + DBUG_RETURN(error);
> +}
> +
> +static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
> +{
> + DBUG_ENTER("binlog_savepoint_rollback");
> +
> + /*
> + Write ROLLBACK TO SAVEPOINT to the binlog cache if we have updated some
> + non-transactional table. Otherwise, truncate the binlog cache starting
> + from the SAVEPOINT command.
> + */
> + if (unlikely(trans_has_updated_non_trans_table(thd) ||
> + (thd->variables.option_bits & OPTION_KEEP_LOG)))
> + {
> + int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
> + int error=
> + thd->binlog_query(THD::STMT_QUERY_TYPE,
> + thd->query(), thd->query_length(), TRUE, FALSE,
> FALSE,
> + errcode);
> + DBUG_RETURN(error);
> + }
> + binlog_trans_log_truncate(thd, *(my_off_t*)sv);
> + DBUG_RETURN(0);
> +}
> +
> +
> +/*
> + 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];
> + DBUG_ASSERT(my_b_tell(log) == 0);
> +
> + if (my_b_read(log, (uchar*) magic, sizeof(magic)))
> + {
> + *errmsg = "I/O error reading the header from the binary log";
> + sql_print_error("%s, errno=%d, io cache code=%d", *errmsg, my_errno,
> + log->error);
> + return 1;
> + }
> + if (memcmp(magic, BINLOG_MAGIC, sizeof(magic)))
> + {
> + *errmsg = "Binlog has bad magic number; It's not a binary log file that can be
> used by this version of MySQL";
> + return 1;
> + }
> + return 0;
> +}
> +
> +
> +File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg)
> +{
> + File file;
> + DBUG_ENTER("open_binlog");
> +
> + if ((file= mysql_file_open(key_file_binlog,
> + log_file_name, O_RDONLY | O_BINARY | O_SHARE,
> + MYF(MY_WME))) < 0)
> + {
> + sql_print_error("Failed to open log (file '%s', errno %d)",
> + log_file_name, my_errno);
> + *errmsg = "Could not open log file";
> + goto err;
> + }
> + if (init_io_cache(log, file, IO_SIZE*2, READ_CACHE, 0, 0,
> + MYF(MY_WME|MY_DONT_CHECK_FILESIZE)))
> + {
> + sql_print_error("Failed to create a cache on log (file '%s')",
> + log_file_name);
> + *errmsg = "Could not open log file";
> + goto err;
> + }
> + if (check_binlog_magic(log,errmsg))
> + goto err;
> + DBUG_RETURN(file);
> +
> +err:
> + if (file >= 0)
> + {
> + mysql_file_close(file, MYF(0));
> + end_io_cache(log);
> + }
> + DBUG_RETURN(-1);
> +}
> +
> +/**
> + 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)
> +{
> + binlog_cache_mngr *const cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> +
> + return (cache_mngr ? !cache_mngr->trx_cache.empty() : 0);
> +}
> +
> +/**
> + This function checks if a transactional table was updated by the
> + current statement.
> +
> + @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())
> + {
> + if (ha_info->is_trx_read_write() && ha_info->ht() != binlog_hton)
> + return (TRUE);
> + }
> + return (FALSE);
> +}
> +
> +/**
> + 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.
> +
> + 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.
> +
> + @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)
> +{
> + 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.
> +
> + @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)
> +{
> + return (all || (!all && !thd->in_multi_stmt_transaction()));
> +}
> +
> +/**
> + This function checks if a non-transactional table was updated by
> + the current transaction.
> +
> + @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);
> +}
> +
> +/**
> + This function checks if a non-transactional table was updated by the
> + current statement.
> +
> + @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);
> +}
> +
> +#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())
> + {
> + my_ok(thd);
> + return FALSE;
> + }
> +
> + 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())
> + {
> + my_ok(thd);
> + return 0;
> + }
> + return purge_error_message(thd,
> + mysql_bin_log.purge_logs_before_date(purge_time));
> +}
> +#endif /* EMBEDDED_LIBRARY */
> +
> +/*
> + 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;
> +
> + /* 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();
> + }
> +
> + return error;
> +}
> +
> +
> +/**
> + 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
> +
> + @note
> + File will be truncated to be 'offset' shorter or filled up with newlines
> +
> + @retval
> + 0 ok
> +*/
> +
> +#ifdef HAVE_REPLICATION
> +
> +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");
> +
> + 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;
> +
> + /* Reset data in old index cache */
> + reinit_io_cache(index_file, READ_CACHE, (my_off_t) 0, 0, 1);
> + DBUG_RETURN(0);
> +
> +err:
> + DBUG_RETURN(1);
> +}
> +
> +/**
> + 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);
> +}
> +
> +/* 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
> +
> + 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 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;
> + DBUG_ENTER("mysql_show_binlog_events");
> +
> + DBUG_ASSERT(thd->lex->sql_command == SQLCOM_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);
> +
> + /*
> + 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 */
> +
> +
> +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 is called only once */
> +
> +void MYSQL_BIN_LOG::cleanup()
> +{
> + 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;
> +}
> +
> +
> +/* 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;
> +}
> +
> +
> +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);
> +}
> +
> +
> +bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
> + const char *log_name, bool need_mutex)
> +{
> + File index_file_nr= -1;
> + DBUG_ASSERT(!my_b_inited(&index_file));
> +
> + /*
> + First open of this class instance
> + Create an index file that will hold all file names uses for logging.
> + Add new entries to the end of it.
> + */
> + 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;
> + }
> +
> +#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.
> + */
> +
> + 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))
> + {
> + sql_print_error("MYSQL_BIN_LOG::open_index_file failed to sync the index "
> + "file.");
> + return TRUE;
> + }
> +#endif
> +
> + return FALSE;
> +}
> +
> +
> +/**
> + Open a (new) binlog 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.
> +
> + @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;
> +
> + DBUG_ENTER("MYSQL_BIN_LOG::open");
> + DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg));
> +
> + 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);
> + }
> +
> +#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
> +
> + write_error= 0;
> +
> + /* 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 */
> + }
> +
> + init(no_auto_events_arg, max_size_arg);
> +
> + open_count++;
> +
> + DBUG_ASSERT(log_type == LOG_BIN);
> +
> + {
> + bool write_file_name_to_index_file=0;
> +
> + if (!my_b_filelength(&log_file))
> + {
> + /*
> + The binary log file was empty (probably newly created)
> + This is the normal case and happens when the user doesn't specify
> + an extension for the binary log files.
> + In this case we write a standard header to it.
> + */
> + if (my_b_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 (need_start_event && !no_auto_events)
> + {
> + /*
> + 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)
> + {
> + /*
> + 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
> + }
> + }
> + log_state= LOG_OPENED;
> +
> +#ifdef HAVE_REPLICATION
> + close_purge_index_file();
> +#endif
> +
> + DBUG_RETURN(0);
> +
> +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);
> + 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);
> + 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");
> +
> + bool checked= FALSE;
> +
> + if (!thd->is_error())
> + DBUG_RETURN(checked);
> +
> + 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
> + 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)
> +{
> + int error= 0;
> + char *fname= linfo->log_file_name;
> + uint log_name_len= log_name ? (uint) strlen(log_name) : 0;
> + DBUG_ENTER("find_log_pos");
> + DBUG_PRINT("enter",("log_name: %s", log_name ? log_name : "NULL"));
> +
> + /*
> + Mutex needed because we need to make sure the file pointer does not
> + move from under our feet
> + */
> + if (need_lock)
> + mysql_mutex_lock(&LOCK_index);
> + mysql_mutex_assert_owner(&LOCK_index);
> +
> + /* As the file is flushed, we can't get an error here */
> + (void) reinit_io_cache(&index_file, READ_CACHE, (my_off_t) 0, 0, 0);
> +
> + for (;;)
> + {
> + uint length;
> + 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;
> + }
> + }
> +
> + if (need_lock)
> + mysql_mutex_unlock(&LOCK_index);
> + DBUG_RETURN(error);
> +}
> +
> +
> +/**
> + 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
> +
> + @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
> +*/
> +
> +int MYSQL_BIN_LOG::find_next_log(LOG_INFO* linfo, bool need_lock)
> +{
> + int error= 0;
> + uint length;
> + char *fname= linfo->log_file_name;
> +
> + if (need_lock)
> + mysql_mutex_lock(&LOCK_index);
> + mysql_mutex_assert_owner(&LOCK_index);
> +
> + /* As the file is flushed, we can't get an error here */
> + (void) reinit_io_cache(&index_file, READ_CACHE, linfo->index_file_offset,
> 0,
> + 0);
> +
> + linfo->index_file_start_offset= linfo->index_file_offset;
> + if ((length=my_b_gets(&index_file, fname, FN_REFLEN)) <= 1)
> + {
> + error = !index_file.error ? LOG_INFO_EOF : LOG_INFO_IO;
> + goto err;
> + }
> + fname[length-1]=0; // kill \n
> + linfo->index_file_offset = my_b_tell(&index_file);
> +
> +err:
> + if (need_lock)
> + mysql_mutex_unlock(&LOCK_index);
> + return error;
> +}
> +
> +
> +/**
> + 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
> +
> + @note
> + If not called from slave thread, write start event to new log
> +
> + @retval
> + 0 ok
> + @retval
> + 1 error
> +*/
> +
> +bool MYSQL_BIN_LOG::reset_logs(THD* thd)
> +{
> + 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);
> +
> + /*
> + 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));
> +
> + 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::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;
> +}
> +
> +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;
> +}
> +
> +void MYSQL_BIN_LOG::stop_union_events(THD *thd)
> +{
> + DBUG_ASSERT(thd->binlog_evt_union.do_union);
> + thd->binlog_evt_union.do_union= FALSE;
> +}
> +
> +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);
> +}
> +
> +
> +/**
> + 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.
> +*/
> +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);
> +
> + 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);
> +}
> +
> +/*
> + 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));
> +
> + int error= 0;
> + 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));
> +
> + DBUG_PRINT("info", ("cache_mngr->pending(): 0x%lx", (long)
> cache_data->pending()));
> +
> + if (Rows_log_event* pending= cache_data->pending())
> + {
> + IO_CACHE *file= &cache_data->cache_log;
> +
> + /*
> + Write pending event to the cache.
> + */
> + if (pending->write(file))
> + {
> + 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);
> + }
> +
> + /*
> + We step the table map version if we are writing an event
> + representing the end of a statement.
> +
> + 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 (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 (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);
> +
> + /* 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;
> + }
> + }
> + }
> + }
> +
> + /*
> + Write the event.
> + */
> + if (event_info->write(file) ||
> + DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0))
> + goto err;
> +
> + error= 0;
> +
> +err:
> + if (event_info->use_direct_logging())
> + {
> + if (!error)
> + {
> + 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_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);
> + }
> +
> + if (error)
> + {
> + set_write_error(thd);
> + if (check_write_error(thd) && cache_data &&
> + stmt_has_updated_non_trans_table(thd))
> + cache_data->set_incident();
> + }
> + }
> +
> + if (event_info->flags & LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F)
> + ++m_table_map_version;
> +
> + DBUG_RETURN(error);
> +}
> +
> +
> +void MYSQL_BIN_LOG::rotate_and_purge(uint flags)
> +{
> +#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);
> +
> +#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
> +}
> +
> +uint MYSQL_BIN_LOG::next_file_id()
> +{
> + uint res;
> + mysql_mutex_lock(&LOCK_log);
> + res = file_id++;
> + mysql_mutex_unlock(&LOCK_log);
> + return res;
> +}
> +
> +
> +/*
> + Write the contents of a cache to the binary 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
> +
> + 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.
> + */
> +
> +int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log)
> +{
> + Mutex_sentry sentry(lock_log ? &LOCK_log : NULL);
> +
> + 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];
> +
> + /*
> + 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.
> + */
> +
> + group= (uint)my_b_tell(&log_file);
> + hdr_offs= carry= 0;
> +
> + do
> + {
> + /*
> + 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);
> +
> + /* assemble both halves */
> + memcpy(&header[carry], (char *)cache->read_pos, LOG_EVENT_HEADER_LEN -
> carry);
> +
> + /* 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;
> +
> + /*
> + 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);
> +
> + /* next event header at ... */
> + hdr_offs = uint4korr(&header[EVENT_LEN_OFFSET]) - carry;
> +
> + carry= 0;
> + }
> +
> + /* if there is anything to write, process it. */
> +
> + 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").
> + */
> +
> + while (hdr_offs < length)
> + {
> + /*
> + partial header only? save what we can get, process once
> + we get the rest.
> + */
> +
> + 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 */
> +
> + uchar *log_pos= (uchar *)cache->read_pos + hdr_offs + LOG_POS_OFFSET;
> +
> + /* fix end_log_pos */
> + val= uint4korr(log_pos) + group;
> + int4store(log_pos, val);
> +
> + /* next event header at ... */
> + log_pos= (uchar *)cache->read_pos + hdr_offs + EVENT_LEN_OFFSET;
> + hdr_offs += uint4korr(log_pos);
> +
> + }
> + }
> +
> + /*
> + 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;
> + }
> +
> + /* 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)));
> +
> + DBUG_ASSERT(carry == 0);
> +
> + if (sync_log)
> + return flush_and_sync(0);
> +
> + return 0; // All OK
> +}
> +
> +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);
> +}
> +
> +/**
> + 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
> + @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 MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event,
> + bool incident)
> +{
> + DBUG_ENTER("MYSQL_BIN_LOG::write(THD *, IO_CACHE *, Log_event *)");
> + mysql_mutex_lock(&LOCK_log);
> +
> + 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();
> + });
> +
> + if ((write_error= write_cache(cache, false, false)))
> + goto err;
> +
> + if (commit_event && commit_event->write(&log_file))
> + goto err;
> +
> + if (incident && write_incident(thd, FALSE))
> + goto err;
> +
> + 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;
> + }
> +
> + 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;
> + }
> +
> + signal_update();
> + }
> +
> + /*
> + 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);
> +
> + DBUG_RETURN(0);
> +
> +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);
> +}
> +
> +
> +/**
> + Wait until we get a signal that the relay log has been updated.
> +
> + @param thd Thread variable
> +
> + @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 MYSQL_BIN_LOG::wait_for_update_relay_log(THD* thd)
> +{
> + const char *old_msg;
> + DBUG_ENTER("wait_for_update_relay_log");
> +
> + 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;
> +}
> +
> +/**
> + 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.
> +*/
> +
> +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);
> +}
> +
> +
> +/**
> + Close the log file.
> +
> + @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
> +
> + @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 */
> +
> + /* 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));
> + }
> +
> + /* 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)
> + {
> + 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;
> +}
> +
> +
> +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;
> +}
> +
> +
> +void MYSQL_BIN_LOG::signal_update()
> +{
> + 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 ******/
> +
> +/**
> + @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::open(const char *opt_name)
> +{
> + LOG_INFO log_info;
> + int error= 1;
> +
> + DBUG_ASSERT(total_ha_2pc > 1);
> + DBUG_ASSERT(opt_name && opt_name[0]);
> +
> + 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 (!my_b_inited(&index_file))
> + {
> + /* There was a failure to open the index file, can't open the binlog */
> + cleanup();
> + return 1;
> + }
> +
> + 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;
> + }
> +
> + 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;
> + }
> +
> + {
> + const char *errmsg;
> + IO_CACHE log;
> + File file;
> + Log_event *ev=0;
> + Format_description_log_event fdle(BINLOG_VERSION);
> + char log_name[FN_REFLEN];
> +
> + if (! fdle.is_valid())
> + goto err;
> +
> + do
> + {
> + strmake(log_name, log_info.log_file_name, sizeof(log_name)-1);
> + } while (!(error= find_next_log(&log_info, 1)));
> +
> + if (error != LOG_INFO_EOF)
> + {
> + sql_print_error("find_log_pos() failed (error: %d)", error);
> + goto err;
> + }
> +
> + if ((file= open_binlog(&log, log_name, &errmsg)) < 0)
> + {
> + sql_print_error("%s", errmsg);
> + goto err;
> + }
> +
> + 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;
> +
> + delete ev;
> + end_io_cache(&log);
> + mysql_file_close(file, MYF(MY_WME));
> +
> + if (error)
> + goto err;
> + }
> +
> +err:
> + 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);
> +}
> +
> +/**
> + @todo
> + group commit
> +
> + @retval
> + 0 error
> + @retval
> + 1 success
> +*/
> +int MYSQL_BIN_LOG::log_xid(THD *thd, my_xid xid)
> +{
> + 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));
> +}
> +
> +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
> +}
> +
> +int MYSQL_BIN_LOG::recover(IO_CACHE *log, Format_description_log_event *fdle)
> +{
> + Log_event *ev;
> + HASH xids;
> + MEM_ROOT mem_root;
> +
> + 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;
> +
> + init_alloc_root(&mem_root, TC_LOG_PAGE_SIZE, TC_LOG_PAGE_SIZE);
> +
> + 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)
> + {
> + 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;
> + }
> +
> + if (ha_recover(&xids))
> + goto err2;
> +
> + free_root(&mem_root, MYF(0));
> + my_hash_free(&xids);
> + return 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;
> +}
> +
> +
> +/*
> + These functions are placed in this file since they need access to
> + binlog_hton, which has internal linkage.
> +*/
> +
> +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 (cache_mngr)
> + DBUG_RETURN(0); // Already set up
> +
> + 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);
> +
> + cache_mngr= new (thd_get_ha_data(this, binlog_hton)) binlog_cache_mngr;
> +
> + DBUG_RETURN(0);
> +}
> +
> +/*
> + Function to start a statement and optionally a transaction for the
> + binary log.
> +
> + SYNOPSIS
> + binlog_start_trans_and_stmt()
> +
> + DESCRIPTION
> +
> + 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.
> +
> + - 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()
> +{
> + 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)
> + {
> + 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();
> + }
> + DBUG_VOID_RETURN;
> +}
> +
> +void THD::binlog_set_stmt_begin() {
> + binlog_cache_mngr *cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
> +
> + /*
> + 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);
> +}
> +
> +
> +/**
> + 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));
> +
> + /* Pre-conditions */
> + DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
> mysql_bin_log.is_open());
> + DBUG_ASSERT(table->s->table_map_id != ULONG_MAX);
> +
> + Table_map_log_event
> + the_event(this, table, table->s->table_map_id, is_transactional);
> +
> + if (binlog_table_maps == 0)
> + binlog_start_trans_and_stmt();
> +
> + binlog_cache_mngr *const cache_mngr=
> + (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
> +
> + IO_CACHE *file=
> + cache_mngr->get_binlog_cache_log(use_trans_cache(this, is_transactional));
> + if ((error= the_event.write(file)))
> + DBUG_RETURN(error);
> +
> + binlog_table_maps++;
> + table->s->table_map_version= mysql_bin_log.table_map_version();
> + DBUG_RETURN(0);
> +}
> +
> +/**
> + 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
> +{
> + 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);
> +}
> +
> +/**
> + 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)
> +{
> + 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);
> +}
> +
> +/**
> + 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
> +
> +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);
> +}
> +
> +#endif /* !defined(MYSQL_CLIENT) */
> +
> +#ifdef INNODB_COMPATIBILITY_HOOKS
> +/**
> + Get the file name of the MySQL binlog.
> + @return the name of the binlog file
> +*/
> +extern "C"
> +const char* mysql_bin_log_file_name(void)
> +{
> + return mysql_bin_log.get_log_fname();
> +}
> +/**
> + Get the current position of the MySQL binlog.
> + @return byte offset from the beginning of the binlog
> +*/
> +extern "C"
> +ulonglong mysql_bin_log_file_pos(void)
> +{
> + return (ulonglong) mysql_bin_log.get_log_file()->pos_in_file;
> +}
> +#endif /* INNODB_COMPATIBILITY_HOOKS */
> +
> +
> +struct st_mysql_storage_engine binlog_storage_engine=
> +{ MYSQL_HANDLERTON_INTERFACE_VERSION };
> +
> +mysql_declare_plugin(binlog)
> +{
> + MYSQL_STORAGE_ENGINE_PLUGIN,
> + &binlog_storage_engine,
> + "binlog",
> + "MySQL AB",
> + "This is a pseudo storage engine to represent the binlog in a transaction",
> + PLUGIN_LICENSE_GPL,
> + binlog_init, /* Plugin Init */
> + NULL, /* Plugin Deinit */
> + 0x0100 /* 1.0 */,
> + NULL, /* status variables */
> + NULL, /* system variables */
> + NULL /* config options */
> +}
> +mysql_declare_plugin_end;
>
> === added file 'sql/binlog.h'
> --- a/sql/binlog.h 1970-01-01 00:00:00 +0000
> +++ b/sql/binlog.h 2010-06-21 05:56:56 +0000
> @@ -0,0 +1,273 @@
> +#ifndef BINLOG_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 BINLOG_H_INCLUDED
> +
> +#include "log_event.h"
> +#include "log.h"
> +
> +class Relay_log_info;
> +
> +class Format_description_log_event;
> +
> +class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
> +{
> + private:
> + /* LOCK_log and LOCK_index are inited by init_pthread_objects() */
> + mysql_mutex_t LOCK_index;
> + mysql_mutex_t LOCK_prep_xids;
> + mysql_cond_t COND_prep_xids;
> + mysql_cond_t update_cond;
> + ulonglong bytes_written;
> + IO_CACHE index_file;
> + char index_file_name[FN_REFLEN];
> + /*
> + purge_file is a temp file used in purge_logs so that the index file
> + can be updated before deleting files from disk, yielding better crash
> + recovery. It is created on demand the first time purge_logs is called
> + and then reused for subsequent calls. It is cleaned up in cleanup().
> + */
> + IO_CACHE purge_index_file;
> + char purge_index_file_name[FN_REFLEN];
> + /*
> + The max size before rotation (usable only if log_type == LOG_BIN: binary
> + logs and relay logs).
> + For a binlog, max_size should be max_binlog_size.
> + For a relay log, it should be max_relay_log_size if this is non-zero,
> + max_binlog_size otherwise.
> + max_size is set in init(), and dynamically changed (when one does SET
> + GLOBAL MAX_BINLOG_SIZE|MAX_RELAY_LOG_SIZE) by fix_max_binlog_size and
> + fix_max_relay_log_size).
> + */
> + ulong max_size;
> + long prepared_xids; /* for tc log - number of xids to remember */
> + // current file sequence number for load data infile binary logging
> + uint file_id;
> + uint open_count; // For replication
> + int readers_count;
> + bool need_start_event;
> + /*
> + no_auto_events means we don't want any of these automatic events :
> + Start/Rotate/Stop. That is, in 4.x when we rotate a relay log, we don't
> + want a Rotate_log event to be written to the relay log. When we start a
> + relay log etc. So in 4.x this is 1 for relay logs, 0 for binlogs.
> + In 5.0 it's 0 for relay logs too!
> + */
> + bool no_auto_events;
> +
> + ulonglong m_table_map_version;
> +
> + /* pointer to the sync period variable, for binlog this will be
> + sync_binlog_period, for relay log this will be
> + sync_relay_log_period
> + */
> + uint *sync_period_ptr;
> + uint sync_counter;
> +
> + inline uint get_sync_period()
> + {
> + return *sync_period_ptr;
> + }
> +
> + int write_to_file(IO_CACHE *cache);
> + /*
> + This is used to start writing to a new log file. The difference from
> + new_file() is locking. new_file_without_locking() does not acquire
> + LOCK_log.
> + */
> + void new_file_without_locking();
> + void new_file_impl(bool need_lock);
> +
> +public:
> + MYSQL_LOG::generate_name;
> + MYSQL_LOG::is_open;
> +// int generate_new_name(char *new_name, const char *log_name);
> +
> + /* This is relay log */
> + bool is_relay_log;
> + ulong signal_cnt; // update of the counter is checked by heartbeat
> + /*
> + These describe the log's format. This is used only for relay logs.
> + _for_exec is used by the SQL thread, _for_queue by the I/O thread. It's
> + necessary to have 2 distinct objects, because the I/O thread may be reading
> + events in a different format from what the SQL thread is reading (consider
> + the case of a master which has been upgraded from 5.0 to 5.1 without doing
> + RESET MASTER, or from 4.x to 5.0).
> + */
> + Format_description_log_event *description_event_for_exec,
> + *description_event_for_queue;
> +
> + MYSQL_BIN_LOG(uint *sync_period);
> + /*
> + note that there's no destructor ~MYSQL_BIN_LOG() !
> + The reason is that we don't want it to be automatically called
> + on exit() - but only during the correct shutdown process
> + */
> +
> + int open(const char *opt_name);
> + void close();
> + int log_xid(THD *thd, my_xid xid);
> + void unlog(ulong cookie, my_xid xid);
> + int recover(IO_CACHE *log, Format_description_log_event *fdle);
> +#if !defined(MYSQL_CLIENT)
> + bool is_table_mapped(TABLE *table) const
> + {
> + return table->s->table_map_version == table_map_version();
> + }
> +
> + ulonglong table_map_version() const { return m_table_map_version; }
> + void update_table_map_version() { ++m_table_map_version; }
> +
> + int flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event,
> + bool is_transactional);
> + int remove_pending_rows_event(THD *thd, bool is_transactional);
> +
> +#endif /* !defined(MYSQL_CLIENT) */
> + void reset_bytes_written()
> + {
> + bytes_written = 0;
> + }
> + void harvest_bytes_written(ulonglong* counter)
> + {
> +#ifndef DBUG_OFF
> + char buf1[22],buf2[22];
> +#endif
> + DBUG_ENTER("harvest_bytes_written");
> + (*counter)+=bytes_written;
> + DBUG_PRINT("info",("counter: %s bytes_written: %s", llstr(*counter,buf1),
> + llstr(bytes_written,buf2)));
> + bytes_written=0;
> + DBUG_VOID_RETURN;
> + }
> + void set_max_size(ulong max_size_arg);
> + void signal_update();
> + void wait_for_update_relay_log(THD* thd);
> + int wait_for_update_bin_log(THD* thd, const struct timespec * timeout);
> + void set_need_start_event() { need_start_event = 1; }
> + void init(bool no_auto_events_arg, ulong max_size);
> + void init_pthread_objects();
> + void cleanup();
> + bool open(const char *log_name,
> + enum_log_type log_type,
> + const char *new_name,
> + enum cache_type io_cache_type_arg,
> + bool no_auto_events_arg, ulong max_size,
> + bool null_created,
> + bool need_mutex);
> + bool open_index_file(const char *index_file_name_arg,
> + const char *log_name, bool need_mutex);
> + /* Use this to start writing a new log file */
> + void new_file();
> +
> + bool write(Log_event* event_info);
> + bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event, bool incident);
> + bool write_incident(THD *thd, bool lock);
> +
> + int write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync);
> + void set_write_error(THD *thd);
> + bool check_write_error(THD *thd);
> +
> + void start_union_events(THD *thd, query_id_t query_id_param);
> + void stop_union_events(THD *thd);
> + bool is_query_in_union(THD *thd, query_id_t query_id_param);
> +
> + /*
> + v stands for vector
> + invoked as appendv(buf1,len1,buf2,len2,...,bufn,lenn,0)
> + */
> + bool appendv(const char* buf,uint len,...);
> + bool append(Log_event* ev);
> +
> + void make_log_name(char* buf, const char* log_ident);
> + bool is_active(const char* log_file_name);
> + int update_log_index(LOG_INFO* linfo, bool need_update_threads);
> + void rotate_and_purge(uint flags);
> + /**
> + Flush binlog cache and synchronize to disk.
> +
> + This function flushes events in binlog cache to binary log file,
> + it will do synchronizing according to the setting of system
> + variable 'sync_binlog'. If file is synchronized, @c synced will
> + be set to 1, otherwise 0.
> +
> + @param[out] synced if not NULL, set to 1 if file is synchronized, otherwise 0
> +
> + @retval 0 Success
> + @retval other Failure
> + */
> + bool flush_and_sync(bool *synced);
> + int purge_logs(const char *to_log, bool included,
> + bool need_mutex, bool need_update_threads,
> + ulonglong *decrease_log_space);
> + int purge_logs_before_date(time_t purge_time);
> + int purge_first_log(Relay_log_info* rli, bool included);
> + int set_purge_index_file_name(const char *base_file_name);
> + int open_purge_index_file(bool destroy);
> + bool is_inited_purge_index_file();
> + int close_purge_index_file();
> + int clean_purge_index_file();
> + int sync_purge_index_file();
> + int register_purge_index_entry(const char* entry);
> + int register_create_index_entry(const char* entry);
> + int purge_index_entry(THD *thd, ulonglong *decrease_log_space,
> + bool need_mutex);
> + bool reset_logs(THD* thd);
> + void close(uint exiting);
> +
> + // iterating through the log index file
> + int find_log_pos(LOG_INFO* linfo, const char* log_name,
> + bool need_mutex);
> + int find_next_log(LOG_INFO* linfo, bool need_mutex);
> + int get_current_log(LOG_INFO* linfo);
> + int raw_get_current_log(LOG_INFO* linfo);
> + uint next_file_id();
> + inline char* get_index_fname() { return index_file_name;}
> + inline char* get_log_fname() { return log_file_name; }
> + inline char* get_name() { return name; }
> + inline mysql_mutex_t* get_log_lock() { return &LOCK_log; }
> + inline IO_CACHE* get_log_file() { return &log_file; }
> +
> + inline void lock_index() { mysql_mutex_lock(&LOCK_index);}
> + inline void unlock_index() { mysql_mutex_unlock(&LOCK_index);}
> + inline IO_CACHE *get_index_file() { return &index_file;}
> + 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/field.cc'
> --- a/sql/field.cc 2010-04-11 06:54:40 +0000
> +++ b/sql/field.cc 2010-06-21 06:01:50 +0000
> @@ -28,7 +28,7 @@
> #include "sql_priv.h"
> #include "sql_select.h"
> #include "rpl_rli.h" // Pull in Relay_log_info
> -#include "slave.h" // Pull in rpl_master_has_bug()
> +#include "rpl_slave.h" // Pull in rpl_master_has_bug()
> #include "strfunc.h" // find_type2, find_set
> #include "sql_time.h" // str_to_datetime_with_warn,
> // str_to_time_with_warn,
>
> === modified file 'sql/ha_ndbcluster_binlog.cc'
> --- a/sql/ha_ndbcluster_binlog.cc 2010-05-17 12:10:26 +0000
> +++ b/sql/ha_ndbcluster_binlog.cc 2010-06-21 06:01:50 +0000
> @@ -23,7 +23,7 @@
> #ifdef HAVE_NDB_BINLOG
> #include "rpl_injector.h"
> #include "rpl_filter.h"
> -#include "slave.h"
> +#include "rpl_slave.h"
> #include "ha_ndbcluster_binlog.h"
> #include "NdbDictionary.hpp"
> #include "ndb_cluster_connection.hpp"
>
> === modified file 'sql/ha_partition.cc'
> --- a/sql/ha_partition.cc 2010-04-01 19:34:09 +0000
> +++ b/sql/ha_partition.cc 2010-06-21 06:01:50 +0000
> @@ -53,6 +53,7 @@
>
> #include "sql_priv.h"
> #include "sql_parse.h" // append_file_to_dir
> +#include "binlog.h" // mysql_bin_log
>
> #ifdef WITH_PARTITION_STORAGE_ENGINE
> #include "ha_partition.h"
>
> === modified file 'sql/item_func.cc'
> --- a/sql/item_func.cc 2010-05-23 04:28:04 +0000
> +++ b/sql/item_func.cc 2010-06-21 06:01:50 +0000
> @@ -34,7 +34,7 @@
> */
> #include "sql_class.h" // set_var.h: THD
> #include "set_var.h"
> -#include "slave.h" // for wait_for_master_pos
> +#include "rpl_slave.h" // for wait_for_master_pos
> #include "sql_show.h" // append_identifier
> #include "strfunc.h" // find_type
> #include "sql_parse.h" // is_update_query
>
> === modified file 'sql/log.cc'
> --- a/sql/log.cc 2010-04-30 04:13:44 +0000
> +++ b/sql/log.cc 2010-06-21 06:01:50 +0000
> @@ -28,15 +28,11 @@
> #include "sql_priv.h"
> #include "log.h"
> #include "sql_base.h" // close_thread_tables
> -#include "sql_repl.h"
> #include "sql_delete.h" // mysql_truncate
> #include "sql_parse.h" // command_name
> #include "sql_time.h" // calc_time_from_sec, my_time_compare
> #include "tztime.h" // my_tz_OFFSET0, struct Time_zone
> #include "sql_acl.h" // SUPER_ACL
> -#include "log_event.h" // Query_log_event
> -#include "rpl_filter.h"
> -#include "rpl_rli.h"
> #include "sql_audit.h"
>
> #include <my_dir.h>
> @@ -47,29 +43,14 @@
> #include "message.h"
> #endif
>
> -#include "sql_plugin.h"
> -#include "rpl_handler.h"
> -
> /* max size of the log message */
> #define MAX_LOG_BUFFER_SIZE 1024
> #define MAX_TIME_SIZE 32
> -#define MY_OFF_T_UNDEF (~(my_off_t)0UL)
> -
> -#define FLAGSTR(V,F) ((V)&(F)?#F" ":"")
>
> LOGGER logger;
>
> -MYSQL_BIN_LOG mysql_bin_log(&sync_binlog_period);
> -
> static bool test_if_number(const char *str,
> ulong *res, bool allow_wildcards);
> -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);
> -static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv);
> -static int binlog_commit(handlerton *hton, THD *thd, bool all);
> -static int binlog_rollback(handlerton *hton, THD *thd, bool all);
> -static int binlog_prepare(handlerton *hton, THD *thd, bool all);
>
> /**
> purge logs, master and slave sides both, related error code
> @@ -166,195 +147,6 @@ char *make_log_name(char *buff, const ch
> MYF(MY_UNPACK_FILENAME|MY_REPLACE_EXT));
> }
>
> -/*
> - Helper class to hold a mutex for the duration of the
> - block.
> -
> - Eliminates the need for explicit unlocking of mutexes on, e.g.,
> - error returns. On passing a null pointer, the sentry will not do
> - anything.
> - */
> -class Mutex_sentry
> -{
> -public:
> - Mutex_sentry(mysql_mutex_t *mutex)
> - : m_mutex(mutex)
> - {
> - if (m_mutex)
> - mysql_mutex_lock(mutex);
> - }
> -
> - ~Mutex_sentry()
> - {
> - if (m_mutex)
> - mysql_mutex_unlock(m_mutex);
> -#ifndef DBUG_OFF
> - m_mutex= 0;
> -#endif
> - }
> -
> -private:
> - mysql_mutex_t *m_mutex;
> -
> - // It's not allowed to copy this object in any way
> - Mutex_sentry(Mutex_sentry const&);
> - void operator=(Mutex_sentry const&);
> -};
> -
> -/*
> - Helper classes to store non-transactional and transactional data
> - before copying it to the binary log.
> -*/
> -class binlog_cache_data
> -{
> -public:
> - binlog_cache_data(): m_pending(0), before_stmt_pos(MY_OFF_T_UNDEF),
> - incident(FALSE)
> - {
> - cache_log.end_of_file= max_binlog_cache_size;
> - }
> -
> - ~binlog_cache_data()
> - {
> - DBUG_ASSERT(empty());
> - close_cached_file(&cache_log);
> - }
> -
> - bool empty() const
> - {
> - return pending() == NULL && my_b_tell(&cache_log) == 0;
> - }
> -
> - Rows_log_event *pending() const
> - {
> - return m_pending;
> - }
> -
> - void set_pending(Rows_log_event *const pending)
> - {
> - m_pending= pending;
> - }
> -
> - void set_incident(void)
> - {
> - incident= TRUE;
> - }
> -
> - bool has_incident(void)
> - {
> - return(incident);
> - }
> -
> - void reset()
> - {
> - truncate(0);
> - incident= FALSE;
> - before_stmt_pos= MY_OFF_T_UNDEF;
> - cache_log.end_of_file= max_binlog_cache_size;
> - DBUG_ASSERT(empty());
> - }
> -
> - my_off_t get_byte_position() const
> - {
> - return my_b_tell(&cache_log);
> - }
> -
> - my_off_t get_prev_position()
> - {
> - return(before_stmt_pos);
> - }
> -
> - void set_prev_position(my_off_t pos)
> - {
> - before_stmt_pos= pos;
> - }
> -
> - void restore_prev_position()
> - {
> - truncate(before_stmt_pos);
> - }
> -
> - void restore_savepoint(my_off_t pos)
> - {
> - truncate(pos);
> - if (pos < before_stmt_pos)
> - before_stmt_pos= MY_OFF_T_UNDEF;
> - }
> -
> - /*
> - Cache to store data before copying it to the binary log.
> - */
> - IO_CACHE cache_log;
> -
> -private:
> - /*
> - Pending binrows event. This event is the event where the rows are currently
> - written.
> - */
> - Rows_log_event *m_pending;
> -
> - /*
> - Binlog position before the start of the current statement.
> - */
> - my_off_t before_stmt_pos;
> -
> - /*
> - This indicates that some events did not get into the cache and most likely
> - it is corrupted.
> - */
> - bool incident;
> -
> - /*
> - It truncates the cache to a certain position. This includes deleting the
> - pending event.
> - */
> - void truncate(my_off_t pos)
> - {
> - DBUG_PRINT("info", ("truncating to position %lu", (ulong) pos));
> - if (pending())
> - {
> - delete pending();
> - set_pending(0);
> - }
> - reinit_io_cache(&cache_log, WRITE_CACHE, pos, 0, 0);
> - cache_log.end_of_file= max_binlog_cache_size;
> - }
> -
> - binlog_cache_data& operator=(const binlog_cache_data& info);
> - binlog_cache_data(const binlog_cache_data& info);
> -};
> -
> -class binlog_cache_mngr {
> -public:
> - binlog_cache_mngr() {}
> -
> - void reset_cache(binlog_cache_data* cache_data)
> - {
> - cache_data->reset();
> - }
> -
> - binlog_cache_data* get_binlog_cache_data(bool is_transactional)
> - {
> - return (is_transactional ? &trx_cache : &stmt_cache);
> - }
> -
> - IO_CACHE* get_binlog_cache_log(bool is_transactional)
> - {
> - return (is_transactional ? &trx_cache.cache_log :
> &stmt_cache.cache_log);
> - }
> -
> - binlog_cache_data stmt_cache;
> -
> - binlog_cache_data trx_cache;
> -
> -private:
> -
> - binlog_cache_mngr& operator=(const binlog_cache_mngr& info);
> - binlog_cache_mngr(const binlog_cache_mngr& info);
> -};
> -
> -handlerton *binlog_hton;
> -
> bool LOGGER::is_log_table_enabled(uint log_table_type)
> {
> switch (log_table_type) {
> @@ -1392,3977 +1184,754 @@ int LOGGER::set_handlers(uint error_log_
> return 0;
> }
>
> - /*
> - Save position of binary log transaction cache.
> -
> - SYNPOSIS
> - binlog_trans_log_savepos()
> +#ifdef _WIN32
> +static int eventSource = 0;
>
> - thd The thread to take the binlog data from
> - pos Pointer to variable where the position will be stored
> +static void setup_windows_event_source()
> +{
> + HKEY hRegKey= NULL;
> + DWORD dwError= 0;
> + TCHAR szPath[MAX_PATH];
> + DWORD dwTypes;
>
> - DESCRIPTION
> + if (eventSource) // Ensure that we are only called once
> + return;
> + eventSource= 1;
>
> - Save the current position in the binary log transaction cache into
> - the variable pointed to by 'pos'
> - */
> -
> -static void
> -binlog_trans_log_savepos(THD *thd, my_off_t *pos)
> -{
> - DBUG_ENTER("binlog_trans_log_savepos");
> - DBUG_ASSERT(pos != NULL);
> - if (thd_get_ha_data(thd, binlog_hton) == NULL)
> - thd->binlog_setup_trx_data();
> - binlog_cache_mngr *const cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> - DBUG_ASSERT(mysql_bin_log.is_open());
> - *pos= cache_mngr->trx_cache.get_byte_position();
> - DBUG_PRINT("return", ("*pos: %lu", (ulong) *pos));
> - DBUG_VOID_RETURN;
> -}
> + // Create the event source registry key
> + dwError= RegCreateKey(HKEY_LOCAL_MACHINE,
> +
> "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\MySQL",
> + &hRegKey);
>
> + /* Name of the PE module that contains the message resource */
> + GetModuleFileName(NULL, szPath, MAX_PATH);
>
> -/*
> - Truncate the binary log transaction cache.
> + /* Register EventMessageFile */
> + dwError = RegSetValueEx(hRegKey, "EventMessageFile", 0, REG_EXPAND_SZ,
> + (PBYTE) szPath, (DWORD) (strlen(szPath) + 1));
>
> - SYNPOSIS
> - binlog_trans_log_truncate()
> + /* Register supported event types */
> + dwTypes= (EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
> + EVENTLOG_INFORMATION_TYPE);
> + dwError= RegSetValueEx(hRegKey, "TypesSupported", 0, REG_DWORD,
> + (LPBYTE) &dwTypes, sizeof dwTypes);
>
> - thd The thread to take the binlog data from
> - pos Position to truncate to
> + RegCloseKey(hRegKey);
> +}
>
> - DESCRIPTION
> +#endif /* _WIN32 */
>
> - Truncate the binary log to the given position. Will not change
> - anything else.
> +/**
> + Find a unique filename for 'filename.#'.
>
> - */
> -static void
> -binlog_trans_log_truncate(THD *thd, my_off_t pos)
> -{
> - DBUG_ENTER("binlog_trans_log_truncate");
> - DBUG_PRINT("enter", ("pos: %lu", (ulong) pos));
> -
> - DBUG_ASSERT(thd_get_ha_data(thd, binlog_hton) != NULL);
> - /* Only true if binlog_trans_log_savepos() wasn't called before */
> - DBUG_ASSERT(pos != ~(my_off_t) 0);
> -
> - binlog_cache_mngr *const cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> - cache_mngr->trx_cache.restore_savepoint(pos);
> - DBUG_VOID_RETURN;
> -}
> + Set '#' to the number next to the maximum found in the most
> + recent log file extension.
>
> + This function will return nonzero if: (i) the generated name
> + exceeds FN_REFLEN; (ii) if the number of extensions is exhausted;
> + or (iii) some other error happened while examining the filesystem.
>
> -/*
> - this function is mostly a placeholder.
> - conceptually, binlog initialization (now mostly done in MYSQL_BIN_LOG::open)
> - should be moved here.
> + @return
> + nonzero if not possible to get unique filename.
> */
>
> -int binlog_init(void *p)
> -{
> - binlog_hton= (handlerton *)p;
> - binlog_hton->state=opt_bin_log ? SHOW_OPTION_YES : SHOW_OPTION_NO;
> - binlog_hton->db_type=DB_TYPE_BINLOG;
> - binlog_hton->savepoint_offset= sizeof(my_off_t);
> - binlog_hton->close_connection= binlog_close_connection;
> - binlog_hton->savepoint_set= binlog_savepoint_set;
> - binlog_hton->savepoint_rollback= binlog_savepoint_rollback;
> - binlog_hton->commit= binlog_commit;
> - binlog_hton->rollback= binlog_rollback;
> - binlog_hton->prepare= binlog_prepare;
> - binlog_hton->flags= HTON_NOT_USER_SELECTABLE | HTON_HIDDEN;
> - return 0;
> -}
> -
> -static int binlog_close_connection(handlerton *hton, THD *thd)
> +static int find_uniq_filename(char *name)
> {
> - binlog_cache_mngr *const cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> - DBUG_ASSERT(cache_mngr->trx_cache.empty() &&
> cache_mngr->stmt_cache.empty());
> - thd_set_ha_data(thd, binlog_hton, NULL);
> - cache_mngr->~binlog_cache_mngr();
> - my_free((uchar*)cache_mngr, MYF(0));
> - return 0;
> -}
> -
> -/**
> - This function flushes a transactional cache upon commit/rollback.
> -
> - @param thd The thread whose transaction should be flushed
> - @param cache_mngr Pointer to the cache data to be flushed
> - @param end_ev The end event either commit/rollback.
> + uint i;
> + char buff[FN_REFLEN], ext_buf[FN_REFLEN];
> + struct st_my_dir *dir_info;
> + reg1 struct fileinfo *file_info;
> + ulong max_found= 0, next= 0, number= 0;
> + size_t buf_length, length;
> + char *start, *end;
> + int error= 0;
> + DBUG_ENTER("find_uniq_filename");
>
> - @return
> - nonzero if an error pops up when flushing the transactional cache.
> -*/
> -static int
> -binlog_flush_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr,
> - Log_event *end_ev)
> -{
> - DBUG_ENTER("binlog_flush_trx_cache");
> - int error=0;
> - IO_CACHE *cache_log= &cache_mngr->trx_cache.cache_log;
> + length= dirname_part(buff, name, &buf_length);
> + start= name + length;
> + end= strend(start);
>
> - /*
> - This function handles transactional changes and as such
> - this flag equals to true.
> - */
> - bool const is_transactional= TRUE;
> + *end='.';
> + length= (size_t) (end - start + 1);
>
> - if (thd->binlog_flush_pending_rows_event(TRUE, is_transactional))
> + if (!(dir_info= my_dir(buff,MYF(MY_DONT_SORT))))
> + { // This shouldn't happen
> + strmov(end,".1"); // use name+1
> DBUG_RETURN(1);
> - /*
> - Doing a commit or a rollback including non-transactional tables,
> - i.e., ending a transaction where we might write the transaction
> - cache to the binary log.
> -
> - We can always end the statement when ending a transaction since
> - transactions are not allowed inside stored functions. If they
> - were, we would have to ensure that we're not ending a statement
> - inside a stored function.
> - */
> - error= mysql_bin_log.write(thd, &cache_mngr->trx_cache.cache_log, end_ev,
> - cache_mngr->trx_cache.has_incident());
> - cache_mngr->reset_cache(&cache_mngr->trx_cache);
> -
> - /*
> - We need to step the table map version after writing the
> - transaction cache to disk.
> - */
> - mysql_bin_log.update_table_map_version();
> - statistic_increment(binlog_cache_use, &LOCK_status);
> - if (cache_log->disk_writes != 0)
> + }
> + file_info= dir_info->dir_entry;
> + for (i= dir_info->number_off_files ; i-- ; file_info++)
> {
> - statistic_increment(binlog_cache_disk_use, &LOCK_status);
> - cache_log->disk_writes= 0;
> + if (bcmp((uchar*) file_info->name, (uchar*) start, length) == 0 &&
> + test_if_number(file_info->name+length, &number,0))
> + {
> + set_if_bigger(max_found,(ulong) number);
> + }
> }
> + my_dirend(dir_info);
>
> - DBUG_ASSERT(cache_mngr->trx_cache.empty());
> - DBUG_RETURN(error);
> -}
> -
> -/**
> - This function truncates the transactional cache upon committing or rolling
> - back either a transaction or a statement.
> -
> - @param thd The thread whose transaction should be flushed
> - @param cache_mngr Pointer to the cache data to be flushed
> - @param all @c true means truncate the transaction, otherwise the
> - statement must be truncated.
> -
> - @return
> - nonzero if an error pops up when truncating the transactional cache.
> -*/
> -static int
> -binlog_truncate_trx_cache(THD *thd, binlog_cache_mngr *cache_mngr, bool all)
> -{
> - DBUG_ENTER("binlog_truncate_trx_cache");
> - int error=0;
> - /*
> - This function handles transactional changes and as such this flag
> - equals to true.
> - */
> - bool const is_transactional= TRUE;
> -
> - DBUG_PRINT("info", ("thd->options={ %s%s}, transaction: %s",
> - FLAGSTR(thd->variables.option_bits,
> OPTION_NOT_AUTOCOMMIT),
> - FLAGSTR(thd->variables.option_bits, OPTION_BEGIN),
> - all ? "all" : "stmt"));
> - /*
> - If rolling back an entire transaction or a single statement not
> - inside a transaction, we reset the transaction cache.
> - */
> - thd->binlog_remove_pending_rows_event(TRUE, is_transactional);
> - if (ending_trans(thd, all))
> + /* check if reached the maximum possible extension number */
> + if ((max_found == MAX_LOG_UNIQUE_FN_EXT))
> {
> - if (cache_mngr->trx_cache.has_incident())
> - error= mysql_bin_log.write_incident(thd, TRUE);
> + sql_print_error("Log filename extension number exhausted: %06lu. \
> +Please fix this by archiving old logs and \
> +updating the index files.", max_found);
> + error= 1;
> + goto end;
> + }
>
> - cache_mngr->reset_cache(&cache_mngr->trx_cache);
> + next= max_found + 1;
> + sprintf(ext_buf, "%06lu", next);
> + *end++='.';
>
> - thd->clear_binlog_table_maps();
> + /*
> + Check if the generated extension size + the file name exceeds the
> + buffer size used. If one did not check this, then the filename might be
> + truncated, resulting in error.
> + */
> + if (((strlen(ext_buf) + (end - name)) >= FN_REFLEN))
> + {
> + sql_print_error("Log filename too large: %s%s (%zu). \
> +Please fix this by archiving old logs and updating the \
> +index files.", name, ext_buf, (strlen(ext_buf) + (end - name)));
> + error= 1;
> + goto end;
> }
> - /*
> - If rolling back a statement in a transaction, we truncate the
> - transaction cache to remove the statement.
> - */
> - else
> - cache_mngr->trx_cache.restore_prev_position();
>
> - /*
> - We need to step the table map version on a rollback to ensure that a new
> - table map event is generated instead of the one that was written to the
> - thrown-away transaction cache.
> - */
> - mysql_bin_log.update_table_map_version();
> + sprintf(end, "%06lu", next);
> +
> + /* print warning if reaching the end of available extensions. */
> + if ((next > (MAX_LOG_UNIQUE_FN_EXT - LOG_WARN_UNIQUE_FN_EXT_LEFT)))
> + sql_print_warning("Next log extension: %lu. \
> +Remaining log filename extensions: %lu. \
> +Please consider archiving some logs.", next, (MAX_LOG_UNIQUE_FN_EXT - next));
>
> - DBUG_ASSERT(thd->binlog_get_pending_rows_event(is_transactional) == NULL);
> +end:
> DBUG_RETURN(error);
> }
>
> -static int binlog_prepare(handlerton *hton, THD *thd, bool all)
> +
> +void MYSQL_LOG::init(enum_log_type log_type_arg,
> + enum cache_type io_cache_type_arg)
> {
> - /*
> - do nothing.
> - just pretend we can do 2pc, so that MySQL won't
> - switch to 1pc.
> - real work will be done in MYSQL_BIN_LOG::log_xid()
> - */
> - return 0;
> + DBUG_ENTER("MYSQL_LOG::init");
> + log_type= log_type_arg;
> + io_cache_type= io_cache_type_arg;
> + DBUG_PRINT("info",("log_type: %d", log_type));
> + DBUG_VOID_RETURN;
> }
>
> -/**
> - This function flushes the non-transactional to the binary log upon
> - committing or rolling back a statement.
> -
> - @param thd The thread whose transaction should be flushed
> - @param cache_mngr Pointer to the cache data to be flushed
>
> - @return
> - nonzero if an error pops up when flushing the non-transactional cache.
> -*/
> -static int
> -binlog_flush_stmt_cache(THD *thd, binlog_cache_mngr *cache_mngr)
> +bool MYSQL_LOG::init_and_set_log_file_name(const char *log_name,
> + const char *new_name,
> + enum_log_type log_type_arg,
> + enum cache_type io_cache_type_arg)
> {
> - int error= 0;
> - DBUG_ENTER("binlog_flush_stmt_cache");
> - /*
> - If we are flushing the statement cache, it means that the changes get
> - through otherwise the cache is empty and this routine should not be called.
> - */
> - DBUG_ASSERT(cache_mngr->stmt_cache.has_incident() == FALSE);
> - /*
> - This function handles non-transactional changes and as such this flag equals
> - to false.
> - */
> - bool const is_transactional= FALSE;
> - IO_CACHE *cache_log= &cache_mngr->stmt_cache.cache_log;
> -
> - if (thd->binlog_flush_pending_rows_event(TRUE, is_transactional))
> - DBUG_RETURN(1);
> + init(log_type_arg, io_cache_type_arg);
>
> - Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE, TRUE, 0);
> - if ((error= mysql_bin_log.write(thd, cache_log, &qev,
> - cache_mngr->stmt_cache.has_incident())))
> - DBUG_RETURN(error);
> - cache_mngr->reset_cache(&cache_mngr->stmt_cache);
> + if (new_name && !strmov(log_file_name, new_name))
> + return TRUE;
> + else if (!new_name && generate_new_name(log_file_name, log_name))
> + return TRUE;
>
> - /*
> - We need to step the table map version after writing the
> - transaction cache to disk.
> - */
> - mysql_bin_log.update_table_map_version();
> - statistic_increment(binlog_cache_use, &LOCK_status);
> - if (cache_log->disk_writes != 0)
> - {
> - statistic_increment(binlog_cache_disk_use, &LOCK_status);
> - cache_log->disk_writes= 0;
> - }
> - DBUG_RETURN(error);
> + return FALSE;
> }
>
> -/**
> - This function is called once after each statement.
>
> - It has the responsibility to flush the caches to the binary log on commits.
> +/*
> + Open a (new) log file.
> +
> + SYNOPSIS
> + open()
> +
> + log_name The name of the log to open
> + log_type_arg The type of the log. E.g. LOG_NORMAL
> + new_name The new name for the logfile. This is only needed
> + when the method is used to open the binlog file.
> + io_cache_type_arg The type of the IO_CACHE to use for this log file
>
> - @param hton The binlog handlerton.
> - @param thd The client thread that executes the transaction.
> - @param all This is @c true if this is a real transaction commit, and
> - @false otherwise.
> + DESCRIPTION
> + Open the logfile, init IO_CACHE and write startup messages
> + (in case of general and slow query logs).
>
> - @see handlerton::commit
> + RETURN VALUES
> + 0 ok
> + 1 error
> */
> -static int binlog_commit(handlerton *hton, THD *thd, bool all)
> +
> +bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
> + const char *new_name, enum cache_type io_cache_type_arg)
> {
> - int error= 0;
> - DBUG_ENTER("binlog_commit");
> - binlog_cache_mngr *const cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> + char buff[FN_REFLEN];
> + File file= -1;
> + int open_flags= O_CREAT | O_BINARY;
> + DBUG_ENTER("MYSQL_LOG::open");
> + DBUG_PRINT("enter", ("log_type: %d", (int) log_type_arg));
>
> - DBUG_PRINT("debug",
> - ("all: %d, in_transaction: %s, all.modified_non_trans_table: %s,
> stmt.modified_non_trans_table: %s",
> - all,
> - YESNO(thd->in_multi_stmt_transaction()),
> - YESNO(thd->transaction.all.modified_non_trans_table),
> - YESNO(thd->transaction.stmt.modified_non_trans_table)));
> + write_error= 0;
>
> - if (!cache_mngr->stmt_cache.empty())
> + if (!(name= my_strdup(log_name, MYF(MY_WME))))
> {
> - binlog_flush_stmt_cache(thd, cache_mngr);
> + name= (char *)log_name; // for the error message
> + goto err;
> }
>
> - if (cache_mngr->trx_cache.empty())
> - {
> - /*
> - we're here because cache_log was flushed in MYSQL_BIN_LOG::log_xid()
> - */
> - cache_mngr->reset_cache(&cache_mngr->trx_cache);
> - DBUG_RETURN(0);
> - }
> + if (init_and_set_log_file_name(name, new_name,
> + log_type_arg, io_cache_type_arg))
> + goto err;
>
> - /*
> - We commit the transaction if:
> - - We are not in a transaction and committing a statement, or
> - - We are in a transaction and a full transaction is committed.
> - Otherwise, we accumulate the changes.
> - */
> - if (ending_trans(thd, all))
> + if (io_cache_type == SEQ_READ_APPEND)
> + open_flags |= O_RDWR | O_APPEND;
> + else
> + open_flags |= O_WRONLY | (log_type == LOG_BIN ? 0 : O_APPEND);
> +
> + db[0]= 0;
> +
> + if ((file= mysql_file_open(key_file_MYSQL_LOG,
> + log_file_name, open_flags,
> + MYF(MY_WME | ME_WAITTANG))) < 0 ||
> + init_io_cache(&log_file, file, IO_SIZE, io_cache_type,
> + mysql_file_tell(file, MYF(MY_WME)), 0,
> + MYF(MY_WME | MY_NABP |
> + ((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0))))
> + goto err;
> +
> + if (log_type == LOG_NORMAL)
> {
> - Query_log_event qev(thd, STRING_WITH_LEN("COMMIT"), TRUE, FALSE, TRUE, 0);
> - error= binlog_flush_trx_cache(thd, cache_mngr, &qev);
> + char *end;
> + int len=my_snprintf(buff, sizeof(buff), "%s, Version: %s (%s). "
> +#ifdef EMBEDDED_LIBRARY
> + "embedded library\n",
> + my_progname, server_version, MYSQL_COMPILATION_COMMENT
> +#elif _WIN32
> + "started with:\nTCP Port: %d, Named Pipe: %s\n",
> + my_progname, server_version, MYSQL_COMPILATION_COMMENT,
> + mysqld_port, mysqld_unix_port
> +#else
> + "started with:\nTcp port: %d Unix socket: %s\n",
> + my_progname, server_version, MYSQL_COMPILATION_COMMENT,
> + mysqld_port, mysqld_unix_port
> +#endif
> + );
> + end= strnmov(buff + len, "Time Id Command Argument\n",
> + sizeof(buff) - len);
> + if (my_b_write(&log_file, (uchar*) buff, (uint) (end-buff)) ||
> + flush_io_cache(&log_file))
> + goto err;
> }
>
> + log_state= LOG_OPENED;
> + DBUG_RETURN(0);
> +
> +err:
> + 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);
> + safeFree(name);
> + log_state= LOG_CLOSED;
> + DBUG_RETURN(1);
> +}
> +
> +MYSQL_LOG::MYSQL_LOG()
> + : name(0), write_error(FALSE), inited(FALSE), log_type(LOG_UNKNOWN),
> + log_state(LOG_CLOSED)
> +{
> /*
> - This is part of the stmt rollback.
> + We don't want to initialize LOCK_Log 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 (!all)
> - cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
> - DBUG_RETURN(error);
> + bzero((char*) &log_file, sizeof(log_file));
> }
>
> -/**
> - This function is called when a transaction or a statement is rolled back.
> +void MYSQL_LOG::init_pthread_objects()
> +{
> + DBUG_ASSERT(inited == 0);
> + inited= 1;
> + mysql_mutex_init(key_LOG_LOCK_log, &LOCK_log, MY_MUTEX_INIT_SLOW);
> +}
> +
> +/*
> + Close the log file
>
> - @param hton The binlog handlerton.
> - @param thd The client thread that executes the transaction.
> - @param all This is @c true if this is a real transaction rollback, and
> - @false otherwise.
> + SYNOPSIS
> + close()
> + exiting Bitmask. For the slow and general logs the only used bit is
> + LOG_CLOSE_TO_BE_OPENED. This is used if we intend to call
> + open at once after close.
>
> - @see handlerton::rollback
> + NOTES
> + One can do an open on the object at once after doing a close.
> + The internal structures are not freed until cleanup() is called
> */
> -static int binlog_rollback(handlerton *hton, THD *thd, bool all)
> -{
> - DBUG_ENTER("binlog_rollback");
> - int error=0;
> - binlog_cache_mngr *const cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> -
> - DBUG_PRINT("debug", ("all: %s, all.modified_non_trans_table: %s,
> stmt.modified_non_trans_table: %s",
> - YESNO(all),
> - YESNO(thd->transaction.all.modified_non_trans_table),
> - YESNO(thd->transaction.stmt.modified_non_trans_table)));
>
> - /*
> - If an incident event is set we do not flush the content of the statement
> - cache because it may be corrupted.
> - */
> - if (cache_mngr->stmt_cache.has_incident())
> - {
> - error= mysql_bin_log.write_incident(thd, TRUE);
> - cache_mngr->reset_cache(&cache_mngr->stmt_cache);
> - }
> - else if (!cache_mngr->stmt_cache.empty())
> +void MYSQL_LOG::close(uint exiting)
> +{ // One can't set log_type here!
> + DBUG_ENTER("MYSQL_LOG::close");
> + DBUG_PRINT("enter",("exiting: %d", (int) exiting));
> + if (log_state == LOG_OPENED)
> {
> - binlog_flush_stmt_cache(thd, cache_mngr);
> - }
> + end_io_cache(&log_file);
>
> - if (cache_mngr->trx_cache.empty())
> - {
> - /*
> - we're here because cache_log was flushed in MYSQL_BIN_LOG::log_xid()
> - */
> - cache_mngr->reset_cache(&cache_mngr->trx_cache);
> - DBUG_RETURN(0);
> - }
> + if (mysql_file_sync(log_file.file, MYF(MY_WME)) && ! write_error)
> + {
> + write_error= 1;
> + sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
> + }
>
> - if (mysql_bin_log.check_write_error(thd))
> - {
> - /*
> - "all == true" means that a "rollback statement" triggered the error and
> - this function was called. However, this must not happen as a rollback
> - is written directly to the binary log. And in auto-commit mode, a single
> - statement that is rolled back has the flag all == false.
> - */
> - DBUG_ASSERT(!all);
> - /*
> - We reach this point if the effect of a statement did not properly get into
> - a cache and need to be rolled back.
> - */
> - error= binlog_truncate_trx_cache(thd, cache_mngr, all);
> - }
> - else
> - {
> - /*
> - We flush the cache wrapped in a beging/rollback if:
> - . aborting a single or multi-statement transaction and;
> - . the format is STMT and non-trans engines were updated or;
> - . the OPTION_KEEP_LOG is activate.
> - */
> - if (ending_trans(thd, all) &&
> - ((thd->variables.option_bits & OPTION_KEEP_LOG) ||
> - (trans_has_updated_non_trans_table(thd) &&
> - thd->variables.binlog_format == BINLOG_FORMAT_STMT)))
> + if (mysql_file_close(log_file.file, MYF(MY_WME)) && ! write_error)
> {
> - Query_log_event qev(thd, STRING_WITH_LEN("ROLLBACK"), TRUE, FALSE, TRUE, 0);
> - error= binlog_flush_trx_cache(thd, cache_mngr, &qev);
> + write_error= 1;
> + sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
> }
> - /*
> - Truncate the cache if:
> - . aborting a single or multi-statement transaction or;
> - . the OPTION_KEEP_LOG is not activate and;
> - . the format is not STMT or no non-trans were updated.
> - */
> - else if (ending_trans(thd, all) ||
> - (!(thd->variables.option_bits & OPTION_KEEP_LOG) &&
> - ((!stmt_has_updated_non_trans_table(thd) ||
> - thd->variables.binlog_format != BINLOG_FORMAT_STMT))))
> - error= binlog_truncate_trx_cache(thd, cache_mngr, all);
> }
>
> - /*
> - This is part of the stmt rollback.
> - */
> - if (!all)
> - cache_mngr->trx_cache.set_prev_position(MY_OFF_T_UNDEF);
> - DBUG_RETURN(error);
> + log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED :
> LOG_CLOSED;
> + safeFree(name);
> + DBUG_VOID_RETURN;
> }
>
> -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);
> +/** This is called only once. */
>
> +void MYSQL_LOG::cleanup()
> +{
> + DBUG_ENTER("cleanup");
> + if (inited)
> + {
> + inited= 0;
> + mysql_mutex_destroy(&LOCK_log);
> + close(0);
> + }
> DBUG_VOID_RETURN;
> }
>
> -bool MYSQL_BIN_LOG::check_write_error(THD *thd)
> -{
> - DBUG_ENTER("MYSQL_BIN_LOG::check_write_error");
> -
> - bool checked= FALSE;
>
> - if (!thd->is_error())
> - DBUG_RETURN(checked);
> -
> - switch (thd->stmt_da->sql_errno())
> +int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name)
> +{
> + fn_format(new_name, log_name, mysql_data_home, "", 4);
> + if (log_type == LOG_BIN)
> {
> - case ER_TRANS_CACHE_FULL:
> - case ER_ERROR_ON_WRITE:
> - case ER_BINLOG_LOGGING_IMPOSSIBLE:
> - checked= TRUE;
> - break;
> + if (!fn_ext(log_name)[0])
> + {
> + if (find_uniq_filename(new_name))
> + {
> + /*
> + This should be treated as error once propagation of error further
> + up in the stack gets proper handling.
> + */
> + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> + ER_NO_UNIQUE_LOGFILE, ER(ER_NO_UNIQUE_LOGFILE),
> + log_name);
> + sql_print_error(ER(ER_NO_UNIQUE_LOGFILE), log_name);
> + return 1;
> + }
> + }
> }
> -
> - DBUG_RETURN(checked);
> + return 0;
> }
>
> -/**
> - @note
> - How do we handle this (unlikely but legal) case:
> - @verbatim
> - [transaction] + [update to non-trans table] + [rollback to savepoint] ?
> - @endverbatim
> - The problem occurs when a savepoint is before the update to the
> - non-transactional table. Then when there's a rollback to the savepoint, if we
> - simply truncate the binlog cache, we lose the part of the binlog cache where
> - the update is. If we want to not lose it, we need to write the SAVEPOINT
> - command and the ROLLBACK TO SAVEPOINT command to the binlog cache. The latter
> - is easy: it's just write at the end of the binlog cache, but the former
> - should be *inserted* to the place where the user called SAVEPOINT. The
> - solution is that when the user calls SAVEPOINT, we write it to the binlog
> - cache (so no need to later insert it). As transactions are never intermixed
> - in the binary log (i.e. they are serialized), we won't have conflicts with
> - savepoint names when using mysqlbinlog or in the slave SQL thread.
> - Then when ROLLBACK TO SAVEPOINT is called, if we updated some
> - non-transactional table, we don't truncate the binlog cache but instead write
> - ROLLBACK TO SAVEPOINT to it; otherwise we truncate the binlog cache (which
> - will chop the SAVEPOINT command from the binlog cache, which is good as in
> - that case there is no need to have it in the binlog).
> +
> +/*
> + Reopen the log file
> +
> + SYNOPSIS
> + reopen_file()
> +
> + DESCRIPTION
> + Reopen the log file. The method is used during FLUSH LOGS
> + and locks LOCK_log mutex
> */
>
> -static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv)
> +
> +void MYSQL_QUERY_LOG::reopen_file()
> {
> - DBUG_ENTER("binlog_savepoint_set");
> + char *save_name;
>
> - binlog_trans_log_savepos(thd, (my_off_t*) sv);
> - /* Write it to the binary log */
> + DBUG_ENTER("MYSQL_LOG::reopen_file");
> + if (!is_open())
> + {
> + DBUG_PRINT("info",("log is closed"));
> + DBUG_VOID_RETURN;
> + }
>
> - int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
> - int const error=
> - thd->binlog_query(THD::STMT_QUERY_TYPE,
> - thd->query(), thd->query_length(), TRUE, FALSE, FALSE,
> - errcode);
> - DBUG_RETURN(error);
> -}
> + mysql_mutex_lock(&LOCK_log);
>
> -static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
> -{
> - DBUG_ENTER("binlog_savepoint_rollback");
> + save_name= name;
> + name= 0; // Don't free name
> + close(LOG_CLOSE_TO_BE_OPENED);
>
> /*
> - Write ROLLBACK TO SAVEPOINT to the binlog cache if we have updated some
> - non-transactional table. Otherwise, truncate the binlog cache starting
> - from the SAVEPOINT command.
> + Note that at this point, log_state != LOG_CLOSED (important for is_open()).
> */
> - if (unlikely(trans_has_updated_non_trans_table(thd) ||
> - (thd->variables.option_bits & OPTION_KEEP_LOG)))
> - {
> - int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
> - int error=
> - thd->binlog_query(THD::STMT_QUERY_TYPE,
> - thd->query(), thd->query_length(), TRUE, FALSE,
> FALSE,
> - errcode);
> - DBUG_RETURN(error);
> - }
> - binlog_trans_log_truncate(thd, *(my_off_t*)sv);
> - DBUG_RETURN(0);
> -}
>
> + open(save_name, log_type, 0, io_cache_type);
> + my_free(save_name, MYF(0));
>
> -int check_binlog_magic(IO_CACHE* log, const char** errmsg)
> -{
> - char magic[4];
> - DBUG_ASSERT(my_b_tell(log) == 0);
> + mysql_mutex_unlock(&LOCK_log);
>
> - if (my_b_read(log, (uchar*) magic, sizeof(magic)))
> - {
> - *errmsg = "I/O error reading the header from the binary log";
> - sql_print_error("%s, errno=%d, io cache code=%d", *errmsg, my_errno,
> - log->error);
> - return 1;
> - }
> - if (memcmp(magic, BINLOG_MAGIC, sizeof(magic)))
> - {
> - *errmsg = "Binlog has bad magic number; It's not a binary log file that can be
> used by this version of MySQL";
> - return 1;
> - }
> - return 0;
> -}
> -
> -
> -File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg)
> -{
> - File file;
> - DBUG_ENTER("open_binlog");
> -
> - if ((file= mysql_file_open(key_file_binlog,
> - log_file_name, O_RDONLY | O_BINARY | O_SHARE,
> - MYF(MY_WME))) < 0)
> - {
> - sql_print_error("Failed to open log (file '%s', errno %d)",
> - log_file_name, my_errno);
> - *errmsg = "Could not open log file";
> - goto err;
> - }
> - if (init_io_cache(log, file, IO_SIZE*2, READ_CACHE, 0, 0,
> - MYF(MY_WME|MY_DONT_CHECK_FILESIZE)))
> - {
> - sql_print_error("Failed to create a cache on log (file '%s')",
> - log_file_name);
> - *errmsg = "Could not open log file";
> - goto err;
> - }
> - if (check_binlog_magic(log,errmsg))
> - goto err;
> - DBUG_RETURN(file);
> -
> -err:
> - if (file >= 0)
> - {
> - mysql_file_close(file, MYF(0));
> - end_io_cache(log);
> - }
> - DBUG_RETURN(-1);
> -}
> -
> -#ifdef _WIN32
> -static int eventSource = 0;
> -
> -static void setup_windows_event_source()
> -{
> - HKEY hRegKey= NULL;
> - DWORD dwError= 0;
> - TCHAR szPath[MAX_PATH];
> - DWORD dwTypes;
> -
> - if (eventSource) // Ensure that we are only called once
> - return;
> - eventSource= 1;
> -
> - // Create the event source registry key
> - dwError= RegCreateKey(HKEY_LOCAL_MACHINE,
> -
> "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\MySQL",
> - &hRegKey);
> -
> - /* Name of the PE module that contains the message resource */
> - GetModuleFileName(NULL, szPath, MAX_PATH);
> -
> - /* Register EventMessageFile */
> - dwError = RegSetValueEx(hRegKey, "EventMessageFile", 0, REG_EXPAND_SZ,
> - (PBYTE) szPath, (DWORD) (strlen(szPath) + 1));
> -
> - /* Register supported event types */
> - dwTypes= (EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
> - EVENTLOG_INFORMATION_TYPE);
> - dwError= RegSetValueEx(hRegKey, "TypesSupported", 0, REG_DWORD,
> - (LPBYTE) &dwTypes, sizeof dwTypes);
> -
> - RegCloseKey(hRegKey);
> -}
> -
> -#endif /* _WIN32 */
> -
> -
> -/**
> - Find a unique filename for 'filename.#'.
> -
> - Set '#' to the number next to the maximum found in the most
> - recent log file extension.
> -
> - This function will return nonzero if: (i) the generated name
> - exceeds FN_REFLEN; (ii) if the number of extensions is exhausted;
> - or (iii) some other error happened while examining the filesystem.
> -
> - @return
> - nonzero if not possible to get unique filename.
> -*/
> -
> -static int find_uniq_filename(char *name)
> -{
> - uint i;
> - char buff[FN_REFLEN], ext_buf[FN_REFLEN];
> - struct st_my_dir *dir_info;
> - reg1 struct fileinfo *file_info;
> - ulong max_found= 0, next= 0, number= 0;
> - size_t buf_length, length;
> - char *start, *end;
> - int error= 0;
> - DBUG_ENTER("find_uniq_filename");
> -
> - length= dirname_part(buff, name, &buf_length);
> - start= name + length;
> - end= strend(start);
> -
> - *end='.';
> - length= (size_t) (end - start + 1);
> -
> - if (!(dir_info= my_dir(buff,MYF(MY_DONT_SORT))))
> - { // This shouldn't happen
> - strmov(end,".1"); // use name+1
> - DBUG_RETURN(1);
> - }
> - file_info= dir_info->dir_entry;
> - for (i= dir_info->number_off_files ; i-- ; file_info++)
> - {
> - if (bcmp((uchar*) file_info->name, (uchar*) start, length) == 0 &&
> - test_if_number(file_info->name+length, &number,0))
> - {
> - set_if_bigger(max_found,(ulong) number);
> - }
> - }
> - my_dirend(dir_info);
> -
> - /* check if reached the maximum possible extension number */
> - if ((max_found == MAX_LOG_UNIQUE_FN_EXT))
> - {
> - sql_print_error("Log filename extension number exhausted: %06lu. \
> -Please fix this by archiving old logs and \
> -updating the index files.", max_found);
> - error= 1;
> - goto end;
> - }
> -
> - next= max_found + 1;
> - sprintf(ext_buf, "%06lu", next);
> - *end++='.';
> -
> - /*
> - Check if the generated extension size + the file name exceeds the
> - buffer size used. If one did not check this, then the filename might be
> - truncated, resulting in error.
> - */
> - if (((strlen(ext_buf) + (end - name)) >= FN_REFLEN))
> - {
> - sql_print_error("Log filename too large: %s%s (%zu). \
> -Please fix this by archiving old logs and updating the \
> -index files.", name, ext_buf, (strlen(ext_buf) + (end - name)));
> - error= 1;
> - goto end;
> - }
> -
> - sprintf(end, "%06lu", next);
> -
> - /* print warning if reaching the end of available extensions. */
> - if ((next > (MAX_LOG_UNIQUE_FN_EXT - LOG_WARN_UNIQUE_FN_EXT_LEFT)))
> - sql_print_warning("Next log extension: %lu. \
> -Remaining log filename extensions: %lu. \
> -Please consider archiving some logs.", next, (MAX_LOG_UNIQUE_FN_EXT - next));
> -
> -end:
> - DBUG_RETURN(error);
> -}
> -
> -
> -void MYSQL_LOG::init(enum_log_type log_type_arg,
> - enum cache_type io_cache_type_arg)
> -{
> - DBUG_ENTER("MYSQL_LOG::init");
> - log_type= log_type_arg;
> - io_cache_type= io_cache_type_arg;
> - DBUG_PRINT("info",("log_type: %d", log_type));
> DBUG_VOID_RETURN;
> }
>
>
> -bool MYSQL_LOG::init_and_set_log_file_name(const char *log_name,
> - const char *new_name,
> - enum_log_type log_type_arg,
> - enum cache_type io_cache_type_arg)
> -{
> - init(log_type_arg, io_cache_type_arg);
> -
> - if (new_name && !strmov(log_file_name, new_name))
> - return TRUE;
> - else if (!new_name && generate_new_name(log_file_name, log_name))
> - return TRUE;
> -
> - return FALSE;
> -}
> -
> -
> /*
> - Open a (new) log file.
> + Write a command to traditional general log file
>
> SYNOPSIS
> - open()
> + write()
>
> - log_name The name of the log to open
> - log_type_arg The type of the log. E.g. LOG_NORMAL
> - new_name The new name for the logfile. This is only needed
> - when the method is used to open the binlog file.
> - io_cache_type_arg The type of the IO_CACHE to use for this log file
> + event_time command start timestamp
> + user_host the pointer to the string with user@host info
> + user_host_len length of the user_host string. this is computed once
> + and passed to all general log event handlers
> + thread_id Id of the thread, issued a query
> + command_type the type of the command being logged
> + command_type_len the length of the string above
> + sql_text the very text of the query being executed
> + sql_text_len the length of sql_text string
>
> DESCRIPTION
> - Open the logfile, init IO_CACHE and write startup messages
> - (in case of general and slow query logs).
>
> - RETURN VALUES
> - 0 ok
> - 1 error
> + Log given command to to normal (not rotable) log file
> +
> + RETURN
> + FASE - OK
> + TRUE - error occured
> */
>
> -bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
> - const char *new_name, enum cache_type io_cache_type_arg)
> -{
> - char buff[FN_REFLEN];
> - File file= -1;
> - int open_flags= O_CREAT | O_BINARY;
> - DBUG_ENTER("MYSQL_LOG::open");
> - DBUG_PRINT("enter", ("log_type: %d", (int) log_type_arg));
> -
> - write_error= 0;
> -
> - if (!(name= my_strdup(log_name, MYF(MY_WME))))
> - {
> - name= (char *)log_name; // for the error message
> - goto err;
> - }
> -
> - if (init_and_set_log_file_name(name, new_name,
> - log_type_arg, io_cache_type_arg))
> - goto err;
> -
> - if (io_cache_type == SEQ_READ_APPEND)
> - open_flags |= O_RDWR | O_APPEND;
> - else
> - open_flags |= O_WRONLY | (log_type == LOG_BIN ? 0 : O_APPEND);
> -
> - db[0]= 0;
> -
> - if ((file= mysql_file_open(key_file_MYSQL_LOG,
> - log_file_name, open_flags,
> - MYF(MY_WME | ME_WAITTANG))) < 0 ||
> - init_io_cache(&log_file, file, IO_SIZE, io_cache_type,
> - mysql_file_tell(file, MYF(MY_WME)), 0,
> - MYF(MY_WME | MY_NABP |
> - ((log_type == LOG_BIN) ? MY_WAIT_IF_FULL : 0))))
> - goto err;
> -
> - if (log_type == LOG_NORMAL)
> - {
> - char *end;
> - int len=my_snprintf(buff, sizeof(buff), "%s, Version: %s (%s). "
> -#ifdef EMBEDDED_LIBRARY
> - "embedded library\n",
> - my_progname, server_version, MYSQL_COMPILATION_COMMENT
> -#elif _WIN32
> - "started with:\nTCP Port: %d, Named Pipe: %s\n",
> - my_progname, server_version, MYSQL_COMPILATION_COMMENT,
> - mysqld_port, mysqld_unix_port
> -#else
> - "started with:\nTcp port: %d Unix socket: %s\n",
> - my_progname, server_version, MYSQL_COMPILATION_COMMENT,
> - mysqld_port, mysqld_unix_port
> -#endif
> - );
> - end= strnmov(buff + len, "Time Id Command Argument\n",
> - sizeof(buff) - len);
> - if (my_b_write(&log_file, (uchar*) buff, (uint) (end-buff)) ||
> - flush_io_cache(&log_file))
> - goto err;
> - }
> -
> - log_state= LOG_OPENED;
> - DBUG_RETURN(0);
> -
> -err:
> - 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);
> - safeFree(name);
> - log_state= LOG_CLOSED;
> - DBUG_RETURN(1);
> -}
> -
> -MYSQL_LOG::MYSQL_LOG()
> - : name(0), write_error(FALSE), inited(FALSE), log_type(LOG_UNKNOWN),
> - log_state(LOG_CLOSED)
> -{
> - /*
> - We don't want to initialize LOCK_Log 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().
> - */
> - bzero((char*) &log_file, sizeof(log_file));
> -}
> -
> -void MYSQL_LOG::init_pthread_objects()
> -{
> - DBUG_ASSERT(inited == 0);
> - inited= 1;
> - mysql_mutex_init(key_LOG_LOCK_log, &LOCK_log, MY_MUTEX_INIT_SLOW);
> -}
> -
> -/*
> - Close the log file
> -
> - SYNOPSIS
> - close()
> - exiting Bitmask. For the slow and general logs the only used bit is
> - LOG_CLOSE_TO_BE_OPENED. This is used if we intend to call
> - open at once after close.
> -
> - NOTES
> - One can do an open on the object at once after doing a close.
> - The internal structures are not freed until cleanup() is called
> -*/
> -
> -void MYSQL_LOG::close(uint exiting)
> -{ // One can't set log_type here!
> - DBUG_ENTER("MYSQL_LOG::close");
> - DBUG_PRINT("enter",("exiting: %d", (int) exiting));
> - if (log_state == LOG_OPENED)
> - {
> - end_io_cache(&log_file);
> -
> - if (mysql_file_sync(log_file.file, MYF(MY_WME)) && ! write_error)
> - {
> - write_error= 1;
> - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
> - }
> -
> - if (mysql_file_close(log_file.file, MYF(MY_WME)) && ! write_error)
> - {
> - write_error= 1;
> - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
> - }
> - }
> -
> - log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED :
> LOG_CLOSED;
> - safeFree(name);
> - DBUG_VOID_RETURN;
> -}
> -
> -/** This is called only once. */
> -
> -void MYSQL_LOG::cleanup()
> -{
> - DBUG_ENTER("cleanup");
> - if (inited)
> - {
> - inited= 0;
> - mysql_mutex_destroy(&LOCK_log);
> - close(0);
> - }
> - DBUG_VOID_RETURN;
> -}
> -
> -
> -int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name)
> -{
> - fn_format(new_name, log_name, mysql_data_home, "", 4);
> - if (log_type == LOG_BIN)
> - {
> - if (!fn_ext(log_name)[0])
> - {
> - if (find_uniq_filename(new_name))
> - {
> - /*
> - This should be treated as error once propagation of error further
> - up in the stack gets proper handling.
> - */
> - push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
> - ER_NO_UNIQUE_LOGFILE, ER(ER_NO_UNIQUE_LOGFILE),
> - log_name);
> - sql_print_error(ER(ER_NO_UNIQUE_LOGFILE), log_name);
> - return 1;
> - }
> - }
> - }
> - return 0;
> -}
> -
> -
> -/*
> - Reopen the log file
> -
> - SYNOPSIS
> - reopen_file()
> -
> - DESCRIPTION
> - Reopen the log file. The method is used during FLUSH LOGS
> - and locks LOCK_log mutex
> -*/
> -
> -
> -void MYSQL_QUERY_LOG::reopen_file()
> -{
> - char *save_name;
> -
> - DBUG_ENTER("MYSQL_LOG::reopen_file");
> - if (!is_open())
> - {
> - DBUG_PRINT("info",("log is closed"));
> - DBUG_VOID_RETURN;
> - }
> -
> - mysql_mutex_lock(&LOCK_log);
> -
> - save_name= name;
> - name= 0; // Don't free name
> - close(LOG_CLOSE_TO_BE_OPENED);
> -
> - /*
> - Note that at this point, log_state != LOG_CLOSED (important for is_open()).
> - */
> -
> - open(save_name, log_type, 0, io_cache_type);
> - my_free(save_name, MYF(0));
> -
> - mysql_mutex_unlock(&LOCK_log);
> -
> - DBUG_VOID_RETURN;
> -}
> -
> -
> -/*
> - Write a command to traditional general log file
> -
> - SYNOPSIS
> - write()
> -
> - event_time command start timestamp
> - user_host the pointer to the string with user@host info
> - user_host_len length of the user_host string. this is computed once
> - and passed to all general log event handlers
> - thread_id Id of the thread, issued a query
> - command_type the type of the command being logged
> - command_type_len the length of the string above
> - sql_text the very text of the query being executed
> - sql_text_len the length of sql_text string
> -
> - DESCRIPTION
> -
> - Log given command to to normal (not rotable) log file
> -
> - RETURN
> - FASE - OK
> - TRUE - error occured
> -*/
> -
> -bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host,
> - uint user_host_len, int thread_id,
> - const char *command_type, uint command_type_len,
> - const char *sql_text, uint sql_text_len)
> +bool MYSQL_QUERY_LOG::write(time_t event_time, const char *user_host,
> + uint user_host_len, int thread_id,
> + const char *command_type, uint command_type_len,
> + const char *sql_text, uint sql_text_len)
> {
> char buff[32];
> uint length= 0;
> char local_time_buff[MAX_TIME_SIZE];
> struct tm start;
> - uint time_buff_len= 0;
> -
> - mysql_mutex_lock(&LOCK_log);
> -
> - /* Test if someone closed between the is_open test and lock */
> - if (is_open())
> - {
> - /* for testing output of timestamp and thread id */
> - DBUG_EXECUTE_IF("reset_log_last_time", last_time= 0;);
> -
> - /* Note that my_b_write() assumes it knows the length for this */
> - if (event_time != last_time)
> - {
> - last_time= event_time;
> -
> - localtime_r(&event_time, &start);
> -
> - time_buff_len= my_snprintf(local_time_buff, MAX_TIME_SIZE,
> - "%02d%02d%02d %2d:%02d:%02d\t",
> - start.tm_year % 100, start.tm_mon + 1,
> - start.tm_mday, start.tm_hour,
> - start.tm_min, start.tm_sec);
> -
> - if (my_b_write(&log_file, (uchar*) local_time_buff, time_buff_len))
> - goto err;
> - }
> - else
> - if (my_b_write(&log_file, (uchar*) "\t\t" ,2) < 0)
> - goto err;
> -
> - /* command_type, thread_id */
> - length= my_snprintf(buff, 32, "%5ld ", (long) thread_id);
> -
> - if (my_b_write(&log_file, (uchar*) buff, length))
> - goto err;
> -
> - if (my_b_write(&log_file, (uchar*) command_type, command_type_len))
> - goto err;
> -
> - if (my_b_write(&log_file, (uchar*) "\t", 1))
> - goto err;
> -
> - /* sql_text */
> - if (my_b_write(&log_file, (uchar*) sql_text, sql_text_len))
> - goto err;
> -
> - if (my_b_write(&log_file, (uchar*) "\n", 1) ||
> - flush_io_cache(&log_file))
> - goto err;
> - }
> -
> - mysql_mutex_unlock(&LOCK_log);
> - return FALSE;
> -err:
> -
> - if (!write_error)
> - {
> - write_error= 1;
> - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
> - }
> - mysql_mutex_unlock(&LOCK_log);
> - return TRUE;
> -}
> -
> -
> -/*
> - Log a query to the traditional slow log file
> -
> - SYNOPSIS
> - write()
> -
> - thd THD of the query
> - current_time current timestamp
> - query_start_arg command start timestamp
> - user_host the pointer to the string with user@host info
> - user_host_len length of the user_host string. this is computed once
> - and passed to all general log event handlers
> - query_utime Amount of time the query took to execute (in microseconds)
> - lock_utime Amount of time the query was locked (in microseconds)
> - is_command The flag, which determines, whether the sql_text is a
> - query or an administrator command.
> - sql_text the very text of the query or administrator command
> - processed
> - sql_text_len the length of sql_text string
> -
> - DESCRIPTION
> -
> - Log a query to the slow log file.
> -
> - RETURN
> - FALSE - OK
> - TRUE - error occured
> -*/
> -
> -bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
> - time_t query_start_arg, const char *user_host,
> - uint user_host_len, ulonglong query_utime,
> - ulonglong lock_utime, bool is_command,
> - const char *sql_text, uint sql_text_len)
> -{
> - bool error= 0;
> - DBUG_ENTER("MYSQL_QUERY_LOG::write");
> -
> - mysql_mutex_lock(&LOCK_log);
> -
> - if (!is_open())
> - {
> - mysql_mutex_unlock(&LOCK_log);
> - DBUG_RETURN(0);
> - }
> -
> - if (is_open())
> - { // Safety agains reopen
> - int tmp_errno= 0;
> - char buff[80], *end;
> - char query_time_buff[22+7], lock_time_buff[22+7];
> - uint buff_len;
> - end= buff;
> -
> - if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT))
> - {
> - if (current_time != last_time)
> - {
> - last_time= current_time;
> - struct tm start;
> - localtime_r(¤t_time, &start);
> -
> - buff_len= my_snprintf(buff, sizeof buff,
> - "# Time: %02d%02d%02d %2d:%02d:%02d\n",
> - start.tm_year % 100, start.tm_mon + 1,
> - start.tm_mday, start.tm_hour,
> - start.tm_min, start.tm_sec);
> -
> - /* Note that my_b_write() assumes it knows the length for this */
> - if (my_b_write(&log_file, (uchar*) buff, buff_len))
> - tmp_errno= errno;
> - }
> - const uchar uh[]= "# User@Host: ";
> - if (my_b_write(&log_file, uh, sizeof(uh) - 1))
> - tmp_errno= errno;
> - if (my_b_write(&log_file, (uchar*) user_host, user_host_len))
> - tmp_errno= errno;
> - if (my_b_write(&log_file, (uchar*) "\n", 1))
> - tmp_errno= errno;
> - }
> - /* For slow query log */
> - sprintf(query_time_buff, "%.6f", ulonglong2double(query_utime)/1000000.0);
> - sprintf(lock_time_buff, "%.6f", ulonglong2double(lock_utime)/1000000.0);
> - if (my_b_printf(&log_file,
> - "# Query_time: %s Lock_time: %s"
> - " Rows_sent: %lu Rows_examined: %lu\n",
> - query_time_buff, lock_time_buff,
> - (ulong) thd->sent_row_count,
> - (ulong) thd->examined_row_count) == (uint) -1)
> - tmp_errno= errno;
> - if (thd->db && strcmp(thd->db, db))
> - { // Database changed
> - if (my_b_printf(&log_file,"use %s;\n",thd->db) == (uint) -1)
> - tmp_errno= errno;
> - strmov(db,thd->db);
> - }
> - if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt)
> - {
> - end=strmov(end, ",last_insert_id=");
> - end=longlong10_to_str((longlong)
> -
> thd->first_successful_insert_id_in_prev_stmt_for_binlog,
> - end, -10);
> - }
> - // Save value if we do an insert.
> - if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
> - {
> - if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT))
> - {
> - end=strmov(end,",insert_id=");
> - end=longlong10_to_str((longlong)
> -
> thd->auto_inc_intervals_in_cur_stmt_for_binlog.minimum(),
> - end, -10);
> - }
> - }
> -
> - /*
> - This info used to show up randomly, depending on whether the query
> - checked the query start time or not. now we always write current
> - timestamp to the slow log
> - */
> - end= strmov(end, ",timestamp=");
> - end= int10_to_str((long) current_time, end, 10);
> -
> - if (end != buff)
> - {
> - *end++=';';
> - *end='\n';
> - if (my_b_write(&log_file, (uchar*) "SET ", 4) ||
> - my_b_write(&log_file, (uchar*) buff + 1, (uint) (end-buff)))
> - tmp_errno= errno;
> - }
> - if (is_command)
> - {
> - end= strxmov(buff, "# administrator command: ", NullS);
> - buff_len= (ulong) (end - buff);
> - my_b_write(&log_file, (uchar*) buff, buff_len);
> - }
> - if (my_b_write(&log_file, (uchar*) sql_text, sql_text_len) ||
> - my_b_write(&log_file, (uchar*) ";\n",2) ||
> - flush_io_cache(&log_file))
> - tmp_errno= errno;
> - if (tmp_errno)
> - {
> - error= 1;
> - if (! write_error)
> - {
> - write_error= 1;
> - sql_print_error(ER(ER_ERROR_ON_WRITE), name, error);
> - }
> - }
> - }
> - mysql_mutex_unlock(&LOCK_log);
> - DBUG_RETURN(error);
> -}
> -
> -
> -/**
> - @todo
> - The following should be using fn_format(); We just need to
> - first change fn_format() to cut the file name if it's too long.
> -*/
> -const char *MYSQL_LOG::generate_name(const char *log_name,
> - const char *suffix,
> - bool strip_ext, char *buff)
> -{
> - if (!log_name || !log_name[0])
> - {
> - strmake(buff, pidfile_name, FN_REFLEN - strlen(suffix) - 1);
> - return (const char *)
> - fn_format(buff, buff, "", suffix, MYF(MY_REPLACE_EXT|MY_REPLACE_DIR));
> - }
> - // get rid of extension if the log is binary to avoid problems
> - if (strip_ext)
> - {
> - char *p= fn_ext(log_name);
> - uint length= (uint) (p - log_name);
> - strmake(buff, log_name, min(length, FN_REFLEN-1));
> - return (const char*)buff;
> - }
> - return log_name;
> -}
> -
> -
> -
> -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 is called only once */
> -
> -void MYSQL_BIN_LOG::cleanup()
> -{
> - 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;
> -}
> -
> -
> -/* 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;
> -}
> -
> -
> -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);
> -}
> -
> -
> -bool MYSQL_BIN_LOG::open_index_file(const char *index_file_name_arg,
> - const char *log_name, bool need_mutex)
> -{
> - File index_file_nr= -1;
> - DBUG_ASSERT(!my_b_inited(&index_file));
> -
> - /*
> - First open of this class instance
> - Create an index file that will hold all file names uses for logging.
> - Add new entries to the end of it.
> - */
> - 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;
> - }
> -
> -#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.
> - */
> -
> - 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))
> - {
> - sql_print_error("MYSQL_BIN_LOG::open_index_file failed to sync the index "
> - "file.");
> - return TRUE;
> - }
> -#endif
> -
> - return FALSE;
> -}
> -
> -
> -/**
> - Open a (new) binlog 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.
> -
> - @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;
> -
> - DBUG_ENTER("MYSQL_BIN_LOG::open");
> - DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg));
> -
> - 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);
> - }
> -
> -#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
> -
> - write_error= 0;
> -
> - /* 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 */
> - }
> -
> - init(no_auto_events_arg, max_size_arg);
> -
> - open_count++;
> -
> - DBUG_ASSERT(log_type == LOG_BIN);
> -
> - {
> - bool write_file_name_to_index_file=0;
> -
> - if (!my_b_filelength(&log_file))
> - {
> - /*
> - The binary log file was empty (probably newly created)
> - This is the normal case and happens when the user doesn't specify
> - an extension for the binary log files.
> - In this case we write a standard header to it.
> - */
> - if (my_b_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 (need_start_event && !no_auto_events)
> - {
> - /*
> - 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)
> - {
> - /*
> - 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
> - }
> - }
> - log_state= LOG_OPENED;
> -
> -#ifdef HAVE_REPLICATION
> - close_purge_index_file();
> -#endif
> -
> - DBUG_RETURN(0);
> -
> -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);
> - 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);
> - 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;
> -}
> -
> -/**
> - 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
> -
> - @note
> - File will be truncated to be 'offset' shorter or filled up with newlines
> -
> - @retval
> - 0 ok
> -*/
> -
> -#ifdef HAVE_REPLICATION
> -
> -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");
> -
> - 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;
> -
> - /* Reset data in old index cache */
> - reinit_io_cache(index_file, READ_CACHE, (my_off_t) 0, 0, 1);
> - DBUG_RETURN(0);
> -
> -err:
> - DBUG_RETURN(1);
> -}
> -
> -#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)
> -{
> - int error= 0;
> - char *fname= linfo->log_file_name;
> - uint log_name_len= log_name ? (uint) strlen(log_name) : 0;
> - DBUG_ENTER("find_log_pos");
> - DBUG_PRINT("enter",("log_name: %s", log_name ? log_name : "NULL"));
> -
> - /*
> - Mutex needed because we need to make sure the file pointer does not
> - move from under our feet
> - */
> - if (need_lock)
> - mysql_mutex_lock(&LOCK_index);
> - mysql_mutex_assert_owner(&LOCK_index);
> -
> - /* As the file is flushed, we can't get an error here */
> - (void) reinit_io_cache(&index_file, READ_CACHE, (my_off_t) 0, 0, 0);
> -
> - for (;;)
> - {
> - uint length;
> - 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;
> - }
> - }
> -
> - if (need_lock)
> - mysql_mutex_unlock(&LOCK_index);
> - DBUG_RETURN(error);
> -}
> -
> -
> -/**
> - 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
> -
> - @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
> -*/
> -
> -int MYSQL_BIN_LOG::find_next_log(LOG_INFO* linfo, bool need_lock)
> -{
> - int error= 0;
> - uint length;
> - char *fname= linfo->log_file_name;
> -
> - if (need_lock)
> - mysql_mutex_lock(&LOCK_index);
> - mysql_mutex_assert_owner(&LOCK_index);
> -
> - /* As the file is flushed, we can't get an error here */
> - (void) reinit_io_cache(&index_file, READ_CACHE, linfo->index_file_offset,
> 0,
> - 0);
> -
> - linfo->index_file_start_offset= linfo->index_file_offset;
> - if ((length=my_b_gets(&index_file, fname, FN_REFLEN)) <= 1)
> - {
> - error = !index_file.error ? LOG_INFO_EOF : LOG_INFO_IO;
> - goto err;
> - }
> - fname[length-1]=0; // kill \n
> - linfo->index_file_offset = my_b_tell(&index_file);
> -
> -err:
> - if (need_lock)
> - mysql_mutex_unlock(&LOCK_index);
> - return error;
> -}
> -
> -
> -/**
> - 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
> -
> - @note
> - If not called from slave thread, write start event to new log
> -
> - @retval
> - 0 ok
> - @retval
> - 1 error
> -*/
> -
> -bool MYSQL_BIN_LOG::reset_logs(THD* thd)
> -{
> - 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);
> -
> - /*
> - 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));
> -
> - 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::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;
> -}
> -
> -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;
> -}
> -
> -void MYSQL_BIN_LOG::stop_union_events(THD *thd)
> -{
> - DBUG_ASSERT(thd->binlog_evt_union.do_union);
> - thd->binlog_evt_union.do_union= FALSE;
> -}
> -
> -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);
> -}
> -
> -/**
> - 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)
> -{
> - binlog_cache_mngr *const cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(thd, binlog_hton);
> -
> - return (cache_mngr ? !cache_mngr->trx_cache.empty() : 0);
> -}
> -
> -/**
> - This function checks if a transactional table was updated by the
> - current statement.
> -
> - @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())
> - {
> - if (ha_info->is_trx_read_write() && ha_info->ht() != binlog_hton)
> - return (TRUE);
> - }
> - return (FALSE);
> -}
> -
> -/**
> - 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.
> -
> - 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.
> -
> - @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)
> -{
> - 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.
> -
> - @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)
> -{
> - return (all || (!all && !thd->in_multi_stmt_transaction()));
> -}
> -
> -/**
> - This function checks if a non-transactional table was updated by
> - the current transaction.
> -
> - @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);
> -}
> -
> -/**
> - This function checks if a non-transactional table was updated by the
> - current statement.
> -
> - @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);
> -}
> -
> -/*
> - These functions are placed in this file since they need access to
> - binlog_hton, which has internal linkage.
> -*/
> -
> -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 (cache_mngr)
> - DBUG_RETURN(0); // Already set up
> -
> - 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);
> -
> - cache_mngr= new (thd_get_ha_data(this, binlog_hton)) binlog_cache_mngr;
> -
> - DBUG_RETURN(0);
> -}
> -
> -/*
> - Function to start a statement and optionally a transaction for the
> - binary log.
> -
> - SYNOPSIS
> - binlog_start_trans_and_stmt()
> -
> - DESCRIPTION
> -
> - 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.
> -
> - - 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()
> -{
> - 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)
> - {
> - 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();
> - }
> - DBUG_VOID_RETURN;
> -}
> -
> -void THD::binlog_set_stmt_begin() {
> - binlog_cache_mngr *cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
> -
> - /*
> - 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);
> -}
> -
> -
> -/**
> - 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));
> -
> - /* Pre-conditions */
> - DBUG_ASSERT(is_current_stmt_binlog_format_row() &&
> mysql_bin_log.is_open());
> - DBUG_ASSERT(table->s->table_map_id != ULONG_MAX);
> -
> - Table_map_log_event
> - the_event(this, table, table->s->table_map_id, is_transactional);
> -
> - if (binlog_table_maps == 0)
> - binlog_start_trans_and_stmt();
> -
> - binlog_cache_mngr *const cache_mngr=
> - (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton);
> -
> - IO_CACHE *file=
> - cache_mngr->get_binlog_cache_log(use_trans_cache(this, is_transactional));
> - if ((error= the_event.write(file)))
> - DBUG_RETURN(error);
> -
> - binlog_table_maps++;
> - table->s->table_map_version= mysql_bin_log.table_map_version();
> - DBUG_RETURN(0);
> -}
> -
> -/**
> - 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
> -{
> - 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);
> -}
> -
> -/**
> - 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)
> -{
> - 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);
> -}
> -
> -
> -/**
> - 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.
> -*/
> -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);
> -
> - 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);
> -}
> -
> -/*
> - 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));
> -
> - int error= 0;
> - 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));
> -
> - DBUG_PRINT("info", ("cache_mngr->pending(): 0x%lx", (long)
> cache_data->pending()));
> -
> - if (Rows_log_event* pending= cache_data->pending())
> - {
> - IO_CACHE *file= &cache_data->cache_log;
> -
> - /*
> - Write pending event to the cache.
> - */
> - if (pending->write(file))
> - {
> - 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);
> - }
> -
> - /*
> - We step the table map version if we are writing an event
> - representing the end of a statement.
> -
> - 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 (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 (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);
> -
> - /* 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;
> - }
> - }
> - }
> - }
> -
> - /*
> - Write the event.
> - */
> - if (event_info->write(file) ||
> - DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0))
> - goto err;
> -
> - error= 0;
> -
> -err:
> - if (event_info->use_direct_logging())
> - {
> - if (!error)
> - {
> - 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_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);
> - }
> -
> - if (error)
> - {
> - set_write_error(thd);
> - if (check_write_error(thd) && cache_data &&
> - stmt_has_updated_non_trans_table(thd))
> - cache_data->set_incident();
> - }
> - }
> -
> - if (event_info->flags & LOG_EVENT_UPDATE_TABLE_MAP_VERSION_F)
> - ++m_table_map_version;
> -
> - DBUG_RETURN(error);
> -}
> -
> -
> -int error_log_print(enum loglevel level, const char *format,
> - va_list args)
> -{
> - return logger.error_log_print(level, format, args);
> -}
> -
> -
> -bool slow_log_print(THD *thd, const char *query, uint query_length,
> - ulonglong current_utime)
> -{
> - return logger.slow_log_print(thd, query, query_length, current_utime);
> -}
> -
> -
> -bool LOGGER::log_command(THD *thd, enum enum_server_command command)
> -{
> -#ifndef NO_EMBEDDED_ACCESS_CHECKS
> - Security_context *sctx= thd->security_ctx;
> -#endif
> - /*
> - Log command if we have at least one log event handler enabled and want
> - to log this king of commands
> - */
> - if (*general_log_handler_list && (what_to_log & (1L << (uint)
> command)))
> + uint time_buff_len= 0;
> +
> + mysql_mutex_lock(&LOCK_log);
> +
> + /* Test if someone closed between the is_open test and lock */
> + if (is_open())
> {
> - if ((thd->variables.option_bits & OPTION_LOG_OFF)
> -#ifndef NO_EMBEDDED_ACCESS_CHECKS
> - && (sctx->master_access & SUPER_ACL)
> -#endif
> - )
> - {
> - /* No logging */
> - return FALSE;
> - }
> + /* for testing output of timestamp and thread id */
> + DBUG_EXECUTE_IF("reset_log_last_time", last_time= 0;);
>
> - return TRUE;
> - }
> + /* Note that my_b_write() assumes it knows the length for this */
> + if (event_time != last_time)
> + {
> + last_time= event_time;
>
> - return FALSE;
> -}
> + localtime_r(&event_time, &start);
>
> + time_buff_len= my_snprintf(local_time_buff, MAX_TIME_SIZE,
> + "%02d%02d%02d %2d:%02d:%02d\t",
> + start.tm_year % 100, start.tm_mon + 1,
> + start.tm_mday, start.tm_hour,
> + start.tm_min, start.tm_sec);
>
> -bool general_log_print(THD *thd, enum enum_server_command command,
> - const char *format, ...)
> -{
> - va_list args;
> - uint error= 0;
> + if (my_b_write(&log_file, (uchar*) local_time_buff, time_buff_len))
> + goto err;
> + }
> + else
> + if (my_b_write(&log_file, (uchar*) "\t\t" ,2) < 0)
> + goto err;
>
> - /* Print the message to the buffer if we want to log this king of commands */
> - if (! logger.log_command(thd, command))
> - return FALSE;
> + /* command_type, thread_id */
> + length= my_snprintf(buff, 32, "%5ld ", (long) thread_id);
>
> - va_start(args, format);
> - error= logger.general_log_print(thd, command, format, args);
> - va_end(args);
> + if (my_b_write(&log_file, (uchar*) buff, length))
> + goto err;
>
> - return error;
> -}
> + if (my_b_write(&log_file, (uchar*) command_type, command_type_len))
> + goto err;
>
> -bool general_log_write(THD *thd, enum enum_server_command command,
> - const char *query, uint query_length)
> -{
> - /* Write the message to the log if we want to log this king of commands */
> - if (logger.log_command(thd, command))
> - return logger.general_log_write(thd, command, query, query_length);
> + if (my_b_write(&log_file, (uchar*) "\t", 1))
> + goto err;
>
> - return FALSE;
> -}
> + /* sql_text */
> + if (my_b_write(&log_file, (uchar*) sql_text, sql_text_len))
> + goto err;
>
> -void MYSQL_BIN_LOG::rotate_and_purge(uint flags)
> -{
> -#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 (my_b_write(&log_file, (uchar*) "\n", 1) ||
> + flush_io_cache(&log_file))
> + goto err;
> }
> - if (!(flags & RP_LOCK_LOG_IS_ALREADY_LOCKED))
> - mysql_mutex_unlock(&LOCK_log);
>
> -#ifdef HAVE_REPLICATION
> - /*
> - NOTE: Run purge_logs wo/ holding LOCK_log
> - as it otherwise will deadlock in ndbcluster_binlog_index_purge_file
> - */
> - if (check_purge && expire_logs_days)
> + mysql_mutex_unlock(&LOCK_log);
> + return FALSE;
> +err:
> +
> + if (!write_error)
> {
> - time_t purge_time= my_time(0) - expire_logs_days*24*60*60;
> - if (purge_time >= 0)
> - purge_logs_before_date(purge_time);
> + write_error= 1;
> + sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
> }
> -#endif
> -}
> -
> -uint MYSQL_BIN_LOG::next_file_id()
> -{
> - uint res;
> - mysql_mutex_lock(&LOCK_log);
> - res = file_id++;
> mysql_mutex_unlock(&LOCK_log);
> - return res;
> + return TRUE;
> }
>
>
> /*
> - Write the contents of a cache to the binary log.
> + Log a query to the traditional slow log file
>
> 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
> + write()
> +
> + thd THD of the query
> + current_time current timestamp
> + query_start_arg command start timestamp
> + user_host the pointer to the string with user@host info
> + user_host_len length of the user_host string. this is computed once
> + and passed to all general log event handlers
> + query_utime Amount of time the query took to execute (in microseconds)
> + lock_utime Amount of time the query was locked (in microseconds)
> + is_command The flag, which determines, whether the sql_text is a
> + query or an administrator command.
> + sql_text the very text of the query or administrator command
> + processed
> + sql_text_len the length of sql_text string
>
> 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.
> - */
> -
> -int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log)
> -{
> - Mutex_sentry sentry(lock_log ? &LOCK_log : NULL);
> -
> - 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];
>
> - /*
> - 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.
> - */
> + Log a query to the slow log file.
>
> - group= (uint)my_b_tell(&log_file);
> - hdr_offs= carry= 0;
> + RETURN
> + FALSE - OK
> + TRUE - error occured
> +*/
>
> - do
> - {
> - /*
> - 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);
> +bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time,
> + time_t query_start_arg, const char *user_host,
> + uint user_host_len, ulonglong query_utime,
> + ulonglong lock_utime, bool is_command,
> + const char *sql_text, uint sql_text_len)
> +{
> + bool error= 0;
> + DBUG_ENTER("MYSQL_QUERY_LOG::write");
>
> - /* assemble both halves */
> - memcpy(&header[carry], (char *)cache->read_pos, LOG_EVENT_HEADER_LEN -
> carry);
> + mysql_mutex_lock(&LOCK_log);
>
> - /* fix end_log_pos */
> - val= uint4korr(&header[LOG_POS_OFFSET]) + group;
> - int4store(&header[LOG_POS_OFFSET], val);
> + if (!is_open())
> + {
> + mysql_mutex_unlock(&LOCK_log);
> + DBUG_RETURN(0);
> + }
>
> - /* write the first half of the split header */
> - if (my_b_write(&log_file, header, carry))
> - return ER_ERROR_ON_WRITE;
> + if (is_open())
> + { // Safety agains reopen
> + int tmp_errno= 0;
> + char buff[80], *end;
> + char query_time_buff[22+7], lock_time_buff[22+7];
> + uint buff_len;
> + end= buff;
>
> - /*
> - 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);
> + if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT))
> + {
> + if (current_time != last_time)
> + {
> + last_time= current_time;
> + struct tm start;
> + localtime_r(¤t_time, &start);
>
> - /* next event header at ... */
> - hdr_offs = uint4korr(&header[EVENT_LEN_OFFSET]) - carry;
> + buff_len= my_snprintf(buff, sizeof buff,
> + "# Time: %02d%02d%02d %2d:%02d:%02d\n",
> + start.tm_year % 100, start.tm_mon + 1,
> + start.tm_mday, start.tm_hour,
> + start.tm_min, start.tm_sec);
>
> - carry= 0;
> + /* Note that my_b_write() assumes it knows the length for this */
> + if (my_b_write(&log_file, (uchar*) buff, buff_len))
> + tmp_errno= errno;
> + }
> + const uchar uh[]= "# User@Host: ";
> + if (my_b_write(&log_file, uh, sizeof(uh) - 1))
> + tmp_errno= errno;
> + if (my_b_write(&log_file, (uchar*) user_host, user_host_len))
> + tmp_errno= errno;
> + if (my_b_write(&log_file, (uchar*) "\n", 1))
> + tmp_errno= errno;
> }
> -
> - /* if there is anything to write, process it. */
> -
> - if (likely(length > 0))
> + /* For slow query log */
> + sprintf(query_time_buff, "%.6f", ulonglong2double(query_utime)/1000000.0);
> + sprintf(lock_time_buff, "%.6f", ulonglong2double(lock_utime)/1000000.0);
> + if (my_b_printf(&log_file,
> + "# Query_time: %s Lock_time: %s"
> + " Rows_sent: %lu Rows_examined: %lu\n",
> + query_time_buff, lock_time_buff,
> + (ulong) thd->sent_row_count,
> + (ulong) thd->examined_row_count) == (uint) -1)
> + tmp_errno= errno;
> + if (thd->db && strcmp(thd->db, db))
> + { // Database changed
> + if (my_b_printf(&log_file,"use %s;\n",thd->db) == (uint) -1)
> + tmp_errno= errno;
> + strmov(db,thd->db);
> + }
> + if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt)
> {
> - /*
> - 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").
> - */
> -
> - while (hdr_offs < length)
> + end=strmov(end, ",last_insert_id=");
> + end=longlong10_to_str((longlong)
> +
> thd->first_successful_insert_id_in_prev_stmt_for_binlog,
> + end, -10);
> + }
> + // Save value if we do an insert.
> + if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0)
> + {
> + if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT))
> {
> - /*
> - partial header only? save what we can get, process once
> - we get the rest.
> - */
> -
> - 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 */
> -
> - uchar *log_pos= (uchar *)cache->read_pos + hdr_offs + LOG_POS_OFFSET;
> -
> - /* fix end_log_pos */
> - val= uint4korr(log_pos) + group;
> - int4store(log_pos, val);
> -
> - /* next event header at ... */
> - log_pos= (uchar *)cache->read_pos + hdr_offs + EVENT_LEN_OFFSET;
> - hdr_offs += uint4korr(log_pos);
> -
> - }
> + end=strmov(end,",insert_id=");
> + end=longlong10_to_str((longlong)
> +
> thd->auto_inc_intervals_in_cur_stmt_for_binlog.minimum(),
> + end, -10);
> }
> -
> - /*
> - 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;
> }
>
> - /* 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)));
> -
> - DBUG_ASSERT(carry == 0);
> -
> - if (sync_log)
> - return flush_and_sync(0);
> -
> - return 0; // All OK
> -}
> -
> -/*
> - 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;
> -
> - /* 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.
> + /*
> + This info used to show up randomly, depending on whether the query
> + checked the query start time or not. now we always write current
> + timestamp to the slow log
> */
> - 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();
> - }
> -
> - return error;
> -}
> + end= strmov(end, ",timestamp=");
> + end= int10_to_str((long) current_time, end, 10);
>
> -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)))
> + if (end != buff)
> {
> - signal_update();
> - rotate_and_purge(RP_LOCK_LOG_IS_ALREADY_LOCKED);
> + *end++=';';
> + *end='\n';
> + if (my_b_write(&log_file, (uchar*) "SET ", 4) ||
> + my_b_write(&log_file, (uchar*) buff + 1, (uint) (end-buff)))
> + tmp_errno= errno;
> }
> - mysql_mutex_unlock(&LOCK_log);
> - }
> - DBUG_RETURN(error);
> -}
> -
> -/**
> - 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
> - @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 MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event,
> - bool incident)
> -{
> - DBUG_ENTER("MYSQL_BIN_LOG::write(THD *, IO_CACHE *, Log_event *)");
> - mysql_mutex_lock(&LOCK_log);
> -
> - 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)
> + if (is_command)
> + {
> + end= strxmov(buff, "# administrator command: ", NullS);
> + buff_len= (ulong) (end - buff);
> + my_b_write(&log_file, (uchar*) buff, buff_len);
> + }
> + if (my_b_write(&log_file, (uchar*) sql_text, sql_text_len) ||
> + my_b_write(&log_file, (uchar*) ";\n",2) ||
> + flush_io_cache(&log_file))
> + tmp_errno= errno;
> + if (tmp_errno)
> {
> - /*
> - 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();
> - });
> -
> - if ((write_error= write_cache(cache, false, false)))
> - goto err;
> -
> - if (commit_event && commit_event->write(&log_file))
> - goto err;
> -
> - if (incident && write_incident(thd, FALSE))
> - goto err;
> -
> - 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;
> - }
> -
> - if (RUN_HOOK(binlog_storage, after_flush,
> - (thd, log_file_name, log_file.pos_in_file, synced)))
> + error= 1;
> + if (! write_error)
> {
> - sql_print_error("Failed to run 'after_flush' hooks");
> - write_error=1;
> - goto err;
> + write_error= 1;
> + sql_print_error(ER(ER_ERROR_ON_WRITE), name, error);
> }
> -
> - signal_update();
> - }
> -
> - /*
> - 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);
> + DBUG_RETURN(error);
> +}
>
> - DBUG_RETURN(0);
>
> -err:
> - if (!write_error)
> +/**
> + @todo
> + The following should be using fn_format(); We just need to
> + first change fn_format() to cut the file name if it's too long.
> +*/
> +const char *MYSQL_LOG::generate_name(const char *log_name,
> + const char *suffix,
> + bool strip_ext, char *buff)
> +{
> + if (!log_name || !log_name[0])
> {
> - write_error= 1;
> - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
> + strmake(buff, pidfile_name, FN_REFLEN - strlen(suffix) - 1);
> + return (const char *)
> + fn_format(buff, buff, "", suffix, MYF(MY_REPLACE_EXT|MY_REPLACE_DIR));
> }
> - mysql_mutex_unlock(&LOCK_log);
> - DBUG_RETURN(1);
> + // get rid of extension if the log is binary to avoid problems
> + if (strip_ext)
> + {
> + char *p= fn_ext(log_name);
> + uint length= (uint) (p - log_name);
> + strmake(buff, log_name, min(length, FN_REFLEN-1));
> + return (const char*)buff;
> + }
> + return log_name;
> }
>
>
> -/**
> - Wait until we get a signal that the relay log has been updated.
> -
> - @param thd Thread variable
> -
> - @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 MYSQL_BIN_LOG::wait_for_update_relay_log(THD* thd)
> +int error_log_print(enum loglevel level, const char *format,
> + va_list args)
> {
> - const char *old_msg;
> - DBUG_ENTER("wait_for_update_relay_log");
> -
> - 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;
> + return logger.error_log_print(level, format, args);
> }
>
> -/**
> - 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.
> -*/
>
> -int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd,
> - const struct timespec *timeout)
> +bool slow_log_print(THD *thd, const char *query, uint query_length,
> + ulonglong current_utime)
> {
> - 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);
> + return logger.slow_log_print(thd, query, query_length, current_utime);
> }
>
>
> -/**
> - Close the log file.
> -
> - @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
> -
> - @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 */
> -
> - /* 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));
> - }
> -
> - /* this will cleanup IO_CACHE, sync and close the file */
> - MYSQL_LOG::close(exiting);
> - }
> -
> +bool LOGGER::log_command(THD *thd, enum enum_server_command command)
> +{
> +#ifndef NO_EMBEDDED_ACCESS_CHECKS
> + Security_context *sctx= thd->security_ctx;
> +#endif
> /*
> - 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.
> + Log command if we have at least one log event handler enabled and want
> + to log this king of commands
> */
> -
> - if ((exiting & LOG_CLOSE_INDEX) && my_b_inited(&index_file))
> + if (*general_log_handler_list && (what_to_log & (1L << (uint)
> command)))
> {
> - end_io_cache(&index_file);
> - if (mysql_file_close(index_file.file, MYF(0)) < 0 && ! write_error)
> + if ((thd->variables.option_bits & OPTION_LOG_OFF)
> +#ifndef NO_EMBEDDED_ACCESS_CHECKS
> + && (sctx->master_access & SUPER_ACL)
> +#endif
> + )
> {
> - write_error= 1;
> - sql_print_error(ER(ER_ERROR_ON_WRITE), index_file_name, errno);
> + /* No logging */
> + return FALSE;
> }
> +
> + return TRUE;
> }
> - log_state= (exiting & LOG_CLOSE_TO_BE_OPENED) ? LOG_TO_BE_OPENED :
> LOG_CLOSED;
> - safeFree(name);
> - DBUG_VOID_RETURN;
> +
> + return FALSE;
> }
>
>
> -void MYSQL_BIN_LOG::set_max_size(ulong max_size_arg)
> +bool general_log_print(THD *thd, enum enum_server_command command,
> + const char *format, ...)
> {
> - /*
> - 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;
> + va_list args;
> + uint error= 0;
> +
> + /* Print the message to the buffer if we want to log this king of commands */
> + if (! logger.log_command(thd, command))
> + return FALSE;
> +
> + va_start(args, format);
> + error= logger.general_log_print(thd, command, format, args);
> + va_end(args);
> +
> + return error;
> +}
> +
> +bool general_log_write(THD *thd, enum enum_server_command command,
> + const char *query, uint query_length)
> +{
> + /* Write the message to the log if we want to log this king of commands */
> + if (logger.log_command(thd, command))
> + return logger.general_log_write(thd, command, query, query_length);
> +
> + return FALSE;
> }
>
>
> +
> /**
> Check if a string is a valid number.
>
> @@ -5482,14 +2051,6 @@ bool flush_error_log()
> return result;
> }
>
> -void MYSQL_BIN_LOG::signal_update()
> -{
> - DBUG_ENTER("MYSQL_BIN_LOG::signal_update");
> - signal_cnt++;
> - mysql_cond_broadcast(&update_cond);
> - DBUG_VOID_RETURN;
> -}
> -
> #ifdef _WIN32
> static void print_buffer_to_nt_eventlog(enum loglevel level, char *buff,
> size_t length, size_t buffLen)
> @@ -6123,232 +2684,3 @@ int TC_LOG::using_heuristic_recover()
> sql_print_information("Please restart mysqld without --tc-heuristic-recover");
> return 1;
> }
> -
> -/****** transaction coordinator log for 2pc - binlog() based solution ******/
> -#define TC_LOG_BINLOG MYSQL_BIN_LOG
> -
> -/**
> - @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 TC_LOG_BINLOG::open(const char *opt_name)
> -{
> - LOG_INFO log_info;
> - int error= 1;
> -
> - DBUG_ASSERT(total_ha_2pc > 1);
> - DBUG_ASSERT(opt_name && opt_name[0]);
> -
> - 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 (!my_b_inited(&index_file))
> - {
> - /* There was a failure to open the index file, can't open the binlog */
> - cleanup();
> - return 1;
> - }
> -
> - 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;
> - }
> -
> - 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;
> - }
> -
> - {
> - const char *errmsg;
> - IO_CACHE log;
> - File file;
> - Log_event *ev=0;
> - Format_description_log_event fdle(BINLOG_VERSION);
> - char log_name[FN_REFLEN];
> -
> - if (! fdle.is_valid())
> - goto err;
> -
> - do
> - {
> - strmake(log_name, log_info.log_file_name, sizeof(log_name)-1);
> - } while (!(error= find_next_log(&log_info, 1)));
> -
> - if (error != LOG_INFO_EOF)
> - {
> - sql_print_error("find_log_pos() failed (error: %d)", error);
> - goto err;
> - }
> -
> - if ((file= open_binlog(&log, log_name, &errmsg)) < 0)
> - {
> - sql_print_error("%s", errmsg);
> - goto err;
> - }
> -
> - 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;
> -
> - delete ev;
> - end_io_cache(&log);
> - mysql_file_close(file, MYF(MY_WME));
> -
> - if (error)
> - goto err;
> - }
> -
> -err:
> - return error;
> -}
> -
> -/** 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);
> -}
> -
> -/**
> - @todo
> - group commit
> -
> - @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.
> - */
> - DBUG_RETURN(!binlog_flush_stmt_cache(thd, cache_mngr) &&
> - !binlog_flush_trx_cache(thd, cache_mngr, &xle));
> -}
> -
> -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
> -}
> -
> -int TC_LOG_BINLOG::recover(IO_CACHE *log, Format_description_log_event *fdle)
> -{
> - Log_event *ev;
> - HASH xids;
> - MEM_ROOT mem_root;
> -
> - 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;
> -
> - init_alloc_root(&mem_root, TC_LOG_PAGE_SIZE, TC_LOG_PAGE_SIZE);
> -
> - 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)
> - {
> - 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;
> - }
> -
> - if (ha_recover(&xids))
> - goto err2;
> -
> - free_root(&mem_root, MYF(0));
> - my_hash_free(&xids);
> - return 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;
> -}
> -
> -
> -#ifdef INNODB_COMPATIBILITY_HOOKS
> -/**
> - Get the file name of the MySQL binlog.
> - @return the name of the binlog file
> -*/
> -extern "C"
> -const char* mysql_bin_log_file_name(void)
> -{
> - return mysql_bin_log.get_log_fname();
> -}
> -/**
> - Get the current position of the MySQL binlog.
> - @return byte offset from the beginning of the binlog
> -*/
> -extern "C"
> -ulonglong mysql_bin_log_file_pos(void)
> -{
> - return (ulonglong) mysql_bin_log.get_log_file()->pos_in_file;
> -}
> -#endif /* INNODB_COMPATIBILITY_HOOKS */
> -
> -
> -struct st_mysql_storage_engine binlog_storage_engine=
> -{ MYSQL_HANDLERTON_INTERFACE_VERSION };
> -
> -mysql_declare_plugin(binlog)
> -{
> - MYSQL_STORAGE_ENGINE_PLUGIN,
> - &binlog_storage_engine,
> - "binlog",
> - "MySQL AB",
> - "This is a pseudo storage engine to represent the binlog in a transaction",
> - PLUGIN_LICENSE_GPL,
> - binlog_init, /* Plugin Init */
> - NULL, /* Plugin Deinit */
> - 0x0100 /* 1.0 */,
> - NULL, /* status variables */
> - NULL, /* system variables */
> - NULL /* config options */
> -}
> -mysql_declare_plugin_end;
>
> === modified file 'sql/log.h'
> --- a/sql/log.h 2010-04-20 11:58:28 +0000
> +++ b/sql/log.h 2010-06-21 06:01:50 +0000
> @@ -19,17 +19,6 @@
> #include "unireg.h" // REQUIRED: for other includes
> #include "handler.h" /* my_xid */
>
> -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);
> -
> /*
> Transaction Coordinator log - a base abstract class
> for two different implementations
> @@ -144,8 +133,6 @@ extern TC_LOG_DUMMY tc_log_dummy;
> */
> #define LOG_WARN_UNIQUE_FN_EXT_LEFT 1000
>
> -class Relay_log_info;
> -
> #ifdef HAVE_PSI_INTERFACE
> extern PSI_mutex_key key_LOG_INFO_lock;
> #endif
> @@ -178,9 +165,6 @@ typedef struct st_log_info
> #define LOG_FILE 2
> #define LOG_TABLE 4
>
> -class Log_event;
> -class Rows_log_event;
> -
> enum enum_log_type { LOG_UNKNOWN, LOG_NORMAL, LOG_BIN };
> enum enum_log_state { LOG_OPENED, LOG_CLOSED, LOG_TO_BE_OPENED };
>
> @@ -254,228 +238,6 @@ private:
> time_t last_time;
> };
>
> -class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
> -{
> - private:
> - /* LOCK_log and LOCK_index are inited by init_pthread_objects() */
> - mysql_mutex_t LOCK_index;
> - mysql_mutex_t LOCK_prep_xids;
> - mysql_cond_t COND_prep_xids;
> - mysql_cond_t update_cond;
> - ulonglong bytes_written;
> - IO_CACHE index_file;
> - char index_file_name[FN_REFLEN];
> - /*
> - purge_file is a temp file used in purge_logs so that the index file
> - can be updated before deleting files from disk, yielding better crash
> - recovery. It is created on demand the first time purge_logs is called
> - and then reused for subsequent calls. It is cleaned up in cleanup().
> - */
> - IO_CACHE purge_index_file;
> - char purge_index_file_name[FN_REFLEN];
> - /*
> - The max size before rotation (usable only if log_type == LOG_BIN: binary
> - logs and relay logs).
> - For a binlog, max_size should be max_binlog_size.
> - For a relay log, it should be max_relay_log_size if this is non-zero,
> - max_binlog_size otherwise.
> - max_size is set in init(), and dynamically changed (when one does SET
> - GLOBAL MAX_BINLOG_SIZE|MAX_RELAY_LOG_SIZE) by fix_max_binlog_size and
> - fix_max_relay_log_size).
> - */
> - ulong max_size;
> - long prepared_xids; /* for tc log - number of xids to remember */
> - // current file sequence number for load data infile binary logging
> - uint file_id;
> - uint open_count; // For replication
> - int readers_count;
> - bool need_start_event;
> - /*
> - no_auto_events means we don't want any of these automatic events :
> - Start/Rotate/Stop. That is, in 4.x when we rotate a relay log, we don't
> - want a Rotate_log event to be written to the relay log. When we start a
> - relay log etc. So in 4.x this is 1 for relay logs, 0 for binlogs.
> - In 5.0 it's 0 for relay logs too!
> - */
> - bool no_auto_events;
> -
> - ulonglong m_table_map_version;
> -
> - /* pointer to the sync period variable, for binlog this will be
> - sync_binlog_period, for relay log this will be
> - sync_relay_log_period
> - */
> - uint *sync_period_ptr;
> - uint sync_counter;
> -
> - inline uint get_sync_period()
> - {
> - return *sync_period_ptr;
> - }
> -
> - int write_to_file(IO_CACHE *cache);
> - /*
> - This is used to start writing to a new log file. The difference from
> - new_file() is locking. new_file_without_locking() does not acquire
> - LOCK_log.
> - */
> - void new_file_without_locking();
> - void new_file_impl(bool need_lock);
> -
> -public:
> - MYSQL_LOG::generate_name;
> - MYSQL_LOG::is_open;
> -
> - /* This is relay log */
> - bool is_relay_log;
> - ulong signal_cnt; // update of the counter is checked by heartbeat
> - /*
> - These describe the log's format. This is used only for relay logs.
> - _for_exec is used by the SQL thread, _for_queue by the I/O thread. It's
> - necessary to have 2 distinct objects, because the I/O thread may be reading
> - events in a different format from what the SQL thread is reading (consider
> - the case of a master which has been upgraded from 5.0 to 5.1 without doing
> - RESET MASTER, or from 4.x to 5.0).
> - */
> - Format_description_log_event *description_event_for_exec,
> - *description_event_for_queue;
> -
> - MYSQL_BIN_LOG(uint *sync_period);
> - /*
> - note that there's no destructor ~MYSQL_BIN_LOG() !
> - The reason is that we don't want it to be automatically called
> - on exit() - but only during the correct shutdown process
> - */
> -
> - int open(const char *opt_name);
> - void close();
> - int log_xid(THD *thd, my_xid xid);
> - void unlog(ulong cookie, my_xid xid);
> - int recover(IO_CACHE *log, Format_description_log_event *fdle);
> -#if !defined(MYSQL_CLIENT)
> - bool is_table_mapped(TABLE *table) const
> - {
> - return table->s->table_map_version == table_map_version();
> - }
> -
> - ulonglong table_map_version() const { return m_table_map_version; }
> - void update_table_map_version() { ++m_table_map_version; }
> -
> - int flush_and_set_pending_rows_event(THD *thd, Rows_log_event* event,
> - bool is_transactional);
> - int remove_pending_rows_event(THD *thd, bool is_transactional);
> -
> -#endif /* !defined(MYSQL_CLIENT) */
> - void reset_bytes_written()
> - {
> - bytes_written = 0;
> - }
> - void harvest_bytes_written(ulonglong* counter)
> - {
> -#ifndef DBUG_OFF
> - char buf1[22],buf2[22];
> -#endif
> - DBUG_ENTER("harvest_bytes_written");
> - (*counter)+=bytes_written;
> - DBUG_PRINT("info",("counter: %s bytes_written: %s", llstr(*counter,buf1),
> - llstr(bytes_written,buf2)));
> - bytes_written=0;
> - DBUG_VOID_RETURN;
> - }
> - void set_max_size(ulong max_size_arg);
> - void signal_update();
> - void wait_for_update_relay_log(THD* thd);
> - int wait_for_update_bin_log(THD* thd, const struct timespec * timeout);
> - void set_need_start_event() { need_start_event = 1; }
> - void init(bool no_auto_events_arg, ulong max_size);
> - void init_pthread_objects();
> - void cleanup();
> - bool open(const char *log_name,
> - enum_log_type log_type,
> - const char *new_name,
> - enum cache_type io_cache_type_arg,
> - bool no_auto_events_arg, ulong max_size,
> - bool null_created,
> - bool need_mutex);
> - bool open_index_file(const char *index_file_name_arg,
> - const char *log_name, bool need_mutex);
> - /* Use this to start writing a new log file */
> - void new_file();
> -
> - bool write(Log_event* event_info);
> - bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event, bool incident);
> - bool write_incident(THD *thd, bool lock);
> -
> - int write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync);
> - void set_write_error(THD *thd);
> - bool check_write_error(THD *thd);
> -
> - void start_union_events(THD *thd, query_id_t query_id_param);
> - void stop_union_events(THD *thd);
> - bool is_query_in_union(THD *thd, query_id_t query_id_param);
> -
> - /*
> - v stands for vector
> - invoked as appendv(buf1,len1,buf2,len2,...,bufn,lenn,0)
> - */
> - bool appendv(const char* buf,uint len,...);
> - bool append(Log_event* ev);
> -
> - void make_log_name(char* buf, const char* log_ident);
> - bool is_active(const char* log_file_name);
> - int update_log_index(LOG_INFO* linfo, bool need_update_threads);
> - void rotate_and_purge(uint flags);
> - /**
> - Flush binlog cache and synchronize to disk.
> -
> - This function flushes events in binlog cache to binary log file,
> - it will do synchronizing according to the setting of system
> - variable 'sync_binlog'. If file is synchronized, @c synced will
> - be set to 1, otherwise 0.
> -
> - @param[out] synced if not NULL, set to 1 if file is synchronized, otherwise 0
> -
> - @retval 0 Success
> - @retval other Failure
> - */
> - bool flush_and_sync(bool *synced);
> - int purge_logs(const char *to_log, bool included,
> - bool need_mutex, bool need_update_threads,
> - ulonglong *decrease_log_space);
> - int purge_logs_before_date(time_t purge_time);
> - int purge_first_log(Relay_log_info* rli, bool included);
> - int set_purge_index_file_name(const char *base_file_name);
> - int open_purge_index_file(bool destroy);
> - bool is_inited_purge_index_file();
> - int close_purge_index_file();
> - int clean_purge_index_file();
> - int sync_purge_index_file();
> - int register_purge_index_entry(const char* entry);
> - int register_create_index_entry(const char* entry);
> - int purge_index_entry(THD *thd, ulonglong *decrease_log_space,
> - bool need_mutex);
> - bool reset_logs(THD* thd);
> - void close(uint exiting);
> -
> - // iterating through the log index file
> - int find_log_pos(LOG_INFO* linfo, const char* log_name,
> - bool need_mutex);
> - int find_next_log(LOG_INFO* linfo, bool need_mutex);
> - int get_current_log(LOG_INFO* linfo);
> - int raw_get_current_log(LOG_INFO* linfo);
> - uint next_file_id();
> - inline char* get_index_fname() { return index_file_name;}
> - inline char* get_log_fname() { return log_file_name; }
> - inline char* get_name() { return name; }
> - inline mysql_mutex_t* get_log_lock() { return &LOCK_log; }
> - inline IO_CACHE* get_log_file() { return &log_file; }
> -
> - inline void lock_index() { mysql_mutex_lock(&LOCK_index);}
> - inline void unlock_index() { mysql_mutex_unlock(&LOCK_index);}
> - inline IO_CACHE *get_index_file() { return &index_file;}
> - inline uint32 get_open_count() { return open_count; }
> -};
> -
> class Log_event_handler
> {
> public:
> @@ -674,12 +436,8 @@ 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 MYSQL_PLUGIN_IMPORT MYSQL_BIN_LOG mysql_bin_log;
> extern LOGGER logger;
>
> #endif /* LOG_H */
>
> === modified file 'sql/log_event.cc'
> --- a/sql/log_event.cc 2010-05-14 18:38:28 +0000
> +++ b/sql/log_event.cc 2010-06-21 06:01:50 +0000
> @@ -37,7 +37,7 @@
> #include "tztime.h" // struct Time_zone
> #include "sql_load.h" // mysql_load
> #include "sql_db.h" // load_db_opt_by_name
> -#include "slave.h"
> +#include "rpl_slave.h"
> #include "rpl_rli.h"
> #include "rpl_mi.h"
> #include "rpl_filter.h"
>
> === modified file 'sql/log_event.h'
> --- a/sql/log_event.h 2010-04-01 19:34:09 +0000
> +++ b/sql/log_event.h 2010-06-21 06:01:50 +0000
> @@ -1755,8 +1755,7 @@ public: /* !!! Public in this pat
> @class Slave_log_event
>
> Note that this class is currently not used at all; no code writes a
> - @c Slave_log_event (though some code in @c repl_failsafe.cc reads @c
> - Slave_log_event). So it's not a problem if this code is not
> + @c Slave_log_event. So it's not a problem if this code is not
> maintained.
>
> @section Slave_log_event_binary_format Binary Format
>
> === modified file 'sql/mysqld.cc'
> --- a/sql/mysqld.cc 2010-04-19 08:29:52 +0000
> +++ b/sql/mysqld.cc 2010-06-21 06:01:50 +0000
> @@ -53,11 +53,10 @@
> #include <m_ctype.h>
> #include <my_dir.h>
> #include <my_bit.h>
> -#include "slave.h"
> +#include "rpl_slave.h"
> +#include "rpl_master.h"
> #include "rpl_mi.h"
> -#include "sql_repl.h"
> #include "rpl_filter.h"
> -#include "repl_failsafe.h"
> #include <my_stacktrace.h>
> #include "mysqld_suffix.h"
> #include "mysys_err.h"
> @@ -540,7 +539,6 @@ ulong slow_launch_threads = 0;
> uint sync_binlog_period= 0, sync_relaylog_period= 0,
> sync_relayloginfo_period= 0, sync_masterinfo_period= 0;
> ulong expire_logs_days = 0;
> -ulong rpl_recovery_rank=0;
>
> const double log_10[] = {
> 1e000, 1e001, 1e002, 1e003, 1e004, 1e005, 1e006, 1e007, 1e008, 1e009,
> @@ -1615,10 +1613,6 @@ static void clean_up_mutexes()
> OPENSSL_free(openssl_stdlocks);
> #endif
> #endif
> -#ifdef HAVE_REPLICATION
> - mysql_mutex_destroy(&LOCK_rpl_status);
> - mysql_cond_destroy(&COND_rpl_status);
> -#endif
> mysql_mutex_destroy(&LOCK_active_mi);
> mysql_rwlock_destroy(&LOCK_sys_init_connect);
> mysql_rwlock_destroy(&LOCK_sys_init_slave);
> @@ -3820,10 +3814,6 @@ static int init_thread_environment()
> mysql_cond_init(key_COND_thread_cache, &COND_thread_cache, NULL);
> mysql_cond_init(key_COND_flush_thread_cache, &COND_flush_thread_cache, NULL);
> mysql_cond_init(key_COND_manager, &COND_manager, NULL);
> -#ifdef HAVE_REPLICATION
> - mysql_mutex_init(key_LOCK_rpl_status, &LOCK_rpl_status, MY_MUTEX_INIT_FAST);
> - mysql_cond_init(key_COND_rpl_status, &COND_rpl_status, NULL);
> -#endif
> mysql_mutex_init(key_LOCK_server_started,
> &LOCK_server_started, MY_MUTEX_INIT_FAST);
> mysql_cond_init(key_COND_server_started, &COND_server_started, NULL);
> @@ -6129,9 +6119,6 @@ thread is in the master's binlogs.",
> (uchar**) &master_retry_count, (uchar**) &master_retry_count, 0,
> GET_ULONG,
> REQUIRED_ARG, 3600*24, 0, 0, 0, 0, 0},
> #ifdef HAVE_REPLICATION
> - {"init-rpl-role", 0, "Set the replication role.",
> - (uchar**)&rpl_status, (uchar**)&rpl_status, &rpl_role_typelib,
> - GET_ENUM, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
> {"max-binlog-dump-events", 0,
> "Option used by mysql-test for debugging and testing of replication.",
> (uchar**) &max_binlog_dump_events, (uchar**) &max_binlog_dump_events, 0,
> @@ -6335,13 +6322,6 @@ static int show_flushstatustime(THD *thd
> #endif
>
> #ifdef HAVE_REPLICATION
> -static int show_rpl_status(THD *thd, SHOW_VAR *var, char *buff)
> -{
> - var->type= SHOW_CHAR;
> - var->value= const_cast<char*>(rpl_status_type[(int)rpl_status]);
> - return 0;
> -}
> -
> static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff)
> {
> var->type= SHOW_MY_BOOL;
> @@ -6761,9 +6741,6 @@ SHOW_VAR status_vars[]= {
> #endif /*HAVE_QUERY_CACHE*/
> {"Queries", (char*) &show_queries, SHOW_FUNC},
> {"Questions", (char*) offsetof(STATUS_VAR, questions),
> SHOW_LONG_STATUS},
> -#ifdef HAVE_REPLICATION
> - {"Rpl_status", (char*) &show_rpl_status, SHOW_FUNC},
> -#endif
> {"Select_full_join", (char*) offsetof(STATUS_VAR, select_full_join_count),
> SHOW_LONG_STATUS},
> {"Select_full_range_join", (char*) offsetof(STATUS_VAR,
> select_full_range_join_count), SHOW_LONG_STATUS},
> {"Select_range", (char*) offsetof(STATUS_VAR, select_range_count),
> SHOW_LONG_STATUS},
> @@ -7912,7 +7889,7 @@ PSI_mutex_key key_BINLOG_LOCK_index, key
> key_LOCK_gdl, key_LOCK_global_read_lock, key_LOCK_global_system_variables,
> key_LOCK_lock_db, key_LOCK_manager, key_LOCK_mapped_file,
> key_LOCK_mysql_create_db, key_LOCK_open, key_LOCK_prepared_stmt_count,
> - key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status,
> + key_LOCK_server_started, key_LOCK_status,
> key_LOCK_system_variables_hash, key_LOCK_table_share, key_LOCK_thd_data,
> key_LOCK_user_conn, key_LOCK_uuid_generator, key_LOG_LOCK_log,
> key_master_info_data_lock, key_master_info_run_lock,
> @@ -7954,7 +7931,6 @@ static PSI_mutex_info all_server_mutexes
> { &key_LOCK_mysql_create_db, "LOCK_mysql_create_db", PSI_FLAG_GLOBAL},
> { &key_LOCK_open, "LOCK_open", PSI_FLAG_GLOBAL},
> { &key_LOCK_prepared_stmt_count, "LOCK_prepared_stmt_count",
> PSI_FLAG_GLOBAL},
> - { &key_LOCK_rpl_status, "LOCK_rpl_status", PSI_FLAG_GLOBAL},
> { &key_LOCK_server_started, "LOCK_server_started", PSI_FLAG_GLOBAL},
> { &key_LOCK_status, "LOCK_status", PSI_FLAG_GLOBAL},
> { &key_LOCK_system_variables_hash, "LOCK_system_variables_hash",
> PSI_FLAG_GLOBAL},
> @@ -7999,7 +7975,7 @@ PSI_cond_key key_PAGE_cond, key_COND_act
>
> PSI_cond_key key_BINLOG_COND_prep_xids, key_BINLOG_update_cond,
> key_COND_cache_status_changed, key_COND_global_read_lock, key_COND_manager,
> - key_COND_refresh, key_COND_rpl_status, key_COND_server_started,
> + key_COND_refresh, key_COND_server_started,
> key_delayed_insert_cond, key_delayed_insert_cond_client,
> key_item_func_sleep_cond, key_master_info_data_cond,
> key_master_info_start_cond, key_master_info_stop_cond,
> @@ -8024,7 +8000,6 @@ static PSI_cond_info all_server_conds[]=
> { &key_COND_global_read_lock, "COND_global_read_lock", PSI_FLAG_GLOBAL},
> { &key_COND_manager, "COND_manager", PSI_FLAG_GLOBAL},
> { &key_COND_refresh, "COND_refresh", PSI_FLAG_GLOBAL},
> - { &key_COND_rpl_status, "COND_rpl_status", PSI_FLAG_GLOBAL},
> { &key_COND_server_started, "COND_server_started", PSI_FLAG_GLOBAL},
> { &key_delayed_insert_cond, "Delayed_insert::cond", 0},
> { &key_delayed_insert_cond_client, "Delayed_insert::cond_client", 0},
>
> === modified file 'sql/mysqld.h'
> --- a/sql/mysqld.h 2010-04-12 13:35:06 +0000
> +++ b/sql/mysqld.h 2010-06-21 05:56:56 +0000
> @@ -177,7 +177,7 @@ extern ulong binlog_cache_size, open_fil
> extern ulonglong max_binlog_cache_size;
> extern ulong max_binlog_size, max_relay_log_size;
> extern ulong opt_binlog_rows_event_max_size;
> -extern ulong rpl_recovery_rank, thread_cache_size, thread_pool_size;
> +extern ulong thread_cache_size, thread_pool_size;
> extern ulong back_log;
> extern char language[FN_REFLEN];
> extern ulong server_id, concurrency;
> @@ -234,7 +234,7 @@ extern PSI_mutex_key key_BINLOG_LOCK_ind
> key_LOCK_gdl, key_LOCK_global_read_lock, key_LOCK_global_system_variables,
> key_LOCK_lock_db, key_LOCK_logger, key_LOCK_manager, key_LOCK_mapped_file,
> key_LOCK_mysql_create_db, key_LOCK_open, key_LOCK_prepared_stmt_count,
> - key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status,
> + key_LOCK_server_started, key_LOCK_status,
> key_LOCK_table_share, key_LOCK_thd_data,
> key_LOCK_user_conn, key_LOCK_uuid_generator, key_LOG_LOCK_log,
> key_master_info_data_lock, key_master_info_run_lock,
> @@ -253,7 +253,7 @@ extern PSI_cond_key key_PAGE_cond, key_C
>
> extern PSI_cond_key key_BINLOG_COND_prep_xids, key_BINLOG_update_cond,
> key_COND_cache_status_changed, key_COND_global_read_lock, key_COND_manager,
> - key_COND_refresh, key_COND_rpl_status, key_COND_server_started,
> + key_COND_refresh, key_COND_server_started,
> key_delayed_insert_cond, key_delayed_insert_cond_client,
> key_item_func_sleep_cond, key_master_info_data_cond,
> key_master_info_start_cond, key_master_info_stop_cond,
>
> === removed file 'sql/repl_failsafe.cc'
> --- a/sql/repl_failsafe.cc 2010-04-01 19:34:09 +0000
> +++ b/sql/repl_failsafe.cc 1970-01-01 00:00:00 +0000
> @@ -1,745 +0,0 @@
> -/* Copyright (C) 2001-2006 MySQL AB & Sasha, 2008-2009 Sun Microsystems, Inc
> -
> - 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 */
> -
> -/**
> - @file
> -
> - All of the functions defined in this file which are not used (the ones to
> - handle failsafe) are not used; their code has not been updated for more
> - than one year now so should be considered as BADLY BROKEN. Do not enable
> - it. The used functions (to handle LOAD DATA FROM MASTER, plus some small
> - functions like register_slave()) are working.
> -*/
> -
> -#include "sql_priv.h"
> -#include "sql_parse.h" // check_access
> -#ifdef HAVE_REPLICATION
> -
> -#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"
> -#include "log_event.h"
> -#include "sql_db.h" // mysql_create_db
> -#include <mysql.h>
> -
> -#define SLAVE_LIST_CHUNK 128
> -#define SLAVE_ERRMSG_SIZE (FN_REFLEN+64)
> -
> -
> -RPL_STATUS rpl_status=RPL_NULL;
> -mysql_mutex_t LOCK_rpl_status;
> -mysql_cond_t COND_rpl_status;
> -HASH slave_list;
> -
> -const char *rpl_role_type[] = {"MASTER","SLAVE",NullS};
> -TYPELIB rpl_role_typelib = {array_elements(rpl_role_type)-1,"",
> - rpl_role_type, NULL};
> -
> -const char* rpl_status_type[]=
> -{
> - "AUTH_MASTER","IDLE_SLAVE","ACTIVE_SLAVE","LOST_SOLDIER","TROOP_SOLDIER",
> - "RECOVERY_CAPTAIN","NULL",NullS
> -};
> -TYPELIB rpl_status_typelib= {array_elements(rpl_status_type)-1,"",
> - rpl_status_type, NULL};
> -
> -
> -static Slave_log_event* find_slave_event(IO_CACHE* log,
> - const char* log_file_name,
> - char* errmsg);
> -
> -/*
> - All of the functions defined in this file which are not used (the ones to
> - handle failsafe) are not used; their code has not been updated for more than
> - one year now so should be considered as BADLY BROKEN. Do not enable it.
> - The used functions (to handle LOAD DATA FROM MASTER, plus some small
> - functions like register_slave()) are working.
> -*/
> -
> -#if NOT_USED
> -static int init_failsafe_rpl_thread(THD* thd)
> -{
> - DBUG_ENTER("init_failsafe_rpl_thread");
> - thd->system_thread = SYSTEM_THREAD_DELAYED_INSERT;
> - /*
> - thd->bootstrap is to report errors barely to stderr; if this code is
> - enable again one day, one should check if bootstrap is still needed (maybe
> - this thread has no other error reporting method).
> - */
> - thd->bootstrap = 1;
> - thd->security_ctx->skip_grants();
> - my_net_init(&thd->net, 0);
> - thd->net.read_timeout = slave_net_timeout;
> - thd->max_client_packet_length=thd->net.max_packet;
> - mysql_mutex_lock(&LOCK_thread_count);
> - thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
> - mysql_mutex_unlock(&LOCK_thread_count);
> -
> - if (init_thr_lock() || thd->store_globals())
> - {
> - /* purecov: begin inspected */
> - close_connection(thd, ER_OUT_OF_RESOURCES, 1); // is this needed?
> - statistic_increment(aborted_connects,&LOCK_status);
> - one_thread_per_connection_end(thd,0);
> - DBUG_RETURN(-1);
> - /* purecov: end */
> - }
> -
> - thd->mem_root->free= thd->mem_root->used= 0;
> - thd_proc_info(thd, "Thread initialized");
> - thd->version=refresh_version;
> - thd->set_time();
> - DBUG_RETURN(0);
> -}
> -#endif
> -
> -void change_rpl_status(RPL_STATUS from_status, RPL_STATUS to_status)
> -{
> - mysql_mutex_lock(&LOCK_rpl_status);
> - if (rpl_status == from_status || rpl_status == RPL_ANY)
> - rpl_status = to_status;
> - mysql_cond_signal(&COND_rpl_status);
> - mysql_mutex_unlock(&LOCK_rpl_status);
> -}
> -
> -
> -#define get_object(p, obj, msg) \
> -{\
> - uint len = (uint)*p++; \
> - if (p + len > p_end || len >= sizeof(obj)) \
> - {\
> - errmsg= msg;\
> - goto err; \
> - }\
> - strmake(obj,(char*) p,len); \
> - p+= len; \
> -}\
> -
> -
> -static inline int cmp_master_pos(Slave_log_event* sev, LEX_MASTER_INFO* mi)
> -{
> - return cmp_master_pos(sev->master_log, sev->master_pos,
> mi->log_file_name,
> - mi->pos);
> -}
> -
> -
> -void unregister_slave(THD* thd, bool only_mine, bool need_mutex)
> -{
> - if (thd->server_id)
> - {
> - if (need_mutex)
> - mysql_mutex_lock(&LOCK_slave_list);
> -
> - SLAVE_INFO* old_si;
> - if ((old_si = (SLAVE_INFO*)my_hash_search(&slave_list,
> - (uchar*)&thd->server_id, 4))
> &&
> - (!only_mine || old_si->thd == thd))
> - my_hash_delete(&slave_list, (uchar*)old_si);
> -
> - if (need_mutex)
> - mysql_mutex_unlock(&LOCK_slave_list);
> - }
> -}
> -
> -
> -/**
> - Register slave in 'slave_list' hash table.
> -
> - @return
> - 0 ok
> - @return
> - 1 Error. Error message sent to client
> -*/
> -
> -int register_slave(THD* thd, uchar* packet, uint packet_length)
> -{
> - int res;
> - SLAVE_INFO *si;
> - uchar *p= packet, *p_end= packet + packet_length;
> - const char *errmsg= "Wrong parameters to function register_slave";
> -
> - if (check_access(thd, REPL_SLAVE_ACL, any_db, NULL, NULL, 0, 0))
> - return 1;
> - if (!(si = (SLAVE_INFO*)my_malloc(sizeof(SLAVE_INFO), MYF(MY_WME))))
> - goto err2;
> -
> - thd->server_id= si->server_id= uint4korr(p);
> - p+= 4;
> - get_object(p,si->host, "Failed to register slave: too long 'report-host'");
> - get_object(p,si->user, "Failed to register slave: too long 'report-user'");
> - get_object(p,si->password, "Failed to register slave; too long
> 'report-password'");
> - if (p+10 > p_end)
> - goto err;
> - si->port= uint2korr(p);
> - p += 2;
> - /*
> - We need to by pass the bytes used in the fake rpl_recovery_rank
> - variable. It was removed in patch for BUG#13963. But this would
> - make a server with that patch unable to connect to an old master.
> - See: BUG#49259
> - */
> - // si->rpl_recovery_rank= uint4korr(p);
> - p += 4;
> - if (!(si->master_id= uint4korr(p)))
> - si->master_id= server_id;
> - si->thd= thd;
> -
> - mysql_mutex_lock(&LOCK_slave_list);
> - unregister_slave(thd,0,0);
> - res= my_hash_insert(&slave_list, (uchar*) si);
> - mysql_mutex_unlock(&LOCK_slave_list);
> - return res;
> -
> -err:
> - my_free(si, MYF(MY_WME));
> - my_message(ER_UNKNOWN_ERROR, errmsg, MYF(0)); /* purecov: inspected */
> -err2:
> - return 1;
> -}
> -
> -extern "C" uint32
> -*slave_list_key(SLAVE_INFO* si, size_t *len,
> - my_bool not_used __attribute__((unused)))
> -{
> - *len = 4;
> - return &si->server_id;
> -}
> -
> -extern "C" void slave_info_free(void *s)
> -{
> - my_free(s, MYF(MY_WME));
> -}
> -
> -#ifdef HAVE_PSI_INTERFACE
> -static PSI_mutex_key key_LOCK_slave_list;
> -
> -static PSI_mutex_info all_slave_list_mutexes[]=
> -{
> - { &key_LOCK_slave_list, "LOCK_slave_list", PSI_FLAG_GLOBAL}
> -};
> -
> -static void init_all_slave_list_mutexes(void)
> -{
> - const char* category= "sql";
> - int count;
> -
> - if (PSI_server == NULL)
> - return;
> -
> - count= array_elements(all_slave_list_mutexes);
> - PSI_server->register_mutex(category, all_slave_list_mutexes, count);
> -}
> -#endif /* HAVE_PSI_INTERFACE */
> -
> -void init_slave_list()
> -{
> -#ifdef HAVE_PSI_INTERFACE
> - init_all_slave_list_mutexes();
> -#endif
> -
> - my_hash_init(&slave_list, system_charset_info, SLAVE_LIST_CHUNK, 0, 0,
> - (my_hash_get_key) slave_list_key,
> - (my_hash_free_key) slave_info_free, 0);
> - mysql_mutex_init(key_LOCK_slave_list, &LOCK_slave_list, MY_MUTEX_INIT_FAST);
> -}
> -
> -void end_slave_list()
> -{
> - /* No protection by a mutex needed as we are only called at shutdown */
> - if (my_hash_inited(&slave_list))
> - {
> - my_hash_free(&slave_list);
> - mysql_mutex_destroy(&LOCK_slave_list);
> - }
> -}
> -
> -static int find_target_pos(LEX_MASTER_INFO *mi, IO_CACHE *log, char *errmsg)
> -{
> - my_off_t log_pos = (my_off_t) mi->pos;
> - uint32 target_server_id = mi->server_id;
> -
> - for (;;)
> - {
> - Log_event* ev;
> - if (!(ev= Log_event::read_log_event(log, (mysql_mutex_t*) 0, 0)))
> - {
> - if (log->error > 0)
> - strmov(errmsg, "Binary log truncated in the middle of event");
> - else if (log->error < 0)
> - strmov(errmsg, "I/O error reading binary log");
> - else
> - strmov(errmsg, "Could not find target event in the binary log");
> - return 1;
> - }
> -
> - if (ev->log_pos >= log_pos && ev->server_id ==
> target_server_id)
> - {
> - delete ev;
> - mi->pos = my_b_tell(log);
> - return 0;
> - }
> - delete ev;
> - }
> - /* Impossible */
> -}
> -
> -/**
> - @details
> - Before 4.0.15 we had a member of THD called log_pos, it was meant for
> - failsafe replication code in repl_failsafe.cc which is disabled until
> - it is reworked. Event's log_pos used to be preserved through
> - log-slave-updates to make code in repl_failsafe.cc work (this
> - function, SHOW NEW MASTER); but on the other side it caused unexpected
> - values in Exec_Master_Log_Pos in A->B->C replication setup,
> - synchronization problems in master_pos_wait(), ... So we
> - (Dmitri & Guilhem) removed it.
> -
> - So for now this function is broken.
> -*/
> -
> -int translate_master(THD* thd, LEX_MASTER_INFO* mi, char* errmsg)
> -{
> - LOG_INFO linfo;
> - char last_log_name[FN_REFLEN];
> - IO_CACHE log;
> - File file = -1, last_file = -1;
> - mysql_mutex_t *log_lock;
> - const char* errmsg_p;
> - Slave_log_event* sev = 0;
> - my_off_t last_pos = 0;
> - int error = 1;
> - int cmp_res;
> - LINT_INIT(cmp_res);
> - DBUG_ENTER("translate_master");
> -
> - if (!mysql_bin_log.is_open())
> - {
> - strmov(errmsg,"Binary log is not open");
> - DBUG_RETURN(1);
> - }
> -
> - if (!server_id_supplied)
> - {
> - strmov(errmsg, "Misconfigured master - server id was not set");
> - DBUG_RETURN(1);
> - }
> -
> - if (mysql_bin_log.find_log_pos(&linfo, NullS, 1))
> - {
> - strmov(errmsg,"Could not find first log");
> - DBUG_RETURN(1);
> - }
> - thd->current_linfo = &linfo;
> -
> - bzero((char*) &log,sizeof(log));
> - log_lock = mysql_bin_log.get_log_lock();
> - mysql_mutex_lock(log_lock);
> -
> - for (;;)
> - {
> - if ((file=open_binlog(&log, linfo.log_file_name, &errmsg_p)) < 0)
> - {
> - strmov(errmsg, errmsg_p);
> - goto err;
> - }
> -
> - if (!(sev = find_slave_event(&log, linfo.log_file_name, errmsg)))
> - goto err;
> -
> - cmp_res = cmp_master_pos(sev, mi);
> - delete sev;
> -
> - if (!cmp_res)
> - {
> - /* Copy basename */
> - fn_format(mi->log_file_name, linfo.log_file_name, "","",1);
> - mi->pos = my_b_tell(&log);
> - goto mi_inited;
> - }
> - else if (cmp_res > 0)
> - {
> - if (!last_pos)
> - {
> - strmov(errmsg,
> - "Slave event in first log points past the target position");
> - goto err;
> - }
> - end_io_cache(&log);
> - mysql_file_close(file, MYF(MY_WME));
> - if (init_io_cache(&log, (file = last_file), IO_SIZE, READ_CACHE, 0, 0,
> - MYF(MY_WME)))
> - {
> - errmsg[0] = 0;
> - goto err;
> - }
> - break;
> - }
> -
> - strmov(last_log_name, linfo.log_file_name);
> - last_pos = my_b_tell(&log);
> -
> - switch (mysql_bin_log.find_next_log(&linfo, 1)) {
> - case LOG_INFO_EOF:
> - if (last_file >= 0)
> - mysql_file_close(last_file, MYF(MY_WME));
> - last_file = -1;
> - goto found_log;
> - case 0:
> - break;
> - default:
> - strmov(errmsg, "Error reading log index");
> - goto err;
> - }
> -
> - end_io_cache(&log);
> - if (last_file >= 0)
> - mysql_file_close(last_file, MYF(MY_WME));
> - last_file = file;
> - }
> -
> -found_log:
> - my_b_seek(&log, last_pos);
> - if (find_target_pos(mi,&log,errmsg))
> - goto err;
> - fn_format(mi->log_file_name, last_log_name, "","",1); /* Copy basename */
> -
> -mi_inited:
> - error = 0;
> -err:
> - mysql_mutex_unlock(log_lock);
> - end_io_cache(&log);
> - mysql_mutex_lock(&LOCK_thread_count);
> - thd->current_linfo = 0;
> - mysql_mutex_unlock(&LOCK_thread_count);
> - if (file >= 0)
> - mysql_file_close(file, MYF(MY_WME));
> - if (last_file >= 0 && last_file != file)
> - mysql_file_close(last_file, MYF(MY_WME));
> -
> - DBUG_RETURN(error);
> -}
> -
> -
> -/**
> - Caller must delete result when done.
> -*/
> -
> -static Slave_log_event* find_slave_event(IO_CACHE* log,
> - const char* log_file_name,
> - char* errmsg)
> -{
> - Log_event* ev;
> - int i;
> - bool slave_event_found = 0;
> - LINT_INIT(ev);
> -
> - for (i = 0; i < 2; i++)
> - {
> - if (!(ev= Log_event::read_log_event(log, (mysql_mutex_t*)0, 0)))
> - {
> - my_snprintf(errmsg, SLAVE_ERRMSG_SIZE,
> - "Error reading event in log '%s'",
> - (char*)log_file_name);
> - return 0;
> - }
> - if (ev->get_type_code() == SLAVE_EVENT)
> - {
> - slave_event_found = 1;
> - break;
> - }
> - delete ev;
> - }
> - if (!slave_event_found)
> - {
> - my_snprintf(errmsg, SLAVE_ERRMSG_SIZE,
> - "Could not find slave event in log '%s'",
> - (char*)log_file_name);
> - return 0;
> - }
> -
> - return (Slave_log_event*)ev;
> -}
> -
> -/**
> - This function is broken now.
> -
> - @seealso translate_master()
> -*/
> -
> -bool show_new_master(THD* thd)
> -{
> - Protocol *protocol= thd->protocol;
> - DBUG_ENTER("show_new_master");
> - List<Item> field_list;
> - char errmsg[SLAVE_ERRMSG_SIZE];
> - LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
> -
> - errmsg[0]=0; // Safety
> - if (translate_master(thd, lex_mi, errmsg))
> - {
> - if (errmsg[0])
> - my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0),
> - "SHOW NEW MASTER", errmsg);
> - DBUG_RETURN(TRUE);
> - }
> - else
> - {
> - field_list.push_back(new Item_empty_string("Log_name", 20));
> - field_list.push_back(new Item_return_int("Log_pos", 10,
> - MYSQL_TYPE_LONGLONG));
> - if (protocol->send_result_set_metadata(&field_list,
> - Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
> - DBUG_RETURN(TRUE);
> - protocol->prepare_for_resend();
> - protocol->store(lex_mi->log_file_name, &my_charset_bin);
> - protocol->store((ulonglong) lex_mi->pos);
> - if (protocol->write())
> - DBUG_RETURN(TRUE);
> - my_eof(thd);
> - DBUG_RETURN(FALSE);
> - }
> -}
> -
> -/**
> - Asks the master for the list of its other connected slaves.
> -
> - This is for failsafe replication:
> - in order for failsafe replication to work, the servers involved in
> - replication must know of each other. We accomplish this by having each
> - slave report to the master how to reach it, and on connection, each
> - slave receives information about where the other slaves are.
> -
> - @param mysql pre-existing connection to the master
> - @param mi master info
> -
> - @note
> - mi is used only to give detailed error messages which include the
> - hostname/port of the master, the username used by the slave to connect to
> - the master.
> - If the user used by the slave to connect to the master does not have the
> - REPLICATION SLAVE privilege, it will pop in this function because
> - SHOW SLAVE HOSTS will fail on the master.
> -
> - @retval
> - 1 error
> - @retval
> - 0 success
> -*/
> -
> -int update_slave_list(MYSQL* mysql, Master_info* mi)
> -{
> - MYSQL_RES* res=0;
> - MYSQL_ROW row;
> - const char* error=0;
> - bool have_auth_info;
> - int port_ind;
> - DBUG_ENTER("update_slave_list");
> -
> - if (mysql_real_query(mysql, STRING_WITH_LEN("SHOW SLAVE HOSTS")) ||
> - !(res = mysql_store_result(mysql)))
> - {
> - error= mysql_error(mysql);
> - goto err;
> - }
> -
> - switch (mysql_num_fields(res)) {
> - case 5:
> - have_auth_info = 0;
> - port_ind=2;
> - break;
> - case 7:
> - have_auth_info = 1;
> - port_ind=4;
> - break;
> - default:
> - error= "the master returned an invalid number of fields for SHOW SLAVE \
> -HOSTS";
> - goto err;
> - }
> -
> - mysql_mutex_lock(&LOCK_slave_list);
> -
> - while ((row= mysql_fetch_row(res)))
> - {
> - uint32 log_server_id;
> - SLAVE_INFO* si, *old_si;
> - log_server_id = atoi(row[0]);
> - if ((old_si= (SLAVE_INFO*)my_hash_search(&slave_list,
> - (uchar*)&log_server_id,4)))
> - si = old_si;
> - else
> - {
> - if (!(si = (SLAVE_INFO*)my_malloc(sizeof(SLAVE_INFO), MYF(MY_WME))))
> - {
> - error= "the slave is out of memory";
> - mysql_mutex_unlock(&LOCK_slave_list);
> - goto err;
> - }
> - si->server_id = log_server_id;
> - if (my_hash_insert(&slave_list, (uchar*)si))
> - {
> - error= "the slave is out of memory";
> - mysql_mutex_unlock(&LOCK_slave_list);
> - goto err;
> - }
> - }
> - strmake(si->host, row[1], sizeof(si->host)-1);
> - si->port = atoi(row[port_ind]);
> - si->rpl_recovery_rank = atoi(row[port_ind+1]);
> - si->master_id = atoi(row[port_ind+2]);
> - if (have_auth_info)
> - {
> - strmake(si->user, row[2], sizeof(si->user)-1);
> - strmake(si->password, row[3], sizeof(si->password)-1);
> - }
> - }
> - mysql_mutex_unlock(&LOCK_slave_list);
> -
> -err:
> - if (res)
> - mysql_free_result(res);
> - if (error)
> - {
> - sql_print_error("While trying to obtain the list of slaves from the master "
> - "'%s:%d', user '%s' got the following error: '%s'",
> - mi->host, mi->port, mi->user, error);
> - DBUG_RETURN(1);
> - }
> - DBUG_RETURN(0);
> -}
> -
> -
> -#if NOT_USED
> -int find_recovery_captain(THD* thd, MYSQL* mysql)
> -{
> - return 0;
> -}
> -#endif
> -
> -#if NOT_USED
> -pthread_handler_t handle_failsafe_rpl(void *arg)
> -{
> - DBUG_ENTER("handle_failsafe_rpl");
> - THD *thd = new THD;
> - thd->thread_stack = (char*)&thd;
> - MYSQL* recovery_captain = 0;
> - const char* msg;
> -
> - pthread_detach_this_thread();
> - if (init_failsafe_rpl_thread(thd) || !(recovery_captain=mysql_init(0)))
> - {
> - sql_print_error("Could not initialize failsafe replication thread");
> - goto err;
> - }
> - mysql_mutex_lock(&LOCK_rpl_status);
> - msg= thd->enter_cond(&COND_rpl_status,
> - &LOCK_rpl_status, "Waiting for request");
> - while (!thd->killed && !abort_loop)
> - {
> - bool break_req_chain = 0;
> - mysql_cond_wait(&COND_rpl_status, &LOCK_rpl_status);
> - thd_proc_info(thd, "Processing request");
> - while (!break_req_chain)
> - {
> - switch (rpl_status) {
> - case RPL_LOST_SOLDIER:
> - if (find_recovery_captain(thd, recovery_captain))
> - rpl_status=RPL_TROOP_SOLDIER;
> - else
> - rpl_status=RPL_RECOVERY_CAPTAIN;
> - break_req_chain=1; /* for now until other states are implemented */
> - break;
> - default:
> - break_req_chain=1;
> - break;
> - }
> - }
> - }
> - thd->exit_cond(msg);
> -err:
> - if (recovery_captain)
> - mysql_close(recovery_captain);
> - delete thd;
> -
> - DBUG_LEAVE; // Must match DBUG_ENTER()
> - my_thread_end();
> - pthread_exit(0);
> - return 0; // Avoid compiler warnings
> -}
> -#endif
> -
> -
> -/**
> - Execute a SHOW SLAVE HOSTS statement.
> -
> - @param thd Pointer to THD object for the client thread executing the
> - statement.
> -
> - @retval FALSE success
> - @retval TRUE failure
> -*/
> -bool show_slave_hosts(THD* thd)
> -{
> - List<Item> field_list;
> - Protocol *protocol= thd->protocol;
> - DBUG_ENTER("show_slave_hosts");
> -
> - field_list.push_back(new Item_return_int("Server_id", 10,
> - MYSQL_TYPE_LONG));
> - field_list.push_back(new Item_empty_string("Host", 20));
> - if (opt_show_slave_auth_info)
> - {
> - field_list.push_back(new Item_empty_string("User",20));
> - field_list.push_back(new Item_empty_string("Password",20));
> - }
> - field_list.push_back(new Item_return_int("Port", 7, MYSQL_TYPE_LONG));
> - field_list.push_back(new Item_return_int("Master_id", 10,
> - MYSQL_TYPE_LONG));
> -
> - if (protocol->send_result_set_metadata(&field_list,
> - Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
> - DBUG_RETURN(TRUE);
> -
> - mysql_mutex_lock(&LOCK_slave_list);
> -
> - for (uint i = 0; i < slave_list.records; ++i)
> - {
> - SLAVE_INFO* si = (SLAVE_INFO*) my_hash_element(&slave_list, i);
> - protocol->prepare_for_resend();
> - protocol->store((uint32) si->server_id);
> - protocol->store(si->host, &my_charset_bin);
> - if (opt_show_slave_auth_info)
> - {
> - protocol->store(si->user, &my_charset_bin);
> - protocol->store(si->password, &my_charset_bin);
> - }
> - protocol->store((uint32) si->port);
> - protocol->store((uint32) si->master_id);
> - if (protocol->write())
> - {
> - mysql_mutex_unlock(&LOCK_slave_list);
> - DBUG_RETURN(TRUE);
> - }
> - }
> - mysql_mutex_unlock(&LOCK_slave_list);
> - my_eof(thd);
> - DBUG_RETURN(FALSE);
> -}
> -
> -#endif /* HAVE_REPLICATION */
> -
>
> === removed file 'sql/repl_failsafe.h'
> --- a/sql/repl_failsafe.h 2010-01-25 21:34:34 +0000
> +++ b/sql/repl_failsafe.h 1970-01-01 00:00:00 +0000
> @@ -1,55 +0,0 @@
> -#ifndef REPL_FAILSAFE_INCLUDED
> -#define REPL_FAILSAFE_INCLUDED
> -
> -/* Copyright (C) 2001-2005 MySQL AB & Sasha, 2008-2009 Sun Microsystems, Inc
> -
> - 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 */
> -
> -#ifdef HAVE_REPLICATION
> -
> -#include "mysql.h"
> -#include "my_sys.h"
> -#include "slave.h"
> -
> -typedef enum {RPL_AUTH_MASTER=0,RPL_IDLE_SLAVE,RPL_ACTIVE_SLAVE,
> - RPL_LOST_SOLDIER,RPL_TROOP_SOLDIER,
> - RPL_RECOVERY_CAPTAIN,RPL_NULL /* inactive */,
> - RPL_ANY /* wild card used by change_rpl_status */ } RPL_STATUS;
> -extern RPL_STATUS rpl_status;
> -
> -extern mysql_mutex_t LOCK_rpl_status;
> -extern mysql_cond_t COND_rpl_status;
> -extern TYPELIB rpl_role_typelib, rpl_status_typelib;
> -extern const char* rpl_role_type[], *rpl_status_type[];
> -
> -pthread_handler_t handle_failsafe_rpl(void *arg);
> -void change_rpl_status(RPL_STATUS from_status, RPL_STATUS to_status);
> -int find_recovery_captain(THD* thd, MYSQL* mysql);
> -int update_slave_list(MYSQL* mysql, Master_info* mi);
> -
> -extern HASH slave_list;
> -
> -bool load_master_data(THD* thd);
> -int connect_to_master(THD *thd, MYSQL* mysql, Master_info* mi);
> -
> -bool show_new_master(THD* thd);
> -bool show_slave_hosts(THD* thd);
> -int translate_master(THD* thd, LEX_MASTER_INFO* mi, char* errmsg);
> -void init_slave_list();
> -void end_slave_list();
> -int register_slave(THD* thd, uchar* packet, uint packet_length);
> -void unregister_slave(THD* thd, bool only_mine, bool need_mutex);
> -
> -#endif /* HAVE_REPLICATION */
> -#endif /* REPL_FAILSAFE_INCLUDED */
>
> === modified file 'sql/rpl_handler.cc'
> --- a/sql/rpl_handler.cc 2010-03-31 14:05:33 +0000
> +++ b/sql/rpl_handler.cc 2010-06-21 05:56:56 +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_injector.cc'
> --- a/sql/rpl_injector.cc 2010-04-01 19:34:09 +0000
> +++ b/sql/rpl_injector.cc 2010-06-21 06:01:50 +0000
> @@ -20,6 +20,7 @@
> #include "sql_parse.h" // begin_trans, end_trans, COMMIT
> #include "sql_base.h" // close_thread_tables
> #include "log_event.h" // Incident_log_event
> +#include "binlog.h" // mysql_bin_log
>
> /*
> injector::transaction - member definitions
>
> === added file 'sql/rpl_master.cc'
> --- a/sql/rpl_master.cc 1970-01-01 00:00:00 +0000
> +++ b/sql/rpl_master.cc 2010-06-21 05:56:56 +0000
> @@ -0,0 +1,1267 @@
> +/* 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 */
> +
> +
> +#include "sql_priv.h"
> +#include "unireg.h"
> +#include "sql_parse.h" // check_access
> +#ifdef HAVE_REPLICATION
> +
> +#include "sql_acl.h" // SUPER_ACL
> +#include "log_event.h"
> +#include "rpl_filter.h"
> +#include <my_dir.h>
> +#include "rpl_handler.h"
> +#include "rpl_master.h"
> +
> +int max_binlog_dump_events = 0; // unlimited
> +my_bool opt_sporadic_binlog_dump_fail = 0;
> +extern my_bool opt_show_slave_auth_info;
> +
> +#ifndef DBUG_OFF
> +static int binlog_dump_count = 0;
> +#endif
> +
> +#define SLAVE_LIST_CHUNK 128
> +#define SLAVE_ERRMSG_SIZE (FN_REFLEN+64)
> +HASH slave_list;
> +
> +#define get_object(p, obj, msg) \
> +{\
> + uint len = (uint)*p++; \
> + if (p + len > p_end || len >= sizeof(obj)) \
> + {\
> + errmsg= msg;\
> + goto err; \
> + }\
> + strmake(obj,(char*) p,len); \
> + p+= len; \
> +}\
> +
> +typedef struct st_slave_info
> +{
> + uint32 server_id;
> + uint32 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 "C" uint32
> +*slave_list_key(SLAVE_INFO* si, size_t *len,
> + my_bool not_used __attribute__((unused)))
> +{
> + *len = 4;
> + return &si->server_id;
> +}
> +
> +extern "C" void slave_info_free(void *s)
> +{
> + my_free(s, MYF(MY_WME));
> +}
> +
> +#ifdef HAVE_PSI_INTERFACE
> +static PSI_mutex_key key_LOCK_slave_list;
> +
> +static PSI_mutex_info all_slave_list_mutexes[]=
> +{
> + { &key_LOCK_slave_list, "LOCK_slave_list", PSI_FLAG_GLOBAL}
> +};
> +
> +static void init_all_slave_list_mutexes(void)
> +{
> + const char* category= "sql";
> + int count;
> +
> + if (PSI_server == NULL)
> + return;
> +
> + count= array_elements(all_slave_list_mutexes);
> + PSI_server->register_mutex(category, all_slave_list_mutexes, count);
> +}
> +#endif /* HAVE_PSI_INTERFACE */
> +
> +void init_slave_list()
> +{
> +#ifdef HAVE_PSI_INTERFACE
> + init_all_slave_list_mutexes();
> +#endif
> +
> + my_hash_init(&slave_list, system_charset_info, SLAVE_LIST_CHUNK, 0, 0,
> + (my_hash_get_key) slave_list_key,
> + (my_hash_free_key) slave_info_free, 0);
> + mysql_mutex_init(key_LOCK_slave_list, &LOCK_slave_list, MY_MUTEX_INIT_FAST);
> +}
> +
> +void end_slave_list()
> +{
> + /* No protection by a mutex needed as we are only called at shutdown */
> + if (my_hash_inited(&slave_list))
> + {
> + my_hash_free(&slave_list);
> + mysql_mutex_destroy(&LOCK_slave_list);
> + }
> +}
> +
> +/**
> + Register slave in 'slave_list' hash table.
> +
> + @return
> + 0 ok
> + @return
> + 1 Error. Error message sent to client
> +*/
> +
> +int register_slave(THD* thd, uchar* packet, uint packet_length)
> +{
> + int res;
> + SLAVE_INFO *si;
> + uchar *p= packet, *p_end= packet + packet_length;
> + const char *errmsg= "Wrong parameters to function register_slave";
> +
> + if (check_access(thd, REPL_SLAVE_ACL, any_db, NULL, NULL, 0, 0))
> + return 1;
> + if (!(si = (SLAVE_INFO*)my_malloc(sizeof(SLAVE_INFO), MYF(MY_WME))))
> + goto err2;
> +
> + thd->server_id= si->server_id= uint4korr(p);
> + p+= 4;
> + get_object(p,si->host, "Failed to register slave: too long 'report-host'");
> + get_object(p,si->user, "Failed to register slave: too long 'report-user'");
> + get_object(p,si->password, "Failed to register slave; too long
> 'report-password'");
> + if (p+10 > p_end)
> + goto err;
> + si->port= uint2korr(p);
> + p += 2;
> + /*
> + We need to by pass the bytes used in the fake rpl_recovery_rank
> + variable. It was removed in patch for BUG#13963. But this would
> + make a server with that patch unable to connect to an old master.
> + See: BUG#49259
> + */
> + p += 4;
> + if (!(si->master_id= uint4korr(p)))
> + si->master_id= server_id;
> + si->thd= thd;
> +
> + mysql_mutex_lock(&LOCK_slave_list);
> + unregister_slave(thd,0,0);
> + res= my_hash_insert(&slave_list, (uchar*) si);
> + mysql_mutex_unlock(&LOCK_slave_list);
> + return res;
> +
> +err:
> + my_free(si, MYF(MY_WME));
> + my_message(ER_UNKNOWN_ERROR, errmsg, MYF(0)); /* purecov: inspected */
> +err2:
> + return 1;
> +}
> +
> +void unregister_slave(THD* thd, bool only_mine, bool need_mutex)
> +{
> + if (thd->server_id)
> + {
> + if (need_mutex)
> + mysql_mutex_lock(&LOCK_slave_list);
> +
> + SLAVE_INFO* old_si;
> + if ((old_si = (SLAVE_INFO*)my_hash_search(&slave_list,
> + (uchar*)&thd->server_id, 4))
> &&
> + (!only_mine || old_si->thd == thd))
> + my_hash_delete(&slave_list, (uchar*)old_si);
> +
> + if (need_mutex)
> + mysql_mutex_unlock(&LOCK_slave_list);
> + }
> +}
> +
> +
> +/**
> + Execute a SHOW SLAVE HOSTS statement.
> +
> + @param thd Pointer to THD object for the client thread executing the
> + statement.
> +
> + @retval FALSE success
> + @retval TRUE failure
> +*/
> +bool show_slave_hosts(THD* thd)
> +{
> + List<Item> field_list;
> + Protocol *protocol= thd->protocol;
> + DBUG_ENTER("show_slave_hosts");
> +
> + field_list.push_back(new Item_return_int("Server_id", 10,
> + MYSQL_TYPE_LONG));
> + field_list.push_back(new Item_empty_string("Host", 20));
> + if (opt_show_slave_auth_info)
> + {
> + field_list.push_back(new Item_empty_string("User",20));
> + field_list.push_back(new Item_empty_string("Password",20));
> + }
> + field_list.push_back(new Item_return_int("Port", 7, MYSQL_TYPE_LONG));
> + field_list.push_back(new Item_return_int("Master_id", 10,
> + MYSQL_TYPE_LONG));
> +
> + if (protocol->send_result_set_metadata(&field_list,
> + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
> + DBUG_RETURN(TRUE);
> +
> + mysql_mutex_lock(&LOCK_slave_list);
> +
> + for (uint i = 0; i < slave_list.records; ++i)
> + {
> + SLAVE_INFO* si = (SLAVE_INFO*) my_hash_element(&slave_list, i);
> + protocol->prepare_for_resend();
> + protocol->store((uint32) si->server_id);
> + protocol->store(si->host, &my_charset_bin);
> + if (opt_show_slave_auth_info)
> + {
> + protocol->store(si->user, &my_charset_bin);
> + protocol->store(si->password, &my_charset_bin);
> + }
> + protocol->store((uint32) si->port);
> + protocol->store((uint32) si->master_id);
> + if (protocol->write())
> + {
> + mysql_mutex_unlock(&LOCK_slave_list);
> + DBUG_RETURN(TRUE);
> + }
> + }
> + mysql_mutex_unlock(&LOCK_slave_list);
> + my_eof(thd);
> + DBUG_RETURN(FALSE);
> +}
> +
> +
> +/*
> + fake_rotate_event() builds a fake (=which does not exist physically in any
> + binlog) Rotate event, which contains the name of the binlog we are going to
> + send to the slave (because the slave may not know it if it just asked for
> + MASTER_LOG_FILE='', MASTER_LOG_POS=4).
> + < 4.0.14, fake_rotate_event() was called only if the requested pos was 4.
> + After this version we always call it, so that a 3.23.58 slave can rely on
> + it to detect if the master is 4.0 (and stop) (the _fake_ Rotate event has
> + zeros in the good positions which, by chance, make it possible for the 3.23
> + slave to detect that this event is unexpected) (this is luck which happens
> + because the master and slave disagree on the size of the header of
> + Log_event).
> +
> + Relying on the event length of the Rotate event instead of these
> + well-placed zeros was not possible as Rotate events have a variable-length
> + part.
> +*/
> +
> +static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
> + ulonglong position, const char** errmsg)
> +{
> + DBUG_ENTER("fake_rotate_event");
> + char header[LOG_EVENT_HEADER_LEN], buf[ROTATE_HEADER_LEN+100];
> + /*
> + 'when' (the timestamp) is set to 0 so that slave could distinguish between
> + real and fake Rotate events (if necessary)
> + */
> + memset(header, 0, 4);
> + header[EVENT_TYPE_OFFSET] = ROTATE_EVENT;
> +
> + char* p = log_file_name+dirname_length(log_file_name);
> + uint ident_len = (uint) strlen(p);
> + ulong event_len = ident_len + LOG_EVENT_HEADER_LEN + ROTATE_HEADER_LEN;
> + int4store(header + SERVER_ID_OFFSET, server_id);
> + int4store(header + EVENT_LEN_OFFSET, event_len);
> + int2store(header + FLAGS_OFFSET, LOG_EVENT_ARTIFICIAL_F);
> +
> + // TODO: check what problems this may cause and fix them
> + int4store(header + LOG_POS_OFFSET, 0);
> +
> + packet->append(header, sizeof(header));
> + int8store(buf+R_POS_OFFSET,position);
> + packet->append(buf, ROTATE_HEADER_LEN);
> + packet->append(p,ident_len);
> + if (my_net_write(net, (uchar*) packet->ptr(), packet->length()))
> + {
> + *errmsg = "failed on my_net_write()";
> + DBUG_RETURN(-1);
> + }
> + DBUG_RETURN(0);
> +}
> +
> +/*
> + Reset thread transmit packet buffer for event sending
> +
> + This function allocates header bytes for event transmission, and
> + should be called before store the event data to the packet buffer.
> +*/
> +static int reset_transmit_packet(THD *thd, ushort flags,
> + ulong *ev_offset, const char **errmsg)
> +{
> + int ret= 0;
> + String *packet= &thd->packet;
> +
> + /* reserve and set default header */
> + packet->length(0);
> + packet->set("\0", 1, &my_charset_bin);
> +
> + if (RUN_HOOK(binlog_transmit, reserve_header, (thd, flags, packet)))
> + {
> + *errmsg= "Failed to run hook 'reserve_header'";
> + my_errno= ER_UNKNOWN_ERROR;
> + ret= 1;
> + }
> + *ev_offset= packet->length();
> + return ret;
> +}
> +
> +static int send_file(THD *thd)
> +{
> + NET* net = &thd->net;
> + int fd = -1, error = 1;
> + size_t bytes;
> + char fname[FN_REFLEN+1];
> + const char *errmsg = 0;
> + int old_timeout;
> + unsigned long packet_len;
> + uchar buf[IO_SIZE]; // It's safe to alloc this
> + DBUG_ENTER("send_file");
> +
> + /*
> + The client might be slow loading the data, give him wait_timeout to do
> + the job
> + */
> + old_timeout= net->read_timeout;
> + my_net_set_read_timeout(net, thd->variables.net_wait_timeout);
> +
> + /*
> + We need net_flush here because the client will not know it needs to send
> + us the file name until it has processed the load event entry
> + */
> + if (net_flush(net) || (packet_len = my_net_read(net)) == packet_error)
> + {
> + errmsg = "while reading file name";
> + goto err;
> + }
> +
> + // terminate with \0 for fn_format
> + *((char*)net->read_pos + packet_len) = 0;
> + fn_format(fname, (char*) net->read_pos + 1, "", "", 4);
> + // this is needed to make replicate-ignore-db
> + if (!strcmp(fname,"/dev/null"))
> + goto end;
> +
> + if ((fd= mysql_file_open(key_file_send_file,
> + fname, O_RDONLY, MYF(0))) < 0)
> + {
> + errmsg = "on open of file";
> + goto err;
> + }
> +
> + while ((long) (bytes= mysql_file_read(fd, buf, IO_SIZE, MYF(0))) > 0)
> + {
> + if (my_net_write(net, buf, bytes))
> + {
> + errmsg = "while writing data to client";
> + goto err;
> + }
> + }
> +
> + end:
> + if (my_net_write(net, (uchar*) "", 0) || net_flush(net) ||
> + (my_net_read(net) == packet_error))
> + {
> + errmsg = "while negotiating file transfer close";
> + goto err;
> + }
> + error = 0;
> +
> + err:
> + my_net_set_read_timeout(net, old_timeout);
> + if (fd >= 0)
> + mysql_file_close(fd, MYF(0));
> + if (errmsg)
> + {
> + sql_print_error("Failed in send_file() %s", errmsg);
> + DBUG_PRINT("error", ("%s", errmsg));
> + }
> + DBUG_RETURN(error);
> +}
> +
> +
> +int test_for_non_eof_log_read_errors(int error, const char **errmsg)
> +{
> + if (error == LOG_READ_EOF)
> + return 0;
> + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
> + switch (error) {
> + case LOG_READ_BOGUS:
> + *errmsg = "bogus data in log event";
> + break;
> + case LOG_READ_TOO_LARGE:
> + *errmsg = "log event entry exceeded max_allowed_packet; \
> +Increase max_allowed_packet on master";
> + break;
> + case LOG_READ_IO:
> + *errmsg = "I/O error reading log event";
> + break;
> + case LOG_READ_MEM:
> + *errmsg = "memory allocation failed reading log event";
> + break;
> + case LOG_READ_TRUNC:
> + *errmsg = "binlog truncated in the middle of event";
> + break;
> + default:
> + *errmsg = "unknown error reading log event on the master";
> + break;
> + }
> + return error;
> +}
> +
> +
> +/**
> + An auxiliary function for calling in mysql_binlog_send
> + to initialize the heartbeat timeout in waiting for a binlogged event.
> +
> + @param[in] thd THD to access a user variable
> +
> + @return heartbeat period an ulonglong of nanoseconds
> + or zero if heartbeat was not demanded by slave
> +*/
> +static ulonglong get_heartbeat_period(THD * thd)
> +{
> + my_bool null_value;
> + LEX_STRING name= { C_STRING_WITH_LEN("master_heartbeat_period")};
> + user_var_entry *entry=
> + (user_var_entry*) my_hash_search(&thd->user_vars, (uchar*) name.str,
> + name.length);
> + return entry? entry->val_int(&null_value) : 0;
> +}
> +
> +/*
> + Function prepares and sends repliation heartbeat event.
> +
> + @param net net object of THD
> + @param packet buffer to store the heartbeat instance
> + @param event_coordinates binlog file name and position of the last
> + real event master sent from binlog
> +
> + @note
> + Among three essential pieces of heartbeat data Log_event::when
> + is computed locally.
> + The error to send is serious and should force terminating
> + the dump thread.
> +*/
> +static int send_heartbeat_event(NET* net, String* packet,
> + const struct event_coordinates *coord)
> +{
> + DBUG_ENTER("send_heartbeat_event");
> + char header[LOG_EVENT_HEADER_LEN];
> + /*
> + 'when' (the timestamp) is set to 0 so that slave could distinguish between
> + real and fake Rotate events (if necessary)
> + */
> + memset(header, 0, 4); // when
> +
> + header[EVENT_TYPE_OFFSET] = HEARTBEAT_LOG_EVENT;
> +
> + char* p= coord->file_name + dirname_length(coord->file_name);
> +
> + uint ident_len = strlen(p);
> + ulong event_len = ident_len + LOG_EVENT_HEADER_LEN;
> + int4store(header + SERVER_ID_OFFSET, server_id);
> + int4store(header + EVENT_LEN_OFFSET, event_len);
> + int2store(header + FLAGS_OFFSET, 0);
> +
> + int4store(header + LOG_POS_OFFSET, coord->pos); // log_pos
> +
> + packet->append(header, sizeof(header));
> + packet->append(p, ident_len); // log_file_name
> +
> + if (my_net_write(net, (uchar*) packet->ptr(), packet->length()) ||
> + net_flush(net))
> + {
> + DBUG_RETURN(-1);
> + }
> + DBUG_RETURN(0);
> +}
> +
> +/*
> + TODO: Clean up loop to only have one call to send_file()
> +*/
> +
> +void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
> + ushort flags)
> +{
> + LOG_INFO linfo;
> + char *log_file_name = linfo.log_file_name;
> + char search_file_name[FN_REFLEN], *name;
> +
> + ulong ev_offset;
> +
> + IO_CACHE log;
> + File file = -1;
> + String* packet = &thd->packet;
> + int error;
> + const char *errmsg = "Unknown error";
> + NET* net = &thd->net;
> + mysql_mutex_t *log_lock;
> + bool binlog_can_be_corrupted= FALSE;
> +#ifndef DBUG_OFF
> + int left_events = max_binlog_dump_events;
> +#endif
> + DBUG_ENTER("mysql_binlog_send");
> + DBUG_PRINT("enter",("log_ident: '%s' pos: %ld", log_ident, (long) pos));
> +
> + bzero((char*) &log,sizeof(log));
> + /*
> + heartbeat_period from @master_heartbeat_period user variable
> + */
> + ulonglong heartbeat_period= get_heartbeat_period(thd);
> + struct timespec heartbeat_buf;
> + struct event_coordinates coord_buf;
> + struct timespec *heartbeat_ts= NULL;
> + struct event_coordinates *coord= NULL;
> + if (heartbeat_period != LL(0))
> + {
> + heartbeat_ts= &heartbeat_buf;
> + set_timespec_nsec(*heartbeat_ts, 0);
> + coord= &coord_buf;
> + coord->file_name= log_file_name; // initialization basing on what slave
> remembers
> + coord->pos= pos;
> + }
> + sql_print_information("Start binlog_dump to slave_server(%d), pos(%s, %lu)",
> + thd->server_id, log_ident, (ulong)pos);
> + if (RUN_HOOK(binlog_transmit, transmit_start, (thd, flags, log_ident, pos)))
> + {
> + errmsg= "Failed to run hook 'transmit_start'";
> + my_errno= ER_UNKNOWN_ERROR;
> + goto err;
> + }
> +
> +#ifndef DBUG_OFF
> + if (opt_sporadic_binlog_dump_fail && (binlog_dump_count++ % 2))
> + {
> + errmsg = "Master failed COM_BINLOG_DUMP to test if slave can recover";
> + my_errno= ER_UNKNOWN_ERROR;
> + goto err;
> + }
> +#endif
> +
> + if (!mysql_bin_log.is_open())
> + {
> + errmsg = "Binary log is not open";
> + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
> + goto err;
> + }
> + if (!server_id_supplied)
> + {
> + errmsg = "Misconfigured master - server id was not set";
> + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
> + goto err;
> + }
> +
> + name=search_file_name;
> + if (log_ident[0])
> + mysql_bin_log.make_log_name(search_file_name, log_ident);
> + else
> + name=0; // Find first log
> +
> + linfo.index_file_offset = 0;
> +
> + if (mysql_bin_log.find_log_pos(&linfo, name, 1))
> + {
> + errmsg = "Could not find first log file name in binary log index file";
> + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
> + goto err;
> + }
> +
> + mysql_mutex_lock(&LOCK_thread_count);
> + thd->current_linfo = &linfo;
> + mysql_mutex_unlock(&LOCK_thread_count);
> +
> + if ((file=open_binlog(&log, log_file_name, &errmsg)) < 0)
> + {
> + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
> + goto err;
> + }
> + if (pos < BIN_LOG_HEADER_SIZE || pos > my_b_filelength(&log))
> + {
> + errmsg= "Client requested master to start replication from \
> +impossible position";
> + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
> + goto err;
> + }
> +
> + /* reset transmit packet for the fake rotate event below */
> + if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
> + goto err;
> +
> + /*
> + Tell the client about the log name with a fake Rotate event;
> + this is needed even if we also send a Format_description_log_event
> + just after, because that event does not contain the binlog's name.
> + Note that as this Rotate event is sent before
> + Format_description_log_event, the slave cannot have any info to
> + understand this event's format, so the header len of
> + Rotate_log_event is FROZEN (so in 5.0 it will have a header shorter
> + than other events except FORMAT_DESCRIPTION_EVENT).
> + Before 4.0.14 we called fake_rotate_event below only if (pos ==
> + BIN_LOG_HEADER_SIZE), because if this is false then the slave
> + already knows the binlog's name.
> + Since, we always call fake_rotate_event; if the slave already knew
> + the log's name (ex: CHANGE MASTER TO MASTER_LOG_FILE=...) this is
> + useless but does not harm much. It is nice for 3.23 (>=.58) slaves
> + which test Rotate events to see if the master is 4.0 (then they
> + choose to stop because they can't replicate 4.0); by always calling
> + fake_rotate_event we are sure that 3.23.58 and newer will detect the
> + problem as soon as replication starts (BUG#198).
> + Always calling fake_rotate_event makes sending of normal
> + (=from-binlog) Rotate events a priori unneeded, but it is not so
> + simple: the 2 Rotate events are not equivalent, the normal one is
> + before the Stop event, the fake one is after. If we don't send the
> + normal one, then the Stop event will be interpreted (by existing 4.0
> + slaves) as "the master stopped", which is wrong. So for safety,
> + given that we want minimum modification of 4.0, we send the normal
> + and fake Rotates.
> + */
> + if (fake_rotate_event(net, packet, log_file_name, pos, &errmsg))
> + {
> + /*
> + This error code is not perfect, as fake_rotate_event() does not
> + read anything from the binlog; if it fails it's because of an
> + error in my_net_write(), fortunately it will say so in errmsg.
> + */
> + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
> + goto err;
> + }
> +
> + /*
> + Adding MAX_LOG_EVENT_HEADER_LEN, since a binlog event can become
> + this larger than the corresponding packet (query) sent
> + from client to master.
> + */
> + thd->variables.max_allowed_packet+= MAX_LOG_EVENT_HEADER;
> +
> + /*
> + We can set log_lock now, it does not move (it's a member of
> + mysql_bin_log, and it's already inited, and it will be destroyed
> + only at shutdown).
> + */
> + log_lock = mysql_bin_log.get_log_lock();
> + if (pos > BIN_LOG_HEADER_SIZE)
> + {
> + /* reset transmit packet for the event read from binary log
> + file */
> + if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
> + goto err;
> +
> + /*
> + Try to find a Format_description_log_event at the beginning of
> + the binlog
> + */
> + if (!(error = Log_event::read_log_event(&log, packet, log_lock)))
> + {
> + /*
> + The packet has offsets equal to the normal offsets in a
> + binlog event + ev_offset (the first ev_offset characters are
> + the header (default \0)).
> + */
> + DBUG_PRINT("info",
> + ("Looked for a Format_description_log_event, found event type
> %d",
> + (*packet)[EVENT_TYPE_OFFSET+ev_offset]));
> + if ((*packet)[EVENT_TYPE_OFFSET+ev_offset] == FORMAT_DESCRIPTION_EVENT)
> + {
> + binlog_can_be_corrupted= test((*packet)[FLAGS_OFFSET+ev_offset] &
> + LOG_EVENT_BINLOG_IN_USE_F);
> + (*packet)[FLAGS_OFFSET+ev_offset] &= ~LOG_EVENT_BINLOG_IN_USE_F;
> + /*
> + mark that this event with "log_pos=0", so the slave
> + should not increment master's binlog position
> + (rli->group_master_log_pos)
> + */
> + int4store((char*) packet->ptr()+LOG_POS_OFFSET+ev_offset, 0);
> + /*
> + if reconnect master sends FD event with `created' as 0
> + to avoid destroying temp tables.
> + */
> + int4store((char*) packet->ptr()+LOG_EVENT_MINIMAL_HEADER_LEN+
> + ST_CREATED_OFFSET+ev_offset, (ulong) 0);
> + /* send it */
> + if (my_net_write(net, (uchar*) packet->ptr(), packet->length()))
> + {
> + errmsg = "Failed on my_net_write()";
> + my_errno= ER_UNKNOWN_ERROR;
> + goto err;
> + }
> +
> + /*
> + No need to save this event. We are only doing simple reads
> + (no real parsing of the events) so we don't need it. And so
> + we don't need the artificial Format_description_log_event of
> + 3.23&4.x.
> + */
> + }
> + }
> + else
> + {
> + if (test_for_non_eof_log_read_errors(error, &errmsg))
> + goto err;
> + /*
> + It's EOF, nothing to do, go on reading next events, the
> + Format_description_log_event will be found naturally if it is written.
> + */
> + }
> + } /* end of if (pos > BIN_LOG_HEADER_SIZE); */
> + else
> + {
> + /* The Format_description_log_event event will be found naturally. */
> + }
> +
> + /* seek to the requested position, to start the requested dump */
> + my_b_seek(&log, pos); // Seek will done on next read
> +
> + while (!net->error && net->vio != 0 && !thd->killed)
> + {
> + Log_event_type event_type= UNKNOWN_EVENT;
> +
> + /* reset the transmit packet for the event read from binary log
> + file */
> + if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
> + goto err;
> + while (!(error = Log_event::read_log_event(&log, packet, log_lock)))
> + {
> +#ifndef DBUG_OFF
> + if (max_binlog_dump_events && !left_events--)
> + {
> + net_flush(net);
> + errmsg = "Debugging binlog dump abort";
> + my_errno= ER_UNKNOWN_ERROR;
> + goto err;
> + }
> +#endif
> + /*
> + log's filename does not change while it's active
> + */
> + if (coord)
> + coord->pos= uint4korr(packet->ptr() + ev_offset + LOG_POS_OFFSET);
> +
> + event_type= (Log_event_type)((*packet)[LOG_EVENT_OFFSET+ev_offset]);
> + if (event_type == FORMAT_DESCRIPTION_EVENT)
> + {
> + binlog_can_be_corrupted= test((*packet)[FLAGS_OFFSET+ev_offset] &
> + LOG_EVENT_BINLOG_IN_USE_F);
> + (*packet)[FLAGS_OFFSET+ev_offset] &= ~LOG_EVENT_BINLOG_IN_USE_F;
> + }
> + else if (event_type == STOP_EVENT)
> + binlog_can_be_corrupted= FALSE;
> +
> + pos = my_b_tell(&log);
> + if (RUN_HOOK(binlog_transmit, before_send_event,
> + (thd, flags, packet, log_file_name, pos)))
> + {
> + my_errno= ER_UNKNOWN_ERROR;
> + errmsg= "run 'before_send_event' hook failed";
> + goto err;
> + }
> +
> + if (my_net_write(net, (uchar*) packet->ptr(), packet->length()))
> + {
> + errmsg = "Failed on my_net_write()";
> + my_errno= ER_UNKNOWN_ERROR;
> + goto err;
> + }
> +
> + DBUG_PRINT("info", ("log event code %d", event_type));
> + if (event_type == LOAD_EVENT)
> + {
> + if (send_file(thd))
> + {
> + errmsg = "failed in send_file()";
> + my_errno= ER_UNKNOWN_ERROR;
> + goto err;
> + }
> + }
> +
> + if (RUN_HOOK(binlog_transmit, after_send_event, (thd, flags, packet)))
> + {
> + errmsg= "Failed to run hook 'after_send_event'";
> + my_errno= ER_UNKNOWN_ERROR;
> + goto err;
> + }
> +
> + /* reset transmit packet for next loop */
> + if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
> + goto err;
> + }
> +
> + /*
> + here we were reading binlog that was not closed properly (as a result
> + of a crash ?). treat any corruption as EOF
> + */
> + if (binlog_can_be_corrupted && error != LOG_READ_MEM)
> + error=LOG_READ_EOF;
> + /*
> + TODO: now that we are logging the offset, check to make sure
> + the recorded offset and the actual match.
> + Guilhem 2003-06: this is not true if this master is a slave
> + <4.0.15 running with --log-slave-updates, because then log_pos may
> + be the offset in the-master-of-this-master's binlog.
> + */
> + if (test_for_non_eof_log_read_errors(error, &errmsg))
> + goto err;
> +
> + if (!(flags & BINLOG_DUMP_NON_BLOCK) &&
> + mysql_bin_log.is_active(log_file_name))
> + {
> + /*
> + Block until there is more data in the log
> + */
> + if (net_flush(net))
> + {
> + errmsg = "failed on net_flush()";
> + my_errno= ER_UNKNOWN_ERROR;
> + goto err;
> + }
> +
> + /*
> + We may have missed the update broadcast from the log
> + that has just happened, let's try to catch it if it did.
> + If we did not miss anything, we just wait for other threads
> + to signal us.
> + */
> + {
> + log.error=0;
> + bool read_packet = 0;
> +
> +#ifndef DBUG_OFF
> + if (max_binlog_dump_events && !left_events--)
> + {
> + errmsg = "Debugging binlog dump abort";
> + my_errno= ER_UNKNOWN_ERROR;
> + goto err;
> + }
> +#endif
> +
> + /* reset the transmit packet for the event read from binary log
> + file */
> + if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
> + goto err;
> +
> + /*
> + No one will update the log while we are reading
> + now, but we'll be quick and just read one record
> +
> + TODO:
> + Add an counter that is incremented for each time we update the
> + binary log. We can avoid the following read if the counter
> + has not been updated since last read.
> + */
> +
> + mysql_mutex_lock(log_lock);
> + switch (error= Log_event::read_log_event(&log, packet, (mysql_mutex_t*)
> 0)) {
> + case 0:
> + /* we read successfully, so we'll need to send it to the slave */
> + mysql_mutex_unlock(log_lock);
> + read_packet = 1;
> + if (coord)
> + coord->pos= uint4korr(packet->ptr() + ev_offset +
> LOG_POS_OFFSET);
> + event_type= (Log_event_type)((*packet)[LOG_EVENT_OFFSET+ev_offset]);
> + break;
> +
> + case LOG_READ_EOF:
> + {
> + int ret;
> + ulong signal_cnt;
> + DBUG_PRINT("wait",("waiting for data in binary log"));
> + if (thd->server_id==0) // for mysqlbinlog (mysqlbinlog.server_id==0)
> + {
> + mysql_mutex_unlock(log_lock);
> + goto end;
> + }
> +
> +#ifndef DBUG_OFF
> + ulong hb_info_counter= 0;
> +#endif
> + signal_cnt= mysql_bin_log.signal_cnt;
> + do
> + {
> + if (coord)
> + {
> + DBUG_ASSERT(heartbeat_ts && heartbeat_period != 0);
> + set_timespec_nsec(*heartbeat_ts, heartbeat_period);
> + }
> + ret= mysql_bin_log.wait_for_update_bin_log(thd, heartbeat_ts);
> + DBUG_ASSERT(ret == 0 || (heartbeat_period != 0 && coord !=
> NULL));
> + if (ret == ETIMEDOUT || ret == ETIME)
> + {
> +#ifndef DBUG_OFF
> + if (hb_info_counter < 3)
> + {
> + sql_print_information("master sends heartbeat message");
> + hb_info_counter++;
> + if (hb_info_counter == 3)
> + sql_print_information("the rest of heartbeat info skipped ...");
> + }
> +#endif
> + /* reset transmit packet for the heartbeat event */
> + if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
> + goto err;
> + if (send_heartbeat_event(net, packet, coord))
> + {
> + errmsg = "Failed on my_net_write()";
> + my_errno= ER_UNKNOWN_ERROR;
> + mysql_mutex_unlock(log_lock);
> + goto err;
> + }
> + }
> + else
> + {
> + DBUG_PRINT("wait",("binary log received update or a broadcast signal
> caught"));
> + }
> + } while (signal_cnt == mysql_bin_log.signal_cnt &&
> !thd->killed);
> + mysql_mutex_unlock(log_lock);
> + }
> + break;
> +
> + default:
> + mysql_mutex_unlock(log_lock);
> + test_for_non_eof_log_read_errors(error, &errmsg);
> + goto err;
> + }
> +
> + if (read_packet)
> + {
> + thd_proc_info(thd, "Sending binlog event to slave");
> + pos = my_b_tell(&log);
> + if (RUN_HOOK(binlog_transmit, before_send_event,
> + (thd, flags, packet, log_file_name, pos)))
> + {
> + my_errno= ER_UNKNOWN_ERROR;
> + errmsg= "run 'before_send_event' hook failed";
> + goto err;
> + }
> +
> + if (my_net_write(net, (uchar*) packet->ptr(), packet->length()) )
> + {
> + errmsg = "Failed on my_net_write()";
> + my_errno= ER_UNKNOWN_ERROR;
> + goto err;
> + }
> +
> + if (event_type == LOAD_EVENT)
> + {
> + if (send_file(thd))
> + {
> + errmsg = "failed in send_file()";
> + my_errno= ER_UNKNOWN_ERROR;
> + goto err;
> + }
> + }
> +
> + if (RUN_HOOK(binlog_transmit, after_send_event, (thd, flags, packet)))
> + {
> + my_errno= ER_UNKNOWN_ERROR;
> + errmsg= "Failed to run hook 'after_send_event'";
> + goto err;
> + }
> + }
> +
> + log.error=0;
> + }
> + }
> + else
> + {
> + bool loop_breaker = 0;
> + /* need this to break out of the for loop from switch */
> +
> + thd_proc_info(thd, "Finished reading one binlog; switching to next binlog");
> + switch (mysql_bin_log.find_next_log(&linfo, 1)) {
> + case 0:
> + break;
> + case LOG_INFO_EOF:
> + if (mysql_bin_log.is_active(log_file_name))
> + {
> + loop_breaker = (flags & BINLOG_DUMP_NON_BLOCK);
> + break;
> + }
> + default:
> + errmsg = "could not find next log";
> + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
> + goto err;
> + }
> +
> + if (loop_breaker)
> + break;
> +
> + end_io_cache(&log);
> + mysql_file_close(file, MYF(MY_WME));
> +
> + /* reset transmit packet for the possible fake rotate event */
> + if (reset_transmit_packet(thd, flags, &ev_offset, &errmsg))
> + goto err;
> +
> + /*
> + Call fake_rotate_event() in case the previous log (the one which
> + we have just finished reading) did not contain a Rotate event
> + (for example (I don't know any other example) the previous log
> + was the last one before the master was shutdown & restarted).
> + This way we tell the slave about the new log's name and
> + position. If the binlog is 5.0, the next event we are going to
> + read and send is Format_description_log_event.
> + */
> + if ((file=open_binlog(&log, log_file_name, &errmsg)) < 0 ||
> + fake_rotate_event(net, packet, log_file_name, BIN_LOG_HEADER_SIZE,
> + &errmsg))
> + {
> + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
> + goto err;
> + }
> +
> + if (coord)
> + coord->file_name= log_file_name; // reset to the next
> + }
> + }
> +
> +end:
> + end_io_cache(&log);
> + mysql_file_close(file, MYF(MY_WME));
> +
> + RUN_HOOK(binlog_transmit, transmit_stop, (thd, flags));
> + my_eof(thd);
> + thd_proc_info(thd, "Waiting to finalize termination");
> + mysql_mutex_lock(&LOCK_thread_count);
> + thd->current_linfo = 0;
> + mysql_mutex_unlock(&LOCK_thread_count);
> + DBUG_VOID_RETURN;
> +
> +err:
> + thd_proc_info(thd, "Waiting to finalize termination");
> + end_io_cache(&log);
> + RUN_HOOK(binlog_transmit, transmit_stop, (thd, flags));
> + /*
> + Exclude iteration through thread list
> + this is needed for purge_logs() - it will iterate through
> + thread list and update thd->current_linfo->index_file_offset
> + this mutex will make sure that it never tried to update our linfo
> + after we return from this stack frame
> + */
> + mysql_mutex_lock(&LOCK_thread_count);
> + thd->current_linfo = 0;
> + mysql_mutex_unlock(&LOCK_thread_count);
> + if (file >= 0)
> + mysql_file_close(file, MYF(MY_WME));
> +
> + my_message(my_errno, errmsg, MYF(0));
> + DBUG_VOID_RETURN;
> +}
> +
> +
> +void kill_zombie_dump_threads(uint32 slave_server_id)
> +{
> + mysql_mutex_lock(&LOCK_thread_count);
> + I_List_iterator<THD> it(threads);
> + THD *tmp;
> +
> + while ((tmp=it++))
> + {
> + if (tmp->command == COM_BINLOG_DUMP &&
> + tmp->server_id == slave_server_id)
> + {
> + mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete
> + break;
> + }
> + }
> + mysql_mutex_unlock(&LOCK_thread_count);
> + if (tmp)
> + {
> + /*
> + Here we do not call kill_one_thread() as
> + it will be slow because it will iterate through the list
> + again. We just to do kill the thread ourselves.
> + */
> + tmp->awake(THD::KILL_QUERY);
> + mysql_mutex_unlock(&tmp->LOCK_thd_data);
> + }
> +}
> +
> +
> +/**
> + Execute a RESET MASTER statement.
> +
> + @param thd Pointer to THD object of the client thread executing the
> + statement.
> +
> + @retval 0 success
> + @retval 1 error
> +*/
> +int reset_master(THD* thd)
> +{
> + if (!mysql_bin_log.is_open())
> + {
> + my_message(ER_FLUSH_MASTER_BINLOG_CLOSED,
> + ER(ER_FLUSH_MASTER_BINLOG_CLOSED), MYF(ME_BELL+ME_WAITTANG));
> + return 1;
> + }
> +
> + if (mysql_bin_log.reset_logs(thd))
> + return 1;
> + RUN_HOOK(binlog_transmit, after_reset_master, (thd, 0 /* flags */));
> + return 0;
> +}
> +
> +int cmp_master_pos(const char* log_file_name1, ulonglong log_pos1,
> + const char* log_file_name2, ulonglong log_pos2)
> +{
> + int res;
> + size_t log_file_name1_len= strlen(log_file_name1);
> + size_t log_file_name2_len= strlen(log_file_name2);
> +
> + // We assume that both log names match up to '.'
> + if (log_file_name1_len == log_file_name2_len)
> + {
> + if ((res= strcmp(log_file_name1, log_file_name2)))
> + return res;
> + return (log_pos1 < log_pos2) ? -1 : (log_pos1 == log_pos2) ? 0 : 1;
> + }
> + return ((log_file_name1_len < log_file_name2_len) ? -1 : 1);
> +}
> +
> +
> +/**
> + Execute a SHOW MASTER STATUS statement.
> +
> + @param thd Pointer to THD object for the client thread executing the
> + statement.
> +
> + @retval FALSE success
> + @retval TRUE failure
> +*/
> +bool show_binlog_info(THD* thd)
> +{
> + Protocol *protocol= thd->protocol;
> + DBUG_ENTER("show_binlog_info");
> + List<Item> field_list;
> + field_list.push_back(new Item_empty_string("File", FN_REFLEN));
> + field_list.push_back(new Item_return_int("Position",20,
> + MYSQL_TYPE_LONGLONG));
> + field_list.push_back(new Item_empty_string("Binlog_Do_DB",255));
> + field_list.push_back(new Item_empty_string("Binlog_Ignore_DB",255));
> +
> + if (protocol->send_result_set_metadata(&field_list,
> + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
> + DBUG_RETURN(TRUE);
> + protocol->prepare_for_resend();
> +
> + if (mysql_bin_log.is_open())
> + {
> + LOG_INFO li;
> + mysql_bin_log.get_current_log(&li);
> + int dir_len = dirname_length(li.log_file_name);
> + protocol->store(li.log_file_name + dir_len, &my_charset_bin);
> + protocol->store((ulonglong) li.pos);
> + protocol->store(binlog_filter->get_do_db());
> + protocol->store(binlog_filter->get_ignore_db());
> + if (protocol->write())
> + DBUG_RETURN(TRUE);
> + }
> + my_eof(thd);
> + DBUG_RETURN(FALSE);
> +}
> +
> +
> +/**
> + Execute a SHOW BINARY LOGS statement.
> +
> + @param thd Pointer to THD object for the client thread executing the
> + statement.
> +
> + @retval FALSE success
> + @retval TRUE failure
> +*/
> +bool show_binlogs(THD* thd)
> +{
> + IO_CACHE *index_file;
> + LOG_INFO cur;
> + File file;
> + char fname[FN_REFLEN];
> + List<Item> field_list;
> + uint length;
> + int cur_dir_len;
> + Protocol *protocol= thd->protocol;
> + DBUG_ENTER("show_binlogs");
> +
> + if (!mysql_bin_log.is_open())
> + {
> + my_message(ER_NO_BINARY_LOGGING, ER(ER_NO_BINARY_LOGGING), MYF(0));
> + DBUG_RETURN(TRUE);
> + }
> +
> + field_list.push_back(new Item_empty_string("Log_name", 255));
> + field_list.push_back(new Item_return_int("File_size", 20,
> + MYSQL_TYPE_LONGLONG));
> + if (protocol->send_result_set_metadata(&field_list,
> + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
> + DBUG_RETURN(TRUE);
> +
> + mysql_mutex_lock(mysql_bin_log.get_log_lock());
> + mysql_bin_log.lock_index();
> + index_file=mysql_bin_log.get_index_file();
> +
> + mysql_bin_log.raw_get_current_log(&cur); // dont take mutex
> + mysql_mutex_unlock(mysql_bin_log.get_log_lock()); // lockdep, OK
> +
> + cur_dir_len= dirname_length(cur.log_file_name);
> +
> + reinit_io_cache(index_file, READ_CACHE, (my_off_t) 0, 0, 0);
> +
> + /* The file ends with EOF or empty line */
> + while ((length=my_b_gets(index_file, fname, sizeof(fname))) > 1)
> + {
> + int dir_len;
> + ulonglong file_length= 0; // Length if open fails
> + fname[--length] = '\0'; // remove the newline
> +
> + protocol->prepare_for_resend();
> + dir_len= dirname_length(fname);
> + length-= dir_len;
> + protocol->store(fname + dir_len, length, &my_charset_bin);
> +
> + if (!(strncmp(fname+dir_len, cur.log_file_name+cur_dir_len, length)))
> + file_length= cur.pos; /* The active log, use the active position */
> + else
> + {
> + /* this is an old log, open it and find the size */
> + if ((file= mysql_file_open(key_file_binlog,
> + fname, O_RDONLY | O_SHARE | O_BINARY,
> + MYF(0))) >= 0)
> + {
> + file_length= (ulonglong) mysql_file_seek(file, 0L, MY_SEEK_END, MYF(0));
> + mysql_file_close(file, MYF(0));
> + }
> + }
> + protocol->store(file_length);
> + if (protocol->write())
> + goto err;
> + }
> + mysql_bin_log.unlock_index();
> + my_eof(thd);
> + DBUG_RETURN(FALSE);
> +
> +err:
> + mysql_bin_log.unlock_index();
> + DBUG_RETURN(TRUE);
> +}
> +
> +#endif /* HAVE_REPLICATION */
>
> === added file 'sql/rpl_master.h'
> --- a/sql/rpl_master.h 1970-01-01 00:00:00 +0000
> +++ b/sql/rpl_master.h 2010-06-21 05:56:56 +0000
> @@ -0,0 +1,34 @@
> +#ifndef RPL_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 RPL_MASTER_H_INCLUDED
> +extern bool server_id_supplied;
> +extern int max_binlog_dump_events;
> +extern my_bool opt_sporadic_binlog_dump_fail;
> +
> +void init_slave_list();
> +void end_slave_list();
> +int register_slave(THD* thd, uchar* packet, uint packet_length);
> +void unregister_slave(THD* thd, bool only_mine, bool need_mutex);
> +bool show_slave_hosts(THD* thd);
> +
> +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 /* RPL_MASTER_H_INCLUDED */
>
> === modified file 'sql/rpl_mi.cc'
> --- a/sql/rpl_mi.cc 2010-05-18 15:35:14 +0000
> +++ b/sql/rpl_mi.cc 2010-06-21 06:01:50 +0000
> @@ -18,7 +18,7 @@
> #include <my_dir.h>
> #include "unireg.h" // REQUIRED by other includes
> #include "rpl_mi.h"
> -#include "slave.h" // SLAVE_MAX_HEARTBEAT_PERIOD
> +#include "rpl_slave.h" // SLAVE_MAX_HEARTBEAT_PERIOD
>
> #ifdef HAVE_REPLICATION
>
>
> === modified file 'sql/rpl_record.cc'
> --- a/sql/rpl_record.cc 2010-04-01 19:34:09 +0000
> +++ b/sql/rpl_record.cc 2010-06-21 06:01:50 +0000
> @@ -17,7 +17,7 @@
> #include "unireg.h"
> #include "rpl_rli.h"
> #include "rpl_record.h"
> -#include "slave.h" // Need to pull in slave_print_msg
> +#include "rpl_slave.h" // Need to pull in slave_print_msg
> #include "rpl_utility.h"
> #include "rpl_rli.h"
>
>
> === modified file 'sql/rpl_rli.cc'
> --- a/sql/rpl_rli.cc 2010-04-01 19:34:09 +0000
> +++ b/sql/rpl_rli.cc 2010-06-21 06:01:50 +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 "rpl_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-04-01 19:34:09 +0000
> +++ b/sql/rpl_rli.h 2010-06-21 06:01:50 +0000
> @@ -19,7 +19,8 @@
> #include "rpl_tblmap.h"
> #include "rpl_reporting.h"
> #include "rpl_utility.h"
> -#include "log.h" /* LOG_INFO, MYSQL_BIN_LOG */
> +#include "log.h" /* LOG_INFO */
> +#include "binlog.h" /* MYSQL_BIN_LOG */
> #include "sql_class.h" /* THD */
>
> struct RPL_TABLE_LIST;
> @@ -449,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 */
>
> === renamed file 'sql/slave.cc' => 'sql/rpl_slave.cc'
> --- a/sql/slave.cc 2010-05-18 15:35:14 +0000
> +++ b/sql/rpl_slave.cc 2010-06-21 06:01:50 +0000
> @@ -26,14 +26,12 @@
>
> #include "sql_priv.h"
> #include "my_global.h"
> -#include "slave.h"
> +#include "rpl_slave.h"
> #include "sql_parse.h" // execute_init_command
> #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"
> #include <thr_alarm.h>
> #include <my_dir.h>
> @@ -3043,8 +3041,6 @@ err:
> /* Forget the relay log's format */
> delete mi->rli.relay_log.description_event_for_queue;
> mi->rli.relay_log.description_event_for_queue= 0;
> - // TODO: make rpl_status part of Master_info
> - change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE);
> DBUG_ASSERT(thd->net.buff != 0);
> net_end(&thd->net); // destructor will not free it, because net.vio is 0
> close_thread_tables(thd);
> @@ -4201,8 +4197,6 @@ static int connect_to_master(THD* thd, M
> if (++err_count == master_retry_count)
> {
> slave_was_killed=1;
> - if (reconnect)
> - change_rpl_status(RPL_ACTIVE_SLAVE,RPL_LOST_SOLDIER);
> break;
> }
> safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed,
> @@ -4223,7 +4217,6 @@ replication resumed in log '%s' at posit
> }
> else
> {
> - change_rpl_status(RPL_IDLE_SLAVE,RPL_ACTIVE_SLAVE);
> general_log_print(thd, COM_CONNECT_OUT, "%s@%s:%d",
> mi->user, mi->host, mi->port);
> }
> @@ -4932,6 +4925,591 @@ template class I_List_iterator<i_string_
> #endif
>
> /**
> + a copy of active_mi->rli->slave_skip_counter, for showing in SHOW
> VARIABLES,
> + INFORMATION_SCHEMA.GLOBAL_VARIABLES and @@sql_slave_skip_counter without
> + taking all the mutexes needed to access active_mi->rli->slave_skip_counter
> + properly.
> +*/
> +uint sql_slave_skip_counter;
> +
> +/**
> + Execute a START SLAVE statement.
> +
> + @param thd Pointer to THD object for the client thread executing the
> + statement.
> +
> + @param mi Pointer to Master_info object for the slave's IO thread.
> +
> + @param net_report If true, saves the exit status into thd->stmt_da.
> +
> + @retval 0 success
> + @retval 1 error
> +*/
> +int start_slave(THD* thd , Master_info* mi, bool net_report)
> +{
> + int slave_errno= 0;
> + int thread_mask;
> + DBUG_ENTER("start_slave");
> +
> + if (check_access(thd, SUPER_ACL, any_db, NULL, NULL, 0, 0))
> + DBUG_RETURN(1);
> + lock_slave_threads(mi); // this allows us to cleanly read slave_running
> + // Get a mask of _stopped_ threads
> + init_thread_mask(&thread_mask,mi,1 /* inverse */);
> + /*
> + Below we will start all stopped threads. But if the user wants to
> + start only one thread, do as if the other thread was running (as we
> + don't wan't to touch the other thread), so set the bit to 0 for the
> + other thread
> + */
> + if (thd->lex->slave_thd_opt)
> + thread_mask&= thd->lex->slave_thd_opt;
> + if (thread_mask) //some threads are stopped, start them
> + {
> + if (init_master_info(mi,master_info_file,relay_log_info_file, 0,
> + thread_mask))
> + slave_errno=ER_MASTER_INFO;
> + else if (server_id_supplied && *mi->host)
> + {
> + /*
> + If we will start SQL thread we will care about UNTIL options If
> + not and they are specified we will ignore them and warn user
> + about this fact.
> + */
> + if (thread_mask & SLAVE_SQL)
> + {
> + mysql_mutex_lock(&mi->rli.data_lock);
> +
> + if (thd->lex->mi.pos)
> + {
> + mi->rli.until_condition= Relay_log_info::UNTIL_MASTER_POS;
> + mi->rli.until_log_pos= thd->lex->mi.pos;
> + /*
> + We don't check thd->lex->mi.log_file_name for NULL here
> + since it is checked in sql_yacc.yy
> + */
> + strmake(mi->rli.until_log_name, thd->lex->mi.log_file_name,
> + sizeof(mi->rli.until_log_name)-1);
> + }
> + else if (thd->lex->mi.relay_log_pos)
> + {
> + mi->rli.until_condition= Relay_log_info::UNTIL_RELAY_POS;
> + mi->rli.until_log_pos= thd->lex->mi.relay_log_pos;
> + strmake(mi->rli.until_log_name, thd->lex->mi.relay_log_name,
> + sizeof(mi->rli.until_log_name)-1);
> + }
> + else
> + mi->rli.clear_until_condition();
> +
> + if (mi->rli.until_condition != Relay_log_info::UNTIL_NONE)
> + {
> + /* Preparing members for effective until condition checking */
> + const char *p= fn_ext(mi->rli.until_log_name);
> + char *p_end;
> + if (*p)
> + {
> + //p points to '.'
> + mi->rli.until_log_name_extension= strtoul(++p,&p_end, 10);
> + /*
> + p_end points to the first invalid character. If it equals
> + to p, no digits were found, error. If it contains '\0' it
> + means conversion went ok.
> + */
> + if (p_end==p || *p_end)
> + slave_errno=ER_BAD_SLAVE_UNTIL_COND;
> + }
> + else
> + slave_errno=ER_BAD_SLAVE_UNTIL_COND;
> +
> + /* mark the cached result of the UNTIL comparison as "undefined" */
> + mi->rli.until_log_names_cmp_result=
> + Relay_log_info::UNTIL_LOG_NAMES_CMP_UNKNOWN;
> +
> + /* Issuing warning then started without --skip-slave-start */
> + if (!opt_skip_slave_start)
> + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
> + ER_MISSING_SKIP_SLAVE,
> + ER(ER_MISSING_SKIP_SLAVE));
> + }
> +
> + mysql_mutex_unlock(&mi->rli.data_lock);
> + }
> + else if (thd->lex->mi.pos || thd->lex->mi.relay_log_pos)
> + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_UNTIL_COND_IGNORED,
> + ER(ER_UNTIL_COND_IGNORED));
> +
> + if (!slave_errno)
> + slave_errno = start_slave_threads(0 /*no mutex */,
> + 1 /* wait for start */,
> + mi,
> + master_info_file,relay_log_info_file,
> + thread_mask);
> + }
> + else
> + slave_errno = ER_BAD_SLAVE;
> + }
> + else
> + {
> + /* no error if all threads are already started, only a warning */
> + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_SLAVE_WAS_RUNNING,
> + ER(ER_SLAVE_WAS_RUNNING));
> + }
> +
> + unlock_slave_threads(mi);
> +
> + if (slave_errno)
> + {
> + if (net_report)
> + my_message(slave_errno, ER(slave_errno), MYF(0));
> + DBUG_RETURN(1);
> + }
> + else if (net_report)
> + my_ok(thd);
> +
> + DBUG_RETURN(0);
> +}
> +
> +
> +/**
> + Execute a STOP SLAVE statement.
> +
> + @param thd Pointer to THD object for the client thread executing the
> + statement.
> +
> + @param mi Pointer to Master_info object for the slave's IO thread.
> +
> + @param net_report If true, saves the exit status into thd->stmt_da.
> +
> + @retval 0 success
> + @retval 1 error
> +*/
> +int stop_slave(THD* thd, Master_info* mi, bool net_report )
> +{
> + DBUG_ENTER("stop_slave");
> +
> + int slave_errno;
> + if (!thd)
> + thd = current_thd;
> +
> + if (check_access(thd, SUPER_ACL, any_db, NULL, NULL, 0, 0))
> + DBUG_RETURN(1);
> + thd_proc_info(thd, "Killing slave");
> + int thread_mask;
> + lock_slave_threads(mi);
> + // Get a mask of _running_ threads
> + init_thread_mask(&thread_mask,mi,0 /* not inverse*/);
> + /*
> + Below we will stop all running threads.
> + But if the user wants to stop only one thread, do as if the other thread
> + was stopped (as we don't wan't to touch the other thread), so set the
> + bit to 0 for the other thread
> + */
> + if (thd->lex->slave_thd_opt)
> + thread_mask &= thd->lex->slave_thd_opt;
> +
> + if (thread_mask)
> + {
> + slave_errno= terminate_slave_threads(mi,thread_mask,
> + 1 /*skip lock */);
> + }
> + else
> + {
> + //no error if both threads are already stopped, only a warning
> + slave_errno= 0;
> + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_SLAVE_WAS_NOT_RUNNING,
> + ER(ER_SLAVE_WAS_NOT_RUNNING));
> + }
> + unlock_slave_threads(mi);
> + thd_proc_info(thd, 0);
> +
> + if (slave_errno)
> + {
> + if (net_report)
> + my_message(slave_errno, ER(slave_errno), MYF(0));
> + DBUG_RETURN(1);
> + }
> + else if (net_report)
> + my_ok(thd);
> +
> + DBUG_RETURN(0);
> +}
> +
> +
> +/**
> + Execute a RESET SLAVE statement.
> +
> + @param thd Pointer to THD object of the client thread executing the
> + statement.
> +
> + @param mi Pointer to Master_info object for the slave.
> +
> + @retval 0 success
> + @retval 1 error
> +*/
> +int reset_slave(THD *thd, Master_info* mi)
> +{
> + MY_STAT stat_area;
> + char fname[FN_REFLEN];
> + int thread_mask= 0, error= 0;
> + uint sql_errno=ER_UNKNOWN_ERROR;
> + const char* errmsg= "Unknown error occured while reseting slave";
> + DBUG_ENTER("reset_slave");
> +
> + lock_slave_threads(mi);
> + init_thread_mask(&thread_mask,mi,0 /* not inverse */);
> + if (thread_mask) // We refuse if any slave thread is running
> + {
> + sql_errno= ER_SLAVE_MUST_STOP;
> + error=1;
> + goto err;
> + }
> +
> + ha_reset_slave(thd);
> +
> + // delete relay logs, clear relay log coordinates
> + if ((error= purge_relay_logs(&mi->rli, thd,
> + 1 /* just reset */,
> + &errmsg)))
> + {
> + sql_errno= ER_RELAY_LOG_FAIL;
> + goto err;
> + }
> +
> + /* Clear master's log coordinates */
> + init_master_log_pos(mi);
> + /*
> + Reset errors (the idea is that we forget about the
> + old master).
> + */
> + mi->clear_error();
> + mi->rli.clear_error();
> + mi->rli.clear_until_condition();
> +
> + // close master_info_file, relay_log_info_file, set
> mi->inited=rli->inited=0
> + end_master_info(mi);
> + // and delete these two files
> + fn_format(fname, master_info_file, mysql_data_home, "", 4+32);
> + if (mysql_file_stat(key_file_master_info, fname, &stat_area, MYF(0))
> &&
> + mysql_file_delete(key_file_master_info, fname, MYF(MY_WME)))
> + {
> + error=1;
> + goto err;
> + }
> + // delete relay_log_info_file
> + fn_format(fname, relay_log_info_file, mysql_data_home, "", 4+32);
> + if (mysql_file_stat(key_file_relay_log_info, fname, &stat_area, MYF(0))
> &&
> + mysql_file_delete(key_file_relay_log_info, fname, MYF(MY_WME)))
> + {
> + error=1;
> + goto err;
> + }
> +
> + RUN_HOOK(binlog_relay_io, after_reset_slave, (thd, mi));
> +err:
> + unlock_slave_threads(mi);
> + if (error)
> + my_error(sql_errno, MYF(0), errmsg);
> + DBUG_RETURN(error);
> +}
> +
> +/*
> +
> + Kill all Binlog_dump threads which previously talked to the same slave
> + ("same" means with the same server id). Indeed, if the slave stops, if the
> + Binlog_dump thread is waiting (mysql_cond_wait) for binlog update, then it
> + will keep existing until a query is written to the binlog. If the master is
> + idle, then this could last long, and if the slave reconnects, we could have 2
> + Binlog_dump threads in SHOW PROCESSLIST, until a query is written to the
> + binlog. To avoid this, when the slave reconnects and sends COM_BINLOG_DUMP,
> + the master kills any existing thread with the slave's server id (if this id is
> + not zero; it will be true for real slaves, but false for mysqlbinlog when it
> + sends COM_BINLOG_DUMP to get a remote binlog dump).
> +
> + SYNOPSIS
> + kill_zombie_dump_threads()
> + slave_server_id the slave's server id
> +
> +*/
> +
> +
> +/**
> + Execute a CHANGE MASTER statement.
> +
> + @param thd Pointer to THD object for the client thread executing the
> + statement.
> +
> + @param mi Pointer to Master_info object belonging to the slave's IO
> + thread.
> +
> + @retval FALSE success
> + @retval TRUE error
> +*/
> +bool change_master(THD* thd, Master_info* mi)
> +{
> + int thread_mask;
> + const char* errmsg= 0;
> + bool need_relay_log_purge= 1;
> + bool ret= FALSE;
> + DBUG_ENTER("change_master");
> +
> + lock_slave_threads(mi);
> + init_thread_mask(&thread_mask,mi,0 /*not inverse*/);
> + LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
> + if (thread_mask) // We refuse if any slave thread is running
> + {
> + my_message(ER_SLAVE_MUST_STOP, ER(ER_SLAVE_MUST_STOP), MYF(0));
> + ret= TRUE;
> + goto err;
> + }
> +
> + thd_proc_info(thd, "Changing master");
> + /*
> + We need to check if there is an empty master_host. Otherwise
> + change master succeeds, a master.info file is created containing
> + empty master_host string and when issuing: start slave; an error
> + is thrown stating that the server is not configured as slave.
> + (See BUG#28796).
> + */
> + if(lex_mi->host && !*lex_mi->host)
> + {
> + my_error(ER_WRONG_ARGUMENTS, MYF(0), "MASTER_HOST");
> + unlock_slave_threads(mi);
> + DBUG_RETURN(TRUE);
> + }
> + // TODO: see if needs re-write
> + if (init_master_info(mi, master_info_file, relay_log_info_file, 0,
> + thread_mask))
> + {
> + my_message(ER_MASTER_INFO, ER(ER_MASTER_INFO), MYF(0));
> + ret= TRUE;
> + goto err;
> + }
> +
> + /*
> + Data lock not needed since we have already stopped the running threads,
> + and we have the hold on the run locks which will keep all threads that
> + could possibly modify the data structures from running
> + */
> +
> + /*
> + If the user specified host or port without binlog or position,
> + reset binlog's name to FIRST and position to 4.
> + */
> +
> + if ((lex_mi->host || lex_mi->port) && !lex_mi->log_file_name
> && !lex_mi->pos)
> + {
> + mi->master_log_name[0] = 0;
> + mi->master_log_pos= BIN_LOG_HEADER_SIZE;
> + }
> +
> + if (lex_mi->log_file_name)
> + strmake(mi->master_log_name, lex_mi->log_file_name,
> + sizeof(mi->master_log_name)-1);
> + if (lex_mi->pos)
> + {
> + mi->master_log_pos= lex_mi->pos;
> + }
> + DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos));
> +
> + if (lex_mi->host)
> + strmake(mi->host, lex_mi->host, sizeof(mi->host)-1);
> + if (lex_mi->user)
> + strmake(mi->user, lex_mi->user, sizeof(mi->user)-1);
> + if (lex_mi->password)
> + strmake(mi->password, lex_mi->password, sizeof(mi->password)-1);
> + if (lex_mi->port)
> + mi->port = lex_mi->port;
> + if (lex_mi->connect_retry)
> + mi->connect_retry = lex_mi->connect_retry;
> + if (lex_mi->heartbeat_opt != LEX_MASTER_INFO::LEX_MI_UNCHANGED)
> + mi->heartbeat_period = lex_mi->heartbeat_period;
> + else
> + mi->heartbeat_period= (float) min(SLAVE_MAX_HEARTBEAT_PERIOD,
> + (slave_net_timeout/2.0));
> + mi->received_heartbeats= LL(0); // counter lives until master is CHANGEd
> + /*
> + reset the last time server_id list if the current CHANGE MASTER
> + is mentioning IGNORE_SERVER_IDS= (...)
> + */
> + if (lex_mi->repl_ignore_server_ids_opt == LEX_MASTER_INFO::LEX_MI_ENABLE)
> + reset_dynamic(&mi->ignore_server_ids);
> + for (uint i= 0; i < lex_mi->repl_ignore_server_ids.elements; i++)
> + {
> + ulong s_id;
> + get_dynamic(&lex_mi->repl_ignore_server_ids, (uchar*) &s_id, i);
> + if (s_id == ::server_id && replicate_same_server_id)
> + {
> + my_error(ER_SLAVE_IGNORE_SERVER_IDS, MYF(0), s_id);
> + ret= TRUE;
> + goto err;
> + }
> + else
> + {
> + if (bsearch((const ulong *) &s_id,
> + mi->ignore_server_ids.buffer,
> + mi->ignore_server_ids.elements, sizeof(ulong),
> + (int (*) (const void*, const void*))
> + change_master_server_id_cmp) == NULL)
> + insert_dynamic(&mi->ignore_server_ids, (uchar*) &s_id);
> + }
> + }
> + sort_dynamic(&mi->ignore_server_ids, (qsort_cmp)
> change_master_server_id_cmp);
> +
> + if (lex_mi->ssl != LEX_MASTER_INFO::LEX_MI_UNCHANGED)
> + mi->ssl= (lex_mi->ssl == LEX_MASTER_INFO::LEX_MI_ENABLE);
> +
> + if (lex_mi->ssl_verify_server_cert != LEX_MASTER_INFO::LEX_MI_UNCHANGED)
> + mi->ssl_verify_server_cert=
> + (lex_mi->ssl_verify_server_cert == LEX_MASTER_INFO::LEX_MI_ENABLE);
> +
> + if (lex_mi->ssl_ca)
> + strmake(mi->ssl_ca, lex_mi->ssl_ca, sizeof(mi->ssl_ca)-1);
> + if (lex_mi->ssl_capath)
> + strmake(mi->ssl_capath, lex_mi->ssl_capath, sizeof(mi->ssl_capath)-1);
> + if (lex_mi->ssl_cert)
> + strmake(mi->ssl_cert, lex_mi->ssl_cert, sizeof(mi->ssl_cert)-1);
> + if (lex_mi->ssl_cipher)
> + strmake(mi->ssl_cipher, lex_mi->ssl_cipher, sizeof(mi->ssl_cipher)-1);
> + if (lex_mi->ssl_key)
> + strmake(mi->ssl_key, lex_mi->ssl_key, sizeof(mi->ssl_key)-1);
> +#ifndef HAVE_OPENSSL
> + if (lex_mi->ssl || lex_mi->ssl_ca || lex_mi->ssl_capath ||
> + lex_mi->ssl_cert || lex_mi->ssl_cipher || lex_mi->ssl_key ||
> + lex_mi->ssl_verify_server_cert )
> + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
> + ER_SLAVE_IGNORED_SSL_PARAMS, ER(ER_SLAVE_IGNORED_SSL_PARAMS));
> +#endif
> +
> + if (lex_mi->relay_log_name)
> + {
> + need_relay_log_purge= 0;
> + char relay_log_name[FN_REFLEN];
> + mi->rli.relay_log.make_log_name(relay_log_name, lex_mi->relay_log_name);
> + strmake(mi->rli.group_relay_log_name, relay_log_name,
> + sizeof(mi->rli.group_relay_log_name)-1);
> + strmake(mi->rli.event_relay_log_name, relay_log_name,
> + sizeof(mi->rli.event_relay_log_name)-1);
> + }
> +
> + if (lex_mi->relay_log_pos)
> + {
> + need_relay_log_purge= 0;
> + mi->rli.group_relay_log_pos= mi->rli.event_relay_log_pos=
> lex_mi->relay_log_pos;
> + }
> +
> + /*
> + If user did specify neither host nor port nor any log name nor any log
> + pos, i.e. he specified only user/password/master_connect_retry, he probably
> + wants replication to resume from where it had left, i.e. from the
> + coordinates of the **SQL** thread (imagine the case where the I/O is ahead
> + of the SQL; restarting from the coordinates of the I/O would lose some
> + events which is probably unwanted when you are just doing minor changes
> + like changing master_connect_retry).
> + A side-effect is that if only the I/O thread was started, this thread may
> + restart from ''/4 after the CHANGE MASTER. That's a minor problem (it is a
> + much more unlikely situation than the one we are fixing here).
> + Note: coordinates of the SQL thread must be read here, before the
> + 'if (need_relay_log_purge)' block which resets them.
> + */
> + if (!lex_mi->host && !lex_mi->port &&
> + !lex_mi->log_file_name && !lex_mi->pos &&
> + need_relay_log_purge)
> + {
> + /*
> + Sometimes mi->rli.master_log_pos == 0 (it happens when the SQL thread is
> + not initialized), so we use a max().
> + What happens to mi->rli.master_log_pos during the initialization stages
> + of replication is not 100% clear, so we guard against problems using
> + max().
> + */
> + mi->master_log_pos = max(BIN_LOG_HEADER_SIZE,
> + mi->rli.group_master_log_pos);
> + strmake(mi->master_log_name, mi->rli.group_master_log_name,
> + sizeof(mi->master_log_name)-1);
> + }
> + /*
> + Relay log's IO_CACHE may not be inited, if rli->inited==0 (server was never
> + a slave before).
> + */
> + if (flush_master_info(mi, FALSE, FALSE))
> + {
> + my_error(ER_RELAY_LOG_INIT, MYF(0), "Failed to flush master info file");
> + ret= TRUE;
> + goto err;
> + }
> + if (need_relay_log_purge)
> + {
> + relay_log_purge= 1;
> + thd_proc_info(thd, "Purging old relay logs");
> + if (purge_relay_logs(&mi->rli, thd,
> + 0 /* not only reset, but also reinit */,
> + &errmsg))
> + {
> + my_error(ER_RELAY_LOG_FAIL, MYF(0), errmsg);
> + ret= TRUE;
> + goto err;
> + }
> + }
> + else
> + {
> + const char* msg;
> + relay_log_purge= 0;
> + /* Relay log is already initialized */
> + if (init_relay_log_pos(&mi->rli,
> + mi->rli.group_relay_log_name,
> + mi->rli.group_relay_log_pos,
> + 0 /*no data lock*/,
> + &msg, 0))
> + {
> + my_error(ER_RELAY_LOG_INIT, MYF(0), msg);
> + ret= TRUE;
> + goto err;
> + }
> + }
> + /*
> + Coordinates in rli were spoilt by the 'if (need_relay_log_purge)' block,
> + so restore them to good values. If we left them to ''/0, that would work;
> + but that would fail in the case of 2 successive CHANGE MASTER (without a
> + START SLAVE in between): because first one would set the coords in mi to
> + the good values of those in rli, the set those in rli to ''/0, then
> + second CHANGE MASTER would set the coords in mi to those of rli, i.e. to
> + ''/0: we have lost all copies of the original good coordinates.
> + That's why we always save good coords in rli.
> + */
> + mi->rli.group_master_log_pos= mi->master_log_pos;
> + DBUG_PRINT("info", ("master_log_pos: %lu", (ulong) mi->master_log_pos));
> + strmake(mi->rli.group_master_log_name,mi->master_log_name,
> + sizeof(mi->rli.group_master_log_name)-1);
> +
> + if (!mi->rli.group_master_log_name[0]) // uninitialized case
> + mi->rli.group_master_log_pos=0;
> +
> + mysql_mutex_lock(&mi->rli.data_lock);
> + mi->rli.abort_pos_wait++; /* for MASTER_POS_WAIT() to abort */
> + /* Clear the errors, for a clean start */
> + mi->rli.clear_error();
> + mi->rli.clear_until_condition();
> + /*
> + If we don't write new coordinates to disk now, then old will remain in
> + relay-log.info until START SLAVE is issued; but if mysqld is shutdown
> + before START SLAVE, then old will remain in relay-log.info, and will be the
> + in-memory value at restart (thus causing errors, as the old relay log does
> + not exist anymore).
> + */
> + flush_relay_log_info(&mi->rli);
> + mysql_cond_broadcast(&mi->data_cond);
> + mysql_mutex_unlock(&mi->rli.data_lock);
> +
> +err:
> + unlock_slave_threads(mi);
> + thd_proc_info(thd, 0);
> + if (ret == FALSE)
> + my_ok(thd);
> + delete_dynamic(&lex_mi->repl_ignore_server_ids); //freeing of parser-time
> alloc
> + DBUG_RETURN(ret);
> +}
> +
> +/**
> @} (end of group Replication)
> */
>
>
> === renamed file 'sql/slave.h' => 'sql/rpl_slave.h'
> --- a/sql/slave.h 2010-04-01 19:34:09 +0000
> +++ b/sql/rpl_slave.h 2010-06-21 06:01:50 +0000
> @@ -13,8 +13,8 @@
> along with this program; if not, write to the Free Software
> Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
>
> -#ifndef SLAVE_H
> -#define SLAVE_H
> +#ifndef RPL_SLAVE_H
> +#define RPL_SLAVE_H
>
> /**
> @defgroup Replication Replication
> @@ -36,6 +36,7 @@
> #ifdef HAVE_REPLICATION
>
> #include "log.h"
> +#include "binlog.h"
> #include "my_list.h"
> #include "rpl_filter.h"
> #include "rpl_tblmap.h"
> @@ -48,6 +49,7 @@
> class Relay_log_info;
> class Master_info;
>
> +extern bool server_id_supplied;
>
> /*****************************************************************************
>
> @@ -144,10 +146,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);
> @@ -193,19 +200,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/set_var.h'
> --- a/sql/set_var.h 2010-04-01 19:34:09 +0000
> +++ b/sql/set_var.h 2010-06-21 06:01:50 +0000
> @@ -169,7 +169,7 @@ protected:
> { return ((uchar*)&global_system_variables) + offset; }
> };
>
> -#include "log.h" /* binlog_format_typelib */
> +#include "binlog.h" /* binlog_format_typelib */
> #include "sql_plugin.h" /* SHOW_HA_ROWS, SHOW_MY_BOOL */
>
> /****************************************************************************
>
> === modified file 'sql/sql_binlog.cc'
> --- a/sql/sql_binlog.cc 2010-03-31 14:05:33 +0000
> +++ b/sql/sql_binlog.cc 2010-06-21 05:56:56 +0000
> @@ -19,7 +19,7 @@
> #include "sql_acl.h" // *_ACL
> #include "rpl_rli.h"
> #include "base64.h"
> -#include "slave.h" // apply_event_and_update_pos
> +#include "rpl_slave.h" // apply_event_and_update_pos
> #include "log_event.h" // Format_description_log_event,
> // EVENT_LEN_OFFSET,
> // EVENT_TYPE_OFFSET,
>
> === modified file 'sql/sql_class.cc'
> --- a/sql/sql_class.cc 2010-05-14 05:29:47 +0000
> +++ b/sql/sql_class.cc 2010-06-21 06:01:50 +0000
> @@ -40,7 +40,7 @@
> #include "rpl_rli.h"
> #include "rpl_filter.h"
> #include "rpl_record.h"
> -#include "slave.h"
> +#include "rpl_slave.h"
> #include <my_bitmap.h>
> #include "log_event.h"
> #include "sql_audit.h"
> @@ -3473,610 +3473,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)
> @@ -4140,505 +3536,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_db.cc'
> --- a/sql/sql_db.cc 2010-04-07 12:02:19 +0000
> +++ b/sql/sql_db.cc 2010-06-21 06:01:50 +0000
> @@ -36,6 +36,7 @@
> #include <my_dir.h>
> #include <m_ctype.h>
> #include "log.h"
> +#include "binlog.h" // mysql_bin_log
> #ifdef __WIN__
> #include <direct.h>
> #endif
>
> === modified file 'sql/sql_insert.cc'
> --- a/sql/sql_insert.cc 2010-05-14 05:29:47 +0000
> +++ b/sql/sql_insert.cc 2010-06-21 06:01:50 +0000
> @@ -70,7 +70,7 @@
> #include "sql_trigger.h"
> #include "sql_select.h"
> #include "sql_show.h"
> -#include "slave.h"
> +#include "rpl_slave.h"
> #include "sql_parse.h" // end_active_trans
> #include "rpl_mi.h"
> #include "transaction.h"
>
> === modified file 'sql/sql_load.cc'
> --- a/sql/sql_load.cc 2010-05-05 10:45:26 +0000
> +++ b/sql/sql_load.cc 2010-06-21 06:01:50 +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 "rpl_slave.h"
> #include "sp_head.h"
> #include "sql_trigger.h"
>
>
> === modified file 'sql/sql_parse.cc'
> --- a/sql/sql_parse.cc 2010-05-14 18:38:28 +0000
> +++ b/sql/sql_parse.cc 2010-06-21 06:01:50 +0000
> @@ -76,9 +76,9 @@
> #include "sql_help.h" // mysqld_help
> #include "rpl_constants.h" // Incident, INCIDENT_LOST_EVENTS
> #include "log_event.h"
> -#include "sql_repl.h"
> +#include "rpl_slave.h"
> +#include "rpl_master.h"
> #include "rpl_filter.h"
> -#include "repl_failsafe.h"
> #include <m_ctype.h>
> #include <myisam.h>
> #include <my_dir.h>
> @@ -2356,14 +2356,9 @@ case SQLCOM_PREPARE:
> {
> if (check_global_access(thd, REPL_SLAVE_ACL))
> goto error;
> - /* This query don't work now. See comment in repl_failsafe.cc */
> -#ifndef WORKING_NEW_MASTER
> + /* This query don't work now.*/
> my_error(ER_NOT_SUPPORTED_YET, MYF(0), "SHOW NEW MASTER");
> goto error;
> -#else
> - res = show_new_master(thd);
> - break;
> -#endif
> }
>
> #ifdef HAVE_REPLICATION
> @@ -2374,7 +2369,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.cc'
> --- a/sql/sql_repl.cc 2010-04-01 19:34:09 +0000
> +++ b/sql/sql_repl.cc 1970-01-01 00:00:00 +0000
> @@ -1,2005 +0,0 @@
> -/* Copyright (C) 2000-2006 MySQL AB & Sasha, 2008-2009 Sun Microsystems, Inc
> -
> - 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 */
> -
> -#include "sql_priv.h"
> -#include "unireg.h"
> -#include "sql_parse.h" // check_access
> -#ifdef HAVE_REPLICATION
> -
> -#include "rpl_mi.h"
> -#include "sql_repl.h"
> -#include "sql_acl.h" // SUPER_ACL
> -#include "log_event.h"
> -#include "rpl_filter.h"
> -#include <my_dir.h>
> -#include "rpl_handler.h"
> -
> -int max_binlog_dump_events = 0; // unlimited
> -my_bool opt_sporadic_binlog_dump_fail = 0;
> -#ifndef DBUG_OFF
> -static int binlog_dump_count = 0;
> -#endif
> -
> -/**
> - a copy of active_mi->rli->slave_skip_counter, for showing in SHOW
> VARIABLES,
> - INFORMATION_SCHEMA.GLOBAL_VARIABLES and @@sql_slave_skip_counter without
> - taking all the mutexes needed to access active_mi->rli->slave_skip_counter
> - properly.
> -*/
> -uint sql_slave_skip_counter;
> -
> -/*
> - fake_rotate_event() builds a fake (=which does not exist physically in any
> - binlog) Rotate event, which contains the name of the binlog we are going to
> - send to the slave (because the slave may not know it if it just asked for
> - MASTER_LOG_FILE='', MASTER_LOG_POS=4).
> - < 4.0.14, fake_rotate_event() was called only if the requested pos was 4.
> - After this version we always call it, so that a 3.23.58 slave can rely on
> - it to detect if the master is 4.0 (and stop) (the _fake_ Rotate event has
> - zeros in the good positions which, by chance, make it possible for the 3.23
> - slave to detect that this event is unexpected) (this is luck which happens
> - because the master and slave disagree on the size of the header of
> - Log_event).
> -
> - Relying on the event length of the Rotate event instead of these
> - well-placed zeros was not possible as Rotate events have a variable-length
> - part.
> -*/
> -
> -static int fake_rotate_event(NET* net, String* packet, char* log_file_name,
> - ulonglong position, const char** errmsg)
> -{
> - DBUG_ENTER("fake_rotate_event");
> - char header[LOG_EVENT_HEADER_LEN], buf[ROTATE_HEADER_LEN+100];
> - /*
> - 'when' (the timestamp) is set to 0 so that slave could distinguish between
> - real and fake Rotate events (if necessary)
> - */
> - memset(header, 0, 4);
> - header[EVENT_TYPE_OFFSET] = ROTATE_EVENT;
> -
> - char* p = log_file_name+dirname_length(log_file_name);
> - uint ident_len = (uint) strlen(p);
> - ulong event_len = ident_len + LOG_EVENT_HEADER_LEN + ROTATE_HEADER_LEN;
> - int4store(header + SERVER_ID_OFFSET, server_id);
> - int4store(header + EVENT_LEN_OFFSET, event_len);
> - int2store(header + FLAGS_OFFSET, LOG_EVENT_ARTIFICIAL_F);
> -
> - // TODO: check what problems this may cause and fix them
> - int4store(header + LOG_POS_OFFSET, 0);
> -
> - packet->append(header, sizeof(header));
> - int8store(buf+R_POS_OFFSET,position);
> - packet->append(buf, ROTATE_HEADER_LEN);
> - packet->append(p,ident_len);
> - if (my_net_write(net, (uchar*) packet->ptr(), packet->length()))
> - {
> - *errmsg = "failed on my_net_write()";
> - DBUG_RETURN(-1);
> - }
> - DBUG_RETURN(0);
> -}
> -
> -/*
> - Reset thread transmit packet buffer for event sending
> -
> - This function allocates header bytes for event transmission, and
> - should be called before store the event data to the packet buffer.
> -*/
> -static int reset_transmit_packet(THD *thd, ushort flags,
> - ulong *ev_offset, const char **errmsg)
> -{
> - int ret= 0;
> - String *packet= &thd->packet;
> -
> - /* reserve and set default header */
> - packet->length(0);
> - packet->set("\0", 1, &my_charset_bin);
> -
> - if (RUN_HOOK(binlog_transmit, reserve_header, (thd, flags, packet)))
> - {
> - *errmsg= "Failed to run hook 'reserve_header'";
> - my_errno= ER_UNKNOWN_ERROR;
> - ret= 1;
> - }
> - *ev_offset= packet->length();
> - return ret;
> -}
> -
> -static int send_file(THD *thd)
> -{
> - NET* net = &thd->net;
> - int fd = -1, error = 1;
> - size_t bytes;
> - char fname[FN_REFLEN+1];
> - const char *errmsg = 0;
> - int old_timeout;
> - unsigned long packet_len;
> - uchar buf[IO_SIZE]; // It's safe to alloc this
> - DBUG_ENTER("send_file");
> -
> - /*
> - The client might be slow loading the data, give him wait_timeout to do
> - the job
> - */
> - old_timeout= net->read_timeout;
> - my_net_set_read_timeout(net, thd->variables.net_wait_timeout);
> -
> - /*
> - We need net_flush here because the client will not know it needs to send
> - us the file name until it has processed the load event entry
> - */
> - if (net_flush(net) || (packet_len = my_net_read(net)) == packet_error)
> - {
> - errmsg = "while reading file name";
> - goto err;
> - }
> -
> - // terminate with \0 for fn_format
> - *((char*)net->read_pos + packet_len) = 0;
> - fn_format(fname, (char*) net->read_pos + 1, "", "", 4);
> - // this is needed to make replicate-ignore-db
> - if (!strcmp(fname,"/dev/null"))
> - goto end;
> -
> - if ((fd= mysql_file_open(key_file_send_file,
> - fname, O_RDONLY, MYF(0))) < 0)
> - {
> - errmsg = "on open of file";
> - goto err;
> - }
> -
> - while ((long) (bytes= mysql_file_read(fd, buf, IO_SIZE, MYF(0))) > 0)
> - {
> - if (my_net_write(net, buf, bytes))
> - {
> - errmsg = "while writing data to client";
> - goto err;
> - }
> - }
> -
> - end:
> - if (my_net_write(net, (uchar*) "", 0) || net_flush(net) ||
> - (my_net_read(net) == packet_error))
> - {
> - errmsg = "while negotiating file transfer close";
> - goto err;
> - }
> - error = 0;
> -
> - err:
> - my_net_set_read_timeout(net, old_timeout);
> - if (fd >= 0)
> - mysql_file_close(fd, MYF(0));
> - if (errmsg)
> - {
> - sql_print_error("Failed in send_file() %s", errmsg);
> - DBUG_PRINT("error", ("%s", errmsg));
> - }
> - DBUG_RETURN(error);
> -}
> -
> -
> -/*
> - 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.
> -*/
> -
> -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);
> -}
> -
> -
> -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;
> -}
> -
> -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;
> -}
> -
> -
> -/**
> - 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())
> - {
> - my_ok(thd);
> - return FALSE;
> - }
> -
> - 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())
> - {
> - my_ok(thd);
> - return 0;
> - }
> - return purge_error_message(thd,
> - mysql_bin_log.purge_logs_before_date(purge_time));
> -}
> -
> -int test_for_non_eof_log_read_errors(int error, const char **errmsg)
> -{
> - if (error == LOG_READ_EOF)
> - return 0;
> - my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG;
> - switch (error) {
> - case LOG_READ_BOGUS:
> - *errmsg = "bogus data in log event";
> - break;
> - case LOG_READ_TOO_LARGE:
> - *errmsg = "log event entry exceeded max_allowed_packet; \
> -Increase max_allowed_packet on master";
> - break;
> - case LOG_READ_IO:
> - *errmsg = "I/O error reading log event";
> - break;
> - case LOG_READ_MEM:
> - *errmsg = "memory allocat