summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorNirbhay Choubey <nirbhay@mariadb.com>2014-09-28 20:43:56 -0400
committerNirbhay Choubey <nirbhay@mariadb.com>2014-09-28 20:43:56 -0400
commitc916085e271cd049537b1e07b36cd060c44750bd (patch)
tree337ddfcd033174143cf9bd08e6ba50174459d7ff /sql
parent023366e6eb68edca3858c32e7492788e047d927a (diff)
parentf1afc003eefe0aafd3e070c7453d9e029d8445a8 (diff)
downloadmariadb-git-c916085e271cd049537b1e07b36cd060c44750bd.tar.gz
bzr merge -rtag:mariadb-10.0.14 maria/10.0/
Diffstat (limited to 'sql')
-rw-r--r--sql/CMakeLists.txt21
-rw-r--r--sql/ha_partition.cc5
-rw-r--r--sql/handler.cc11
-rw-r--r--sql/item.cc356
-rw-r--r--sql/item.h390
-rw-r--r--sql/item_cmpfunc.h18
-rw-r--r--sql/item_create.cc21
-rw-r--r--sql/item_create.h9
-rw-r--r--sql/item_func.cc8
-rw-r--r--sql/item_strfunc.cc97
-rw-r--r--sql/item_strfunc.h15
-rw-r--r--sql/item_sum.cc14
-rw-r--r--sql/item_timefunc.cc2
-rw-r--r--sql/item_xmlfunc.cc39
-rw-r--r--sql/log.cc10
-rw-r--r--sql/log_event.cc46
-rw-r--r--sql/mysqld.cc17
-rw-r--r--sql/mysqld.h5
-rw-r--r--sql/rpl_mi.cc13
-rw-r--r--sql/rpl_mi.h6
-rw-r--r--sql/rpl_parallel.cc184
-rw-r--r--sql/rpl_parallel.h20
-rw-r--r--sql/rpl_rli.cc4
-rw-r--r--sql/rpl_rli.h9
-rw-r--r--sql/set_var.h4
-rw-r--r--sql/slave.cc95
-rw-r--r--sql/slave.h10
-rw-r--r--sql/sp_head.cc9
-rw-r--r--sql/sql_acl.cc3
-rw-r--r--sql/sql_base.cc9
-rw-r--r--sql/sql_class.cc188
-rw-r--r--sql/sql_class.h14
-rw-r--r--sql/sql_explain.cc20
-rw-r--r--sql/sql_get_diagnostics.cc6
-rw-r--r--sql/sql_help.cc2
-rw-r--r--sql/sql_join_cache.cc2
-rw-r--r--sql/sql_parse.cc14
-rw-r--r--sql/sql_reload.cc57
-rw-r--r--sql/sql_repl.cc6
-rw-r--r--sql/sql_select.cc105
-rw-r--r--sql/sql_show.cc169
-rw-r--r--sql/sql_string.cc15
-rw-r--r--sql/sql_string.h23
-rw-r--r--sql/sql_table.cc13
-rw-r--r--sql/sql_test.cc5
-rw-r--r--sql/sql_yacc.yy119
-rw-r--r--sql/sys_vars.cc45
-rw-r--r--sql/sys_vars.h33
-rw-r--r--sql/table.cc47
-rw-r--r--sql/table.h2
50 files changed, 1423 insertions, 912 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index e99c7dca01c..f948685bb87 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -37,13 +37,6 @@ SET_SOURCE_FILES_PROPERTIES(${GEN_SOURCES} PROPERTIES GENERATED 1)
ADD_DEFINITIONS(-DMYSQL_SERVER -DHAVE_EVENT_SCHEDULER)
-IF (CMAKE_SYSTEM_NAME MATCHES "Linux" OR
- CMAKE_SYSTEM_NAME MATCHES "Windows" OR
- CMAKE_SYSTEM_NAME MATCHES "SunOS" OR
- HAVE_KQUEUE)
- ADD_DEFINITIONS(-DHAVE_POOL_OF_THREADS)
-ENDIF()
-
IF(SSL_DEFINES)
ADD_DEFINITIONS(${SSL_DEFINES})
ENDIF()
@@ -125,10 +118,16 @@ SET (SQL_SOURCE
${GEN_SOURCES}
${MYSYS_LIBWRAP_SOURCE})
-IF(WIN32)
- SET(SQL_SOURCE ${SQL_SOURCE} threadpool_win.cc)
-ELSE()
- SET(SQL_SOURCE ${SQL_SOURCE} threadpool_unix.cc)
+IF (CMAKE_SYSTEM_NAME MATCHES "Linux" OR
+ CMAKE_SYSTEM_NAME MATCHES "Windows" OR
+ CMAKE_SYSTEM_NAME MATCHES "SunOS" OR
+ HAVE_KQUEUE)
+ ADD_DEFINITIONS(-DHAVE_POOL_OF_THREADS)
+ IF(WIN32)
+ SET(SQL_SOURCE ${SQL_SOURCE} threadpool_win.cc)
+ ELSE()
+ SET(SQL_SOURCE ${SQL_SOURCE} threadpool_unix.cc)
+ ENDIF()
ENDIF()
MYSQL_ADD_PLUGIN(partition ha_partition.cc STORAGE_ENGINE DEFAULT STATIC_ONLY
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index 788254bcc3f..e53d8bace6f 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -8595,8 +8595,7 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
ulonglong first_value_part, max_first_value;
handler **file= m_file;
first_value_part= max_first_value= *first_value;
- /* Must lock and find highest value among all partitions. */
- lock_auto_increment();
+ /* Must find highest value among all partitions. */
do
{
/* Only nb_desired_values = 1 makes sense */
@@ -8607,7 +8606,6 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
*first_value= first_value_part;
/* log that the error was between table/partition handler */
sql_print_error("Partition failed to reserve auto_increment value");
- unlock_auto_increment();
DBUG_VOID_RETURN;
}
DBUG_PRINT("info", ("first_value_part: %lu", (ulong) first_value_part));
@@ -8615,7 +8613,6 @@ void ha_partition::get_auto_increment(ulonglong offset, ulonglong increment,
} while (*(++file));
*first_value= max_first_value;
*nb_reserved_values= 1;
- unlock_auto_increment();
}
else
{
diff --git a/sql/handler.cc b/sql/handler.cc
index 8c0a2013094..2e0a10609db 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -3270,15 +3270,10 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment,
if (error)
{
if (error == HA_ERR_END_OF_FILE || error == HA_ERR_KEY_NOT_FOUND)
- {
- /* No entry found, start with 1. */
- nr= 1;
- }
+ /* No entry found, that's fine */;
else
- {
- DBUG_ASSERT(0);
- nr= ULONGLONG_MAX;
- }
+ print_error(error, MYF(0));
+ nr= 1;
}
else
nr= ((ulonglong) table->next_number_field->
diff --git a/sql/item.cc b/sql/item.cc
index 107468030bb..447137a1f68 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -686,7 +686,7 @@ void Item::cleanup()
{
DBUG_ENTER("Item::cleanup");
DBUG_PRINT("enter", ("this: %p", this));
- fixed=0;
+ fixed= 0;
marker= 0;
join_tab_idx= MAX_TABLES;
if (orig_name)
@@ -1073,10 +1073,15 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
name_length= 0;
return;
}
- if (cs->ctype)
- {
- const char *str_start= str;
+ const char *str_start= str;
+ if (!cs->ctype || cs->mbminlen > 1)
+ {
+ str+= cs->cset->scan(cs, str, str + length, MY_SEQ_SPACES);
+ length-= str - str_start;
+ }
+ else
+ {
/*
This will probably need a better implementation in the future:
a function in CHARSET_INFO structure.
@@ -1086,21 +1091,21 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
length--;
str++;
}
- if (str != str_start && !is_autogenerated_name)
- {
- char buff[SAFE_NAME_LEN];
- strmake(buff, str_start,
- MY_MIN(sizeof(buff)-1, length + (int) (str-str_start)));
-
- if (length == 0)
- push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
- ER_NAME_BECOMES_EMPTY, ER(ER_NAME_BECOMES_EMPTY),
- buff);
- else
- push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
- ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES),
- buff);
- }
+ }
+ if (str != str_start && !is_autogenerated_name)
+ {
+ char buff[SAFE_NAME_LEN];
+ strmake(buff, str_start,
+ MY_MIN(sizeof(buff)-1, length + (int) (str-str_start)));
+
+ if (length == 0)
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_NAME_BECOMES_EMPTY, ER(ER_NAME_BECOMES_EMPTY),
+ buff);
+ else
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_REMOVED_SPACES, ER(ER_REMOVED_SPACES),
+ buff);
}
if (!my_charset_same(cs, system_charset_info))
{
@@ -1166,6 +1171,8 @@ bool Item::eq(const Item *item, bool binary_cmp) const
Item *Item::safe_charset_converter(CHARSET_INFO *tocs)
{
+ if (!needs_charset_converter(tocs))
+ return this;
Item_func_conv_charset *conv= new Item_func_conv_charset(this, tocs, 1);
return conv->safe ? conv : NULL;
}
@@ -1192,123 +1199,55 @@ Item *Item_num::safe_charset_converter(CHARSET_INFO *tocs)
if (!(tocs->state & MY_CS_NONASCII))
return this;
- Item_string *conv;
- uint conv_errors;
- char buf[64], buf2[64];
- String tmp(buf, sizeof(buf), &my_charset_bin);
- String cstr(buf2, sizeof(buf2), &my_charset_bin);
- String *ostr= val_str(&tmp);
- char *ptr;
- cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
- if (conv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(),
- cstr.charset(),
- collation.derivation)))
- {
- /*
- Safe conversion is not possible (or EOM).
- We could not convert a string into the requested character set
- without data loss. The target charset does not cover all the
- characters from the string. Operation cannot be done correctly.
- */
- return NULL;
- }
- if (!(ptr= current_thd->strmake(cstr.ptr(), cstr.length())))
- return NULL;
- conv->str_value.set(ptr, cstr.length(), cstr.charset());
- /* Ensure that no one is going to change the result string */
- conv->str_value.mark_as_const();
- conv->fix_char_length(max_char_length());
- return conv;
-}
-
-
-Item *Item_static_float_func::safe_charset_converter(CHARSET_INFO *tocs)
-{
- Item_string *conv;
- char buf[64];
- String *s, tmp(buf, sizeof(buf), &my_charset_bin);
- s= val_str(&tmp);
- if ((conv= new Item_static_string_func(func_name, s->ptr(), s->length(),
- s->charset())))
- {
- conv->str_value.copy();
- conv->str_value.mark_as_const();
- }
+ Item *conv;
+ if ((conv= const_charset_converter(tocs, true)))
+ conv->fix_char_length(max_char_length());
return conv;
}
-Item *Item_string::safe_charset_converter(CHARSET_INFO *tocs)
-{
- return charset_converter(tocs, true);
-}
-
-
/**
- Convert a string item into the requested character set.
+ Create character set converter for constant items
+ using Item_null, Item_string or Item_static_string_func.
@param tocs Character set to to convert the string to.
@param lossless Whether data loss is acceptable.
-
- @return A new item representing the converted string.
+ @param func_name Function name, or NULL.
+
+ @return this, if conversion is not needed,
+ NULL, if safe conversion is not possible, or
+ a new item representing the converted constant.
*/
-Item *Item_string::charset_converter(CHARSET_INFO *tocs, bool lossless)
+Item *Item::const_charset_converter(CHARSET_INFO *tocs,
+ bool lossless,
+ const char *func_name)
{
- Item_string *conv;
- uint conv_errors;
- char *ptr;
- String tmp, cstr, *ostr= val_str(&tmp);
- cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
- conv_errors= lossless && conv_errors;
- if (conv_errors || !(conv= new Item_string(cstr.ptr(), cstr.length(),
- cstr.charset(),
- collation.derivation)))
- {
- /*
- Safe conversion is not possible (or EOM).
- We could not convert a string into the requested character set
- without data loss. The target charset does not cover all the
- characters from the string. Operation cannot be done correctly.
- */
- return NULL;
- }
- if (!(ptr= current_thd->strmake(cstr.ptr(), cstr.length())))
- return NULL;
- conv->str_value.set(ptr, cstr.length(), cstr.charset());
- /* Ensure that no one is going to change the result string */
- conv->str_value.mark_as_const();
- return conv;
-}
+ DBUG_ASSERT(const_item());
+ DBUG_ASSERT(fixed);
+ StringBuffer<64>tmp;
+ String *s= val_str(&tmp);
+ if (!s)
+ return new Item_null((char *) func_name, tocs);
-Item *Item_param::safe_charset_converter(CHARSET_INFO *tocs)
-{
- if (const_item())
+ if (!needs_charset_converter(s->length(), tocs))
{
- uint cnv_errors;
- String *ostr= val_str(&cnvstr);
- cnvitem->str_value.copy(ostr->ptr(), ostr->length(),
- ostr->charset(), tocs, &cnv_errors);
- if (cnv_errors)
- return NULL;
- cnvitem->str_value.mark_as_const();
- cnvitem->max_length= cnvitem->str_value.numchars() * tocs->mbmaxlen;
- return cnvitem;
+ if (collation.collation == &my_charset_bin && tocs != &my_charset_bin &&
+ !this->check_well_formed_result(s, true))
+ return NULL;
+ return this;
}
- return Item::safe_charset_converter(tocs);
-}
-
-Item *Item_static_string_func::safe_charset_converter(CHARSET_INFO *tocs)
-{
- Item_string *conv;
uint conv_errors;
- String tmp, cstr, *ostr= val_str(&tmp);
- cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
- if (conv_errors ||
- !(conv= new Item_static_string_func(func_name,
- cstr.ptr(), cstr.length(),
- cstr.charset(),
- collation.derivation)))
+ Item_string *conv= func_name ?
+ new Item_static_string_func(func_name,
+ s, tocs, &conv_errors,
+ collation.derivation,
+ collation.repertoire) :
+ new Item_string(s, tocs, &conv_errors,
+ collation.derivation,
+ collation.repertoire);
+
+ if (!conv || (conv_errors && lossless))
{
/*
Safe conversion is not possible (or EOM).
@@ -1318,23 +1257,28 @@ Item *Item_static_string_func::safe_charset_converter(CHARSET_INFO *tocs)
*/
return NULL;
}
- conv->str_value.copy();
- /* Ensure that no one is going to change the result string */
- conv->str_value.mark_as_const();
+ if (s->charset() == &my_charset_bin && tocs != &my_charset_bin &&
+ !conv->check_well_formed_result(true))
+ return NULL;
return conv;
}
-bool Item_string::eq(const Item *item, bool binary_cmp) const
+Item *Item_param::safe_charset_converter(CHARSET_INFO *tocs)
{
- if (type() == item->type() && item->basic_const_item())
- {
- if (binary_cmp)
- return !stringcmp(&str_value, &item->str_value);
- return (collation.collation == item->collation.collation &&
- !sortcmp(&str_value, &item->str_value, collation.collation));
- }
- return 0;
+ /*
+ Return "this" if in prepare. result_type may change at execition time,
+ to it's possible that the converter will not be needed at all:
+
+ PREPARE stmt FROM 'SELECT * FROM t1 WHERE field = ?';
+ SET @@arg= 1;
+ EXECUTE stms USING @arg;
+
+ In the above example result_type is STRING_RESULT at prepare time,
+ and INT_RESULT at execution time.
+ */
+ return !const_item() || state == NULL_VALUE ?
+ this : const_charset_converter(tocs, true);
}
@@ -2203,33 +2147,10 @@ bool agg_item_set_converter(DTCollation &coll, const char *fname,
for (i= 0, arg= args; i < nargs; i++, arg+= item_sep)
{
- Item* conv;
- uint32 dummy_offset;
- if (!String::needs_conversion(1, (*arg)->collation.collation,
- coll.collation,
- &dummy_offset))
- continue;
-
- /*
- No needs to add converter if an "arg" is NUMERIC or DATETIME
- value (which is pure ASCII) and at the same time target DTCollation
- is ASCII-compatible. For example, no needs to rewrite:
- SELECT * FROM t1 WHERE datetime_field = '2010-01-01';
- to
- SELECT * FROM t1 WHERE CONVERT(datetime_field USING cs) = '2010-01-01';
-
- TODO: avoid conversion of any values with
- repertoire ASCII and 7bit-ASCII-compatible,
- not only numeric/datetime origin.
- */
- if ((*arg)->collation.derivation == DERIVATION_NUMERIC &&
- (*arg)->collation.repertoire == MY_REPERTOIRE_ASCII &&
- !((*arg)->collation.collation->state & MY_CS_NONASCII) &&
- !(coll.collation->state & MY_CS_NONASCII))
+ Item* conv= (*arg)->safe_charset_converter(coll.collation);
+ if (conv == *arg)
continue;
-
- if (!(conv= (*arg)->safe_charset_converter(coll.collation)) &&
- ((*arg)->collation.repertoire == MY_REPERTOIRE_ASCII))
+ if (!conv && ((*arg)->collation.repertoire == MY_REPERTOIRE_ASCII))
conv= new Item_func_conv_charset(*arg, coll.collation, 1);
if (!conv)
@@ -3015,7 +2936,7 @@ String *Item_float::val_str(String *str)
{
// following assert is redundant, because fixed=1 assigned in constructor
DBUG_ASSERT(fixed == 1);
- str->set_real(value,decimals,&my_charset_bin);
+ str->set_real(value, decimals, &my_charset_numeric);
return str;
}
@@ -3174,10 +3095,6 @@ my_decimal *Item_string::val_decimal(my_decimal *decimal_value)
}
-bool Item_null::eq(const Item *item, bool binary_cmp) const
-{ return item->type() == type(); }
-
-
double Item_null::val_real()
{
// following assert is redundant, because fixed=1 assigned in constructor
@@ -3247,8 +3164,6 @@ Item_param::Item_param(uint pos_in_query_arg) :
value is set.
*/
maybe_null= 1;
- cnvitem= new Item_string("", 0, &my_charset_bin, DERIVATION_COERCIBLE);
- cnvstr.set(cnvbuf, sizeof(cnvbuf), &my_charset_bin);
}
@@ -3808,18 +3723,14 @@ bool Item_param::convert_str_value(THD *thd)
str_value.set_charset(value.cs_info.final_character_set_of_str_value);
/* Here str_value is guaranteed to be in final_character_set_of_str_value */
- max_length= str_value.numchars() * str_value.charset()->mbmaxlen;
-
- /* For the strings converted to numeric form within some functions */
- decimals= NOT_FIXED_DEC;
/*
str_value_ptr is returned from val_str(). It must be not alloced
to prevent it's modification by val_str() invoker.
*/
str_value_ptr.set(str_value.ptr(), str_value.length(),
str_value.charset());
- /* Synchronize item charset with value charset */
- collation.set(str_value.charset(), DERIVATION_COERCIBLE);
+ /* Synchronize item charset and length with value charset */
+ fix_charset_and_length_from_str_value(DERIVATION_COERCIBLE);
}
return rc;
}
@@ -3849,7 +3760,8 @@ Item_param::clone_item()
case STRING_VALUE:
case LONG_DATA_VALUE:
return new Item_string(name, str_value.c_ptr_quick(), str_value.length(),
- str_value.charset());
+ str_value.charset(),
+ collation.derivation, collation.repertoire);
case TIME_VALUE:
break;
case NO_VALUE:
@@ -3861,30 +3773,21 @@ Item_param::clone_item()
bool
-Item_param::eq(const Item *arg, bool binary_cmp) const
+Item_param::eq(const Item *item, bool binary_cmp) const
{
- Item *item;
- if (!basic_const_item() || !arg->basic_const_item() || arg->type() != type())
+ if (!basic_const_item())
return FALSE;
- /*
- We need to cast off const to call val_int(). This should be OK for
- a basic constant.
- */
- item= (Item*) arg;
switch (state) {
case NULL_VALUE:
- return TRUE;
+ return null_eq(item);
case INT_VALUE:
- return value.integer == item->val_int() &&
- unsigned_flag == item->unsigned_flag;
+ return int_eq(value.integer, item);
case REAL_VALUE:
- return value.real == item->val_real();
+ return real_eq(value.real, item);
case STRING_VALUE:
case LONG_DATA_VALUE:
- if (binary_cmp)
- return !stringcmp(&str_value, &item->str_value);
- return !sortcmp(&str_value, &item->str_value, collation.collation);
+ return str_eq(&str_value, item, binary_cmp);
default:
break;
}
@@ -5375,13 +5278,6 @@ bool Item_field::vcol_in_partition_func_processor(uchar *int_arg)
}
-Item *Item_field::safe_charset_converter(CHARSET_INFO *tocs)
-{
- no_const_subst= 1;
- return Item::safe_charset_converter(tocs);
-}
-
-
void Item_field::cleanup()
{
DBUG_ENTER("Item_field::cleanup");
@@ -5687,10 +5583,7 @@ String *Item::check_well_formed_result(String *str, bool send_error)
{
/* Check whether we got a well-formed string */
CHARSET_INFO *cs= str->charset();
- int well_formed_error;
- uint wlen= cs->cset->well_formed_len(cs,
- str->ptr(), str->ptr() + str->length(),
- str->length(), &well_formed_error);
+ uint wlen= str->well_formed_length();
if (wlen < str->length())
{
THD *thd= current_thd;
@@ -6178,24 +6071,6 @@ int Item_decimal::save_in_field(Field *field, bool no_conversions)
}
-bool Item_int::eq(const Item *arg, bool binary_cmp) const
-{
- /* No need to check for null value as basic constant can't be NULL */
- if (arg->basic_const_item() && arg->type() == type())
- {
- /*
- We need to cast off const to call val_int(). This should be OK for
- a basic constant.
- */
- Item *item= (Item*) arg;
- return (item->val_int() == value &&
- ((longlong) value >= 0 ||
- (item->unsigned_flag == unsigned_flag)));
- }
- return FALSE;
-}
-
-
Item *Item_int_with_ref::clone_item()
{
DBUG_ASSERT(ref->const_item());
@@ -6313,27 +6188,6 @@ void Item_float::print(String *str, enum_query_type query_type)
}
-/*
- hex item
- In string context this is a binary string.
- In number context this is a longlong value.
-*/
-
-bool Item_float::eq(const Item *arg, bool binary_cmp) const
-{
- if (arg->basic_const_item() && arg->type() == type())
- {
- /*
- We need to cast off const to call val_int(). This should be OK for
- a basic constant.
- */
- Item *item= (Item*) arg;
- return item->val_real() == value;
- }
- return FALSE;
-}
-
-
inline uint char_val(char X)
{
return (uint) (X >= '0' && X <= '9' ? X-'0' :
@@ -6428,32 +6282,6 @@ void Item_hex_string::print(String *str, enum_query_type query_type)
}
-bool Item_hex_constant::eq(const Item *arg, bool binary_cmp) const
-{
- if (arg->basic_const_item() && arg->type() == type() &&
- arg->cast_to_int_type() == cast_to_int_type())
- {
- if (binary_cmp)
- return !stringcmp(&str_value, &arg->str_value);
- return !sortcmp(&str_value, &arg->str_value, collation.collation);
- }
- return FALSE;
-}
-
-
-Item *Item_hex_constant::safe_charset_converter(CHARSET_INFO *tocs)
-{
- Item_string *conv;
- String tmp, *str= val_str(&tmp);
-
- if (!(conv= new Item_string(str->ptr(), str->length(), tocs)))
- return NULL;
- conv->str_value.copy();
- conv->str_value.mark_as_const();
- return conv;
-}
-
-
/*
bin item.
In string context this is a binary string.
diff --git a/sql/item.h b/sql/item.h
index 00c1468bb48..22d147028ef 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -614,11 +614,20 @@ public:
/* Reuse size, only used by SP local variable assignment, otherwize 0 */
uint rsize;
+protected:
/*
str_values's main purpose is to be used to cache the value in
save_in_field
*/
String str_value;
+
+public:
+ /*
+ Cache val_str() into the own buffer, e.g. to evaluate constant
+ expressions with subqueries in the ORDER/GROUP clauses.
+ */
+ String *val_str() { return val_str(&str_value); }
+
char * name; /* Name from select */
/* Original item name (if it was renamed)*/
char * orig_name;
@@ -1231,7 +1240,6 @@ public:
virtual bool intro_version(uchar *int_arg) { return 0; }
virtual bool remove_dependence_processor(uchar * arg) { return 0; }
- virtual bool remove_fixed(uchar * arg) { fixed= 0; return 0; }
virtual bool cleanup_processor(uchar *arg);
virtual bool collect_item_field_processor(uchar * arg) { return 0; }
virtual bool add_field_to_set_processor(uchar * arg) { return 0; }
@@ -1463,6 +1471,48 @@ public:
virtual Item *expr_cache_insert_transformer(uchar *thd_arg) { return this; }
virtual bool expr_cache_is_needed(THD *) { return FALSE; }
virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
+ bool needs_charset_converter(uint32 length, CHARSET_INFO *tocs)
+ {
+ /*
+ This will return "true" if conversion happens:
+ - between two non-binary different character sets
+ - from "binary" to "unsafe" character set
+ (those that can have non-well-formed string)
+ - from "binary" to UCS2-alike character set with mbminlen>1,
+ when prefix left-padding is needed for an incomplete character:
+ binary 0xFF -> ucs2 0x00FF)
+ */
+ if (!String::needs_conversion_on_storage(length,
+ collation.collation, tocs))
+ return false;
+ /*
+ No needs to add converter if an "arg" is NUMERIC or DATETIME
+ value (which is pure ASCII) and at the same time target DTCollation
+ is ASCII-compatible. For example, no needs to rewrite:
+ SELECT * FROM t1 WHERE datetime_field = '2010-01-01';
+ to
+ SELECT * FROM t1 WHERE CONVERT(datetime_field USING cs) = '2010-01-01';
+
+ TODO: avoid conversion of any values with
+ repertoire ASCII and 7bit-ASCII-compatible,
+ not only numeric/datetime origin.
+ */
+ if (collation.derivation == DERIVATION_NUMERIC &&
+ collation.repertoire == MY_REPERTOIRE_ASCII &&
+ !(collation.collation->state & MY_CS_NONASCII) &&
+ !(tocs->state & MY_CS_NONASCII))
+ return false;
+ return true;
+ }
+ bool needs_charset_converter(CHARSET_INFO *tocs)
+ {
+ // Pass 1 as length to force conversion if tocs->mbminlen>1.
+ return needs_charset_converter(1, tocs);
+ }
+ Item *const_charset_converter(CHARSET_INFO *tocs, bool lossless,
+ const char *func_name);
+ Item *const_charset_converter(CHARSET_INFO *tocs, bool lossless)
+ { return const_charset_converter(tocs, lossless, NULL); }
void delete_self()
{
cleanup();
@@ -1620,12 +1670,102 @@ public:
};
class sp_head;
+class Item_string;
-class Item_basic_constant :public Item
+
+/**
+ A common class for Item_basic_constant and Item_param
+*/
+class Item_basic_value :public Item
+{
+ bool is_basic_value(const Item *item, Type type_arg) const
+ {
+ return item->basic_const_item() && item->type() == type_arg;
+ }
+ bool is_basic_value(Type type_arg) const
+ {
+ return basic_const_item() && type() == type_arg;
+ }
+ bool str_eq(const String *value,
+ const String *other, CHARSET_INFO *cs, bool binary_cmp) const
+ {
+ return binary_cmp ?
+ value->bin_eq(other) :
+ collation.collation == cs && value->eq(other, collation.collation);
+ }
+
+protected:
+ // Value metadata, e.g. to make string processing easier
+ class Metadata: private MY_STRING_METADATA
+ {
+ public:
+ Metadata(const String *str)
+ {
+ my_string_metadata_get(this, str->charset(), str->ptr(), str->length());
+ }
+ Metadata(const String *str, uint repertoire)
+ {
+ MY_STRING_METADATA::repertoire= repertoire;
+ MY_STRING_METADATA::char_length= str->numchars();
+ }
+ uint repertoire() const { return MY_STRING_METADATA::repertoire; }
+ size_t char_length() const { return MY_STRING_METADATA::char_length; }
+ };
+ void fix_charset_and_length_from_str_value(Derivation dv, Metadata metadata)
+ {
+ /*
+ We have to have a different max_length than 'length' here to
+ ensure that we get the right length if we do use the item
+ to create a new table. In this case max_length must be the maximum
+ number of chars for a string of this type because we in Create_field::
+ divide the max_length with mbmaxlen).
+ */
+ collation.set(str_value.charset(), dv, metadata.repertoire());
+ fix_char_length(metadata.char_length());
+ decimals= NOT_FIXED_DEC;
+ }
+ void fix_charset_and_length_from_str_value(Derivation dv)
+ {
+ fix_charset_and_length_from_str_value(dv, Metadata(&str_value));
+ }
+ Item_basic_value(): Item() {}
+ /*
+ In the xxx_eq() methods below we need to cast off "const" to
+ call val_xxx(). This is OK for Item_basic_constant and Item_param.
+ */
+ bool null_eq(const Item *item) const
+ {
+ DBUG_ASSERT(is_basic_value(NULL_ITEM));
+ return item->type() == NULL_ITEM;
+ }
+ bool str_eq(const String *value, const Item *item, bool binary_cmp) const
+ {
+ DBUG_ASSERT(is_basic_value(STRING_ITEM));
+ return is_basic_value(item, STRING_ITEM) &&
+ str_eq(value, ((Item_basic_value*)item)->val_str(NULL),
+ item->collation.collation, binary_cmp);
+ }
+ bool real_eq(double value, const Item *item) const
+ {
+ DBUG_ASSERT(is_basic_value(REAL_ITEM));
+ return is_basic_value(item, REAL_ITEM) &&
+ value == ((Item_basic_value*)item)->val_real();
+ }
+ bool int_eq(longlong value, const Item *item) const
+ {
+ DBUG_ASSERT(is_basic_value(INT_ITEM));
+ return is_basic_value(item, INT_ITEM) &&
+ value == ((Item_basic_value*)item)->val_int() &&
+ (value >= 0 || item->unsigned_flag == unsigned_flag);
+ }
+};
+
+
+class Item_basic_constant :public Item_basic_value
{
table_map used_table_map;
public:
- Item_basic_constant(): Item(), used_table_map(0) {};
+ Item_basic_constant(): Item_basic_value(), used_table_map(0) {};
void set_used_tables(table_map map) { used_table_map= map; }
table_map used_tables() const { return used_table_map; }
/* to prevent drop fixed flag (no need parent cleanup call) */
@@ -2189,7 +2329,6 @@ public:
Item *replace_equal_field(uchar *arg);
inline uint32 max_disp_length() { return field->max_display_length(); }
Item_field *field_for_view_update() { return this; }
- Item *safe_charset_converter(CHARSET_INFO *tocs);
int fix_outer_field(THD *thd, Field **field, Item **reference);
virtual Item *update_value_transformer(uchar *select_arg);
virtual void print(String *str, enum_query_type query_type);
@@ -2213,16 +2352,16 @@ public:
class Item_null :public Item_basic_constant
{
public:
- Item_null(char *name_par=0)
+ Item_null(char *name_par=0, CHARSET_INFO *cs= &my_charset_bin)
{
maybe_null= null_value= TRUE;
max_length= 0;
name= name_par ? name_par : (char*) "NULL";
fixed= 1;
- collation.set(&my_charset_bin, DERIVATION_IGNORABLE);
+ collation.set(cs, DERIVATION_IGNORABLE);
}
enum Type type() const { return NULL_ITEM; }
- bool eq(const Item *item, bool binary_cmp) const;
+ bool eq(const Item *item, bool binary_cmp) const { return null_eq(item); }
double val_real();
longlong val_int();
String *val_str(String *str);
@@ -2265,13 +2404,9 @@ public:
/* Item represents one placeholder ('?') of prepared statement */
-class Item_param :public Item,
+class Item_param :public Item_basic_value,
private Settable_routine_parameter
{
- char cnvbuf[MAX_FIELD_WIDTH];
- String cnvstr;
- Item *cnvitem;
-
public:
enum enum_item_param_state
{
@@ -2455,7 +2590,8 @@ public:
Item_num *neg() { value= -value; return this; }
uint decimal_precision() const
{ return (uint) (max_length - MY_TEST(value < 0)); }
- bool eq(const Item *, bool binary_cmp) const;
+ bool eq(const Item *item, bool binary_cmp) const
+ { return int_eq(value, item); }
bool check_partition_func_processor(uchar *bool_arg) { return FALSE;}
bool check_vcol_func_processor(uchar *arg) { return FALSE;}
};
@@ -2576,7 +2712,8 @@ public:
{ return new Item_float(name, value, decimals, max_length); }
Item_num *neg() { value= -value; return this; }
virtual void print(String *str, enum_query_type query_type);
- bool eq(const Item *, bool binary_cmp) const;
+ bool eq(const Item *item, bool binary_cmp) const
+ { return real_eq(value, item); }
};
@@ -2594,70 +2731,98 @@ public:
str->append(func_name);
}
- Item *safe_charset_converter(CHARSET_INFO *tocs);
+ Item *safe_charset_converter(CHARSET_INFO *tocs)
+ {
+ return const_charset_converter(tocs, true, func_name);
+ }
};
class Item_string :public Item_basic_constant
{
-public:
- Item_string(const char *str,uint length,
- CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE,
- uint repertoire= MY_REPERTOIRE_UNICODE30)
- : m_cs_specified(FALSE)
+ bool m_cs_specified;
+protected:
+ /**
+ Set the value of m_cs_specified attribute.
+
+ m_cs_specified attribute shows whether character-set-introducer was
+ explicitly specified in the original query for this text literal or
+ not. The attribute makes sense (is used) only for views.
+
+ This operation is to be called from the parser during parsing an input
+ query.
+ */
+ inline void set_cs_specified(bool cs_specified)
{
- str_value.set_or_copy_aligned(str, length, cs);
- collation.set(cs, dv, repertoire);
- /*
- We have to have a different max_length than 'length' here to
- ensure that we get the right length if we do use the item
- to create a new table. In this case max_length must be the maximum
- number of chars for a string of this type because we in Create_field::
- divide the max_length with mbmaxlen).
- */
- max_length= str_value.numchars()*cs->mbmaxlen;
- set_name(str, length, cs);
- decimals=NOT_FIXED_DEC;
+ m_cs_specified= cs_specified;
+ }
+ void fix_from_value(Derivation dv, const Metadata metadata)
+ {
+ fix_charset_and_length_from_str_value(dv, metadata);
// it is constant => can be used without fix_fields (and frequently used)
fixed= 1;
}
+ void fix_and_set_name_from_value(Derivation dv, const Metadata metadata)
+ {
+ fix_from_value(dv, metadata);
+ set_name(str_value.ptr(), str_value.length(), str_value.charset());
+ }
+protected:
/* Just create an item and do not fill string representation */
Item_string(CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE)
: m_cs_specified(FALSE)
{
collation.set(cs, dv);
max_length= 0;
- set_name(NULL, 0, cs);
+ set_name(NULL, 0, system_charset_info);
decimals= NOT_FIXED_DEC;
fixed= 1;
}
- Item_string(const char *name_par, const char *str, uint length,
- CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE,
- uint repertoire= MY_REPERTOIRE_UNICODE30)
+public:
+ // Constructors with the item name set from its value
+ Item_string(const char *str, uint length, CHARSET_INFO *cs,
+ Derivation dv, uint repertoire)
: m_cs_specified(FALSE)
{
str_value.set_or_copy_aligned(str, length, cs);
- collation.set(cs, dv, repertoire);
- max_length= str_value.numchars()*cs->mbmaxlen;
- set_name(name_par, 0, cs);
- decimals=NOT_FIXED_DEC;
- // it is constant => can be used without fix_fields (and frequently used)
- fixed= 1;
+ fix_and_set_name_from_value(dv, Metadata(&str_value, repertoire));
}
- /*
- This is used in stored procedures to avoid memory leaks and
- does a deep copy of its argument.
- */
- void set_str_with_copy(const char *str_arg, uint length_arg)
+ Item_string(const char *str, uint length,
+ CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE)
+ : m_cs_specified(FALSE)
{
- str_value.copy(str_arg, length_arg, collation.collation);
- max_length= str_value.numchars() * collation.collation->mbmaxlen;
+ str_value.set_or_copy_aligned(str, length, cs);
+ fix_and_set_name_from_value(dv, Metadata(&str_value));
}
- void set_repertoire_from_value()
+ Item_string(const String *str, CHARSET_INFO *tocs, uint *conv_errors,
+ Derivation dv, uint repertoire)
+ :m_cs_specified(false)
{
- collation.repertoire= my_string_repertoire(str_value.charset(),
- str_value.ptr(),
- str_value.length());
+ if (str_value.copy(str, tocs, conv_errors))
+ str_value.set("", 0, tocs); // EOM ?
+ str_value.mark_as_const();
+ fix_and_set_name_from_value(dv, Metadata(&str_value, repertoire));
+ }
+ // Constructors with an externally provided item name
+ Item_string(const char *name_par, const char *str, uint length,
+ CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE)
+ :m_cs_specified(false)
+ {
+ str_value.set_or_copy_aligned(str, length, cs);
+ fix_from_value(dv, Metadata(&str_value));
+ set_name(name_par, 0, system_charset_info);
+ }
+ Item_string(const char *name_par, const char *str, uint length,
+ CHARSET_INFO *cs, Derivation dv, uint repertoire)
+ :m_cs_specified(false)
+ {
+ str_value.set_or_copy_aligned(str, length, cs);
+ fix_from_value(dv, Metadata(&str_value, repertoire));
+ set_name(name_par, 0, system_charset_info);
+ }
+ void print_value(String *to) const
+ {
+ str_value.print(to);
}
enum Type type() const { return STRING_ITEM; }
double val_real();
@@ -2672,14 +2837,19 @@ public:
enum Item_result result_type () const { return STRING_RESULT; }
enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
bool basic_const_item() const { return 1; }
- bool eq(const Item *item, bool binary_cmp) const;
+ bool eq(const Item *item, bool binary_cmp) const
+ {
+ return str_eq(&str_value, item, binary_cmp);
+ }
Item *clone_item()
{
return new Item_string(name, str_value.ptr(),
- str_value.length(), collation.collation);
+ str_value.length(), collation.collation);
+ }
+ Item *safe_charset_converter(CHARSET_INFO *tocs)
+ {
+ return const_charset_converter(tocs, true);
}
- Item *safe_charset_converter(CHARSET_INFO *tocs);
- Item *charset_converter(CHARSET_INFO *tocs, bool lossless);
inline void append(char *str, uint length)
{
str_value.append(str, length);
@@ -2713,23 +2883,79 @@ public:
return m_cs_specified;
}
- /**
- Set the value of m_cs_specified attribute.
+ String *check_well_formed_result(bool send_error)
+ { return Item::check_well_formed_result(&str_value, send_error); }
- m_cs_specified attribute shows whether character-set-introducer was
- explicitly specified in the original query for this text literal or
- not. The attribute makes sense (is used) only for views.
+ enum_field_types odbc_temporal_literal_type(const LEX_STRING *type_str) const
+ {
+ /*
+ If string is a reasonably short pure ASCII string literal,
+ try to parse known ODBC style date, time or timestamp literals,
+ e.g:
+ SELECT {d'2001-01-01'};
+ SELECT {t'10:20:30'};
+ SELECT {ts'2001-01-01 10:20:30'};
+ */
+ if (collation.repertoire == MY_REPERTOIRE_ASCII &&
+ str_value.length() < MAX_DATE_STRING_REP_LENGTH * 4)
+ {
+ if (type_str->length == 1)
+ {
+ if (type_str->str[0] == 'd') /* {d'2001-01-01'} */
+ return MYSQL_TYPE_DATE;
+ else if (type_str->str[0] == 't') /* {t'10:20:30'} */
+ return MYSQL_TYPE_TIME;
+ }
+ else if (type_str->length == 2) /* {ts'2001-01-01 10:20:30'} */
+ {
+ if (type_str->str[0] == 't' && type_str->str[1] == 's')
+ return MYSQL_TYPE_DATETIME;
+ }
+ }
+ return MYSQL_TYPE_STRING; // Not a temporal literal
+ }
+};
- This operation is to be called from the parser during parsing an input
- query.
- */
- inline void set_cs_specified(bool cs_specified)
+
+class Item_string_with_introducer :public Item_string
+{
+public:
+ Item_string_with_introducer(const char *str, uint length, CHARSET_INFO *cs)
+ :Item_string(str, length, cs)
{
- m_cs_specified= cs_specified;
+ set_cs_specified(true);
}
+ Item_string_with_introducer(const String *str, CHARSET_INFO *tocs)
+ :Item_string(str->ptr(), str->length(), tocs)
+ {
+ set_cs_specified(true);
+ }
+};
-private:
- bool m_cs_specified;
+
+class Item_string_sys :public Item_string
+{
+public:
+ Item_string_sys(const char *str, uint length)
+ :Item_string(str, length, system_charset_info)
+ { }
+ Item_string_sys(const char *str)
+ :Item_string(str, strlen(str), system_charset_info)
+ { }
+};
+
+
+class Item_string_ascii :public Item_string
+{
+public:
+ Item_string_ascii(const char *str, uint length)
+ :Item_string(str, length, &my_charset_latin1,
+ DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII)
+ { }
+ Item_string_ascii(const char *str)
+ :Item_string(str, strlen(str), &my_charset_latin1,
+ DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII)
+ { }
};
@@ -2749,7 +2975,17 @@ public:
Derivation dv= DERIVATION_COERCIBLE)
:Item_string(NullS, str, length, cs, dv), func_name(name_par)
{}
- Item *safe_charset_converter(CHARSET_INFO *tocs);
+ Item_static_string_func(const char *name_par,
+ const String *str,
+ CHARSET_INFO *tocs, uint *conv_errors,
+ Derivation dv, uint repertoire)
+ :Item_string(str, tocs, conv_errors, dv, repertoire),
+ func_name(name_par)
+ {}
+ Item *safe_charset_converter(CHARSET_INFO *tocs)
+ {
+ return const_charset_converter(tocs, true, func_name);
+ }
virtual inline void print(String *str, enum_query_type query_type)
{
@@ -2852,11 +3088,19 @@ public:
enum Type type() const { return VARBIN_ITEM; }
enum Item_result result_type () const { return STRING_RESULT; }
enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; }
- virtual Item *safe_charset_converter(CHARSET_INFO *tocs);
+ virtual Item *safe_charset_converter(CHARSET_INFO *tocs)
+ {
+ return const_charset_converter(tocs, true);
+ }
bool check_partition_func_processor(uchar *int_arg) {return FALSE;}
bool check_vcol_func_processor(uchar *arg) { return FALSE;}
bool basic_const_item() const { return 1; }
- bool eq(const Item *item, bool binary_cmp) const;
+ bool eq(const Item *item, bool binary_cmp) const
+ {
+ return item->basic_const_item() && item->type() == type() &&
+ item->cast_to_int_type() == cast_to_int_type() &&
+ str_value.bin_eq(&((Item_hex_constant*)item)->str_value);
+ }
String *val_str(String*) { DBUG_ASSERT(fixed == 1); return &str_value; }
};
@@ -3652,7 +3896,7 @@ public:
{
ref= &outer_ref;
set_properties();
- fixed= 0;
+ fixed= 0; /* reset flag set in set_properties() */
}
Item_outer_ref(Name_resolution_context *context_arg, Item **item,
const char *table_name_arg, const char *field_name_arg,
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index bf28b00c908..71674c61d47 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -883,6 +883,18 @@ class in_string :public in_vector
{
char buff[STRING_BUFFER_USUAL_SIZE];
String tmp;
+ class Item_string_for_in_vector: public Item_string
+ {
+ public:
+ Item_string_for_in_vector(CHARSET_INFO *cs):
+ Item_string(cs)
+ { }
+ void set_value(const String *str)
+ {
+ str_value= *str;
+ collation.set(str->charset());
+ }
+ };
public:
in_string(uint elements,qsort2_cmp cmp_func, CHARSET_INFO *cs);
~in_string();
@@ -890,13 +902,13 @@ public:
uchar *get_value(Item *item);
Item* create_item()
{
- return new Item_string(collation);
+ return new Item_string_for_in_vector(collation);
}
void value_to_item(uint pos, Item *item)
{
String *str=((String*) base)+pos;
- Item_string *to= (Item_string*)item;
- to->str_value= *str;
+ Item_string_for_in_vector *to= (Item_string_for_in_vector*) item;
+ to->set_value(str);
}
Item_result result_type() { return STRING_RESULT; }
};
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 532654910e2..fa8249c3321 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -5235,26 +5235,7 @@ Create_func_space Create_func_space::s_singleton;
Item*
Create_func_space::create_1_arg(THD *thd, Item *arg1)
{
- /**
- TODO: Fix Bug#23637
- The parsed item tree should not depend on
- <code>thd->variables.collation_connection</code>.
- */
- CHARSET_INFO *cs= thd->variables.collation_connection;
- Item *sp;
-
- if (cs->mbminlen > 1)
- {
- uint dummy_errors;
- sp= new (thd->mem_root) Item_string("", 0, cs, DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
- sp->str_value.copy(" ", 1, &my_charset_latin1, cs, &dummy_errors);
- }
- else
- {
- sp= new (thd->mem_root) Item_string(" ", 1, cs, DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
- }
-
- return new (thd->mem_root) Item_func_repeat(sp, arg1);
+ return new (thd->mem_root) Item_func_space(arg1);
}
diff --git a/sql/item_create.h b/sql/item_create.h
index 5f1a8c6006d..05fe48f656a 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -173,6 +173,15 @@ Item *create_temporal_literal(THD *thd,
CHARSET_INFO *cs,
enum_field_types type,
bool send_error);
+inline
+Item *create_temporal_literal(THD *thd, const String *str,
+ enum_field_types type,
+ bool send_error)
+{
+ return create_temporal_literal(thd,
+ str->ptr(), str->length(), str->charset(),
+ type, send_error);
+}
int item_create_init();
void item_create_cleanup();
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 6e715192a22..1845c6dff98 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -3973,9 +3973,13 @@ longlong Item_master_pos_wait::val_int()
else
connection_name= thd->variables.default_master_connection;
- if (!(mi= master_info_index->get_master_info(&connection_name,
- Sql_condition::WARN_LEVEL_WARN)))
+ mysql_mutex_lock(&LOCK_active_mi);
+ mi= master_info_index->get_master_info(&connection_name,
+ Sql_condition::WARN_LEVEL_WARN);
+ mysql_mutex_unlock(&LOCK_active_mi);
+ if (!mi)
goto err;
+
if ((event_count = mi->rli.wait_for_pos(thd, log_name, pos, timeout)) == -2)
{
null_value = 1;
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index bb999f132c4..01ff2d412f2 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -2328,32 +2328,6 @@ void Item_func_decode::crypto_transform(String *res)
}
-Item *Item_func_sysconst::safe_charset_converter(CHARSET_INFO *tocs)
-{
- Item_string *conv;
- uint conv_errors;
- String tmp, cstr, *ostr= val_str(&tmp);
- if (null_value)
- {
- Item *null_item= new Item_null((char *) fully_qualified_func_name());
- null_item->collation.set (tocs);
- return null_item;
- }
- cstr.copy(ostr->ptr(), ostr->length(), ostr->charset(), tocs, &conv_errors);
- if (conv_errors ||
- !(conv= new Item_static_string_func(fully_qualified_func_name(),
- cstr.ptr(), cstr.length(),
- cstr.charset(),
- collation.derivation)))
- {
- return NULL;
- }
- conv->str_value.copy();
- conv->str_value.mark_as_const();
- return conv;
-}
-
-
String *Item_func_database::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
@@ -3025,6 +2999,75 @@ err:
}
+void Item_func_space::fix_length_and_dec()
+{
+ collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII);
+ if (args[0]->const_item())
+ {
+ /* must be longlong to avoid truncation */
+ longlong count= args[0]->val_int();
+ if (args[0]->null_value)
+ goto end;
+ /*
+ Assumes that the maximum length of a String is < INT_MAX32.
+ Set here so that rest of code sees out-of-bound value as such.
+ */
+ if (count > INT_MAX32)
+ count= INT_MAX32;
+ fix_char_length_ulonglong(count);
+ return;
+ }
+
+end:
+ max_length= MAX_BLOB_WIDTH;
+ maybe_null= 1;
+}
+
+
+String *Item_func_space::val_str(String *str)
+{
+ uint tot_length;
+ longlong count= args[0]->val_int();
+ const CHARSET_INFO *cs= collation.collation;
+
+ if (args[0]->null_value)
+ goto err; // string and/or delim are null
+ null_value= 0;
+
+ if (count <= 0 && (count == 0 || !args[0]->unsigned_flag))
+ return make_empty_result();
+ /*
+ Assumes that the maximum length of a String is < INT_MAX32.
+ Bounds check on count: If this is triggered, we will error.
+ */
+ if ((ulonglong) count > INT_MAX32)
+ count= INT_MAX32;
+
+ // Safe length check
+ tot_length= (uint) count * cs->mbminlen;
+ if (tot_length > current_thd->variables.max_allowed_packet)
+ {
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WARN_ALLOWED_PACKET_OVERFLOWED,
+ ER(ER_WARN_ALLOWED_PACKET_OVERFLOWED),
+ func_name(),
+ current_thd->variables.max_allowed_packet);
+ goto err;
+ }
+
+ if (str->alloc(tot_length))
+ goto err;
+ str->length(tot_length);
+ str->set_charset(cs);
+ cs->cset->fill(cs, (char*) str->ptr(), tot_length, ' ');
+ return str;
+
+err:
+ null_value= 1;
+ return 0;
+}
+
+
void Item_func_binlog_gtid_pos::fix_length_and_dec()
{
collation.set(system_charset_info);
@@ -3423,7 +3466,7 @@ void Item_func_set_collation::print(String *str, enum_query_type query_type)
str->append(STRING_WITH_LEN(" collate "));
DBUG_ASSERT(args[1]->basic_const_item() &&
args[1]->type() == Item::STRING_ITEM);
- args[1]->str_value.print(str);
+ ((Item_string *)args[1])->print_value(str);
str->append(')');
}
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 4551cc1ab46..8377a20e0a4 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -542,7 +542,10 @@ class Item_func_sysconst :public Item_str_func
public:
Item_func_sysconst()
{ collation.set(system_charset_info,DERIVATION_SYSCONST); }
- Item *safe_charset_converter(CHARSET_INFO *tocs);
+ Item *safe_charset_converter(CHARSET_INFO *tocs)
+ {
+ return const_charset_converter(tocs, true, fully_qualified_func_name());
+ }
/*
Used to create correct Item name in new converted item in
safe_charset_converter, return string representation of this function
@@ -716,6 +719,16 @@ public:
};
+class Item_func_space :public Item_str_func
+{
+public:
+ Item_func_space(Item *arg1):Item_str_func(arg1) {}
+ String *val_str(String *);
+ void fix_length_and_dec();
+ const char *func_name() const { return "space"; }
+};
+
+
class Item_func_binlog_gtid_pos :public Item_str_func
{
String tmp_value;
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 62db351150b..2dadf8b8835 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -3197,19 +3197,13 @@ Item_func_group_concat(Name_resolution_context *context_arg,
/*
We need to allocate:
args - arg_count_field+arg_count_order
- (for possible order items in temporare tables)
+ (for possible order items in temporary tables)
order - arg_count_order
*/
- if (!(args= (Item**) sql_alloc(sizeof(Item*) * arg_count +
+ if (!(args= (Item**) sql_alloc(sizeof(Item*) * arg_count * 2 +
sizeof(ORDER*)*arg_count_order)))
return;
- if (!(orig_args= (Item **) sql_alloc(sizeof(Item *) * arg_count)))
- {
- args= NULL;
- return;
- }
-
order= (ORDER**)(args + arg_count);
/* fill args items of show and sort */
@@ -3230,6 +3224,9 @@ Item_func_group_concat(Name_resolution_context *context_arg,
order_item->item= arg_ptr++;
}
}
+
+ /* orig_args is only used for print() */
+ orig_args= (Item**) (order + arg_count_order);
memcpy(orig_args, args, sizeof(Item*) * arg_count);
}
@@ -3313,6 +3310,7 @@ void Item_func_group_concat::cleanup()
}
DBUG_ASSERT(tree == 0);
}
+
DBUG_VOID_RETURN;
}
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 5fddad56028..4a8bb4cc77d 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -1816,7 +1816,7 @@ void Item_func_date_format::fix_length_and_dec()
if (arg1->type() == STRING_ITEM)
{ // Optimize the normal case
fixed_length=1;
- max_length= format_length(&arg1->str_value) *
+ max_length= format_length(arg1->val_str(NULL)) *
collation.collation->mbmaxlen;
}
else
diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc
index 759b929ff82..932f4245c27 100644
--- a/sql/item_xmlfunc.cc
+++ b/sql/item_xmlfunc.cc
@@ -532,6 +532,32 @@ public:
};
+/**
+ A string whose value may be changed during execution.
+*/
+class Item_string_xml_non_const: public Item_string
+{
+public:
+ Item_string_xml_non_const(const char *str, uint length, CHARSET_INFO *cs)
+ :Item_string(str, length, cs)
+ { }
+ bool const_item() const { return false ; }
+ bool basic_const_item() const { return false; }
+ void set_value(const char *str, uint length, CHARSET_INFO *cs)
+ {
+ str_value.set(str, length, cs);
+ }
+ Item *safe_charset_converter(CHARSET_INFO *tocs)
+ {
+ /*
+ Item_string::safe_charset_converter() does not accept non-constants.
+ Note, conversion is not really needed here anyway.
+ */
+ return this;
+ }
+};
+
+
class Item_nodeset_to_const_comparator :public Item_bool_func
{
String *pxml;
@@ -550,7 +576,8 @@ public:
longlong val_int()
{
Item_func *comp= (Item_func*)args[1];
- Item_string *fake= (Item_string*)(comp->arguments()[0]);
+ Item_string_xml_non_const *fake=
+ (Item_string_xml_non_const*)(comp->arguments()[0]);
String *res= args[0]->val_nodeset(&tmp_nodeset);
MY_XPATH_FLT *fltbeg= (MY_XPATH_FLT*) res->ptr();
MY_XPATH_FLT *fltend= (MY_XPATH_FLT*) (res->ptr() + res->length());
@@ -568,8 +595,8 @@ public:
if ((node->parent == flt->num) &&
(node->type == MY_XML_NODE_TEXT))
{
- fake->str_value.set(node->beg, node->end - node->beg,
- collation.collation);
+ fake->set_value(node->beg, node->end - node->beg,
+ collation.collation);
if (args[1]->val_int())
return 1;
}
@@ -956,14 +983,12 @@ static Item *create_comparator(MY_XPATH *xpath,
{
/*
Compare a node set to a scalar value.
- We just create a fake Item_string() argument,
+ We just create a fake Item_string_xml_non_const() argument,
which will be filled to the partular value
in a loop through all of the nodes in the node set.
*/
- Item_string *fake= new Item_string("", 0, xpath->cs);
- /* Don't cache fake because its value will be changed during comparison.*/
- fake->set_used_tables(RAND_TABLE_BIT);
+ Item_string *fake= new Item_string_xml_non_const("", 0, xpath->cs);
Item_nodeset_func *nodeset;
Item *scalar, *comp;
if (a->type() == Item::XPATH_NODESET)
diff --git a/sql/log.cc b/sql/log.cc
index 79771ab8950..a5fda53ccea 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -4297,6 +4297,7 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included)
included= 1;
to_purge_if_included= my_strdup(ir->name, MYF(0));
}
+ my_atomic_rwlock_destroy(&ir->inuse_relaylog_atomic_lock);
my_free(ir);
ir= next;
}
@@ -7667,6 +7668,13 @@ MYSQL_BIN_LOG::write_transaction_or_stmt(group_commit_entry *entry,
}
}
+ DBUG_EXECUTE_IF("inject_error_writing_xid",
+ {
+ entry->error_cache= NULL;
+ entry->commit_errno= 28;
+ DBUG_RETURN(ER_ERROR_ON_WRITE);
+ });
+
if (entry->end_event->write(&log_file))
{
entry->error_cache= NULL;
@@ -9837,7 +9845,7 @@ set_binlog_snapshot_file(const char *src)
Copy out current values of status variables, for SHOW STATUS or
information_schema.global_status.
- This is called only under LOCK_status, so we can fill in a static array.
+ This is called only under LOCK_show_status, so we can fill in a static array.
*/
void
TC_LOG_BINLOG::set_status_variables(THD *thd)
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 77353d33bf1..0b2807669fa 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -1023,8 +1023,9 @@ Log_event::do_shall_skip(rpl_group_info *rgi)
Relay_log_info *rli= rgi->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) global_system_variables.server_id,
+ " rli->slave_skip_counter: %llu",
+ (ulong) server_id,
+ (ulong) global_system_variables.server_id,
rli->replicate_same_server_id,
rli->slave_skip_counter));
if ((server_id == global_system_variables.server_id &&
@@ -4279,28 +4280,31 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi,
Record any GTID in the same transaction, so slave state is
transactionally consistent.
*/
- if (current_stmt_is_commit && rgi->gtid_pending)
+ if (current_stmt_is_commit)
{
- sub_id= rgi->gtid_sub_id;
- rgi->gtid_pending= false;
-
- gtid= rgi->current_gtid;
thd->variables.option_bits&= ~OPTION_GTID_BEGIN;
- if (rpl_global_gtid_slave_state.record_gtid(thd, &gtid, sub_id, true, false))
+ if (rgi->gtid_pending)
{
- int errcode= thd->get_stmt_da()->sql_errno();
- if (!is_parallel_retry_error(rgi, errcode))
- rli->report(ERROR_LEVEL, ER_CANNOT_UPDATE_GTID_STATE,
- rgi->gtid_info(),
- "Error during COMMIT: failed to update GTID state in "
- "%s.%s: %d: %s",
- "mysql", rpl_gtid_slave_state_table_name.str,
- errcode,
- thd->get_stmt_da()->message());
- trans_rollback(thd);
- sub_id= 0;
- thd->is_slave_error= 1;
- goto end;
+ sub_id= rgi->gtid_sub_id;
+ rgi->gtid_pending= false;
+
+ gtid= rgi->current_gtid;
+ if (rpl_global_gtid_slave_state.record_gtid(thd, &gtid, sub_id, true, false))
+ {
+ int errcode= thd->get_stmt_da()->sql_errno();
+ if (!is_parallel_retry_error(rgi, errcode))
+ rli->report(ERROR_LEVEL, ER_CANNOT_UPDATE_GTID_STATE,
+ rgi->gtid_info(),
+ "Error during COMMIT: failed to update GTID state in "
+ "%s.%s: %d: %s",
+ "mysql", rpl_gtid_slave_state_table_name.str,
+ errcode,
+ thd->get_stmt_da()->message());
+ trans_rollback(thd);
+ sub_id= 0;
+ thd->is_slave_error= 1;
+ goto end;
+ }
}
}
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 64b51ec8138..b08af09b160 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -722,7 +722,7 @@ pthread_key(MEM_ROOT**,THR_MALLOC);
pthread_key(THD*, THR_THD);
mysql_mutex_t LOCK_thread_count, LOCK_thread_cache;
mysql_mutex_t
- LOCK_status, LOCK_error_log, LOCK_short_uuid_generator,
+ LOCK_status, LOCK_show_status, LOCK_error_log, LOCK_short_uuid_generator,
LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create,
LOCK_crypt,
LOCK_global_system_variables,
@@ -896,7 +896,8 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_LOCK_gdl, key_LOCK_global_system_variables,
key_LOCK_manager,
key_LOCK_prepared_stmt_count,
- key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status,
+ key_LOCK_rpl_status, key_LOCK_server_started,
+ key_LOCK_status, key_LOCK_show_status,
key_LOCK_system_variables_hash, key_LOCK_thd_data,
key_LOCK_user_conn, key_LOCK_uuid_short_generator, key_LOG_LOCK_log,
key_master_info_data_lock, key_master_info_run_lock,
@@ -962,6 +963,7 @@ static PSI_mutex_info all_server_mutexes[]=
{ &key_LOCK_rpl_status, "LOCK_rpl_status", PSI_FLAG_GLOBAL},
{ &key_LOCK_server_started, "LOCK_server_started", PSI_FLAG_GLOBAL},
{ &key_LOCK_status, "LOCK_status", PSI_FLAG_GLOBAL},
+ { &key_LOCK_show_status, "LOCK_show_status", PSI_FLAG_GLOBAL},
{ &key_LOCK_system_variables_hash, "LOCK_system_variables_hash", PSI_FLAG_GLOBAL},
{ &key_LOCK_stats, "LOCK_stats", PSI_FLAG_GLOBAL},
{ &key_LOCK_global_user_client_stats, "LOCK_global_user_client_stats", PSI_FLAG_GLOBAL},
@@ -2262,6 +2264,7 @@ static void clean_up_mutexes()
mysql_mutex_destroy(&LOCK_thread_count);
mysql_mutex_destroy(&LOCK_thread_cache);
mysql_mutex_destroy(&LOCK_status);
+ mysql_mutex_destroy(&LOCK_show_status);
mysql_mutex_destroy(&LOCK_delayed_insert);
mysql_mutex_destroy(&LOCK_delayed_status);
mysql_mutex_destroy(&LOCK_delayed_create);
@@ -4545,6 +4548,7 @@ static int init_thread_environment()
mysql_mutex_init(key_LOCK_thread_count, &LOCK_thread_count, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_thread_cache, &LOCK_thread_cache, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_status, &LOCK_status, MY_MUTEX_INIT_FAST);
+ mysql_mutex_init(key_LOCK_show_status, &LOCK_show_status, MY_MUTEX_INIT_SLOW);
mysql_mutex_init(key_LOCK_delayed_insert,
&LOCK_delayed_insert, MY_MUTEX_INIT_FAST);
mysql_mutex_init(key_LOCK_delayed_status,
@@ -6974,7 +6978,8 @@ void handle_connections_sockets()
(void) mysql_socket_close(new_sock);
/*
The connection was refused by TCP wrappers.
- There are no details (by client IP) available to update the host_cache.
+ There are no details (by client IP) available to update the
+ host_cache.
*/
statistic_increment(connection_errors_tcpwrap, &LOCK_status);
continue;
@@ -7975,7 +7980,6 @@ static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff)
var->type= SHOW_MY_BOOL;
var->value= buff;
- mysql_mutex_unlock(&LOCK_status);
mysql_mutex_lock(&LOCK_active_mi);
if (master_info_index)
{
@@ -7987,7 +7991,6 @@ static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff)
mi->rli.slave_running);
}
mysql_mutex_unlock(&LOCK_active_mi);
- mysql_mutex_lock(&LOCK_status);
if (mi)
*((my_bool *)buff)= tmp;
else
@@ -8004,7 +8007,6 @@ static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff)
var->type= SHOW_LONGLONG;
var->value= buff;
- mysql_mutex_unlock(&LOCK_status);
mysql_mutex_lock(&LOCK_active_mi);
if (master_info_index)
{
@@ -8015,7 +8017,6 @@ static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff)
tmp= mi->received_heartbeats;
}
mysql_mutex_unlock(&LOCK_active_mi);
- mysql_mutex_lock(&LOCK_status);
if (mi)
*((longlong *)buff)= tmp;
else
@@ -8032,7 +8033,6 @@ static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff)
var->type= SHOW_CHAR;
var->value= buff;
- mysql_mutex_unlock(&LOCK_status);
mysql_mutex_lock(&LOCK_active_mi);
if (master_info_index)
{
@@ -8043,7 +8043,6 @@ static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff)
tmp= mi->heartbeat_period;
}
mysql_mutex_unlock(&LOCK_active_mi);
- mysql_mutex_lock(&LOCK_status);
if (mi)
sprintf(buff, "%.3f", tmp);
else
diff --git a/sql/mysqld.h b/sql/mysqld.h
index 4e6d9026195..8458d335ea0 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -262,7 +262,8 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list,
key_LOCK_gdl, key_LOCK_global_system_variables,
key_LOCK_logger, key_LOCK_manager,
key_LOCK_prepared_stmt_count,
- key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status,
+ key_LOCK_rpl_status, key_LOCK_server_started,
+ key_LOCK_status, key_LOCK_show_status,
key_LOCK_thd_data,
key_LOCK_user_conn, key_LOG_LOCK_log,
key_master_info_data_lock, key_master_info_run_lock,
@@ -518,7 +519,7 @@ extern MYSQL_PLUGIN_IMPORT key_map key_map_full; /* Should be threaded
Server mutex locks and condition variables.
*/
extern mysql_mutex_t
- LOCK_item_func_sleep, LOCK_status,
+ LOCK_item_func_sleep, LOCK_status, LOCK_show_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,
diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc
index 7764400becb..78218e0418e 100644
--- a/sql/rpl_mi.cc
+++ b/sql/rpl_mi.cc
@@ -38,6 +38,7 @@ Master_info::Master_info(LEX_STRING *connection_name_arg,
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),
+ prev_master_id(0),
using_gtid(USE_GTID_NO), events_queued_since_last_gtid(0),
gtid_reconnect_event_skip_count(0), gtid_event_seen(false)
{
@@ -890,6 +891,9 @@ bool Master_info_index::init_all_master_info()
File index_file_nr;
DBUG_ENTER("init_all_master_info");
+ mysql_mutex_assert_owner(&LOCK_active_mi);
+ DBUG_ASSERT(master_info_index);
+
if ((index_file_nr= my_open(index_file_name,
O_RDWR | O_CREAT | O_BINARY ,
MYF(MY_WME | ME_NOREFRESH))) < 0 ||
@@ -1089,6 +1093,10 @@ Master_info_index::get_master_info(LEX_STRING *connection_name,
("connection_name: '%.*s'", (int) connection_name->length,
connection_name->str));
+ mysql_mutex_assert_owner(&LOCK_active_mi);
+ if (!this) // master_info_index is set to NULL on server shutdown
+ return NULL;
+
/* Make name lower case for comparison */
res= strmake(buff, connection_name->str, connection_name->length);
my_casedn_str(system_charset_info, buff);
@@ -1116,6 +1124,9 @@ bool Master_info_index::check_duplicate_master_info(LEX_STRING *name_arg,
Master_info *mi;
DBUG_ENTER("check_duplicate_master_info");
+ mysql_mutex_assert_owner(&LOCK_active_mi);
+ DBUG_ASSERT(master_info_index);
+
/* Get full host and port name */
if ((mi= master_info_index->get_master_info(name_arg,
Sql_condition::WARN_LEVEL_NOTE)))
@@ -1238,6 +1249,8 @@ bool Master_info_index::give_error_if_slave_running()
{
DBUG_ENTER("warn_if_slave_running");
mysql_mutex_assert_owner(&LOCK_active_mi);
+ if (!this) // master_info_index is set to NULL on server shutdown
+ return TRUE;
for (uint i= 0; i< master_info_hash.records; ++i)
{
diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h
index af739b1dad4..ebb1b541728 100644
--- a/sql/rpl_mi.h
+++ b/sql/rpl_mi.h
@@ -136,6 +136,12 @@ class Master_info : public Slave_reporting_capability
DYNAMIC_ARRAY ignore_server_ids;
ulong master_id;
/*
+ At reconnect and until the first rotate event is seen, prev_master_id is
+ the value of master_id during the previous connection, used to detect
+ silent change of master server during reconnects.
+ */
+ ulong prev_master_id;
+ /*
Which kind of GTID position (if any) is used when connecting to master.
Note that you can not change the numeric values of these, they are used
diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc
index 0d23248539c..cda224ff01b 100644
--- a/sql/rpl_parallel.cc
+++ b/sql/rpl_parallel.cc
@@ -4,7 +4,6 @@
#include "rpl_mi.h"
#include "debug_sync.h"
-
/*
Code for optional parallel execution of replicated events on the slave.
*/
@@ -22,18 +21,22 @@ rpt_handle_event(rpl_parallel_thread::queued_event *qev,
rpl_group_info *rgi= qev->rgi;
Relay_log_info *rli= rgi->rli;
THD *thd= rgi->thd;
+ Log_event *ev;
+
+ DBUG_ASSERT(qev->typ == rpl_parallel_thread::queued_event::QUEUED_EVENT);
+ ev= qev->ev;
thd->system_thread_info.rpl_sql_info->rpl_filter = rli->mi->rpl_filter;
+ ev->thd= thd;
- /* ToDo: Access to thd, and what about rli, split out a parallel part? */
- mysql_mutex_lock(&rli->data_lock);
- qev->ev->thd= thd;
strcpy(rgi->event_relay_log_name_buf, qev->event_relay_log_name);
rgi->event_relay_log_name= rgi->event_relay_log_name_buf;
rgi->event_relay_log_pos= qev->event_relay_log_pos;
rgi->future_event_relay_log_pos= qev->future_event_relay_log_pos;
strcpy(rgi->future_event_master_log_name, qev->future_event_master_log_name);
- err= apply_event_and_update_pos(qev->ev, thd, rgi, rpt);
+ mysql_mutex_lock(&rli->data_lock);
+ /* Mutex will be released in apply_event_and_update_pos(). */
+ err= apply_event_and_update_pos(ev, thd, rgi, rpt);
thread_safe_increment64(&rli->executed_entries,
&slave_executed_entries_lock);
@@ -47,6 +50,8 @@ handle_queued_pos_update(THD *thd, rpl_parallel_thread::queued_event *qev)
{
int cmp;
Relay_log_info *rli;
+ rpl_parallel_entry *e;
+
/*
Events that are not part of an event group, such as Format Description,
Stop, GTID List and such, are executed directly in the driver SQL thread,
@@ -57,6 +62,13 @@ handle_queued_pos_update(THD *thd, rpl_parallel_thread::queued_event *qev)
if ((thd->variables.option_bits & OPTION_BEGIN) &&
opt_using_transactions)
return;
+
+ /* Do not update position if an earlier event group caused an error abort. */
+ DBUG_ASSERT(qev->typ == rpl_parallel_thread::queued_event::QUEUED_POS_UPDATE);
+ e= qev->entry_for_queued;
+ if (e->stop_on_error_sub_id < (uint64)ULONGLONG_MAX || e->force_abort)
+ return;
+
rli= qev->rgi->rli;
mysql_mutex_lock(&rli->data_lock);
cmp= strcmp(rli->group_relay_log_name, qev->event_relay_log_name);
@@ -318,6 +330,15 @@ do_retry:
thd->wait_for_commit_ptr->unregister_wait_for_prior_commit();
rgi->cleanup_context(thd, 1);
+ /*
+ If we retry due to a deadlock kill that occured during the commit step, we
+ might have already updated (but not committed) an update of table
+ mysql.gtid_slave_pos, and cleared the gtid_pending flag. Now we have
+ rolled back any such update, so we must set the gtid_pending flag back to
+ true so that we will do a new update when/if we succeed with the retry.
+ */
+ rgi->gtid_pending= true;
+
mysql_mutex_lock(&rli->data_lock);
++rli->retried_trans;
statistic_increment(slave_retried_transactions, LOCK_status);
@@ -557,7 +578,7 @@ handle_rpl_parallel_thread(void *arg)
bool end_of_group, group_ending;
total_event_size+= events->event_size;
- if (!events->ev)
+ if (events->typ == rpl_parallel_thread::queued_event::QUEUED_POS_UPDATE)
{
handle_queued_pos_update(thd, events);
events->next= qevs_to_free;
@@ -565,6 +586,31 @@ handle_rpl_parallel_thread(void *arg)
events= next;
continue;
}
+ else if (events->typ ==
+ rpl_parallel_thread::queued_event::QUEUED_MASTER_RESTART)
+ {
+ if (in_event_group)
+ {
+ /*
+ Master restarted (crashed) in the middle of an event group.
+ So we need to roll back and discard that event group.
+ */
+ group_rgi->cleanup_context(thd, 1);
+ in_event_group= false;
+ finish_event_group(thd, group_rgi->gtid_sub_id,
+ events->entry_for_queued, group_rgi);
+
+ group_rgi->next= rgis_to_free;
+ rgis_to_free= group_rgi;
+ thd->rgi_slave= group_rgi= NULL;
+ }
+
+ events->next= qevs_to_free;
+ qevs_to_free= events;
+ events= next;
+ continue;
+ }
+ DBUG_ASSERT(events->typ==rpl_parallel_thread::queued_event::QUEUED_EVENT);
thd->rgi_slave= group_rgi= rgi;
gco= rgi->gco;
@@ -797,9 +843,9 @@ handle_rpl_parallel_thread(void *arg)
{
if (last_ir)
{
- my_atomic_rwlock_wrlock(&rli->inuse_relaylog_atomic_lock);
+ my_atomic_rwlock_wrlock(&last_ir->inuse_relaylog_atomic_lock);
my_atomic_add64(&last_ir->dequeued_count, accumulated_ir_count);
- my_atomic_rwlock_wrunlock(&rli->inuse_relaylog_atomic_lock);
+ my_atomic_rwlock_wrunlock(&last_ir->inuse_relaylog_atomic_lock);
accumulated_ir_count= 0;
}
last_ir= ir;
@@ -810,9 +856,9 @@ handle_rpl_parallel_thread(void *arg)
}
if (last_ir)
{
- my_atomic_rwlock_wrlock(&rli->inuse_relaylog_atomic_lock);
+ my_atomic_rwlock_wrlock(&last_ir->inuse_relaylog_atomic_lock);
my_atomic_add64(&last_ir->dequeued_count, accumulated_ir_count);
- my_atomic_rwlock_wrunlock(&rli->inuse_relaylog_atomic_lock);
+ my_atomic_rwlock_wrunlock(&last_ir->inuse_relaylog_atomic_lock);
}
if ((events= rpt->event_queue) != NULL)
@@ -1073,6 +1119,7 @@ rpl_parallel_thread::get_qev_common(Log_event *ev, ulonglong event_size)
my_error(ER_OUTOFMEMORY, MYF(0), (int)sizeof(*qev));
return NULL;
}
+ qev->typ= rpl_parallel_thread::queued_event::QUEUED_EVENT;
qev->ev= ev;
qev->event_size= event_size;
qev->next= NULL;
@@ -1593,6 +1640,91 @@ rpl_parallel::workers_idle()
}
+int
+rpl_parallel_entry::queue_master_restart(rpl_group_info *rgi,
+ Format_description_log_event *fdev)
+{
+ uint32 idx;
+ rpl_parallel_thread *thr;
+ rpl_parallel_thread::queued_event *qev;
+ Relay_log_info *rli= rgi->rli;
+
+ /*
+ We only need to queue the server restart if we still have a thread working
+ on a (potentially partial) event group.
+
+ If the last thread we queued for has finished, then it cannot have any
+ partial event group that needs aborting.
+
+ Thus there is no need for the full complexity of choose_thread(). We only
+ need to check if we have a current worker thread, and queue for it if so.
+ */
+ idx= rpl_thread_idx;
+ thr= rpl_threads[idx];
+ if (!thr)
+ return 0;
+ mysql_mutex_lock(&thr->LOCK_rpl_thread);
+ if (thr->current_owner != &rpl_threads[idx])
+ {
+ /* No active worker thread, so no need to queue the master restart. */
+ mysql_mutex_unlock(&thr->LOCK_rpl_thread);
+ return 0;
+ }
+
+ if (!(qev= thr->get_qev(fdev, 0, rli)))
+ {
+ mysql_mutex_unlock(&thr->LOCK_rpl_thread);
+ return 1;
+ }
+
+ qev->rgi= rgi;
+ qev->typ= rpl_parallel_thread::queued_event::QUEUED_MASTER_RESTART;
+ qev->entry_for_queued= this;
+ qev->ir= rli->last_inuse_relaylog;
+ ++qev->ir->queued_count;
+ thr->enqueue(qev);
+ mysql_mutex_unlock(&thr->LOCK_rpl_thread);
+ return 0;
+}
+
+
+int
+rpl_parallel::wait_for_workers_idle(THD *thd)
+{
+ uint32 i, max_i;
+
+ /*
+ The domain_hash is only accessed by the SQL driver thread, so it is safe
+ to iterate over without a lock.
+ */
+ max_i= domain_hash.records;
+ for (i= 0; i < max_i; ++i)
+ {
+ bool active;
+ wait_for_commit my_orderer;
+ struct rpl_parallel_entry *e;
+
+ e= (struct rpl_parallel_entry *)my_hash_element(&domain_hash, i);
+ mysql_mutex_lock(&e->LOCK_parallel_entry);
+ if ((active= (e->current_sub_id > e->last_committed_sub_id)))
+ {
+ wait_for_commit *waitee= &e->current_group_info->commit_orderer;
+ my_orderer.register_wait_for_prior_commit(waitee);
+ thd->wait_for_commit_ptr= &my_orderer;
+ }
+ mysql_mutex_unlock(&e->LOCK_parallel_entry);
+ if (active)
+ {
+ int err= my_orderer.wait_for_prior_commit(thd);
+ thd->wait_for_commit_ptr= NULL;
+ if (err)
+ return err;
+ }
+ }
+ return 0;
+}
+
+
/*
This is used when we get an error during processing in do_event();
We will not queue any event to the thread, but we still need to wake it up
@@ -1660,6 +1792,33 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
/* ToDo: what to do with this lock?!? */
mysql_mutex_unlock(&rli->data_lock);
+ if (typ == FORMAT_DESCRIPTION_EVENT)
+ {
+ Format_description_log_event *fdev=
+ static_cast<Format_description_log_event *>(ev);
+ if (fdev->created)
+ {
+ /*
+ This format description event marks a new binlog after a master server
+ restart. We are going to close all temporary tables to clean up any
+ possible left-overs after a prior master crash.
+
+ Thus we need to wait for all prior events to execute to completion,
+ in case they need access to any of the temporary tables.
+
+ We also need to notify the worker thread running the prior incomplete
+ event group (if any), as such event group signifies an incompletely
+ written group cut short by a master crash, and must be rolled back.
+ */
+ if (current->queue_master_restart(serial_rgi, fdev) ||
+ wait_for_workers_idle(rli->sql_driver_thd))
+ {
+ delete ev;
+ return 1;
+ }
+ }
+ }
+
/*
Stop queueing additional event groups once the SQL thread is requested to
stop.
@@ -1815,7 +1974,7 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
return 1;
}
/*
- Queue an empty event, so that the position will be updated in a
+ Queue a position update, so that the position will be updated in a
reasonable way relative to other events:
- If the currently executing events are queued serially for a single
@@ -1826,7 +1985,8 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
least the position will not be updated until one of them has reached
the current point.
*/
- qev->ev= NULL;
+ qev->typ= rpl_parallel_thread::queued_event::QUEUED_POS_UPDATE;
+ qev->entry_for_queued= e;
}
else
{
diff --git a/sql/rpl_parallel.h b/sql/rpl_parallel.h
index 415259cd3c4..b114ee4ebcb 100644
--- a/sql/rpl_parallel.h
+++ b/sql/rpl_parallel.h
@@ -72,7 +72,20 @@ struct rpl_parallel_thread {
rpl_parallel_entry *current_entry;
struct queued_event {
queued_event *next;
- Log_event *ev;
+ /*
+ queued_event can hold either an event to be executed, or just a binlog
+ position to be updated without any associated event.
+ */
+ enum queued_event_t {
+ QUEUED_EVENT,
+ QUEUED_POS_UPDATE,
+ QUEUED_MASTER_RESTART
+ } typ;
+ union {
+ Log_event *ev; /* QUEUED_EVENT */
+ rpl_parallel_entry *entry_for_queued; /* QUEUED_POS_UPDATE and
+ QUEUED_MASTER_RESTART */
+ };
rpl_group_info *rgi;
inuse_relaylog *ir;
ulonglong future_event_relay_log_pos;
@@ -216,8 +229,8 @@ struct rpl_parallel_entry {
rpl_parallel_thread * choose_thread(rpl_group_info *rgi, bool *did_enter_cond,
PSI_stage_info *old_stage, bool reuse);
- group_commit_orderer *get_gco();
- void free_gco(group_commit_orderer *gco);
+ int queue_master_restart(rpl_group_info *rgi,
+ Format_description_log_event *fdev);
};
struct rpl_parallel {
HASH domain_hash;
@@ -231,6 +244,7 @@ struct rpl_parallel {
void wait_for_done(THD *thd, Relay_log_info *rli);
void stop_during_until();
bool workers_idle();
+ int wait_for_workers_idle(THD *thd);
int do_event(rpl_group_info *serial_rgi, Log_event *ev, ulonglong event_size);
};
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index 0b133555cea..754b877f654 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -92,7 +92,6 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery)
mysql_cond_init(key_relay_log_info_start_cond, &start_cond, NULL);
mysql_cond_init(key_relay_log_info_stop_cond, &stop_cond, NULL);
mysql_cond_init(key_relay_log_info_log_space_cond, &log_space_cond, NULL);
- my_atomic_rwlock_init(&inuse_relaylog_atomic_lock);
relay_log.init_pthread_objects();
DBUG_VOID_RETURN;
}
@@ -108,6 +107,7 @@ Relay_log_info::~Relay_log_info()
{
DBUG_ASSERT(cur->queued_count == cur->dequeued_count);
inuse_relaylog *next= cur->next;
+ my_atomic_rwlock_destroy(&cur->inuse_relaylog_atomic_lock);
my_free(cur);
cur= next;
}
@@ -118,7 +118,6 @@ Relay_log_info::~Relay_log_info()
mysql_cond_destroy(&start_cond);
mysql_cond_destroy(&stop_cond);
mysql_cond_destroy(&log_space_cond);
- my_atomic_rwlock_destroy(&inuse_relaylog_atomic_lock);
relay_log.cleanup();
DBUG_VOID_RETURN;
}
@@ -1371,6 +1370,7 @@ Relay_log_info::alloc_inuse_relaylog(const char *name)
last_inuse_relaylog->next= ir;
}
last_inuse_relaylog= ir;
+ my_atomic_rwlock_init(&ir->inuse_relaylog_atomic_lock);
return 0;
}
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index ce30813790c..3a8d87030ad 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -170,8 +170,6 @@ public:
*/
inuse_relaylog *inuse_relaylog_list;
inuse_relaylog *last_inuse_relaylog;
- /* Lock used to protect inuse_relaylog::dequeued_count */
- my_atomic_rwlock_t inuse_relaylog_atomic_lock;
/*
Needed to deal properly with cur_log getting closed and re-opened with
@@ -253,10 +251,11 @@ public:
errors, and have been manually applied by DBA already.
Must be ulong as it's refered to from set_var.cc
*/
- volatile ulong slave_skip_counter;
+ volatile ulonglong slave_skip_counter;
+ ulonglong max_relay_log_size;
+
volatile ulong abort_pos_wait; /* Incremented on change master */
volatile ulong slave_run_id; /* Incremented on slave start */
- ulong max_relay_log_size;
mysql_mutex_t log_space_lock;
mysql_cond_t log_space_cond;
/*
@@ -504,6 +503,8 @@ struct inuse_relaylog {
/* Set when all events have been read from a relaylog. */
bool completed;
char name[FN_REFLEN];
+ /* Lock used to protect inuse_relaylog::dequeued_count */
+ my_atomic_rwlock_t inuse_relaylog_atomic_lock;
};
diff --git a/sql/set_var.h b/sql/set_var.h
index 19d2b4fd14c..064da1404fa 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -238,9 +238,7 @@ public:
if (value_arg && value_arg->type() == Item::FIELD_ITEM)
{
Item_field *item= (Item_field*) value_arg;
- if (!(value=new Item_string(item->field_name,
- (uint) strlen(item->field_name),
- system_charset_info))) // names are utf8
+ if (!(value=new Item_string_sys(item->field_name))) // names are utf8
value=value_arg; /* Give error message later */
}
else
diff --git a/sql/slave.cc b/sql/slave.cc
index 93bb8669632..0f2d9f1d3d4 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -2232,6 +2232,7 @@ slave_killed_err:
static bool wait_for_relay_log_space(Relay_log_info* rli)
{
bool slave_killed=0;
+ bool ignore_log_space_limit;
Master_info* mi = rli->mi;
PSI_stage_info old_stage;
THD* thd = mi->io_thd;
@@ -2247,6 +2248,11 @@ static bool wait_for_relay_log_space(Relay_log_info* rli)
!rli->ignore_log_space_limit)
mysql_cond_wait(&rli->log_space_cond, &rli->log_space_lock);
+ ignore_log_space_limit= rli->ignore_log_space_limit;
+ rli->ignore_log_space_limit= 0;
+
+ thd->EXIT_COND(&old_stage);
+
/*
Makes the IO thread read only one event at a time
until the SQL thread is able to purge the relay
@@ -2270,7 +2276,8 @@ static bool wait_for_relay_log_space(Relay_log_info* rli)
thread sleeps waiting for events.
*/
- if (rli->ignore_log_space_limit)
+
+ if (ignore_log_space_limit)
{
#ifndef DBUG_OFF
{
@@ -2292,11 +2299,8 @@ static bool wait_for_relay_log_space(Relay_log_info* rli)
mysql_mutex_unlock(&mi->data_lock);
rli->sql_force_rotate_relay= false;
}
-
- rli->ignore_log_space_limit= false;
}
- thd->EXIT_COND(&old_stage);
DBUG_RETURN(slave_killed);
}
@@ -2860,7 +2864,8 @@ bool show_all_master_info(THD* thd)
if (send_show_master_info_header(thd, 1, gtid_pos.length()))
DBUG_RETURN(TRUE);
- if (!(elements= master_info_index->master_info_hash.records))
+ if (!master_info_index ||
+ !(elements= master_info_index->master_info_hash.records))
goto end;
/*
@@ -5308,6 +5313,86 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
event_len - BINLOG_CHECKSUM_LEN : event_len,
mi->rli.relay_log.description_event_for_queue);
+ if (unlikely(mi->gtid_reconnect_event_skip_count) &&
+ unlikely(!mi->gtid_event_seen) &&
+ rev.is_artificial_event() &&
+ (mi->prev_master_id != mi->master_id ||
+ strcmp(rev.new_log_ident, mi->master_log_name) != 0))
+ {
+ /*
+ Artificial Rotate_log_event is the first event we receive at the start
+ of each master binlog file. It gives the name of the new binlog file.
+
+ Normally, we already have this name from the real rotate event at the
+ end of the previous binlog file (unless we are making a new connection
+ using GTID). But if the master server restarted/crashed, there is no
+ rotate event at the end of the prior binlog file, so the name is new.
+
+ We use this fact to handle a special case of master crashing. If the
+ master crashed while writing the binlog, it might end with a partial
+ event group lacking the COMMIT/XID event, which must be rolled
+ back. If the slave IO thread happens to get a disconnect in the middle
+ of exactly this event group, it will try to reconnect at the same GTID
+ and skip already fetched events. However, that GTID did not commit on
+ the master before the crash, so it does not really exist, and the
+ master will connect the slave at the next following GTID starting in
+ the next binlog. This could confuse the slave and make it mix the
+ start of one event group with the end of another.
+
+ But we detect this case here, by noticing the change of binlog name
+ which detects the missing rotate event at the end of the previous
+ binlog file. In this case, we reset the counters to make us not skip
+ the next event group, and queue an artificial Format Description
+ event. The previously fetched incomplete event group will then be
+ rolled back when the Format Description event is executed by the SQL
+ thread.
+
+ A similar case is if the reconnect somehow connects to a different
+ master server (like due to a network proxy or IP address takeover).
+ We detect this case by noticing a change of server_id and in this
+ case likewise rollback the partially received event group.
+ */
+ Format_description_log_event fdle(4);
+
+ if (mi->prev_master_id != mi->master_id)
+ sql_print_warning("The server_id of master server changed in the "
+ "middle of GTID %u-%u-%llu. Assuming a change of "
+ "master server, so rolling back the previously "
+ "received partial transaction. Expected: %lu, "
+ "received: %lu", mi->last_queued_gtid.domain_id,
+ mi->last_queued_gtid.server_id,
+ mi->last_queued_gtid.seq_no,
+ mi->prev_master_id, mi->master_id);
+ else if (strcmp(rev.new_log_ident, mi->master_log_name) != 0)
+ sql_print_warning("Unexpected change of master binlog file name in the "
+ "middle of GTID %u-%u-%llu, assuming that master has "
+ "crashed and rolling back the transaction. Expected: "
+ "'%s', received: '%s'",
+ mi->last_queued_gtid.domain_id,
+ mi->last_queued_gtid.server_id,
+ mi->last_queued_gtid.seq_no,
+ mi->master_log_name, rev.new_log_ident);
+
+ mysql_mutex_lock(log_lock);
+ if (likely(!fdle.write(rli->relay_log.get_log_file()) &&
+ !rli->relay_log.flush_and_sync(NULL)))
+ {
+ rli->relay_log.harvest_bytes_written(&rli->log_space_total);
+ }
+ else
+ {
+ error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
+ mysql_mutex_unlock(log_lock);
+ goto err;
+ }
+ rli->relay_log.signal_update();
+ mysql_mutex_unlock(log_lock);
+
+ mi->gtid_reconnect_event_skip_count= 0;
+ mi->events_queued_since_last_gtid= 0;
+ }
+ mi->prev_master_id= mi->master_id;
+
if (unlikely(process_io_rotate(mi, &rev)))
{
error= ER_SLAVE_RELAY_LOG_WRITE_FAILURE;
diff --git a/sql/slave.h b/sql/slave.h
index 4b5bc1686fb..e65b4a589a1 100644
--- a/sql/slave.h
+++ b/sql/slave.h
@@ -240,25 +240,19 @@ pthread_handler_t handle_slave_sql(void *arg);
bool net_request_file(NET* net, const char* fname);
extern bool volatile abort_loop;
-extern Master_info main_mi, *active_mi; /* active_mi for multi-master */
+extern Master_info *active_mi; /* active_mi for multi-master */
extern Master_info *default_master_info; /* To replace active_mi */
extern Master_info_index *master_info_index;
extern LEX_STRING default_master_connection_name;
-extern LIST master_list;
extern my_bool replicate_same_server_id;
extern int disconnect_slave_event_count, abort_slave_event_count ;
/* the master variables are defaults read from my.cnf or command line */
-extern uint master_port, master_connect_retry, report_port;
-extern char * master_user, *master_password, *master_host;
+extern uint report_port;
extern char *master_info_file, *report_user;
extern char *report_host, *report_password;
-extern my_bool master_ssl;
-extern char *master_ssl_ca, *master_ssl_capath, *master_ssl_cert;
-extern char *master_ssl_cipher, *master_ssl_key;
-
extern I_List<THD> threads;
#else
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 366679a2fb0..13f7facedea 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -1159,6 +1159,8 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
Item_change_list old_change_list;
String old_packet;
uint old_server_status;
+ const uint status_backup_mask= SERVER_STATUS_CURSOR_EXISTS |
+ SERVER_STATUS_LAST_ROW_SENT;
Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer;
Object_creation_ctx *saved_creation_ctx;
Diagnostics_area *da= thd->get_stmt_da();
@@ -1292,7 +1294,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
It is probably safe to use same thd->convert_buff everywhere.
*/
old_packet.swap(thd->packet);
- old_server_status= thd->server_status;
+ old_server_status= thd->server_status & status_backup_mask;
/*
Switch to per-instruction arena here. We can do it since we cleanup
@@ -1414,7 +1416,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success)
thd->spcont->pop_all_cursors(); // To avoid memory leaks after an error
/* Restore all saved */
- thd->server_status= old_server_status;
+ thd->server_status= (thd->server_status & ~status_backup_mask) | old_server_status;
old_packet.swap(thd->packet);
DBUG_ASSERT(thd->change_list.is_empty());
old_change_list.move_elements_to(&thd->change_list);
@@ -3144,7 +3146,10 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
thd->query_name_consts= 0;
if (!thd->is_error())
+ {
+ res= 0;
thd->get_stmt_da()->reset_diagnostics_area();
+ }
}
DBUG_RETURN(res || thd->is_error());
}
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index c980c435c79..a67828ac680 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -7590,7 +7590,7 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user)
}
DBUG_ASSERT(rolename || username);
- Item_string *field=new Item_string("",0,&my_charset_latin1);
+ Item_string *field=new Item_string_ascii("", 0);
List<Item> field_list;
field->name=buff;
field->max_length=1024;
@@ -8907,6 +8907,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
acl_user->user.str= strdup_root(&acl_memroot, user_to->user.str);
acl_user->user.length= user_to->user.length;
acl_user->host.hostname= strdup_root(&acl_memroot, user_to->host.str);
+ acl_user->hostname_length= user_to->host.length;
break;
case DB_ACL:
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index e253f467b5c..44a4ee67699 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -2929,6 +2929,7 @@ Locked_tables_list::reopen_tables(THD *thd)
size_t reopen_count= 0;
MYSQL_LOCK *lock;
MYSQL_LOCK *merged_lock;
+ DBUG_ENTER("Locked_tables_list::reopen_tables");
for (TABLE_LIST *table_list= m_locked_tables;
table_list; table_list= table_list->next_global)
@@ -2940,7 +2941,7 @@ Locked_tables_list::reopen_tables(THD *thd)
if (open_table(thd, table_list, thd->mem_root, &ot_ctx))
{
unlink_all_closed_tables(thd, 0, reopen_count);
- return TRUE;
+ DBUG_RETURN(TRUE);
}
table_list->table->pos_in_locked_tables= table_list;
/* See also the comment on lock type in init_locked_tables(). */
@@ -2972,11 +2973,11 @@ Locked_tables_list::reopen_tables(THD *thd)
unlink_all_closed_tables(thd, lock, reopen_count);
if (! thd->killed)
my_error(ER_LOCK_DEADLOCK, MYF(0));
- return TRUE;
+ DBUG_RETURN(TRUE);
}
thd->lock= merged_lock;
}
- return FALSE;
+ DBUG_RETURN(FALSE);
}
/**
@@ -5345,6 +5346,7 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_AUTOINC_NOT_FIRST);
}
+#ifdef NOT_USED_IN_MARIADB
/*
INSERT...ON DUPLICATE KEY UPDATE on a table with more than one unique keys
can be unsafe.
@@ -5370,6 +5372,7 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count,
thd->lex->duplicates == DUP_UPDATE)
thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS);
}
+#endif
/* We have to emulate LOCK TABLES if we are statement needs prelocking. */
if (thd->lex->requires_prelocking())
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 70441b1bcc0..50ca9da6e34 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -4619,6 +4619,7 @@ thd_need_ordering_with(const MYSQL_THD thd, const MYSQL_THD other_thd)
{
rpl_group_info *rgi, *other_rgi;
+ DBUG_EXECUTE_IF("disable_thd_need_ordering_with", return 1;);
if (!thd || !other_thd)
return 1;
rgi= thd->rgi_slave;
@@ -4634,7 +4635,7 @@ thd_need_ordering_with(const MYSQL_THD thd, const MYSQL_THD other_thd)
if (!rgi->commit_id || rgi->commit_id != other_rgi->commit_id)
return 1;
/*
- These two threads are doing parallel replication within the same
+ Otherwise, these two threads are doing parallel replication within the same
replication domain. Their commit order is already fixed, so we do not need
gap locks or similar to otherwise enforce ordering (and in fact such locks
could lead to unnecessary deadlocks and transaction retry).
@@ -4730,9 +4731,18 @@ extern "C" bool thd_binlog_filter_ok(const MYSQL_THD thd)
return binlog_filter->db_ok(thd->db);
}
+/*
+ This is similar to sqlcom_can_generate_row_events, with the expection
+ that we only return 1 if we are going to generate row events in a
+ transaction.
+ CREATE OR REPLACE is always safe to do as this will run in it's own
+ transaction.
+*/
+
extern "C" bool thd_sqlcom_can_generate_row_events(const MYSQL_THD thd)
{
- return sqlcom_can_generate_row_events(thd);
+ return (sqlcom_can_generate_row_events(thd) && thd->lex->sql_command !=
+ SQLCOM_CREATE_TABLE);
}
@@ -6275,23 +6285,35 @@ show_query_type(THD::enum_binlog_query_type qtype)
Constants required for the limit unsafe warnings suppression
*/
//seconds after which the limit unsafe warnings suppression will be activated
-#define LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT 50
+#define LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT 5*60
//number of limit unsafe warnings after which the suppression will be activated
-#define LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT 50
+#define LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT 10
-static ulonglong limit_unsafe_suppression_start_time= 0;
-static bool unsafe_warning_suppression_is_activated= false;
-static int limit_unsafe_warning_count= 0;
+static ulonglong unsafe_suppression_start_time= 0;
+static bool unsafe_warning_suppression_active[LEX::BINLOG_STMT_UNSAFE_COUNT];
+static ulong unsafe_warnings_count[LEX::BINLOG_STMT_UNSAFE_COUNT];
+static ulong total_unsafe_warnings_count;
/**
Auxiliary function to reset the limit unsafety warning suppression.
+ This is done without mutex protection, but this should be good
+ enough as it doesn't matter if we loose a couple of suppressed
+ messages or if this is called multiple times.
*/
-static void reset_binlog_unsafe_suppression()
+
+static void reset_binlog_unsafe_suppression(ulonglong now)
{
+ uint i;
DBUG_ENTER("reset_binlog_unsafe_suppression");
- unsafe_warning_suppression_is_activated= false;
- limit_unsafe_warning_count= 0;
- limit_unsafe_suppression_start_time= my_interval_timer()/10000000;
+
+ unsafe_suppression_start_time= now;
+ total_unsafe_warnings_count= 0;
+
+ for (i= 0 ; i < LEX::BINLOG_STMT_UNSAFE_COUNT ; i++)
+ {
+ unsafe_warnings_count[i]= 0;
+ unsafe_warning_suppression_active[i]= 0;
+ }
DBUG_VOID_RETURN;
}
@@ -6309,95 +6331,94 @@ static void print_unsafe_warning_to_log(int unsafe_type, char* buf,
}
/**
- Auxiliary function to check if the warning for limit unsafety should be
- thrown or suppressed. Details of the implementation can be found in the
- comments inline.
+ Auxiliary function to check if the warning for unsafe repliction statements
+ should be thrown or suppressed.
+
+ Logic is:
+ - If we get more than LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT errors
+ of one type, that type of errors will be suppressed for
+ LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT.
+ - When the time limit has been reached, all suppression is reset.
+
+ This means that if one gets many different types of errors, some of them
+ may be reset less than LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT. However at
+ least one error is disable for this time.
+
SYNOPSIS:
@params
- buf - buffer to hold the warning message text
unsafe_type - The type of unsafety.
- query - The actual query statement.
- TODO: Remove this function and implement a general service for all warnings
- that would prevent flooding the error log.
+ RETURN:
+ 0 0k to log
+ 1 Message suppressed
*/
-static void do_unsafe_limit_checkout(char* buf, int unsafe_type, char* query)
+
+static bool protect_against_unsafe_warning_flood(int unsafe_type)
{
- ulonglong now= 0;
- DBUG_ENTER("do_unsafe_limit_checkout");
- DBUG_ASSERT(unsafe_type == LEX::BINLOG_STMT_UNSAFE_LIMIT);
- limit_unsafe_warning_count++;
+ ulong count;
+ ulonglong now= my_interval_timer()/1000000000ULL;
+ DBUG_ENTER("protect_against_unsafe_warning_flood");
+
+ count= ++unsafe_warnings_count[unsafe_type];
+ total_unsafe_warnings_count++;
+
/*
INITIALIZING:
If this is the first time this function is called with log warning
enabled, the monitoring the unsafe warnings should start.
*/
- if (limit_unsafe_suppression_start_time == 0)
+ if (unsafe_suppression_start_time == 0)
{
- limit_unsafe_suppression_start_time= my_interval_timer()/10000000;
- print_unsafe_warning_to_log(unsafe_type, buf, query);
+ reset_binlog_unsafe_suppression(now);
+ DBUG_RETURN(0);
}
- else
+
+ /*
+ The following is true if we got too many errors or if the error was
+ already suppressed
+ */
+ if (count >= LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT)
{
- if (!unsafe_warning_suppression_is_activated)
- print_unsafe_warning_to_log(unsafe_type, buf, query);
+ ulonglong diff_time= (now - unsafe_suppression_start_time);
- if (limit_unsafe_warning_count >=
- LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT)
+ if (!unsafe_warning_suppression_active[unsafe_type])
{
- now= my_interval_timer()/10000000;
- if (!unsafe_warning_suppression_is_activated)
+ /*
+ ACTIVATION:
+ We got LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT warnings in
+ less than LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT we activate the
+ suppression.
+ */
+ if (diff_time <= LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT)
{
- /*
- ACTIVATION:
- We got LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT warnings in
- less than LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT we activate the
- suppression.
- */
- if ((now-limit_unsafe_suppression_start_time) <=
- LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT)
- {
- unsafe_warning_suppression_is_activated= true;
- DBUG_PRINT("info",("A warning flood has been detected and the limit \
-unsafety warning suppression has been activated."));
- }
- else
- {
- /*
- there is no flooding till now, therefore we restart the monitoring
- */
- limit_unsafe_suppression_start_time= my_interval_timer()/10000000;
- limit_unsafe_warning_count= 0;
- }
+ unsafe_warning_suppression_active[unsafe_type]= 1;
+ sql_print_information("Suppressing warnings of type '%s' for up to %d seconds because of flooding",
+ ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]),
+ LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT);
}
else
{
/*
- Print the suppression note and the unsafe warning.
- */
- sql_print_information("The following warning was suppressed %d times \
-during the last %d seconds in the error log",
- limit_unsafe_warning_count,
- (int)
- (now-limit_unsafe_suppression_start_time));
- print_unsafe_warning_to_log(unsafe_type, buf, query);
- /*
- DEACTIVATION: We got LIMIT_UNSAFE_WARNING_ACTIVATION_THRESHOLD_COUNT
- warnings in more than LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT, the
- suppression should be deactivated.
+ There is no flooding till now, therefore we restart the monitoring
*/
- if ((now - limit_unsafe_suppression_start_time) >
- LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT)
- {
- reset_binlog_unsafe_suppression();
- DBUG_PRINT("info",("The limit unsafety warning supression has been \
-deactivated"));
- }
+ reset_binlog_unsafe_suppression(now);
+ }
+ }
+ else
+ {
+ /* This type of warnings was suppressed */
+ if (diff_time > LIMIT_UNSAFE_WARNING_ACTIVATION_TIMEOUT)
+ {
+ ulong save_count= total_unsafe_warnings_count;
+ /* Print a suppression note and remove the suppression */
+ reset_binlog_unsafe_suppression(now);
+ sql_print_information("Suppressed %lu unsafe warnings during "
+ "the last %d seconds",
+ save_count, (int) diff_time);
}
- limit_unsafe_warning_count= 0;
}
}
- DBUG_VOID_RETURN;
+ DBUG_RETURN(unsafe_warning_suppression_active[unsafe_type]);
}
/**
@@ -6409,6 +6430,7 @@ deactivated"));
void THD::issue_unsafe_warnings()
{
char buf[MYSQL_ERRMSG_SIZE * 2];
+ uint32 unsafe_type_flags;
DBUG_ENTER("issue_unsafe_warnings");
/*
Ensure that binlog_unsafe_warning_flags is big enough to hold all
@@ -6416,8 +6438,10 @@ void THD::issue_unsafe_warnings()
*/
DBUG_ASSERT(LEX::BINLOG_STMT_UNSAFE_COUNT <=
sizeof(binlog_unsafe_warning_flags) * CHAR_BIT);
-
- uint32 unsafe_type_flags= binlog_unsafe_warning_flags;
+
+ if (!(unsafe_type_flags= binlog_unsafe_warning_flags))
+ DBUG_VOID_RETURN; // Nothing to do
+
/*
For each unsafe_type, check if the statement is unsafe in this way
and issue a warning.
@@ -6432,13 +6456,9 @@ void THD::issue_unsafe_warnings()
ER_BINLOG_UNSAFE_STATEMENT,
ER(ER_BINLOG_UNSAFE_STATEMENT),
ER(LEX::binlog_stmt_unsafe_errcode[unsafe_type]));
- if (global_system_variables.log_warnings)
- {
- if (unsafe_type == LEX::BINLOG_STMT_UNSAFE_LIMIT)
- do_unsafe_limit_checkout( buf, unsafe_type, query());
- else //cases other than LIMIT unsafety
- print_unsafe_warning_to_log(unsafe_type, buf, query());
- }
+ if (global_system_variables.log_warnings > 0 &&
+ !protect_against_unsafe_warning_flood(unsafe_type))
+ print_unsafe_warning_to_log(unsafe_type, buf, query());
}
}
DBUG_VOID_RETURN;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 459ff9dc8b6..7eacf9d347c 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -531,6 +531,14 @@ typedef struct system_variables
ulonglong sortbuff_size;
ulonglong group_concat_max_len;
ulonglong default_regex_flags;
+
+ /**
+ Place holders to store Multi-source variables in sys_var.cc during
+ update and show of variables.
+ */
+ ulonglong slave_skip_counter;
+ ulonglong max_relay_log_size;
+
ha_rows select_limit;
ha_rows max_join_size;
ha_rows expensive_subquery_limit;
@@ -599,12 +607,6 @@ typedef struct system_variables
*/
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.
- */
- ulong slave_skip_counter;
- ulong max_relay_log_size;
/**
Default transaction access mode. READ ONLY (true) or READ WRITE (false).
diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc
index 84ff8759d96..2b523b323a4 100644
--- a/sql/sql_explain.cc
+++ b/sql/sql_explain.cc
@@ -203,15 +203,13 @@ bool Explain_query::print_explain_str(THD *thd, String *out_str)
static void push_str(List<Item> *item_list, const char *str)
{
- item_list->push_back(new Item_string(str,
- strlen(str), system_charset_info));
+ item_list->push_back(new Item_string_sys(str));
}
static void push_string(List<Item> *item_list, String *str)
{
- item_list->push_back(new Item_string(str->ptr(), str->length(),
- system_charset_info));
+ item_list->push_back(new Item_string_sys(str->ptr(), str->length()));
}
@@ -262,8 +260,7 @@ int Explain_union::print_explain(Explain_query *query,
len+= lastop;
table_name_buffer[len - 1]= '>'; // change ',' to '>'
}
- const CHARSET_INFO *cs= system_charset_info;
- item_list.push_back(new Item_string(table_name_buffer, len, cs));
+ item_list.push_back(new Item_string_sys(table_name_buffer, len));
}
/* `partitions` column */
@@ -298,8 +295,7 @@ int Explain_union::print_explain(Explain_query *query,
{
extra_buf.append(STRING_WITH_LEN("Using filesort"));
}
- const CHARSET_INFO *cs= system_charset_info;
- item_list.push_back(new Item_string(extra_buf.ptr(), extra_buf.length(), cs));
+ item_list.push_back(new Item_string_sys(extra_buf.ptr(), extra_buf.length()));
//output->unit.offset_limit_cnt= 0;
if (output->send_data(item_list))
@@ -349,12 +345,10 @@ int Explain_select::print_explain(Explain_query *query,
if (message)
{
List<Item> item_list;
- const CHARSET_INFO *cs= system_charset_info;
Item *item_null= new Item_null();
item_list.push_back(new Item_int((int32) select_id));
- item_list.push_back(new Item_string(select_type,
- strlen(select_type), cs));
+ item_list.push_back(new Item_string_sys(select_type));
for (uint i=0 ; i < 7; i++)
item_list.push_back(item_null);
if (explain_flags & DESCRIBE_PARTITIONS)
@@ -362,7 +356,7 @@ int Explain_select::print_explain(Explain_query *query,
if (explain_flags & DESCRIBE_EXTENDED)
item_list.push_back(item_null);
- item_list.push_back(new Item_string(message,strlen(message),cs));
+ item_list.push_back(new Item_string_sys(message));
if (output->send_data(item_list))
return 1;
@@ -560,7 +554,7 @@ int Explain_table_access::print_explain(select_result_sink *output, uint8 explai
extra_buf.append(STRING_WITH_LEN("Using filesort"));
}
- item_list.push_back(new Item_string(extra_buf.ptr(), extra_buf.length(), cs));
+ item_list.push_back(new Item_string_sys(extra_buf.ptr(), extra_buf.length()));
if (output->send_data(item_list))
return 1;
diff --git a/sql/sql_get_diagnostics.cc b/sql/sql_get_diagnostics.cc
index be1e3589cc6..8b0d86aa7d1 100644
--- a/sql/sql_get_diagnostics.cc
+++ b/sql/sql_get_diagnostics.cc
@@ -267,9 +267,11 @@ Condition_information_item::make_utf8_string_item(THD *thd, const String *str)
CHARSET_INFO *to_cs= &my_charset_utf8_general_ci;
/* If a charset was not set, assume that no conversion is needed. */
CHARSET_INFO *from_cs= str->charset() ? str->charset() : to_cs;
- Item_string *item= new Item_string(str->ptr(), str->length(), from_cs);
+ String tmp(str->ptr(), str->length(), from_cs);
/* If necessary, convert the string (ignoring errors), then copy it over. */
- return item ? item->charset_converter(to_cs, false) : NULL;
+ uint conv_errors;
+ return new Item_string(&tmp, to_cs, &conv_errors,
+ DERIVATION_COERCIBLE, MY_REPERTOIRE_UNICODE30);
}
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index 844810af0f4..8f458ea0b9f 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -626,7 +626,7 @@ SQL_SELECT *prepare_select_for_name(THD *thd, const char *mask, uint mlen,
{
Item *cond= new Item_func_like(new Item_field(pfname),
new Item_string(mask,mlen,pfname->charset()),
- new Item_string("\\",1,&my_charset_latin1),
+ new Item_string_ascii("\\"),
FALSE);
if (thd->is_fatal_error)
return 0; // OOM
diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc
index cab9628837c..254b7026e96 100644
--- a/sql/sql_join_cache.cc
+++ b/sql/sql_join_cache.cc
@@ -2094,7 +2094,7 @@ enum_nested_loop_state JOIN_CACHE::join_records(bool skip_last)
goto finish;
if (outer_join_first_inner)
{
- if (next_cache)
+ if (next_cache && join_tab != join_tab->last_inner)
{
/*
Ensure that all matches for outer records from join buffer are to be
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 8eb282b38f0..fc48db37f99 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -287,7 +287,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS |
CF_CAN_GENERATE_ROW_EVENTS;
- sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS;
sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS |
CF_INSERTS_DATA;
@@ -2996,6 +2996,9 @@ case SQLCOM_PREPARE:
goto error;
mysql_mutex_lock(&LOCK_active_mi);
+ if (!master_info_index)
+ goto error;
+
mi= master_info_index->get_master_info(&lex_mi->connection_name,
Sql_condition::WARN_LEVEL_NOTE);
@@ -3463,7 +3466,7 @@ end_with_restore_list:
case SQLCOM_SLAVE_ALL_START:
{
mysql_mutex_lock(&LOCK_active_mi);
- if (!master_info_index->start_all_slaves(thd))
+ if (master_info_index && !master_info_index->start_all_slaves(thd))
my_ok(thd);
mysql_mutex_unlock(&LOCK_active_mi);
break;
@@ -3479,7 +3482,7 @@ end_with_restore_list:
goto error;
}
mysql_mutex_lock(&LOCK_active_mi);
- if (!master_info_index->stop_all_slaves(thd))
+ if (master_info_index && !master_info_index->stop_all_slaves(thd))
my_ok(thd);
mysql_mutex_unlock(&LOCK_active_mi);
break;
@@ -4715,11 +4718,12 @@ end_with_restore_list:
case SQLCOM_SHOW_GRANTS:
{
LEX_USER *grant_user= lex->grant_user;
+ Security_context *sctx= thd->security_ctx;
if (!grant_user)
goto error;
- if (grant_user->user.str &&
- !strcmp(thd->security_ctx->priv_user, grant_user->user.str))
+ if (grant_user->user.str && !strcmp(sctx->priv_user, grant_user->user.str) &&
+ grant_user->host.str && !strcmp(sctx->priv_host, grant_user->host.str))
grant_user->user= current_user;
if (grant_user->user.str == current_user.str ||
diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc
index f1f9ba9452f..e2b3b482ffe 100644
--- a/sql/sql_reload.cc
+++ b/sql/sql_reload.cc
@@ -174,18 +174,21 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
*/
tmp_write_to_binlog= 0;
mysql_mutex_lock(&LOCK_active_mi);
- if (!(mi= (master_info_index->
- get_master_info(&connection_name,
- Sql_condition::WARN_LEVEL_ERROR))))
+ if (master_info_index)
{
- result= 1;
- }
- else
- {
- mysql_mutex_lock(&mi->data_lock);
- if (rotate_relay_log(mi))
- *write_to_binlog= -1;
- mysql_mutex_unlock(&mi->data_lock);
+ if (!(mi= (master_info_index->
+ get_master_info(&connection_name,
+ Sql_condition::WARN_LEVEL_ERROR))))
+ {
+ result= 1;
+ }
+ else
+ {
+ mysql_mutex_lock(&mi->data_lock);
+ if (rotate_relay_log(mi))
+ *write_to_binlog= -1;
+ mysql_mutex_unlock(&mi->data_lock);
+ }
}
mysql_mutex_unlock(&LOCK_active_mi);
#endif
@@ -357,22 +360,24 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options,
Master_info *mi;
tmp_write_to_binlog= 0;
mysql_mutex_lock(&LOCK_active_mi);
-
- if (!(mi= (master_info_index->
- get_master_info(&lex_mi->connection_name,
- Sql_condition::WARN_LEVEL_ERROR))))
+ if (master_info_index)
{
- result= 1;
- }
- else if (reset_slave(thd, mi))
- {
- /* NOTE: my_error() has been already called by reset_slave(). */
- result= 1;
- }
- else if (mi->connection_name.length && thd->lex->reset_slave_info.all)
- {
- /* If not default connection and 'all' is used */
- master_info_index->remove_master_info(&mi->connection_name);
+ if (!(mi= (master_info_index->
+ get_master_info(&lex_mi->connection_name,
+ Sql_condition::WARN_LEVEL_ERROR))))
+ {
+ result= 1;
+ }
+ else if (reset_slave(thd, mi))
+ {
+ /* NOTE: my_error() has been already called by reset_slave(). */
+ result= 1;
+ }
+ else if (mi->connection_name.length && thd->lex->reset_slave_info.all)
+ {
+ /* If not default connection and 'all' is used */
+ master_info_index->remove_master_info(&mi->connection_name);
+ }
}
mysql_mutex_unlock(&LOCK_active_mi);
}
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 8cd1c84b668..0af3bdd4e14 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -3235,6 +3235,9 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added)
LEX_MASTER_INFO* lex_mi= &thd->lex->mi;
DBUG_ENTER("change_master");
+ mysql_mutex_assert_owner(&LOCK_active_mi);
+ DBUG_ASSERT(master_info_index);
+
*master_info_added= false;
/*
We need to check if there is an empty master_host. Otherwise
@@ -3641,7 +3644,8 @@ bool mysql_show_binlog_events(THD* thd)
else /* showing relay log contents */
{
mysql_mutex_lock(&LOCK_active_mi);
- if (!(mi= master_info_index->
+ if (!master_info_index ||
+ !(mi= master_info_index->
get_master_info(&thd->variables.default_master_connection,
Sql_condition::WARN_LEVEL_ERROR)))
{
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 160ffe11abc..eff1c382945 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -2521,7 +2521,7 @@ void JOIN::exec_inner()
Item *cur_const_item;
while ((cur_const_item= const_item_it++))
{
- cur_const_item->val_str(&cur_const_item->str_value);
+ cur_const_item->val_str(); // This caches val_str() to Item::str_value
if (thd->is_error())
{
error= thd->is_error();
@@ -9875,6 +9875,25 @@ uint get_next_field_for_derived_key(uchar *arg)
}
+static
+uint get_next_field_for_derived_key_simple(uchar *arg)
+{
+ KEYUSE *keyuse= *(KEYUSE **) arg;
+ if (!keyuse)
+ return (uint) (-1);
+ TABLE *table= keyuse->table;
+ uint key= keyuse->key;
+ uint fldno= keyuse->keypart;
+ for ( ;
+ keyuse->table == table && keyuse->key == key && keyuse->keypart == fldno;
+ keyuse++)
+ ;
+ if (keyuse->key != key)
+ keyuse= 0;
+ *((KEYUSE **) arg)= keyuse;
+ return fldno;
+}
+
static
bool generate_derived_keys_for_table(KEYUSE *keyuse, uint count, uint keys)
{
@@ -9905,12 +9924,28 @@ bool generate_derived_keys_for_table(KEYUSE *keyuse, uint count, uint keys)
}
else
{
- if (table->add_tmp_key(table->s->keys, parts,
- get_next_field_for_derived_key,
- (uchar *) &first_keyuse,
- FALSE))
- return TRUE;
- table->reginfo.join_tab->keys.set_bit(table->s->keys);
+ KEYUSE *save_first_keyuse= first_keyuse;
+ if (table->check_tmp_key(table->s->keys, parts,
+ get_next_field_for_derived_key_simple,
+ (uchar *) &first_keyuse))
+
+ {
+ first_keyuse= save_first_keyuse;
+ if (table->add_tmp_key(table->s->keys, parts,
+ get_next_field_for_derived_key,
+ (uchar *) &first_keyuse,
+ FALSE))
+ return TRUE;
+ table->reginfo.join_tab->keys.set_bit(table->s->keys);
+ }
+ else
+ {
+ /* Mark keyuses for this key to be excluded */
+ for (KEYUSE *curr=save_first_keyuse; curr < first_keyuse; curr++)
+ {
+ curr->key= MAX_KEY;
+ }
+ }
first_keyuse= keyuse;
key_count++;
parts= 0;
@@ -21066,7 +21101,7 @@ cp_buffer_from_ref(THD *thd, TABLE *table, TABLE_REF *ref)
ref_pointer_array and all_fields are updated.
- @param[in] thd Pointer to current thread structure
+ @param[in] thd Pointer to current thread structure
@param[in,out] ref_pointer_array All select, group and order by fields
@param[in] tables List of tables to search in (usually
FROM clause)
@@ -21108,11 +21143,11 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
order_item->full_name(), thd->where);
return TRUE;
}
- order->item= ref_pointer_array + count - 1;
+ thd->change_item_tree((Item**)&order->item, (Item*)(ref_pointer_array + count - 1));
order->in_field_list= 1;
order->counter= count;
order->counter_used= 1;
- return FALSE;
+ return FALSE;
}
/* Lookup the current GROUP/ORDER field in the SELECT clause. */
select_item= find_item_in_list(order_item, fields, &counter,
@@ -21180,7 +21215,8 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
warning so the user knows that the field from the FROM clause
overshadows the column reference from the SELECT list.
*/
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NON_UNIQ_ERROR,
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_NON_UNIQ_ERROR,
ER(ER_NON_UNIQ_ERROR),
((Item_ident*) order_item)->field_name,
current_thd->where);
@@ -22950,13 +22986,11 @@ int print_explain_message_line(select_result_sink *result,
ha_rows *rows,
const char *message)
{
- const CHARSET_INFO *cs= system_charset_info;
Item *item_null= new Item_null();
List<Item> item_list;
item_list.push_back(new Item_int((int32) select_number));
- item_list.push_back(new Item_string(select_type,
- strlen(select_type), cs));
+ item_list.push_back(new Item_string_sys(select_type));
/* `table` */
item_list.push_back(item_null);
@@ -22983,7 +23017,7 @@ int print_explain_message_line(select_result_sink *result,
/* `Extra` */
if (message)
- item_list.push_back(new Item_string(message,strlen(message),cs));
+ item_list.push_back(new Item_string_sys(message));
else
item_list.push_back(item_null);
@@ -23042,45 +23076,39 @@ int print_explain_row(select_result_sink *result,
ha_rows *rows,
const char *extra)
{
- const CHARSET_INFO *cs= system_charset_info;
Item *item_null= new Item_null();
List<Item> item_list;
Item *item;
item_list.push_back(new Item_int((int32) select_number));
- item_list.push_back(new Item_string(select_type,
- strlen(select_type), cs));
- item_list.push_back(new Item_string(table_name,
- strlen(table_name), cs));
+ item_list.push_back(new Item_string_sys(select_type));
+ item_list.push_back(new Item_string_sys(table_name));
if (options & DESCRIBE_PARTITIONS)
{
if (partitions)
{
- item_list.push_back(new Item_string(partitions,
- strlen(partitions), cs));
+ item_list.push_back(new Item_string_sys(partitions));
}
else
item_list.push_back(item_null);
}
const char *jtype_str= join_type_str[jtype];
- item_list.push_back(new Item_string(jtype_str,
- strlen(jtype_str), cs));
+ item_list.push_back(new Item_string_sys(jtype_str));
- item= possible_keys? new Item_string(possible_keys, strlen(possible_keys),
- cs) : item_null;
+ item= possible_keys? new Item_string_sys(possible_keys) : item_null;
item_list.push_back(item);
/* 'index */
- item= index ? new Item_string(index, strlen(index), cs) : item_null;
+ item= index ? new Item_string_sys(index) : item_null;
item_list.push_back(item);
/* 'key_len */
- item= key_len ? new Item_string(key_len, strlen(key_len), cs) : item_null;
+ item= key_len ? new Item_string_sys(key_len) : item_null;
item_list.push_back(item);
/* 'ref' */
- item= ref ? new Item_string(ref, strlen(ref), cs) : item_null;
+ item= ref ? new Item_string_sys(ref) : item_null;
item_list.push_back(item);
/* 'rows' */
@@ -23099,7 +23127,7 @@ int print_explain_row(select_result_sink *result,
/* 'Extra' */
if (extra)
- item_list.push_back(new Item_string(extra, strlen(extra), cs));
+ item_list.push_back(new Item_string_sys(extra));
else
item_list.push_back(item_null);
@@ -23112,7 +23140,6 @@ int print_explain_row(select_result_sink *result,
int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
SELECT_LEX *select_lex, uint8 explain_flags)
{
- const CHARSET_INFO *cs= system_charset_info;
Item *item_null= new Item_null();
List<Item> item_list;
if (on_the_fly)
@@ -23129,9 +23156,7 @@ int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
/* id */
item_list.push_back(new Item_null);
/* select_type */
- item_list.push_back(new Item_string(select_lex->type,
- strlen(select_lex->type),
- cs));
+ item_list.push_back(new Item_string_sys(select_lex->type));
/* table */
{
SELECT_LEX *sl= select_lex->master_unit()->first_select();
@@ -23153,15 +23178,14 @@ int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
len+= lastop;
table_name_buffer[len - 1]= '>'; // change ',' to '>'
}
- item_list.push_back(new Item_string(table_name_buffer, len, cs));
+ item_list.push_back(new Item_string_sys(table_name_buffer, len));
}
/* partitions */
if (explain_flags & DESCRIBE_PARTITIONS)
item_list.push_back(item_null);
/* type */
- item_list.push_back(new Item_string(join_type_str[JT_ALL],
- strlen(join_type_str[JT_ALL]),
- cs));
+ item_list.push_back(new Item_string_sys(join_type_str[JT_ALL]));
+
/* possible_keys */
item_list.push_back(item_null);
/* key*/
@@ -23177,10 +23201,9 @@ int print_fake_select_lex_join(select_result_sink *result, bool on_the_fly,
item_list.push_back(item_null);
/* extra */
if (select_lex->master_unit()->global_parameters->order_list.first)
- item_list.push_back(new Item_string("Using filesort",
- 14, cs));
+ item_list.push_back(new Item_string_sys("Using filesort", 14));
else
- item_list.push_back(new Item_string("", 0, cs));
+ item_list.push_back(new Item_string_sys("", 0));
if (result->send_data(item_list))
return 1;
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 16fd1807b34..c6889648a09 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -1325,9 +1325,22 @@ append_identifier(THD *thd, String *packet, const char *name, uint length)
it's a keyword
*/
+ /*
+ Special code for swe7. It encodes the letter "E WITH ACUTE" on
+ the position 0x60, where backtick normally resides.
+ In swe7 we cannot append 0x60 using system_charset_info,
+ because it cannot be converted to swe7 and will be replaced to
+ question mark '?'. Use &my_charset_bin to avoid this.
+ It will prevent conversion and will append the backtick as is.
+ */
+ CHARSET_INFO *quote_charset= q == 0x60 &&
+ (packet->charset()->state & MY_CS_NONASCII) &&
+ packet->charset()->mbmaxlen == 1 ?
+ &my_charset_bin : system_charset_info;
+
(void) packet->reserve(length*2 + 2);
quote_char= (char) q;
- if (packet->append(&quote_char, 1, system_charset_info))
+ if (packet->append(&quote_char, 1, quote_charset))
return true;
for (name_end= name+length ; name < name_end ; name+= length)
@@ -1344,12 +1357,12 @@ append_identifier(THD *thd, String *packet, const char *name, uint length)
if (!length)
length= 1;
if (length == 1 && chr == (uchar) quote_char &&
- packet->append(&quote_char, 1, system_charset_info))
+ packet->append(&quote_char, 1, quote_charset))
return true;
if (packet->append(name, length, system_charset_info))
return true;
}
- return packet->append(&quote_char, 1, system_charset_info);
+ return packet->append(&quote_char, 1, quote_charset);
}
@@ -2281,77 +2294,77 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_VOID_RETURN;
+ if (thd->killed)
+ DBUG_VOID_RETURN;
+
mysql_mutex_lock(&LOCK_thread_count); // For unlink from list
- if (!thd->killed)
+ I_List_iterator<THD> it(threads);
+ THD *tmp;
+ while ((tmp=it++))
{
- I_List_iterator<THD> it(threads);
- THD *tmp;
- while ((tmp=it++))
+ Security_context *tmp_sctx= tmp->security_ctx;
+ struct st_my_thread_var *mysys_var;
+ if ((tmp->vio_ok() || tmp->system_thread) &&
+ (!user || (tmp_sctx->user && !strcmp(tmp_sctx->user, user))))
{
- Security_context *tmp_sctx= tmp->security_ctx;
- struct st_my_thread_var *mysys_var;
- if ((tmp->vio_ok() || tmp->system_thread) &&
- (!user || (tmp_sctx->user && !strcmp(tmp_sctx->user, user))))
+ thread_info *thd_info= new thread_info;
+
+ thd_info->thread_id=tmp->thread_id;
+ thd_info->user= thd->strdup(tmp_sctx->user ? tmp_sctx->user :
+ (tmp->system_thread ?
+ "system user" : "unauthenticated user"));
+ if (tmp->peer_port && (tmp_sctx->host || tmp_sctx->ip) &&
+ thd->security_ctx->host_or_ip[0])
{
- thread_info *thd_info= new thread_info;
-
- thd_info->thread_id=tmp->thread_id;
- thd_info->user= thd->strdup(tmp_sctx->user ? tmp_sctx->user :
- (tmp->system_thread ?
- "system user" : "unauthenticated user"));
- if (tmp->peer_port && (tmp_sctx->host || tmp_sctx->ip) &&
- thd->security_ctx->host_or_ip[0])
- {
- if ((thd_info->host= (char*) thd->alloc(LIST_PROCESS_HOST_LEN+1)))
- my_snprintf((char *) thd_info->host, LIST_PROCESS_HOST_LEN,
- "%s:%u", tmp_sctx->host_or_ip, tmp->peer_port);
- }
- else
- thd_info->host= thd->strdup(tmp_sctx->host_or_ip[0] ?
- tmp_sctx->host_or_ip :
- tmp_sctx->host ? tmp_sctx->host : "");
- thd_info->command=(int) tmp->get_command();
- mysql_mutex_lock(&tmp->LOCK_thd_data);
- if ((thd_info->db= tmp->db)) // Safe test
- thd_info->db= thd->strdup(thd_info->db);
- if ((mysys_var= tmp->mysys_var))
- mysql_mutex_lock(&mysys_var->mutex);
- thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ?
- "Killed" : 0);
- thd_info->state_info= thread_state_info(tmp);
- if (mysys_var)
- mysql_mutex_unlock(&mysys_var->mutex);
-
- /* Lock THD mutex that protects its data when looking at it. */
- if (tmp->query())
- {
- uint length= MY_MIN(max_query_length, tmp->query_length());
- char *q= thd->strmake(tmp->query(),length);
- /* Safety: in case strmake failed, we set length to 0. */
- thd_info->query_string=
- CSET_STRING(q, q ? length : 0, tmp->query_charset());
- }
+ if ((thd_info->host= (char*) thd->alloc(LIST_PROCESS_HOST_LEN+1)))
+ my_snprintf((char *) thd_info->host, LIST_PROCESS_HOST_LEN,
+ "%s:%u", tmp_sctx->host_or_ip, tmp->peer_port);
+ }
+ else
+ thd_info->host= thd->strdup(tmp_sctx->host_or_ip[0] ?
+ tmp_sctx->host_or_ip :
+ tmp_sctx->host ? tmp_sctx->host : "");
+ thd_info->command=(int) tmp->get_command();
+ mysql_mutex_lock(&tmp->LOCK_thd_data);
+ if ((thd_info->db= tmp->db)) // Safe test
+ thd_info->db= thd->strdup(thd_info->db);
+ if ((mysys_var= tmp->mysys_var))
+ mysql_mutex_lock(&mysys_var->mutex);
+ thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ?
+ "Killed" : 0);
+ thd_info->state_info= thread_state_info(tmp);
+ if (mysys_var)
+ mysql_mutex_unlock(&mysys_var->mutex);
- /*
- Progress report. We need to do this under a lock to ensure that all
- is from the same stage.
- */
- if (tmp->progress.max_counter)
- {
- uint max_stage= MY_MAX(tmp->progress.max_stage, 1);
- thd_info->progress= (((tmp->progress.stage / (double) max_stage) +
- ((tmp->progress.counter /
- (double) tmp->progress.max_counter) /
- (double) max_stage)) *
- 100.0);
- set_if_smaller(thd_info->progress, 100);
- }
- else
- thd_info->progress= 0.0;
- thd_info->start_time= tmp->start_time;
- mysql_mutex_unlock(&tmp->LOCK_thd_data);
- thread_infos.append(thd_info);
+ /* Lock THD mutex that protects its data when looking at it. */
+ if (tmp->query())
+ {
+ uint length= MY_MIN(max_query_length, tmp->query_length());
+ char *q= thd->strmake(tmp->query(),length);
+ /* Safety: in case strmake failed, we set length to 0. */
+ thd_info->query_string=
+ CSET_STRING(q, q ? length : 0, tmp->query_charset());
}
+
+ /*
+ Progress report. We need to do this under a lock to ensure that all
+ is from the same stage.
+ */
+ if (tmp->progress.max_counter)
+ {
+ uint max_stage= MY_MAX(tmp->progress.max_stage, 1);
+ thd_info->progress= (((tmp->progress.stage / (double) max_stage) +
+ ((tmp->progress.counter /
+ (double) tmp->progress.max_counter) /
+ (double) max_stage)) *
+ 100.0);
+ set_if_smaller(thd_info->progress, 100);
+ }
+ else
+ thd_info->progress= 0.0;
+ thd_info->start_time= tmp->start_time;
+ mysql_mutex_unlock(&tmp->LOCK_thd_data);
+ thread_infos.append(thd_info);
}
}
mysql_mutex_unlock(&LOCK_thread_count);
@@ -2837,7 +2850,7 @@ int add_status_vars(SHOW_VAR *list)
{
int res= 0;
if (status_vars_inited)
- mysql_mutex_lock(&LOCK_status);
+ mysql_mutex_lock(&LOCK_show_status);
if (!all_status_vars.buffer && // array is not allocated yet - do it now
my_init_dynamic_array(&all_status_vars, sizeof(SHOW_VAR), 200, 20, MYF(0)))
{
@@ -2852,7 +2865,7 @@ int add_status_vars(SHOW_VAR *list)
sort_dynamic(&all_status_vars, show_var_cmp);
err:
if (status_vars_inited)
- mysql_mutex_unlock(&LOCK_status);
+ mysql_mutex_unlock(&LOCK_show_status);
return res;
}
@@ -2914,7 +2927,7 @@ void remove_status_vars(SHOW_VAR *list)
{
if (status_vars_inited)
{
- mysql_mutex_lock(&LOCK_status);
+ mysql_mutex_lock(&LOCK_show_status);
SHOW_VAR *all= dynamic_element(&all_status_vars, 0, SHOW_VAR *);
for (; list->name; list++)
@@ -2935,7 +2948,7 @@ void remove_status_vars(SHOW_VAR *list)
}
}
shrink_var_array(&all_status_vars);
- mysql_mutex_unlock(&LOCK_status);
+ mysql_mutex_unlock(&LOCK_show_status);
}
else
{
@@ -7328,7 +7341,7 @@ int fill_variables(THD *thd, TABLE_LIST *tables, COND *cond)
bool upper_case_names= (schema_table_idx != SCH_VARIABLES);
bool sorted_vars= (schema_table_idx == SCH_VARIABLES);
- if (lex->option_type == OPT_GLOBAL ||
+ if ((sorted_vars && lex->option_type == OPT_GLOBAL) ||
schema_table_idx == SCH_GLOBAL_VARIABLES)
option_type= OPT_GLOBAL;
@@ -7379,14 +7392,20 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond)
if (partial_cond)
partial_cond->val_int();
- mysql_mutex_lock(&LOCK_status);
if (option_type == OPT_GLOBAL)
+ {
+ /* We only hold LOCK_status for summary status vars */
+ mysql_mutex_lock(&LOCK_status);
calc_sum_of_all_status(&tmp);
+ mysql_mutex_unlock(&LOCK_status);
+ }
+
+ mysql_mutex_lock(&LOCK_show_status);
res= show_status_array(thd, wild,
(SHOW_VAR *)all_status_vars.buffer,
option_type, tmp1, "", tables->table,
upper_case_names, partial_cond);
- mysql_mutex_unlock(&LOCK_status);
+ mysql_mutex_unlock(&LOCK_show_status);
DBUG_RETURN(res);
}
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
index f8348cfb30e..a7bfa6c1455 100644
--- a/sql/sql_string.cc
+++ b/sql/sql_string.cc
@@ -580,7 +580,7 @@ bool String::append_with_prefill(const char *s,uint32 arg_length,
return FALSE;
}
-uint32 String::numchars()
+uint32 String::numchars() const
{
return str_charset->cset->numchars(str_charset, Ptr, Ptr+str_length);
}
@@ -1022,8 +1022,15 @@ well_formed_copy_nchars(CHARSET_INFO *to_cs,
wc= '?';
}
else
- break; // Not enough characters
-
+ {
+ if ((uchar *) from >= from_end)
+ break; // End of line
+ // Incomplete byte sequence
+ if (!*well_formed_error_pos)
+ *well_formed_error_pos= from;
+ from++;
+ wc= '?';
+ }
outp:
if ((cnvres= (*wc_mb)(to_cs, wc, (uchar*) to, to_end)) > 0)
to+= cnvres;
@@ -1074,7 +1081,7 @@ bool String::append_for_single_quote(const char *st, uint len)
return 0;
}
-void String::print(String *str)
+void String::print(String *str) const
{
str->append_for_single_quote(Ptr, str_length);
}
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 95c82518f9e..8c7e69edf4b 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -351,6 +351,10 @@ public:
bool set_or_copy_aligned(const char *s, uint32 arg_length, CHARSET_INFO *cs);
bool copy(const char*s,uint32 arg_length, CHARSET_INFO *csfrom,
CHARSET_INFO *csto, uint *errors);
+ bool copy(const String *str, CHARSET_INFO *tocs, uint *errors)
+ {
+ return copy(str->ptr(), str->length(), str->charset(), tocs, errors);
+ }
void move(String &s)
{
free();
@@ -407,7 +411,7 @@ public:
friend int stringcmp(const String *a,const String *b);
friend String *copy_if_not_alloced(String *a,String *b,uint32 arg_length);
friend class Field;
- uint32 numchars();
+ uint32 numchars() const;
int charpos(longlong i,uint32 offset=0);
int reserve(uint32 space_needed)
@@ -498,7 +502,7 @@ public:
str_length+= arg_length;
return FALSE;
}
- void print(String *print);
+ void print(String *print) const;
bool append_for_single_quote(const char *st, uint len);
bool append_for_single_quote(const String *s)
@@ -517,6 +521,12 @@ public:
{
return (s->alloced && Ptr >= s->Ptr && Ptr < s->Ptr + s->str_length);
}
+ uint well_formed_length() const
+ {
+ int dummy_error;
+ return charset()->cset->well_formed_len(charset(), ptr(), ptr() + length(),
+ length(), &dummy_error);
+ }
bool is_ascii() const
{
if (length() == 0)
@@ -530,6 +540,15 @@ public:
}
return TRUE;
}
+ bool bin_eq(const String *other) const
+ {
+ return length() == other->length() &&
+ !memcmp(ptr(), other->ptr(), length());
+ }
+ bool eq(const String *other, CHARSET_INFO *cs) const
+ {
+ return !sortcmp(this, other, cs);
+ }
};
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 1a4f8fce158..606c33aeed9 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -4372,9 +4372,6 @@ handler *mysql_create_frm_image(THD *thd,
DBUG_RETURN(NULL);
}
- if (check_engine(thd, db, table_name, create_info))
- DBUG_RETURN(NULL);
-
set_table_default_charset(thd, create_info, (char*) db);
db_options= create_info->table_options;
@@ -4780,6 +4777,9 @@ int create_table_impl(THD *thd,
THD_STAGE_INFO(thd, stage_creating_table);
+ if (check_engine(thd, orig_db, orig_table_name, create_info))
+ goto err;
+
if (create_table_mode == C_ASSISTED_DISCOVERY)
{
/* check that it's used correctly */
@@ -5029,7 +5029,10 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table,
*/
thd->locked_tables_list.add_back_last_deleted_lock(pos_in_locked_tables);
if (thd->locked_tables_list.reopen_tables(thd))
+ {
thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
+ result= 1;
+ }
else
{
TABLE *table= pos_in_locked_tables->table;
@@ -5348,6 +5351,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
if (res)
{
+ /* is_error() may be 0 if table existed and we generated a warning */
res= thd->is_error();
goto err;
}
@@ -5430,7 +5434,10 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table,
*/
thd->locked_tables_list.add_back_last_deleted_lock(pos_in_locked_tables);
if (thd->locked_tables_list.reopen_tables(thd))
+ {
thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
+ res= 1; // We got an error
+ }
else
{
/*
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index ae16a281277..60e9b2cc54c 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -468,8 +468,7 @@ static void display_table_locks(void)
DYNAMIC_ARRAY saved_table_locks;
(void) my_init_dynamic_array(&saved_table_locks,sizeof(TABLE_LOCK_INFO),
- tc_records() + 20, 50,
- MYF(MY_THREAD_SPECIFIC));
+ tc_records() + 20, 50, MYF(0));
mysql_mutex_lock(&THR_LOCK_lock);
for (list= thr_lock_thread_list; list; list= list_rest(list))
{
@@ -576,7 +575,6 @@ void mysql_print_status()
/* Print key cache status */
puts("\nKey caches:");
process_key_caches(print_key_cache_status, 0);
- mysql_mutex_lock(&LOCK_status);
printf("\nhandler status:\n\
read_key: %10lu\n\
read_next: %10lu\n\
@@ -592,7 +590,6 @@ update: %10lu\n",
tmp.ha_write_count,
tmp.ha_delete_count,
tmp.ha_update_count);
- mysql_mutex_unlock(&LOCK_status);
printf("\nTable status:\n\
Opened tables: %10lu\n\
Open tables: %10lu\n\
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index d6f4e2e94ee..b52fce9394e 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1648,7 +1648,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <lex_str>
IDENT IDENT_QUOTED TEXT_STRING DECIMAL_NUM FLOAT_NUM NUM LONG_NUM
- HEX_NUM HEX_STRING hex_num_or_string
+ HEX_NUM HEX_STRING
LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text
IDENT_sys TEXT_STRING_sys TEXT_STRING_literal
NCHAR_STRING opt_component key_cache_name
@@ -1666,7 +1666,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
remember_name remember_end opt_db text_or_password
%type <string>
- text_string opt_gconcat_separator
+ text_string hex_or_bin_String opt_gconcat_separator
%type <num>
type type_with_opt_collate int_type real_type order_dir lock_option
@@ -6517,11 +6517,6 @@ now_or_signed_literal:
{ $$=$1; }
;
-hex_num_or_string:
- HEX_NUM {}
- | HEX_STRING {}
- ;
-
charset:
CHAR_SYM SET {}
| CHARSET {}
@@ -9201,7 +9196,6 @@ simple_expr:
}
| '{' ident expr '}'
{
- Item_string *item;
$$= NULL;
/*
If "expr" is reasonably short pure ASCII string literal,
@@ -9211,31 +9205,13 @@ simple_expr:
SELECT {t'10:20:30'};
SELECT {ts'2001-01-01 10:20:30'};
*/
- if ($3->type() == Item::STRING_ITEM &&
- (item= (Item_string *) $3) &&
- item->collation.repertoire == MY_REPERTOIRE_ASCII &&
- item->str_value.length() < MAX_DATE_STRING_REP_LENGTH * 4)
- {
- enum_field_types type= MYSQL_TYPE_STRING;
- LEX_STRING *ls= &$2;
- if (ls->length == 1)
- {
- if (ls->str[0] == 'd') /* {d'2001-01-01'} */
- type= MYSQL_TYPE_DATE;
- else if (ls->str[0] == 't') /* {t'10:20:30'} */
- type= MYSQL_TYPE_TIME;
- }
- else if (ls->length == 2) /* {ts'2001-01-01 10:20:30'} */
- {
- if (ls->str[0] == 't' && ls->str[1] == 's')
- type= MYSQL_TYPE_DATETIME;
- }
+ if ($3->type() == Item::STRING_ITEM)
+ {
+ Item_string *item= (Item_string *) $3;
+ enum_field_types type= item->odbc_temporal_literal_type(&$2);
if (type != MYSQL_TYPE_STRING)
{
- $$= create_temporal_literal(thd,
- item->str_value.ptr(),
- item->str_value.length(),
- item->str_value.charset(),
+ $$= create_temporal_literal(thd, item->val_str(NULL),
type, false);
}
}
@@ -11154,8 +11130,8 @@ opt_escape:
{
Lex->escape_used= FALSE;
$$= ((thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ?
- new (thd->mem_root) Item_string("", 0, &my_charset_latin1) :
- new (thd->mem_root) Item_string("\\", 1, &my_charset_latin1));
+ new (thd->mem_root) Item_string_ascii("", 0) :
+ new (thd->mem_root) Item_string_ascii("\\", 1));
if ($$ == NULL)
MYSQL_YYABORT;
}
@@ -13278,14 +13254,10 @@ text_literal:
}
| UNDERSCORE_CHARSET TEXT_STRING
{
- Item_string *str= new (thd->mem_root) Item_string($2.str,
+ $$= new (thd->mem_root) Item_string_with_introducer($2.str,
$2.length, $1);
- if (str == NULL)
+ if ($$ == NULL)
MYSQL_YYABORT;
- str->set_repertoire_from_value();
- str->set_cs_specified(TRUE);
-
- $$= str;
}
| text_literal TEXT_STRING_literal
{
@@ -13314,7 +13286,12 @@ text_string:
if ($$ == NULL)
MYSQL_YYABORT;
}
- | HEX_NUM
+ | hex_or_bin_String { $$= $1; }
+ ;
+
+
+hex_or_bin_String:
+ HEX_NUM
{
Item *tmp= new (thd->mem_root) Item_hex_hybrid($1.str, $1.length);
if (tmp == NULL)
@@ -13419,60 +13396,12 @@ literal:
if ($$ == NULL)
MYSQL_YYABORT;
}
- | UNDERSCORE_CHARSET hex_num_or_string
- {
- Item *tmp= new (thd->mem_root) Item_hex_string($2.str, $2.length);
- if (tmp == NULL)
- MYSQL_YYABORT;
- /*
- it is OK only emulate fix_fieds, because we need only
- value of constant
- */
- tmp->quick_fix_field();
- String *str= tmp->val_str((String*) 0);
-
- Item_string *item_str;
- item_str= new (thd->mem_root)
- Item_string(NULL, /* name will be set in select_item */
- str ? str->ptr() : "",
- str ? str->length() : 0,
- $1);
- if (!item_str ||
- !item_str->check_well_formed_result(&item_str->str_value, TRUE))
- {
- MYSQL_YYABORT;
- }
-
- item_str->set_repertoire_from_value();
- item_str->set_cs_specified(TRUE);
-
- $$= item_str;
- }
- | UNDERSCORE_CHARSET BIN_NUM
+ | UNDERSCORE_CHARSET hex_or_bin_String
{
- Item *tmp= new (thd->mem_root) Item_bin_string($2.str, $2.length);
- if (tmp == NULL)
+ Item_string_with_introducer *item_str;
+ item_str= new (thd->mem_root) Item_string_with_introducer($2, $1);
+ if (!item_str || !item_str->check_well_formed_result(true))
MYSQL_YYABORT;
- /*
- it is OK only emulate fix_fieds, because we need only
- value of constant
- */
- tmp->quick_fix_field();
- String *str= tmp->val_str((String*) 0);
-
- Item_string *item_str;
- item_str= new (thd->mem_root)
- Item_string(NULL, /* name will be set in select_item */
- str ? str->ptr() : "",
- str ? str->length() : 0,
- $1);
- if (!item_str ||
- !item_str->check_well_formed_result(&item_str->str_value, TRUE))
- {
- MYSQL_YYABORT;
- }
-
- item_str->set_cs_specified(TRUE);
$$= item_str;
}
@@ -14869,19 +14798,19 @@ set_expr_or_default:
| DEFAULT { $$=0; }
| ON
{
- $$=new (thd->mem_root) Item_string("ON", 2, system_charset_info);
+ $$=new (thd->mem_root) Item_string_sys("ON", 2);
if ($$ == NULL)
MYSQL_YYABORT;
}
| ALL
{
- $$=new (thd->mem_root) Item_string("ALL", 3, system_charset_info);
+ $$=new (thd->mem_root) Item_string_sys("ALL", 3);
if ($$ == NULL)
MYSQL_YYABORT;
}
| BINARY
{
- $$=new (thd->mem_root) Item_string("binary", 6, system_charset_info);
+ $$=new (thd->mem_root) Item_string_sys("binary", 6);
if ($$ == NULL)
MYSQL_YYABORT;
}
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index 4236b37e4e3..5c822fa3ecc 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -4272,11 +4272,11 @@ static Sys_var_uint Sys_slave_net_timeout(
Return 0 + warning if it doesn't exist
*/
-ulong Sys_var_multi_source_ulong::
-get_master_info_ulong_value(THD *thd, ptrdiff_t offset)
+ulonglong Sys_var_multi_source_ulonglong::
+get_master_info_ulonglong_value(THD *thd, ptrdiff_t offset)
{
Master_info *mi;
- ulong res= 0; // Default value
+ ulonglong res= 0; // Default value
mysql_mutex_unlock(&LOCK_global_system_variables);
mysql_mutex_lock(&LOCK_active_mi);
mi= master_info_index->
@@ -4285,7 +4285,7 @@ get_master_info_ulong_value(THD *thd, ptrdiff_t offset)
if (mi)
{
mysql_mutex_lock(&mi->rli.data_lock);
- res= *((ulong*) (((uchar*) mi) + master_info_offset));
+ res= *((ulonglong*) (((uchar*) mi) + master_info_offset));
mysql_mutex_unlock(&mi->rli.data_lock);
}
mysql_mutex_unlock(&LOCK_active_mi);
@@ -4297,7 +4297,7 @@ get_master_info_ulong_value(THD *thd, ptrdiff_t offset)
bool update_multi_source_variable(sys_var *self_var, THD *thd,
enum_var_type type)
{
- Sys_var_multi_source_ulong *self= (Sys_var_multi_source_ulong*) self_var;
+ Sys_var_multi_source_ulonglong *self= (Sys_var_multi_source_ulonglong*) self_var;
bool result= true;
Master_info *mi;
@@ -4334,16 +4334,12 @@ static bool update_slave_skip_counter(sys_var *self, THD *thd, Master_info *mi)
return false;
}
-
-static Sys_var_multi_source_ulong
-Sys_slave_skip_counter("sql_slave_skip_counter",
- "Skip the next N events from the master log",
- SESSION_VAR(slave_skip_counter),
- NO_CMD_LINE,
- my_offsetof(Master_info, rli.slave_skip_counter),
- VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1),
- ON_UPDATE(update_slave_skip_counter));
-
+static Sys_var_multi_source_ulonglong Sys_slave_skip_counter(
+ "sql_slave_skip_counter", "Skip the next N events from the master log",
+ SESSION_VAR(slave_skip_counter), NO_CMD_LINE,
+ MASTER_INFO_VAR(rli.slave_skip_counter),
+ VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1),
+ ON_UPDATE(update_slave_skip_counter));
static bool update_max_relay_log_size(sys_var *self, THD *thd, Master_info *mi)
{
@@ -4352,17 +4348,14 @@ static bool update_max_relay_log_size(sys_var *self, THD *thd, Master_info *mi)
return false;
}
-static Sys_var_multi_source_ulong
-Sys_max_relay_log_size( "max_relay_log_size",
- "relay log will be rotated automatically when the "
- "size exceeds this value. If 0 are startup, it's "
- "set to max_binlog_size",
- SESSION_VAR(max_relay_log_size),
- CMD_LINE(REQUIRED_ARG),
- my_offsetof(Master_info, rli.max_relay_log_size),
- VALID_RANGE(0, 1024L*1024*1024), DEFAULT(0),
- BLOCK_SIZE(IO_SIZE),
- ON_UPDATE(update_max_relay_log_size));
+static Sys_var_multi_source_ulonglong Sys_max_relay_log_size(
+ "max_relay_log_size",
+ "relay log will be rotated automatically when the size exceeds this "
+ "value. If 0 are startup, it's set to max_binlog_size",
+ SESSION_VAR(max_relay_log_size), CMD_LINE(REQUIRED_ARG),
+ MASTER_INFO_VAR(rli.max_relay_log_size),
+ VALID_RANGE(0, 1024L*1024*1024), DEFAULT(0), BLOCK_SIZE(IO_SIZE),
+ ON_UPDATE(update_max_relay_log_size));
static Sys_var_charptr Sys_slave_skip_errors(
"slave_skip_errors", "Tells the slave thread to continue "
diff --git a/sql/sys_vars.h b/sql/sys_vars.h
index e7f9cf8a886..36067c50cc1 100644
--- a/sql/sys_vars.h
+++ b/sql/sys_vars.h
@@ -1993,7 +1993,8 @@ public:
like sql_slave_skip_counter are GLOBAL.
*/
-class Sys_var_multi_source_ulong;
+#define MASTER_INFO_VAR(X) my_offsetof(Master_info, X), sizeof(((Master_info *)0x10)->X)
+class Sys_var_multi_source_ulonglong;
class Master_info;
typedef bool (*on_multi_source_update_function)(sys_var *self, THD *thd,
@@ -2002,31 +2003,27 @@ bool update_multi_source_variable(sys_var *self,
THD *thd, enum_var_type type);
-class Sys_var_multi_source_ulong :public Sys_var_ulong
+class Sys_var_multi_source_ulonglong :public Sys_var_ulonglong
{
ptrdiff_t master_info_offset;
on_multi_source_update_function update_multi_source_variable_func;
public:
- Sys_var_multi_source_ulong(const char *name_arg,
+ Sys_var_multi_source_ulonglong(const char *name_arg,
const char *comment, int flag_args,
ptrdiff_t off, size_t size,
CMD_LINE getopt,
ptrdiff_t master_info_offset_arg,
- ulong min_val, ulong max_val, ulong def_val,
- uint block_size,
+ size_t master_info_arg_size,
+ ulonglong min_val, ulonglong max_val,
+ ulonglong def_val, uint block_size,
on_multi_source_update_function on_update_func)
- :Sys_var_ulong(name_arg, comment, flag_args, off, size,
- getopt, min_val, max_val, def_val, block_size,
- 0, VARIABLE_NOT_IN_BINLOG, 0, update_multi_source_variable),
+ :Sys_var_ulonglong(name_arg, comment, flag_args, off, size,
+ getopt, min_val, max_val, def_val, block_size,
+ 0, VARIABLE_NOT_IN_BINLOG, 0, update_multi_source_variable),
master_info_offset(master_info_offset_arg),
update_multi_source_variable_func(on_update_func)
{
- }
- bool session_update(THD *thd, set_var *var)
- {
- session_var(thd, ulong)= (ulong) (var->save_result.ulonglong_value);
- /* Value should be moved to multi_master in on_update_func */
- return false;
+ SYSVAR_ASSERT(master_info_arg_size == size);
}
bool global_update(THD *thd, set_var *var)
{
@@ -2039,9 +2036,9 @@ public:
}
uchar *session_value_ptr(THD *thd,LEX_STRING *base)
{
- ulong *tmp, res;
- tmp= (ulong*) (((uchar*)&(thd->variables)) + offset);
- res= get_master_info_ulong_value(thd, master_info_offset);
+ ulonglong *tmp, res;
+ tmp= (ulonglong*) (((uchar*)&(thd->variables)) + offset);
+ res= get_master_info_ulonglong_value(thd, master_info_offset);
*tmp= res;
return (uchar*) tmp;
}
@@ -2049,7 +2046,7 @@ public:
{
return session_value_ptr(thd, base);
}
- ulong get_master_info_ulong_value(THD *thd, ptrdiff_t offset);
+ ulonglong get_master_info_ulonglong_value(THD *thd, ptrdiff_t offset);
bool update_variable(THD *thd, Master_info *mi)
{
return update_multi_source_variable_func(this, thd, mi);
diff --git a/sql/table.cc b/sql/table.cc
index aca80b73afd..bf84f1b282e 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -6120,6 +6120,52 @@ void TABLE::create_key_part_by_field(KEY *keyinfo,
/**
@brief
+ Check validity of a possible key for the derived table
+
+ @param key the number of the key
+ @param key_parts number of components of the key
+ @param next_field_no the call-back function that returns the number of
+ the field used as the next component of the key
+ @param arg the argument for the above function
+
+ @details
+ The function checks whether a possible key satisfies the constraints
+ imposed on the keys of any temporary table.
+
+ @return TRUE if the key is valid
+ @return FALSE otherwise
+*/
+
+bool TABLE::check_tmp_key(uint key, uint key_parts,
+ uint (*next_field_no) (uchar *), uchar *arg)
+{
+ Field **reg_field;
+ uint i;
+ uint key_len= 0;
+
+ for (i= 0; i < key_parts; i++)
+ {
+ uint fld_idx= next_field_no(arg);
+ reg_field= field + fld_idx;
+ uint fld_store_len= (uint16) (*reg_field)->key_length();
+ if ((*reg_field)->real_maybe_null())
+ fld_store_len+= HA_KEY_NULL_LENGTH;
+ if ((*reg_field)->type() == MYSQL_TYPE_BLOB ||
+ (*reg_field)->real_type() == MYSQL_TYPE_VARCHAR ||
+ (*reg_field)->type() == MYSQL_TYPE_GEOMETRY)
+ fld_store_len+= HA_KEY_BLOB_LENGTH;
+ key_len+= fld_store_len;
+ }
+ /*
+ We use MI_MAX_KEY_LENGTH (myisam's default) below because it is
+ smaller than MAX_KEY_LENGTH (heap's default) and it's unknown whether
+ myisam or heap will be used for the temporary table.
+ */
+ return key_len <= MI_MAX_KEY_LENGTH;
+}
+
+/**
+ @brief
Add one key to a temporary table
@param key the number of the key
@@ -6149,6 +6195,7 @@ bool TABLE::add_tmp_key(uint key, uint key_parts,
KEY* keyinfo;
Field **reg_field;
uint i;
+
bool key_start= TRUE;
KEY_PART_INFO* key_part_info=
(KEY_PART_INFO*) alloc_root(&mem_root, sizeof(KEY_PART_INFO)*key_parts);
diff --git a/sql/table.h b/sql/table.h
index 3ac75ec06e1..8e31c0e2600 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1335,6 +1335,8 @@ public:
{ return !db_stat || m_needs_reopen; }
bool alloc_keys(uint key_count);
+ bool check_tmp_key(uint key, uint key_parts,
+ uint (*next_field_no) (uchar *), uchar *arg);
bool add_tmp_key(uint key, uint key_parts,
uint (*next_field_no) (uchar *), uchar *arg,
bool unique);