#At file:///media/sdb2/hezx/work/mysql/bzr/w5385/5.5-bugfixing/ based on revid:marc.alff@stripped
3206 He Zhenxing 2010-09-10
Backport WL#5385 BUG#54649 to 5.5
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_parse.h
sql/sql_reload.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-08-18 11:29:04 +0000
+++ b/libmysqld/CMakeLists.txt 2010-09-10 07:27:02 +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
@@ -70,7 +70,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc l
../sql/sql_binlog.cc ../sql/sql_manager.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
@@ -83,7 +83,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc l
../sql/sql_alter.cc ../sql/sql_partition_admin.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-09-01 03:38:53 +0000
+++ b/libmysqld/Makefile.am 2010-09-10 07:27:02 +0000
@@ -77,7 +77,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_alter.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 \
sql_partition_admin.cc
libmysqld_int_a_SOURCES= $(libmysqld_sources)
=== modified file 'mysql-test/lib/v1/mysql-test-run.pl'
--- a/mysql-test/lib/v1/mysql-test-run.pl 2010-08-05 12:53:09 +0000
+++ b/mysql-test/lib/v1/mysql-test-run.pl 2010-09-10 07:27:02 +0000
@@ -3956,7 +3956,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,
@@ -4013,9 +4012,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'}];
@@ -4091,12 +4088,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-09-10 07:27:02 +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-09-10 07:27:02 +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-09-10 07:27:02 +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-09-10 07:27:02 +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-09-10 07:27:02 +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 2010-05-23 20:41:18 +0000
+++ b/mysql-test/suite/rpl_ndb/my.cnf 2010-09-10 07:27:02 +0000
@@ -37,7 +37,6 @@ relay-log= slave-rela
# Cluster only supports row format
binlog-format= row
-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-09-10 07:27:02 +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-09-02 22:17:08 +0000
+++ b/sql/CMakeLists.txt 2010-09-10 07:27:02 +0000
@@ -46,14 +46,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
@@ -61,18 +59,18 @@ SET (SQL_SOURCE
sql_list.cc sql_load.cc sql_manager.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_partition_admin.cc
sql_profile.cc event_parse_data.cc sql_alter.cc
- sql_signal.cc rpl_handler.cc mdl.cc sql_admin.cc
+ sql_signal.cc mdl.cc sql_admin.cc
transaction.cc sys_vars.cc sql_truncate.cc datadict.cc
sql_reload.cc
${GEN_SOURCES}
@@ -89,7 +87,21 @@ 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_DEPENDENCIES(binlog GenError)
+ADD_LIBRARY(rpl ${RPL_SOURCE})
+SET (MASTER_SOURCE rpl_master.cc)
+ADD_DEPENDENCIES(rpl GenError)
+ADD_LIBRARY(master ${MASTER_SOURCE})
+ADD_DEPENDENCIES(master GenError)
+SET (SLAVE_SOURCE rpl_slave.cc rpl_reporting.cc rpl_mi.cc rpl_rli.cc)
+ADD_LIBRARY(slave ${SLAVE_SOURCE})
+ADD_DEPENDENCIES(slave GenError)
+
IF(WIN32)
SET(MYSQLD_SOURCE main.cc nt_servc.cc nt_servc.h message.rc)
@@ -131,7 +143,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 sql)
# Provide plugins with minimal set of libraries
SET(INTERFACE_LIBS ${LIBRT})
=== modified file 'sql/Makefile.am'
--- a/sql/Makefile.am 2010-08-18 11:29:04 +0000
+++ b/sql/Makefile.am 2010-09-10 07:27:02 +0000
@@ -68,6 +68,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
@@ -77,9 +79,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@ \
@@ -111,7 +115,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 \
@@ -149,8 +153,6 @@ mysqld_SOURCES = sql_lex.cc sql_handler.
sql_truncate.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 \
@@ -158,26 +160,30 @@ 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_view.cc \
+ sql_view.cc \
gstream.cc spatial.cc sql_help.cc sql_cursor.cc \
tztime.cc my_decimal.cc\
sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \
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 \
sql_alter.cc sql_partition_admin.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-09-10 07:27:02 +0000
@@ -0,0 +1,5050 @@
+/* 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), changes_to_non_trans_temp_table_flag(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 set_changes_to_non_trans_temp_table()
+ {
+ changes_to_non_trans_temp_table_flag= TRUE;
+ }
+
+ bool changes_to_non_trans_temp_table()
+ {
+ return (changes_to_non_trans_temp_table_flag);
+ }
+
+ void reset()
+ {
+ truncate(0);
+ changes_to_non_trans_temp_table_flag= FALSE;
+ 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;
+
+ /*
+ This flag indicates if the cache has changes to temporary tables.
+ @TODO This a temporary fix and should be removed after BUG#54562.
+ */
+ bool changes_to_non_trans_temp_table_flag;
+
+ /*
+ 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(cache_mngr);
+ 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);
+
+ 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();
+
+ 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);
+
+ 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_mode()),
+ 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 OPTION_KEEP_LOG is active or;
+ . the format is STMT and a non-trans table was updated or;
+ . the format is MIXED and a temporary non-trans table was
+ updated or;
+ . the format is MIXED, non-trans table was updated and
+ aborting a single statement transaction;
+ */
+
+ 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) ||
+ (cache_mngr->trx_cache.changes_to_non_trans_temp_table() &&
+ thd->variables.binlog_format == BINLOG_FORMAT_MIXED) ||
+ (trans_has_updated_non_trans_table(thd) &&
+ ending_single_stmt_trans(thd,all) &&
+ thd->variables.binlog_format == BINLOG_FORMAT_MIXED)))
+ {
+ 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 active and;
+ . the format is not STMT or no non-trans table was
+ updated and;
+ . the format is not MIXED or no temporary non-trans table
+ was 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) &&
+ (!cache_mngr->trx_cache.changes_to_non_trans_temp_table() ||
+ thd->variables.binlog_format != BINLOG_FORMAT_MIXED)))
+ 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 */
+
+ String log_query;
+ if (log_query.append(STRING_WITH_LEN("SAVEPOINT ")) ||
+ log_query.append(thd->lex->ident.str, thd->lex->ident.length))
+ DBUG_RETURN(1);
+ int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
+ Query_log_event qinfo(thd, log_query.c_ptr_safe(), log_query.length(),
+ TRUE, FALSE, TRUE, errcode);
+ DBUG_RETURN(mysql_bin_log.write(&qinfo));
+}
+
+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)))
+ {
+ String log_query;
+ if (log_query.append(STRING_WITH_LEN("ROLLBACK TO ")) ||
+ log_query.append(thd->lex->ident.str, thd->lex->ident.length))
+ DBUG_RETURN(1);
+ int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
+ Query_log_event qinfo(thd, log_query.c_ptr_safe(), log_query.length(),
+ TRUE, FALSE, TRUE, errcode);
+ DBUG_RETURN(mysql_bin_log.write(&qinfo));
+ }
+ 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 = !memcmp(log_name, 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->is_current_stmt_binlog_format_row() ||
+ 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 || ending_single_stmt_trans(thd, all));
+}
+
+/**
+ This function checks if 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 single statement transaction, otherwise
+ @c false.
+*/
+bool ending_single_stmt_trans(THD* thd, const bool all)
+{
+ return (!all && !thd->in_multi_stmt_transaction_mode());
+}
+
+/**
+ 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;
+ int old_max_allowed_packet= thd->variables.max_allowed_packet;
+ 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);
+ thd->variables.max_allowed_packet= old_max_allowed_packet;
+ 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),
+ 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);
+ my_free(name);
+ name= NULL;
+ 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((void *) save_name);
+
+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);
+ 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);
+
+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);
+ }
+
+ 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)) ||
+ (thd->lex->sql_command != SQLCOM_ROLLBACK_TO_SAVEPOINT &&
+ thd->lex->sql_command != SQLCOM_SAVEPOINT &&
+ !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);
+
+ if (thd->lex->stmt_accessed_non_trans_temp_table())
+ cache_data->set_changes_to_non_trans_temp_table();
+
+ 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();
+ }
+ }
+
+ 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;
+ my_free(name);
+ name= NULL;
+ 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(cache_mngr);
+ 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_mode())
+ 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++;
+ 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: %lu",
+ 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_access_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;
+ /*
+ Identifies if a table is changed.
+ */
+ my_bool is_write= FALSE;
+ /*
+ A pointer to a previous table that was changed.
+ */
+ TABLE* prev_write_table= NULL;
+ /*
+ A pointer to a previous table that was accessed.
+ */
+ 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 ||
+ table->table->s->table_category == TABLE_CATEGORY_LOG)
+ 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;
+
+ my_bool trans= table->table->file->has_transactions();
+
+ if (table->table->s->tmp_table)
+ lex->set_stmt_accessed_table(trans ? LEX::STMT_WRITES_TEMP_TRANS_TABLE :
+ LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE);
+ else
+ lex->set_stmt_accessed_table(trans ? LEX::STMT_WRITES_TRANS_TABLE :
+ LEX::STMT_WRITES_NON_TRANS_TABLE);
+
+ flags_write_all_set &= flags;
+ flags_write_some_set |= flags;
+ is_write= TRUE;
+
+ prev_write_table= table->table;
+
+ }
+ flags_access_some_set |= flags;
+
+ if (lex->sql_command != SQLCOM_CREATE_TABLE ||
+ (lex->sql_command == SQLCOM_CREATE_TABLE &&
+ (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)))
+ {
+ my_bool trans= table->table->file->has_transactions();
+
+ if (table->table->s->tmp_table)
+ lex->set_stmt_accessed_table(trans ? LEX::STMT_READS_TEMP_TRANS_TABLE :
+ LEX::STMT_READS_TEMP_NON_TRANS_TABLE);
+ else
+ lex->set_stmt_accessed_table(trans ? LEX::STMT_READS_TRANS_TABLE :
+ LEX::STMT_READS_NON_TRANS_TABLE);
+ }
+
+ if (prev_access_table && prev_access_table->file->ht !=
+ table->table->file->ht)
+ multi_access_engine= TRUE;
+
+ 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_access_some_set: 0x%llx", flags_access_some_set));
+ DBUG_PRINT("info", ("multi_write_engine: %d", multi_write_engine));
+ DBUG_PRINT("info", ("multi_access_engine: %d", multi_access_engine));
+
+ int error= 0;
+ int unsafe_flags;
+
+ bool multi_stmt_trans= in_multi_stmt_transaction_mode();
+ bool trans_table= trans_has_updated_trans_table(this);
+ bool binlog_direct= variables.binlog_direct_non_trans_update;
+
+ if (lex->is_mixed_stmt_unsafe(multi_stmt_trans, binlog_direct,
+ trans_table, tx_isolation))
+ lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_MIXED_STATEMENT);
+ else if (multi_stmt_trans && trans_table && !binlog_direct &&
+ lex->stmt_accessed_table(LEX::STMT_WRITES_NON_TRANS_TABLE))
+ 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_access_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 &&
+ sqlcom_can_generate_row_events(this))
+ {
+ /*
+ 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 &&
+ sqlcom_can_generate_row_events(this))
+ {
+ /*
+ 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 (is_write && (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 = %lu "
+ "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(m_memory);
+ }
+
+ /**
+ 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];
+ };
+
+CPP_UNNAMED_NS_END
+
+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);
+ 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(LEX::BINLOG_STMT_UNSAFE_COUNT <=
+ sizeof(binlog_unsafe_warning_flags) * CHAR_BIT);
+
+ uint32 unsafe_type_flags= binlog_unsafe_warning_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());
+ }
+ }
+ }
+ 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 in three places 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:
+
+ 1 - sp_head::execute_procedure which prints out warnings for calls to
+ stored procedures.
+
+ 2 - sp_head::execute_function which prints out warnings for calls
+ involving functions.
+
+ 3 - THD::binlog_query (here) which prints warning for top level
+ statements not covered by the two cases above: i.e., if not insided a
+ procedure and a function.
+
+ Besides, we should not try to print these warnings if it is not
+ possible to write statements to the binary log as it happens when
+ the execution is inside a function, or generaly speaking, when
+ the variables.option_bits & OPTION_BIN_LOG is false.
+
+ */
+ if ((variables.option_bits & OPTION_BIN_LOG) &&
+ spcont == NULL && !binlog_evt_union.do_union)
+ 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);
+ /*
+ 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-09-10 07:27:02 +0000
@@ -0,0 +1,266 @@
+#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;
+
+ /* 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)
+
+ 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); // binary log write
+ 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 ending_single_stmt_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);
+bool sqlcom_can_generate_row_events(const THD *thd);
+
+#endif /* BINLOG_H_INCLUDED */
=== modified file 'sql/field.cc'
--- a/sql/field.cc 2010-08-23 09:56:21 +0000
+++ b/sql/field.cc 2010-09-10 07:27:02 +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-08-12 13:50:23 +0000
+++ b/sql/ha_ndbcluster_binlog.cc 2010-09-10 07:27:02 +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-08-19 08:22:23 +0000
+++ b/sql/ha_partition.cc 2010-09-10 07:27:02 +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-08-25 10:22:34 +0000
+++ b/sql/item_func.cc 2010-09-10 07:27:02 +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-08-30 08:36:02 +0000
+++ b/sql/log.cc 2010-09-10 07:27:02 +0000
@@ -28,15 +28,11 @@
#include "sql_priv.h"
#include "log.h"
#include "sql_base.h" // open_log_table
-#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,212 +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), changes_to_non_trans_temp_table_flag(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 set_changes_to_non_trans_temp_table()
- {
- changes_to_non_trans_temp_table_flag= TRUE;
- }
-
- bool changes_to_non_trans_temp_table()
- {
- return (changes_to_non_trans_temp_table_flag);
- }
-
- void reset()
- {
- truncate(0);
- changes_to_non_trans_temp_table_flag= FALSE;
- 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;
-
- /*
- This flag indicates if the cache has changes to temporary tables.
- @TODO This a temporary fix and should be removed after BUG#54562.
- */
- bool changes_to_non_trans_temp_table_flag;
-
- /*
- 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) {
@@ -1409,3985 +1184,759 @@ 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(cache_mngr);
- 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);
-
- 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 (memcmp(file_info->name, 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.
+ /* 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;
+ }
- @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;
+ next= max_found + 1;
+ sprintf(ext_buf, "%06lu", next);
+ *end++='.';
- 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 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))
{
- if (cache_mngr->trx_cache.has_incident())
- error= mysql_bin_log.write_incident(thd, TRUE);
+ 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;
+ }
- cache_mngr->reset_cache(&cache_mngr->trx_cache);
+ sprintf(end, "%06lu", next);
- 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();
+ /* 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)
-{
- 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);
+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);
- 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;
- 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_mode()),
- 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);
+ my_free(name);
+ name= NULL;
+ 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);
+}
- @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.
+/*
+ Close the log file
- @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)));
+ 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.
- /*
- 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);
- }
+ 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
+*/
- if (cache_mngr->trx_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)
{
- /*
- 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);
- }
+ end_io_cache(&log_file);
- 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 OPTION_KEEP_LOG is active or;
- . the format is STMT and a non-trans table was updated or;
- . the format is MIXED and a temporary non-trans table was
- updated or;
- . the format is MIXED, non-trans table was updated and
- aborting a single statement transaction;
- */
+ 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 (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) ||
- (cache_mngr->trx_cache.changes_to_non_trans_temp_table() &&
- thd->variables.binlog_format == BINLOG_FORMAT_MIXED) ||
- (trans_has_updated_non_trans_table(thd) &&
- ending_single_stmt_trans(thd,all) &&
- thd->variables.binlog_format == BINLOG_FORMAT_MIXED)))
+ 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 active and;
- . the format is not STMT or no non-trans table was
- updated and;
- . the format is not MIXED or no temporary non-trans table
- was 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) &&
- (!cache_mngr->trx_cache.changes_to_non_trans_temp_table() ||
- thd->variables.binlog_format != BINLOG_FORMAT_MIXED)))
- 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;
+ my_free(name);
+ name= NULL;
+ 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).
-*/
-static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv)
-{
- DBUG_ENTER("binlog_savepoint_set");
+/*
+ Reopen the log file
+
+ SYNOPSIS
+ reopen_file()
- binlog_trans_log_savepos(thd, (my_off_t*) sv);
- /* Write it to the binary log */
+ DESCRIPTION
+ Reopen the log file. The method is used during FLUSH LOGS
+ and locks LOCK_log mutex
+*/
- String log_query;
- if (log_query.append(STRING_WITH_LEN("SAVEPOINT ")) ||
- log_query.append(thd->lex->ident.str, thd->lex->ident.length))
- DBUG_RETURN(1);
- int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
- Query_log_event qinfo(thd, log_query.c_ptr_safe(), log_query.length(),
- TRUE, FALSE, TRUE, errcode);
- DBUG_RETURN(mysql_bin_log.write(&qinfo));
-}
-static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv)
+void MYSQL_QUERY_LOG::reopen_file()
{
- DBUG_ENTER("binlog_savepoint_rollback");
+ char *save_name;
- /*
- 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)))
+ DBUG_ENTER("MYSQL_LOG::reopen_file");
+ if (!is_open())
{
- String log_query;
- if (log_query.append(STRING_WITH_LEN("ROLLBACK TO ")) ||
- log_query.append(thd->lex->ident.str, thd->lex->ident.length))
- DBUG_RETURN(1);
- int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
- Query_log_event qinfo(thd, log_query.c_ptr_safe(), log_query.length(),
- TRUE, FALSE, TRUE, errcode);
- DBUG_RETURN(mysql_bin_log.write(&qinfo));
+ DBUG_PRINT("info",("log is closed"));
+ DBUG_VOID_RETURN;
}
- binlog_trans_log_truncate(thd, *(my_off_t*)sv);
- DBUG_RETURN(0);
-}
+ mysql_mutex_lock(&LOCK_log);
-int check_binlog_magic(IO_CACHE* log, const char** errmsg)
-{
- char magic[4];
- DBUG_ASSERT(my_b_tell(log) == 0);
+ save_name= name;
+ name= 0; // Don't free name
+ close(LOG_CLOSE_TO_BE_OPENED);
- 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 (memcmp(file_info->name, 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.
-
- 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
-
- 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
-*/
-
-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);
- my_free(name);
- name= NULL;
- 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;
- my_free(name);
- name= NULL;
- 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);
-
- 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)
-{
- 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),
- 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);
- my_free(name);
- name= NULL;
- 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((void *) save_name);
-
-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);
- 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);
-
-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->is_current_stmt_binlog_format_row() ||
- 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 || ending_single_stmt_trans(thd, all));
-}
-
-/**
- This function checks if 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 single statement transaction, otherwise
- @c false.
-*/
-bool ending_single_stmt_trans(THD* thd, const bool all)
-{
- return (!all && !thd->in_multi_stmt_transaction_mode());
-}
-
-/**
- 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(cache_mngr);
- 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_mode())
- 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++;
- 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);
- }
-
- 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)) ||
- (thd->lex->sql_command != SQLCOM_ROLLBACK_TO_SAVEPOINT &&
- thd->lex->sql_command != SQLCOM_SAVEPOINT &&
- !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);
-
- if (thd->lex->stmt_accessed_non_trans_temp_table())
- cache_data->set_changes_to_non_trans_temp_table();
-
- 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();
- }
- }
-
- 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)))
- {
- if ((thd->variables.option_bits & OPTION_LOG_OFF)
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- && (sctx->master_access & SUPER_ACL)
-#endif
- )
- {
- /* No logging */
- return FALSE;
- }
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-
-bool general_log_print(THD *thd, enum enum_server_command command,
- const char *format, ...)
-{
- 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;
-}
-
-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
+ Note that at this point, log_state != LOG_CLOSED (important for is_open()).
*/
- 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++;
+ open(save_name, log_type, 0, io_cache_type);
+ my_free(save_name);
+
mysql_mutex_unlock(&LOCK_log);
- return res;
+
+ DBUG_VOID_RETURN;
}
/*
- Write the contents of a cache to the binary log.
+ Write a command to traditional general 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()
+
+ 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
- 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 given command to to normal (not rotable) log file
- group= (uint)my_b_tell(&log_file);
- hdr_offs= carry= 0;
+ RETURN
+ FASE - OK
+ TRUE - error occured
+*/
- do
+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())
{
- /*
- 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);
+ /* for testing output of timestamp and thread id */
+ DBUG_EXECUTE_IF("reset_log_last_time", last_time= 0;);
- /* assemble both halves */
- memcpy(&header[carry], (char *)cache->read_pos, LOG_EVENT_HEADER_LEN - carry);
+ /* Note that my_b_write() assumes it knows the length for this */
+ if (event_time != last_time)
+ {
+ last_time= event_time;
- /* fix end_log_pos */
- val= uint4korr(&header[LOG_POS_OFFSET]) + group;
- int4store(&header[LOG_POS_OFFSET], val);
+ localtime_r(&event_time, &start);
- /* write the first half of the split header */
- if (my_b_write(&log_file, header, carry))
- return ER_ERROR_ON_WRITE;
+ 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);
- /*
- 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 (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;
- /* next event header at ... */
- hdr_offs = uint4korr(&header[EVENT_LEN_OFFSET]) - carry;
+ /* command_type, thread_id */
+ length= my_snprintf(buff, 32, "%5ld ", (long) thread_id);
- carry= 0;
- }
+ if (my_b_write(&log_file, (uchar*) buff, length))
+ goto err;
- /* if there is anything to write, process it. */
+ if (my_b_write(&log_file, (uchar*) command_type, command_type_len))
+ goto err;
- if (likely(length > 0))
- {
- /*
- process all event-headers in this (partial) cache.
- if next header is beyond current read-buffer,
- we'll get it later (though not necessarily in the
- very next iteration, just "eventually").
- */
+ if (my_b_write(&log_file, (uchar*) "\t", 1))
+ goto err;
- while (hdr_offs < length)
- {
- /*
- partial header only? save what we can get, process once
- we get the rest.
- */
+ /* sql_text */
+ if (my_b_write(&log_file, (uchar*) sql_text, sql_text_len))
+ goto err;
- 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);
+ if (my_b_write(&log_file, (uchar*) "\n", 1) ||
+ flush_io_cache(&log_file))
+ goto err;
+ }
- }
- }
+ mysql_mutex_unlock(&LOCK_log);
+ return FALSE;
+err:
- /*
- 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;
- }
+ if (!write_error)
+ {
+ write_error= 1;
+ sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
+ }
+ mysql_mutex_unlock(&LOCK_log);
+ return TRUE;
+}
- /* 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);
+/*
+ Log a query to the traditional slow log file
- if (sync_log)
- return flush_and_sync(0);
+ SYNOPSIS
+ write()
- return 0; // All OK
-}
+ 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
-/*
- 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;
+ DESCRIPTION
- /* 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();
- }
+ Log a query to the slow log file.
- return error;
-}
+ RETURN
+ FALSE - OK
+ TRUE - error occured
+*/
-bool MYSQL_BIN_LOG::write_incident(THD *thd, bool lock)
+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)
{
- 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)
+ bool error= 0;
+ DBUG_ENTER("MYSQL_QUERY_LOG::write");
+
+ mysql_mutex_lock(&LOCK_log);
+
+ if (!is_open())
{
- 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(0);
}
- 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.
+ 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;
- @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.
-*/
+ if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT))
+ {
+ if (current_time != last_time)
+ {
+ last_time= current_time;
+ struct tm start;
+ localtime_r(¤t_time, &start);
-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);
+ 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);
+ }
+ }
- 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)
+ 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)
{
- /*
- 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;
- my_free(name);
- name= NULL;
- 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.
@@ -5536,14 +2085,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)
@@ -6176,232 +2717,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-08-20 11:22:46 +0000
+++ b/sql/log.h 2010-09-10 07:27:02 +0000
@@ -19,18 +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 ending_single_stmt_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
@@ -145,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
@@ -179,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 };
@@ -255,218 +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;
-
- /* 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)
-
- 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); // binary log write
- 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
{
@@ -665,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-08-23 22:31:12 +0000
+++ b/sql/log_event.cc 2010-09-10 07:27:02 +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-07-15 13:47:50 +0000
+++ b/sql/log_event.h 2010-09-10 07:27:02 +0000
@@ -1782,8 +1782,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-09-02 18:37:04 +0000
+++ b/sql/mysqld.cc 2010-09-10 07:27:02 +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"
@@ -497,7 +496,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,
@@ -1551,10 +1549,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);
@@ -3555,10 +3549,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);
@@ -5825,9 +5815,6 @@ struct my_option my_long_options[]=
&master_retry_count, &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.",
- &rpl_status, &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.",
&max_binlog_dump_events, &max_binlog_dump_events, 0,
@@ -6043,13 +6030,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;
@@ -6470,9 +6450,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},
@@ -7670,7 +7647,7 @@ PSI_mutex_key key_BINLOG_LOCK_index, key
key_LOCK_gdl, key_LOCK_global_read_lock, key_LOCK_global_system_variables,
key_LOCK_manager,
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,
@@ -7709,7 +7686,6 @@ static PSI_mutex_info all_server_mutexes
{ &key_LOCK_global_system_variables, "LOCK_global_system_variables", PSI_FLAG_GLOBAL},
{ &key_LOCK_manager, "LOCK_manager", 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},
@@ -7755,7 +7731,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_rpl_status, key_COND_server_started,
+ 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,
@@ -7779,7 +7755,6 @@ static PSI_cond_info all_server_conds[]=
{ &key_COND_cache_status_changed, "Query_cache::COND_cache_status_changed", 0},
{ &key_COND_global_read_lock, "COND_global_read_lock", PSI_FLAG_GLOBAL},
{ &key_COND_manager, "COND_manager", 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-08-30 14:07:40 +0000
+++ b/sql/mysqld.h 2010-09-10 07:27:02 +0000
@@ -175,7 +175,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;
+extern ulong thread_cache_size;
extern ulong back_log;
extern char language[FN_REFLEN];
extern ulong server_id, concurrency;
@@ -230,7 +230,7 @@ extern PSI_mutex_key key_BINLOG_LOCK_ind
key_LOCK_gdl, key_LOCK_global_read_lock, key_LOCK_global_system_variables,
key_LOCK_logger, key_LOCK_manager,
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,
@@ -249,7 +249,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_rpl_status, key_COND_server_started,
+ 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-07-23 20:13:36 +0000
+++ b/sql/repl_failsafe.cc 1970-01-01 00:00:00 +0000
@@ -1,645 +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
-};
-
-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.
-*/
-
-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);
- 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);
-}
-
-#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);
-}
-
-
-/**
- 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-07-23 20:13:36 +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;
-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-07-15 19:29:25 +0000
+++ b/sql/rpl_handler.cc 2010-09-10 07:27:02 +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-07-08 21:20:08 +0000
+++ b/sql/rpl_injector.cc 2010-09-10 07:27:02 +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-09-10 07:27:02 +0000
@@ -0,0 +1,1270 @@
+/* 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);
+}
+
+#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);
+ 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
+ int old_max_allowed_packet= thd->variables.max_allowed_packet;
+ 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);
+ thd->variables.max_allowed_packet= old_max_allowed_packet;
+ 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));
+ thd->variables.max_allowed_packet= old_max_allowed_packet;
+
+ 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-09-10 07:27:02 +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-07-15 13:47:50 +0000
+++ b/sql/rpl_mi.cc 2010-09-10 07:27:02 +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-07-24 13:31:48 +0000
+++ b/sql/rpl_record.cc 2010-09-10 07:27:02 +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-07-27 10:25:53 +0000
+++ b/sql/rpl_rli.cc 2010-09-10 07:27:02 +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
@@ -1276,4 +1276,32 @@ void Relay_log_info::slave_close_thread_
thd->mdl_context.release_transactional_locks();
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-03-31 14:05:33 +0000
+++ b/sql/rpl_rli.h 2010-09-10 07:27:02 +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-07-29 12:32:11 +0000
+++ b/sql/rpl_slave.cc 2010-09-10 07:27:02 +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>
@@ -3028,8 +3026,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
mysql_mutex_lock(&LOCK_thread_count);
@@ -4213,8 +4209,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,
@@ -4235,7 +4229,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);
}
@@ -4944,6 +4937,612 @@ 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;
+ char saved_host[HOSTNAME_LENGTH + 1];
+ uint saved_port;
+ char saved_log_name[FN_REFLEN];
+ my_off_t saved_log_pos;
+ 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
+ */
+
+ /*
+ Before processing the command, save the previous state.
+ */
+ strmake(saved_host, mi->host, HOSTNAME_LENGTH);
+ saved_port= mi->port;
+ strmake(saved_log_name, mi->master_log_name, FN_REFLEN - 1);
+ saved_log_pos= mi->master_log_pos;
+
+ /*
+ 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();
+
+ sql_print_information("'CHANGE MASTER TO executed'. "
+ "Previous state master_host='%s', master_port='%u', master_log_file='%s', "
+ "master_log_pos='%ld'. "
+ "New state master_host='%s', master_port='%u', master_log_file='%s', "
+ "master_log_pos='%ld'.", saved_host, saved_port, saved_log_name,
+ (ulong) saved_log_pos, mi->host, mi->port, mi->master_log_name,
+ (ulong) mi->master_log_pos);
+
+ /*
+ 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-03-31 14:05:33 +0000
+++ b/sql/rpl_slave.h 2010-09-10 07:27:02 +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-09-09 12:37:09 +0000
+++ b/sql/set_var.h 2010-09-10 07:27:02 +0000
@@ -171,6 +171,7 @@ protected:
{ return ((uchar*)&global_system_variables) + offset; }
};
+#include "binlog.h" /* mysql_bin_log */
#include "sql_plugin.h" /* SHOW_HA_ROWS, SHOW_MY_BOOL */
/****************************************************************************
=== modified file 'sql/sql_binlog.cc'
--- a/sql/sql_binlog.cc 2010-07-15 13:47:50 +0000
+++ b/sql/sql_binlog.cc 2010-09-10 07:27:02 +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-08-30 14:07:40 +0000
+++ b/sql/sql_class.cc 2010-09-10 07:27:02 +0000
@@ -39,7 +39,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"
@@ -3611,1002 +3611,3 @@ void xid_cache_delete(XID_STATE *xid_sta
my_hash_delete(&xid_cache, (uchar *)xid_state);
mysql_mutex_unlock(&LOCK_xid_cache);
}
-
-
-/**
- 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: %lu",
- 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_access_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;
- /*
- Identifies if a table is changed.
- */
- my_bool is_write= FALSE;
- /*
- A pointer to a previous table that was changed.
- */
- TABLE* prev_write_table= NULL;
- /*
- A pointer to a previous table that was accessed.
- */
- 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 ||
- table->table->s->table_category == TABLE_CATEGORY_LOG)
- 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;
-
- my_bool trans= table->table->file->has_transactions();
-
- if (table->table->s->tmp_table)
- lex->set_stmt_accessed_table(trans ? LEX::STMT_WRITES_TEMP_TRANS_TABLE :
- LEX::STMT_WRITES_TEMP_NON_TRANS_TABLE);
- else
- lex->set_stmt_accessed_table(trans ? LEX::STMT_WRITES_TRANS_TABLE :
- LEX::STMT_WRITES_NON_TRANS_TABLE);
-
- flags_write_all_set &= flags;
- flags_write_some_set |= flags;
- is_write= TRUE;
-
- prev_write_table= table->table;
-
- }
- flags_access_some_set |= flags;
-
- if (lex->sql_command != SQLCOM_CREATE_TABLE ||
- (lex->sql_command == SQLCOM_CREATE_TABLE &&
- (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)))
- {
- my_bool trans= table->table->file->has_transactions();
-
- if (table->table->s->tmp_table)
- lex->set_stmt_accessed_table(trans ? LEX::STMT_READS_TEMP_TRANS_TABLE :
- LEX::STMT_READS_TEMP_NON_TRANS_TABLE);
- else
- lex->set_stmt_accessed_table(trans ? LEX::STMT_READS_TRANS_TABLE :
- LEX::STMT_READS_NON_TRANS_TABLE);
- }
-
- if (prev_access_table && prev_access_table->file->ht !=
- table->table->file->ht)
- multi_access_engine= TRUE;
-
- 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_access_some_set: 0x%llx", flags_access_some_set));
- DBUG_PRINT("info", ("multi_write_engine: %d", multi_write_engine));
- DBUG_PRINT("info", ("multi_access_engine: %d", multi_access_engine));
-
- int error= 0;
- int unsafe_flags;
-
- bool multi_stmt_trans= in_multi_stmt_transaction_mode();
- bool trans_table= trans_has_updated_trans_table(this);
- bool binlog_direct= variables.binlog_direct_non_trans_update;
-
- if (lex->is_mixed_stmt_unsafe(multi_stmt_trans, binlog_direct,
- trans_table, tx_isolation))
- lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_MIXED_STATEMENT);
- else if (multi_stmt_trans && trans_table && !binlog_direct &&
- lex->stmt_accessed_table(LEX::STMT_WRITES_NON_TRANS_TABLE))
- 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_access_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 &&
- sqlcom_can_generate_row_events(this))
- {
- /*
- 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 &&
- sqlcom_can_generate_row_events(this))
- {
- /*
- 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 (is_write && (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 = %lu "
- "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
-
-/* Declare in unnamed namespace. */
-CPP_UNNAMED_NS_START
-
- /**
- 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(m_memory);
- }
-
- /**
- 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];
- };
-
-CPP_UNNAMED_NS_END
-
-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);
- 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(LEX::BINLOG_STMT_UNSAFE_COUNT <=
- sizeof(binlog_unsafe_warning_flags) * CHAR_BIT);
-
- uint32 unsafe_type_flags= binlog_unsafe_warning_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());
- }
- }
- }
- 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 in three places 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:
-
- 1 - sp_head::execute_procedure which prints out warnings for calls to
- stored procedures.
-
- 2 - sp_head::execute_function which prints out warnings for calls
- involving functions.
-
- 3 - THD::binlog_query (here) which prints warning for top level
- statements not covered by the two cases above: i.e., if not insided a
- procedure and a function.
-
- Besides, we should not try to print these warnings if it is not
- possible to write statements to the binary log as it happens when
- the execution is inside a function, or generaly speaking, when
- the variables.option_bits & OPTION_BIN_LOG is false.
-
- */
- if ((variables.option_bits & OPTION_BIN_LOG) &&
- spcont == NULL && !binlog_evt_union.do_union)
- 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);
- /*
- 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-07-19 08:27:53 +0000
+++ b/sql/sql_db.cc 2010-09-10 07:27:02 +0000
@@ -34,6 +34,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-09-01 13:12:42 +0000
+++ b/sql/sql_insert.cc 2010-09-10 07:27:02 +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-08-03 03:49:14 +0000
+++ b/sql/sql_load.cc 2010-09-10 07:27:02 +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-08-25 10:22:34 +0000
+++ b/sql/sql_parse.cc 2010-09-10 07:27:02 +0000
@@ -79,9 +79,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>
@@ -2271,14 +2271,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
@@ -2289,7 +2284,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))
=== modified file 'sql/sql_parse.h'
--- a/sql/sql_parse.h 2010-08-18 11:29:04 +0000
+++ b/sql/sql_parse.h 2010-09-10 07:27:02 +0000
@@ -78,7 +78,6 @@ bool check_host_name(LEX_STRING *str);
bool check_identifier_name(LEX_STRING *str, uint max_char_length,
uint err_code, const char *param_for_err_msg);
bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length);
-bool sqlcom_can_generate_row_events(const THD *thd);
bool is_update_query(enum enum_sql_command command);
bool is_log_table_write_query(enum enum_sql_command command);
bool alloc_query(THD *thd, const char *packet, uint packet_length);
=== modified file 'sql/sql_reload.cc'
--- a/sql/sql_reload.cc 2010-08-13 09:51:48 +0000
+++ b/sql/sql_reload.cc 2010-09-10 07:27:02 +0000
@@ -23,7 +23,9 @@
#include "sql_base.h" // close_cached_tables
#include "sql_db.h" // my_dbopt_cleanup
#include "hostname.h" // hostname_cache_refresh
-#include "sql_repl.h" // reset_master, reset_slave
+#include "rpl_rli.h" // rotate_relay_log
+#include "rpl_master.h" // reset_master
+#include "rpl_slave.h" // reset_slave
#include "debug_sync.h"
=== removed file 'sql/sql_repl.cc'
--- a/sql/sql_repl.cc 2010-07-26 10:52:59 +0000
+++ b/sql/sql_repl.cc 1970-01-01 00:00:00 +0000
@@ -1,2030 +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 = !memcmp(log_name, 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 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
- int old_max_allowed_packet= thd->variables.max_allowed_packet;
- 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);
- thd->variables.max_allowed_packet= old_max_allowed_packet;
- 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));
- thd->variables.max_allowed_packet= old_max_allowed_packet;
-
- my_message(my_errno, errmsg, MYF(0));
- DBUG_VOID_RETURN;
-}
-
-
-/**
- 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
-
-*/
-
-
-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 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;
- char saved_host[HOSTNAME_LENGTH + 1];
- uint saved_port;
- char saved_log_name[FN_REFLEN];
- my_off_t saved_log_pos;
- 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
- */
-
- /*
- Before processing the command, save the previous state.
- */
- strmake(saved_host, mi->host, HOSTNAME_LENGTH);
- saved_port= mi->port;
- strmake(saved_log_name, mi->master_log_name, FN_REFLEN - 1);
- saved_log_pos= mi->master_log_pos;
-
- /*
- 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();
-
- sql_print_information("'CHANGE MASTER TO executed'. "
- "Previous state master_host='%s', master_port='%u', master_log_file='%s', "
- "master_log_pos='%ld'. "
- "New state master_host='%s', master_port='%u', master_log_file='%s', "
- "master_log_pos='%ld'.", saved_host, saved_port, saved_log_name,
- (ulong) saved_log_pos, mi->host, mi->port, mi->master_log_name,
- (ulong) mi->master_log_pos);
-
- /*
- 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);
-}
-
-
-/**
- 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 BINLOG EVENTS statement.
-
- @param thd Pointer to THD object for the client thread executing the
- statement.
-
- @retval FALSE success
- @retval TRUE failure
-*/
-bool mysql_show_binlog_events(THD* thd)
-{
- Protocol *protocol= thd->protocol;
- List<Item> field_list;
- const char *errmsg = 0;
- bool ret = TRUE;
- IO_CACHE log;
- File file = -1;
- MYSQL_BIN_LOG *binary_log= NULL;
- int old_max_allowed_packet= thd->variables.max_allowed_packet;
- DBUG_ENTER("mysql_show_binlog_events");
-
- Log_event::init_show_field_list(&field_list);
- if (protocol->send_result_set_metadata(&field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
- DBUG_RETURN(TRUE);
-
- Format_description_log_event *description_event= new
- Format_description_log_event(3); /* MySQL 4.0 by default */
-
- DBUG_ASSERT(thd->lex->sql_command == SQLCOM_SHOW_BINLOG_EVENTS ||
- thd->lex->sql_command == SQLCOM_SHOW_RELAYLOG_EVENTS);
-
- /* select wich binary log to use: binlog or relay */
- if ( thd->lex->sql_command == SQLCOM_SHOW_BINLOG_EVENTS )
- {
- /*
- Wait for handlers to insert any pending information
- into the binlog. For e.g. ndb which updates the binlog asynchronously
- this is needed so that the uses sees all its own commands in the binlog
- */
- ha_binlog_wait(thd);
-
- binary_log= &mysql_bin_log;
- }
- else /* showing relay log contents */
- {
- if (!active_mi)
- DBUG_RETURN(TRUE);
-
- binary_log= &(active_mi->rli.relay_log);
- }
-
- if (binary_log->is_open())
- {
- LEX_MASTER_INFO *lex_mi= &thd->lex->mi;
- SELECT_LEX_UNIT *unit= &thd->lex->unit;
- ha_rows event_count, limit_start, limit_end;
- my_off_t pos = max(BIN_LOG_HEADER_SIZE, lex_mi->pos); // user-friendly
- char search_file_name[FN_REFLEN], *name;
- const char *log_file_name = lex_mi->log_file_name;
- mysql_mutex_t *log_lock = binary_log->get_log_lock();
- LOG_INFO linfo;
- Log_event* ev;
-
- unit->set_limit(thd->lex->current_select);
- limit_start= unit->offset_limit_cnt;
- limit_end= unit->select_limit_cnt;
-
- name= search_file_name;
- if (log_file_name)
- binary_log->make_log_name(search_file_name, log_file_name);
- else
- name=0; // Find first log
-
- linfo.index_file_offset = 0;
-
- if (binary_log->find_log_pos(&linfo, name, 1))
- {
- errmsg = "Could not find target log";
- goto err;
- }
-
- mysql_mutex_lock(&LOCK_thread_count);
- thd->current_linfo = &linfo;
- mysql_mutex_unlock(&LOCK_thread_count);
-
- if ((file=open_binlog(&log, linfo.log_file_name, &errmsg)) < 0)
- goto err;
-
- /*
- to account binlog event header size
- */
- thd->variables.max_allowed_packet += MAX_LOG_EVENT_HEADER;
-
- mysql_mutex_lock(log_lock);
-
- /*
- open_binlog() sought to position 4.
- Read the first event in case it's a Format_description_log_event, to
- know the format. If there's no such event, we are 3.23 or 4.x. This
- code, like before, can't read 3.23 binlogs.
- This code will fail on a mixed relay log (one which has Format_desc then
- Rotate then Format_desc).
- */
- ev= Log_event::read_log_event(&log, (mysql_mutex_t*)0, description_event);
- if (ev)
- {
- if (ev->get_type_code() == FORMAT_DESCRIPTION_EVENT)
- {
- delete description_event;
- description_event= (Format_description_log_event*) ev;
- }
- else
- delete ev;
- }
-
- my_b_seek(&log, pos);
-
- if (!description_event->is_valid())
- {
- errmsg="Invalid Format_description event; could be out of memory";
- goto err;
- }
-
- for (event_count = 0;
- (ev = Log_event::read_log_event(&log, (mysql_mutex_t*) 0,
- description_event)); )
- {
- if (event_count >= limit_start &&
- ev->net_send(protocol, linfo.log_file_name, pos))
- {
- errmsg = "Net error";
- delete ev;
- mysql_mutex_unlock(log_lock);
- goto err;
- }
-
- pos = my_b_tell(&log);
- delete ev;
-
- if (++event_count >= limit_end)
- break;
- }
-
- if (event_count < limit_end && log.error)
- {
- errmsg = "Wrong offset or I/O error";
- mysql_mutex_unlock(log_lock);
- goto err;
- }
-
- mysql_mutex_unlock(log_lock);
- }
-
- ret= FALSE;
-
-err:
- delete description_event;
- if (file >= 0)
- {
- end_io_cache(&log);
- mysql_file_close(file, MYF(MY_WME));
- }
-
- if (errmsg)
- my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0),
- "SHOW BINLOG EVENTS", errmsg);
- else
- my_eof(thd);
-
- mysql_mutex_lock(&LOCK_thread_count);
- thd->current_linfo = 0;
- mysql_mutex_unlock(&LOCK_thread_count);
- thd->variables.max_allowed_packet= old_max_allowed_packet;
- DBUG_RETURN(ret);
-}
-
-
-/**
- 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);
-}
-
-/**
- Load data's io cache specific hook to be executed
- before a chunk of data is being read into the cache's buffer
- The fuction instantianates and writes into the binlog
- replication events along LOAD DATA processing.
-
- @param file pointer to io-cache
- @retval 0 success
- @retval 1 failure
-*/
-int log_loaded_block(IO_CACHE* file)
-{
- DBUG_ENTER("log_loaded_block");
- LOAD_FILE_INFO *lf_info;
- uint block_len;
- /* buffer contains position where we started last read */
- uchar* buffer= (uchar*) my_b_get_buffer_start(file);
- uint max_event_size= current_thd->variables.max_allowed_packet;
- lf_info= (LOAD_FILE_INFO*) file->arg;
- if (lf_info->thd->is_current_stmt_binlog_format_row())
- DBUG_RETURN(0);
- if (lf_info->last_pos_in_file != HA_POS_ERROR &&
- lf_info->last_pos_in_file >= my_b_get_pos_in_file(file))
- DBUG_RETURN(0);
-
- for (block_len= (uint) (my_b_get_bytes_in_buffer(file)); block_len > 0;
- buffer += min(block_len, max_event_size),
- block_len -= min(block_len, max_event_size))
- {
- lf_info->last_pos_in_file= my_b_get_pos_in_file(file);
- if (lf_info->wrote_create_file)
- {
- Append_block_log_event a(lf_info->thd, lf_info->thd->db, buffer,
- min(block_len, max_event_size),
- lf_info->log_delayed);
- if (mysql_bin_log.write(&a))
- DBUG_RETURN(1);
- }
- else
- {
- Begin_load_query_log_event b(lf_info->thd, lf_info->thd->db,
- buffer,
- min(block_len, max_event_size),
- lf_info->log_delayed);
- if (mysql_bin_log.write(&b))
- DBUG_RETURN(1);
- lf_info->wrote_create_file= 1;
- }
- }
- DBUG_RETURN(0);
-}
-
-#endif /* HAVE_REPLICATION */
=== removed file 'sql/sql_repl.h'
--- a/sql/sql_repl.h 2010-03-31 14:05:33 +0000
+++ b/sql/sql_repl.h 1970-01-01 00:00:00 +0000
@@ -1,72 +0,0 @@
-/* Copyright (C) 2000-2006 MySQL AB & Sasha
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-
-#ifndef SQL_REPL_INCLUDED
-#define SQL_REPL_INCLUDED
-
-#include "rpl_filter.h"
-
-#ifdef HAVE_REPLICATION
-#include "slave.h"
-
-typedef struct st_slave_info
-{
- uint32 server_id;
- uint32 rpl_recovery_rank, master_id;
- char host[HOSTNAME_LENGTH+1];
- char user[USERNAME_LENGTH+1];
- char password[MAX_PASSWORD_LENGTH+1];
- uint16 port;
- THD* thd;
-} SLAVE_INFO;
-
-extern my_bool opt_show_slave_auth_info;
-extern char *master_host, *master_info_file;
-extern bool server_id_supplied;
-
-extern int max_binlog_dump_events;
-extern my_bool opt_sporadic_binlog_dump_fail;
-
-int start_slave(THD* thd, Master_info* mi, bool net_report);
-int stop_slave(THD* thd, Master_info* mi, bool net_report);
-bool change_master(THD* thd, Master_info* mi);
-bool mysql_show_binlog_events(THD* thd);
-int cmp_master_pos(const char* log_file_name1, ulonglong log_pos1,
- const char* log_file_name2, ulonglong log_pos2);
-int reset_slave(THD *thd, Master_info* mi);
-int reset_master(THD* thd);
-bool purge_master_logs(THD* thd, const char* to_log);
-bool purge_master_logs_before_date(THD* thd, time_t purge_time);
-bool log_in_use(const char* log_name);
-void adjust_linfo_offsets(my_off_t purge_offset);
-bool show_binlogs(THD* thd);
-extern int init_master_info(Master_info* mi);
-void kill_zombie_dump_threads(uint32 slave_server_id);
-int check_binlog_magic(IO_CACHE* log, const char** errmsg);
-
-typedef struct st_load_file_info
-{
- THD* thd;
- my_off_t last_pos_in_file;
- bool wrote_create_file, log_delayed;
-} LOAD_FILE_INFO;
-
-int log_loaded_block(IO_CACHE* file);
-int init_replication_sys_vars();
-void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, ushort flags);
-
-#endif /* HAVE_REPLICATION */
-
-#endif /* SQL_REPL_INCLUDED */
=== modified file 'sql/sql_servers.h'
--- a/sql/sql_servers.h 2010-03-31 14:05:33 +0000
+++ b/sql/sql_servers.h 2010-09-10 07:27:02 +0000
@@ -17,7 +17,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include "my_global.h" /* uint */
-#include "slave.h" // for tables_ok(), rpl_filter
+#include "rpl_slave.h" // for tables_ok(), rpl_filter
class THD;
typedef struct st_lex_server_options LEX_SERVER_OPTIONS;
=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc 2010-08-30 14:07:40 +0000
+++ b/sql/sql_show.cc 2010-09-10 07:27:02 +0000
@@ -26,7 +26,6 @@
#include "sql_table.h" // filename_to_tablename,
// primary_key_name,
// build_table_filename
-#include "repl_failsafe.h"
#include "sql_parse.h" // check_access, check_table_access
#include "sql_partition.h" // partition_element
#include "sql_db.h" // check_db_dir_existence, load_db_opt_by_name
=== modified file 'sql/sql_yacc.yy'
--- a/sql/sql_yacc.yy 2010-09-01 13:12:42 +0000
+++ b/sql/sql_yacc.yy 2010-09-10 07:27:02 +0000
@@ -44,7 +44,7 @@
#include "sql_acl.h" /* *_ACL */
#include "password.h" /* my_make_scrambled_password_323, my_make_scrambled_password */
#include "sql_class.h" /* Key_part_spec, enum_filetype, Diag_condition_item_name */
-#include "slave.h"
+#include "rpl_slave.h"
#include "lex_symbol.h"
#include "item_create.h"
#include "sp_head.h"
=== modified file 'sql/structs.h'
--- a/sql/structs.h 2010-03-31 14:05:33 +0000
+++ b/sql/structs.h 2010-09-10 07:27:02 +0000
@@ -358,8 +358,30 @@ public:
return tmp;
}
~Discrete_intervals_list() { empty(); };
- bool append(ulonglong start, ulonglong val, ulonglong incr);
- bool append(Discrete_interval *interval);
+ bool append(ulonglong start, ulonglong val, ulonglong incr)
+ {
+ /* first, see if this can be merged with previous */
+ if ((head == NULL) || tail->merge_if_contiguous(start, val, incr))
+ {
+ /* it cannot, so need to add a new interval */
+ Discrete_interval *new_interval= new Discrete_interval(start, val, incr);
+ return append(new_interval);
+ }
+ return 0;
+ }
+
+ bool append(Discrete_interval *new_interval)
+ {
+ if (unlikely(new_interval == NULL))
+ return 1;
+ if (head == NULL)
+ head= current= new_interval;
+ else
+ tail->next= new_interval;
+ tail= new_interval;
+ elements++;
+ return 0;
+ }
ulonglong minimum() const { return (head ? head->minimum() : 0); };
ulonglong maximum() const { return (head ? tail->maximum() : 0); };
uint nb_elements() const { return elements; }
=== modified file 'sql/sys_vars.cc'
--- a/sql/sys_vars.cc 2010-08-30 14:07:40 +0000
+++ b/sql/sys_vars.cc 2010-09-10 07:27:02 +0000
@@ -34,7 +34,7 @@
#include "events.h"
#include <thr_alarm.h>
-#include "slave.h"
+#include "rpl_slave.h"
#include "rpl_mi.h"
#include "transaction.h"
#include "mysqld.h"
@@ -1534,13 +1534,6 @@ static Sys_var_ulong Sys_div_precincreme
SESSION_VAR(div_precincrement), CMD_LINE(REQUIRED_ARG),
VALID_RANGE(0, DECIMAL_MAX_SCALE), DEFAULT(4), BLOCK_SIZE(1));
-static Sys_var_ulong Sys_rpl_recovery_rank(
- "rpl_recovery_rank", "Unused, will be removed",
- GLOBAL_VAR(rpl_recovery_rank), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(0, ULONG_MAX), DEFAULT(0), BLOCK_SIZE(1),
- NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(0),
- DEPRECATED(70000, 0));
-
static Sys_var_ulong Sys_range_alloc_block_size(
"range_alloc_block_size",
"Allocation block size for storing ranges during optimization",
Attachment: [text/bzr-bundle] bzr/zhenxing.he@sun.com-20100910072702-23h8mlixrt5tjf3e.bundle
| Thread |
|---|
| • bzr commit into mysql-5.5-bugfixing branch (zhenxing.he:3206) Bug#54649WL#5385 | He Zhenxing | 10 Sep |