Merge 10.0-base -> mwl253

This commit is contained in:
Igor Babaev 2013-04-15 09:16:54 -07:00
commit f4cd2b37b1
329 changed files with 19225 additions and 9200 deletions

View file

@ -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}
)

View file

@ -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;
}

View file

@ -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;

View file

@ -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()
{

View file

@ -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();
}

View file

@ -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)},

View file

@ -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));
}

View file

@ -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
{

View file

@ -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].

View file

@ -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;

View file

@ -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)},

View file

@ -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(&gtid);
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(&gtid))
goto err2;
}
break;
default:
/* Nothing. */
break;

View file

@ -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

View file

@ -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, &gtid, 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, &gtid);
/*
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, &gtid, 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, &gtid);
/*
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)
{
/*

View file

@ -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 */
/**

View file

@ -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

View file

@ -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.

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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);
};

View file

@ -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

File diff suppressed because it is too large Load diff

179
sql/rpl_gtid.h Normal file
View 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 */

View file

@ -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();

View file

@ -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);

View file

@ -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)
{

View file

@ -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);

View file

@ -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

View file

@ -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 */

View file

@ -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"

View file

@ -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(&gtid_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, &gtid_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(&gtid_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, &gtid_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);

View file

@ -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)

View file

@ -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);

View file

@ -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));

View file

@ -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.

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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());
/*

File diff suppressed because it is too large Load diff

View file

@ -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 */

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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),

View file

@ -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 {}

View file

@ -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;
}

View file

@ -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);
};

View file

@ -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;

View file

@ -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;