mirror of
https://github.com/MariaDB/server.git
synced 2026-05-09 00:24:30 +02:00
Merge 10.0-base -> mwl253
This commit is contained in:
commit
f4cd2b37b1
329 changed files with 19225 additions and 9200 deletions
|
|
@ -88,6 +88,7 @@ SET (SQL_SOURCE
|
|||
threadpool_common.cc
|
||||
../sql-common/mysql_async.c
|
||||
my_apc.cc my_apc.h
|
||||
rpl_gtid.cc
|
||||
${GEN_SOURCES}
|
||||
${MYSYS_LIBWRAP_SOURCE}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -574,8 +574,8 @@ void Event_parse_data::check_originator_id(THD *thd)
|
|||
status= Event_parse_data::SLAVESIDE_DISABLED;
|
||||
status_changed= true;
|
||||
}
|
||||
originator = thd->server_id;
|
||||
originator = thd->variables.server_id;
|
||||
}
|
||||
else
|
||||
originator = server_id;
|
||||
originator = global_system_variables.server_id;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8956,6 +8956,7 @@ void Create_field::init_for_tmp_table(enum_field_types sql_type_arg,
|
|||
FLAGSTR(pack_flag, FIELDFLAG_DECIMAL),
|
||||
f_packtype(pack_flag)));
|
||||
vcol_info= 0;
|
||||
create_if_not_exists= FALSE;
|
||||
stored_in_db= TRUE;
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
|
|
@ -8993,7 +8994,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
|
|||
char *fld_change, List<String> *fld_interval_list,
|
||||
CHARSET_INFO *fld_charset, uint fld_geom_type,
|
||||
Virtual_column_info *fld_vcol_info,
|
||||
engine_option_value *create_opt)
|
||||
engine_option_value *create_opt, bool check_exists)
|
||||
{
|
||||
uint sign_len, allowed_type_modifier= 0;
|
||||
ulong max_field_charlength= MAX_FIELD_CHARLENGTH;
|
||||
|
|
@ -9047,6 +9048,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type,
|
|||
|
||||
comment= *fld_comment;
|
||||
vcol_info= fld_vcol_info;
|
||||
create_if_not_exists= check_exists;
|
||||
stored_in_db= TRUE;
|
||||
|
||||
/* Initialize data for a computed field */
|
||||
|
|
@ -9655,6 +9657,7 @@ Create_field::Create_field(Field *old_field,Field *orig_field)
|
|||
comment= old_field->comment;
|
||||
decimals= old_field->decimals();
|
||||
vcol_info= old_field->vcol_info;
|
||||
create_if_not_exists= FALSE;
|
||||
stored_in_db= old_field->stored_in_db;
|
||||
option_list= old_field->option_list;
|
||||
option_struct= old_field->option_struct;
|
||||
|
|
|
|||
|
|
@ -2419,8 +2419,9 @@ public:
|
|||
|
||||
uint8 interval_id; // For rea_create_table
|
||||
uint offset,pack_flag;
|
||||
bool create_if_not_exists; // Used in ALTER TABLE IF NOT EXISTS
|
||||
|
||||
/*
|
||||
/*
|
||||
This is additinal data provided for any computed(virtual) field.
|
||||
In particular it includes a pointer to the item by which this field
|
||||
can be computed from other fields.
|
||||
|
|
@ -2433,7 +2434,8 @@ public:
|
|||
*/
|
||||
bool stored_in_db;
|
||||
|
||||
Create_field() :after(0), option_list(NULL), option_struct(NULL)
|
||||
Create_field() :after(0), option_list(NULL), option_struct(NULL),
|
||||
create_if_not_exists(FALSE)
|
||||
{}
|
||||
Create_field(Field *field, Field *orig_field);
|
||||
/* Used to make a clone of this object for ALTER/CREATE TABLE */
|
||||
|
|
@ -2451,7 +2453,7 @@ public:
|
|||
Item *on_update_value, LEX_STRING *comment, char *change,
|
||||
List<String> *interval_list, CHARSET_INFO *cs,
|
||||
uint uint_geom_type, Virtual_column_info *vcol_info,
|
||||
engine_option_value *option_list);
|
||||
engine_option_value *option_list, bool check_exists);
|
||||
|
||||
bool field_flags_are_binary()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ Hybrid_type_traits_integer::fix_length_and_dec(Item *item, Item *arg) const
|
|||
|
||||
void item_init(void)
|
||||
{
|
||||
item_user_lock_init();
|
||||
item_func_sleep_init();
|
||||
uuid_short_init();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -447,6 +447,19 @@ protected:
|
|||
};
|
||||
|
||||
|
||||
class Create_func_binlog_gtid_pos : public Create_func_arg2
|
||||
{
|
||||
public:
|
||||
virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2);
|
||||
|
||||
static Create_func_binlog_gtid_pos s_singleton;
|
||||
|
||||
protected:
|
||||
Create_func_binlog_gtid_pos() {}
|
||||
virtual ~Create_func_binlog_gtid_pos() {}
|
||||
};
|
||||
|
||||
|
||||
class Create_func_bit_count : public Create_func_arg1
|
||||
{
|
||||
public:
|
||||
|
|
@ -3100,6 +3113,16 @@ Create_func_bin::create_1_arg(THD *thd, Item *arg1)
|
|||
}
|
||||
|
||||
|
||||
Create_func_binlog_gtid_pos Create_func_binlog_gtid_pos::s_singleton;
|
||||
|
||||
Item*
|
||||
Create_func_binlog_gtid_pos::create_2_arg(THD *thd, Item *arg1, Item *arg2)
|
||||
{
|
||||
thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
|
||||
return new (thd->mem_root) Item_func_binlog_gtid_pos(arg1, arg2);
|
||||
}
|
||||
|
||||
|
||||
Create_func_bit_count Create_func_bit_count::s_singleton;
|
||||
|
||||
Item*
|
||||
|
|
@ -5322,6 +5345,7 @@ static Native_func_registry func_array[] =
|
|||
{ { C_STRING_WITH_LEN("ATAN2") }, BUILDER(Create_func_atan)},
|
||||
{ { C_STRING_WITH_LEN("BENCHMARK") }, BUILDER(Create_func_benchmark)},
|
||||
{ { C_STRING_WITH_LEN("BIN") }, BUILDER(Create_func_bin)},
|
||||
{ { C_STRING_WITH_LEN("BINLOG_GTID_POS") }, BUILDER(Create_func_binlog_gtid_pos)},
|
||||
{ { C_STRING_WITH_LEN("BIT_COUNT") }, BUILDER(Create_func_bit_count)},
|
||||
{ { C_STRING_WITH_LEN("BIT_LENGTH") }, BUILDER(Create_func_bit_length)},
|
||||
{ { C_STRING_WITH_LEN("BUFFER") }, GEOM_BUILDER(Create_func_buffer)},
|
||||
|
|
|
|||
635
sql/item_func.cc
635
sql/item_func.cc
|
|
@ -3750,120 +3750,6 @@ udf_handler::~udf_handler()
|
|||
bool udf_handler::get_arguments() { return 0; }
|
||||
#endif /* HAVE_DLOPEN */
|
||||
|
||||
/*
|
||||
** User level locks
|
||||
*/
|
||||
|
||||
mysql_mutex_t LOCK_user_locks;
|
||||
static HASH hash_user_locks;
|
||||
|
||||
class User_level_lock
|
||||
{
|
||||
uchar *key;
|
||||
size_t key_length;
|
||||
|
||||
public:
|
||||
int count;
|
||||
bool locked;
|
||||
mysql_cond_t cond;
|
||||
my_thread_id thread_id;
|
||||
void set_thread(THD *thd) { thread_id= thd->thread_id; }
|
||||
|
||||
User_level_lock(const uchar *key_arg,uint length, ulong id)
|
||||
:key_length(length),count(1),locked(1), thread_id(id)
|
||||
{
|
||||
key= (uchar*) my_memdup(key_arg,length,MYF(0));
|
||||
mysql_cond_init(key_user_level_lock_cond, &cond, NULL);
|
||||
if (key)
|
||||
{
|
||||
if (my_hash_insert(&hash_user_locks,(uchar*) this))
|
||||
{
|
||||
my_free(key);
|
||||
key=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
~User_level_lock()
|
||||
{
|
||||
if (key)
|
||||
{
|
||||
my_hash_delete(&hash_user_locks,(uchar*) this);
|
||||
my_free(key);
|
||||
}
|
||||
mysql_cond_destroy(&cond);
|
||||
}
|
||||
inline bool initialized() { return key != 0; }
|
||||
friend void item_user_lock_release(User_level_lock *ull);
|
||||
friend uchar *ull_get_key(const User_level_lock *ull, size_t *length,
|
||||
my_bool not_used);
|
||||
};
|
||||
|
||||
uchar *ull_get_key(const User_level_lock *ull, size_t *length,
|
||||
my_bool not_used __attribute__((unused)))
|
||||
{
|
||||
*length= ull->key_length;
|
||||
return ull->key;
|
||||
}
|
||||
|
||||
#ifdef HAVE_PSI_INTERFACE
|
||||
static PSI_mutex_key key_LOCK_user_locks;
|
||||
|
||||
static PSI_mutex_info all_user_mutexes[]=
|
||||
{
|
||||
{ &key_LOCK_user_locks, "LOCK_user_locks", PSI_FLAG_GLOBAL}
|
||||
};
|
||||
|
||||
static void init_user_lock_psi_keys(void)
|
||||
{
|
||||
const char* category= "sql";
|
||||
int count;
|
||||
|
||||
if (PSI_server == NULL)
|
||||
return;
|
||||
|
||||
count= array_elements(all_user_mutexes);
|
||||
PSI_server->register_mutex(category, all_user_mutexes, count);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool item_user_lock_inited= 0;
|
||||
|
||||
void item_user_lock_init(void)
|
||||
{
|
||||
#ifdef HAVE_PSI_INTERFACE
|
||||
init_user_lock_psi_keys();
|
||||
#endif
|
||||
|
||||
mysql_mutex_init(key_LOCK_user_locks, &LOCK_user_locks, MY_MUTEX_INIT_SLOW);
|
||||
my_hash_init(&hash_user_locks,system_charset_info,
|
||||
16,0,0,(my_hash_get_key) ull_get_key,NULL,0);
|
||||
item_user_lock_inited= 1;
|
||||
}
|
||||
|
||||
void item_user_lock_free(void)
|
||||
{
|
||||
if (item_user_lock_inited)
|
||||
{
|
||||
item_user_lock_inited= 0;
|
||||
my_hash_free(&hash_user_locks);
|
||||
mysql_mutex_destroy(&LOCK_user_locks);
|
||||
}
|
||||
}
|
||||
|
||||
void item_user_lock_release(User_level_lock *ull)
|
||||
{
|
||||
ull->locked=0;
|
||||
ull->thread_id= 0;
|
||||
if (--ull->count)
|
||||
mysql_cond_signal(&ull->cond);
|
||||
else
|
||||
delete ull;
|
||||
}
|
||||
|
||||
/**
|
||||
Wait until we are at or past the given position in the master binlog
|
||||
on the slave.
|
||||
*/
|
||||
|
||||
longlong Item_master_pos_wait::val_int()
|
||||
{
|
||||
|
|
@ -4010,7 +3896,136 @@ int Interruptible_wait::wait(mysql_cond_t *cond, mysql_mutex_t *mutex)
|
|||
|
||||
|
||||
/**
|
||||
Get a user level lock. If the thread has an old lock this is first released.
|
||||
For locks with EXPLICIT duration, MDL returns a new ticket
|
||||
every time a lock is granted. This allows to implement recursive
|
||||
locks without extra allocation or additional data structures, such
|
||||
as below. However, if there are too many tickets in the same
|
||||
MDL_context, MDL_context::find_ticket() is getting too slow,
|
||||
since it's using a linear search.
|
||||
This is why a separate structure is allocated for a user
|
||||
level lock, and before requesting a new lock from MDL,
|
||||
GET_LOCK() checks thd->ull_hash if such lock is already granted,
|
||||
and if so, simply increments a reference counter.
|
||||
*/
|
||||
|
||||
class User_level_lock
|
||||
{
|
||||
public:
|
||||
MDL_ticket *lock;
|
||||
int refs;
|
||||
};
|
||||
|
||||
|
||||
/** Extract a hash key from User_level_lock. */
|
||||
|
||||
uchar *ull_get_key(const uchar *ptr, size_t *length,
|
||||
my_bool not_used __attribute__((unused)))
|
||||
{
|
||||
User_level_lock *ull = (User_level_lock*) ptr;
|
||||
MDL_key *key = ull->lock->get_key();
|
||||
*length= key->length();
|
||||
return (uchar*) key->ptr();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Release all user level locks for this THD.
|
||||
*/
|
||||
|
||||
void mysql_ull_cleanup(THD *thd)
|
||||
{
|
||||
User_level_lock *ull;
|
||||
DBUG_ENTER("mysql_ull_cleanup");
|
||||
|
||||
for (uint i= 0; i < thd->ull_hash.records; i++)
|
||||
{
|
||||
ull = (User_level_lock*) my_hash_element(&thd->ull_hash, i);
|
||||
thd->mdl_context.release_lock(ull->lock);
|
||||
my_free(ull);
|
||||
}
|
||||
|
||||
my_hash_free(&thd->ull_hash);
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Set explicit duration for metadata locks corresponding to
|
||||
user level locks to protect them from being released at the end
|
||||
of transaction.
|
||||
*/
|
||||
|
||||
void mysql_ull_set_explicit_lock_duration(THD *thd)
|
||||
{
|
||||
User_level_lock *ull;
|
||||
DBUG_ENTER("mysql_ull_set_explicit_lock_duration");
|
||||
|
||||
for (uint i= 0; i < thd->ull_hash.records; i++)
|
||||
{
|
||||
ull= (User_level_lock*) my_hash_element(&thd->ull_hash, i);
|
||||
thd->mdl_context.set_lock_duration(ull->lock, MDL_EXPLICIT);
|
||||
}
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
When MDL detects a lock wait timeout, it pushes
|
||||
an error into the statement diagnostics area.
|
||||
For GET_LOCK(), lock wait timeout is not an error,
|
||||
but a special return value (0). NULL is returned in
|
||||
case of error.
|
||||
Capture and suppress lock wait timeout.
|
||||
*/
|
||||
|
||||
class Lock_wait_timeout_handler: public Internal_error_handler
|
||||
{
|
||||
public:
|
||||
Lock_wait_timeout_handler() :m_lock_wait_timeout(false) {}
|
||||
|
||||
bool m_lock_wait_timeout;
|
||||
|
||||
bool handle_condition(THD * /* thd */, uint sql_errno,
|
||||
const char * /* sqlstate */,
|
||||
MYSQL_ERROR::enum_warning_level /* level */,
|
||||
const char *message,
|
||||
MYSQL_ERROR ** /* cond_hdl */);
|
||||
};
|
||||
|
||||
bool
|
||||
Lock_wait_timeout_handler::
|
||||
handle_condition(THD * /* thd */, uint sql_errno,
|
||||
const char * /* sqlstate */,
|
||||
MYSQL_ERROR::enum_warning_level /* level */,
|
||||
const char *message,
|
||||
MYSQL_ERROR ** /* cond_hdl */)
|
||||
{
|
||||
if (sql_errno == ER_LOCK_WAIT_TIMEOUT)
|
||||
{
|
||||
m_lock_wait_timeout= true;
|
||||
return true; /* condition handled */
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static int ull_name_ok(String *name)
|
||||
{
|
||||
if (!name || !name->length())
|
||||
return 0;
|
||||
|
||||
if (name->length() > NAME_LEN)
|
||||
{
|
||||
my_error(ER_TOO_LONG_IDENT, MYF(0), name->c_ptr_safe());
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Get a user level lock.
|
||||
|
||||
@retval
|
||||
1 : Got lock
|
||||
|
|
@ -4023,14 +4038,13 @@ int Interruptible_wait::wait(mysql_cond_t *cond, mysql_mutex_t *mutex)
|
|||
longlong Item_func_get_lock::val_int()
|
||||
{
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
String *res=args[0]->val_str(&value);
|
||||
String *res= args[0]->val_str(&value);
|
||||
ulonglong timeout= args[1]->val_int();
|
||||
THD *thd=current_thd;
|
||||
THD *thd= current_thd;
|
||||
User_level_lock *ull;
|
||||
int error;
|
||||
Interruptible_wait timed_cond(thd);
|
||||
DBUG_ENTER("Item_func_get_lock::val_int");
|
||||
|
||||
null_value= 1;
|
||||
/*
|
||||
In slave thread no need to get locks, everything is serialized. Anyway
|
||||
there is no way to make GET_LOCK() work on slave like it did on master
|
||||
|
|
@ -4039,104 +4053,70 @@ longlong Item_func_get_lock::val_int()
|
|||
it's not guaranteed to be same as on master.
|
||||
*/
|
||||
if (thd->slave_thread)
|
||||
DBUG_RETURN(1);
|
||||
|
||||
mysql_mutex_lock(&LOCK_user_locks);
|
||||
|
||||
if (!res || !res->length())
|
||||
{
|
||||
mysql_mutex_unlock(&LOCK_user_locks);
|
||||
null_value=1;
|
||||
DBUG_RETURN(0);
|
||||
null_value= 0;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
if (!ull_name_ok(res))
|
||||
DBUG_RETURN(0);
|
||||
|
||||
DBUG_PRINT("info", ("lock %.*s, thd=%ld", res->length(), res->ptr(),
|
||||
(long) thd->real_id));
|
||||
null_value=0;
|
||||
|
||||
if (thd->ull)
|
||||
/* HASH entries are of type User_level_lock. */
|
||||
if (! my_hash_inited(&thd->ull_hash) &&
|
||||
my_hash_init(&thd->ull_hash, &my_charset_bin,
|
||||
16 /* small hash */, 0, 0, ull_get_key, NULL, 0))
|
||||
{
|
||||
item_user_lock_release(thd->ull);
|
||||
thd->ull=0;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
if (!(ull= ((User_level_lock *) my_hash_search(&hash_user_locks,
|
||||
(uchar*) res->ptr(),
|
||||
(size_t) res->length()))))
|
||||
MDL_request ull_request;
|
||||
ull_request.init(MDL_key::USER_LOCK, res->c_ptr_safe(), "",
|
||||
MDL_SHARED_NO_WRITE, MDL_EXPLICIT);
|
||||
MDL_key *ull_key = &ull_request.key;
|
||||
|
||||
|
||||
if ((ull= (User_level_lock*)
|
||||
my_hash_search(&thd->ull_hash, ull_key->ptr(), ull_key->length())))
|
||||
{
|
||||
ull= new User_level_lock((uchar*) res->ptr(), (size_t) res->length(),
|
||||
thd->thread_id);
|
||||
if (!ull || !ull->initialized())
|
||||
{
|
||||
delete ull;
|
||||
mysql_mutex_unlock(&LOCK_user_locks);
|
||||
null_value=1; // Probably out of memory
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
ull->set_thread(thd);
|
||||
thd->ull=ull;
|
||||
mysql_mutex_unlock(&LOCK_user_locks);
|
||||
DBUG_PRINT("info", ("made new lock"));
|
||||
DBUG_RETURN(1); // Got new lock
|
||||
/* Recursive lock */
|
||||
ull->refs++;
|
||||
null_value = 0;
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
ull->count++;
|
||||
DBUG_PRINT("info", ("ull->count=%d", ull->count));
|
||||
|
||||
/*
|
||||
Structure is now initialized. Try to get the lock.
|
||||
Set up control struct to allow others to abort locks.
|
||||
*/
|
||||
thd_proc_info(thd, "User lock");
|
||||
thd->mysys_var->current_mutex= &LOCK_user_locks;
|
||||
thd->mysys_var->current_cond= &ull->cond;
|
||||
|
||||
timed_cond.set_timeout(timeout * 1000000000ULL);
|
||||
|
||||
error= 0;
|
||||
thd_wait_begin(thd, THD_WAIT_USER_LOCK);
|
||||
while (ull->locked && !thd->killed)
|
||||
Lock_wait_timeout_handler lock_wait_timeout_handler;
|
||||
thd->push_internal_handler(&lock_wait_timeout_handler);
|
||||
bool error= thd->mdl_context.acquire_lock(&ull_request, timeout);
|
||||
(void) thd->pop_internal_handler();
|
||||
if (error)
|
||||
{
|
||||
DBUG_PRINT("info", ("waiting on lock"));
|
||||
error= timed_cond.wait(&ull->cond, &LOCK_user_locks);
|
||||
if (error == ETIMEDOUT || error == ETIME)
|
||||
{
|
||||
DBUG_PRINT("info", ("lock wait timeout"));
|
||||
break;
|
||||
}
|
||||
error= 0;
|
||||
if (lock_wait_timeout_handler.m_lock_wait_timeout)
|
||||
null_value= 0;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
thd_wait_end(thd);
|
||||
|
||||
if (ull->locked)
|
||||
ull= (User_level_lock*) my_malloc(sizeof(User_level_lock),
|
||||
MYF(MY_WME|MY_THREAD_SPECIFIC));
|
||||
if (ull == NULL)
|
||||
{
|
||||
if (!--ull->count)
|
||||
{
|
||||
DBUG_ASSERT(0);
|
||||
delete ull; // Should never happen
|
||||
}
|
||||
if (!error) // Killed (thd->killed != 0)
|
||||
{
|
||||
error=1;
|
||||
null_value=1; // Return NULL
|
||||
}
|
||||
thd->mdl_context.release_lock(ull_request.ticket);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
else // We got the lock
|
||||
|
||||
ull->lock= ull_request.ticket;
|
||||
ull->refs= 1;
|
||||
|
||||
if (my_hash_insert(&thd->ull_hash, (uchar*) ull))
|
||||
{
|
||||
ull->locked=1;
|
||||
ull->set_thread(thd);
|
||||
ull->thread_id= thd->thread_id;
|
||||
thd->ull=ull;
|
||||
error=0;
|
||||
DBUG_PRINT("info", ("got the lock"));
|
||||
thd->mdl_context.release_lock(ull->lock);
|
||||
my_free(ull);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
mysql_mutex_unlock(&LOCK_user_locks);
|
||||
null_value= 0;
|
||||
|
||||
mysql_mutex_lock(&thd->mysys_var->mutex);
|
||||
thd_proc_info(thd, 0);
|
||||
thd->mysys_var->current_mutex= 0;
|
||||
thd->mysys_var->current_cond= 0;
|
||||
mysql_mutex_unlock(&thd->mysys_var->mutex);
|
||||
|
||||
DBUG_RETURN(!error ? 1 : 0);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -4151,43 +4131,86 @@ longlong Item_func_get_lock::val_int()
|
|||
longlong Item_func_release_lock::val_int()
|
||||
{
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
String *res=args[0]->val_str(&value);
|
||||
User_level_lock *ull;
|
||||
longlong result;
|
||||
THD *thd=current_thd;
|
||||
String *res= args[0]->val_str(&value);
|
||||
THD *thd= current_thd;
|
||||
DBUG_ENTER("Item_func_release_lock::val_int");
|
||||
if (!res || !res->length())
|
||||
null_value= 1;
|
||||
|
||||
if (!ull_name_ok(res))
|
||||
DBUG_RETURN(0);
|
||||
|
||||
DBUG_PRINT("info", ("lock %.*s", res->length(), res->ptr()));
|
||||
|
||||
MDL_key ull_key;
|
||||
ull_key.mdl_key_init(MDL_key::USER_LOCK, res->c_ptr_safe(), "");
|
||||
|
||||
User_level_lock *ull;
|
||||
|
||||
if (!(ull=
|
||||
(User_level_lock*) my_hash_search(&thd->ull_hash,
|
||||
ull_key.ptr(), ull_key.length())))
|
||||
{
|
||||
null_value=1;
|
||||
null_value= thd->mdl_context.get_lock_owner(&ull_key) == 0;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
DBUG_PRINT("info", ("lock %.*s", res->length(), res->ptr()));
|
||||
null_value=0;
|
||||
null_value= 0;
|
||||
if (--ull->refs == 0)
|
||||
{
|
||||
my_hash_delete(&thd->ull_hash, (uchar*) ull);
|
||||
thd->mdl_context.release_lock(ull->lock);
|
||||
my_free(ull);
|
||||
}
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
result=0;
|
||||
mysql_mutex_lock(&LOCK_user_locks);
|
||||
if (!(ull= ((User_level_lock*) my_hash_search(&hash_user_locks,
|
||||
(const uchar*) res->ptr(),
|
||||
(size_t) res->length()))))
|
||||
{
|
||||
null_value=1;
|
||||
}
|
||||
else
|
||||
{
|
||||
DBUG_PRINT("info", ("ull->locked=%d ull->thread=%lu thd=%lu",
|
||||
(int) ull->locked,
|
||||
(long)ull->thread_id,
|
||||
(long)thd->thread_id));
|
||||
if (ull->locked && current_thd->thread_id == ull->thread_id)
|
||||
{
|
||||
DBUG_PRINT("info", ("release lock"));
|
||||
result=1; // Release is ok
|
||||
item_user_lock_release(ull);
|
||||
thd->ull=0;
|
||||
}
|
||||
}
|
||||
mysql_mutex_unlock(&LOCK_user_locks);
|
||||
DBUG_RETURN(result);
|
||||
|
||||
/**
|
||||
Check a user level lock.
|
||||
|
||||
Sets null_value=TRUE on error.
|
||||
|
||||
@retval
|
||||
1 Available
|
||||
@retval
|
||||
0 Already taken, or error
|
||||
*/
|
||||
|
||||
longlong Item_func_is_free_lock::val_int()
|
||||
{
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
String *res= args[0]->val_str(&value);
|
||||
THD *thd= current_thd;
|
||||
null_value= 1;
|
||||
|
||||
if (!ull_name_ok(res))
|
||||
return 0;
|
||||
|
||||
MDL_key ull_key;
|
||||
ull_key.mdl_key_init(MDL_key::USER_LOCK, res->c_ptr_safe(), "");
|
||||
|
||||
null_value= 0;
|
||||
return thd->mdl_context.get_lock_owner(&ull_key) == 0;
|
||||
}
|
||||
|
||||
|
||||
longlong Item_func_is_used_lock::val_int()
|
||||
{
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
String *res= args[0]->val_str(&value);
|
||||
THD *thd= current_thd;
|
||||
null_value= 1;
|
||||
|
||||
if (!ull_name_ok(res))
|
||||
return 0;
|
||||
|
||||
MDL_key ull_key;
|
||||
ull_key.mdl_key_init(MDL_key::USER_LOCK, res->c_ptr_safe(), "");
|
||||
ulong thread_id = thd->mdl_context.get_lock_owner(&ull_key);
|
||||
if (thread_id == 0)
|
||||
return 0;
|
||||
|
||||
null_value= 0;
|
||||
return thread_id;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -4288,6 +4311,54 @@ void Item_func_benchmark::print(String *str, enum_query_type query_type)
|
|||
}
|
||||
|
||||
|
||||
mysql_mutex_t LOCK_item_func_sleep;
|
||||
|
||||
#ifdef HAVE_PSI_INTERFACE
|
||||
static PSI_mutex_key key_LOCK_item_func_sleep;
|
||||
|
||||
static PSI_mutex_info item_func_sleep_mutexes[]=
|
||||
{
|
||||
{ &key_LOCK_item_func_sleep, "LOCK_user_locks", PSI_FLAG_GLOBAL}
|
||||
};
|
||||
|
||||
|
||||
static void init_item_func_sleep_psi_keys(void)
|
||||
{
|
||||
const char* category= "sql";
|
||||
int count;
|
||||
|
||||
if (PSI_server == NULL)
|
||||
return;
|
||||
|
||||
count= array_elements(item_func_sleep_mutexes);
|
||||
PSI_server->register_mutex(category, item_func_sleep_mutexes, count);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool item_func_sleep_inited= 0;
|
||||
|
||||
|
||||
void item_func_sleep_init(void)
|
||||
{
|
||||
#ifdef HAVE_PSI_INTERFACE
|
||||
init_item_func_sleep_psi_keys();
|
||||
#endif
|
||||
|
||||
mysql_mutex_init(key_LOCK_item_func_sleep, &LOCK_item_func_sleep, MY_MUTEX_INIT_SLOW);
|
||||
item_func_sleep_inited= 1;
|
||||
}
|
||||
|
||||
|
||||
void item_func_sleep_free(void)
|
||||
{
|
||||
if (item_func_sleep_inited)
|
||||
{
|
||||
item_func_sleep_inited= 0;
|
||||
mysql_mutex_destroy(&LOCK_item_func_sleep);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** This function is just used to create tests with time gaps. */
|
||||
|
||||
longlong Item_func_sleep::val_int()
|
||||
|
|
@ -4316,24 +4387,24 @@ longlong Item_func_sleep::val_int()
|
|||
timed_cond.set_timeout((ulonglong) (timeout * 1000000000.0));
|
||||
|
||||
mysql_cond_init(key_item_func_sleep_cond, &cond, NULL);
|
||||
mysql_mutex_lock(&LOCK_user_locks);
|
||||
mysql_mutex_lock(&LOCK_item_func_sleep);
|
||||
|
||||
thd_proc_info(thd, "User sleep");
|
||||
thd->mysys_var->current_mutex= &LOCK_user_locks;
|
||||
thd->mysys_var->current_mutex= &LOCK_item_func_sleep;
|
||||
thd->mysys_var->current_cond= &cond;
|
||||
|
||||
error= 0;
|
||||
thd_wait_begin(thd, THD_WAIT_SLEEP);
|
||||
while (!thd->killed)
|
||||
{
|
||||
error= timed_cond.wait(&cond, &LOCK_user_locks);
|
||||
error= timed_cond.wait(&cond, &LOCK_item_func_sleep);
|
||||
if (error == ETIMEDOUT || error == ETIME)
|
||||
break;
|
||||
error= 0;
|
||||
}
|
||||
thd_wait_end(thd);
|
||||
thd_proc_info(thd, 0);
|
||||
mysql_mutex_unlock(&LOCK_user_locks);
|
||||
mysql_mutex_unlock(&LOCK_item_func_sleep);
|
||||
mysql_mutex_lock(&thd->mysys_var->mutex);
|
||||
thd->mysys_var->current_mutex= 0;
|
||||
thd->mysys_var->current_cond= 0;
|
||||
|
|
@ -5672,6 +5743,14 @@ longlong Item_func_get_system_var::val_int()
|
|||
{
|
||||
THD *thd= current_thd;
|
||||
|
||||
DBUG_EXECUTE_IF("simulate_non_gtid_aware_master",
|
||||
{
|
||||
if (0 == strcmp("gtid_domain_id", var->name.str))
|
||||
{
|
||||
my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str);
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
if (cache_present && thd->query_id == used_query_id)
|
||||
{
|
||||
if (cache_present & GET_SYS_VAR_CACHE_LONG)
|
||||
|
|
@ -5869,15 +5948,12 @@ void Item_func_match::init_search(bool no_order)
|
|||
{
|
||||
DBUG_ENTER("Item_func_match::init_search");
|
||||
|
||||
if (!table->file->get_table()) // the handler isn't opened yet
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
/* Check if init_search() has been called before */
|
||||
if (ft_handler)
|
||||
{
|
||||
/*
|
||||
We should reset ft_handler as it is cleaned up
|
||||
on destruction of FT_SELECT object
|
||||
(necessary in case of re-execution of subquery).
|
||||
TODO: FT_SELECT should not clean up ft_handler.
|
||||
*/
|
||||
if (join_key)
|
||||
table->file->ft_handler= ft_handler;
|
||||
DBUG_VOID_RETURN;
|
||||
|
|
@ -5886,10 +5962,10 @@ void Item_func_match::init_search(bool no_order)
|
|||
if (key == NO_SUCH_KEY)
|
||||
{
|
||||
List<Item> fields;
|
||||
fields.push_back(new Item_string(" ",1, cmp_collation.collation));
|
||||
for (uint i=1; i < arg_count; i++)
|
||||
fields.push_back(new Item_string(" ", 1, cmp_collation.collation));
|
||||
for (uint i= 1; i < arg_count; i++)
|
||||
fields.push_back(args[i]);
|
||||
concat_ws=new Item_func_concat_ws(fields);
|
||||
concat_ws= new Item_func_concat_ws(fields);
|
||||
/*
|
||||
Above function used only to get value and do not need fix_fields for it:
|
||||
Item_string - basic constant
|
||||
|
|
@ -5901,10 +5977,10 @@ void Item_func_match::init_search(bool no_order)
|
|||
|
||||
if (master)
|
||||
{
|
||||
join_key=master->join_key=join_key|master->join_key;
|
||||
join_key= master->join_key= join_key | master->join_key;
|
||||
master->init_search(no_order);
|
||||
ft_handler=master->ft_handler;
|
||||
join_key=master->join_key;
|
||||
ft_handler= master->ft_handler;
|
||||
join_key= master->join_key;
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
|
@ -5914,7 +5990,7 @@ void Item_func_match::init_search(bool no_order)
|
|||
if (!(ft_tmp=key_item()->val_str(&value)))
|
||||
{
|
||||
ft_tmp= &value;
|
||||
value.set("",0,cmp_collation.collation);
|
||||
value.set("", 0, cmp_collation.collation);
|
||||
}
|
||||
|
||||
if (ft_tmp->charset() != cmp_collation.collation)
|
||||
|
|
@ -5927,7 +6003,11 @@ void Item_func_match::init_search(bool no_order)
|
|||
|
||||
if (join_key && !no_order)
|
||||
flags|=FT_SORTED;
|
||||
ft_handler=table->file->ft_init_ext(flags, key, ft_tmp);
|
||||
|
||||
if (key != NO_SUCH_KEY)
|
||||
thd_proc_info(table->in_use, "FULLTEXT initialization");
|
||||
|
||||
ft_handler= table->file->ft_init_ext(flags, key, ft_tmp);
|
||||
|
||||
if (join_key)
|
||||
table->file->ft_handler=ft_handler;
|
||||
|
|
@ -6208,61 +6288,6 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
Check a user level lock.
|
||||
|
||||
Sets null_value=TRUE on error.
|
||||
|
||||
@retval
|
||||
1 Available
|
||||
@retval
|
||||
0 Already taken, or error
|
||||
*/
|
||||
|
||||
longlong Item_func_is_free_lock::val_int()
|
||||
{
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
String *res=args[0]->val_str(&value);
|
||||
User_level_lock *ull;
|
||||
|
||||
null_value=0;
|
||||
if (!res || !res->length())
|
||||
{
|
||||
null_value=1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
mysql_mutex_lock(&LOCK_user_locks);
|
||||
ull= (User_level_lock *) my_hash_search(&hash_user_locks, (uchar*) res->ptr(),
|
||||
(size_t) res->length());
|
||||
mysql_mutex_unlock(&LOCK_user_locks);
|
||||
if (!ull || !ull->locked)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
longlong Item_func_is_used_lock::val_int()
|
||||
{
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
String *res=args[0]->val_str(&value);
|
||||
User_level_lock *ull;
|
||||
|
||||
null_value=1;
|
||||
if (!res || !res->length())
|
||||
return 0;
|
||||
|
||||
mysql_mutex_lock(&LOCK_user_locks);
|
||||
ull= (User_level_lock *) my_hash_search(&hash_user_locks, (uchar*) res->ptr(),
|
||||
(size_t) res->length());
|
||||
mysql_mutex_unlock(&LOCK_user_locks);
|
||||
if (!ull || !ull->locked)
|
||||
return 0;
|
||||
|
||||
null_value=0;
|
||||
return ull->thread_id;
|
||||
}
|
||||
|
||||
|
||||
longlong Item_func_row_count::val_int()
|
||||
{
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
|
|
@ -6713,7 +6738,7 @@ ulonglong uuid_value;
|
|||
|
||||
void uuid_short_init()
|
||||
{
|
||||
uuid_value= ((((ulonglong) server_id) << 56) +
|
||||
uuid_value= ((((ulonglong) global_system_variables.server_id) << 56) +
|
||||
(((ulonglong) server_start_time) << 24));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1257,6 +1257,9 @@ public:
|
|||
};
|
||||
|
||||
|
||||
void item_func_sleep_init(void);
|
||||
void item_func_sleep_free(void);
|
||||
|
||||
class Item_func_sleep :public Item_int_func
|
||||
{
|
||||
public:
|
||||
|
|
@ -1506,14 +1509,8 @@ public:
|
|||
|
||||
#endif /* HAVE_DLOPEN */
|
||||
|
||||
/*
|
||||
** User level locks
|
||||
*/
|
||||
|
||||
class User_level_lock;
|
||||
void item_user_lock_init(void);
|
||||
void item_user_lock_release(User_level_lock *ull);
|
||||
void item_user_lock_free(void);
|
||||
void mysql_ull_cleanup(THD *thd);
|
||||
void mysql_ull_set_explicit_lock_duration(THD *thd);
|
||||
|
||||
class Item_func_get_lock :public Item_int_func
|
||||
{
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ C_MODE_START
|
|||
#include "../mysys/my_static.h" // For soundex_map
|
||||
C_MODE_END
|
||||
#include "sql_show.h" // append_identifier
|
||||
#include <sql_repl.h>
|
||||
|
||||
/**
|
||||
@todo Remove this. It is not safe to use a shared String object.
|
||||
|
|
@ -2668,6 +2669,46 @@ err:
|
|||
}
|
||||
|
||||
|
||||
void Item_func_binlog_gtid_pos::fix_length_and_dec()
|
||||
{
|
||||
collation.set(system_charset_info);
|
||||
max_length= MAX_BLOB_WIDTH;
|
||||
maybe_null= 1;
|
||||
}
|
||||
|
||||
|
||||
String *Item_func_binlog_gtid_pos::val_str(String *str)
|
||||
{
|
||||
DBUG_ASSERT(fixed == 1);
|
||||
#ifndef HAVE_REPLICATION
|
||||
null_value= 0;
|
||||
str->copy("", 0, system_charset_info);
|
||||
return str;
|
||||
#else
|
||||
String name_str, *name;
|
||||
longlong pos;
|
||||
|
||||
if (args[0]->null_value || args[1]->null_value)
|
||||
goto err;
|
||||
|
||||
name= args[0]->val_str(&name_str);
|
||||
pos= args[1]->val_int();
|
||||
|
||||
if (pos < 0 || pos > UINT_MAX32)
|
||||
goto err;
|
||||
|
||||
if (gtid_state_from_binlog_pos(name->c_ptr_safe(), (uint32)pos, str))
|
||||
goto err;
|
||||
null_value= 0;
|
||||
return str;
|
||||
|
||||
err:
|
||||
null_value= 1;
|
||||
return NULL;
|
||||
#endif /* !HAVE_REPLICATION */
|
||||
}
|
||||
|
||||
|
||||
void Item_func_rpad::fix_length_and_dec()
|
||||
{
|
||||
// Handle character set for args[0] and args[2].
|
||||
|
|
|
|||
|
|
@ -598,6 +598,17 @@ public:
|
|||
};
|
||||
|
||||
|
||||
class Item_func_binlog_gtid_pos :public Item_str_func
|
||||
{
|
||||
String tmp_value;
|
||||
public:
|
||||
Item_func_binlog_gtid_pos(Item *arg1,Item *arg2) :Item_str_func(arg1,arg2) {}
|
||||
String *val_str(String *);
|
||||
void fix_length_and_dec();
|
||||
const char *func_name() const { return "binlog_gtid_pos"; }
|
||||
};
|
||||
|
||||
|
||||
class Item_func_rpad :public Item_str_func
|
||||
{
|
||||
String tmp_value, rpad_str;
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ static SYMBOL symbols[] = {
|
|||
{ "AUTHORS", SYM(AUTHORS_SYM)},
|
||||
{ "AUTO_INCREMENT", SYM(AUTO_INC)},
|
||||
{ "AUTOEXTEND_SIZE", SYM(AUTOEXTEND_SIZE_SYM)},
|
||||
{ "AUTO", SYM(AUTO_SYM)},
|
||||
{ "AVG", SYM(AVG_SYM)},
|
||||
{ "AVG_ROW_LENGTH", SYM(AVG_ROW_LENGTH)},
|
||||
{ "BACKUP", SYM(BACKUP_SYM)},
|
||||
|
|
@ -329,6 +330,7 @@ static SYMBOL symbols[] = {
|
|||
{ "LOW_PRIORITY", SYM(LOW_PRIORITY)},
|
||||
{ "MASTER", SYM(MASTER_SYM)},
|
||||
{ "MASTER_CONNECT_RETRY", SYM(MASTER_CONNECT_RETRY_SYM)},
|
||||
{ "MASTER_USE_GTID", SYM(MASTER_USE_GTID_SYM)},
|
||||
{ "MASTER_HOST", SYM(MASTER_HOST_SYM)},
|
||||
{ "MASTER_LOG_FILE", SYM(MASTER_LOG_FILE_SYM)},
|
||||
{ "MASTER_LOG_POS", SYM(MASTER_LOG_POS_SYM)},
|
||||
|
|
|
|||
387
sql/log.cc
387
sql/log.cc
|
|
@ -120,6 +120,8 @@ static MYSQL_BIN_LOG::xid_count_per_binlog *
|
|||
static bool start_binlog_background_thread();
|
||||
|
||||
|
||||
static rpl_binlog_state rpl_global_gtid_binlog_state;
|
||||
|
||||
/**
|
||||
purge logs, master and slave sides both, related error code
|
||||
convertor.
|
||||
|
|
@ -686,7 +688,8 @@ bool Log_to_csv_event_handler::
|
|||
/* do a write */
|
||||
if (table->field[1]->store(user_host, user_host_len, client_cs) ||
|
||||
table->field[2]->store((longlong) thread_id, TRUE) ||
|
||||
table->field[3]->store((longlong) server_id, TRUE) ||
|
||||
table->field[3]->store((longlong) global_system_variables.server_id,
|
||||
TRUE) ||
|
||||
table->field[4]->store(command_type, command_type_len, client_cs))
|
||||
goto err;
|
||||
|
||||
|
|
@ -883,7 +886,7 @@ bool Log_to_csv_event_handler::
|
|||
table->field[8]->set_notnull();
|
||||
}
|
||||
|
||||
if (table->field[9]->store((longlong) server_id, TRUE))
|
||||
if (table->field[9]->store((longlong)global_system_variables.server_id, TRUE))
|
||||
goto err;
|
||||
table->field[9]->set_notnull();
|
||||
|
||||
|
|
@ -2288,7 +2291,7 @@ static int find_uniq_filename(char *name)
|
|||
my_dirend(dir_info);
|
||||
|
||||
/* check if reached the maximum possible extension number */
|
||||
if ((max_found == MAX_LOG_UNIQUE_FN_EXT))
|
||||
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 \
|
||||
|
|
@ -2925,7 +2928,7 @@ MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
|
|||
bytes_written(0), file_id(1), open_count(1),
|
||||
group_commit_queue(0), group_commit_queue_busy(FALSE),
|
||||
num_commits(0), num_group_commits(0),
|
||||
sync_period_ptr(sync_period), sync_counter(0),
|
||||
sync_period_ptr(sync_period), sync_counter(0), state_read(false),
|
||||
is_relay_log(0), signal_cnt(0),
|
||||
checksum_alg_reset(BINLOG_CHECKSUM_ALG_UNDEF),
|
||||
relay_log_checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF),
|
||||
|
|
@ -3119,6 +3122,9 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
|
|||
DBUG_ENTER("MYSQL_BIN_LOG::open");
|
||||
DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg));
|
||||
|
||||
if (!is_relay_log && read_state_from_file())
|
||||
DBUG_RETURN(1);
|
||||
|
||||
if (!is_relay_log && !binlog_background_thread_started &&
|
||||
start_binlog_background_thread())
|
||||
DBUG_RETURN(1);
|
||||
|
|
@ -3232,6 +3238,47 @@ bool MYSQL_BIN_LOG::open(const char *log_name,
|
|||
if (!is_relay_log)
|
||||
{
|
||||
char buf[FN_REFLEN];
|
||||
|
||||
/*
|
||||
Output a Gtid_list_log_event at the start of the binlog file.
|
||||
|
||||
This is used to quickly determine which GTIDs are found in binlog
|
||||
files earlier than this one, and which are found in this (or later)
|
||||
binlogs.
|
||||
|
||||
The list gives a mapping from (domain_id, server_id) -> seq_no (so
|
||||
this means that there is at most one entry for every unique pair
|
||||
(domain_id, server_id) in the list). It indicates that this seq_no is
|
||||
the last one found in an earlier binlog file for this (domain_id,
|
||||
server_id) combination - so any higher seq_no should be search for
|
||||
from this binlog file, or a later one.
|
||||
|
||||
This allows to locate the binlog file containing a given GTID by
|
||||
scanning backwards, reading just the Gtid_list_log_event at the
|
||||
start of each file, and scanning only the relevant binlog file when
|
||||
found, not all binlog files.
|
||||
|
||||
The existence of a given entry (domain_id, server_id, seq_no)
|
||||
guarantees only that this seq_no will not be found in this or any
|
||||
later binlog file. It does not guarantee that it can be found it an
|
||||
earlier binlog file, for example the file may have been purged.
|
||||
|
||||
If there is no entry for a given (domain_id, server_id) pair, then
|
||||
it means that no such GTID exists in any earlier binlog. It is
|
||||
permissible to remove such pair from future Gtid_list_log_events
|
||||
if all previous binlog files containing such GTIDs have been purged
|
||||
(though such optimization is not performed at the time of this
|
||||
writing). So if there is no entry for given GTID it means that such
|
||||
GTID should be search for in this or later binlog file, same as if
|
||||
there had been an entry (domain_id, server_id, 0).
|
||||
*/
|
||||
|
||||
Gtid_list_log_event gl_ev(&rpl_global_gtid_binlog_state);
|
||||
if (gl_ev.write(&log_file))
|
||||
goto err;
|
||||
|
||||
/* Output a binlog checkpoint event at the start of the binlog file. */
|
||||
|
||||
/*
|
||||
Construct an entry in the binlog_xid_count_list for the new binlog
|
||||
file (we will not link it into the list until we know the new file
|
||||
|
|
@ -3658,7 +3705,8 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log)
|
|||
const char* save_name;
|
||||
DBUG_ENTER("reset_logs");
|
||||
|
||||
ha_reset_logs(thd);
|
||||
if (thd)
|
||||
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.
|
||||
|
|
@ -3783,6 +3831,14 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log)
|
|||
break;
|
||||
}
|
||||
|
||||
if (!is_relay_log)
|
||||
{
|
||||
rpl_global_gtid_binlog_state.reset();
|
||||
mysql_mutex_lock(&LOCK_gtid_counter);
|
||||
global_gtid_counter= 0;
|
||||
mysql_mutex_unlock(&LOCK_gtid_counter);
|
||||
}
|
||||
|
||||
/* Start logging with a new file */
|
||||
close(LOG_CLOSE_INDEX | LOG_CLOSE_TO_BE_OPENED);
|
||||
if ((error= my_delete_allow_opened(index_file_name, MYF(0)))) // Reset (open will update)
|
||||
|
|
@ -5297,6 +5353,213 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd,
|
|||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
|
||||
/* Generate a new global transaction ID, and write it to the binlog */
|
||||
bool
|
||||
MYSQL_BIN_LOG::write_gtid_event(THD *thd, bool standalone,
|
||||
bool is_transactional)
|
||||
{
|
||||
rpl_gtid gtid;
|
||||
uint64 seq_no;
|
||||
|
||||
seq_no= thd->variables.gtid_seq_no;
|
||||
/*
|
||||
Reset the session variable gtid_seq_no, to reduce the risk of accidentally
|
||||
producing a duplicate GTID.
|
||||
*/
|
||||
thd->variables.gtid_seq_no= 0;
|
||||
if (seq_no != 0)
|
||||
{
|
||||
/*
|
||||
If we see a higher sequence number, use that one as the basis of any
|
||||
later generated sequence numbers.
|
||||
*/
|
||||
bump_seq_no_counter_if_needed(seq_no);
|
||||
}
|
||||
else
|
||||
{
|
||||
mysql_mutex_lock(&LOCK_gtid_counter);
|
||||
seq_no= ++global_gtid_counter;
|
||||
mysql_mutex_unlock(&LOCK_gtid_counter);
|
||||
}
|
||||
gtid.seq_no= seq_no;
|
||||
gtid.domain_id= thd->variables.gtid_domain_id;
|
||||
|
||||
Gtid_log_event gtid_event(thd, gtid.seq_no, gtid.domain_id, standalone,
|
||||
LOG_EVENT_SUPPRESS_USE_F, is_transactional);
|
||||
gtid.server_id= gtid_event.server_id;
|
||||
|
||||
/* Write the event to the binary log. */
|
||||
if (gtid_event.write(&mysql_bin_log.log_file))
|
||||
return true;
|
||||
status_var_add(thd->status_var.binlog_bytes_written, gtid_event.data_written);
|
||||
|
||||
/* Update the replication state (last GTID in each replication domain). */
|
||||
mysql_mutex_lock(&LOCK_rpl_gtid_state);
|
||||
rpl_global_gtid_binlog_state.update(>id);
|
||||
mysql_mutex_unlock(&LOCK_rpl_gtid_state);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
MYSQL_BIN_LOG::write_state_to_file()
|
||||
{
|
||||
File file_no;
|
||||
IO_CACHE cache;
|
||||
char buf[FN_REFLEN];
|
||||
int err;
|
||||
bool opened= false;
|
||||
bool inited= false;
|
||||
|
||||
fn_format(buf, opt_bin_logname, mysql_data_home, ".state",
|
||||
MY_UNPACK_FILENAME);
|
||||
if ((file_no= mysql_file_open(key_file_binlog_state, buf,
|
||||
O_RDWR|O_CREAT|O_TRUNC|O_BINARY,
|
||||
MYF(MY_WME))) < 0)
|
||||
{
|
||||
err= 1;
|
||||
goto err;
|
||||
}
|
||||
opened= true;
|
||||
if ((err= init_io_cache(&cache, file_no, IO_SIZE, WRITE_CACHE, 0, 0,
|
||||
MYF(MY_WME|MY_WAIT_IF_FULL))))
|
||||
goto err;
|
||||
inited= true;
|
||||
if ((err= rpl_global_gtid_binlog_state.write_to_iocache(&cache)))
|
||||
goto err;
|
||||
inited= false;
|
||||
if ((err= end_io_cache(&cache)))
|
||||
goto err;
|
||||
if ((err= mysql_file_sync(file_no, MYF(MY_WME|MY_SYNC_FILESIZE))))
|
||||
goto err;
|
||||
goto end;
|
||||
|
||||
err:
|
||||
sql_print_error("Error writing binlog state to file '%s'.\n", buf);
|
||||
if (inited)
|
||||
end_io_cache(&cache);
|
||||
end:
|
||||
if (opened)
|
||||
mysql_file_close(file_no, MYF(0));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
MYSQL_BIN_LOG::read_state_from_file()
|
||||
{
|
||||
File file_no;
|
||||
IO_CACHE cache;
|
||||
char buf[FN_REFLEN];
|
||||
int err;
|
||||
bool opened= false;
|
||||
bool inited= false;
|
||||
|
||||
if (state_read)
|
||||
return 0;
|
||||
state_read= true;
|
||||
|
||||
fn_format(buf, opt_bin_logname, mysql_data_home, ".state",
|
||||
MY_UNPACK_FILENAME);
|
||||
if ((file_no= mysql_file_open(key_file_binlog_state, buf,
|
||||
O_RDONLY|O_BINARY, MYF(0))) < 0)
|
||||
{
|
||||
if (my_errno != ENOENT)
|
||||
{
|
||||
err= 1;
|
||||
goto err;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
If the state file does not exist, this is the first server startup
|
||||
with GTID enabled. So initialize to empty state.
|
||||
*/
|
||||
rpl_global_gtid_binlog_state.reset();
|
||||
err= 0;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
opened= true;
|
||||
if ((err= init_io_cache(&cache, file_no, IO_SIZE, READ_CACHE, 0, 0,
|
||||
MYF(MY_WME|MY_WAIT_IF_FULL))))
|
||||
goto err;
|
||||
inited= true;
|
||||
if ((err= rpl_global_gtid_binlog_state.read_from_iocache(&cache)))
|
||||
goto err;
|
||||
goto end;
|
||||
|
||||
err:
|
||||
sql_print_error("Error reading binlog GTID state from file '%s'.\n", buf);
|
||||
end:
|
||||
if (inited)
|
||||
end_io_cache(&cache);
|
||||
if (opened)
|
||||
mysql_file_close(file_no, MYF(0));
|
||||
/* Pick the next unused seq_no from the loaded binlog state. */
|
||||
bump_seq_no_counter_if_needed(
|
||||
rpl_global_gtid_binlog_state.seq_no_from_state());
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
MYSQL_BIN_LOG::get_most_recent_gtid_list(rpl_gtid **list, uint32 *size)
|
||||
{
|
||||
return rpl_global_gtid_binlog_state.get_most_recent_gtid_list(list, size);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MYSQL_BIN_LOG::find_in_binlog_state(uint32 domain_id, uint32 server_id,
|
||||
rpl_gtid *out_gtid)
|
||||
{
|
||||
rpl_gtid *gtid;
|
||||
mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
|
||||
if ((gtid= rpl_global_gtid_binlog_state.find(domain_id, server_id)))
|
||||
*out_gtid= *gtid;
|
||||
mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
|
||||
return gtid != NULL;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
MYSQL_BIN_LOG::lookup_domain_in_binlog_state(uint32 domain_id,
|
||||
rpl_gtid *out_gtid)
|
||||
{
|
||||
rpl_binlog_state::element *elem;
|
||||
bool res;
|
||||
|
||||
mysql_mutex_lock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
|
||||
elem= (rpl_binlog_state::element *)
|
||||
my_hash_search(&rpl_global_gtid_binlog_state.hash,
|
||||
(const uchar *)&domain_id, 0);
|
||||
if (elem)
|
||||
{
|
||||
res= true;
|
||||
*out_gtid= *elem->last_gtid;
|
||||
}
|
||||
else
|
||||
res= false;
|
||||
mysql_mutex_unlock(&rpl_global_gtid_binlog_state.LOCK_binlog_state);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
MYSQL_BIN_LOG::bump_seq_no_counter_if_needed(uint64 seq_no)
|
||||
{
|
||||
mysql_mutex_lock(&LOCK_gtid_counter);
|
||||
if (global_gtid_counter < seq_no)
|
||||
global_gtid_counter= seq_no;
|
||||
mysql_mutex_unlock(&LOCK_gtid_counter);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Write an event to the binary log. If with_annotate != NULL and
|
||||
*with_annotate = TRUE write also Annotate_rows before the event
|
||||
|
|
@ -5366,6 +5629,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate)
|
|||
my_org_b_tell= my_b_tell(file);
|
||||
mysql_mutex_lock(&LOCK_log);
|
||||
prev_binlog_id= current_binlog_id;
|
||||
write_gtid_event(thd, true, using_trans);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -6238,19 +6502,6 @@ MYSQL_BIN_LOG::write_transaction_to_binlog(THD *thd,
|
|||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
Log "BEGIN" at the beginning of every transaction. Here, a transaction is
|
||||
either a BEGIN..COMMIT block or a single statement in autocommit mode.
|
||||
|
||||
Create the necessary events here, where we have the correct THD (and
|
||||
thread context).
|
||||
|
||||
Due to group commit the actual writing to binlog may happen in a different
|
||||
thread.
|
||||
*/
|
||||
Query_log_event qinfo(thd, STRING_WITH_LEN("BEGIN"), using_trx_cache, TRUE,
|
||||
TRUE, 0);
|
||||
entry.begin_event= &qinfo;
|
||||
entry.end_event= end_ev;
|
||||
if (cache_mngr->stmt_cache.has_incident() ||
|
||||
cache_mngr->trx_cache.has_incident())
|
||||
|
|
@ -6626,10 +6877,8 @@ MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry)
|
|||
{
|
||||
binlog_cache_mngr *mngr= entry->cache_mngr;
|
||||
|
||||
if (entry->begin_event->write(&log_file))
|
||||
if (write_gtid_event(entry->thd, false, entry->using_trx_cache))
|
||||
return ER_ERROR_ON_WRITE;
|
||||
status_var_add(entry->thd->status_var.binlog_bytes_written,
|
||||
entry->begin_event->data_written);
|
||||
|
||||
if (entry->using_stmt_cache && !mngr->stmt_cache.empty() &&
|
||||
write_cache(entry->thd, mngr->get_binlog_cache_log(FALSE)))
|
||||
|
|
@ -6770,6 +7019,8 @@ int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd,
|
|||
|
||||
void MYSQL_BIN_LOG::close(uint exiting)
|
||||
{ // One can't set log_type here!
|
||||
bool failed_to_save_state= false;
|
||||
|
||||
DBUG_ENTER("MYSQL_BIN_LOG::close");
|
||||
DBUG_PRINT("enter",("exiting: %d", (int) exiting));
|
||||
if (log_state == LOG_OPENED)
|
||||
|
|
@ -6787,6 +7038,27 @@ void MYSQL_BIN_LOG::close(uint exiting)
|
|||
s.write(&log_file);
|
||||
bytes_written+= s.data_written;
|
||||
signal_update();
|
||||
|
||||
/*
|
||||
When we shut down server, write out the binlog state to a separate
|
||||
file so we do not have to scan an entire binlog file to recover it
|
||||
at next server start.
|
||||
|
||||
Note that this must be written and synced to disk before marking the
|
||||
last binlog file as "not crashed".
|
||||
*/
|
||||
if (!is_relay_log && write_state_to_file())
|
||||
{
|
||||
sql_print_error("Failed to save binlog GTID state during shutdown. "
|
||||
"Binlog will be marked as crashed, so that crash "
|
||||
"recovery can recover the state at next server "
|
||||
"startup.");
|
||||
/*
|
||||
Leave binlog file marked as crashed, so we can recover state by
|
||||
scanning it now that we failed to write out the state properly.
|
||||
*/
|
||||
failed_to_save_state= true;
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_REPLICATION */
|
||||
|
||||
|
|
@ -6795,7 +7067,8 @@ void MYSQL_BIN_LOG::close(uint exiting)
|
|||
&& !(exiting & LOG_CLOSE_DELAYED_CLOSE))
|
||||
{
|
||||
my_off_t org_position= mysql_file_tell(log_file.file, MYF(0));
|
||||
clear_inuse_flag_when_closing(log_file.file);
|
||||
if (!failed_to_save_state)
|
||||
clear_inuse_flag_when_closing(log_file.file);
|
||||
/*
|
||||
Restore position so that anything we have in the IO_cache is written
|
||||
to the correct position.
|
||||
|
|
@ -7971,9 +8244,13 @@ int TC_LOG_BINLOG::open(const char *opt_name)
|
|||
sql_print_information("Recovering after a crash using %s", opt_name);
|
||||
error= recover(&log_info, log_name, &log,
|
||||
(Format_description_log_event *)ev);
|
||||
state_read= true;
|
||||
/* Pick the next unused seq_no from the recovered binlog state. */
|
||||
bump_seq_no_counter_if_needed(
|
||||
rpl_global_gtid_binlog_state.seq_no_from_state());
|
||||
}
|
||||
else
|
||||
error=0;
|
||||
error= read_state_from_file();
|
||||
|
||||
delete ev;
|
||||
end_io_cache(&log);
|
||||
|
|
@ -8223,6 +8500,28 @@ binlog_background_thread(void *arg __attribute__((unused)))
|
|||
mysql_mutex_unlock(&LOCK_thread_count);
|
||||
thd->store_globals();
|
||||
|
||||
/*
|
||||
Load the slave replication GTID state from the mysql.rpl_slave_state
|
||||
table.
|
||||
|
||||
This is mostly so that we can start our seq_no counter from the highest
|
||||
seq_no seen by a slave. This way, we have a way to tell if a transaction
|
||||
logged by ourselves as master is newer or older than a replicated
|
||||
transaction.
|
||||
*/
|
||||
#ifdef HAVE_REPLICATION
|
||||
if (rpl_load_gtid_slave_state(thd))
|
||||
sql_print_warning("Failed to load slave replication state from table "
|
||||
"%s.%s: %u: %s", "mysql",
|
||||
rpl_gtid_slave_state_table_name.str,
|
||||
thd->stmt_da->sql_errno(), thd->stmt_da->message());
|
||||
#endif
|
||||
|
||||
mysql_mutex_lock(&mysql_bin_log.LOCK_binlog_background_thread);
|
||||
binlog_background_thread_started= true;
|
||||
mysql_cond_signal(&mysql_bin_log.COND_binlog_background_thread_end);
|
||||
mysql_mutex_unlock(&mysql_bin_log.LOCK_binlog_background_thread);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
/*
|
||||
|
|
@ -8321,7 +8620,16 @@ start_binlog_background_thread()
|
|||
binlog_background_thread, NULL))
|
||||
return 1;
|
||||
|
||||
binlog_background_thread_started= true;
|
||||
/*
|
||||
Wait for the thread to have started (so we know that the slave replication
|
||||
state is loaded and we have correct global_gtid_counter).
|
||||
*/
|
||||
mysql_mutex_lock(&mysql_bin_log.LOCK_binlog_background_thread);
|
||||
while (!binlog_background_thread_started)
|
||||
mysql_cond_wait(&mysql_bin_log.COND_binlog_background_thread_end,
|
||||
&mysql_bin_log.LOCK_binlog_background_thread);
|
||||
mysql_mutex_unlock(&mysql_bin_log.LOCK_binlog_background_thread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -8400,6 +8708,37 @@ int TC_LOG_BINLOG::recover(LOG_INFO *linfo, const char *last_log_name,
|
|||
}
|
||||
break;
|
||||
}
|
||||
case GTID_LIST_EVENT:
|
||||
if (first_round)
|
||||
{
|
||||
uint32 i;
|
||||
Gtid_list_log_event *glev= (Gtid_list_log_event *)ev;
|
||||
|
||||
/* Initialise the binlog state from the Gtid_list event. */
|
||||
rpl_global_gtid_binlog_state.reset();
|
||||
for (i= 0; i < glev->count; ++i)
|
||||
{
|
||||
if (rpl_global_gtid_binlog_state.update(&(glev->list[i])))
|
||||
goto err2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GTID_EVENT:
|
||||
if (first_round)
|
||||
{
|
||||
Gtid_log_event *gev= (Gtid_log_event *)ev;
|
||||
rpl_gtid gtid;
|
||||
|
||||
/* Update the binlog state with any GTID logged after Gtid_list. */
|
||||
gtid.domain_id= gev->domain_id;
|
||||
gtid.server_id= gev->server_id;
|
||||
gtid.seq_no= gev->seq_no;
|
||||
if (rpl_global_gtid_binlog_state.update(>id))
|
||||
goto err2;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Nothing. */
|
||||
break;
|
||||
|
|
|
|||
14
sql/log.h
14
sql/log.h
|
|
@ -396,6 +396,7 @@ private:
|
|||
( ((ulong)(c)>>1) == BINLOG_COOKIE_DUMMY_ID )
|
||||
|
||||
class binlog_cache_mngr;
|
||||
struct rpl_gtid;
|
||||
class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
|
||||
{
|
||||
private:
|
||||
|
|
@ -420,11 +421,10 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
|
|||
bool using_stmt_cache;
|
||||
bool using_trx_cache;
|
||||
/*
|
||||
Extra events (BEGIN, COMMIT/ROLLBACK/XID, and possibly INCIDENT) to be
|
||||
Extra events (COMMIT/ROLLBACK/XID, and possibly INCIDENT) to be
|
||||
written during group commit. The incident_event is only valid if
|
||||
trx_data->has_incident() is true.
|
||||
*/
|
||||
Log_event *begin_event;
|
||||
Log_event *end_event;
|
||||
Log_event *incident_event;
|
||||
/* Set during group commit to record any per-thread error. */
|
||||
|
|
@ -507,6 +507,8 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
|
|||
*/
|
||||
uint *sync_period_ptr;
|
||||
uint sync_counter;
|
||||
/* Protect against reading the binlog state file twice. */
|
||||
bool state_read;
|
||||
|
||||
inline uint get_sync_period()
|
||||
{
|
||||
|
|
@ -773,6 +775,14 @@ public:
|
|||
inline uint32 get_open_count() { return open_count; }
|
||||
void set_status_variables(THD *thd);
|
||||
bool is_xidlist_idle();
|
||||
bool write_gtid_event(THD *thd, bool standalone, bool is_transactional);
|
||||
int read_state_from_file();
|
||||
int write_state_to_file();
|
||||
int get_most_recent_gtid_list(rpl_gtid **list, uint32 *size);
|
||||
bool find_in_binlog_state(uint32 domain_id, uint32 server_id,
|
||||
rpl_gtid *out_gtid);
|
||||
bool lookup_domain_in_binlog_state(uint32 domain_id, rpl_gtid *out_gtid);
|
||||
void bump_seq_no_counter_if_needed(uint64 seq_no);
|
||||
};
|
||||
|
||||
class Log_event_handler
|
||||
|
|
|
|||
556
sql/log_event.cc
556
sql/log_event.cc
|
|
@ -537,7 +537,7 @@ static char *slave_load_file_stem(char *buf, uint file_id,
|
|||
to_unix_path(buf);
|
||||
|
||||
buf = strend(buf);
|
||||
buf = int10_to_str(::server_id, buf, 10);
|
||||
buf = int10_to_str(global_system_variables.server_id, buf, 10);
|
||||
*buf++ = '-';
|
||||
buf = int10_to_str(event_server_id, buf, 10);
|
||||
*buf++ = '-';
|
||||
|
|
@ -573,7 +573,7 @@ static void cleanup_load_tmpdir()
|
|||
LOAD DATA.
|
||||
*/
|
||||
p= strmake(prefbuf, STRING_WITH_LEN(PREFIX_SQL_LOAD));
|
||||
p= int10_to_str(::server_id, p, 10);
|
||||
p= int10_to_str(global_system_variables.server_id, p, 10);
|
||||
*(p++)= '-';
|
||||
*p= 0;
|
||||
|
||||
|
|
@ -749,6 +749,8 @@ const char* Log_event::get_type_str(Log_event_type type)
|
|||
case INCIDENT_EVENT: return "Incident";
|
||||
case ANNOTATE_ROWS_EVENT: return "Annotate_rows";
|
||||
case BINLOG_CHECKPOINT_EVENT: return "Binlog_checkpoint";
|
||||
case GTID_EVENT: return "Gtid";
|
||||
case GTID_LIST_EVENT: return "Gtid_list";
|
||||
default: return "Unknown"; /* impossible */
|
||||
}
|
||||
}
|
||||
|
|
@ -769,7 +771,7 @@ Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans)
|
|||
crc(0), thd(thd_arg),
|
||||
checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF)
|
||||
{
|
||||
server_id= thd->server_id;
|
||||
server_id= thd->variables.server_id;
|
||||
when= thd->start_time;
|
||||
when_sec_part=thd->start_time_sec_part;
|
||||
|
||||
|
|
@ -794,7 +796,7 @@ Log_event::Log_event()
|
|||
cache_type(Log_event::EVENT_INVALID_CACHE), crc(0),
|
||||
thd(0), checksum_alg(BINLOG_CHECKSUM_ALG_UNDEF)
|
||||
{
|
||||
server_id= ::server_id;
|
||||
server_id= global_system_variables.server_id;
|
||||
/*
|
||||
We can't call my_time() here as this would cause a call before
|
||||
my_init() is called
|
||||
|
|
@ -909,9 +911,11 @@ int Log_event::do_update_pos(Relay_log_info *rli)
|
|||
if (debug_not_change_ts_if_art_event == 1
|
||||
&& is_artificial_event())
|
||||
debug_not_change_ts_if_art_event= 0; );
|
||||
rli->stmt_done(log_pos, is_artificial_event() &&
|
||||
IF_DBUG(debug_not_change_ts_if_art_event > 0, 1) ?
|
||||
0 : when);
|
||||
rli->stmt_done(log_pos,
|
||||
(is_artificial_event() &&
|
||||
IF_DBUG(debug_not_change_ts_if_art_event > 0, 1) ?
|
||||
0 : when),
|
||||
thd);
|
||||
DBUG_EXECUTE_IF("let_first_flush_log_change_timestamp",
|
||||
if (debug_not_change_ts_if_art_event == 0)
|
||||
debug_not_change_ts_if_art_event= 2; );
|
||||
|
|
@ -926,10 +930,11 @@ Log_event::do_shall_skip(Relay_log_info *rli)
|
|||
DBUG_PRINT("info", ("ev->server_id: %lu, ::server_id: %lu,"
|
||||
" rli->replicate_same_server_id: %d,"
|
||||
" rli->slave_skip_counter: %lu",
|
||||
(ulong) server_id, (ulong) ::server_id,
|
||||
(ulong) server_id, (ulong) global_system_variables.server_id,
|
||||
rli->replicate_same_server_id,
|
||||
rli->slave_skip_counter));
|
||||
if ((server_id == ::server_id && !rli->replicate_same_server_id) ||
|
||||
if ((server_id == global_system_variables.server_id &&
|
||||
!rli->replicate_same_server_id) ||
|
||||
(rli->slave_skip_counter == 1 && rli->is_in_group()) ||
|
||||
(flags & LOG_EVENT_SKIP_REPLICATION_F &&
|
||||
opt_replicate_events_marked_for_skip != RPL_SKIP_REPLICATE))
|
||||
|
|
@ -1370,7 +1375,7 @@ failed my_b_read"));
|
|||
Log_event *res= 0;
|
||||
#ifndef max_allowed_packet
|
||||
THD *thd=current_thd;
|
||||
uint max_allowed_packet= thd ? slave_max_allowed_packet:~(ulong)0;
|
||||
uint max_allowed_packet= thd ? slave_max_allowed_packet:~(uint)0;
|
||||
#endif
|
||||
|
||||
if (data_len > max_allowed_packet)
|
||||
|
|
@ -1560,6 +1565,12 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
|
|||
case BINLOG_CHECKPOINT_EVENT:
|
||||
ev = new Binlog_checkpoint_log_event(buf, event_len, description_event);
|
||||
break;
|
||||
case GTID_EVENT:
|
||||
ev = new Gtid_log_event(buf, event_len, description_event);
|
||||
break;
|
||||
case GTID_LIST_EVENT:
|
||||
ev = new Gtid_list_log_event(buf, event_len, description_event);
|
||||
break;
|
||||
#ifdef HAVE_REPLICATION
|
||||
case SLAVE_EVENT: /* can never happen (unused event) */
|
||||
ev = new Slave_log_event(buf, event_len, description_event);
|
||||
|
|
@ -3437,6 +3448,53 @@ Query_log_event::dummy_event(String *packet, ulong ev_offset,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Replace an event (GTID event) with a BEGIN query event, to be compatible
|
||||
with an old slave.
|
||||
*/
|
||||
int
|
||||
Query_log_event::begin_event(String *packet, ulong ev_offset,
|
||||
uint8 checksum_alg)
|
||||
{
|
||||
uchar *p= (uchar *)packet->ptr() + ev_offset;
|
||||
uchar *q= p + LOG_EVENT_HEADER_LEN;
|
||||
size_t data_len= packet->length() - ev_offset;
|
||||
uint16 flags;
|
||||
|
||||
if (checksum_alg == BINLOG_CHECKSUM_ALG_CRC32)
|
||||
data_len-= BINLOG_CHECKSUM_LEN;
|
||||
else
|
||||
DBUG_ASSERT(checksum_alg == BINLOG_CHECKSUM_ALG_UNDEF ||
|
||||
checksum_alg == BINLOG_CHECKSUM_ALG_OFF);
|
||||
|
||||
/* Currently we only need to replace GTID event. */
|
||||
DBUG_ASSERT(data_len == LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN);
|
||||
if (data_len != LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN)
|
||||
return 1;
|
||||
|
||||
flags= uint2korr(p + FLAGS_OFFSET);
|
||||
flags&= ~LOG_EVENT_THREAD_SPECIFIC_F;
|
||||
flags|= LOG_EVENT_SUPPRESS_USE_F;
|
||||
int2store(p + FLAGS_OFFSET, flags);
|
||||
|
||||
p[EVENT_TYPE_OFFSET]= QUERY_EVENT;
|
||||
int4store(q + Q_THREAD_ID_OFFSET, 0);
|
||||
int4store(q + Q_EXEC_TIME_OFFSET, 0);
|
||||
q[Q_DB_LEN_OFFSET]= 0;
|
||||
int2store(q + Q_ERR_CODE_OFFSET, 0);
|
||||
int2store(q + Q_STATUS_VARS_LEN_OFFSET, 0);
|
||||
q[Q_DATA_OFFSET]= 0; /* Zero terminator for empty db */
|
||||
q+= Q_DATA_OFFSET + 1;
|
||||
memcpy(q, "BEGIN", 5);
|
||||
|
||||
if (checksum_alg == BINLOG_CHECKSUM_ALG_CRC32)
|
||||
{
|
||||
ha_checksum crc= my_checksum(0L, p, data_len);
|
||||
int4store(p + data_len, crc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MYSQL_CLIENT
|
||||
/**
|
||||
|
|
@ -3696,6 +3754,8 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
|
|||
LEX_STRING new_db;
|
||||
int expected_error,actual_error= 0;
|
||||
HA_CREATE_INFO db_options;
|
||||
uint64 sub_id= 0;
|
||||
rpl_gtid gtid;
|
||||
DBUG_ENTER("Query_log_event::do_apply_event");
|
||||
|
||||
/*
|
||||
|
|
@ -3883,6 +3943,30 @@ int Query_log_event::do_apply_event(Relay_log_info const *rli,
|
|||
else
|
||||
thd->variables.collation_database= thd->db_charset;
|
||||
|
||||
/*
|
||||
Record any GTID in the same transaction, so slave state is
|
||||
transactionally consistent.
|
||||
*/
|
||||
if (strcmp("COMMIT", query) == 0 && (sub_id= rli->gtid_sub_id))
|
||||
{
|
||||
/* Clear the GTID from the RLI so we don't accidentally reuse it. */
|
||||
const_cast<Relay_log_info*>(rli)->gtid_sub_id= 0;
|
||||
|
||||
gtid= rli->current_gtid;
|
||||
if (rpl_global_gtid_slave_state.record_gtid(thd, >id, sub_id, true))
|
||||
{
|
||||
rli->report(ERROR_LEVEL, ER_CANNOT_UPDATE_GTID_STATE,
|
||||
"Error during COMMIT: failed to update GTID state in "
|
||||
"%s.%s: %d: %s",
|
||||
"mysql", rpl_gtid_slave_state_table_name.str,
|
||||
thd->stmt_da->sql_errno(), thd->stmt_da->message());
|
||||
trans_rollback(thd);
|
||||
sub_id= 0;
|
||||
thd->is_slave_error= 1;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
thd->table_map_for_update= (table_map)table_map_for_update;
|
||||
thd->set_invoker(&user, &host);
|
||||
/*
|
||||
|
|
@ -4068,6 +4152,9 @@ Default database: '%s'. Query: '%s'",
|
|||
}
|
||||
|
||||
end:
|
||||
if (sub_id && !thd->is_slave_error)
|
||||
rpl_global_gtid_slave_state.update_state_hash(sub_id, >id);
|
||||
|
||||
/*
|
||||
Probably we have set thd->query, thd->db, thd->catalog to point to places
|
||||
in the data_buf of this event. Now the event is going to be deleted
|
||||
|
|
@ -4145,6 +4232,17 @@ Query_log_event::do_shall_skip(Relay_log_info *rli)
|
|||
DBUG_RETURN(Log_event::do_shall_skip(rli));
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Query_log_event::peek_is_commit_rollback(const char *event_start,
|
||||
size_t event_len)
|
||||
{
|
||||
if (event_len < LOG_EVENT_HEADER_LEN + QUERY_HEADER_LEN || event_len < 9)
|
||||
return false;
|
||||
return !memcmp(event_start + (event_len-7), "\0COMMIT", 7) ||
|
||||
!memcmp(event_start + (event_len-9), "\0ROLLBACK", 9);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -4459,6 +4557,8 @@ Format_description_log_event(uint8 binlog_ver, const char* server_ver)
|
|||
post_header_len[ANNOTATE_ROWS_EVENT-1]= ANNOTATE_ROWS_HEADER_LEN;
|
||||
post_header_len[BINLOG_CHECKPOINT_EVENT-1]=
|
||||
BINLOG_CHECKPOINT_HEADER_LEN;
|
||||
post_header_len[GTID_EVENT-1]= GTID_HEADER_LEN;
|
||||
post_header_len[GTID_LIST_EVENT-1]= GTID_LIST_HEADER_LEN;
|
||||
|
||||
// Sanity-check that all post header lengths are initialized.
|
||||
int i;
|
||||
|
|
@ -4663,7 +4763,7 @@ int Format_description_log_event::do_apply_event(Relay_log_info const *rli)
|
|||
perform, we don't call Start_log_event_v3::do_apply_event()
|
||||
(this was just to update the log's description event).
|
||||
*/
|
||||
if (server_id != (uint32) ::server_id)
|
||||
if (server_id != (uint32) global_system_variables.server_id)
|
||||
{
|
||||
/*
|
||||
If the event was not requested by the slave i.e. the master sent
|
||||
|
|
@ -4689,7 +4789,7 @@ int Format_description_log_event::do_apply_event(Relay_log_info const *rli)
|
|||
|
||||
int Format_description_log_event::do_update_pos(Relay_log_info *rli)
|
||||
{
|
||||
if (server_id == (uint32) ::server_id)
|
||||
if (server_id == (uint32) global_system_variables.server_id)
|
||||
{
|
||||
/*
|
||||
We only increase the relay log position if we are skipping
|
||||
|
|
@ -5744,7 +5844,7 @@ int Rotate_log_event::do_update_pos(Relay_log_info *rli)
|
|||
#endif
|
||||
|
||||
DBUG_PRINT("info", ("server_id=%lu; ::server_id=%lu",
|
||||
(ulong) this->server_id, (ulong) ::server_id));
|
||||
(ulong) this->server_id, (ulong) global_system_variables.server_id));
|
||||
DBUG_PRINT("info", ("new_log_ident: %s", this->new_log_ident));
|
||||
DBUG_PRINT("info", ("pos: %s", llstr(this->pos, buf)));
|
||||
|
||||
|
|
@ -5764,7 +5864,8 @@ int Rotate_log_event::do_update_pos(Relay_log_info *rli)
|
|||
5.0.0, there also are some rotates from the slave itself, in the
|
||||
relay log, which shall not change the group positions.
|
||||
*/
|
||||
if ((server_id != ::server_id || rli->replicate_same_server_id) &&
|
||||
if ((server_id != global_system_variables.server_id ||
|
||||
rli->replicate_same_server_id) &&
|
||||
!is_relay_log_event() &&
|
||||
!rli->is_in_group())
|
||||
{
|
||||
|
|
@ -5781,6 +5882,7 @@ int Rotate_log_event::do_update_pos(Relay_log_info *rli)
|
|||
rli->group_master_log_name,
|
||||
(ulong) rli->group_master_log_pos));
|
||||
mysql_mutex_unlock(&rli->data_lock);
|
||||
rpl_global_gtid_slave_state.record_and_update_gtid(thd, rli);
|
||||
flush_relay_log_info(rli);
|
||||
|
||||
/*
|
||||
|
|
@ -5904,6 +6006,394 @@ bool Binlog_checkpoint_log_event::write(IO_CACHE *file)
|
|||
#endif /* MYSQL_CLIENT */
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
Global transaction ID stuff
|
||||
**************************************************************************/
|
||||
|
||||
Gtid_log_event::Gtid_log_event(const char *buf, uint event_len,
|
||||
const Format_description_log_event *description_event)
|
||||
: Log_event(buf, description_event), seq_no(0)
|
||||
{
|
||||
uint8 header_size= description_event->common_header_len;
|
||||
uint8 post_header_len= description_event->post_header_len[GTID_EVENT-1];
|
||||
if (event_len < header_size + post_header_len ||
|
||||
post_header_len < GTID_HEADER_LEN)
|
||||
return;
|
||||
|
||||
buf+= header_size;
|
||||
seq_no= uint8korr(buf);
|
||||
buf+= 8;
|
||||
domain_id= uint4korr(buf);
|
||||
buf+= 4;
|
||||
flags2= *buf;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MYSQL_SERVER
|
||||
|
||||
Gtid_log_event::Gtid_log_event(THD *thd_arg, uint64 seq_no_arg,
|
||||
uint32 domain_id_arg, bool standalone,
|
||||
uint16 flags_arg, bool is_transactional)
|
||||
: Log_event(thd_arg, flags_arg, is_transactional),
|
||||
seq_no(seq_no_arg), domain_id(domain_id_arg),
|
||||
flags2(standalone ? FL_STANDALONE : 0)
|
||||
{
|
||||
cache_type= Log_event::EVENT_NO_CACHE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Used to record GTID while sending binlog to slave, without having to
|
||||
fully contruct every Gtid_log_event() needlessly.
|
||||
*/
|
||||
bool
|
||||
Gtid_log_event::peek(const char *event_start, size_t event_len,
|
||||
uint32 *domain_id, uint32 *server_id, uint64 *seq_no,
|
||||
uchar *flags2)
|
||||
{
|
||||
const char *p;
|
||||
if (event_len < LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN)
|
||||
return true;
|
||||
*server_id= uint4korr(event_start + SERVER_ID_OFFSET);
|
||||
p= event_start + LOG_EVENT_HEADER_LEN;
|
||||
*seq_no= uint8korr(p);
|
||||
p+= 8;
|
||||
*domain_id= uint4korr(p);
|
||||
p+= 4;
|
||||
*flags2= (uchar)*p;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Gtid_log_event::write(IO_CACHE *file)
|
||||
{
|
||||
uchar buf[GTID_HEADER_LEN];
|
||||
int8store(buf, seq_no);
|
||||
int4store(buf+8, domain_id);
|
||||
buf[12]= flags2;
|
||||
bzero(buf+13, GTID_HEADER_LEN-13);
|
||||
return write_header(file, GTID_HEADER_LEN) ||
|
||||
wrapper_my_b_safe_write(file, buf, GTID_HEADER_LEN) ||
|
||||
write_footer(file);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Replace a GTID event with either a BEGIN event, dummy event, or nothing, as
|
||||
appropriate to work with old slave that does not know global transaction id.
|
||||
|
||||
The need_dummy_event argument is an IN/OUT argument. It is passed as TRUE
|
||||
if slave has capability lower than MARIA_SLAVE_CAPABILITY_TOLERATE_HOLES.
|
||||
It is returned TRUE if we return a BEGIN (or dummy) event to be sent to the
|
||||
slave, FALSE if event should be skipped completely.
|
||||
*/
|
||||
int
|
||||
Gtid_log_event::make_compatible_event(String *packet, bool *need_dummy_event,
|
||||
ulong ev_offset, uint8 checksum_alg)
|
||||
{
|
||||
uchar flags2;
|
||||
if (packet->length() - ev_offset < LOG_EVENT_HEADER_LEN + GTID_HEADER_LEN)
|
||||
return 1;
|
||||
flags2= (*packet)[ev_offset + LOG_EVENT_HEADER_LEN + 12];
|
||||
if (flags2 & FL_STANDALONE)
|
||||
{
|
||||
if (need_dummy_event)
|
||||
return Query_log_event::dummy_event(packet, ev_offset, checksum_alg);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
*need_dummy_event= true;
|
||||
return Query_log_event::begin_event(packet, ev_offset, checksum_alg);
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_REPLICATION
|
||||
void
|
||||
Gtid_log_event::pack_info(THD *thd, Protocol *protocol)
|
||||
{
|
||||
char buf[6+5+10+1+10+1+20+1];
|
||||
char *p;
|
||||
p = strmov(buf, (flags2 & FL_STANDALONE ? "GTID " : "BEGIN GTID "));
|
||||
p= longlong10_to_str(domain_id, p, 10);
|
||||
*p++= '-';
|
||||
p= longlong10_to_str(server_id, p, 10);
|
||||
*p++= '-';
|
||||
p= longlong10_to_str(seq_no, p, 10);
|
||||
|
||||
protocol->store(buf, p-buf, &my_charset_bin);
|
||||
}
|
||||
|
||||
static char gtid_begin_string[] = "BEGIN";
|
||||
|
||||
int
|
||||
Gtid_log_event::do_apply_event(Relay_log_info const *rli)
|
||||
{
|
||||
thd->variables.server_id= this->server_id;
|
||||
thd->variables.gtid_domain_id= this->domain_id;
|
||||
thd->variables.gtid_seq_no= this->seq_no;
|
||||
|
||||
if (flags2 & FL_STANDALONE)
|
||||
return 0;
|
||||
|
||||
/* Execute this like a BEGIN query event. */
|
||||
thd->set_query_and_id(gtid_begin_string, sizeof(gtid_begin_string)-1,
|
||||
&my_charset_bin, next_query_id());
|
||||
Parser_state parser_state;
|
||||
if (!parser_state.init(thd, thd->query(), thd->query_length()))
|
||||
{
|
||||
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
|
||||
/* Finalize server status flags after executing a statement. */
|
||||
thd->update_server_status();
|
||||
log_slow_statement(thd);
|
||||
if (unlikely(thd->is_fatal_error))
|
||||
thd->is_slave_error= 1;
|
||||
else if (likely(!thd->is_slave_error))
|
||||
general_log_write(thd, COM_QUERY, thd->query(), thd->query_length());
|
||||
}
|
||||
|
||||
thd->reset_query();
|
||||
free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC));
|
||||
return thd->is_slave_error;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
Gtid_log_event::do_update_pos(Relay_log_info *rli)
|
||||
{
|
||||
rli->inc_event_relay_log_pos();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Log_event::enum_skip_reason
|
||||
Gtid_log_event::do_shall_skip(Relay_log_info *rli)
|
||||
{
|
||||
/*
|
||||
An event skipped due to @@skip_replication must not be counted towards the
|
||||
number of events to be skipped due to @@sql_slave_skip_counter.
|
||||
*/
|
||||
if (flags & LOG_EVENT_SKIP_REPLICATION_F &&
|
||||
opt_replicate_events_marked_for_skip != RPL_SKIP_REPLICATE)
|
||||
return Log_event::EVENT_SKIP_IGNORE;
|
||||
|
||||
if (rli->slave_skip_counter > 0)
|
||||
{
|
||||
if (!(flags2 & FL_STANDALONE))
|
||||
thd->variables.option_bits|= OPTION_BEGIN;
|
||||
return Log_event::continue_group(rli);
|
||||
}
|
||||
return Log_event::do_shall_skip(rli);
|
||||
}
|
||||
|
||||
|
||||
#endif /* HAVE_REPLICATION */
|
||||
|
||||
#else /* !MYSQL_SERVER */
|
||||
|
||||
void
|
||||
Gtid_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
|
||||
{
|
||||
Write_on_release_cache cache(&print_event_info->head_cache, file,
|
||||
Write_on_release_cache::FLUSH_F);
|
||||
char buf[21];
|
||||
|
||||
if (!print_event_info->short_form)
|
||||
{
|
||||
print_header(&cache, print_event_info, FALSE);
|
||||
longlong10_to_str(seq_no, buf, 10);
|
||||
my_b_printf(&cache, "\tGTID %u-%u-%s\n", domain_id, server_id, buf);
|
||||
|
||||
if (!print_event_info->domain_id_printed ||
|
||||
print_event_info->domain_id != domain_id)
|
||||
{
|
||||
my_b_printf(&cache, "/*!100001 SET @@session.gtid_domain_id=%u*/%s\n",
|
||||
domain_id, print_event_info->delimiter);
|
||||
print_event_info->domain_id= domain_id;
|
||||
print_event_info->domain_id_printed= true;
|
||||
}
|
||||
|
||||
if (!print_event_info->server_id_printed ||
|
||||
print_event_info->server_id != server_id)
|
||||
{
|
||||
my_b_printf(&cache, "/*!100001 SET @@session.server_id=%u*/%s\n",
|
||||
server_id, print_event_info->delimiter);
|
||||
print_event_info->server_id= server_id;
|
||||
print_event_info->server_id_printed= true;
|
||||
}
|
||||
|
||||
my_b_printf(&cache, "/*!100001 SET @@session.gtid_seq_no=%s*/%s\n",
|
||||
buf, print_event_info->delimiter);
|
||||
}
|
||||
if (!(flags2 & FL_STANDALONE))
|
||||
my_b_printf(&cache, "BEGIN\n%s\n", print_event_info->delimiter);
|
||||
}
|
||||
|
||||
#endif /* MYSQL_SERVER */
|
||||
|
||||
|
||||
/* GTID list. */
|
||||
|
||||
Gtid_list_log_event::Gtid_list_log_event(const char *buf, uint event_len,
|
||||
const Format_description_log_event *description_event)
|
||||
: Log_event(buf, description_event), count(0), list(0)
|
||||
{
|
||||
uint32 i;
|
||||
uint8 header_size= description_event->common_header_len;
|
||||
uint8 post_header_len= description_event->post_header_len[GTID_LIST_EVENT-1];
|
||||
if (event_len < header_size + post_header_len ||
|
||||
post_header_len < GTID_LIST_HEADER_LEN)
|
||||
return;
|
||||
|
||||
buf+= header_size;
|
||||
count= uint4korr(buf) & ((1<<28)-1);
|
||||
buf+= 4;
|
||||
if (event_len - (header_size + post_header_len) < count*element_size ||
|
||||
(!(list= (rpl_gtid *)my_malloc(count*sizeof(*list) + (count == 0),
|
||||
MYF(MY_WME)))))
|
||||
return;
|
||||
|
||||
for (i= 0; i < count; ++i)
|
||||
{
|
||||
list[i].domain_id= uint4korr(buf);
|
||||
buf+= 4;
|
||||
list[i].server_id= uint4korr(buf);
|
||||
buf+= 4;
|
||||
list[i].seq_no= uint8korr(buf);
|
||||
buf+= 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef MYSQL_SERVER
|
||||
|
||||
Gtid_list_log_event::Gtid_list_log_event(rpl_binlog_state *gtid_set)
|
||||
: count(gtid_set->count()), list(0)
|
||||
{
|
||||
cache_type= EVENT_NO_CACHE;
|
||||
/* Failure to allocate memory will be caught by is_valid() returning false. */
|
||||
if (count < (1<<28) &&
|
||||
(list = (rpl_gtid *)my_malloc(count * sizeof(*list) + (count == 0),
|
||||
MYF(MY_WME))))
|
||||
gtid_set->get_gtid_list(list, count);
|
||||
}
|
||||
|
||||
bool
|
||||
Gtid_list_log_event::write(IO_CACHE *file)
|
||||
{
|
||||
uint32 i;
|
||||
uchar buf[element_size];
|
||||
|
||||
DBUG_ASSERT(count < 1<<28);
|
||||
|
||||
if (write_header(file, get_data_size()))
|
||||
return 1;
|
||||
int4store(buf, count & ((1<<28)-1));
|
||||
if (wrapper_my_b_safe_write(file, buf, GTID_LIST_HEADER_LEN))
|
||||
return 1;
|
||||
for (i= 0; i < count; ++i)
|
||||
{
|
||||
int4store(buf, list[i].domain_id);
|
||||
int4store(buf+4, list[i].server_id);
|
||||
int8store(buf+8, list[i].seq_no);
|
||||
if (wrapper_my_b_safe_write(file, buf, element_size))
|
||||
return 1;
|
||||
}
|
||||
return write_footer(file);
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_REPLICATION
|
||||
void
|
||||
Gtid_list_log_event::pack_info(THD *thd, Protocol *protocol)
|
||||
{
|
||||
char buf_mem[1024];
|
||||
String buf(buf_mem, sizeof(buf_mem), system_charset_info);
|
||||
uint32 i;
|
||||
bool first;
|
||||
|
||||
buf.length(0);
|
||||
buf.append(STRING_WITH_LEN("["));
|
||||
first= true;
|
||||
for (i= 0; i < count; ++i)
|
||||
rpl_slave_state_tostring_helper(&buf, &list[i], &first);
|
||||
buf.append(STRING_WITH_LEN("]"));
|
||||
|
||||
protocol->store(&buf);
|
||||
}
|
||||
#endif /* HAVE_REPLICATION */
|
||||
|
||||
#else /* !MYSQL_SERVER */
|
||||
|
||||
void
|
||||
Gtid_list_log_event::print(FILE *file, PRINT_EVENT_INFO *print_event_info)
|
||||
{
|
||||
if (!print_event_info->short_form)
|
||||
{
|
||||
Write_on_release_cache cache(&print_event_info->head_cache, file,
|
||||
Write_on_release_cache::FLUSH_F);
|
||||
char buf[21];
|
||||
uint32 i;
|
||||
|
||||
print_header(&cache, print_event_info, FALSE);
|
||||
my_b_printf(&cache, "\tGtid list [");
|
||||
for (i= 0; i < count; ++i)
|
||||
{
|
||||
longlong10_to_str(list[i].seq_no, buf, 10);
|
||||
my_b_printf(&cache, "%u-%u-%s", list[i].domain_id,
|
||||
list[i].server_id, buf);
|
||||
if (i < count-1)
|
||||
my_b_printf(&cache, ",\n# ");
|
||||
}
|
||||
my_b_printf(&cache, "]\n");
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MYSQL_SERVER */
|
||||
|
||||
|
||||
/*
|
||||
Used to record gtid_list event while sending binlog to slave, without having to
|
||||
fully contruct the event object.
|
||||
*/
|
||||
bool
|
||||
Gtid_list_log_event::peek(const char *event_start, uint32 event_len,
|
||||
rpl_gtid **out_gtid_list, uint32 *out_list_len)
|
||||
{
|
||||
const char *p;
|
||||
uint32 count_field, count;
|
||||
rpl_gtid *gtid_list;
|
||||
|
||||
if (event_len < LOG_EVENT_HEADER_LEN + GTID_LIST_HEADER_LEN)
|
||||
return true;
|
||||
p= event_start + LOG_EVENT_HEADER_LEN;
|
||||
count_field= uint4korr(p);
|
||||
p+= 4;
|
||||
count= count_field & ((1<<28)-1);
|
||||
if (event_len < LOG_EVENT_HEADER_LEN + GTID_LIST_HEADER_LEN +
|
||||
16 * count)
|
||||
return true;
|
||||
if (!(gtid_list= (rpl_gtid *)my_malloc(sizeof(rpl_gtid)*count + (count == 0),
|
||||
MYF(MY_WME))))
|
||||
return true;
|
||||
*out_gtid_list= gtid_list;
|
||||
*out_list_len= count;
|
||||
while (count--)
|
||||
{
|
||||
gtid_list->domain_id= uint4korr(p);
|
||||
p+= 4;
|
||||
gtid_list->server_id= uint4korr(p);
|
||||
p+= 4;
|
||||
gtid_list->seq_no= uint8korr(p);
|
||||
p+= 8;
|
||||
++gtid_list;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
Intvar_log_event methods
|
||||
**************************************************************************/
|
||||
|
|
@ -6257,12 +6747,43 @@ void Xid_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
|
|||
int Xid_log_event::do_apply_event(Relay_log_info const *rli)
|
||||
{
|
||||
bool res;
|
||||
int err;
|
||||
rpl_gtid gtid;
|
||||
uint64 sub_id;
|
||||
|
||||
/*
|
||||
Record any GTID in the same transaction, so slave state is transactionally
|
||||
consistent.
|
||||
*/
|
||||
if ((sub_id= rli->gtid_sub_id))
|
||||
{
|
||||
/* Clear the GTID from the RLI so we don't accidentally reuse it. */
|
||||
const_cast<Relay_log_info*>(rli)->gtid_sub_id= 0;
|
||||
|
||||
gtid= rli->current_gtid;
|
||||
err= rpl_global_gtid_slave_state.record_gtid(thd, >id, sub_id, true);
|
||||
if (err)
|
||||
{
|
||||
rli->report(ERROR_LEVEL, ER_CANNOT_UPDATE_GTID_STATE,
|
||||
"Error during XID COMMIT: failed to update GTID state in "
|
||||
"%s.%s: %d: %s",
|
||||
"mysql", rpl_gtid_slave_state_table_name.str,
|
||||
thd->stmt_da->sql_errno(), thd->stmt_da->message());
|
||||
trans_rollback(thd);
|
||||
thd->is_slave_error= 1;
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* For a slave Xid_log_event is COMMIT */
|
||||
general_log_print(thd, COM_QUERY,
|
||||
"COMMIT /* implicit, from Xid_log_event */");
|
||||
res= trans_commit(thd); /* Automatically rolls back on error. */
|
||||
thd->mdl_context.release_transactional_locks();
|
||||
|
||||
if (sub_id)
|
||||
rpl_global_gtid_slave_state.update_state_hash(sub_id, >id);
|
||||
|
||||
/*
|
||||
Increment the global status commit count variable
|
||||
*/
|
||||
|
|
@ -7009,6 +7530,7 @@ int Stop_log_event::do_update_pos(Relay_log_info *rli)
|
|||
rli->inc_event_relay_log_pos();
|
||||
else
|
||||
{
|
||||
rpl_global_gtid_slave_state.record_and_update_gtid(thd, rli);
|
||||
rli->inc_group_relay_log_pos(0);
|
||||
flush_relay_log_info(rli);
|
||||
}
|
||||
|
|
@ -8810,7 +9332,7 @@ Rows_log_event::do_update_pos(Relay_log_info *rli)
|
|||
Step the group log position if we are not in a transaction,
|
||||
otherwise increase the event log position.
|
||||
*/
|
||||
rli->stmt_done(log_pos, when);
|
||||
rli->stmt_done(log_pos, when, thd);
|
||||
/*
|
||||
Clear any errors in thd->net.last_err*. It is not known if this is
|
||||
needed or not. It is believed that any errors that may exist in
|
||||
|
|
@ -11148,7 +11670,9 @@ st_print_event_info::st_print_event_info()
|
|||
auto_increment_increment(0),auto_increment_offset(0), charset_inited(0),
|
||||
lc_time_names_number(~0),
|
||||
charset_database_number(ILLEGAL_CHARSET_INFO_NUMBER),
|
||||
thread_id(0), thread_id_printed(false), skip_replication(0),
|
||||
thread_id(0), thread_id_printed(false), server_id(0),
|
||||
server_id_printed(false), domain_id(0), domain_id_printed(false),
|
||||
skip_replication(0),
|
||||
base64_output_mode(BASE64_OUTPUT_UNSPEC), printed_fd_event(FALSE)
|
||||
{
|
||||
/*
|
||||
|
|
|
|||
256
sql/log_event.h
256
sql/log_event.h
|
|
@ -50,6 +50,8 @@
|
|||
#include "sql_class.h" /* THD */
|
||||
#endif
|
||||
|
||||
#include "rpl_gtid.h"
|
||||
|
||||
/* Forward declarations */
|
||||
class String;
|
||||
|
||||
|
|
@ -261,6 +263,8 @@ struct sql_ex_info
|
|||
#define HEARTBEAT_HEADER_LEN 0
|
||||
#define ANNOTATE_ROWS_HEADER_LEN 0
|
||||
#define BINLOG_CHECKPOINT_HEADER_LEN 4
|
||||
#define GTID_HEADER_LEN 19
|
||||
#define GTID_LIST_HEADER_LEN 4
|
||||
|
||||
/*
|
||||
Max number of possible extra bytes in a replication event compared to a
|
||||
|
|
@ -600,16 +604,13 @@ enum enum_binlog_checksum_alg {
|
|||
because they mis-compute the offsets into the master's binlog).
|
||||
*/
|
||||
#define MARIA_SLAVE_CAPABILITY_TOLERATE_HOLES 2
|
||||
/* MariaDB > 5.5, which knows about binlog_checkpoint_log_event. */
|
||||
/* MariaDB >= 10.0, which knows about binlog_checkpoint_log_event. */
|
||||
#define MARIA_SLAVE_CAPABILITY_BINLOG_CHECKPOINT 3
|
||||
/*
|
||||
MariaDB server which understands MySQL 5.6 ignorable events. This server
|
||||
can tolerate receiving any event with the LOG_EVENT_IGNORABLE_F flag set.
|
||||
*/
|
||||
#define MARIA_SLAVE_CAPABILITY_IGNORABLE 4
|
||||
/* MariaDB >= 10.0.1, which knows about global transaction id events. */
|
||||
#define MARIA_SLAVE_CAPABILITY_GTID 4
|
||||
|
||||
/* Our capability. */
|
||||
#define MARIA_SLAVE_CAPABILITY_MINE MARIA_SLAVE_CAPABILITY_BINLOG_CHECKPOINT
|
||||
#define MARIA_SLAVE_CAPABILITY_MINE MARIA_SLAVE_CAPABILITY_GTID
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -695,6 +696,18 @@ enum Log_event_type
|
|||
that are prepared in storage engines but not yet committed.
|
||||
*/
|
||||
BINLOG_CHECKPOINT_EVENT= 161,
|
||||
/*
|
||||
Gtid event. For global transaction ID, used to start a new event group,
|
||||
instead of the old BEGIN query event, and also to mark stand-alone
|
||||
events.
|
||||
*/
|
||||
GTID_EVENT= 162,
|
||||
/*
|
||||
Gtid list event. Logged at the start of every binlog, to record the
|
||||
current replication state. This consists of the last GTID seen for
|
||||
each replication domain.
|
||||
*/
|
||||
GTID_LIST_EVENT= 163,
|
||||
|
||||
/* Add new MariaDB events here - right above this comment! */
|
||||
|
||||
|
|
@ -767,6 +780,11 @@ typedef struct st_print_event_info
|
|||
uint charset_database_number;
|
||||
uint thread_id;
|
||||
bool thread_id_printed;
|
||||
uint32 server_id;
|
||||
bool server_id_printed;
|
||||
uint32 domain_id;
|
||||
bool domain_id_printed;
|
||||
|
||||
/*
|
||||
Track when @@skip_replication changes so we need to output a SET
|
||||
statement for it.
|
||||
|
|
@ -1302,6 +1320,35 @@ public:
|
|||
return do_shall_skip(rli);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Check if an event is non-final part of a stand-alone event group,
|
||||
such as Intvar_log_event (such events should be processed as part
|
||||
of the following event group, not individually).
|
||||
*/
|
||||
static bool is_part_of_group(enum Log_event_type ev_type)
|
||||
{
|
||||
switch (ev_type)
|
||||
{
|
||||
case GTID_EVENT:
|
||||
case INTVAR_EVENT:
|
||||
case RAND_EVENT:
|
||||
case USER_VAR_EVENT:
|
||||
case TABLE_MAP_EVENT:
|
||||
case ANNOTATE_ROWS_EVENT:
|
||||
return true;
|
||||
case DELETE_ROWS_EVENT:
|
||||
case UPDATE_ROWS_EVENT:
|
||||
case WRITE_ROWS_EVENT:
|
||||
/*
|
||||
ToDo: also check for non-final Rows_log_event (though such events
|
||||
are usually in a BEGIN-COMMIT group).
|
||||
*/
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
|
|
@ -1875,6 +1922,7 @@ public:
|
|||
}
|
||||
Log_event_type get_type_code() { return QUERY_EVENT; }
|
||||
static int dummy_event(String *packet, ulong ev_offset, uint8 checksum_alg);
|
||||
static int begin_event(String *packet, ulong ev_offset, uint8 checksum_alg);
|
||||
#ifdef MYSQL_SERVER
|
||||
bool write(IO_CACHE* file);
|
||||
virtual bool write_post_header_for_derived(IO_CACHE* file) { return FALSE; }
|
||||
|
|
@ -1897,6 +1945,8 @@ public: /* !!! Public in this patch to allow old usage */
|
|||
int do_apply_event(Relay_log_info const *rli,
|
||||
const char *query_arg,
|
||||
uint32 q_len_arg);
|
||||
static bool peek_is_commit_rollback(const char *event_start,
|
||||
size_t event_len);
|
||||
#endif /* HAVE_REPLICATION */
|
||||
/*
|
||||
If true, the event always be applied by slave SQL thread or be printed by
|
||||
|
|
@ -2410,7 +2460,7 @@ protected:
|
|||
Events from ourself should be skipped, but they should not
|
||||
decrease the slave skip counter.
|
||||
*/
|
||||
if (this->server_id == ::server_id)
|
||||
if (this->server_id == global_system_variables.server_id)
|
||||
return Log_event::EVENT_SKIP_IGNORE;
|
||||
else
|
||||
return Log_event::EVENT_SKIP_NOT;
|
||||
|
|
@ -2815,7 +2865,7 @@ private:
|
|||
Events from ourself should be skipped, but they should not
|
||||
decrease the slave skip counter.
|
||||
*/
|
||||
if (this->server_id == ::server_id)
|
||||
if (this->server_id == global_system_variables.server_id)
|
||||
return Log_event::EVENT_SKIP_IGNORE;
|
||||
else
|
||||
return Log_event::EVENT_SKIP_NOT;
|
||||
|
|
@ -2942,6 +2992,194 @@ public:
|
|||
#endif
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@class Gtid_log_event
|
||||
|
||||
This event is logged as part of every event group to give the global
|
||||
transaction id (GTID) of that group.
|
||||
|
||||
It replaces the BEGIN query event used in earlier versions to begin most
|
||||
event groups, but is also used for events that used to be stand-alone.
|
||||
|
||||
@section Gtid_log_event_binary_format Binary Format
|
||||
|
||||
The binary format for Gtid_log_event has 6 extra reserved bytes to make the
|
||||
length a total of 19 byte (+ 19 bytes of header in common with all events).
|
||||
This is just the minimal size for a BEGIN query event, which makes it easy
|
||||
to replace this event with such BEGIN event to remain compatible with old
|
||||
slave servers.
|
||||
|
||||
<table>
|
||||
<caption>Post-Header</caption>
|
||||
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Format</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>seq_no</td>
|
||||
<td>8 byte unsigned integer</td>
|
||||
<td>increasing id within one server_id. Starts at 1, holes in the sequence
|
||||
may occur</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>domain_id</td>
|
||||
<td>4 byte unsigned integer</td>
|
||||
<td>Replication domain id, identifying independent replication streams></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>flags</td>
|
||||
<td>1 byte bitfield</td>
|
||||
<td>Bit 0 set indicates stand-alone event (no terminating COMMIT)</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Reserved</td>
|
||||
<td>6 bytes</td>
|
||||
<td>Reserved bytes, set to 0. Maybe be used for future expansion.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
The Body of Gtid_log_event is empty. The total event size is 19 bytes +
|
||||
the normal 19 bytes common-header.
|
||||
*/
|
||||
|
||||
class Gtid_log_event: public Log_event
|
||||
{
|
||||
public:
|
||||
uint64 seq_no;
|
||||
uint32 domain_id;
|
||||
uchar flags2;
|
||||
|
||||
/* Flags2. */
|
||||
|
||||
/* FL_STANDALONE is set when there is no terminating COMMIT event. */
|
||||
static const uchar FL_STANDALONE= 1;
|
||||
|
||||
#ifdef MYSQL_SERVER
|
||||
Gtid_log_event(THD *thd_arg, uint64 seq_no, uint32 domain_id, bool standalone,
|
||||
uint16 flags, bool is_transactional);
|
||||
#ifdef HAVE_REPLICATION
|
||||
void pack_info(THD *thd, Protocol *protocol);
|
||||
virtual int do_apply_event(Relay_log_info const *rli);
|
||||
virtual int do_update_pos(Relay_log_info *rli);
|
||||
virtual enum_skip_reason do_shall_skip(Relay_log_info *rli);
|
||||
#endif
|
||||
#else
|
||||
void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
|
||||
#endif
|
||||
Gtid_log_event(const char *buf, uint event_len,
|
||||
const Format_description_log_event *description_event);
|
||||
~Gtid_log_event() { }
|
||||
Log_event_type get_type_code() { return GTID_EVENT; }
|
||||
int get_data_size() { return GTID_HEADER_LEN; }
|
||||
bool is_valid() const { return seq_no != 0; }
|
||||
#ifdef MYSQL_SERVER
|
||||
bool write(IO_CACHE *file);
|
||||
static int make_compatible_event(String *packet, bool *need_dummy_event,
|
||||
ulong ev_offset, uint8 checksum_alg);
|
||||
static bool peek(const char *event_start, size_t event_len,
|
||||
uint32 *domain_id, uint32 *server_id, uint64 *seq_no,
|
||||
uchar *flags2);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@class Gtid_list_log_event
|
||||
|
||||
This event is logged at the start of every binlog file to record the
|
||||
current replication state: the last global transaction id (GTID) applied
|
||||
on the server within each replication domain.
|
||||
|
||||
It consists of a list of GTIDs, one for each replication domain ever seen
|
||||
on the server.
|
||||
|
||||
@section Gtid_list_log_event_binary_format Binary Format
|
||||
|
||||
<table>
|
||||
<caption>Post-Header</caption>
|
||||
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Format</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>count</td>
|
||||
<td>4 byte unsigned integer</td>
|
||||
<td>The lower 28 bits are the number of GTIDs. The upper 4 bits are
|
||||
reserved for flags bits for future expansion</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<caption>Body</caption>
|
||||
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Format</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>domain_id</td>
|
||||
<td>4 byte unsigned integer</td>
|
||||
<td>Replication domain id of one GTID</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>server_id</td>
|
||||
<td>4 byte unsigned integer</td>
|
||||
<td>Server id of one GTID</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>seq_no</td>
|
||||
<td>8 byte unsigned integer</td>
|
||||
<td>sequence number of one GTID</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
The three elements in the body repeat COUNT times to form the GTID list.
|
||||
*/
|
||||
|
||||
class Gtid_list_log_event: public Log_event
|
||||
{
|
||||
public:
|
||||
uint32 count;
|
||||
struct rpl_gtid *list;
|
||||
|
||||
static const uint element_size= 4+4+8;
|
||||
|
||||
#ifdef MYSQL_SERVER
|
||||
Gtid_list_log_event(rpl_binlog_state *gtid_set);
|
||||
#ifdef HAVE_REPLICATION
|
||||
void pack_info(THD *thd, Protocol *protocol);
|
||||
#endif
|
||||
#else
|
||||
void print(FILE *file, PRINT_EVENT_INFO *print_event_info);
|
||||
#endif
|
||||
Gtid_list_log_event(const char *buf, uint event_len,
|
||||
const Format_description_log_event *description_event);
|
||||
~Gtid_list_log_event() { my_free(list); }
|
||||
Log_event_type get_type_code() { return GTID_LIST_EVENT; }
|
||||
int get_data_size() { return GTID_LIST_HEADER_LEN + count*element_size; }
|
||||
bool is_valid() const { return list != NULL; }
|
||||
#ifdef MYSQL_SERVER
|
||||
bool write(IO_CACHE *file);
|
||||
#endif
|
||||
static bool peek(const char *event_start, uint32 event_len,
|
||||
rpl_gtid **out_gtid_list, uint32 *out_list_len);
|
||||
};
|
||||
|
||||
|
||||
/* the classes below are for the new LOAD DATA INFILE logging */
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1847,7 +1847,7 @@ Old_rows_log_event::do_update_pos(Relay_log_info *rli)
|
|||
Step the group log position if we are not in a transaction,
|
||||
otherwise increase the event log position.
|
||||
*/
|
||||
rli->stmt_done(log_pos, when);
|
||||
rli->stmt_done(log_pos, when, thd);
|
||||
/*
|
||||
Clear any errors in thd->net.last_err*. It is not known if this is
|
||||
needed or not. It is believed that any errors that may exist in
|
||||
|
|
|
|||
128
sql/mdl.cc
128
sql/mdl.cc
|
|
@ -85,7 +85,8 @@ const char *MDL_key::m_namespace_to_wait_state_name[NAMESPACE_END]=
|
|||
"Waiting for stored procedure metadata lock",
|
||||
"Waiting for trigger metadata lock",
|
||||
"Waiting for event metadata lock",
|
||||
"Waiting for commit lock"
|
||||
"Waiting for commit lock",
|
||||
"User lock" /* Be compatible with old status. */
|
||||
};
|
||||
|
||||
static bool mdl_initialized= 0;
|
||||
|
|
@ -107,6 +108,7 @@ public:
|
|||
void init();
|
||||
void destroy();
|
||||
MDL_lock *find_or_insert(const MDL_key *key);
|
||||
unsigned long get_lock_owner(const MDL_key *key);
|
||||
void remove(MDL_lock *lock);
|
||||
private:
|
||||
bool move_from_hash_to_lock_mutex(MDL_lock *lock);
|
||||
|
|
@ -382,6 +384,7 @@ public:
|
|||
bool ignore_lock_priority) const;
|
||||
|
||||
inline static MDL_lock *create(const MDL_key *key);
|
||||
inline unsigned long get_lock_owner() const;
|
||||
|
||||
void reschedule_waiters();
|
||||
|
||||
|
|
@ -856,6 +859,43 @@ bool MDL_map::move_from_hash_to_lock_mutex(MDL_lock *lock)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return thread id of the owner of the lock, if it is owned.
|
||||
*/
|
||||
|
||||
unsigned long
|
||||
MDL_map::get_lock_owner(const MDL_key *mdl_key)
|
||||
{
|
||||
MDL_lock *lock;
|
||||
unsigned long res= 0;
|
||||
|
||||
if (mdl_key->mdl_namespace() == MDL_key::GLOBAL ||
|
||||
mdl_key->mdl_namespace() == MDL_key::COMMIT)
|
||||
{
|
||||
lock= (mdl_key->mdl_namespace() == MDL_key::GLOBAL) ? m_global_lock :
|
||||
m_commit_lock;
|
||||
mysql_prlock_rdlock(&lock->m_rwlock);
|
||||
res= lock->get_lock_owner();
|
||||
mysql_prlock_unlock(&lock->m_rwlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
my_hash_value_type hash_value= my_calc_hash(&m_locks,
|
||||
mdl_key->ptr(),
|
||||
mdl_key->length());
|
||||
mysql_mutex_lock(&m_mutex);
|
||||
lock= (MDL_lock*) my_hash_search_using_hash_value(&m_locks,
|
||||
hash_value,
|
||||
mdl_key->ptr(),
|
||||
mdl_key->length());
|
||||
if (lock)
|
||||
res= lock->get_lock_owner();
|
||||
mysql_mutex_unlock(&m_mutex);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Destroy MDL_lock object or delegate this responsibility to
|
||||
whatever thread that holds the last outstanding reference to
|
||||
|
|
@ -1621,6 +1661,23 @@ MDL_lock::can_grant_lock(enum_mdl_type type_arg,
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
Return thread id of the thread to which the first ticket was
|
||||
granted.
|
||||
*/
|
||||
|
||||
inline unsigned long
|
||||
MDL_lock::get_lock_owner() const
|
||||
{
|
||||
Ticket_iterator it(m_granted);
|
||||
MDL_ticket *ticket;
|
||||
|
||||
if ((ticket= it++))
|
||||
return thd_get_thread_id(ticket->get_ctx()->get_thd());
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/** Remove a ticket from waiting or pending queue and wakeup up waiters. */
|
||||
|
||||
void MDL_lock::remove_ticket(Ticket_list MDL_lock::*list, MDL_ticket *ticket)
|
||||
|
|
@ -2094,31 +2151,37 @@ MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout)
|
|||
|
||||
find_deadlock();
|
||||
|
||||
if (lock->needs_notification(ticket))
|
||||
struct timespec abs_shortwait;
|
||||
set_timespec(abs_shortwait, 1);
|
||||
wait_status= MDL_wait::EMPTY;
|
||||
|
||||
while (cmp_timespec(abs_shortwait, abs_timeout) <= 0)
|
||||
{
|
||||
struct timespec abs_shortwait;
|
||||
set_timespec(abs_shortwait, 1);
|
||||
wait_status= MDL_wait::EMPTY;
|
||||
/* abs_timeout is far away. Wait a short while and notify locks. */
|
||||
wait_status= m_wait.timed_wait(m_thd, &abs_shortwait, FALSE,
|
||||
mdl_request->key.get_wait_state_name());
|
||||
|
||||
while (cmp_timespec(abs_shortwait, abs_timeout) <= 0)
|
||||
if (wait_status != MDL_wait::EMPTY)
|
||||
break;
|
||||
/* Check if the client is gone while we were waiting. */
|
||||
if (! thd_is_connected(m_thd))
|
||||
{
|
||||
/* abs_timeout is far away. Wait a short while and notify locks. */
|
||||
wait_status= m_wait.timed_wait(m_thd, &abs_shortwait, FALSE,
|
||||
mdl_request->key.get_wait_state_name());
|
||||
|
||||
if (wait_status != MDL_wait::EMPTY)
|
||||
break;
|
||||
|
||||
mysql_prlock_wrlock(&lock->m_rwlock);
|
||||
lock->notify_conflicting_locks(this);
|
||||
mysql_prlock_unlock(&lock->m_rwlock);
|
||||
set_timespec(abs_shortwait, 1);
|
||||
/*
|
||||
* The client is disconnected. Don't wait forever:
|
||||
* assume it's the same as a wait timeout, this
|
||||
* ensures all error handling is correct.
|
||||
*/
|
||||
wait_status= MDL_wait::TIMEOUT;
|
||||
break;
|
||||
}
|
||||
if (wait_status == MDL_wait::EMPTY)
|
||||
wait_status= m_wait.timed_wait(m_thd, &abs_timeout, TRUE,
|
||||
mdl_request->key.get_wait_state_name());
|
||||
|
||||
mysql_prlock_wrlock(&lock->m_rwlock);
|
||||
if (lock->needs_notification(ticket))
|
||||
lock->notify_conflicting_locks(this);
|
||||
mysql_prlock_unlock(&lock->m_rwlock);
|
||||
set_timespec(abs_shortwait, 1);
|
||||
}
|
||||
else
|
||||
if (wait_status == MDL_wait::EMPTY)
|
||||
wait_status= m_wait.timed_wait(m_thd, &abs_timeout, TRUE,
|
||||
mdl_request->key.get_wait_state_name());
|
||||
|
||||
|
|
@ -2613,7 +2676,7 @@ void MDL_context::release_lock(MDL_ticket *ticket)
|
|||
the corresponding lists, i.e. stored in reverse temporal order.
|
||||
This allows to employ this function to:
|
||||
- back off in case of a lock conflict.
|
||||
- release all locks in the end of a statment or transaction
|
||||
- release all locks in the end of a statement or transaction
|
||||
- rollback to a savepoint.
|
||||
*/
|
||||
|
||||
|
|
@ -2724,6 +2787,22 @@ MDL_context::is_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace,
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
Return thread id of the owner of the lock or 0 if
|
||||
there is no owner.
|
||||
@note: Lock type is not considered at all, the function
|
||||
simply checks that there is some lock for the given key.
|
||||
|
||||
@return thread id of the owner of the lock or 0
|
||||
*/
|
||||
|
||||
unsigned long
|
||||
MDL_context::get_lock_owner(MDL_key *key)
|
||||
{
|
||||
return mdl_locks.get_lock_owner(key);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Check if we have any pending locks which conflict with existing shared lock.
|
||||
|
||||
|
|
@ -2737,6 +2816,11 @@ bool MDL_ticket::has_pending_conflicting_lock() const
|
|||
return m_lock->has_pending_conflicting_lock(m_type);
|
||||
}
|
||||
|
||||
/** Return a key identifying this lock. */
|
||||
MDL_key *MDL_ticket::get_key() const
|
||||
{
|
||||
return &m_lock->key;
|
||||
}
|
||||
|
||||
/**
|
||||
Releases metadata locks that were acquired after a specific savepoint.
|
||||
|
|
|
|||
43
sql/mdl.h
43
sql/mdl.h
|
|
@ -212,6 +212,7 @@ public:
|
|||
TRIGGER,
|
||||
EVENT,
|
||||
COMMIT,
|
||||
USER_LOCK, /* user level locks. */
|
||||
/* This should be the last ! */
|
||||
NAMESPACE_END };
|
||||
|
||||
|
|
@ -492,6 +493,7 @@ public:
|
|||
}
|
||||
enum_mdl_type get_type() const { return m_type; }
|
||||
MDL_lock *get_lock() const { return m_lock; }
|
||||
MDL_key *get_key() const;
|
||||
void downgrade_exclusive_lock(enum_mdl_type type);
|
||||
|
||||
bool has_stronger_or_equal_type(enum_mdl_type type) const;
|
||||
|
|
@ -653,6 +655,7 @@ public:
|
|||
bool is_lock_owner(MDL_key::enum_mdl_namespace mdl_namespace,
|
||||
const char *db, const char *name,
|
||||
enum_mdl_type mdl_type);
|
||||
unsigned long get_lock_owner(MDL_key *mdl_key);
|
||||
|
||||
bool has_lock(const MDL_savepoint &mdl_savepoint, MDL_ticket *mdl_ticket);
|
||||
|
||||
|
|
@ -721,9 +724,9 @@ private:
|
|||
Lists of MDL tickets:
|
||||
---------------------
|
||||
The entire set of locks acquired by a connection can be separated
|
||||
in three subsets according to their: locks released at the end of
|
||||
statement, at the end of transaction and locks are released
|
||||
explicitly.
|
||||
in three subsets according to their duration: locks released at
|
||||
the end of statement, at the end of transaction and locks are
|
||||
released explicitly.
|
||||
|
||||
Statement and transactional locks are locks with automatic scope.
|
||||
They are accumulated in the course of a transaction, and released
|
||||
|
|
@ -732,11 +735,12 @@ private:
|
|||
locks). They must not be (and never are) released manually,
|
||||
i.e. with release_lock() call.
|
||||
|
||||
Locks with explicit duration are taken for locks that span
|
||||
Tickets with explicit duration are taken for locks that span
|
||||
multiple transactions or savepoints.
|
||||
These are: HANDLER SQL locks (HANDLER SQL is
|
||||
transaction-agnostic), LOCK TABLES locks (you can COMMIT/etc
|
||||
under LOCK TABLES, and the locked tables stay locked), and
|
||||
under LOCK TABLES, and the locked tables stay locked), user level
|
||||
locks (GET_LOCK()/RELEASE_LOCK() functions) and
|
||||
locks implementing "global read lock".
|
||||
|
||||
Statement/transactional locks are always prepended to the
|
||||
|
|
@ -745,20 +749,19 @@ private:
|
|||
a savepoint, we start popping and releasing tickets from the
|
||||
front until we reach the last ticket acquired after the savepoint.
|
||||
|
||||
Locks with explicit duration stored are not stored in any
|
||||
Locks with explicit duration are not stored in any
|
||||
particular order, and among each other can be split into
|
||||
three sets:
|
||||
four sets:
|
||||
|
||||
[LOCK TABLES locks] [HANDLER locks] [GLOBAL READ LOCK locks]
|
||||
[LOCK TABLES locks] [USER locks] [HANDLER locks] [GLOBAL READ LOCK locks]
|
||||
|
||||
The following is known about these sets:
|
||||
|
||||
* GLOBAL READ LOCK locks are always stored after LOCK TABLES
|
||||
locks and after HANDLER locks. This is because one can't say
|
||||
SET GLOBAL read_only=1 or FLUSH TABLES WITH READ LOCK
|
||||
if one has locked tables. One can, however, LOCK TABLES
|
||||
after having entered the read only mode. Note, that
|
||||
subsequent LOCK TABLES statement will unlock the previous
|
||||
* GLOBAL READ LOCK locks are always stored last.
|
||||
This is because one can't say SET GLOBAL read_only=1 or
|
||||
FLUSH TABLES WITH READ LOCK if one has locked tables. One can,
|
||||
however, LOCK TABLES after having entered the read only mode.
|
||||
Note, that subsequent LOCK TABLES statement will unlock the previous
|
||||
set of tables, but not the GRL!
|
||||
There are no HANDLER locks after GRL locks because
|
||||
SET GLOBAL read_only performs a FLUSH TABLES WITH
|
||||
|
|
@ -853,6 +856,18 @@ extern bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use,
|
|||
extern "C" const char* thd_enter_cond(MYSQL_THD thd, mysql_cond_t *cond,
|
||||
mysql_mutex_t *mutex, const char *msg);
|
||||
extern "C" void thd_exit_cond(MYSQL_THD thd, const char *old_msg);
|
||||
extern "C" unsigned long thd_get_thread_id(const MYSQL_THD thd);
|
||||
|
||||
/**
|
||||
Check if a connection in question is no longer connected.
|
||||
|
||||
@details
|
||||
Replication apply thread is always connected. Otherwise,
|
||||
does a poll on the associated socket to check if the client
|
||||
is gone.
|
||||
*/
|
||||
extern "C" int thd_is_connected(MYSQL_THD thd);
|
||||
|
||||
|
||||
#ifndef DBUG_OFF
|
||||
extern mysql_mutex_t LOCK_open;
|
||||
|
|
|
|||
|
|
@ -1648,7 +1648,7 @@ int DsMrr_impl::dsmrr_explain_info(uint mrr_mode, char *str, size_t size)
|
|||
|
||||
uint used_str_len= strlen(used_str);
|
||||
uint copy_len= min(used_str_len, size);
|
||||
memcpy(str, used_str, size);
|
||||
memcpy(str, used_str, copy_len);
|
||||
return copy_len;
|
||||
}
|
||||
return 0;
|
||||
|
|
|
|||
120
sql/mysqld.cc
120
sql/mysqld.cc
|
|
@ -676,6 +676,8 @@ mysql_mutex_t
|
|||
mysql_mutex_t LOCK_stats, LOCK_global_user_client_stats,
|
||||
LOCK_global_table_stats, LOCK_global_index_stats;
|
||||
|
||||
mysql_mutex_t LOCK_gtid_counter, LOCK_rpl_gtid_state;
|
||||
|
||||
/**
|
||||
The below lock protects access to two global server variables:
|
||||
max_prepared_stmt_count and prepared_stmt_count. These variables
|
||||
|
|
@ -766,12 +768,15 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
|
|||
key_LOCK_thread_count, key_LOCK_thread_cache,
|
||||
key_PARTITION_LOCK_auto_inc;
|
||||
PSI_mutex_key key_RELAYLOG_LOCK_index;
|
||||
PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state;
|
||||
|
||||
PSI_mutex_key key_LOCK_stats,
|
||||
key_LOCK_global_user_client_stats, key_LOCK_global_table_stats,
|
||||
key_LOCK_global_index_stats,
|
||||
key_LOCK_wakeup_ready;
|
||||
|
||||
PSI_mutex_key key_LOCK_gtid_counter, key_LOCK_rpl_gtid_state;
|
||||
|
||||
PSI_mutex_key key_LOCK_prepare_ordered, key_LOCK_commit_ordered;
|
||||
|
||||
static PSI_mutex_info all_server_mutexes[]=
|
||||
|
|
@ -815,6 +820,8 @@ static PSI_mutex_info all_server_mutexes[]=
|
|||
{ &key_LOCK_global_table_stats, "LOCK_global_table_stats", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_global_index_stats, "LOCK_global_index_stats", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_wakeup_ready, "THD::LOCK_wakeup_ready", 0},
|
||||
{ &key_LOCK_gtid_counter, "LOCK_gtid_counter", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_rpl_gtid_state, "LOCK_rpl_gtid_state", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_thd_data, "THD::LOCK_thd_data", 0},
|
||||
{ &key_LOCK_user_conn, "LOCK_user_conn", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_uuid_short_generator, "LOCK_uuid_short_generator", PSI_FLAG_GLOBAL},
|
||||
|
|
@ -835,7 +842,9 @@ static PSI_mutex_info all_server_mutexes[]=
|
|||
{ &key_LOG_INFO_lock, "LOG_INFO::lock", 0},
|
||||
{ &key_LOCK_thread_count, "LOCK_thread_count", PSI_FLAG_GLOBAL},
|
||||
{ &key_LOCK_thread_cache, "LOCK_thread_cache", PSI_FLAG_GLOBAL},
|
||||
{ &key_PARTITION_LOCK_auto_inc, "HA_DATA_PARTITION::LOCK_auto_inc", 0}
|
||||
{ &key_PARTITION_LOCK_auto_inc, "HA_DATA_PARTITION::LOCK_auto_inc", 0},
|
||||
{ &key_LOCK_slave_state, "LOCK_slave_state", 0},
|
||||
{ &key_LOCK_binlog_state, "LOCK_binlog_state", 0}
|
||||
};
|
||||
|
||||
PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger,
|
||||
|
|
@ -959,6 +968,7 @@ PSI_file_key key_file_binlog, key_file_binlog_index, key_file_casetest,
|
|||
key_file_trg, key_file_trn, key_file_init;
|
||||
PSI_file_key key_file_query_log, key_file_slow_log;
|
||||
PSI_file_key key_file_relaylog, key_file_relaylog_index;
|
||||
PSI_file_key key_file_binlog_state;
|
||||
|
||||
static PSI_file_info all_server_files[]=
|
||||
{
|
||||
|
|
@ -989,7 +999,8 @@ static PSI_file_info all_server_files[]=
|
|||
{ &key_file_tclog, "tclog", 0},
|
||||
{ &key_file_trg, "trigger_name", 0},
|
||||
{ &key_file_trn, "trigger", 0},
|
||||
{ &key_file_init, "init", 0}
|
||||
{ &key_file_init, "init", 0},
|
||||
{ &key_file_binlog_state, "binlog_state", 0}
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -1282,6 +1293,12 @@ struct st_VioSSLFd *ssl_acceptor_fd;
|
|||
*/
|
||||
uint connection_count= 0, extra_connection_count= 0;
|
||||
|
||||
/**
|
||||
Running counter for generating new GTIDs locally.
|
||||
*/
|
||||
uint64 global_gtid_counter= 0;
|
||||
|
||||
|
||||
/* Function declarations */
|
||||
|
||||
pthread_handler_t signal_hand(void *arg);
|
||||
|
|
@ -1776,6 +1793,7 @@ static void mysqld_exit(int exit_code)
|
|||
but if a kill -15 signal was sent, the signal thread did
|
||||
spawn the kill_server_thread thread, which is running concurrently.
|
||||
*/
|
||||
rpl_deinit_gtid_slave_state();
|
||||
wait_for_signal_thread_to_end();
|
||||
mysql_audit_finalize();
|
||||
clean_up_mutexes();
|
||||
|
|
@ -1824,7 +1842,7 @@ void clean_up(bool print_message)
|
|||
#endif
|
||||
query_cache_destroy();
|
||||
hostname_cache_free();
|
||||
item_user_lock_free();
|
||||
item_func_sleep_free();
|
||||
lex_free(); /* Free some memory */
|
||||
item_create_cleanup();
|
||||
if (!opt_noacl)
|
||||
|
|
@ -1945,6 +1963,8 @@ static void clean_up_mutexes()
|
|||
mysql_mutex_destroy(&LOCK_global_user_client_stats);
|
||||
mysql_mutex_destroy(&LOCK_global_table_stats);
|
||||
mysql_mutex_destroy(&LOCK_global_index_stats);
|
||||
mysql_mutex_destroy(&LOCK_gtid_counter);
|
||||
mysql_mutex_destroy(&LOCK_rpl_gtid_state);
|
||||
#ifdef HAVE_OPENSSL
|
||||
mysql_mutex_destroy(&LOCK_des_key_file);
|
||||
#ifndef HAVE_YASSL
|
||||
|
|
@ -2184,8 +2204,28 @@ static my_socket activate_tcp_port(uint port)
|
|||
for (a= ai; a != NULL; a= a->ai_next)
|
||||
{
|
||||
ip_sock= socket(a->ai_family, a->ai_socktype, a->ai_protocol);
|
||||
if (ip_sock != INVALID_SOCKET)
|
||||
|
||||
char ip_addr[INET6_ADDRSTRLEN];
|
||||
|
||||
if (vio_get_normalized_ip_string(a->ai_addr, a->ai_addrlen,
|
||||
ip_addr, sizeof (ip_addr)))
|
||||
{
|
||||
ip_addr[0]= 0;
|
||||
}
|
||||
|
||||
if (ip_sock == INVALID_SOCKET)
|
||||
{
|
||||
sql_print_error("Failed to create a socket for %s '%s': errno: %d.",
|
||||
(a->ai_family == AF_INET) ? "IPv4" : "IPv6",
|
||||
(const char *) ip_addr,
|
||||
(int) socket_errno);
|
||||
}
|
||||
else
|
||||
{
|
||||
sql_print_information("Server socket created on IP: '%s'.",
|
||||
(const char *) ip_addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ip_sock == INVALID_SOCKET)
|
||||
|
|
@ -4017,6 +4057,7 @@ static int init_thread_environment()
|
|||
mysql_mutex_init(key_LOCK_active_mi, &LOCK_active_mi, MY_MUTEX_INIT_FAST);
|
||||
mysql_mutex_init(key_LOCK_global_system_variables,
|
||||
&LOCK_global_system_variables, MY_MUTEX_INIT_FAST);
|
||||
mysql_mutex_record_order(&LOCK_active_mi, &LOCK_global_system_variables);
|
||||
mysql_rwlock_init(key_rwlock_LOCK_system_variables_hash,
|
||||
&LOCK_system_variables_hash);
|
||||
mysql_mutex_init(key_LOCK_prepared_stmt_count,
|
||||
|
|
@ -4034,6 +4075,10 @@ static int init_thread_environment()
|
|||
&LOCK_global_table_stats, MY_MUTEX_INIT_FAST);
|
||||
mysql_mutex_init(key_LOCK_global_index_stats,
|
||||
&LOCK_global_index_stats, MY_MUTEX_INIT_FAST);
|
||||
mysql_mutex_init(key_LOCK_gtid_counter,
|
||||
&LOCK_gtid_counter, MY_MUTEX_INIT_FAST);
|
||||
mysql_mutex_init(key_LOCK_rpl_gtid_state,
|
||||
&LOCK_rpl_gtid_state, MY_MUTEX_INIT_SLOW);
|
||||
mysql_mutex_init(key_LOCK_prepare_ordered, &LOCK_prepare_ordered,
|
||||
MY_MUTEX_INIT_SLOW);
|
||||
mysql_mutex_init(key_LOCK_commit_ordered, &LOCK_commit_ordered,
|
||||
|
|
@ -4078,6 +4123,10 @@ static int init_thread_environment()
|
|||
PTHREAD_CREATE_DETACHED);
|
||||
pthread_attr_setscope(&connection_attrib, PTHREAD_SCOPE_SYSTEM);
|
||||
|
||||
#ifdef HAVE_REPLICATION
|
||||
rpl_init_gtid_slave_state();
|
||||
#endif
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
|
@ -4951,9 +5000,9 @@ int mysqld_main(int argc, char **argv)
|
|||
set_user(mysqld_user, user_info);
|
||||
}
|
||||
|
||||
if (opt_bin_log && !server_id)
|
||||
if (opt_bin_log && !global_system_variables.server_id)
|
||||
{
|
||||
server_id= 1;
|
||||
global_system_variables.server_id= ::server_id= 1;
|
||||
#ifdef EXTRA_DEBUG
|
||||
sql_print_warning("You have enabled the binary log, but you haven't set "
|
||||
"server-id to a non-zero value: we force server id to 1; "
|
||||
|
|
@ -6681,19 +6730,25 @@ static int show_rpl_status(THD *thd, SHOW_VAR *var, char *buff)
|
|||
static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff)
|
||||
{
|
||||
Master_info *mi;
|
||||
bool tmp;
|
||||
LINT_INIT(tmp);
|
||||
|
||||
var->type= SHOW_MY_BOOL;
|
||||
var->value= buff;
|
||||
mysql_mutex_unlock(&LOCK_status);
|
||||
mysql_mutex_lock(&LOCK_active_mi);
|
||||
mi= master_info_index->
|
||||
get_master_info(&thd->variables.default_master_connection,
|
||||
MYSQL_ERROR::WARN_LEVEL_NOTE);
|
||||
if (mi)
|
||||
*((my_bool *)buff)= (my_bool) (mi->slave_running ==
|
||||
MYSQL_SLAVE_RUN_CONNECT &&
|
||||
mi->rli.slave_running);
|
||||
tmp= (my_bool) (mi->slave_running == MYSQL_SLAVE_RUN_CONNECT &&
|
||||
mi->rli.slave_running);
|
||||
mysql_mutex_unlock(&LOCK_active_mi);
|
||||
mysql_mutex_lock(&LOCK_status);
|
||||
if (mi)
|
||||
*((my_bool *)buff)= tmp;
|
||||
else
|
||||
var->type= SHOW_UNDEF;
|
||||
mysql_mutex_unlock(&LOCK_active_mi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -6701,17 +6756,24 @@ static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff)
|
|||
static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff)
|
||||
{
|
||||
Master_info *mi;
|
||||
longlong tmp;
|
||||
LINT_INIT(tmp);
|
||||
|
||||
var->type= SHOW_LONGLONG;
|
||||
var->value= buff;
|
||||
mysql_mutex_unlock(&LOCK_status);
|
||||
mysql_mutex_lock(&LOCK_active_mi);
|
||||
mi= master_info_index->
|
||||
get_master_info(&thd->variables.default_master_connection,
|
||||
MYSQL_ERROR::WARN_LEVEL_NOTE);
|
||||
if (mi)
|
||||
*((longlong *)buff)= mi->received_heartbeats;
|
||||
tmp= mi->received_heartbeats;
|
||||
mysql_mutex_unlock(&LOCK_active_mi);
|
||||
mysql_mutex_lock(&LOCK_status);
|
||||
if (mi)
|
||||
*((longlong *)buff)= tmp;
|
||||
else
|
||||
var->type= SHOW_UNDEF;
|
||||
mysql_mutex_unlock(&LOCK_active_mi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -6719,17 +6781,24 @@ static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff)
|
|||
static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff)
|
||||
{
|
||||
Master_info *mi;
|
||||
float tmp;
|
||||
LINT_INIT(tmp);
|
||||
|
||||
var->type= SHOW_CHAR;
|
||||
var->value= buff;
|
||||
mysql_mutex_unlock(&LOCK_status);
|
||||
mysql_mutex_lock(&LOCK_active_mi);
|
||||
mi= master_info_index->
|
||||
get_master_info(&thd->variables.default_master_connection,
|
||||
MYSQL_ERROR::WARN_LEVEL_NOTE);
|
||||
if (mi)
|
||||
sprintf(buff, "%.3f", mi->heartbeat_period);
|
||||
tmp= mi->heartbeat_period;
|
||||
mysql_mutex_unlock(&LOCK_active_mi);
|
||||
mysql_mutex_lock(&LOCK_status);
|
||||
if (mi)
|
||||
sprintf(buff, "%.3f", tmp);
|
||||
else
|
||||
var->type= SHOW_UNDEF;
|
||||
mysql_mutex_unlock(&LOCK_active_mi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -7801,28 +7870,6 @@ mysqld_get_one_option(int optid,
|
|||
case (int) OPT_WANT_CORE:
|
||||
test_flags |= TEST_CORE_ON_SIGNAL;
|
||||
break;
|
||||
case (int) OPT_BIND_ADDRESS:
|
||||
{
|
||||
struct addrinfo *res_lst, hints;
|
||||
|
||||
bzero(&hints, sizeof(struct addrinfo));
|
||||
hints.ai_socktype= SOCK_STREAM;
|
||||
hints.ai_protocol= IPPROTO_TCP;
|
||||
|
||||
if (getaddrinfo(argument, NULL, &hints, &res_lst) != 0)
|
||||
{
|
||||
sql_print_error("Can't start server: cannot resolve hostname!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (res_lst->ai_next)
|
||||
{
|
||||
sql_print_error("Can't start server: bind-address refers to multiple interfaces!");
|
||||
return 1;
|
||||
}
|
||||
freeaddrinfo(res_lst);
|
||||
}
|
||||
break;
|
||||
case OPT_CONSOLE:
|
||||
if (opt_console)
|
||||
opt_error_log= 0; // Force logs to stdout
|
||||
|
|
@ -7832,6 +7879,7 @@ mysqld_get_one_option(int optid,
|
|||
break;
|
||||
case OPT_SERVER_ID:
|
||||
server_id_supplied = 1;
|
||||
::server_id= global_system_variables.server_id;
|
||||
break;
|
||||
case OPT_ONE_THREAD:
|
||||
thread_handling= SCHEDULER_NO_THREADS;
|
||||
|
|
|
|||
|
|
@ -247,11 +247,14 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
|
|||
key_structure_guard_mutex, key_TABLE_SHARE_LOCK_ha_data,
|
||||
key_LOCK_error_messages, key_LOCK_thread_count, key_PARTITION_LOCK_auto_inc;
|
||||
extern PSI_mutex_key key_RELAYLOG_LOCK_index;
|
||||
extern PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state;
|
||||
|
||||
extern PSI_mutex_key key_LOCK_stats,
|
||||
key_LOCK_global_user_client_stats, key_LOCK_global_table_stats,
|
||||
key_LOCK_global_index_stats, key_LOCK_wakeup_ready;
|
||||
|
||||
extern PSI_mutex_key key_LOCK_gtid_counter, key_LOCK_rpl_gtid_state;
|
||||
|
||||
extern PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger,
|
||||
key_rwlock_LOCK_sys_init_connect, key_rwlock_LOCK_sys_init_slave,
|
||||
key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock;
|
||||
|
|
@ -291,6 +294,7 @@ extern PSI_file_key key_file_binlog, key_file_binlog_index, key_file_casetest,
|
|||
key_file_trg, key_file_trn, key_file_init;
|
||||
extern PSI_file_key key_file_query_log, key_file_slow_log;
|
||||
extern PSI_file_key key_file_relaylog, key_file_relaylog_index;
|
||||
extern PSI_file_key key_file_binlog_state;
|
||||
|
||||
void init_server_psi_keys();
|
||||
#endif /* HAVE_PSI_INTERFACE */
|
||||
|
|
@ -335,12 +339,13 @@ extern MYSQL_PLUGIN_IMPORT key_map key_map_full; /* Should be threaded
|
|||
Server mutex locks and condition variables.
|
||||
*/
|
||||
extern mysql_mutex_t
|
||||
LOCK_user_locks, LOCK_status,
|
||||
LOCK_item_func_sleep, LOCK_status,
|
||||
LOCK_error_log, LOCK_delayed_insert, LOCK_short_uuid_generator,
|
||||
LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone,
|
||||
LOCK_slave_list, LOCK_active_mi, LOCK_manager,
|
||||
LOCK_global_system_variables, LOCK_user_conn,
|
||||
LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count;
|
||||
extern mysql_mutex_t LOCK_gtid_counter, LOCK_rpl_gtid_state;
|
||||
extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_thread_count;
|
||||
#ifdef HAVE_OPENSSL
|
||||
extern mysql_mutex_t LOCK_des_key_file;
|
||||
|
|
@ -535,6 +540,7 @@ inline int set_current_thd(THD *thd)
|
|||
extern handlerton *maria_hton;
|
||||
|
||||
extern uint extra_connection_count;
|
||||
extern uint64 global_gtid_counter;
|
||||
extern my_bool opt_userstat_running, debug_assert_if_crashed_table;
|
||||
extern uint mysqld_extra_port;
|
||||
extern ulong opt_progress_report_time;
|
||||
|
|
|
|||
|
|
@ -133,7 +133,12 @@
|
|||
|
||||
static int sel_cmp(Field *f,uchar *a,uchar *b,uint8 a_flag,uint8 b_flag);
|
||||
|
||||
static uchar is_null_string[2]= {1,0};
|
||||
/*
|
||||
this should be long enough so that any memcmp with a string that
|
||||
starts from '\0' won't cross is_null_string boundaries, even
|
||||
if the memcmp is optimized to compare 4- 8- or 16- bytes at once
|
||||
*/
|
||||
static uchar is_null_string[20]= {1,0};
|
||||
|
||||
class RANGE_OPT_PARAM;
|
||||
/*
|
||||
|
|
@ -2001,7 +2006,7 @@ int QUICK_ROR_INTERSECT_SELECT::init()
|
|||
1 error
|
||||
*/
|
||||
|
||||
int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
|
||||
int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler, MEM_ROOT *alloc)
|
||||
{
|
||||
handler *save_file= file, *org_file;
|
||||
my_bool org_key_read;
|
||||
|
|
@ -2029,7 +2034,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler)
|
|||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
if (!(file= head->file->clone(head->s->normalized_path.str, thd->mem_root)))
|
||||
if (!(file= head->file->clone(head->s->normalized_path.str, alloc)))
|
||||
{
|
||||
/*
|
||||
Manually set the error flag. Note: there seems to be quite a few
|
||||
|
|
@ -2130,7 +2135,8 @@ failure:
|
|||
0 OK
|
||||
other error code
|
||||
*/
|
||||
int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler)
|
||||
int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler,
|
||||
MEM_ROOT *alloc)
|
||||
{
|
||||
List_iterator_fast<QUICK_SELECT_WITH_RECORD> quick_it(quick_selects);
|
||||
QUICK_SELECT_WITH_RECORD *cur;
|
||||
|
|
@ -2147,7 +2153,7 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler)
|
|||
There is no use of this->file. Use it for the first of merged range
|
||||
selects.
|
||||
*/
|
||||
int error= quick->init_ror_merged_scan(TRUE);
|
||||
int error= quick->init_ror_merged_scan(TRUE, alloc);
|
||||
if (error)
|
||||
DBUG_RETURN(error);
|
||||
quick->file->extra(HA_EXTRA_KEYREAD_PRESERVE_FIELDS);
|
||||
|
|
@ -2159,7 +2165,7 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler)
|
|||
const MY_BITMAP * const save_read_set= quick->head->read_set;
|
||||
const MY_BITMAP * const save_write_set= quick->head->write_set;
|
||||
#endif
|
||||
if (quick->init_ror_merged_scan(FALSE))
|
||||
if (quick->init_ror_merged_scan(FALSE, alloc))
|
||||
DBUG_RETURN(1);
|
||||
quick->file->extra(HA_EXTRA_KEYREAD_PRESERVE_FIELDS);
|
||||
|
||||
|
|
@ -2193,7 +2199,7 @@ int QUICK_ROR_INTERSECT_SELECT::init_ror_merged_scan(bool reuse_handler)
|
|||
int QUICK_ROR_INTERSECT_SELECT::reset()
|
||||
{
|
||||
DBUG_ENTER("QUICK_ROR_INTERSECT_SELECT::reset");
|
||||
if (!scans_inited && init_ror_merged_scan(TRUE))
|
||||
if (!scans_inited && init_ror_merged_scan(TRUE, &alloc))
|
||||
DBUG_RETURN(1);
|
||||
scans_inited= TRUE;
|
||||
List_iterator_fast<QUICK_SELECT_WITH_RECORD> it(quick_selects);
|
||||
|
|
@ -2330,7 +2336,7 @@ int QUICK_ROR_UNION_SELECT::reset()
|
|||
List_iterator_fast<QUICK_SELECT_I> it(quick_selects);
|
||||
while ((quick= it++))
|
||||
{
|
||||
if (quick->init_ror_merged_scan(FALSE))
|
||||
if (quick->init_ror_merged_scan(FALSE, &alloc))
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
scans_inited= TRUE;
|
||||
|
|
@ -7622,8 +7628,10 @@ static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param,COND *cond)
|
|||
DBUG_RETURN(tree);
|
||||
}
|
||||
/* Here when simple cond */
|
||||
if (cond->const_item() && !cond->is_expensive())
|
||||
if (cond->const_item())
|
||||
{
|
||||
if (cond->is_expensive())
|
||||
DBUG_RETURN(0);
|
||||
/*
|
||||
During the cond->val_int() evaluation we can come across a subselect
|
||||
item which may allocate memory on the thd->mem_root and assumes
|
||||
|
|
|
|||
|
|
@ -323,7 +323,7 @@ public:
|
|||
0 Ok
|
||||
other Error
|
||||
*/
|
||||
virtual int init_ror_merged_scan(bool reuse_handler)
|
||||
virtual int init_ror_merged_scan(bool reuse_handler, MEM_ROOT *alloc)
|
||||
{ DBUG_ASSERT(0); return 1; }
|
||||
|
||||
/*
|
||||
|
|
@ -473,7 +473,7 @@ public:
|
|||
uchar *cur_prefix);
|
||||
bool reverse_sorted() { return 0; }
|
||||
bool unique_key_range();
|
||||
int init_ror_merged_scan(bool reuse_handler);
|
||||
int init_ror_merged_scan(bool reuse_handler, MEM_ROOT *alloc);
|
||||
void save_last_pos()
|
||||
{ file->position(record); }
|
||||
int get_type() { return QS_TYPE_RANGE; }
|
||||
|
|
@ -722,7 +722,7 @@ public:
|
|||
#ifndef DBUG_OFF
|
||||
void dbug_dump(int indent, bool verbose);
|
||||
#endif
|
||||
int init_ror_merged_scan(bool reuse_handler);
|
||||
int init_ror_merged_scan(bool reuse_handler, MEM_ROOT *alloc);
|
||||
bool push_quick_back(MEM_ROOT *alloc, QUICK_RANGE_SELECT *quick_sel_range);
|
||||
|
||||
class QUICK_SELECT_WITH_RECORD : public Sql_alloc
|
||||
|
|
|
|||
|
|
@ -1513,6 +1513,9 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred)
|
|||
*/
|
||||
parent_lex->leaf_tables.concat(&subq_lex->leaf_tables);
|
||||
|
||||
if (subq_lex->options & OPTION_SCHEMA_TABLE)
|
||||
parent_lex->options |= OPTION_SCHEMA_TABLE;
|
||||
|
||||
/*
|
||||
Same as above for next_local chain
|
||||
(a theory: a next_local chain always starts with ::leaf_tables
|
||||
|
|
@ -1730,6 +1733,9 @@ static bool convert_subq_to_jtbm(JOIN *parent_join,
|
|||
*/
|
||||
parent_lex->leaf_tables.push_back(jtbm);
|
||||
|
||||
if (subq_pred->unit->first_select()->options & OPTION_SCHEMA_TABLE)
|
||||
parent_lex->options |= OPTION_SCHEMA_TABLE;
|
||||
|
||||
/*
|
||||
Same as above for TABLE_LIST::next_local chain
|
||||
(a theory: a next_local chain always starts with ::leaf_tables
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ static ulonglong get_exact_record_count(List<TABLE_LIST> &tables)
|
|||
while ((tl= ti++))
|
||||
{
|
||||
ha_rows tmp= tl->table->file->records();
|
||||
if ((tmp == HA_POS_ERROR))
|
||||
if (tmp == HA_POS_ERROR)
|
||||
return ULONGLONG_MAX;
|
||||
count*= tmp;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -306,6 +306,7 @@ private:
|
|||
char *create_default_partition_names(uint part_no, uint num_parts,
|
||||
uint start_no);
|
||||
char *create_subpartition_name(uint subpart_no, const char *part_name);
|
||||
public:
|
||||
bool has_unique_name(partition_element *element);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -89,14 +89,15 @@ void change_rpl_status(ulong from_status, ulong to_status)
|
|||
|
||||
void unregister_slave(THD* thd, bool only_mine, bool need_mutex)
|
||||
{
|
||||
if (thd->server_id)
|
||||
uint32 thd_server_id= thd->variables.server_id;
|
||||
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)) &&
|
||||
(uchar*)&thd_server_id, 4)) &&
|
||||
(!only_mine || old_si->thd == thd))
|
||||
my_hash_delete(&slave_list, (uchar*)old_si);
|
||||
|
||||
|
|
@ -127,7 +128,7 @@ int register_slave(THD* thd, uchar* packet, uint packet_length)
|
|||
if (!(si = (SLAVE_INFO*)my_malloc(sizeof(SLAVE_INFO), MYF(MY_WME))))
|
||||
goto err2;
|
||||
|
||||
thd->server_id= si->server_id= uint4korr(p);
|
||||
thd->variables.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'");
|
||||
|
|
@ -145,7 +146,7 @@ int register_slave(THD* thd, uchar* packet, uint packet_length)
|
|||
// si->rpl_recovery_rank= uint4korr(p);
|
||||
p += 4;
|
||||
if (!(si->master_id= uint4korr(p)))
|
||||
si->master_id= server_id;
|
||||
si->master_id= global_system_variables.server_id;
|
||||
si->thd= thd;
|
||||
|
||||
mysql_mutex_lock(&LOCK_slave_list);
|
||||
|
|
|
|||
1122
sql/rpl_gtid.cc
Normal file
1122
sql/rpl_gtid.cc
Normal file
File diff suppressed because it is too large
Load diff
179
sql/rpl_gtid.h
Normal file
179
sql/rpl_gtid.h
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/* Copyright (c) 2013, Kristian Nielsen and MariaDB Services Ab.
|
||||
|
||||
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#ifndef RPL_GTID_H
|
||||
#define RPL_GTID_H
|
||||
|
||||
/* Definitions for MariaDB global transaction ID (GTID). */
|
||||
|
||||
|
||||
extern const LEX_STRING rpl_gtid_slave_state_table_name;
|
||||
|
||||
class String;
|
||||
|
||||
struct rpl_gtid
|
||||
{
|
||||
uint32 domain_id;
|
||||
uint32 server_id;
|
||||
uint64 seq_no;
|
||||
};
|
||||
|
||||
|
||||
enum enum_gtid_skip_type {
|
||||
GTID_SKIP_NOT, GTID_SKIP_STANDALONE, GTID_SKIP_TRANSACTION
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Replication slave state.
|
||||
|
||||
For every independent replication stream (identified by domain_id), this
|
||||
remembers the last gtid applied on the slave within this domain.
|
||||
|
||||
Since events are always committed in-order within a single domain, this is
|
||||
sufficient to maintain the state of the replication slave.
|
||||
*/
|
||||
struct rpl_slave_state
|
||||
{
|
||||
/* Elements in the list of GTIDs kept for each domain_id. */
|
||||
struct list_element
|
||||
{
|
||||
struct list_element *next;
|
||||
uint64 sub_id;
|
||||
uint64 seq_no;
|
||||
uint32 server_id;
|
||||
};
|
||||
|
||||
/* Elements in the HASH that hold the state for one domain_id. */
|
||||
struct element
|
||||
{
|
||||
struct list_element *list;
|
||||
uint64 last_sub_id;
|
||||
uint32 domain_id;
|
||||
|
||||
list_element *grab_list() { list_element *l= list; list= NULL; return l; }
|
||||
void add(list_element *l)
|
||||
{
|
||||
l->next= list;
|
||||
list= l;
|
||||
if (last_sub_id < l->sub_id)
|
||||
last_sub_id= l->sub_id;
|
||||
}
|
||||
};
|
||||
|
||||
/* Mapping from domain_id to its element. */
|
||||
HASH hash;
|
||||
/* Mutex protecting access to the state. */
|
||||
mysql_mutex_t LOCK_slave_state;
|
||||
|
||||
bool inited;
|
||||
bool loaded;
|
||||
|
||||
rpl_slave_state();
|
||||
~rpl_slave_state();
|
||||
|
||||
void init();
|
||||
void deinit();
|
||||
void truncate_hash();
|
||||
ulong count() const { return hash.records; }
|
||||
int update(uint32 domain_id, uint32 server_id, uint64 sub_id, uint64 seq_no);
|
||||
int truncate_state_table(THD *thd);
|
||||
int record_gtid(THD *thd, const rpl_gtid *gtid, uint64 sub_id,
|
||||
bool in_transaction);
|
||||
uint64 next_subid(uint32 domain_id);
|
||||
int tostring(String *dest, rpl_gtid *extra_gtids, uint32 num_extra);
|
||||
bool domain_to_gtid(uint32 domain_id, rpl_gtid *out_gtid);
|
||||
int load(THD *thd, char *state_from_master, size_t len, bool reset);
|
||||
bool is_empty();
|
||||
|
||||
void lock() { DBUG_ASSERT(inited); mysql_mutex_lock(&LOCK_slave_state); }
|
||||
void unlock() { DBUG_ASSERT(inited); mysql_mutex_unlock(&LOCK_slave_state); }
|
||||
|
||||
element *get_element(uint32 domain_id);
|
||||
|
||||
void update_state_hash(uint64 sub_id, rpl_gtid *gtid);
|
||||
int record_and_update_gtid(THD *thd, Relay_log_info *rli);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Binlog state.
|
||||
This keeps the last GTID written to the binlog for every distinct
|
||||
(domain_id, server_id) pair.
|
||||
This will be logged at the start of the next binlog file as a
|
||||
Gtid_list_log_event; this way, it is easy to find the binlog file
|
||||
containing a gigen GTID, by simply scanning backwards from the newest
|
||||
one until a lower seq_no is found in the Gtid_list_log_event at the
|
||||
start of a binlog for the given domain_id and server_id.
|
||||
|
||||
We also remember the last logged GTID for every domain_id. This is used
|
||||
to know where to start when a master is changed to a slave. As a side
|
||||
effect, it also allows to skip a hash lookup in the very common case of
|
||||
logging a new GTID with same server id as last GTID.
|
||||
*/
|
||||
struct rpl_binlog_state
|
||||
{
|
||||
struct element {
|
||||
uint32 domain_id;
|
||||
HASH hash; /* Containing all server_id for one domain_id */
|
||||
/* The most recent entry in the hash. */
|
||||
rpl_gtid *last_gtid;
|
||||
};
|
||||
/* Mapping from domain_id to collection of elements. */
|
||||
HASH hash;
|
||||
/* Mutex protecting access to the state. */
|
||||
mysql_mutex_t LOCK_binlog_state;
|
||||
|
||||
rpl_binlog_state();
|
||||
~rpl_binlog_state();
|
||||
|
||||
void reset();
|
||||
int update(const struct rpl_gtid *gtid);
|
||||
uint64 seq_no_from_state();
|
||||
int write_to_iocache(IO_CACHE *dest);
|
||||
int read_from_iocache(IO_CACHE *src);
|
||||
uint32 count();
|
||||
int get_gtid_list(rpl_gtid *gtid_list, uint32 list_size);
|
||||
int get_most_recent_gtid_list(rpl_gtid **list, uint32 *size);
|
||||
rpl_gtid *find(uint32 domain_id, uint32 server_id);
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Represent the GTID state that a slave connection to a master requests
|
||||
the master to start sending binlog events from.
|
||||
*/
|
||||
struct slave_connection_state
|
||||
{
|
||||
/* Mapping from domain_id to the GTID requested for that domain. */
|
||||
HASH hash;
|
||||
|
||||
slave_connection_state();
|
||||
~slave_connection_state();
|
||||
|
||||
int load(char *slave_request, size_t len);
|
||||
int load(const rpl_gtid *gtid_list, uint32 count);
|
||||
rpl_gtid *find(uint32 domain_id);
|
||||
int update(const rpl_gtid *in_gtid);
|
||||
void remove(const rpl_gtid *gtid);
|
||||
ulong count() const { return hash.records; }
|
||||
int to_string(String *out_str);
|
||||
};
|
||||
|
||||
extern bool rpl_slave_state_tostring_helper(String *dest, const rpl_gtid *gtid,
|
||||
bool *first);
|
||||
extern int gtid_check_rpl_slave_state_table(TABLE *table);
|
||||
|
||||
#endif /* RPL_GTID_H */
|
||||
|
|
@ -176,7 +176,7 @@ void delegates_destroy()
|
|||
plugins add to thd->lex will be automatically unlocked.
|
||||
*/
|
||||
#define FOREACH_OBSERVER(r, f, thd, args) \
|
||||
param.server_id= thd->server_id; \
|
||||
param.server_id= thd->variables.server_id; \
|
||||
/*
|
||||
Use a struct to make sure that they are allocated adjacent, check
|
||||
delete_dynamic().
|
||||
|
|
@ -348,7 +348,7 @@ int Binlog_transmit_delegate::reserve_header(THD *thd, ushort flags,
|
|||
ulong hlen;
|
||||
Binlog_transmit_param param;
|
||||
param.flags= flags;
|
||||
param.server_id= thd->server_id;
|
||||
param.server_id= thd->variables.server_id;
|
||||
|
||||
int ret= 0;
|
||||
read_lock();
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ int injector::transaction::use_table(server_id_type sid, table tbl)
|
|||
if ((error= check_state(TABLE_STATE)))
|
||||
DBUG_RETURN(error);
|
||||
|
||||
server_id_type save_id= m_thd->server_id;
|
||||
server_id_type save_id= m_thd->variables.server_id;
|
||||
m_thd->set_server_id(sid);
|
||||
error= m_thd->binlog_write_table_map(tbl.get_table(),
|
||||
tbl.is_transactional());
|
||||
|
|
@ -127,7 +127,7 @@ int injector::transaction::write_row (server_id_type sid, table tbl,
|
|||
if (error)
|
||||
DBUG_RETURN(error);
|
||||
|
||||
server_id_type save_id= m_thd->server_id;
|
||||
server_id_type save_id= m_thd->variables.server_id;
|
||||
m_thd->set_server_id(sid);
|
||||
error= m_thd->binlog_write_row(tbl.get_table(), tbl.is_transactional(),
|
||||
cols, colcnt, record);
|
||||
|
|
@ -146,7 +146,7 @@ int injector::transaction::delete_row(server_id_type sid, table tbl,
|
|||
if (error)
|
||||
DBUG_RETURN(error);
|
||||
|
||||
server_id_type save_id= m_thd->server_id;
|
||||
server_id_type save_id= m_thd->variables.server_id;
|
||||
m_thd->set_server_id(sid);
|
||||
error= m_thd->binlog_delete_row(tbl.get_table(), tbl.is_transactional(),
|
||||
cols, colcnt, record);
|
||||
|
|
@ -165,7 +165,7 @@ int injector::transaction::update_row(server_id_type sid, table tbl,
|
|||
if (error)
|
||||
DBUG_RETURN(error);
|
||||
|
||||
server_id_type save_id= m_thd->server_id;
|
||||
server_id_type save_id= m_thd->variables.server_id;
|
||||
m_thd->set_server_id(sid);
|
||||
error= m_thd->binlog_update_row(tbl.get_table(), tbl.is_transactional(),
|
||||
cols, colcnt, before, after);
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ Master_info::Master_info(LEX_STRING *connection_name_arg,
|
|||
checksum_alg_before_fd(BINLOG_CHECKSUM_ALG_UNDEF),
|
||||
connect_retry(DEFAULT_CONNECT_RETRY), inited(0), abort_slave(0),
|
||||
slave_running(0), slave_run_id(0), sync_counter(0),
|
||||
heartbeat_period(0), received_heartbeats(0), master_id(0)
|
||||
heartbeat_period(0), received_heartbeats(0), master_id(0), using_gtid(0)
|
||||
{
|
||||
host[0] = 0; user[0] = 0; password[0] = 0;
|
||||
ssl_ca[0]= 0; ssl_capath[0]= 0; ssl_cert[0]= 0;
|
||||
|
|
@ -61,7 +61,9 @@ Master_info::Master_info(LEX_STRING *connection_name_arg,
|
|||
my_casedn_str(system_charset_info, cmp_connection_name.str);
|
||||
}
|
||||
|
||||
my_init_dynamic_array(&ignore_server_ids, sizeof(::server_id), 16, 16, MYF(0));
|
||||
my_init_dynamic_array(&ignore_server_ids,
|
||||
sizeof(global_system_variables.server_id), 16, 16,
|
||||
MYF(0));
|
||||
bzero((char*) &file, sizeof(file));
|
||||
mysql_mutex_init(key_master_info_run_lock, &run_lock, MY_MUTEX_INIT_FAST);
|
||||
mysql_mutex_init(key_master_info_data_lock, &data_lock, MY_MUTEX_INIT_FAST);
|
||||
|
|
@ -141,6 +143,7 @@ void init_master_log_pos(Master_info* mi)
|
|||
|
||||
mi->master_log_name[0] = 0;
|
||||
mi->master_log_pos = BIN_LOG_HEADER_SIZE; // skip magic number
|
||||
mi->using_gtid= false;
|
||||
|
||||
/* Intentionally init ssl_verify_server_cert to 0, no option available */
|
||||
mi->ssl_verify_server_cert= 0;
|
||||
|
|
@ -170,8 +173,13 @@ enum {
|
|||
LINE_FOR_MASTER_BIND = 17,
|
||||
/* 6.0 added value of master_ignore_server_id */
|
||||
LINE_FOR_REPLICATE_IGNORE_SERVER_IDS= 18,
|
||||
/* Number of lines currently used when saving master info file */
|
||||
LINES_IN_MASTER_INFO= LINE_FOR_REPLICATE_IGNORE_SERVER_IDS
|
||||
/* MySQL 5.6 fixed-position lines. */
|
||||
LINE_FOR_FIRST_MYSQL_5_6=19,
|
||||
LINE_FOR_LAST_MYSQL_5_6=23,
|
||||
/* Reserved lines for MySQL future versions. */
|
||||
LINE_FOR_LAST_MYSQL_FUTURE=33,
|
||||
/* Number of (fixed-position) lines used when saving master info file */
|
||||
LINES_IN_MASTER_INFO= LINE_FOR_LAST_MYSQL_FUTURE
|
||||
};
|
||||
|
||||
int init_master_info(Master_info* mi, const char* master_info_fname,
|
||||
|
|
@ -299,7 +307,7 @@ file '%s')", fname);
|
|||
int ssl= 0, ssl_verify_server_cert= 0;
|
||||
float master_heartbeat_period= 0.0;
|
||||
char *first_non_digit;
|
||||
char dummy_buf[HOSTNAME_LENGTH+1];
|
||||
char buf[HOSTNAME_LENGTH+1];
|
||||
|
||||
/*
|
||||
Starting from 4.1.x master.info has new format. Now its
|
||||
|
|
@ -393,7 +401,7 @@ file '%s')", fname);
|
|||
(this is just a reservation to avoid future upgrade problems)
|
||||
*/
|
||||
if (lines >= LINE_FOR_MASTER_BIND &&
|
||||
init_strvar_from_file(dummy_buf, sizeof(dummy_buf), &mi->file, ""))
|
||||
init_strvar_from_file(buf, sizeof(buf), &mi->file, ""))
|
||||
goto errwithmsg;
|
||||
/*
|
||||
Starting from 6.0 list of server_id of ignorable servers might be
|
||||
|
|
@ -405,6 +413,34 @@ file '%s')", fname);
|
|||
sql_print_error("Failed to initialize master info ignore_server_ids");
|
||||
goto errwithmsg;
|
||||
}
|
||||
|
||||
/*
|
||||
Starting with MariaDB 10.0, we use a key=value syntax, which is nicer
|
||||
in several ways. But we leave a bunch of empty lines to accomodate
|
||||
any future old-style additions in MySQL (this will make it easier for
|
||||
users moving from MariaDB to MySQL, to not have MySQL try to
|
||||
interpret a MariaDB key=value line.)
|
||||
*/
|
||||
if (lines >= LINE_FOR_LAST_MYSQL_FUTURE)
|
||||
{
|
||||
uint i;
|
||||
/* Skip lines used by / reserved for MySQL >= 5.6. */
|
||||
for (i= LINE_FOR_FIRST_MYSQL_5_6; i <= LINE_FOR_LAST_MYSQL_FUTURE; ++i)
|
||||
{
|
||||
if (init_strvar_from_file(buf, sizeof(buf), &mi->file, ""))
|
||||
goto errwithmsg;
|
||||
}
|
||||
|
||||
/*
|
||||
Parse any extra key=value lines.
|
||||
Ignore unknown lines, to facilitate downgrades.
|
||||
*/
|
||||
while (!init_strvar_from_file(buf, sizeof(buf), &mi->file, 0))
|
||||
{
|
||||
if (0 == strncmp(buf, STRING_WITH_LEN("using_gtid=")))
|
||||
mi->using_gtid= (0 != atoi(buf + sizeof("using_gtid")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef HAVE_OPENSSL
|
||||
|
|
@ -510,7 +546,7 @@ int flush_master_info(Master_info* mi,
|
|||
char* ignore_server_ids_buf;
|
||||
{
|
||||
ignore_server_ids_buf=
|
||||
(char *) my_malloc((sizeof(::server_id) * 3 + 1) *
|
||||
(char *) my_malloc((sizeof(global_system_variables.server_id) * 3 + 1) *
|
||||
(1 + mi->ignore_server_ids.elements), MYF(MY_WME));
|
||||
if (!ignore_server_ids_buf)
|
||||
DBUG_RETURN(1);
|
||||
|
|
@ -544,14 +580,16 @@ int flush_master_info(Master_info* mi,
|
|||
sprintf(heartbeat_buf, "%.3f", mi->heartbeat_period);
|
||||
my_b_seek(file, 0L);
|
||||
my_b_printf(file,
|
||||
"%u\n%s\n%s\n%s\n%s\n%s\n%d\n%d\n%d\n%s\n%s\n%s\n%s\n%s\n%d\n%s\n%s\n%s\n",
|
||||
"%u\n%s\n%s\n%s\n%s\n%s\n%d\n%d\n%d\n%s\n%s\n%s\n%s\n%s\n%d\n%s\n%s\n%s\n"
|
||||
"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
|
||||
"using_gtid=%d\n",
|
||||
LINES_IN_MASTER_INFO,
|
||||
mi->master_log_name, llstr(mi->master_log_pos, lbuf),
|
||||
mi->host, mi->user,
|
||||
mi->password, mi->port, mi->connect_retry,
|
||||
(int)(mi->ssl), mi->ssl_ca, mi->ssl_capath, mi->ssl_cert,
|
||||
mi->ssl_cipher, mi->ssl_key, mi->ssl_verify_server_cert,
|
||||
heartbeat_buf, "", ignore_server_ids_buf);
|
||||
heartbeat_buf, "", ignore_server_ids_buf, mi->using_gtid);
|
||||
my_free(ignore_server_ids_buf);
|
||||
err= flush_io_cache(file);
|
||||
if (sync_masterinfo_period && !err &&
|
||||
|
|
@ -639,7 +677,7 @@ bool check_master_connection_name(LEX_STRING *name)
|
|||
file names without a prefix.
|
||||
*/
|
||||
|
||||
void create_logfile_name_with_suffix(char *res_file_name, uint length,
|
||||
void create_logfile_name_with_suffix(char *res_file_name, size_t length,
|
||||
const char *info_file, bool append,
|
||||
LEX_STRING *suffix)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -126,6 +126,11 @@ class Master_info : public Slave_reporting_capability
|
|||
ulonglong received_heartbeats; // counter of received heartbeat events
|
||||
DYNAMIC_ARRAY ignore_server_ids;
|
||||
ulong master_id;
|
||||
/*
|
||||
True if slave position is set using GTID state rather than old-style
|
||||
file/offset binlog position.
|
||||
*/
|
||||
bool using_gtid;
|
||||
};
|
||||
int init_master_info(Master_info* mi, const char* master_info_fname,
|
||||
const char* slave_info_fname,
|
||||
|
|
@ -170,7 +175,7 @@ public:
|
|||
};
|
||||
|
||||
bool check_master_connection_name(LEX_STRING *name);
|
||||
void create_logfile_name_with_suffix(char *res_file_name, uint length,
|
||||
void create_logfile_name_with_suffix(char *res_file_name, size_t length,
|
||||
const char *info_file,
|
||||
bool append,
|
||||
LEX_STRING *suffix);
|
||||
|
|
|
|||
165
sql/rpl_rli.cc
165
sql/rpl_rli.cc
|
|
@ -31,6 +31,13 @@
|
|||
|
||||
static int count_relay_log_space(Relay_log_info* rli);
|
||||
|
||||
/**
|
||||
Current replication state (hash of last GTID executed, per replication
|
||||
domain).
|
||||
*/
|
||||
rpl_slave_state rpl_global_gtid_slave_state;
|
||||
|
||||
|
||||
// Defined in slave.cc
|
||||
int init_intvar_from_file(int* var, IO_CACHE* f, int default_val);
|
||||
int init_strvar_from_file(char *var, int max_size, IO_CACHE *f,
|
||||
|
|
@ -51,7 +58,7 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery)
|
|||
abort_pos_wait(0), slave_run_id(0), sql_thd(0),
|
||||
inited(0), abort_slave(0), slave_running(0), until_condition(UNTIL_NONE),
|
||||
until_log_pos(0), retried_trans(0), executed_entries(0),
|
||||
tables_to_lock(0), tables_to_lock_count(0),
|
||||
gtid_sub_id(0), tables_to_lock(0), tables_to_lock_count(0),
|
||||
last_event_start_time(0), deferred_events(NULL),m_flags(0),
|
||||
row_stmt_start_timestamp(0), long_find_row_note_printed(false),
|
||||
m_annotate_event(0)
|
||||
|
|
@ -1091,7 +1098,8 @@ bool Relay_log_info::is_until_satisfied(THD *thd, Log_event *ev)
|
|||
|
||||
if (until_condition == UNTIL_MASTER_POS)
|
||||
{
|
||||
if (ev && ev->server_id == (uint32) ::server_id && !replicate_same_server_id)
|
||||
if (ev && ev->server_id == (uint32) global_system_variables.server_id &&
|
||||
!replicate_same_server_id)
|
||||
DBUG_RETURN(FALSE);
|
||||
log_name= group_master_log_name;
|
||||
log_pos= (!ev)? group_master_log_pos :
|
||||
|
|
@ -1189,7 +1197,7 @@ bool Relay_log_info::cached_charset_compare(char *charset) const
|
|||
|
||||
|
||||
void Relay_log_info::stmt_done(my_off_t event_master_log_pos,
|
||||
time_t event_creation_time)
|
||||
time_t event_creation_time, THD *thd)
|
||||
{
|
||||
#ifndef DBUG_OFF
|
||||
extern uint debug_not_change_ts_if_art_event;
|
||||
|
|
@ -1224,7 +1232,23 @@ void Relay_log_info::stmt_done(my_off_t event_master_log_pos,
|
|||
else
|
||||
{
|
||||
inc_group_relay_log_pos(event_master_log_pos);
|
||||
if (rpl_global_gtid_slave_state.record_and_update_gtid(thd, this))
|
||||
{
|
||||
report(WARNING_LEVEL, ER_CANNOT_UPDATE_GTID_STATE,
|
||||
"Failed to update GTID state in %s.%s, slave state may become "
|
||||
"inconsistent: %d: %s",
|
||||
"mysql", rpl_gtid_slave_state_table_name.str,
|
||||
thd->stmt_da->sql_errno(), thd->stmt_da->message());
|
||||
/*
|
||||
At this point we are not in a transaction (for example after DDL),
|
||||
so we can not roll back. Anyway, normally updates to the slave
|
||||
state table should not fail, and if they do, at least we made the
|
||||
DBA aware of the problem in the error log.
|
||||
*/
|
||||
}
|
||||
DBUG_EXECUTE_IF("inject_crash_before_flush_rli", DBUG_SUICIDE(););
|
||||
flush_relay_log_info(this);
|
||||
DBUG_EXECUTE_IF("inject_crash_after_flush_rli", DBUG_SUICIDE(););
|
||||
/*
|
||||
Note that Rotate_log_event::do_apply_event() does not call this
|
||||
function, so there is no chance that a fake rotate event resets
|
||||
|
|
@ -1356,4 +1380,139 @@ void Relay_log_info::slave_close_thread_tables(THD *thd)
|
|||
clear_tables_to_lock();
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
rpl_load_gtid_slave_state(THD *thd)
|
||||
{
|
||||
TABLE_LIST tlist;
|
||||
TABLE *table;
|
||||
bool table_opened= false;
|
||||
bool table_scanned= false;
|
||||
struct local_element { uint64 sub_id; rpl_gtid gtid; };
|
||||
struct local_element *entry;
|
||||
HASH hash;
|
||||
int err= 0;
|
||||
uint32 i;
|
||||
uint64 highest_seq_no= 0;
|
||||
DBUG_ENTER("rpl_load_gtid_slave_state");
|
||||
|
||||
rpl_global_gtid_slave_state.lock();
|
||||
bool loaded= rpl_global_gtid_slave_state.loaded;
|
||||
rpl_global_gtid_slave_state.unlock();
|
||||
if (loaded)
|
||||
DBUG_RETURN(0);
|
||||
|
||||
my_hash_init(&hash, &my_charset_bin, 32,
|
||||
offsetof(local_element, gtid) + offsetof(rpl_gtid, domain_id),
|
||||
sizeof(uint32), NULL, my_free, HASH_UNIQUE);
|
||||
|
||||
mysql_reset_thd_for_next_command(thd, 0);
|
||||
|
||||
tlist.init_one_table(STRING_WITH_LEN("mysql"),
|
||||
rpl_gtid_slave_state_table_name.str,
|
||||
rpl_gtid_slave_state_table_name.length,
|
||||
NULL, TL_READ);
|
||||
if ((err= open_and_lock_tables(thd, &tlist, FALSE, 0)))
|
||||
goto end;
|
||||
table_opened= true;
|
||||
table= tlist.table;
|
||||
|
||||
if ((err= gtid_check_rpl_slave_state_table(table)))
|
||||
goto end;
|
||||
|
||||
bitmap_set_all(table->read_set);
|
||||
if ((err= table->file->ha_rnd_init_with_error(1)))
|
||||
goto end;
|
||||
table_scanned= true;
|
||||
for (;;)
|
||||
{
|
||||
uint32 domain_id, server_id;
|
||||
uint64 sub_id, seq_no;
|
||||
uchar *rec;
|
||||
|
||||
if ((err= table->file->ha_rnd_next(table->record[0])))
|
||||
{
|
||||
if (err == HA_ERR_RECORD_DELETED)
|
||||
continue;
|
||||
else if (err == HA_ERR_END_OF_FILE)
|
||||
break;
|
||||
else
|
||||
goto end;
|
||||
}
|
||||
domain_id= (ulonglong)table->field[0]->val_int();
|
||||
sub_id= (ulonglong)table->field[1]->val_int();
|
||||
server_id= (ulonglong)table->field[2]->val_int();
|
||||
seq_no= (ulonglong)table->field[3]->val_int();
|
||||
DBUG_PRINT("info", ("Read slave state row: %u-%u-%lu sub_id=%lu\n",
|
||||
(unsigned)domain_id, (unsigned)server_id,
|
||||
(ulong)seq_no, (ulong)sub_id));
|
||||
if (seq_no > highest_seq_no)
|
||||
highest_seq_no= seq_no;
|
||||
|
||||
if ((rec= my_hash_search(&hash, (const uchar *)&domain_id, 0)))
|
||||
{
|
||||
entry= (struct local_element *)rec;
|
||||
if (entry->sub_id >= sub_id)
|
||||
continue;
|
||||
entry->sub_id= sub_id;
|
||||
DBUG_ASSERT(entry->gtid.domain_id == domain_id);
|
||||
entry->gtid.server_id= server_id;
|
||||
entry->gtid.seq_no= seq_no;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(entry= (struct local_element *)my_malloc(sizeof(*entry),
|
||||
MYF(MY_WME))))
|
||||
{
|
||||
err= 1;
|
||||
goto end;
|
||||
}
|
||||
entry->sub_id= sub_id;
|
||||
entry->gtid.domain_id= domain_id;
|
||||
entry->gtid.server_id= server_id;
|
||||
entry->gtid.seq_no= seq_no;
|
||||
if ((err= my_hash_insert(&hash, (uchar *)entry)))
|
||||
{
|
||||
my_free(entry);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rpl_global_gtid_slave_state.lock();
|
||||
for (i= 0; i < hash.records; ++i)
|
||||
{
|
||||
entry= (struct local_element *)my_hash_element(&hash, i);
|
||||
if ((err= rpl_global_gtid_slave_state.update(entry->gtid.domain_id,
|
||||
entry->gtid.server_id,
|
||||
entry->sub_id,
|
||||
entry->gtid.seq_no)))
|
||||
{
|
||||
rpl_global_gtid_slave_state.unlock();
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
rpl_global_gtid_slave_state.loaded= true;
|
||||
rpl_global_gtid_slave_state.unlock();
|
||||
|
||||
err= 0; /* Clear HA_ERR_END_OF_FILE */
|
||||
|
||||
end:
|
||||
if (table_scanned)
|
||||
{
|
||||
table->file->ha_index_or_rnd_end();
|
||||
ha_commit_trans(thd, FALSE);
|
||||
ha_commit_trans(thd, TRUE);
|
||||
}
|
||||
if (table_opened)
|
||||
{
|
||||
close_thread_tables(thd);
|
||||
thd->mdl_context.release_transactional_locks();
|
||||
}
|
||||
my_hash_free(&hash);
|
||||
mysql_bin_log.bump_seq_no_counter_if_needed(highest_seq_no);
|
||||
DBUG_RETURN(err);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -307,6 +307,14 @@ public:
|
|||
char slave_patternload_file[FN_REFLEN];
|
||||
size_t slave_patternload_file_size;
|
||||
|
||||
/*
|
||||
Current GTID being processed.
|
||||
The sub_id gives the binlog order within one domain_id. A zero sub_id
|
||||
means that there is no active GTID.
|
||||
*/
|
||||
uint64 gtid_sub_id;
|
||||
rpl_gtid current_gtid;
|
||||
|
||||
Relay_log_info(bool is_slave_recovery);
|
||||
~Relay_log_info();
|
||||
|
||||
|
|
@ -445,7 +453,7 @@ public:
|
|||
the <code>Seconds_behind_master</code> field.
|
||||
*/
|
||||
void stmt_done(my_off_t event_log_pos,
|
||||
time_t event_creation_time);
|
||||
time_t event_creation_time, THD *thd);
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -584,4 +592,8 @@ private:
|
|||
int init_relay_log_info(Relay_log_info* rli, const char* info_fname);
|
||||
|
||||
|
||||
extern struct rpl_slave_state rpl_global_gtid_slave_state;
|
||||
|
||||
int rpl_load_gtid_slave_state(THD *thd);
|
||||
|
||||
#endif /* RPL_RLI_H */
|
||||
|
|
|
|||
|
|
@ -6524,4 +6524,22 @@ ER_SLAVE_STOPPED
|
|||
eng "SLAVE '%.*s' stopped"
|
||||
ER_SQL_DISCOVER_ERROR
|
||||
eng "Engine %s failed to discover table %`-.192s.%`-.192s with '%s'"
|
||||
|
||||
ER_FAILED_GTID_STATE_INIT
|
||||
eng "Failed initializing replication GTID state"
|
||||
ER_INCORRECT_GTID_STATE
|
||||
eng "Could not parse GTID list for GTID_POS"
|
||||
ER_CANNOT_UPDATE_GTID_STATE
|
||||
eng "Could not update replication slave gtid state"
|
||||
ER_DUPLICATE_GTID_DOMAIN
|
||||
eng "GTID %u-%u-%llu and %u-%u-%llu conflict (duplicate domain id %u)"
|
||||
ER_GTID_OPEN_TABLE_FAILED
|
||||
eng "Failed to open %s.%s"
|
||||
ger "Öffnen von %s.%s fehlgeschlagen"
|
||||
ER_GTID_POSITION_NOT_FOUND_IN_BINLOG
|
||||
eng "Connecting slave requested to start from GTID %u-%u-%llu, which is not in the master's binlog"
|
||||
ER_CANNOT_LOAD_SLAVE_GTID_STATE
|
||||
eng "Failed to load replication slave GTID state from table %s.%s"
|
||||
ER_MASTER_GTID_POS_CONFLICTS_WITH_BINLOG
|
||||
eng "Requested GTID_POS %u-%u-%llu conflicts with the binary log which contains a more recent GTID %u-%u-%llu. To use the requested GTID_POS, the old binlog must be removed with RESET MASTER to avoid out-of-order binlog"
|
||||
ER_MASTER_GTID_POS_MISSING_DOMAIN
|
||||
eng "Requested GTID_POS contains no value for replication domain %u. This conflicts with the binary log which contains GTID %u-%u-%llu. To use the requested GTID_POS, the old binlog must be removed with RESET MASTER to avoid out-of-order binlog"
|
||||
|
|
|
|||
272
sql/slave.cc
272
sql/slave.cc
|
|
@ -162,8 +162,10 @@ static int terminate_slave_thread(THD *thd,
|
|||
volatile uint *slave_running,
|
||||
bool skip_lock);
|
||||
static bool check_io_slave_killed(THD *thd, Master_info *mi, const char *info);
|
||||
static bool send_show_master_info_header(THD *thd, bool full);
|
||||
static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full);
|
||||
static bool send_show_master_info_header(THD *thd, bool full,
|
||||
size_t gtid_pos_length);
|
||||
static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
|
||||
String *gtid_pos);
|
||||
|
||||
/*
|
||||
Find out which replications threads are running
|
||||
|
|
@ -395,6 +397,7 @@ int init_recovery(Master_info* mi, const char** errmsg)
|
|||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Convert slave skip errors bitmap into a printable string.
|
||||
|
|
@ -706,7 +709,7 @@ int start_slave_thread(
|
|||
|
||||
if (start_lock)
|
||||
mysql_mutex_lock(start_lock);
|
||||
if (!server_id)
|
||||
if (!global_system_variables.server_id)
|
||||
{
|
||||
if (start_cond)
|
||||
mysql_cond_broadcast(start_cond);
|
||||
|
|
@ -782,6 +785,7 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
|
|||
mysql_mutex_t *lock_io=0, *lock_sql=0, *lock_cond_io=0, *lock_cond_sql=0;
|
||||
mysql_cond_t* cond_io=0, *cond_sql=0;
|
||||
int error=0;
|
||||
const char *errmsg;
|
||||
DBUG_ENTER("start_slave_threads");
|
||||
|
||||
if (need_slave_mutex)
|
||||
|
|
@ -797,6 +801,22 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start,
|
|||
lock_cond_sql = &mi->rli.run_lock;
|
||||
}
|
||||
|
||||
/*
|
||||
If we are using GTID and both SQL and IO threads are stopped, then get
|
||||
rid of all relay logs.
|
||||
|
||||
Relay logs are not very useful when using GTID, except as a buffer
|
||||
between the fetch in the IO thread and the apply in SQL thread. However
|
||||
while one of the threads is running, they are in use and cannot be
|
||||
removed.
|
||||
*/
|
||||
if (mi->using_gtid && !mi->slave_running && !mi->rli.slave_running)
|
||||
{
|
||||
purge_relay_logs(&mi->rli, NULL, 0, &errmsg);
|
||||
mi->master_log_name[0]= 0;
|
||||
mi->master_log_pos= 0;
|
||||
}
|
||||
|
||||
if (thread_mask & SLAVE_IO)
|
||||
error= start_slave_thread(
|
||||
#ifdef HAVE_PSI_INTERFACE
|
||||
|
|
@ -1407,7 +1427,8 @@ static int get_master_version_and_clock(MYSQL* mysql, Master_info* mi)
|
|||
(master_res= mysql_store_result(mysql)) &&
|
||||
(master_row= mysql_fetch_row(master_res)))
|
||||
{
|
||||
if ((::server_id == (mi->master_id= strtoul(master_row[1], 0, 10))) &&
|
||||
if ((global_system_variables.server_id ==
|
||||
(mi->master_id= strtoul(master_row[1], 0, 10))) &&
|
||||
!mi->rli.replicate_same_server_id)
|
||||
{
|
||||
errmsg= "The slave I/O thread stops because master and slave have equal \
|
||||
|
|
@ -1787,6 +1808,133 @@ past_checksum:
|
|||
after_set_capability:
|
||||
#endif
|
||||
|
||||
/*
|
||||
Request dump start from slave replication GTID state.
|
||||
|
||||
Only request GTID position the first time we connect after CHANGE MASTER
|
||||
or after starting both IO or SQL thread.
|
||||
|
||||
Otherwise, if the IO thread was ahead of the SQL thread before the
|
||||
restart or reconnect, we might end up re-fetching and hence re-applying
|
||||
the same event(s) again.
|
||||
*/
|
||||
if (mi->using_gtid && !mi->master_log_name[0])
|
||||
{
|
||||
int rc;
|
||||
char str_buf[256];
|
||||
String connect_state(str_buf, sizeof(str_buf), system_charset_info);
|
||||
connect_state.length(0);
|
||||
|
||||
/*
|
||||
Read the master @@GLOBAL.gtid_domain_id variable.
|
||||
This is mostly to check that master is GTID aware, but we could later
|
||||
perhaps use it to check that different multi-source masters are correctly
|
||||
configured with distinct domain_id.
|
||||
*/
|
||||
if (mysql_real_query(mysql,
|
||||
STRING_WITH_LEN("SELECT @@GLOBAL.gtid_domain_id")) ||
|
||||
!(master_res= mysql_store_result(mysql)) ||
|
||||
!(master_row= mysql_fetch_row(master_res)))
|
||||
{
|
||||
err_code= mysql_errno(mysql);
|
||||
errmsg= "The slave I/O thread stops because master does not support "
|
||||
"MariaDB global transaction id. A fatal error is encountered when "
|
||||
"it tries to SELECT @@GLOBAL.gtid_domain_id.";
|
||||
sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
|
||||
goto err;
|
||||
}
|
||||
mysql_free_result(master_res);
|
||||
master_res= NULL;
|
||||
|
||||
connect_state.append(STRING_WITH_LEN("SET @slave_connect_state='"),
|
||||
system_charset_info);
|
||||
if (rpl_append_gtid_state(&connect_state, true))
|
||||
{
|
||||
err_code= ER_OUTOFMEMORY;
|
||||
errmsg= "The slave I/O thread stops because a fatal out-of-memory "
|
||||
"error is encountered when it tries to compute @slave_connect_state.";
|
||||
sprintf(err_buff, "%s Error: Out of memory", errmsg);
|
||||
goto err;
|
||||
}
|
||||
connect_state.append(STRING_WITH_LEN("'"), system_charset_info);
|
||||
|
||||
rc= mysql_real_query(mysql, connect_state.ptr(), connect_state.length());
|
||||
if (rc)
|
||||
{
|
||||
err_code= mysql_errno(mysql);
|
||||
if (is_network_error(err_code))
|
||||
{
|
||||
mi->report(ERROR_LEVEL, err_code,
|
||||
"Setting @slave_connect_state failed with error: %s",
|
||||
mysql_error(mysql));
|
||||
goto network_err;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fatal error */
|
||||
errmsg= "The slave I/O thread stops because a fatal error is "
|
||||
"encountered when it tries to set @slave_connect_state.";
|
||||
sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql));
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!mi->using_gtid)
|
||||
{
|
||||
/*
|
||||
If we are not using GTID to connect this time, then instead request
|
||||
the corresponding GTID position from the master, so that the user
|
||||
can reconnect the next time using MASTER_GTID_POS=AUTO.
|
||||
*/
|
||||
char quote_buf[2*sizeof(mi->master_log_name)+1];
|
||||
char str_buf[28+2*sizeof(mi->master_log_name)+10];
|
||||
String query(str_buf, sizeof(str_buf), system_charset_info);
|
||||
query.length(0);
|
||||
|
||||
query.append("SELECT binlog_gtid_pos('");
|
||||
escape_quotes_for_mysql(&my_charset_bin, quote_buf, sizeof(quote_buf),
|
||||
mi->master_log_name, strlen(mi->master_log_name));
|
||||
query.append(quote_buf);
|
||||
query.append("',");
|
||||
query.append_ulonglong(mi->master_log_pos);
|
||||
query.append(")");
|
||||
|
||||
if (!mysql_real_query(mysql, query.c_ptr_safe(), query.length()) &&
|
||||
(master_res= mysql_store_result(mysql)) &&
|
||||
(master_row= mysql_fetch_row(master_res)) &&
|
||||
(master_row[0] != NULL))
|
||||
{
|
||||
rpl_global_gtid_slave_state.load(mi->io_thd, master_row[0],
|
||||
strlen(master_row[0]), false);
|
||||
}
|
||||
else if (check_io_slave_killed(mi->io_thd, mi, NULL))
|
||||
goto slave_killed_err;
|
||||
else if (is_network_error(mysql_errno(mysql)))
|
||||
{
|
||||
mi->report(WARNING_LEVEL, mysql_errno(mysql),
|
||||
"Get master GTID position failed with error: %s", mysql_error(mysql));
|
||||
goto network_err;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
ToDo: If the master does not have the binlog_gtid_pos() function, it
|
||||
just means that it is an old master with no GTID support, so we should
|
||||
do nothing.
|
||||
|
||||
However, if binlog_gtid_pos() exists, but fails or returns NULL, then
|
||||
it means that the requested position is not valid. We could use this
|
||||
to catch attempts to replicate from within the middle of an event,
|
||||
avoiding strange failures or possible corruption.
|
||||
*/
|
||||
}
|
||||
if (master_res)
|
||||
{
|
||||
mysql_free_result(master_res);
|
||||
master_res= NULL;
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
if (errmsg)
|
||||
{
|
||||
|
|
@ -1980,7 +2128,7 @@ int register_slave_on_master(MYSQL* mysql, Master_info *mi,
|
|||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
int4store(pos, server_id); pos+= 4;
|
||||
int4store(pos, global_system_variables.server_id); pos+= 4;
|
||||
pos= net_store_data(pos, (uchar*) report_host, report_host_len);
|
||||
pos= net_store_data(pos, (uchar*) report_user, report_user_len);
|
||||
pos= net_store_data(pos, (uchar*) report_password, report_password_len);
|
||||
|
|
@ -2029,16 +2177,20 @@ int register_slave_on_master(MYSQL* mysql, Master_info *mi,
|
|||
bool show_master_info(THD *thd, Master_info *mi, bool full)
|
||||
{
|
||||
DBUG_ENTER("show_master_info");
|
||||
String gtid_pos;
|
||||
|
||||
if (send_show_master_info_header(thd, full))
|
||||
if (full && rpl_global_gtid_slave_state.tostring(>id_pos, NULL, 0))
|
||||
DBUG_RETURN(TRUE);
|
||||
if (send_show_master_info_data(thd, mi, full))
|
||||
if (send_show_master_info_header(thd, full, gtid_pos.length()))
|
||||
DBUG_RETURN(TRUE);
|
||||
if (send_show_master_info_data(thd, mi, full, >id_pos))
|
||||
DBUG_RETURN(TRUE);
|
||||
my_eof(thd);
|
||||
DBUG_RETURN(FALSE);
|
||||
}
|
||||
|
||||
static bool send_show_master_info_header(THD *thd, bool full)
|
||||
static bool send_show_master_info_header(THD *thd, bool full,
|
||||
size_t gtid_pos_length)
|
||||
{
|
||||
List<Item> field_list;
|
||||
Protocol *protocol= thd->protocol;
|
||||
|
|
@ -2117,6 +2269,8 @@ static bool send_show_master_info_header(THD *thd, bool full)
|
|||
FN_REFLEN));
|
||||
field_list.push_back(new Item_return_int("Master_Server_Id", sizeof(ulong),
|
||||
MYSQL_TYPE_LONG));
|
||||
field_list.push_back(new Item_return_int("Using_Gtid", sizeof(ulong),
|
||||
MYSQL_TYPE_LONG));
|
||||
if (full)
|
||||
{
|
||||
field_list.push_back(new Item_return_int("Retried_transactions",
|
||||
|
|
@ -2129,6 +2283,7 @@ static bool send_show_master_info_header(THD *thd, bool full)
|
|||
10, MYSQL_TYPE_LONG));
|
||||
field_list.push_back(new Item_float("Slave_heartbeat_period",
|
||||
0.0, 3, 10));
|
||||
field_list.push_back(new Item_empty_string("Gtid_Pos", gtid_pos_length));
|
||||
}
|
||||
if (protocol->send_result_set_metadata(&field_list,
|
||||
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
|
||||
|
|
@ -2137,7 +2292,8 @@ static bool send_show_master_info_header(THD *thd, bool full)
|
|||
}
|
||||
|
||||
|
||||
static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full)
|
||||
static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full,
|
||||
String *gtid_pos)
|
||||
{
|
||||
DBUG_ENTER("send_show_master_info_data");
|
||||
|
||||
|
|
@ -2292,6 +2448,7 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full)
|
|||
}
|
||||
// Master_Server_id
|
||||
protocol->store((uint32) mi->master_id);
|
||||
protocol->store((uint32) (mi->using_gtid != 0));
|
||||
if (full)
|
||||
{
|
||||
protocol->store((uint32) mi->rli.retried_trans);
|
||||
|
|
@ -2299,6 +2456,7 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full)
|
|||
protocol->store((uint32) mi->rli.executed_entries);
|
||||
protocol->store((uint32) mi->received_heartbeats);
|
||||
protocol->store((double) mi->heartbeat_period, 3, &tmp);
|
||||
protocol->store(gtid_pos->ptr(), gtid_pos->length(), &my_charset_bin);
|
||||
}
|
||||
|
||||
mysql_mutex_unlock(&mi->rli.err_lock);
|
||||
|
|
@ -2341,11 +2499,19 @@ static int cmp_mi_by_name(const Master_info **arg1,
|
|||
bool show_all_master_info(THD* thd)
|
||||
{
|
||||
uint i, elements;
|
||||
String gtid_pos;
|
||||
Master_info **tmp;
|
||||
DBUG_ENTER("show_master_info");
|
||||
mysql_mutex_assert_owner(&LOCK_active_mi);
|
||||
|
||||
if (send_show_master_info_header(thd, 1))
|
||||
gtid_pos.length(0);
|
||||
if (rpl_append_gtid_state(>id_pos, true))
|
||||
{
|
||||
my_error(ER_OUT_OF_RESOURCES, MYF(0));
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
if (send_show_master_info_header(thd, 1, gtid_pos.length()))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
if (!(elements= master_info_index->master_info_hash.records))
|
||||
|
|
@ -2367,7 +2533,7 @@ bool show_all_master_info(THD* thd)
|
|||
|
||||
for (i= 0; i < elements; i++)
|
||||
{
|
||||
if (send_show_master_info_data(thd, tmp[i], 1))
|
||||
if (send_show_master_info_data(thd, tmp[i], 1, >id_pos))
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
|
|
@ -2533,7 +2699,7 @@ static int request_dump(THD *thd, MYSQL* mysql, Master_info* mi,
|
|||
// TODO if big log files: Change next to int8store()
|
||||
int4store(buf, (ulong) mi->master_log_pos);
|
||||
int2store(buf + 4, binlog_flags);
|
||||
int4store(buf + 6, server_id);
|
||||
int4store(buf + 6, global_system_variables.server_id);
|
||||
len = (uint) strlen(logname);
|
||||
memcpy(buf + 10, logname,len);
|
||||
if (simple_command(mysql, COM_BINLOG_DUMP, buf, len + 10, 1))
|
||||
|
|
@ -2742,7 +2908,8 @@ int apply_event_and_update_pos(Log_event* ev, THD* thd, Relay_log_info* rli)
|
|||
has a Rotate etc).
|
||||
*/
|
||||
|
||||
thd->server_id = ev->server_id; // use the original server id for logging
|
||||
/* Use the original server id for logging. */
|
||||
thd->variables.server_id = ev->server_id;
|
||||
thd->set_time(); // time the query
|
||||
thd->lex->current_select= 0;
|
||||
if (!ev->when)
|
||||
|
|
@ -3526,17 +3693,34 @@ err_during_init:
|
|||
/*
|
||||
Check the temporary directory used by commands like
|
||||
LOAD DATA INFILE.
|
||||
|
||||
As the directory never changes during a mysqld run, we only
|
||||
test this once and cache the result. This also resolve a race condition
|
||||
when this can be run by multiple threads at the same time.
|
||||
*/
|
||||
|
||||
static bool check_temp_dir_run= 0;
|
||||
static int check_temp_dir_result= 0;
|
||||
|
||||
static
|
||||
int check_temp_dir(char* tmp_file)
|
||||
{
|
||||
int fd;
|
||||
File fd;
|
||||
int result= 1; // Assume failure
|
||||
MY_DIR *dirp;
|
||||
char tmp_dir[FN_REFLEN];
|
||||
size_t tmp_dir_size;
|
||||
|
||||
DBUG_ENTER("check_temp_dir");
|
||||
|
||||
mysql_mutex_lock(&LOCK_thread_count);
|
||||
if (check_temp_dir_run)
|
||||
{
|
||||
result= check_temp_dir_result;
|
||||
goto end;
|
||||
}
|
||||
check_temp_dir_run= 1;
|
||||
|
||||
/*
|
||||
Get the directory from the temporary file.
|
||||
*/
|
||||
|
|
@ -3546,27 +3730,33 @@ int check_temp_dir(char* tmp_file)
|
|||
Check if the directory exists.
|
||||
*/
|
||||
if (!(dirp=my_dir(tmp_dir,MYF(MY_WME))))
|
||||
DBUG_RETURN(1);
|
||||
goto end;
|
||||
my_dirend(dirp);
|
||||
|
||||
/*
|
||||
Check permissions to create a file.
|
||||
Check permissions to create a file. We use O_TRUNC to ensure that
|
||||
things works even if we happen to have and old file laying around.
|
||||
*/
|
||||
if ((fd= mysql_file_create(key_file_misc,
|
||||
tmp_file, CREATE_MODE,
|
||||
O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW,
|
||||
O_WRONLY | O_BINARY | O_TRUNC | O_NOFOLLOW,
|
||||
MYF(MY_WME))) < 0)
|
||||
DBUG_RETURN(1);
|
||||
goto end;
|
||||
|
||||
result= 0; // Directory name ok
|
||||
/*
|
||||
Clean up.
|
||||
*/
|
||||
mysql_file_close(fd, MYF(0));
|
||||
mysql_file_delete(key_file_misc, tmp_file, MYF(0));
|
||||
|
||||
DBUG_RETURN(0);
|
||||
end:
|
||||
check_temp_dir_result= result;
|
||||
mysql_mutex_unlock(&LOCK_thread_count);
|
||||
DBUG_RETURN(result);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Slave SQL thread entry point.
|
||||
|
||||
|
|
@ -3728,6 +3918,15 @@ log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME,
|
|||
goto err;
|
||||
}
|
||||
|
||||
/* Load the set of seen GTIDs, if we did not already. */
|
||||
if (rpl_load_gtid_slave_state(thd))
|
||||
{
|
||||
rli->report(ERROR_LEVEL, thd->stmt_da->sql_errno(),
|
||||
"Unable to load replication GTID slave state from mysql.%s: %s",
|
||||
rpl_gtid_slave_state_table_name.str, thd->stmt_da->message());
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* execute init_slave variable */
|
||||
if (opt_init_slave.length)
|
||||
{
|
||||
|
|
@ -3953,7 +4152,7 @@ static int process_io_create_file(Master_info* mi, Create_file_log_event* cev)
|
|||
}
|
||||
DBUG_ASSERT(cev->inited_from_old);
|
||||
thd->file_id = cev->file_id = mi->file_id++;
|
||||
thd->server_id = cev->server_id;
|
||||
thd->variables.server_id = cev->server_id;
|
||||
cev_not_written = 1;
|
||||
|
||||
if (unlikely(net_request_file(net,cev->fname)))
|
||||
|
|
@ -4565,16 +4764,18 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
|
|||
|
||||
Heartbeat is sent only after an event corresponding to the corrdinates
|
||||
the heartbeat carries.
|
||||
Slave can not have a difference in coordinates except in the only
|
||||
Slave can not have a higher coordinate except in the only
|
||||
special case when mi->master_log_name, master_log_pos have never
|
||||
been updated by Rotate event i.e when slave does not have any history
|
||||
with the master (and thereafter mi->master_log_pos is NULL).
|
||||
|
||||
Slave can have lower coordinates, if some event from master was omitted.
|
||||
|
||||
TODO: handling `when' for SHOW SLAVE STATUS' snds behind
|
||||
*/
|
||||
if ((memcmp(mi->master_log_name, hb.get_log_ident(), hb.get_ident_len())
|
||||
&& mi->master_log_name != NULL)
|
||||
|| mi->master_log_pos != hb.log_pos)
|
||||
|| mi->master_log_pos > hb.log_pos)
|
||||
{
|
||||
/* missed events of heartbeat from the past */
|
||||
error= ER_SLAVE_HEARTBEAT_FAILURE;
|
||||
|
|
@ -4626,7 +4827,8 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
|
|||
|
||||
mysql_mutex_lock(log_lock);
|
||||
s_id= uint4korr(buf + SERVER_ID_OFFSET);
|
||||
if ((s_id == ::server_id && !mi->rli.replicate_same_server_id) ||
|
||||
if ((s_id == global_system_variables.server_id &&
|
||||
!mi->rli.replicate_same_server_id) ||
|
||||
/*
|
||||
the following conjunction deals with IGNORE_SERVER_IDS, if set
|
||||
If the master is on the ignore list, execution of
|
||||
|
|
@ -4657,7 +4859,8 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
|
|||
IGNORE_SERVER_IDS it increments mi->master_log_pos
|
||||
as well as rli->group_relay_log_pos.
|
||||
*/
|
||||
if (!(s_id == ::server_id && !mi->rli.replicate_same_server_id) ||
|
||||
if (!(s_id == global_system_variables.server_id &&
|
||||
!mi->rli.replicate_same_server_id) ||
|
||||
(buf[EVENT_TYPE_OFFSET] != FORMAT_DESCRIPTION_EVENT &&
|
||||
buf[EVENT_TYPE_OFFSET] != ROTATE_EVENT &&
|
||||
buf[EVENT_TYPE_OFFSET] != STOP_EVENT))
|
||||
|
|
@ -5191,6 +5394,27 @@ static Log_event* next_event(Relay_log_info* rli)
|
|||
inc_event_relay_log_pos()
|
||||
*/
|
||||
rli->future_event_relay_log_pos= my_b_tell(cur_log);
|
||||
/*
|
||||
For GTID, allocate a new sub_id for the given domain_id.
|
||||
The sub_id must be allocated in increasing order of binlog order.
|
||||
*/
|
||||
if (ev->get_type_code() == GTID_EVENT)
|
||||
{
|
||||
Gtid_log_event *gev= static_cast<Gtid_log_event *>(ev);
|
||||
uint64 sub_id= rpl_global_gtid_slave_state.next_subid(gev->domain_id);
|
||||
if (!sub_id)
|
||||
{
|
||||
errmsg = "slave SQL thread aborted because of out-of-memory error";
|
||||
if (hot_log)
|
||||
mysql_mutex_unlock(log_lock);
|
||||
goto err;
|
||||
}
|
||||
rli->gtid_sub_id= sub_id;
|
||||
rli->current_gtid.server_id= gev->server_id;
|
||||
rli->current_gtid.domain_id= gev->domain_id;
|
||||
rli->current_gtid.seq_no= gev->seq_no;
|
||||
}
|
||||
|
||||
if (hot_log)
|
||||
mysql_mutex_unlock(log_lock);
|
||||
DBUG_RETURN(ev);
|
||||
|
|
|
|||
|
|
@ -2434,7 +2434,7 @@ sp_head::fill_field_definition(THD *thd, LEX *lex,
|
|||
lex->charset ? lex->charset :
|
||||
thd->variables.collation_database,
|
||||
lex->uint_geom_type,
|
||||
lex->vcol_info, NULL))
|
||||
lex->vcol_info, NULL, FALSE))
|
||||
return TRUE;
|
||||
|
||||
if (field_def->interval_list.elements)
|
||||
|
|
|
|||
|
|
@ -345,7 +345,7 @@ uint create_tmp_table_def_key(THD *thd, char *key,
|
|||
const char *db, const char *table_name)
|
||||
{
|
||||
uint key_length= create_table_def_key(key, db, table_name);
|
||||
int4store(key + key_length, thd->server_id);
|
||||
int4store(key + key_length, thd->variables.server_id);
|
||||
int4store(key + key_length + 4, thd->variables.pseudo_thread_id);
|
||||
key_length+= TMP_TABLE_KEY_EXTRA;
|
||||
return key_length;
|
||||
|
|
@ -388,6 +388,14 @@ bool table_def_init(void)
|
|||
init_tdc_psi_keys();
|
||||
#endif
|
||||
mysql_mutex_init(key_LOCK_open, &LOCK_open, MY_MUTEX_INIT_FAST);
|
||||
mysql_mutex_record_order(&LOCK_active_mi, &LOCK_open);
|
||||
/*
|
||||
When we delete from the table_def_cache(), the free function
|
||||
table_def_free_entry() is invoked from my_hash_delete(), which calls
|
||||
free_table_share(), which may unload plugins, which can remove status
|
||||
variables and hence takes LOCK_status. Record this locking order here.
|
||||
*/
|
||||
mysql_mutex_record_order(&LOCK_open, &LOCK_status);
|
||||
oldest_unused_share= &end_of_unused_share;
|
||||
end_of_unused_share.prev= &oldest_unused_share;
|
||||
|
||||
|
|
@ -2662,7 +2670,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
|
|||
{
|
||||
DBUG_PRINT("error",
|
||||
("query_id: %lu server_id: %u pseudo_thread_id: %lu",
|
||||
(ulong) table->query_id, (uint) thd->server_id,
|
||||
(ulong) table->query_id, (uint) thd->variables.server_id,
|
||||
(ulong) thd->variables.pseudo_thread_id));
|
||||
my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias.c_ptr());
|
||||
DBUG_RETURN(TRUE);
|
||||
|
|
@ -5939,7 +5947,8 @@ TABLE *open_table_uncached(THD *thd, handlerton *hton,
|
|||
("table: '%s'.'%s' path: '%s' server_id: %u "
|
||||
"pseudo_thread_id: %lu",
|
||||
db, table_name, path,
|
||||
(uint) thd->server_id, (ulong) thd->variables.pseudo_thread_id));
|
||||
(uint) thd->variables.server_id,
|
||||
(ulong) thd->variables.pseudo_thread_id));
|
||||
|
||||
/* Create the cache_key for temporary tables */
|
||||
key_length= create_tmp_table_def_key(thd, cache_key, db, table_name);
|
||||
|
|
@ -9343,7 +9352,6 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order)
|
|||
List_iterator<Item_func_match> li(*(select_lex->ftfunc_list));
|
||||
Item_func_match *ifm;
|
||||
DBUG_PRINT("info",("Performing FULLTEXT search"));
|
||||
thd_proc_info(thd, "FULLTEXT initialization");
|
||||
|
||||
while ((ifm=li++))
|
||||
ifm->init_search(no_order);
|
||||
|
|
|
|||
|
|
@ -112,7 +112,8 @@ Key::Key(const Key &rhs, MEM_ROOT *mem_root)
|
|||
columns(rhs.columns, mem_root),
|
||||
name(rhs.name),
|
||||
option_list(rhs.option_list),
|
||||
generated(rhs.generated)
|
||||
generated(rhs.generated),
|
||||
create_if_not_exists(rhs.create_if_not_exists)
|
||||
{
|
||||
list_copy_and_replace_each_value(columns, mem_root);
|
||||
}
|
||||
|
|
@ -827,6 +828,7 @@ THD::THD()
|
|||
col_access=0;
|
||||
is_slave_error= thread_specific_used= FALSE;
|
||||
my_hash_clear(&handler_tables_hash);
|
||||
my_hash_clear(&ull_hash);
|
||||
tmp_table=0;
|
||||
cuted_fields= 0L;
|
||||
sent_row_count= 0L;
|
||||
|
|
@ -866,7 +868,6 @@ THD::THD()
|
|||
net.vio=0;
|
||||
net.buff= 0;
|
||||
client_capabilities= 0; // minimalistic client
|
||||
ull=0;
|
||||
system_thread= NON_SYSTEM_THREAD;
|
||||
cleanup_done= abort_on_warning= 0;
|
||||
peer_port= 0; // For SHOW PROCESSLIST
|
||||
|
|
@ -891,7 +892,7 @@ THD::THD()
|
|||
/* Variables with default values */
|
||||
proc_info="login";
|
||||
where= THD::DEFAULT_WHERE;
|
||||
server_id = ::server_id;
|
||||
variables.server_id = global_system_variables.server_id;
|
||||
slave_net = 0;
|
||||
command=COM_CONNECT;
|
||||
*scramble= '\0';
|
||||
|
|
@ -1400,8 +1401,6 @@ void THD::cleanup(void)
|
|||
if (global_read_lock.is_acquired())
|
||||
global_read_lock.unlock_global_read_lock(this);
|
||||
|
||||
/* All metadata locks must have been released by now. */
|
||||
DBUG_ASSERT(!mdl_context.has_locks());
|
||||
if (user_connect)
|
||||
{
|
||||
decrease_user_connections(user_connect);
|
||||
|
|
@ -1419,13 +1418,9 @@ void THD::cleanup(void)
|
|||
sp_cache_clear(&sp_proc_cache);
|
||||
sp_cache_clear(&sp_func_cache);
|
||||
|
||||
if (ull)
|
||||
{
|
||||
mysql_mutex_lock(&LOCK_user_locks);
|
||||
item_user_lock_release(ull);
|
||||
mysql_mutex_unlock(&LOCK_user_locks);
|
||||
ull= NULL;
|
||||
}
|
||||
mysql_ull_cleanup(this);
|
||||
/* All metadata locks must have been released by now. */
|
||||
DBUG_ASSERT(!mdl_context.has_locks());
|
||||
|
||||
apc_target.destroy();
|
||||
cleanup_done=1;
|
||||
|
|
@ -4001,6 +3996,15 @@ extern "C" unsigned long thd_get_thread_id(const MYSQL_THD thd)
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
Check if THD socket is still connected.
|
||||
*/
|
||||
extern "C" int thd_is_connected(MYSQL_THD thd)
|
||||
{
|
||||
return thd->is_connected();
|
||||
}
|
||||
|
||||
|
||||
#ifdef INNODB_COMPATIBILITY_HOOKS
|
||||
extern "C" const struct charset_info_st *thd_charset(MYSQL_THD thd)
|
||||
{
|
||||
|
|
@ -4321,6 +4325,8 @@ void THD::leave_locked_tables_mode()
|
|||
/* Also ensure that we don't release metadata locks for open HANDLERs. */
|
||||
if (handler_tables_hash.records)
|
||||
mysql_ha_set_explicit_lock_duration(this);
|
||||
if (ull_hash.records)
|
||||
mysql_ull_set_explicit_lock_duration(this);
|
||||
}
|
||||
locked_tables_mode= LTM_NONE;
|
||||
}
|
||||
|
|
@ -5097,7 +5103,7 @@ int THD::binlog_write_row(TABLE* table, bool is_trans,
|
|||
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,
|
||||
binlog_prepare_pending_rows_event(table, variables.server_id, cols, colcnt,
|
||||
len, is_trans,
|
||||
static_cast<Write_rows_log_event*>(0));
|
||||
|
||||
|
|
@ -5141,7 +5147,7 @@ int THD::binlog_update_row(TABLE* table, bool is_trans,
|
|||
#endif
|
||||
|
||||
Rows_log_event* const ev=
|
||||
binlog_prepare_pending_rows_event(table, server_id, cols, colcnt,
|
||||
binlog_prepare_pending_rows_event(table, variables.server_id, cols, colcnt,
|
||||
before_size + after_size, is_trans,
|
||||
static_cast<Update_rows_log_event*>(0));
|
||||
|
||||
|
|
@ -5172,7 +5178,7 @@ int THD::binlog_delete_row(TABLE* table, bool is_trans,
|
|||
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,
|
||||
binlog_prepare_pending_rows_event(table, variables.server_id, cols, colcnt,
|
||||
len, is_trans,
|
||||
static_cast<Delete_rows_log_event*>(0));
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ class Lex_input_stream;
|
|||
class Parser_state;
|
||||
class Rows_log_event;
|
||||
class Sroutine_hash_entry;
|
||||
class User_level_lock;
|
||||
class user_var_entry;
|
||||
|
||||
enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
|
||||
|
|
@ -223,8 +222,9 @@ public:
|
|||
enum drop_type {KEY, COLUMN };
|
||||
const char *name;
|
||||
enum drop_type type;
|
||||
Alter_drop(enum drop_type par_type,const char *par_name)
|
||||
:name(par_name), type(par_type) {}
|
||||
bool drop_if_exists;
|
||||
Alter_drop(enum drop_type par_type,const char *par_name, bool par_exists)
|
||||
:name(par_name), type(par_type), drop_if_exists(par_exists) {}
|
||||
/**
|
||||
Used to make a clone of this object for ALTER/CREATE TABLE
|
||||
@sa comment for Key_part_spec::clone
|
||||
|
|
@ -258,20 +258,23 @@ public:
|
|||
LEX_STRING name;
|
||||
engine_option_value *option_list;
|
||||
bool generated;
|
||||
bool create_if_not_exists;
|
||||
|
||||
Key(enum Keytype type_par, const LEX_STRING &name_arg,
|
||||
KEY_CREATE_INFO *key_info_arg,
|
||||
bool generated_arg, List<Key_part_spec> &cols,
|
||||
engine_option_value *create_opt)
|
||||
engine_option_value *create_opt, bool if_not_exists_opt)
|
||||
:type(type_par), key_create_info(*key_info_arg), columns(cols),
|
||||
name(name_arg), option_list(create_opt), generated(generated_arg)
|
||||
name(name_arg), option_list(create_opt), generated(generated_arg),
|
||||
create_if_not_exists(if_not_exists_opt)
|
||||
{}
|
||||
Key(enum Keytype type_par, const char *name_arg, size_t name_len_arg,
|
||||
KEY_CREATE_INFO *key_info_arg, bool generated_arg,
|
||||
List<Key_part_spec> &cols,
|
||||
engine_option_value *create_opt)
|
||||
engine_option_value *create_opt, bool if_not_exists_opt)
|
||||
:type(type_par), key_create_info(*key_info_arg), columns(cols),
|
||||
option_list(create_opt), generated(generated_arg)
|
||||
option_list(create_opt), generated(generated_arg),
|
||||
create_if_not_exists(if_not_exists_opt)
|
||||
{
|
||||
name.str= (char *)name_arg;
|
||||
name.length= name_len_arg;
|
||||
|
|
@ -302,8 +305,10 @@ public:
|
|||
uint delete_opt, update_opt, match_opt;
|
||||
Foreign_key(const LEX_STRING &name_arg, List<Key_part_spec> &cols,
|
||||
Table_ident *table, List<Key_part_spec> &ref_cols,
|
||||
uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg)
|
||||
:Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols, NULL),
|
||||
uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg,
|
||||
bool if_not_exists_opt)
|
||||
:Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols, NULL,
|
||||
if_not_exists_opt),
|
||||
ref_table(table), ref_columns(ref_cols),
|
||||
delete_opt(delete_opt_arg), update_opt(update_opt_arg),
|
||||
match_opt(match_opt_arg)
|
||||
|
|
@ -532,11 +537,18 @@ typedef struct system_variables
|
|||
ulong tx_isolation;
|
||||
ulong updatable_views_with_limit;
|
||||
int max_user_connections;
|
||||
ulong server_id;
|
||||
/**
|
||||
In slave thread we need to know in behalf of which
|
||||
thread the query is being run to replicate temp tables properly
|
||||
*/
|
||||
my_thread_id pseudo_thread_id;
|
||||
/**
|
||||
When replicating an event group with GTID, keep these values around so
|
||||
slave binlog can receive the same GTID as the original.
|
||||
*/
|
||||
uint32 gtid_domain_id;
|
||||
uint64 gtid_seq_no;
|
||||
/**
|
||||
Place holders to store Multi-source variables in sys_var.cc during
|
||||
update and show of variables.
|
||||
|
|
@ -1685,11 +1697,11 @@ public:
|
|||
|
||||
HASH handler_tables_hash;
|
||||
/*
|
||||
One thread can hold up to one named user-level lock. This variable
|
||||
points to a lock object if the lock is present. See item_func.cc and
|
||||
A thread can hold named user-level locks. This variable
|
||||
contains granted tickets if a lock is present. See item_func.cc and
|
||||
chapter 'Miscellaneous functions', for functions GET_LOCK, RELEASE_LOCK.
|
||||
*/
|
||||
User_level_lock *ull;
|
||||
HASH ull_hash;
|
||||
#ifndef DBUG_OFF
|
||||
uint dbug_sentry; // watch out for memory corruption
|
||||
#endif
|
||||
|
|
@ -1699,7 +1711,6 @@ public:
|
|||
first byte of the packet in do_command()
|
||||
*/
|
||||
enum enum_server_command command;
|
||||
uint32 server_id;
|
||||
uint32 file_id; // for LOAD DATA INFILE
|
||||
/* remote (peer) port */
|
||||
uint16 peer_port;
|
||||
|
|
@ -1776,7 +1787,7 @@ public:
|
|||
MY_BITMAP const* cols, size_t colcnt,
|
||||
const uchar *old_data, const uchar *new_data);
|
||||
|
||||
void set_server_id(uint32 sid) { server_id = sid; }
|
||||
void set_server_id(uint32 sid) { variables.server_id = sid; }
|
||||
|
||||
/*
|
||||
Member functions to handle pending event for row-level logging.
|
||||
|
|
|
|||
|
|
@ -507,6 +507,7 @@ void lex_start(THD *thd)
|
|||
lex->expr_allows_subselect= TRUE;
|
||||
lex->use_only_table_context= FALSE;
|
||||
lex->parse_vcol_expr= FALSE;
|
||||
lex->check_exists= FALSE;
|
||||
lex->verbose= 0;
|
||||
|
||||
lex->name.str= 0;
|
||||
|
|
|
|||
|
|
@ -299,7 +299,8 @@ struct LEX_MASTER_INFO
|
|||
changed variable or if it should be left at old value
|
||||
*/
|
||||
enum {LEX_MI_UNCHANGED, LEX_MI_DISABLE, LEX_MI_ENABLE}
|
||||
ssl, ssl_verify_server_cert, heartbeat_opt, repl_ignore_server_ids_opt;
|
||||
ssl, ssl_verify_server_cert, heartbeat_opt, repl_ignore_server_ids_opt,
|
||||
use_gtid_opt;
|
||||
|
||||
void init()
|
||||
{
|
||||
|
|
@ -315,7 +316,7 @@ struct LEX_MASTER_INFO
|
|||
pos= relay_log_pos= server_id= port= connect_retry= 0;
|
||||
heartbeat_period= 0;
|
||||
ssl= ssl_verify_server_cert= heartbeat_opt=
|
||||
repl_ignore_server_ids_opt= LEX_MI_UNCHANGED;
|
||||
repl_ignore_server_ids_opt= use_gtid_opt= LEX_MI_UNCHANGED;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -2509,7 +2510,8 @@ struct LEX: public Query_tables_list
|
|||
uint16 create_view_algorithm;
|
||||
uint8 create_view_check;
|
||||
uint8 context_analysis_only;
|
||||
bool drop_if_exists, drop_temporary, local_file, one_shot_set;
|
||||
bool drop_temporary, local_file, one_shot_set;
|
||||
bool check_exists;
|
||||
bool autocommit;
|
||||
bool verbose, no_write_to_binlog;
|
||||
|
||||
|
|
|
|||
|
|
@ -1265,10 +1265,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
|||
/* TODO: The following has to be changed to an 8 byte integer */
|
||||
pos = uint4korr(packet);
|
||||
flags = uint2korr(packet + 4);
|
||||
thd->server_id=0; /* avoid suicide */
|
||||
thd->variables.server_id=0; /* avoid suicide */
|
||||
if ((slave_server_id= uint4korr(packet+6))) // mysqlbinlog.server_id==0
|
||||
kill_zombie_dump_threads(slave_server_id);
|
||||
thd->server_id = slave_server_id;
|
||||
thd->variables.server_id = slave_server_id;
|
||||
|
||||
general_log_print(thd, command, "Log: '%s' Pos: %ld", packet+10,
|
||||
(long) pos);
|
||||
|
|
@ -2084,7 +2084,7 @@ mysql_execute_command(THD *thd)
|
|||
if (!(lex->sql_command == SQLCOM_UPDATE_MULTI) &&
|
||||
!(lex->sql_command == SQLCOM_SET_OPTION) &&
|
||||
!(lex->sql_command == SQLCOM_DROP_TABLE &&
|
||||
lex->drop_temporary && lex->drop_if_exists) &&
|
||||
lex->drop_temporary && lex->check_exists) &&
|
||||
all_tables_not_ok(thd, all_tables))
|
||||
{
|
||||
/* we warn the slave SQL thread */
|
||||
|
|
@ -3285,7 +3285,7 @@ end_with_restore_list:
|
|||
thd->variables.option_bits|= OPTION_KEEP_LOG;
|
||||
}
|
||||
/* DDL and binlog write order are protected by metadata locks. */
|
||||
res= mysql_rm_table(thd, first_table, lex->drop_if_exists,
|
||||
res= mysql_rm_table(thd, first_table, lex->check_exists,
|
||||
lex->drop_temporary);
|
||||
}
|
||||
break;
|
||||
|
|
@ -3499,7 +3499,7 @@ end_with_restore_list:
|
|||
#endif
|
||||
if (check_access(thd, DROP_ACL, lex->name.str, NULL, NULL, 1, 0))
|
||||
break;
|
||||
res= mysql_rm_db(thd, lex->name.str, lex->drop_if_exists, 0);
|
||||
res= mysql_rm_db(thd, lex->name.str, lex->check_exists, 0);
|
||||
break;
|
||||
}
|
||||
case SQLCOM_ALTER_DB_UPGRADE:
|
||||
|
|
@ -3627,7 +3627,7 @@ end_with_restore_list:
|
|||
case SQLCOM_DROP_EVENT:
|
||||
if (!(res= Events::drop_event(thd,
|
||||
lex->spname->m_db, lex->spname->m_name,
|
||||
lex->drop_if_exists)))
|
||||
lex->check_exists)))
|
||||
my_ok(thd);
|
||||
break;
|
||||
#else
|
||||
|
|
@ -4314,7 +4314,7 @@ create_sp_error:
|
|||
|
||||
if (lex->spname->m_db.str == NULL)
|
||||
{
|
||||
if (lex->drop_if_exists)
|
||||
if (lex->check_exists)
|
||||
{
|
||||
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
|
||||
|
|
@ -4383,7 +4383,7 @@ create_sp_error:
|
|||
my_ok(thd);
|
||||
break;
|
||||
case SP_KEY_NOT_FOUND:
|
||||
if (lex->drop_if_exists)
|
||||
if (lex->check_exists)
|
||||
{
|
||||
res= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
|
||||
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
|
|
@ -4596,7 +4596,7 @@ create_sp_error:
|
|||
|
||||
if ((err_code= drop_server(thd, &lex->server_options)))
|
||||
{
|
||||
if (! lex->drop_if_exists && err_code == ER_FOREIGN_SERVER_DOESNT_EXIST)
|
||||
if (! lex->check_exists && err_code == ER_FOREIGN_SERVER_DOESNT_EXIST)
|
||||
{
|
||||
DBUG_PRINT("info", ("problem dropping server %s",
|
||||
lex->server_options.server_name));
|
||||
|
|
@ -6016,7 +6016,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
|
|||
lex->col_list.push_back(new Key_part_spec(*field_name, 0));
|
||||
key= new Key(Key::PRIMARY, null_lex_str,
|
||||
&default_key_create_info,
|
||||
0, lex->col_list, NULL);
|
||||
0, lex->col_list, NULL, lex->check_exists);
|
||||
lex->alter_info.key_list.push_back(key);
|
||||
lex->col_list.empty();
|
||||
}
|
||||
|
|
@ -6026,7 +6026,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
|
|||
lex->col_list.push_back(new Key_part_spec(*field_name, 0));
|
||||
key= new Key(Key::UNIQUE, null_lex_str,
|
||||
&default_key_create_info, 0,
|
||||
lex->col_list, NULL);
|
||||
lex->col_list, NULL, lex->check_exists);
|
||||
lex->alter_info.key_list.push_back(key);
|
||||
lex->col_list.empty();
|
||||
}
|
||||
|
|
@ -6078,7 +6078,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type,
|
|||
new_field->init(thd, field_name->str, type, length, decimals, type_modifier,
|
||||
default_value, on_update_value, comment, change,
|
||||
interval_list, cs, uint_geom_type, vcol_info,
|
||||
create_options))
|
||||
create_options, lex->check_exists))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
lex->alter_info.create_list.push_back(new_field);
|
||||
|
|
|
|||
|
|
@ -205,6 +205,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long options,
|
|||
DBUG_ASSERT(!thd || thd->locked_tables_mode ||
|
||||
!thd->mdl_context.has_locks() ||
|
||||
thd->handler_tables_hash.records ||
|
||||
thd->ull_hash.records ||
|
||||
thd->global_read_lock.is_acquired());
|
||||
|
||||
/*
|
||||
|
|
|
|||
995
sql/sql_repl.cc
995
sql/sql_repl.cc
File diff suppressed because it is too large
Load diff
|
|
@ -65,6 +65,14 @@ 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);
|
||||
|
||||
extern PSI_mutex_key key_LOCK_slave_state, key_LOCK_binlog_state;
|
||||
void rpl_init_gtid_slave_state();
|
||||
void rpl_deinit_gtid_slave_state();
|
||||
int gtid_state_from_binlog_pos(const char *name, uint32 pos, String *out_str);
|
||||
int rpl_append_gtid_state(String *dest, bool use_binlog);
|
||||
bool rpl_gtid_pos_check(char *str, size_t len);
|
||||
bool rpl_gtid_pos_update(THD *thd, char *str, size_t len);
|
||||
|
||||
#endif /* HAVE_REPLICATION */
|
||||
|
||||
#endif /* SQL_REPL_INCLUDED */
|
||||
|
|
|
|||
|
|
@ -3877,6 +3877,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
|
|||
DBUG_RETURN(TRUE);
|
||||
|
||||
join->join_tab=stat;
|
||||
join->top_join_tab_count= table_count;
|
||||
join->map2table=stat_ref;
|
||||
join->table= table_vector;
|
||||
join->const_tables=const_count;
|
||||
|
|
@ -3924,6 +3925,8 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
|
|||
if (join->choose_subquery_plan(all_table_map & ~join->const_table_map))
|
||||
goto error;
|
||||
|
||||
DEBUG_SYNC(join->thd, "inside_make_join_statistics");
|
||||
|
||||
/* Generate an execution plan from the found optimal join order. */
|
||||
DBUG_RETURN(join->thd->check_killed() || get_best_combination(join));
|
||||
|
||||
|
|
@ -10854,6 +10857,10 @@ bool JOIN_TAB::preread_init()
|
|||
dbug_serve_apcs(join->thd, 1);
|
||||
);
|
||||
|
||||
/* init ftfuns for just initialized derived table */
|
||||
if (table->fulltext_searched)
|
||||
init_ftfuncs(join->thd, join->select_lex, test(join->order));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7451,20 +7451,20 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
|
|||
break;
|
||||
case MYSQL_TYPE_DATE:
|
||||
if (!(item=new Item_return_date_time(fields_info->field_name,
|
||||
MAX_DATE_WIDTH,
|
||||
strlen(fields_info->field_name),
|
||||
fields_info->field_type)))
|
||||
DBUG_RETURN(0);
|
||||
break;
|
||||
case MYSQL_TYPE_TIME:
|
||||
if (!(item=new Item_return_date_time(fields_info->field_name,
|
||||
MAX_TIME_FULL_WIDTH,
|
||||
strlen(fields_info->field_name),
|
||||
fields_info->field_type)))
|
||||
DBUG_RETURN(0);
|
||||
break;
|
||||
case MYSQL_TYPE_TIMESTAMP:
|
||||
case MYSQL_TYPE_DATETIME:
|
||||
if (!(item=new Item_return_date_time(fields_info->field_name,
|
||||
MAX_DATETIME_WIDTH,
|
||||
strlen(fields_info->field_name),
|
||||
fields_info->field_type)))
|
||||
DBUG_RETURN(0);
|
||||
break;
|
||||
|
|
@ -7837,16 +7837,22 @@ int make_schema_select(THD *thd, SELECT_LEX *sel,
|
|||
We have to make non const db_name & table_name
|
||||
because of lower_case_table_names
|
||||
*/
|
||||
thd->make_lex_string(&db, INFORMATION_SCHEMA_NAME.str,
|
||||
INFORMATION_SCHEMA_NAME.length);
|
||||
thd->make_lex_string(&table, schema_table->table_name,
|
||||
strlen(schema_table->table_name));
|
||||
if (schema_table->old_format(thd, schema_table) || /* Handle old syntax */
|
||||
!sel->add_table_to_list(thd, new Table_ident(thd, db, table, 0),
|
||||
0, 0, TL_READ, MDL_SHARED_READ))
|
||||
{
|
||||
if (!thd->make_lex_string(&db, INFORMATION_SCHEMA_NAME.str,
|
||||
INFORMATION_SCHEMA_NAME.length))
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
if (!thd->make_lex_string(&table, schema_table->table_name,
|
||||
strlen(schema_table->table_name)))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
if (schema_table->old_format(thd, schema_table))
|
||||
|
||||
DBUG_RETURN(1);
|
||||
|
||||
if (!sel->add_table_to_list(thd, new Table_ident(thd, db, table, 0),
|
||||
0, 0, TL_READ, MDL_SHARED_READ))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
|
|
|||
253
sql/sql_table.cc
253
sql/sql_table.cc
|
|
@ -4953,6 +4953,246 @@ is_index_maintenance_unique (TABLE *table, Alter_info *alter_info)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
Preparation for table creation
|
||||
|
||||
SYNOPSIS
|
||||
handle_if_exists_option()
|
||||
thd Thread object.
|
||||
table The altered table.
|
||||
alter_info List of columns and indexes to create
|
||||
|
||||
DESCRIPTION
|
||||
Looks for the IF [NOT] EXISTS options, checks the states and remove items
|
||||
from the list if existing found.
|
||||
|
||||
RETURN VALUES
|
||||
NONE
|
||||
*/
|
||||
|
||||
static void
|
||||
handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info)
|
||||
{
|
||||
Field **f_ptr;
|
||||
DBUG_ENTER("handle_if_exists_option");
|
||||
|
||||
/* Handle ADD COLUMN IF NOT EXISTS. */
|
||||
{
|
||||
List_iterator<Create_field> it(alter_info->create_list);
|
||||
Create_field *sql_field;
|
||||
|
||||
while ((sql_field=it++))
|
||||
{
|
||||
if (!sql_field->create_if_not_exists || sql_field->change)
|
||||
continue;
|
||||
/*
|
||||
If there is a field with the same name in the table already,
|
||||
remove the sql_field from the list.
|
||||
*/
|
||||
for (f_ptr=table->field; *f_ptr; f_ptr++)
|
||||
{
|
||||
if (my_strcasecmp(system_charset_info,
|
||||
sql_field->field_name, (*f_ptr)->field_name) == 0)
|
||||
{
|
||||
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_DUP_FIELDNAME, ER(ER_DUP_FIELDNAME),
|
||||
sql_field->field_name);
|
||||
it.remove();
|
||||
if (alter_info->create_list.is_empty())
|
||||
{
|
||||
alter_info->flags&= ~ALTER_ADD_COLUMN;
|
||||
if (alter_info->key_list.is_empty())
|
||||
alter_info->flags&= ~ALTER_ADD_INDEX;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle MODIFY COLUMN IF EXISTS. */
|
||||
{
|
||||
List_iterator<Create_field> it(alter_info->create_list);
|
||||
Create_field *sql_field;
|
||||
|
||||
while ((sql_field=it++))
|
||||
{
|
||||
if (!sql_field->create_if_not_exists || !sql_field->change)
|
||||
continue;
|
||||
/*
|
||||
If there is NO field with the same name in the table already,
|
||||
remove the sql_field from the list.
|
||||
*/
|
||||
for (f_ptr=table->field; *f_ptr; f_ptr++)
|
||||
{
|
||||
if (my_strcasecmp(system_charset_info,
|
||||
sql_field->field_name, (*f_ptr)->field_name) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*f_ptr == NULL)
|
||||
{
|
||||
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_BAD_FIELD_ERROR, ER(ER_BAD_FIELD_ERROR),
|
||||
sql_field->change, table->s->table_name.str);
|
||||
it.remove();
|
||||
if (alter_info->create_list.is_empty())
|
||||
{
|
||||
alter_info->flags&= ~(ALTER_ADD_COLUMN | ALTER_CHANGE_COLUMN);
|
||||
if (alter_info->key_list.is_empty())
|
||||
alter_info->flags&= ~ALTER_ADD_INDEX;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle DROP COLUMN/KEY IF EXISTS. */
|
||||
{
|
||||
List_iterator<Alter_drop> drop_it(alter_info->drop_list);
|
||||
Alter_drop *drop;
|
||||
bool remove_drop;
|
||||
while ((drop= drop_it++))
|
||||
{
|
||||
if (!drop->drop_if_exists)
|
||||
continue;
|
||||
remove_drop= TRUE;
|
||||
if (drop->type == Alter_drop::COLUMN)
|
||||
{
|
||||
/*
|
||||
If there is NO field with that name in the table,
|
||||
remove the 'drop' from the list.
|
||||
*/
|
||||
for (f_ptr=table->field; *f_ptr; f_ptr++)
|
||||
{
|
||||
if (my_strcasecmp(system_charset_info,
|
||||
drop->name, (*f_ptr)->field_name) == 0)
|
||||
{
|
||||
remove_drop= FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else /* Alter_drop::KEY */
|
||||
{
|
||||
uint n_key;
|
||||
for (n_key=0; n_key < table->s->keys; n_key++)
|
||||
{
|
||||
if (my_strcasecmp(system_charset_info,
|
||||
drop->name, table->key_info[n_key].name) == 0)
|
||||
{
|
||||
remove_drop= FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (remove_drop)
|
||||
{
|
||||
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_CANT_DROP_FIELD_OR_KEY, ER(ER_CANT_DROP_FIELD_OR_KEY),
|
||||
drop->name);
|
||||
drop_it.remove();
|
||||
if (alter_info->drop_list.is_empty())
|
||||
alter_info->flags&= ~(ALTER_DROP_COLUMN | ALTER_DROP_INDEX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ALTER TABLE ADD KEY IF NOT EXISTS */
|
||||
/* ALTER TABLE ADD FOREIGN KEY IF NOT EXISTS */
|
||||
{
|
||||
Key *key;
|
||||
List_iterator<Key> key_it(alter_info->key_list);
|
||||
uint n_key;
|
||||
while ((key=key_it++))
|
||||
{
|
||||
if (!key->create_if_not_exists)
|
||||
continue;
|
||||
for (n_key=0; n_key < table->s->keys; n_key++)
|
||||
{
|
||||
if (my_strcasecmp(system_charset_info,
|
||||
key->name.str, table->key_info[n_key].name) == 0)
|
||||
{
|
||||
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_DUP_KEYNAME, ER(ER_DUP_KEYNAME), key->name.str);
|
||||
key_it.remove();
|
||||
if (key->type == Key::FOREIGN_KEY)
|
||||
{
|
||||
/* ADD FOREIGN KEY appends two items. */
|
||||
key_it.remove();
|
||||
}
|
||||
if (alter_info->key_list.is_empty())
|
||||
alter_info->flags&= ~ALTER_ADD_INDEX;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
partition_info *tab_part_info= table->part_info;
|
||||
if (tab_part_info && thd->lex->check_exists)
|
||||
{
|
||||
/* ALTER TABLE ADD PARTITION IF NOT EXISTS */
|
||||
if (alter_info->flags & ALTER_ADD_PARTITION)
|
||||
{
|
||||
partition_info *alt_part_info= thd->lex->part_info;
|
||||
if (alt_part_info)
|
||||
{
|
||||
List_iterator<partition_element> new_part_it(alt_part_info->partitions);
|
||||
partition_element *pe;
|
||||
while ((pe= new_part_it++))
|
||||
{
|
||||
if (!tab_part_info->has_unique_name(pe))
|
||||
{
|
||||
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_SAME_NAME_PARTITION, ER(ER_SAME_NAME_PARTITION),
|
||||
pe->partition_name);
|
||||
alter_info->flags&= ~ALTER_ADD_PARTITION;
|
||||
thd->lex->part_info= NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* ALTER TABLE DROP PARTITION IF EXISTS */
|
||||
if (alter_info->flags & ALTER_DROP_PARTITION)
|
||||
{
|
||||
List_iterator<char> names_it(alter_info->partition_names);
|
||||
char *name;
|
||||
|
||||
while ((name= names_it++))
|
||||
{
|
||||
List_iterator<partition_element> part_it(tab_part_info->partitions);
|
||||
partition_element *part_elem;
|
||||
while ((part_elem= part_it++))
|
||||
{
|
||||
if (my_strcasecmp(system_charset_info,
|
||||
part_elem->partition_name, name) == 0)
|
||||
break;
|
||||
}
|
||||
if (!part_elem)
|
||||
{
|
||||
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_DROP_PARTITION_NON_EXISTENT,
|
||||
ER(ER_DROP_PARTITION_NON_EXISTENT), "DROP");
|
||||
names_it.remove();
|
||||
}
|
||||
}
|
||||
if (alter_info->partition_names.elements == 0)
|
||||
alter_info->flags&= ~ALTER_DROP_PARTITION;
|
||||
}
|
||||
}
|
||||
#endif /*WITH_PARTITION_STORAGE_ENGINE*/
|
||||
|
||||
/* Clear the ALTER_FOREIGN_KEY flag if nothing other than that set. */
|
||||
if (alter_info->flags == ALTER_FOREIGN_KEY)
|
||||
alter_info->flags= 0;
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
SYNOPSIS
|
||||
mysql_compare_tables()
|
||||
|
|
@ -5873,7 +6113,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table,
|
|||
key= new Key(key_type, key_name, strlen(key_name),
|
||||
&key_create_info,
|
||||
test(key_info->flags & HA_GENERATED_KEY),
|
||||
key_parts, key_info->option_list);
|
||||
key_parts, key_info->option_list, FALSE);
|
||||
new_key_list.push_back(key);
|
||||
}
|
||||
}
|
||||
|
|
@ -6386,6 +6626,17 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
|
|||
DBUG_RETURN(error);
|
||||
}
|
||||
|
||||
handle_if_exists_options(thd, table, alter_info);
|
||||
|
||||
/* Look if we have to do anything at all. */
|
||||
/* Normally ALTER can become NOOP only after handling */
|
||||
/* the IF (NOT) EXISTS options. */
|
||||
if (alter_info->flags == 0)
|
||||
{
|
||||
copied= deleted= 0;
|
||||
goto end_temporary;
|
||||
}
|
||||
|
||||
/* We have to do full alter table. */
|
||||
|
||||
#ifdef WITH_PARTITION_STORAGE_ENGINE
|
||||
|
|
|
|||
|
|
@ -443,7 +443,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
|
|||
|
||||
if (!create)
|
||||
{
|
||||
bool if_exists= thd->lex->drop_if_exists;
|
||||
bool if_exists= thd->lex->check_exists;
|
||||
|
||||
/*
|
||||
Protect the query table list from the temporary and potentially
|
||||
|
|
|
|||
|
|
@ -1672,7 +1672,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
|
|||
{
|
||||
char name[FN_REFLEN];
|
||||
my_snprintf(name, sizeof(name), "%s.%s", view->db, view->table_name);
|
||||
if (thd->lex->drop_if_exists)
|
||||
if (thd->lex->check_exists)
|
||||
{
|
||||
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||
ER_BAD_TABLE_ERROR, ER(ER_BAD_TABLE_ERROR),
|
||||
|
|
|
|||
164
sql/sql_yacc.yy
164
sql/sql_yacc.yy
|
|
@ -722,7 +722,7 @@ static bool add_create_index (LEX *lex, Key::Keytype type,
|
|||
{
|
||||
Key *key;
|
||||
key= new Key(type, name, info ? info : &lex->key_create_info, generated,
|
||||
lex->col_list, lex->option_list);
|
||||
lex->col_list, lex->option_list, lex->check_exists);
|
||||
if (key == NULL)
|
||||
return TRUE;
|
||||
|
||||
|
|
@ -835,6 +835,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
%token AUTHORS_SYM
|
||||
%token AUTOEXTEND_SIZE_SYM
|
||||
%token AUTO_INC
|
||||
%token AUTO_SYM
|
||||
%token AVG_ROW_LENGTH
|
||||
%token AVG_SYM /* SQL-2003-N */
|
||||
%token BACKUP_SYM
|
||||
|
|
@ -1094,6 +1095,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
%token LOW_PRIORITY
|
||||
%token LT /* OPERATOR */
|
||||
%token MASTER_CONNECT_RETRY_SYM
|
||||
%token MASTER_USE_GTID_SYM
|
||||
%token MASTER_HOST_SYM
|
||||
%token MASTER_LOG_FILE_SYM
|
||||
%token MASTER_LOG_POS_SYM
|
||||
|
|
@ -1454,7 +1456,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
IDENT_sys TEXT_STRING_sys TEXT_STRING_literal
|
||||
NCHAR_STRING opt_component key_cache_name
|
||||
sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty
|
||||
opt_constraint constraint opt_ident
|
||||
opt_constraint constraint opt_ident opt_if_not_exists_ident
|
||||
|
||||
%type <lex_str_ptr>
|
||||
opt_table_alias
|
||||
|
|
@ -1471,7 +1473,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
|
||||
%type <num>
|
||||
type type_with_opt_collate int_type real_type order_dir lock_option
|
||||
udf_type if_exists opt_local opt_table_options table_options
|
||||
udf_type opt_if_exists opt_local opt_table_options table_options
|
||||
table_option opt_if_not_exists opt_no_write_to_binlog
|
||||
opt_temporary all_or_any opt_distinct
|
||||
opt_ignore_leaves fulltext_options spatial_type union_option
|
||||
|
|
@ -2062,6 +2064,16 @@ master_file_def:
|
|||
/* Adjust if < BIN_LOG_HEADER_SIZE (same comment as Lex->mi.pos) */
|
||||
Lex->mi.relay_log_pos = max(BIN_LOG_HEADER_SIZE, Lex->mi.relay_log_pos);
|
||||
}
|
||||
| MASTER_USE_GTID_SYM EQ ulong_num
|
||||
{
|
||||
if (Lex->mi.use_gtid_opt != LEX_MASTER_INFO::LEX_MI_UNCHANGED)
|
||||
{
|
||||
my_error(ER_DUP_ARGUMENT, MYF(0), "MASTER_use_gtid");
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
Lex->mi.use_gtid_opt= $3 ?
|
||||
LEX_MASTER_INFO::LEX_MI_ENABLE : LEX_MASTER_INFO::LEX_MI_DISABLE;
|
||||
}
|
||||
;
|
||||
|
||||
optional_connection_name:
|
||||
|
|
@ -2131,36 +2143,36 @@ create:
|
|||
}
|
||||
create_table_set_open_action_and_adjust_tables(lex);
|
||||
}
|
||||
| CREATE opt_unique INDEX_SYM ident key_alg ON table_ident
|
||||
| CREATE opt_unique INDEX_SYM opt_if_not_exists ident key_alg ON table_ident
|
||||
{
|
||||
if (add_create_index_prepare(Lex, $7))
|
||||
if (add_create_index_prepare(Lex, $8))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
'(' key_list ')' normal_key_options
|
||||
{
|
||||
if (add_create_index(Lex, $2, $4))
|
||||
if (add_create_index(Lex, $2, $5))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| CREATE fulltext INDEX_SYM ident init_key_options ON
|
||||
| CREATE fulltext INDEX_SYM opt_if_not_exists ident init_key_options ON
|
||||
table_ident
|
||||
{
|
||||
if (add_create_index_prepare(Lex, $7))
|
||||
if (add_create_index_prepare(Lex, $8))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
'(' key_list ')' fulltext_key_options
|
||||
{
|
||||
if (add_create_index(Lex, $2, $4))
|
||||
if (add_create_index(Lex, $2, $5))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| CREATE spatial INDEX_SYM ident init_key_options ON
|
||||
| CREATE spatial INDEX_SYM opt_if_not_exists ident init_key_options ON
|
||||
table_ident
|
||||
{
|
||||
if (add_create_index_prepare(Lex, $7))
|
||||
if (add_create_index_prepare(Lex, $8))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
'(' key_list ')' spatial_key_options
|
||||
{
|
||||
if (add_create_index(Lex, $2, $4))
|
||||
if (add_create_index(Lex, $2, $5))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| CREATE DATABASE opt_if_not_exists ident
|
||||
|
|
@ -5079,9 +5091,17 @@ table_option:
|
|||
;
|
||||
|
||||
opt_if_not_exists:
|
||||
/* empty */ { $$= 0; }
|
||||
| IF not EXISTS { $$=HA_LEX_CREATE_IF_NOT_EXISTS; }
|
||||
;
|
||||
/* empty */
|
||||
{
|
||||
Lex->check_exists= FALSE;
|
||||
$$= 0;
|
||||
}
|
||||
| IF not EXISTS
|
||||
{
|
||||
Lex->check_exists= TRUE;
|
||||
$$=HA_LEX_CREATE_IF_NOT_EXISTS;
|
||||
}
|
||||
;
|
||||
|
||||
opt_create_table_options:
|
||||
/* empty */
|
||||
|
|
@ -5399,14 +5419,14 @@ column_def:
|
|||
;
|
||||
|
||||
key_def:
|
||||
normal_key_type opt_ident key_alg '(' key_list ')'
|
||||
normal_key_type opt_if_not_exists_ident key_alg '(' key_list ')'
|
||||
{ Lex->option_list= NULL; }
|
||||
normal_key_options
|
||||
{
|
||||
if (add_create_index (Lex, $1, $2))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| fulltext opt_key_or_index opt_ident init_key_options
|
||||
| fulltext opt_key_or_index opt_if_not_exists_ident init_key_options
|
||||
'(' key_list ')'
|
||||
{ Lex->option_list= NULL; }
|
||||
fulltext_key_options
|
||||
|
|
@ -5414,7 +5434,7 @@ key_def:
|
|||
if (add_create_index (Lex, $1, $3))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| spatial opt_key_or_index opt_ident init_key_options
|
||||
| spatial opt_key_or_index opt_if_not_exists_ident init_key_options
|
||||
'(' key_list ')'
|
||||
{ Lex->option_list= NULL; }
|
||||
spatial_key_options
|
||||
|
|
@ -5430,7 +5450,7 @@ key_def:
|
|||
if (add_create_index (Lex, $2, $3.str ? $3 : $1))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| opt_constraint FOREIGN KEY_SYM opt_ident '(' key_list ')' references
|
||||
| opt_constraint FOREIGN KEY_SYM opt_if_not_exists_ident '(' key_list ')' references
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
Key *key= new Foreign_key($4.str ? $4 : $1, lex->col_list,
|
||||
|
|
@ -5438,7 +5458,8 @@ key_def:
|
|||
lex->ref_list,
|
||||
lex->fk_delete_opt,
|
||||
lex->fk_update_opt,
|
||||
lex->fk_match_option);
|
||||
lex->fk_match_option,
|
||||
lex->check_exists);
|
||||
if (key == NULL)
|
||||
MYSQL_YYABORT;
|
||||
lex->alter_info.key_list.push_back(key);
|
||||
|
|
@ -6407,6 +6428,18 @@ opt_ident:
|
|||
| field_ident { $$= $1; }
|
||||
;
|
||||
|
||||
opt_if_not_exists_ident:
|
||||
opt_if_not_exists opt_ident
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
if (lex->check_exists && lex->sql_command != SQLCOM_ALTER_TABLE)
|
||||
{
|
||||
my_parse_error(ER(ER_SYNTAX_ERROR));
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
$$= $2;
|
||||
};
|
||||
|
||||
opt_component:
|
||||
/* empty */ { $$= null_lex_str; }
|
||||
| '.' ident { $$= $2; }
|
||||
|
|
@ -6663,7 +6696,7 @@ alter_commands:
|
|||
new table and so forth.
|
||||
*/
|
||||
| add_partition_rule
|
||||
| DROP PARTITION_SYM alt_part_name_list
|
||||
| DROP PARTITION_SYM opt_if_exists alt_part_name_list
|
||||
{
|
||||
Lex->alter_info.flags|= ALTER_DROP_PARTITION;
|
||||
}
|
||||
|
|
@ -6764,7 +6797,7 @@ all_or_alt_part_name_list:
|
|||
;
|
||||
|
||||
add_partition_rule:
|
||||
ADD PARTITION_SYM opt_no_write_to_binlog
|
||||
ADD PARTITION_SYM opt_if_not_exists opt_no_write_to_binlog
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
lex->part_info= new partition_info();
|
||||
|
|
@ -6774,7 +6807,7 @@ add_partition_rule:
|
|||
MYSQL_YYABORT;
|
||||
}
|
||||
lex->alter_info.flags|= ALTER_ADD_PARTITION;
|
||||
lex->no_write_to_binlog= $3;
|
||||
lex->no_write_to_binlog= $4;
|
||||
}
|
||||
add_part_extra
|
||||
{}
|
||||
|
|
@ -6850,7 +6883,7 @@ alter_list:
|
|||
;
|
||||
|
||||
add_column:
|
||||
ADD opt_column
|
||||
ADD opt_column opt_if_not_exists
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
lex->change=0;
|
||||
|
|
@ -6872,10 +6905,10 @@ alter_list_item:
|
|||
{
|
||||
Lex->alter_info.flags|= ALTER_ADD_COLUMN | ALTER_ADD_INDEX;
|
||||
}
|
||||
| CHANGE opt_column field_ident
|
||||
| CHANGE opt_column opt_if_exists field_ident
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
lex->change= $3.str;
|
||||
lex->change= $4.str;
|
||||
lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
|
||||
lex->option_list= NULL;
|
||||
}
|
||||
|
|
@ -6883,7 +6916,7 @@ alter_list_item:
|
|||
{
|
||||
Lex->create_last_non_select_table= Lex->last_table();
|
||||
}
|
||||
| MODIFY_SYM opt_column field_ident
|
||||
| MODIFY_SYM opt_column opt_if_exists field_ident
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
lex->length=lex->dec=0; lex->type=0;
|
||||
|
|
@ -6897,12 +6930,12 @@ alter_list_item:
|
|||
field_def
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
if (add_field_to_list(lex->thd,&$3,
|
||||
(enum enum_field_types) $5,
|
||||
if (add_field_to_list(lex->thd,&$4,
|
||||
(enum enum_field_types) $6,
|
||||
lex->length,lex->dec,lex->type,
|
||||
lex->default_value, lex->on_update_value,
|
||||
&lex->comment,
|
||||
$3.str, &lex->interval_list, lex->charset,
|
||||
$4.str, &lex->interval_list, lex->charset,
|
||||
lex->uint_geom_type,
|
||||
lex->vcol_info, lex->option_list))
|
||||
MYSQL_YYABORT;
|
||||
|
|
@ -6911,32 +6944,33 @@ alter_list_item:
|
|||
{
|
||||
Lex->create_last_non_select_table= Lex->last_table();
|
||||
}
|
||||
| DROP opt_column field_ident opt_restrict
|
||||
| DROP opt_column opt_if_exists field_ident opt_restrict
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
Alter_drop *ad= new Alter_drop(Alter_drop::COLUMN, $3.str);
|
||||
Alter_drop *ad= new Alter_drop(Alter_drop::COLUMN, $4.str, $3);
|
||||
if (ad == NULL)
|
||||
MYSQL_YYABORT;
|
||||
lex->alter_info.drop_list.push_back(ad);
|
||||
lex->alter_info.flags|= ALTER_DROP_COLUMN;
|
||||
}
|
||||
| DROP FOREIGN KEY_SYM opt_ident
|
||||
| DROP FOREIGN KEY_SYM opt_if_exists opt_ident
|
||||
{
|
||||
Lex->alter_info.flags|= ALTER_DROP_INDEX | ALTER_FOREIGN_KEY;
|
||||
}
|
||||
| DROP PRIMARY_SYM KEY_SYM
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
Alter_drop *ad= new Alter_drop(Alter_drop::KEY, primary_key_name);
|
||||
Alter_drop *ad= new Alter_drop(Alter_drop::KEY, primary_key_name,
|
||||
FALSE);
|
||||
if (ad == NULL)
|
||||
MYSQL_YYABORT;
|
||||
lex->alter_info.drop_list.push_back(ad);
|
||||
lex->alter_info.flags|= ALTER_DROP_INDEX;
|
||||
}
|
||||
| DROP key_or_index field_ident
|
||||
| DROP key_or_index opt_if_exists field_ident
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
Alter_drop *ad= new Alter_drop(Alter_drop::KEY, $3.str);
|
||||
Alter_drop *ad= new Alter_drop(Alter_drop::KEY, $4.str, $3);
|
||||
if (ad == NULL)
|
||||
MYSQL_YYABORT;
|
||||
lex->alter_info.drop_list.push_back(ad);
|
||||
|
|
@ -10815,41 +10849,41 @@ do:
|
|||
*/
|
||||
|
||||
drop:
|
||||
DROP opt_temporary table_or_tables if_exists
|
||||
DROP opt_temporary table_or_tables opt_if_exists
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
lex->sql_command = SQLCOM_DROP_TABLE;
|
||||
lex->drop_temporary= $2;
|
||||
lex->drop_if_exists= $4;
|
||||
lex->check_exists= $4;
|
||||
YYPS->m_lock_type= TL_UNLOCK;
|
||||
YYPS->m_mdl_type= MDL_EXCLUSIVE;
|
||||
}
|
||||
table_list opt_restrict
|
||||
{}
|
||||
| DROP INDEX_SYM ident ON table_ident {}
|
||||
| DROP INDEX_SYM opt_if_exists ident ON table_ident {}
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
Alter_drop *ad= new Alter_drop(Alter_drop::KEY, $3.str);
|
||||
Alter_drop *ad= new Alter_drop(Alter_drop::KEY, $4.str, $3);
|
||||
if (ad == NULL)
|
||||
MYSQL_YYABORT;
|
||||
lex->sql_command= SQLCOM_DROP_INDEX;
|
||||
lex->alter_info.reset();
|
||||
lex->alter_info.flags= ALTER_DROP_INDEX;
|
||||
lex->alter_info.drop_list.push_back(ad);
|
||||
if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL,
|
||||
if (!lex->current_select->add_table_to_list(lex->thd, $6, NULL,
|
||||
TL_OPTION_UPDATING,
|
||||
TL_READ_NO_INSERT,
|
||||
MDL_SHARED_NO_WRITE))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| DROP DATABASE if_exists ident
|
||||
| DROP DATABASE opt_if_exists ident
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
lex->sql_command= SQLCOM_DROP_DB;
|
||||
lex->drop_if_exists=$3;
|
||||
lex->check_exists=$3;
|
||||
lex->name= $4;
|
||||
}
|
||||
| DROP FUNCTION_SYM if_exists ident '.' ident
|
||||
| DROP FUNCTION_SYM opt_if_exists ident '.' ident
|
||||
{
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
|
|
@ -10865,14 +10899,14 @@ drop:
|
|||
MYSQL_YYABORT;
|
||||
}
|
||||
lex->sql_command = SQLCOM_DROP_FUNCTION;
|
||||
lex->drop_if_exists= $3;
|
||||
lex->check_exists= $3;
|
||||
spname= new sp_name($4, $6, true);
|
||||
if (spname == NULL)
|
||||
MYSQL_YYABORT;
|
||||
spname->init_qname(thd);
|
||||
lex->spname= spname;
|
||||
}
|
||||
| DROP FUNCTION_SYM if_exists ident
|
||||
| DROP FUNCTION_SYM opt_if_exists ident
|
||||
{
|
||||
THD *thd= YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
|
|
@ -10886,14 +10920,14 @@ drop:
|
|||
if (thd->db && lex->copy_db_to(&db.str, &db.length))
|
||||
MYSQL_YYABORT;
|
||||
lex->sql_command = SQLCOM_DROP_FUNCTION;
|
||||
lex->drop_if_exists= $3;
|
||||
lex->check_exists= $3;
|
||||
spname= new sp_name(db, $4, false);
|
||||
if (spname == NULL)
|
||||
MYSQL_YYABORT;
|
||||
spname->init_qname(thd);
|
||||
lex->spname= spname;
|
||||
}
|
||||
| DROP PROCEDURE_SYM if_exists sp_name
|
||||
| DROP PROCEDURE_SYM opt_if_exists sp_name
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
if (lex->sphead)
|
||||
|
|
@ -10902,34 +10936,34 @@ drop:
|
|||
MYSQL_YYABORT;
|
||||
}
|
||||
lex->sql_command = SQLCOM_DROP_PROCEDURE;
|
||||
lex->drop_if_exists= $3;
|
||||
lex->check_exists= $3;
|
||||
lex->spname= $4;
|
||||
}
|
||||
| DROP USER clear_privileges user_list
|
||||
{
|
||||
Lex->sql_command = SQLCOM_DROP_USER;
|
||||
}
|
||||
| DROP VIEW_SYM if_exists
|
||||
| DROP VIEW_SYM opt_if_exists
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
lex->sql_command= SQLCOM_DROP_VIEW;
|
||||
lex->drop_if_exists= $3;
|
||||
lex->check_exists= $3;
|
||||
YYPS->m_lock_type= TL_UNLOCK;
|
||||
YYPS->m_mdl_type= MDL_EXCLUSIVE;
|
||||
}
|
||||
table_list opt_restrict
|
||||
{}
|
||||
| DROP EVENT_SYM if_exists sp_name
|
||||
| DROP EVENT_SYM opt_if_exists sp_name
|
||||
{
|
||||
Lex->drop_if_exists= $3;
|
||||
Lex->check_exists= $3;
|
||||
Lex->spname= $4;
|
||||
Lex->sql_command = SQLCOM_DROP_EVENT;
|
||||
}
|
||||
| DROP TRIGGER_SYM if_exists sp_name
|
||||
| DROP TRIGGER_SYM opt_if_exists sp_name
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
lex->sql_command= SQLCOM_DROP_TRIGGER;
|
||||
lex->drop_if_exists= $3;
|
||||
lex->check_exists= $3;
|
||||
lex->spname= $4;
|
||||
}
|
||||
| DROP TABLESPACE tablespace_name opt_ts_engine opt_ts_wait
|
||||
|
|
@ -10942,10 +10976,10 @@ drop:
|
|||
LEX *lex= Lex;
|
||||
lex->alter_tablespace_info->ts_cmd_type= DROP_LOGFILE_GROUP;
|
||||
}
|
||||
| DROP SERVER_SYM if_exists ident_or_text
|
||||
| DROP SERVER_SYM opt_if_exists ident_or_text
|
||||
{
|
||||
Lex->sql_command = SQLCOM_DROP_SERVER;
|
||||
Lex->drop_if_exists= $3;
|
||||
Lex->check_exists= $3;
|
||||
Lex->server_options.server_name= $4.str;
|
||||
Lex->server_options.server_name_length= $4.length;
|
||||
}
|
||||
|
|
@ -10983,9 +11017,17 @@ table_alias_ref:
|
|||
}
|
||||
;
|
||||
|
||||
if_exists:
|
||||
/* empty */ { $$= 0; }
|
||||
| IF EXISTS { $$= 1; }
|
||||
opt_if_exists:
|
||||
/* empty */
|
||||
{
|
||||
Lex->check_exists= FALSE;
|
||||
$$= 0;
|
||||
}
|
||||
| IF EXISTS
|
||||
{
|
||||
Lex->check_exists= TRUE;
|
||||
$$= 1;
|
||||
}
|
||||
;
|
||||
|
||||
opt_temporary:
|
||||
|
|
@ -13173,6 +13215,7 @@ keyword_sp:
|
|||
| AUTHORS_SYM {}
|
||||
| AUTO_INC {}
|
||||
| AUTOEXTEND_SIZE_SYM {}
|
||||
| AUTO_SYM {}
|
||||
| AVG_ROW_LENGTH {}
|
||||
| AVG_SYM {}
|
||||
| BINLOG_SYM {}
|
||||
|
|
@ -13283,6 +13326,7 @@ keyword_sp:
|
|||
| MAX_ROWS {}
|
||||
| MASTER_SYM {}
|
||||
| MASTER_HEARTBEAT_PERIOD_SYM {}
|
||||
| MASTER_USE_GTID_SYM {}
|
||||
| MASTER_HOST_SYM {}
|
||||
| MASTER_PORT_SYM {}
|
||||
| MASTER_LOG_FILE_SYM {}
|
||||
|
|
|
|||
137
sql/sys_vars.cc
137
sql/sys_vars.cc
|
|
@ -55,6 +55,7 @@
|
|||
#include "../storage/perfschema/pfs_server.h"
|
||||
#endif /* WITH_PERFSCHEMA_STORAGE_ENGINE */
|
||||
#include "threadpool.h"
|
||||
#include "sql_repl.h"
|
||||
|
||||
/*
|
||||
The rule for this file: everything should be 'static'. When a sys_var
|
||||
|
|
@ -1092,7 +1093,7 @@ static Sys_var_ulonglong Sys_max_binlog_cache_size(
|
|||
"Sets the total size of the transactional cache",
|
||||
GLOBAL_VAR(max_binlog_cache_size), CMD_LINE(REQUIRED_ARG),
|
||||
VALID_RANGE(IO_SIZE, ULONGLONG_MAX),
|
||||
DEFAULT((UINT_MAX/IO_SIZE)*IO_SIZE),
|
||||
DEFAULT((ULONGLONG_MAX/IO_SIZE)*IO_SIZE),
|
||||
BLOCK_SIZE(IO_SIZE));
|
||||
|
||||
static Sys_var_ulonglong Sys_max_binlog_stmt_cache_size(
|
||||
|
|
@ -1100,7 +1101,7 @@ static Sys_var_ulonglong Sys_max_binlog_stmt_cache_size(
|
|||
"Sets the total size of the statement cache",
|
||||
GLOBAL_VAR(max_binlog_stmt_cache_size), CMD_LINE(REQUIRED_ARG),
|
||||
VALID_RANGE(IO_SIZE, ULONGLONG_MAX),
|
||||
DEFAULT((UINT_MAX/IO_SIZE)*IO_SIZE),
|
||||
DEFAULT((ULONGLONG_MAX/IO_SIZE)*IO_SIZE),
|
||||
BLOCK_SIZE(IO_SIZE));
|
||||
|
||||
static bool fix_max_binlog_size(sys_var *self, THD *thd, enum_var_type type)
|
||||
|
|
@ -1201,6 +1202,114 @@ static Sys_var_ulong Sys_pseudo_thread_id(
|
|||
BLOCK_SIZE(1), NO_MUTEX_GUARD, IN_BINLOG,
|
||||
ON_CHECK(check_has_super));
|
||||
|
||||
static Sys_var_uint Sys_gtid_domain_id(
|
||||
"gtid_domain_id",
|
||||
"Used with global transaction ID to identify logically independent "
|
||||
"replication streams. When events can propagate through multiple "
|
||||
"parallel paths (for example multiple masters), each independent "
|
||||
"source server must use a distinct domain_id. For simple tree-shaped "
|
||||
"replication topologies, it can be left at its default, 0.",
|
||||
SESSION_VAR(gtid_domain_id),
|
||||
CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, UINT_MAX32), DEFAULT(0),
|
||||
BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG,
|
||||
ON_CHECK(check_has_super));
|
||||
|
||||
static Sys_var_ulonglong Sys_gtid_seq_no(
|
||||
"gtid_seq_no",
|
||||
"Internal server usage, for replication with global transaction id. "
|
||||
"When set, next event group logged to the binary log will use this "
|
||||
"sequence number, not generate a new one, thus allowing to preserve "
|
||||
"master's GTID in slave's binlog.",
|
||||
SESSION_ONLY(gtid_seq_no),
|
||||
NO_CMD_LINE, VALID_RANGE(0, ULONGLONG_MAX), DEFAULT(0),
|
||||
BLOCK_SIZE(1), NO_MUTEX_GUARD, NOT_IN_BINLOG,
|
||||
ON_CHECK(check_has_super));
|
||||
|
||||
|
||||
#ifdef HAVE_REPLICATION
|
||||
bool
|
||||
Sys_var_gtid_pos::do_check(THD *thd, set_var *var)
|
||||
{
|
||||
String str, *res;
|
||||
bool running;
|
||||
|
||||
DBUG_ASSERT(var->type == OPT_GLOBAL);
|
||||
mysql_mutex_lock(&LOCK_active_mi);
|
||||
running= master_info_index->give_error_if_slave_running();
|
||||
mysql_mutex_unlock(&LOCK_active_mi);
|
||||
if (running)
|
||||
return true;
|
||||
if (!(res= var->value->val_str(&str)))
|
||||
return true;
|
||||
if (rpl_gtid_pos_check(&((*res)[0]), res->length()))
|
||||
return true;
|
||||
|
||||
if (!(var->save_result.string_value.str=
|
||||
thd->strmake(res->ptr(), res->length())))
|
||||
{
|
||||
my_error(ER_OUT_OF_RESOURCES, MYF(0));
|
||||
return true;
|
||||
}
|
||||
var->save_result.string_value.length= res->length();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Sys_var_gtid_pos::global_update(THD *thd, set_var *var)
|
||||
{
|
||||
bool err;
|
||||
|
||||
DBUG_ASSERT(var->type == OPT_GLOBAL);
|
||||
|
||||
if (!var->value)
|
||||
{
|
||||
my_error(ER_NO_DEFAULT, MYF(0), var->var->name.str);
|
||||
return true;
|
||||
}
|
||||
|
||||
mysql_mutex_unlock(&LOCK_global_system_variables);
|
||||
mysql_mutex_lock(&LOCK_active_mi);
|
||||
if (master_info_index->give_error_if_slave_running())
|
||||
err= true;
|
||||
else
|
||||
err= rpl_gtid_pos_update(thd, var->save_result.string_value.str,
|
||||
var->save_result.string_value.length);
|
||||
mysql_mutex_unlock(&LOCK_active_mi);
|
||||
mysql_mutex_lock(&LOCK_global_system_variables);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
uchar *
|
||||
Sys_var_gtid_pos::global_value_ptr(THD *thd, LEX_STRING *base)
|
||||
{
|
||||
String str;
|
||||
char *p;
|
||||
|
||||
str.length(0);
|
||||
if (rpl_append_gtid_state(&str, true) ||
|
||||
!(p= thd->strmake(str.ptr(), str.length())))
|
||||
{
|
||||
my_error(ER_OUT_OF_RESOURCES, MYF(0));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (uchar *)p;
|
||||
}
|
||||
|
||||
|
||||
static unsigned char opt_gtid_pos_dummy;
|
||||
static Sys_var_gtid_pos Sys_gtid_pos(
|
||||
"gtid_pos",
|
||||
"The list of global transaction IDs that were last replicated on the "
|
||||
"server, one for each replication domain. This defines where a slave "
|
||||
"starts replicating from on a master when connecting with global "
|
||||
"transaction ID.",
|
||||
GLOBAL_VAR(opt_gtid_pos_dummy), NO_CMD_LINE);
|
||||
#endif
|
||||
|
||||
|
||||
static bool fix_max_join_size(sys_var *self, THD *thd, enum_var_type type)
|
||||
{
|
||||
SV *sv= type == OPT_GLOBAL ? &global_system_variables : &thd->variables;
|
||||
|
|
@ -1985,17 +2094,27 @@ static Sys_var_charptr Sys_secure_file_priv(
|
|||
|
||||
static bool fix_server_id(sys_var *self, THD *thd, enum_var_type type)
|
||||
{
|
||||
server_id_supplied = 1;
|
||||
thd->server_id= server_id;
|
||||
if (type == OPT_GLOBAL)
|
||||
{
|
||||
server_id_supplied = 1;
|
||||
thd->variables.server_id= global_system_variables.server_id;
|
||||
/*
|
||||
Historically, server_id was a global variable that is exported to
|
||||
plugins. Now it is a session variable, and lives in the
|
||||
global_system_variables struct, but we still need to export the
|
||||
value for reading to plugins for backwards compatibility reasons.
|
||||
*/
|
||||
::server_id= global_system_variables.server_id;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static Sys_var_ulong Sys_server_id(
|
||||
"server_id",
|
||||
"Uniquely identifies the server instance in the community of "
|
||||
"replication partners",
|
||||
GLOBAL_VAR(server_id), CMD_LINE(REQUIRED_ARG, OPT_SERVER_ID),
|
||||
SESSION_VAR(server_id), CMD_LINE(REQUIRED_ARG, OPT_SERVER_ID),
|
||||
VALID_RANGE(0, UINT_MAX32), DEFAULT(0), BLOCK_SIZE(1), NO_MUTEX_GUARD,
|
||||
NOT_IN_BINLOG, ON_CHECK(0), ON_UPDATE(fix_server_id));
|
||||
NOT_IN_BINLOG, ON_CHECK(check_has_super), ON_UPDATE(fix_server_id));
|
||||
|
||||
static Sys_var_mybool Sys_slave_compressed_protocol(
|
||||
"slave_compressed_protocol",
|
||||
|
|
@ -3386,6 +3505,7 @@ get_master_info_uint_value(THD *thd, ptrdiff_t offset)
|
|||
{
|
||||
Master_info *mi;
|
||||
uint res= 0; // Default value
|
||||
mysql_mutex_unlock(&LOCK_global_system_variables);
|
||||
mysql_mutex_lock(&LOCK_active_mi);
|
||||
mi= master_info_index->
|
||||
get_master_info(&thd->variables.default_master_connection,
|
||||
|
|
@ -3397,6 +3517,7 @@ get_master_info_uint_value(THD *thd, ptrdiff_t offset)
|
|||
mysql_mutex_unlock(&mi->rli.data_lock);
|
||||
}
|
||||
mysql_mutex_unlock(&LOCK_active_mi);
|
||||
mysql_mutex_lock(&LOCK_global_system_variables);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -3408,6 +3529,8 @@ bool update_multi_source_variable(sys_var *self_var, THD *thd,
|
|||
bool result= true;
|
||||
Master_info *mi;
|
||||
|
||||
if (type == OPT_GLOBAL)
|
||||
mysql_mutex_unlock(&LOCK_global_system_variables);
|
||||
mysql_mutex_lock(&LOCK_active_mi);
|
||||
mi= master_info_index->
|
||||
get_master_info(&thd->variables.default_master_connection,
|
||||
|
|
@ -3421,6 +3544,8 @@ bool update_multi_source_variable(sys_var *self_var, THD *thd,
|
|||
mysql_mutex_unlock(&mi->rli.run_lock);
|
||||
}
|
||||
mysql_mutex_unlock(&LOCK_active_mi);
|
||||
if (type == OPT_GLOBAL)
|
||||
mysql_mutex_lock(&LOCK_global_system_variables);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2020,3 +2020,43 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
Class for @@global.gtid_pos.
|
||||
*/
|
||||
class Sys_var_gtid_pos: public sys_var
|
||||
{
|
||||
public:
|
||||
Sys_var_gtid_pos(const char *name_arg,
|
||||
const char *comment, int flag_args, ptrdiff_t off, size_t size,
|
||||
CMD_LINE getopt)
|
||||
: sys_var(&all_sys_vars, name_arg, comment, flag_args, off, getopt.id,
|
||||
getopt.arg_type, SHOW_CHAR, 0, NULL, VARIABLE_NOT_IN_BINLOG,
|
||||
NULL, NULL, NULL)
|
||||
{
|
||||
option.var_type= GET_STR;
|
||||
}
|
||||
bool do_check(THD *thd, set_var *var);
|
||||
bool session_update(THD *thd, set_var *var)
|
||||
{
|
||||
DBUG_ASSERT(false);
|
||||
return true;
|
||||
}
|
||||
bool global_update(THD *thd, set_var *var);
|
||||
bool check_update_type(Item_result type) { return type != STRING_RESULT; }
|
||||
void session_save_default(THD *thd, set_var *var)
|
||||
{
|
||||
DBUG_ASSERT(false);
|
||||
}
|
||||
void global_save_default(THD *thd, set_var *var)
|
||||
{
|
||||
/* Record the attempt to use default so we can error. */
|
||||
var->value= 0;
|
||||
}
|
||||
uchar *session_value_ptr(THD *thd, LEX_STRING *base)
|
||||
{
|
||||
DBUG_ASSERT(false);
|
||||
return NULL;
|
||||
}
|
||||
uchar *global_value_ptr(THD *thd, LEX_STRING *base);
|
||||
};
|
||||
|
|
|
|||
44
sql/table.cc
44
sql/table.cc
|
|
@ -3460,9 +3460,9 @@ bool check_column_name(const char *name)
|
|||
}
|
||||
#else
|
||||
last_char_is_space= *name==' ';
|
||||
#endif
|
||||
if (*name == NAMES_SEP_CHAR)
|
||||
if (*name == '\377')
|
||||
return 1;
|
||||
#endif
|
||||
name++;
|
||||
name_length++;
|
||||
}
|
||||
|
|
@ -3620,6 +3620,46 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def)
|
|||
}
|
||||
}
|
||||
|
||||
if (table_def->primary_key_parts)
|
||||
{
|
||||
if (table->s->primary_key == MAX_KEY)
|
||||
{
|
||||
report_error(0, "Incorrect definition of table %s.%s: "
|
||||
"missing primary key.", table->s->db.str,
|
||||
table->alias.c_ptr());
|
||||
error= TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
KEY *pk= &table->s->key_info[table->s->primary_key];
|
||||
if (pk->key_parts != table_def->primary_key_parts)
|
||||
{
|
||||
report_error(0, "Incorrect definition of table %s.%s: "
|
||||
"Expected primary key to have %u columns, but instead "
|
||||
"found %u columns.", table->s->db.str,
|
||||
table->alias.c_ptr(), table_def->primary_key_parts,
|
||||
pk->key_parts);
|
||||
error= TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i= 0; i < pk->key_parts; ++i)
|
||||
{
|
||||
if (table_def->primary_key_columns[i] + 1 != pk->key_part[i].fieldnr)
|
||||
{
|
||||
report_error(0, "Incorrect definition of table %s.%s: Expected "
|
||||
"primary key part %u to refer to column %u, but "
|
||||
"instead found column %u.", table->s->db.str,
|
||||
table->alias.c_ptr(), i + 1,
|
||||
table_def->primary_key_columns[i] + 1,
|
||||
pk->key_part[i].fieldnr);
|
||||
error= TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! error)
|
||||
table->s->table_field_def_cache= table_def;
|
||||
|
||||
|
|
|
|||
|
|
@ -493,6 +493,8 @@ typedef struct st_table_field_def
|
|||
{
|
||||
uint count;
|
||||
const TABLE_FIELD_TYPE *field;
|
||||
uint primary_key_parts;
|
||||
const uint *primary_key_columns;
|
||||
} TABLE_FIELD_DEF;
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue