774 lines
23 KiB
C++
774 lines
23 KiB
C++
/* Copyright (c) 2010, 2018, 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,
|
|
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
|
|
|
|
|
|
#ifdef HAVE_REPLICATION
|
|
#include "rpl_master.h"
|
|
|
|
#include "hash.h" // HASH
|
|
#include "m_string.h" // strmake
|
|
#include "auth_common.h" // check_global_access
|
|
#include "binlog.h" // mysql_bin_log
|
|
#include "debug_sync.h" // DEBUG_SYNC
|
|
#include "log.h" // sql_print_information
|
|
#include "mysqld_thd_manager.h" // Global_THD_manager
|
|
#include "rpl_binlog_sender.h" // Binlog_sender
|
|
#include "rpl_filter.h" // binlog_filter
|
|
#include "rpl_handler.h" // RUN_HOOK
|
|
#include "sql_class.h" // THD
|
|
#include "rpl_group_replication.h" // is_group_replication_running
|
|
|
|
#include "pfs_file_provider.h"
|
|
#include "mysql/psi/mysql_file.h"
|
|
|
|
|
|
int max_binlog_dump_events = 0; // unlimited
|
|
my_bool opt_sporadic_binlog_dump_fail = 0;
|
|
|
|
#define SLAVE_LIST_CHUNK 128
|
|
#define SLAVE_ERRMSG_SIZE (FN_REFLEN+64)
|
|
HASH slave_list;
|
|
extern TYPELIB binlog_checksum_typelib;
|
|
|
|
|
|
#define get_object(p, obj, msg) \
|
|
{\
|
|
uint len; \
|
|
if (p >= p_end) \
|
|
{ \
|
|
my_error(ER_MALFORMED_PACKET, MYF(0)); \
|
|
my_free(si); \
|
|
return 1; \
|
|
} \
|
|
len= (uint)*p++; \
|
|
if (p + len > p_end || len >= sizeof(obj)) \
|
|
{\
|
|
errmsg= msg;\
|
|
goto err; \
|
|
}\
|
|
strmake(obj,(char*) p,len); \
|
|
p+= len; \
|
|
}\
|
|
|
|
extern "C" uint32
|
|
*slave_list_key(SLAVE_INFO* si, size_t *len,
|
|
my_bool not_used MY_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)
|
|
{
|
|
int count;
|
|
|
|
count= array_elements(all_slave_list_mutexes);
|
|
mysql_mutex_register("sql", 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,
|
|
key_memory_SLAVE_INFO);
|
|
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, size_t 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(key_memory_SLAVE_INFO,
|
|
sizeof(SLAVE_INFO), MYF(MY_WME))))
|
|
goto err2;
|
|
|
|
/* 4 bytes for the server id */
|
|
if (p + 4 > p_end)
|
|
{
|
|
my_error(ER_MALFORMED_PACKET, MYF(0));
|
|
my_free(si);
|
|
return 1;
|
|
}
|
|
|
|
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, false, false/*need_lock_slave_list=false*/);
|
|
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_lock_slave_list)
|
|
{
|
|
if (thd->server_id)
|
|
{
|
|
if (need_lock_slave_list)
|
|
mysql_mutex_lock(&LOCK_slave_list);
|
|
else
|
|
mysql_mutex_assert_owner(&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_lock_slave_list)
|
|
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->get_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));
|
|
field_list.push_back(new Item_empty_string("Slave_UUID", UUID_LENGTH));
|
|
|
|
if (thd->send_result_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->start_row();
|
|
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);
|
|
|
|
/* get slave's UUID */
|
|
String slave_uuid;
|
|
if (get_slave_uuid(si->thd, &slave_uuid))
|
|
protocol->store(slave_uuid.c_ptr_safe(), &my_charset_bin);
|
|
if (protocol->end_row())
|
|
{
|
|
mysql_mutex_unlock(&LOCK_slave_list);
|
|
DBUG_RETURN(TRUE);
|
|
}
|
|
}
|
|
mysql_mutex_unlock(&LOCK_slave_list);
|
|
my_eof(thd);
|
|
DBUG_RETURN(FALSE);
|
|
}
|
|
|
|
/**
|
|
If there are less than BYTES bytes left to read in the packet,
|
|
report error.
|
|
*/
|
|
#define CHECK_PACKET_SIZE(BYTES) \
|
|
do { \
|
|
if (packet_bytes_todo < BYTES) \
|
|
goto error_malformed_packet; \
|
|
} while (0)
|
|
|
|
/**
|
|
Auxiliary macro used to define READ_INT and READ_STRING.
|
|
|
|
Check that there are at least BYTES more bytes to read, then read
|
|
the bytes using the given DECODER, then advance the reading
|
|
position.
|
|
*/
|
|
#define READ(DECODE, BYTES) \
|
|
do { \
|
|
CHECK_PACKET_SIZE(BYTES); \
|
|
DECODE; \
|
|
packet_position+= BYTES; \
|
|
packet_bytes_todo-= BYTES; \
|
|
} while (0)
|
|
|
|
|
|
/**
|
|
Check that there are at least BYTES more bytes to read, then read
|
|
the bytes and decode them into the given integer VAR, then advance
|
|
the reading position.
|
|
*/
|
|
#define READ_INT(VAR, BYTES) \
|
|
READ(VAR= uint ## BYTES ## korr(packet_position), BYTES)
|
|
|
|
/**
|
|
Check that there are at least BYTES more bytes to read and that
|
|
BYTES+1 is not greater than BUFFER_SIZE, then read the bytes into
|
|
the given variable VAR, then advance the reading position.
|
|
*/
|
|
#define READ_STRING(VAR, BYTES, BUFFER_SIZE) \
|
|
do { \
|
|
if (BUFFER_SIZE <= BYTES) \
|
|
goto error_malformed_packet; \
|
|
READ(memcpy(VAR, packet_position, BYTES), BYTES); \
|
|
VAR[BYTES]= '\0'; \
|
|
} while (0)
|
|
|
|
|
|
bool com_binlog_dump(THD *thd, char *packet, size_t packet_length)
|
|
{
|
|
DBUG_ENTER("com_binlog_dump");
|
|
ulong pos;
|
|
ushort flags= 0;
|
|
const uchar* packet_position= (uchar *) packet;
|
|
size_t packet_bytes_todo= packet_length;
|
|
|
|
DBUG_ASSERT(!thd->status_var_aggregated);
|
|
thd->status_var.com_other++;
|
|
thd->enable_slow_log= opt_log_slow_admin_statements;
|
|
if (check_global_access(thd, REPL_SLAVE_ACL))
|
|
DBUG_RETURN(false);
|
|
|
|
/*
|
|
4 bytes is too little, but changing the protocol would break
|
|
compatibility. This has been fixed in the new protocol. @see
|
|
com_binlog_dump_gtid().
|
|
*/
|
|
READ_INT(pos, 4);
|
|
READ_INT(flags, 2);
|
|
READ_INT(thd->server_id, 4);
|
|
|
|
DBUG_PRINT("info", ("pos=%lu flags=%d server_id=%d", pos, flags, thd->server_id));
|
|
|
|
kill_zombie_dump_threads(thd);
|
|
|
|
query_logger.general_log_print(thd, thd->get_command(), "Log: '%s' Pos: %ld",
|
|
packet + 10, (long) pos);
|
|
mysql_binlog_send(thd, thd->mem_strdup(packet + 10), (my_off_t) pos, NULL, flags);
|
|
|
|
unregister_slave(thd, true, true/*need_lock_slave_list=true*/);
|
|
/* fake COM_QUIT -- if we get here, the thread needs to terminate */
|
|
DBUG_RETURN(true);
|
|
|
|
error_malformed_packet:
|
|
my_error(ER_MALFORMED_PACKET, MYF(0));
|
|
DBUG_RETURN(true);
|
|
}
|
|
|
|
|
|
bool com_binlog_dump_gtid(THD *thd, char *packet, size_t packet_length)
|
|
{
|
|
DBUG_ENTER("com_binlog_dump_gtid");
|
|
/*
|
|
Before going GA, we need to make this protocol extensible without
|
|
breaking compatitibilty. /Alfranio.
|
|
*/
|
|
ushort flags= 0;
|
|
uint32 data_size= 0;
|
|
uint64 pos= 0;
|
|
char name[FN_REFLEN + 1];
|
|
uint32 name_size= 0;
|
|
char* gtid_string= NULL;
|
|
const uchar* packet_position= (uchar *) packet;
|
|
size_t packet_bytes_todo= packet_length;
|
|
Sid_map sid_map(NULL/*no sid_lock because this is a completely local object*/);
|
|
Gtid_set slave_gtid_executed(&sid_map);
|
|
|
|
DBUG_ASSERT(!thd->status_var_aggregated);
|
|
thd->status_var.com_other++;
|
|
thd->enable_slow_log= opt_log_slow_admin_statements;
|
|
if (check_global_access(thd, REPL_SLAVE_ACL))
|
|
DBUG_RETURN(false);
|
|
|
|
READ_INT(flags,2);
|
|
READ_INT(thd->server_id, 4);
|
|
READ_INT(name_size, 4);
|
|
READ_STRING(name, name_size, sizeof(name));
|
|
READ_INT(pos, 8);
|
|
DBUG_PRINT("info", ("pos=%llu flags=%d server_id=%d", pos, flags, thd->server_id));
|
|
READ_INT(data_size, 4);
|
|
CHECK_PACKET_SIZE(data_size);
|
|
if (slave_gtid_executed.add_gtid_encoding(packet_position, data_size) !=
|
|
RETURN_STATUS_OK)
|
|
DBUG_RETURN(true);
|
|
slave_gtid_executed.to_string(>id_string);
|
|
DBUG_PRINT("info", ("Slave %d requested to read %s at position %llu gtid set "
|
|
"'%s'.", thd->server_id, name, pos, gtid_string));
|
|
|
|
kill_zombie_dump_threads(thd);
|
|
query_logger.general_log_print(thd, thd->get_command(),
|
|
"Log: '%s' Pos: %llu GTIDs: '%s'",
|
|
name, pos, gtid_string);
|
|
my_free(gtid_string);
|
|
mysql_binlog_send(thd, name, (my_off_t) pos, &slave_gtid_executed, flags);
|
|
|
|
unregister_slave(thd, true, true/*need_lock_slave_list=true*/);
|
|
/* fake COM_QUIT -- if we get here, the thread needs to terminate */
|
|
DBUG_RETURN(true);
|
|
|
|
error_malformed_packet:
|
|
my_error(ER_MALFORMED_PACKET, MYF(0));
|
|
DBUG_RETURN(true);
|
|
}
|
|
|
|
void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos,
|
|
Gtid_set* slave_gtid_executed, uint32 flags)
|
|
{
|
|
Binlog_sender sender(thd, log_ident, pos, slave_gtid_executed, flags);
|
|
|
|
sender.run();
|
|
}
|
|
|
|
/**
|
|
An auxiliary function extracts slave UUID.
|
|
|
|
@param[in] thd THD to access a user variable
|
|
@param[out] value String to return UUID value.
|
|
|
|
@return if success value is returned else NULL is returned.
|
|
*/
|
|
String *get_slave_uuid(THD *thd, String *value)
|
|
{
|
|
uchar name[]= "slave_uuid";
|
|
|
|
if (value == NULL)
|
|
return NULL;
|
|
|
|
/* Protects thd->user_vars. */
|
|
mysql_mutex_lock(&thd->LOCK_thd_data);
|
|
|
|
user_var_entry *entry=
|
|
(user_var_entry*) my_hash_search(&thd->user_vars, name, sizeof(name)-1);
|
|
if (entry && entry->length() > 0)
|
|
{
|
|
value->copy(entry->ptr(), entry->length(), NULL);
|
|
mysql_mutex_unlock(&thd->LOCK_thd_data);
|
|
return value;
|
|
}
|
|
|
|
mysql_mutex_unlock(&thd->LOCK_thd_data);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
Callback function used by kill_zombie_dump_threads() function to
|
|
to find zombie dump thread from the thd list.
|
|
|
|
@note It acquires LOCK_thd_data mutex when it finds matching thd.
|
|
It is the responsibility of the caller to release this mutex.
|
|
*/
|
|
class Find_zombie_dump_thread : public Find_THD_Impl
|
|
{
|
|
public:
|
|
Find_zombie_dump_thread(String value): m_slave_uuid(value) {}
|
|
virtual bool operator()(THD *thd)
|
|
{
|
|
THD *cur_thd= current_thd;
|
|
if (thd != cur_thd && (thd->get_command() == COM_BINLOG_DUMP ||
|
|
thd->get_command() == COM_BINLOG_DUMP_GTID))
|
|
{
|
|
String tmp_uuid;
|
|
bool is_zombie_thread= false;
|
|
get_slave_uuid(thd, &tmp_uuid);
|
|
if (m_slave_uuid.length())
|
|
{
|
|
is_zombie_thread= (tmp_uuid.length() &&
|
|
!strncmp(m_slave_uuid.c_ptr(),
|
|
tmp_uuid.c_ptr(), UUID_LENGTH));
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Check if it is a 5.5 slave's dump thread i.e., server_id should be
|
|
same && dump thread should not contain 'UUID'.
|
|
*/
|
|
is_zombie_thread= ((thd->server_id == cur_thd->server_id) &&
|
|
!tmp_uuid.length());
|
|
}
|
|
if (is_zombie_thread)
|
|
{
|
|
mysql_mutex_lock(&thd->LOCK_thd_data);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
private:
|
|
String m_slave_uuid;
|
|
};
|
|
|
|
/*
|
|
|
|
Kill all Binlog_dump threads which previously talked to the same slave
|
|
("same" means with the same UUID(for slave versions >= 5.6) or same server id
|
|
(for slave versions < 5.6). 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 UUID/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()
|
|
@param thd newly connected dump thread object
|
|
|
|
*/
|
|
|
|
void kill_zombie_dump_threads(THD *thd)
|
|
{
|
|
String slave_uuid;
|
|
get_slave_uuid(thd, &slave_uuid);
|
|
if (slave_uuid.length() == 0 && thd->server_id == 0)
|
|
return;
|
|
|
|
Find_zombie_dump_thread find_zombie_dump_thread(slave_uuid);
|
|
THD *tmp= Global_THD_manager::get_instance()->
|
|
find_thd(&find_zombie_dump_thread);
|
|
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.
|
|
*/
|
|
if (log_warnings > 1)
|
|
{
|
|
if (slave_uuid.length())
|
|
{
|
|
sql_print_information("While initializing dump thread for slave with "
|
|
"UUID <%s>, found a zombie dump thread with the "
|
|
"same UUID. Master is killing the zombie dump "
|
|
"thread(%u).", slave_uuid.c_ptr(),
|
|
tmp->thread_id());
|
|
}
|
|
else
|
|
{
|
|
sql_print_information("While initializing dump thread for slave with "
|
|
"server_id <%u>, found a zombie dump thread with the "
|
|
"same server_id. Master is killing the zombie dump "
|
|
"thread(%u).", thd->server_id,
|
|
tmp->thread_id());
|
|
}
|
|
}
|
|
tmp->duplicate_slave_id= true;
|
|
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 false success
|
|
@retval true error
|
|
*/
|
|
bool reset_master(THD* thd)
|
|
{
|
|
bool ret= false;
|
|
|
|
/*
|
|
RESET MASTER command should ignore 'read-only' and 'super_read_only'
|
|
options so that it can update 'mysql.gtid_executed' replication repository
|
|
table.
|
|
|
|
Please note that skip_readonly_check flag should be set even when binary log
|
|
is not enabled, as RESET MASTER command will clear 'gtid_executed' table.
|
|
*/
|
|
thd->set_skip_readonly_check();
|
|
if (is_group_replication_running())
|
|
{
|
|
my_error(ER_CANT_RESET_MASTER, MYF(0), "Group Replication is running");
|
|
return true;
|
|
}
|
|
|
|
if (mysql_bin_log.is_open())
|
|
{
|
|
/*
|
|
mysql_bin_log.reset_logs will delete the binary logs *and* clear
|
|
gtid_state. It is important to do both these operations from
|
|
within reset_logs, since the operations can then use the same
|
|
lock. I.e., if we would remove the call to gtid_state->clear
|
|
from reset_logs and call gtid_state->clear explicitly from this
|
|
function instead, it would be possible for a concurrent thread
|
|
to commit between the point where the binary log was removed and
|
|
the point where the gtid_executed table is cleared. This would
|
|
lead to an inconsistent state.
|
|
*/
|
|
ret= mysql_bin_log.reset_logs(thd);
|
|
}
|
|
else
|
|
{
|
|
global_sid_lock->wrlock();
|
|
ret= (gtid_state->clear(thd) != 0);
|
|
global_sid_lock->unlock();
|
|
}
|
|
|
|
/*
|
|
Only run after_reset_master hook, when all reset operations preceding this
|
|
have succeeded.
|
|
*/
|
|
if (!ret)
|
|
(void) RUN_HOOK(binlog_transmit, after_reset_master, (thd, 0 /* flags */));
|
|
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_master_status(THD* thd)
|
|
{
|
|
Protocol *protocol= thd->get_protocol();
|
|
char* gtid_set_buffer= NULL;
|
|
int gtid_set_size= 0;
|
|
List<Item> field_list;
|
|
|
|
DBUG_ENTER("show_binlog_info");
|
|
|
|
global_sid_lock->wrlock();
|
|
const Gtid_set* gtid_set= gtid_state->get_executed_gtids();
|
|
if ((gtid_set_size= gtid_set->to_string(>id_set_buffer)) < 0)
|
|
{
|
|
global_sid_lock->unlock();
|
|
my_eof(thd);
|
|
my_free(gtid_set_buffer);
|
|
DBUG_RETURN(true);
|
|
}
|
|
global_sid_lock->unlock();
|
|
|
|
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));
|
|
field_list.push_back(new Item_empty_string("Executed_Gtid_Set",
|
|
gtid_set_size));
|
|
|
|
if (thd->send_result_metadata(&field_list,
|
|
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
|
|
{
|
|
my_free(gtid_set_buffer);
|
|
DBUG_RETURN(true);
|
|
}
|
|
protocol->start_row();
|
|
|
|
if (mysql_bin_log.is_open())
|
|
{
|
|
LOG_INFO li;
|
|
mysql_bin_log.get_current_log(&li);
|
|
size_t dir_len = dirname_length(li.log_file_name);
|
|
protocol->store(li.log_file_name + dir_len, &my_charset_bin);
|
|
protocol->store((ulonglong) li.pos);
|
|
store(protocol, binlog_filter->get_do_db());
|
|
store(protocol, binlog_filter->get_ignore_db());
|
|
protocol->store(gtid_set_buffer, &my_charset_bin);
|
|
if (protocol->end_row())
|
|
{
|
|
my_free(gtid_set_buffer);
|
|
DBUG_RETURN(true);
|
|
}
|
|
}
|
|
my_eof(thd);
|
|
my_free(gtid_set_buffer);
|
|
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;
|
|
size_t length;
|
|
size_t cur_dir_len;
|
|
Protocol *protocol= thd->get_protocol();
|
|
DBUG_ENTER("show_binlogs");
|
|
|
|
if (!mysql_bin_log.is_open())
|
|
{
|
|
my_error(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 (thd->send_result_metadata(&field_list, Protocol::SEND_NUM_ROWS |
|
|
Protocol::SEND_EOF))
|
|
DBUG_RETURN(TRUE);
|
|
|
|
mysql_mutex_lock(mysql_bin_log.get_log_lock());
|
|
DEBUG_SYNC(thd, "show_binlogs_after_lock_log_before_lock_index");
|
|
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)
|
|
{
|
|
size_t dir_len;
|
|
ulonglong file_length= 0; // Length if open fails
|
|
fname[--length] = '\0'; // remove the newline
|
|
|
|
protocol->start_row();
|
|
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->end_row())
|
|
{
|
|
DBUG_PRINT("info", ("stopping dump thread because protocol->write failed at line %d", __LINE__));
|
|
goto err;
|
|
}
|
|
}
|
|
if(index_file->error == -1)
|
|
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 */
|