summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorNirbhay Choubey <nirbhay@mariadb.com>2015-01-26 22:54:27 -0500
committerNirbhay Choubey <nirbhay@mariadb.com>2015-01-26 22:54:27 -0500
commit7cda4bee0ef7c8a3ec85e94bc1443ceaba3a64e8 (patch)
treef8c1f556f36f026038015e7215ae88b0cb7b1ce5 /sql
parentea229eb6bb29ee7b11a7d28a7d4c80a593cb1d3b (diff)
parentfb71449b10100e9a0f887b1585000fbfab294f3c (diff)
downloadmariadb-git-7cda4bee0ef7c8a3ec85e94bc1443ceaba3a64e8.tar.gz
maria-10.0.16 merge
bzr merge -r4588 maria/10.0
Diffstat (limited to 'sql')
-rw-r--r--sql/debug_sync.cc8
-rw-r--r--sql/field.cc2
-rw-r--r--sql/field.h23
-rw-r--r--sql/filesort.cc13
-rw-r--r--sql/handler.cc4
-rw-r--r--sql/handler.h11
-rw-r--r--sql/item.cc18
-rw-r--r--sql/item.h2
-rw-r--r--sql/item_cmpfunc.cc51
-rw-r--r--sql/item_cmpfunc.h71
-rw-r--r--sql/item_func.cc17
-rw-r--r--sql/item_func.h7
-rw-r--r--sql/item_timefunc.cc7
-rw-r--r--sql/lock.cc9
-rw-r--r--sql/log.cc7
-rw-r--r--sql/log.h2
-rw-r--r--sql/log_event.cc16
-rw-r--r--sql/log_event.h4
-rw-r--r--sql/mdl.cc1
-rw-r--r--sql/mdl.h2
-rw-r--r--sql/mysqld.cc5
-rw-r--r--sql/mysqld.h2
-rw-r--r--sql/opt_range.cc30
-rw-r--r--sql/rpl_parallel.cc120
-rw-r--r--sql/rpl_parallel.h22
-rw-r--r--sql/rpl_rli.cc28
-rw-r--r--sql/rpl_rli.h4
-rw-r--r--sql/slave.cc2
-rw-r--r--sql/sql_class.h8
-rw-r--r--sql/sql_insert.cc2
-rw-r--r--sql/sql_parse.cc124
-rw-r--r--sql/sql_parse.h3
-rw-r--r--sql/sql_plugin.cc2
-rw-r--r--sql/sql_select.cc125
-rw-r--r--sql/sql_select.h1
-rw-r--r--sql/sql_show.cc14
-rw-r--r--sql/sql_statistics.cc10
-rw-r--r--sql/sql_table.cc34
-rw-r--r--sql/sql_update.cc3
-rw-r--r--sql/sql_view.cc6
-rw-r--r--sql/sys_vars.cc27
-rw-r--r--sql/table.cc32
-rw-r--r--sql/table.h1
43 files changed, 635 insertions, 245 deletions
diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc
index 2980ecd7dbf..5802d726aa2 100644
--- a/sql/debug_sync.cc
+++ b/sql/debug_sync.cc
@@ -1394,8 +1394,9 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
if (action->wait_for.length())
{
- mysql_mutex_t *old_mutex;
+ mysql_mutex_t *old_mutex= NULL;
mysql_cond_t *old_cond= NULL;
+ bool restore_current_mutex;
int error= 0;
struct timespec abstime;
@@ -1412,11 +1413,12 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
{
old_mutex= thd->mysys_var->current_mutex;
old_cond= thd->mysys_var->current_cond;
+ restore_current_mutex = true;
thd->mysys_var->current_mutex= &debug_sync_global.ds_mutex;
thd->mysys_var->current_cond= &debug_sync_global.ds_cond;
}
else
- old_mutex= NULL;
+ restore_current_mutex = false;
set_timespec(abstime, action->timeout);
DBUG_EXECUTE("debug_sync_exec", {
@@ -1476,7 +1478,7 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action)
is locked. (See comment in THD::exit_cond().)
*/
mysql_mutex_unlock(&debug_sync_global.ds_mutex);
- if (old_mutex)
+ if (restore_current_mutex)
{
mysql_mutex_lock(&thd->mysys_var->mutex);
thd->mysys_var->current_mutex= old_mutex;
diff --git a/sql/field.cc b/sql/field.cc
index ad59b4a63bd..e7e046a8458 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -4899,7 +4899,7 @@ void Field_timestamp::set_explicit_default(Item *value)
{
if (((value->type() == Item::DEFAULT_VALUE_ITEM &&
!((Item_default_value*)value)->arg) ||
- (!maybe_null() && value->is_null())))
+ (!maybe_null() && value->null_value)))
return;
set_has_explicit_value();
}
diff --git a/sql/field.h b/sql/field.h
index dd603d41bf7..0390e95f954 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -656,21 +656,28 @@ public:
inline bool is_null(my_ptrdiff_t row_offset= 0) const
{
/*
+ If the field is NULLable, it returns NULLity based
+ on null_ptr[row_offset] value. Otherwise it returns
+ NULL flag depending on TABLE::null_row value.
+
The table may have been marked as containing only NULL values
for all fields if it is a NULL-complemented row of an OUTER JOIN
or if the query is an implicitly grouped query (has aggregate
functions but no GROUP BY clause) with no qualifying rows. If
- this is the case (in which TABLE::null_row is true), the field
- is considered to be NULL.
+ this is the case (in which TABLE::null_row is true) and the
+ field is not nullable, the field is considered to be NULL.
+
+ Do not change the order of testing. Fields may be associated
+ with a TABLE object without being part of the current row.
+ For NULL value check to work for these fields, they must
+ have a valid null_ptr, and this pointer must be checked before
+ TABLE::null_row.
+
Note that if a table->null_row is set then also all null_bits are
set for the row.
-
- Otherwise, if the field is NULLable, it has a valid null_ptr
- pointer, and its NULLity is recorded in the "null_bit" bit of
- null_ptr[row_offset].
*/
- return (table->null_row ? TRUE :
- null_ptr ? MY_TEST(null_ptr[row_offset] & null_bit) : 0);
+ return real_maybe_null() ?
+ MY_TEST(null_ptr[row_offset] & null_bit) : table->null_row;
}
inline bool is_real_null(my_ptrdiff_t row_offset= 0) const
{ return null_ptr ? (null_ptr[row_offset] & null_bit ? 1 : 0) : 0; }
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 509a7f8e9b3..027437fca67 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -166,8 +166,6 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
TABLE_LIST *tab= table->pos_in_table_list;
Item_subselect *subselect= tab ? tab->containing_subselect() : 0;
- *found_rows= HA_POS_ERROR;
-
MYSQL_FILESORT_START(table->s->db.str, table->s->table_name.str);
DEBUG_SYNC(thd, "filesort_start");
@@ -190,6 +188,7 @@ ha_rows filesort(THD *thd, TABLE *table, SORT_FIELD *sortorder, uint s_length,
my_b_clear(&buffpek_pointers);
buffpek=0;
error= 1;
+ *found_rows= HA_POS_ERROR;
param.init_for_filesort(sortlength(thd, sortorder, s_length,
&multi_byte_charset),
@@ -690,8 +689,7 @@ static ha_rows find_all_keys(Sort_param *param, SQL_SELECT *select,
ref_pos= ref_buff;
quick_select=select && select->quick;
record=0;
- if (pq) // don't count unless pq is used
- *found_rows= 0;
+ *found_rows= 0;
flag= ((file->ha_table_flags() & HA_REC_NOT_IN_SEQ) || quick_select);
if (flag)
ref_pos= &file->ref[0];
@@ -814,14 +812,9 @@ static ha_rows find_all_keys(Sort_param *param, SQL_SELECT *select,
if (write_record)
{
+ ++(*found_rows);
if (pq)
{
- /*
- only count rows when pq is used - otherwise there might be
- other filters *after* the filesort, we don't know the final row
- count here
- */
- (*found_rows)++;
pq->push(ref_pos);
idx= pq->num_elements();
}
diff --git a/sql/handler.cc b/sql/handler.cc
index 85129f11c63..234c9408b74 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -86,9 +86,7 @@ static const LEX_STRING sys_table_aliases[]=
};
const char *ha_row_type[] = {
- "", "FIXED", "DYNAMIC", "COMPRESSED", "REDUNDANT", "COMPACT",
- "PAGE",
- "?","?","?"
+ "", "FIXED", "DYNAMIC", "COMPRESSED", "REDUNDANT", "COMPACT", "PAGE"
};
const char *tx_isolation_names[] =
diff --git a/sql/handler.h b/sql/handler.h
index d5a371027f6..3ad88f8b450 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -453,8 +453,10 @@ enum legacy_db_type
enum row_type { ROW_TYPE_NOT_USED=-1, ROW_TYPE_DEFAULT, ROW_TYPE_FIXED,
ROW_TYPE_DYNAMIC, ROW_TYPE_COMPRESSED,
- ROW_TYPE_REDUNDANT, ROW_TYPE_COMPACT,
- ROW_TYPE_PAGE };
+ ROW_TYPE_REDUNDANT, ROW_TYPE_COMPACT, ROW_TYPE_PAGE };
+
+/* not part of the enum, so that it shouldn't be in switch(row_type) */
+#define ROW_TYPE_MAX ((uint)ROW_TYPE_PAGE + 1)
/* Specifies data storage format for individual columns */
enum column_format_type {
@@ -1397,6 +1399,9 @@ static inline sys_var *find_hton_sysvar(handlerton *hton, st_mysql_sys_var *var)
#define HTON_NO_BINLOG_ROW_OPT (1 << 9)
#define HTON_SUPPORTS_EXTENDED_KEYS (1 <<10) //supports extended keys
+// MySQL compatibility. Unused.
+#define HTON_SUPPORTS_FOREIGN_KEYS (1 << 0) //Foreign key constraint supported.
+
class Ha_trx_info;
struct THD_TRANS
@@ -1578,7 +1583,7 @@ class partition_info;
struct st_partition_iter;
-enum ha_choice { HA_CHOICE_UNDEF, HA_CHOICE_NO, HA_CHOICE_YES };
+enum ha_choice { HA_CHOICE_UNDEF, HA_CHOICE_NO, HA_CHOICE_YES, HA_CHOICE_MAX };
enum enum_stats_auto_recalc { HA_STATS_AUTO_RECALC_DEFAULT= 0,
HA_STATS_AUTO_RECALC_ON,
diff --git a/sql/item.cc b/sql/item.cc
index 1dd4fc2909f..132cfa2846a 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -885,20 +885,20 @@ Item_ident::Item_ident(THD *thd, Item_ident *item)
void Item_ident::cleanup()
{
DBUG_ENTER("Item_ident::cleanup");
-#ifdef CANT_BE_USED_AS_MEMORY_IS_FREED
- db_name ? db_name : "(null)",
- orig_db_name ? orig_db_name : "(null)",
- table_name ? table_name : "(null)",
- orig_table_name ? orig_table_name : "(null)",
- field_name ? field_name : "(null)",
- orig_field_name ? orig_field_name : "(null)"));
-#endif
+ bool was_fixed= fixed;
Item::cleanup();
db_name= orig_db_name;
table_name= orig_table_name;
field_name= orig_field_name;
/* Store if this Item was depended */
- can_be_depended= MY_TEST(depended_from);
+ if (was_fixed)
+ {
+ /*
+ We can trust that depended_from set correctly only if this item
+ was fixed
+ */
+ can_be_depended= MY_TEST(depended_from);
+ }
DBUG_VOID_RETURN;
}
diff --git a/sql/item.h b/sql/item.h
index f337db92ef3..13e80639657 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -4458,7 +4458,7 @@ private:
/**
@todo
Implement the is_null() method for this class. Currently calling is_null()
- on any Item_cache object resolves to Item::is_null(), which reutns FALSE
+ on any Item_cache object resolves to Item::is_null(), which returns FALSE
for any value.
*/
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 62f63501d86..1f1982ffb80 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -621,17 +621,6 @@ int Arg_comparator::set_compare_func(Item_result_field *item, Item_result type)
}
case STRING_RESULT:
{
- /*
- We must set cmp_charset here as we may be called from for an automatic
- generated item, like in natural join
- */
- if (cmp_collation.set((*a)->collation, (*b)->collation) ||
- cmp_collation.derivation == DERIVATION_NONE)
- {
- my_coll_agg_error((*a)->collation, (*b)->collation,
- owner->func_name());
- return 1;
- }
if (cmp_collation.collation == &my_charset_bin)
{
/*
@@ -755,6 +744,37 @@ bool get_mysql_time_from_str(THD *thd, String *str, timestamp_type warn_type,
/**
+ Aggregate comparator argument charsets for comparison.
+ One of the arguments ("a" or "b") can be replaced,
+ typically by Item_string or Item_func_conv_charset.
+
+ @return Aggregation result
+ @retval false - if no conversion is needed,
+ or if one of the arguments was converted
+ @retval true - on error, if arguments are not comparable.
+
+ TODO: get rid of this method eventually and refactor the calling code.
+ Argument conversion should happen on the Item_func level.
+ Arg_comparator should get comparable arguments.
+*/
+bool Arg_comparator::agg_arg_charsets_for_comparison()
+{
+ if (cmp_collation.set((*a)->collation, (*b)->collation, MY_COLL_CMP_CONV) ||
+ cmp_collation.derivation == DERIVATION_NONE)
+ {
+ my_coll_agg_error((*a)->collation, (*b)->collation, owner->func_name());
+ return true;
+ }
+ if (agg_item_set_converter(cmp_collation, owner->func_name(),
+ a, 1, MY_COLL_CMP_CONV, 1) ||
+ agg_item_set_converter(cmp_collation, owner->func_name(),
+ b, 1, MY_COLL_CMP_CONV, 1))
+ return true;
+ return false;
+}
+
+
+/**
Prepare the comparator (set the comparison function) for comparing
items *a1 and *a2 in the context of 'type'.
@@ -781,10 +801,11 @@ int Arg_comparator::set_cmp_func(Item_result_field *owner_arg,
(*a)->result_type() == STRING_RESULT &&
(*b)->result_type() == STRING_RESULT)
{
- DTCollation coll;
- coll.set((*a)->collation.collation);
- if (agg_item_set_converter(coll, owner->func_name(),
- b, 1, MY_COLL_CMP_CONV, 1))
+ /*
+ We must set cmp_collation here as we may be called from for an automatic
+ generated item, like in natural join
+ */
+ if (agg_arg_charsets_for_comparison())
return 1;
}
if (type == INT_RESULT &&
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index d4a1c6b1384..5d11057228c 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -48,6 +48,14 @@ class Arg_comparator: public Sql_alloc
THD *thd;
Item *a_cache, *b_cache; // Cached values of a and b items
// when one of arguments is NULL.
+ int set_compare_func(Item_result_field *owner, Item_result type);
+ inline int set_compare_func(Item_result_field *owner_arg)
+ {
+ return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(),
+ (*b)->result_type()));
+ }
+ bool agg_arg_charsets_for_comparison();
+
public:
DTCollation cmp_collation;
/* Allow owner function to use string buffers. */
@@ -58,12 +66,6 @@ public:
Arg_comparator(Item **a1, Item **a2): a(a1), b(a2), set_null(TRUE),
comparators(0), thd(0), a_cache(0), b_cache(0) {};
- int set_compare_func(Item_result_field *owner, Item_result type);
- inline int set_compare_func(Item_result_field *owner_arg)
- {
- return set_compare_func(owner_arg, item_cmp_type((*a)->result_type(),
- (*b)->result_type()));
- }
int set_cmp_func(Item_result_field *owner_arg,
Item **a1, Item **a2,
Item_result type);
@@ -122,6 +124,8 @@ public:
Item_bool_func() :Item_int_func() {}
Item_bool_func(Item *a) :Item_int_func(a) {}
Item_bool_func(Item *a,Item *b) :Item_int_func(a,b) {}
+ Item_bool_func(Item *a, Item *b, Item *c) :Item_int_func(a, b, c) {}
+ Item_bool_func(List<Item> &list) :Item_int_func(list) { }
Item_bool_func(THD *thd, Item_bool_func *item) :Item_int_func(thd, item) {}
bool is_bool_func() { return 1; }
void fix_length_and_dec() { decimals=0; max_length=1; }
@@ -364,7 +368,7 @@ public:
virtual bool l_op() const { return 1; }
};
-class Item_bool_func2 :public Item_int_func
+class Item_bool_func2 :public Item_bool_func
{ /* Bool with 2 string args */
protected:
Arg_comparator cmp;
@@ -372,7 +376,7 @@ protected:
public:
Item_bool_func2(Item *a,Item *b)
- :Item_int_func(a,b), cmp(tmp_arg, tmp_arg+1),
+ :Item_bool_func(a,b), cmp(tmp_arg, tmp_arg+1),
abort_on_null(FALSE) { sargable= TRUE; }
void fix_length_and_dec();
int set_cmp_func()
@@ -389,14 +393,12 @@ public:
}
bool is_null() { return MY_TEST(args[0]->is_null() || args[1]->is_null()); }
- bool is_bool_func() { return 1; }
CHARSET_INFO *compare_collation() { return cmp.cmp_collation.collation; }
- uint decimal_precision() const { return 1; }
void top_level_item() { abort_on_null= TRUE; }
Arg_comparator *get_comparator() { return &cmp; }
void cleanup()
{
- Item_int_func::cleanup();
+ Item_bool_func::cleanup();
cmp.cleanup();
}
@@ -646,16 +648,16 @@ public:
*/
-class Item_func_opt_neg :public Item_int_func
+class Item_func_opt_neg :public Item_bool_func
{
public:
bool negated; /* <=> the item represents NOT <func> */
bool pred_level; /* <=> [NOT] <func> is used on a predicate level */
public:
Item_func_opt_neg(Item *a, Item *b, Item *c)
- :Item_int_func(a, b, c), negated(0), pred_level(0) {}
+ :Item_bool_func(a, b, c), negated(0), pred_level(0) {}
Item_func_opt_neg(List<Item> &list)
- :Item_int_func(list), negated(0), pred_level(0) {}
+ :Item_bool_func(list), negated(0), pred_level(0) {}
public:
inline void negate() { negated= !negated; }
inline void top_level_item() { pred_level= 1; }
@@ -686,9 +688,7 @@ public:
bool fix_fields(THD *, Item **);
void fix_length_and_dec();
virtual void print(String *str, enum_query_type query_type);
- bool is_bool_func() { return 1; }
CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
- uint decimal_precision() const { return 1; }
bool eval_not_null_tables(uchar *opt_arg);
void fix_after_pullout(st_select_lex *new_parent, Item **ref);
bool count_sargable_conds(uchar *arg);
@@ -1316,7 +1316,6 @@ public:
longlong val_int();
bool fix_fields(THD *, Item **);
void fix_length_and_dec();
- uint decimal_precision() const { return 1; }
void cleanup()
{
uint i;
@@ -1337,7 +1336,6 @@ public:
enum Functype functype() const { return IN_FUNC; }
const char *func_name() const { return " IN "; }
bool nulls_in_row();
- bool is_bool_func() { return 1; }
CHARSET_INFO *compare_collation() { return cmp_collation.collation; }
bool eval_not_null_tables(uchar *opt_arg);
void fix_after_pullout(st_select_lex *new_parent, Item **ref);
@@ -1490,7 +1488,42 @@ public:
longlong val_int();
enum Functype functype() const { return LIKE_FUNC; }
optimize_type select_optimize() const;
- cond_result eq_cmp_result() const { return COND_TRUE; }
+ cond_result eq_cmp_result() const
+ {
+ /**
+ We cannot always rewrite conditions as follows:
+ from: WHERE expr1=const AND expr1 LIKE expr2
+ to: WHERE expr1=const AND const LIKE expr2
+ or
+ from: WHERE expr1=const AND expr2 LIKE expr1
+ to: WHERE expr1=const AND expr2 LIKE const
+
+ because LIKE works differently comparing to the regular "=" operator:
+
+ 1. LIKE performs a stricter one-character-to-one-character comparison
+ and does not recognize contractions and expansions.
+ Replacing "expr1" to "const in LIKE would make the condition
+ stricter in case of a complex collation.
+
+ 2. LIKE does not ignore trailing spaces and thus works differently
+ from the "=" operator in case of "PAD SPACE" collations
+ (which are the majority in MariaDB). So, for "PAD SPACE" collations:
+
+ - expr1=const - ignores trailing spaces
+ - const LIKE expr2 - does not ignore trailing spaces
+ - expr2 LIKE const - does not ignore trailing spaces
+
+ Allow only "binary" for now.
+ It neither ignores trailing spaces nor has contractions/expansions.
+
+ TODO:
+ We could still replace "expr1" to "const" in "expr1 LIKE expr2"
+ in case of a "PAD SPACE" collation, but only if "expr2" has '%'
+ at the end.
+ */
+ return ((Item_func_like *)this)->compare_collation() == &my_charset_bin ?
+ COND_TRUE : COND_OK;
+ }
const char *func_name() const { return "like"; }
bool fix_fields(THD *thd, Item **ref);
void cleanup();
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 1845c6dff98..bd000f6ca62 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -4191,9 +4191,10 @@ void mysql_ull_set_explicit_lock_duration(THD *thd)
When MDL detects a lock wait timeout, it pushes
an error into the statement diagnostics area.
For GET_LOCK(), lock wait timeout is not an error,
- but a special return value (0). NULL is returned in
- case of error.
- Capture and suppress lock wait timeout.
+ but a special return value (0).
+ Similarly, killing get_lock wait is not an error either,
+ but a return value NULL.
+ Capture and suppress lock wait timeouts and kills.
*/
class Lock_wait_timeout_handler: public Internal_error_handler
@@ -4212,7 +4213,7 @@ public:
bool
Lock_wait_timeout_handler::
-handle_condition(THD * /* thd */, uint sql_errno,
+handle_condition(THD *thd, uint sql_errno,
const char * /* sqlstate */,
Sql_condition::enum_warning_level /* level */,
const char *message,
@@ -4223,6 +4224,9 @@ handle_condition(THD * /* thd */, uint sql_errno,
m_lock_wait_timeout= true;
return true; /* condition handled */
}
+ if (thd->is_killed())
+ return true;
+
return false;
}
@@ -4628,6 +4632,11 @@ longlong Item_func_sleep::val_int()
mysql_cond_destroy(&cond);
+ DBUG_EXECUTE_IF("sleep_inject_query_done_debug_sync", {
+ debug_sync_set_action
+ (thd, STRING_WITH_LEN("dispatch_command_end SIGNAL query_done"));
+ };);
+
return MY_TEST(!error); // Return 1 killed
}
diff --git a/sql/item_func.h b/sql/item_func.h
index e40f2d771c6..ce1f2fdd676 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -1,7 +1,7 @@
#ifndef ITEM_FUNC_INCLUDED
#define ITEM_FUNC_INCLUDED
-/* Copyright (c) 2000, 2013, Oracle and/or its affiliates.
- Copyright (c) 2009, 2014, SkySQL Ab.
+/* Copyright (c) 2000, 2014, Oracle and/or its affiliates.
+ Copyright (c) 2009, 2014, MariaDB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -287,7 +287,8 @@ public:
inline longlong check_integer_overflow(longlong value, bool val_unsigned)
{
if ((unsigned_flag && !val_unsigned && value < 0) ||
- (!unsigned_flag && val_unsigned && (ulonglong) value > LONGLONG_MAX))
+ (!unsigned_flag && val_unsigned &&
+ (ulonglong) value > (ulonglong) LONGLONG_MAX))
return raise_integer_overflow();
return value;
}
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 389d9d5380c..fb55b7660cb 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -1564,10 +1564,9 @@ String *Item_temporal_hybrid_func::val_str_ascii(String *str)
bool Item_func_from_days::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date)
{
longlong value=args[0]->val_int();
- if (args[0]->null_value)
- return (null_value= 1);
- if ((fuzzy_date & TIME_NO_ZERO_DATE) && value == 0)
- return (null_value= 1);
+ if ((null_value= (args[0]->null_value ||
+ ((fuzzy_date & TIME_NO_ZERO_DATE) && value == 0))))
+ return true;
bzero(ltime, sizeof(MYSQL_TIME));
if (get_date_from_daynr((long) value, &ltime->year, &ltime->month,
&ltime->day))
diff --git a/sql/lock.cc b/sql/lock.cc
index 4d7afc697ef..7c8368ab0e3 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -305,15 +305,16 @@ bool mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock, uint flags)
int rc= 1;
ulong timeout= (flags & MYSQL_LOCK_IGNORE_TIMEOUT) ?
LONG_TIMEOUT : thd->variables.lock_wait_timeout;
-
+ PSI_stage_info org_stage;
DBUG_ENTER("mysql_lock_tables(sql_lock)");
- THD_STAGE_INFO(thd, stage_system_lock);
+ thd->enter_stage(&stage_system_lock, &org_stage, __func__, __FILE__,
+ __LINE__);
if (sql_lock->table_count && lock_external(thd, sql_lock->table,
sql_lock->table_count))
goto end;
- thd_proc_info(thd, "Table lock");
+ THD_STAGE_INFO(thd, stage_table_lock);
/* Copy the lock data array. thr_multi_lock() reorders its contents. */
memmove(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
@@ -331,7 +332,7 @@ bool mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock, uint flags)
(void) unlock_external(thd, sql_lock->table, sql_lock->table_count);
end:
- THD_STAGE_INFO(thd, stage_after_table_lock);
+ THD_STAGE_INFO(thd, org_stage);
#ifdef WITH_WSREP
thd_proc_info(thd, "mysql_lock_tables(): unlocking tables II");
#else /* WITH_WSREP */
diff --git a/sql/log.cc b/sql/log.cc
index 7a8531b5f46..69e0ed34b01 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -3200,7 +3200,7 @@ const char *MYSQL_LOG::generate_name(const char *log_name,
MYSQL_BIN_LOG::MYSQL_BIN_LOG(uint *sync_period)
- :reset_master_pending(false), mark_xid_done_waiting(0),
+ :reset_master_pending(0), mark_xid_done_waiting(0),
bytes_written(0), file_id(1), open_count(1),
group_commit_queue(0), group_commit_queue_busy(FALSE),
num_commits(0), num_group_commits(0),
@@ -4036,12 +4036,13 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd, bool create_new_log,
do this before we take the LOCK_log to not deadlock.
*/
mysql_mutex_lock(&LOCK_xid_list);
- reset_master_pending= true;
+ reset_master_pending++;
while (mark_xid_done_waiting > 0)
mysql_cond_wait(&COND_xid_list, &LOCK_xid_list);
mysql_mutex_unlock(&LOCK_xid_list);
}
+ DEBUG_SYNC(thd, "reset_logs_after_set_reset_master_pending");
if (thd)
ha_reset_logs(thd);
/*
@@ -4223,7 +4224,7 @@ err:
DBUG_ASSERT(b->xid_count == 0);
my_free(binlog_xid_count_list.get());
}
- reset_master_pending= false;
+ reset_master_pending--;
mysql_mutex_unlock(&LOCK_xid_list);
}
diff --git a/sql/log.h b/sql/log.h
index d5aab4ac612..bda6e55e0c9 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -470,7 +470,7 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG
anyway). Instead we should signal COND_xid_list whenever a new binlog
checkpoint arrives - when all have arrived, RESET MASTER will complete.
*/
- bool reset_master_pending;
+ uint reset_master_pending;
ulong mark_xid_done_waiting;
/* LOCK_log and LOCK_index are inited by init_pthread_objects() */
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 525aa65bc8e..95c81258216 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -1672,7 +1672,7 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
ev = new Execute_load_log_event(buf, event_len, description_event);
break;
case START_EVENT_V3: /* this is sent only by MySQL <=4.x */
- ev = new Start_log_event_v3(buf, description_event);
+ ev = new Start_log_event_v3(buf, event_len, description_event);
break;
case STOP_EVENT:
ev = new Stop_log_event(buf, description_event);
@@ -4691,11 +4691,16 @@ void Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
Start_log_event_v3::Start_log_event_v3()
*/
-Start_log_event_v3::Start_log_event_v3(const char* buf,
+Start_log_event_v3::Start_log_event_v3(const char* buf, uint event_len,
const Format_description_log_event
*description_event)
- :Log_event(buf, description_event)
+ :Log_event(buf, description_event), binlog_version(BINLOG_VERSION)
{
+ if (event_len < LOG_EVENT_MINIMAL_HEADER_LEN + ST_COMMON_HEADER_LEN_OFFSET)
+ {
+ server_version[0]= 0;
+ return;
+ }
buf+= LOG_EVENT_MINIMAL_HEADER_LEN;
binlog_version= uint2korr(buf+ST_BINLOG_VER_OFFSET);
memcpy(server_version, buf+ST_SERVER_VER_OFFSET,
@@ -5000,9 +5005,12 @@ Format_description_log_event(const char* buf,
const
Format_description_log_event*
description_event)
- :Start_log_event_v3(buf, description_event), event_type_permutation(0)
+ :Start_log_event_v3(buf, event_len, description_event),
+ common_header_len(0), post_header_len(NULL), event_type_permutation(0)
{
DBUG_ENTER("Format_description_log_event::Format_description_log_event(char*,...)");
+ if (!Start_log_event_v3::is_valid())
+ DBUG_VOID_RETURN; /* sanity check */
buf+= LOG_EVENT_MINIMAL_HEADER_LEN;
if ((common_header_len=buf[ST_COMMON_HEADER_LEN_OFFSET]) < OLD_HEADER_LEN)
DBUG_VOID_RETURN; /* sanity check */
diff --git a/sql/log_event.h b/sql/log_event.h
index c0370014c7d..6a3e6f174bb 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -2515,7 +2515,7 @@ public:
void print(FILE* file, PRINT_EVENT_INFO* print_event_info);
#endif
- Start_log_event_v3(const char* buf,
+ Start_log_event_v3(const char* buf, uint event_len,
const Format_description_log_event* description_event);
~Start_log_event_v3() {}
Log_event_type get_type_code() { return START_EVENT_V3;}
@@ -2524,7 +2524,7 @@ public:
#ifdef MYSQL_SERVER
bool write(IO_CACHE* file);
#endif
- bool is_valid() const { return 1; }
+ bool is_valid() const { return server_version[0] != 0; }
int get_data_size()
{
return START_V3_HEADER_LEN; //no variable-sized part
diff --git a/sql/mdl.cc b/sql/mdl.cc
index 5755b2bbfd5..4c962d3c570 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -2517,6 +2517,7 @@ MDL_context::acquire_lock(MDL_request *mdl_request, ulong lock_wait_timeout)
my_error(ER_LOCK_WAIT_TIMEOUT, MYF(0));
break;
case MDL_wait::KILLED:
+ get_thd()->send_kill_message();
break;
default:
DBUG_ASSERT(0);
diff --git a/sql/mdl.h b/sql/mdl.h
index 639a8966b33..421258c2ab7 100644
--- a/sql/mdl.h
+++ b/sql/mdl.h
@@ -351,7 +351,7 @@ public:
NAME_LEN) - m_ptr + 1);
m_hash_value= my_hash_sort(&my_charset_bin, (uchar*) m_ptr + 1,
m_length - 1);
- DBUG_ASSERT(ok_for_lower_case_names(db));
+ DBUG_ASSERT(mdl_namespace == USER_LOCK || ok_for_lower_case_names(db));
}
void mdl_key_init(const MDL_key *rhs)
{
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 84e2ae56fa4..19f067f582f 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -6254,6 +6254,7 @@ int mysqld_main(int argc, char **argv)
(char*) "" : mysqld_unix_port),
mysqld_port,
MYSQL_COMPILATION_COMMENT);
+ fclose(stdin);
#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY)
Service.SetRunning();
#endif
@@ -10266,6 +10267,8 @@ PSI_stage_info stage_sql_thd_waiting_until_delay= { 0, "Waiting until MASTER_DEL
PSI_stage_info stage_storing_result_in_query_cache= { 0, "storing result in query cache", 0};
PSI_stage_info stage_storing_row_into_queue= { 0, "storing row into queue", 0};
PSI_stage_info stage_system_lock= { 0, "System lock", 0};
+PSI_stage_info stage_table_lock= { 0, "Table lock", 0};
+PSI_stage_info stage_filling_schema_table= { 0, "Filling schema table", 0};
PSI_stage_info stage_update= { 0, "update", 0};
PSI_stage_info stage_updating= { 0, "updating", 0};
PSI_stage_info stage_updating_main_table= { 0, "updating main table", 0};
@@ -10399,6 +10402,8 @@ PSI_stage_info *all_server_stages[]=
& stage_storing_result_in_query_cache,
& stage_storing_row_into_queue,
& stage_system_lock,
+ & stage_table_lock,
+ & stage_filling_schema_table,
& stage_update,
& stage_updating,
& stage_updating_main_table,
diff --git a/sql/mysqld.h b/sql/mysqld.h
index 860800cb725..a9eb4fd1ca5 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -418,6 +418,8 @@ extern PSI_stage_info stage_statistics;
extern PSI_stage_info stage_storing_result_in_query_cache;
extern PSI_stage_info stage_storing_row_into_queue;
extern PSI_stage_info stage_system_lock;
+extern PSI_stage_info stage_table_lock;
+extern PSI_stage_info stage_filling_schema_table;
extern PSI_stage_info stage_update;
extern PSI_stage_info stage_updating;
extern PSI_stage_info stage_updating_main_table;
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index a5c27fa66e2..3597ade2cba 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -13167,12 +13167,13 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time)
SYNOPSIS
check_group_min_max_predicates()
- cond [in] the expression tree being analyzed
- min_max_arg [in] the field referenced by the MIN/MAX function(s)
- image_type [in]
- has_min_max_arg [out] true if the subtree being analyzed references min_max_arg
- has_other_arg [out] true if the subtree being analyzed references a column
- other min_max_arg
+ cond [in] the expression tree being analyzed
+ min_max_arg [in] the field referenced by the MIN/MAX function(s)
+ image_type [in]
+ has_min_max_arg [out] true if the subtree being analyzed references
+ min_max_arg
+ has_other_arg [out] true if the subtree being analyzed references a
+ column other min_max_arg
DESCRIPTION
The function walks recursively over the cond tree representing a WHERE
@@ -13216,7 +13217,7 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
(2) the subtree passes the test, but it is an OR and it references both
the min/max argument and other columns.
*/
- if (!check_group_min_max_predicates(and_or_arg, min_max_arg_item, //1
+ if (!check_group_min_max_predicates(and_or_arg, min_max_arg_item, //1
image_type,
&has_min_max, &has_other) ||
(func_type == Item_func::COND_OR_FUNC && has_min_max && has_other))//2
@@ -13232,7 +13233,7 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
a subquery in the WHERE clause.
*/
- if (cond_type == Item::SUBSELECT_ITEM)
+ if (unlikely(cond_type == Item::SUBSELECT_ITEM))
{
Item_subselect *subs_cond= (Item_subselect*) cond;
if (subs_cond->is_correlated)
@@ -13249,7 +13250,14 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
}
DBUG_RETURN(TRUE);
}
-
+ /*
+ Subquery with IS [NOT] NULL
+ TODO: Look into the cache_item and optimize it like we do for
+ subselect's above
+ */
+ if (unlikely(cond_type == Item::CACHE_ITEM))
+ DBUG_RETURN(cond->const_item());
+
/*
Condition of the form 'field' is equivalent to 'field <> 0' and thus
satisfies the SA3 condition.
@@ -13266,7 +13274,9 @@ check_group_min_max_predicates(Item *cond, Item_field *min_max_arg_item,
/* We presume that at this point there are no other Items than functions. */
DBUG_ASSERT(cond_type == Item::FUNC_ITEM);
-
+ if (unlikely(cond_type != Item::FUNC_ITEM)) /* Safety */
+ DBUG_RETURN(FALSE);
+
/* Test if cond references only group-by or non-group fields. */
Item_func *pred= (Item_func*) cond;
Item_func::Functype pred_type= pred->functype();
diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc
index 7c273d51a19..46c3e4aaaf4 100644
--- a/sql/rpl_parallel.cc
+++ b/sql/rpl_parallel.cc
@@ -106,9 +106,10 @@ handle_queued_pos_update(THD *thd, rpl_parallel_thread::queued_event *qev)
static void
-finish_event_group(THD *thd, uint64 sub_id, rpl_parallel_entry *entry,
- rpl_group_info *rgi)
+finish_event_group(rpl_parallel_thread *rpt, uint64 sub_id,
+ rpl_parallel_entry *entry, rpl_group_info *rgi)
{
+ THD *thd= rpt->thd;
wait_for_commit *wfc= &rgi->commit_orderer;
int err;
@@ -139,25 +140,47 @@ finish_event_group(THD *thd, uint64 sub_id, rpl_parallel_entry *entry,
signal_error_to_sql_driver_thread(thd, rgi, err);
thd->wait_for_commit_ptr= NULL;
+ mysql_mutex_lock(&entry->LOCK_parallel_entry);
/*
- Record that this event group has finished (eg. transaction is
- committed, if transactional), so other event groups will no longer
- attempt to wait for us to commit. Once we have increased
- entry->last_committed_sub_id, no other threads will execute
- register_wait_for_prior_commit() against us. Thus, by doing one
- extra (usually redundant) wakeup_subsequent_commits() we can ensure
- that no register_wait_for_prior_commit() can ever happen without a
- subsequent wakeup_subsequent_commits() to wake it up.
-
- We can race here with the next transactions, but that is fine, as
- long as we check that we do not decrease last_committed_sub_id. If
- this commit is done, then any prior commits will also have been
- done and also no longer need waiting for.
+ We need to mark that this event group started its commit phase, in case we
+ missed it before (otherwise we would deadlock the next event group that is
+ waiting for this). In most cases (normal DML), it will be a no-op.
*/
- mysql_mutex_lock(&entry->LOCK_parallel_entry);
+ rgi->mark_start_commit_no_lock();
+
if (entry->last_committed_sub_id < sub_id)
+ {
+ /*
+ Record that this event group has finished (eg. transaction is
+ committed, if transactional), so other event groups will no longer
+ attempt to wait for us to commit. Once we have increased
+ entry->last_committed_sub_id, no other threads will execute
+ register_wait_for_prior_commit() against us. Thus, by doing one
+ extra (usually redundant) wakeup_subsequent_commits() we can ensure
+ that no register_wait_for_prior_commit() can ever happen without a
+ subsequent wakeup_subsequent_commits() to wake it up.
+
+ We can race here with the next transactions, but that is fine, as
+ long as we check that we do not decrease last_committed_sub_id. If
+ this commit is done, then any prior commits will also have been
+ done and also no longer need waiting for.
+ */
entry->last_committed_sub_id= sub_id;
+ /* Now free any GCOs in which all transactions have committed. */
+ group_commit_orderer *tmp_gco= rgi->gco;
+ while (tmp_gco &&
+ (!tmp_gco->next_gco || tmp_gco->last_sub_id > sub_id))
+ tmp_gco= tmp_gco->prev_gco;
+ while (tmp_gco)
+ {
+ group_commit_orderer *prev_gco= tmp_gco->prev_gco;
+ tmp_gco->next_gco->prev_gco= NULL;
+ rpt->loc_free_gco(tmp_gco);
+ tmp_gco= prev_gco;
+ }
+ }
+
/*
If this event group got error, then any following event groups that have
not yet started should just skip their group, preparing for stop of the
@@ -166,12 +189,6 @@ finish_event_group(THD *thd, uint64 sub_id, rpl_parallel_entry *entry,
if (unlikely(rgi->worker_error) &&
entry->stop_on_error_sub_id == (uint64)ULONGLONG_MAX)
entry->stop_on_error_sub_id= sub_id;
- /*
- We need to mark that this event group started its commit phase, in case we
- missed it before (otherwise we would deadlock the next event group that is
- waiting for this). In most cases (normal DML), it will be a no-op.
- */
- rgi->mark_start_commit_no_lock();
mysql_mutex_unlock(&entry->LOCK_parallel_entry);
thd->clear_error();
@@ -329,6 +346,7 @@ do_retry:
until after the unmark.
*/
rgi->unmark_start_commit();
+ DEBUG_SYNC(thd, "rpl_parallel_retry_after_unmark");
/*
We might get the deadlock error that causes the retry during commit, while
@@ -517,7 +535,7 @@ handle_rpl_parallel_thread(void *arg)
bool in_event_group= false;
bool skip_event_group= false;
rpl_group_info *group_rgi= NULL;
- group_commit_orderer *gco, *tmp_gco;
+ group_commit_orderer *gco;
uint64 event_gtid_sub_id= 0;
rpl_sql_thread_info sql_info(NULL);
int err;
@@ -610,7 +628,7 @@ handle_rpl_parallel_thread(void *arg)
*/
group_rgi->cleanup_context(thd, 1);
in_event_group= false;
- finish_event_group(thd, group_rgi->gtid_sub_id,
+ finish_event_group(rpt, group_rgi->gtid_sub_id,
qev->entry_for_queued, group_rgi);
rpt->loc_free_rgi(group_rgi);
@@ -631,6 +649,14 @@ handle_rpl_parallel_thread(void *arg)
PSI_stage_info old_stage;
uint64 wait_count;
+ DBUG_EXECUTE_IF("rpl_parallel_scheduled_gtid_0_x_100", {
+ if (rgi->current_gtid.domain_id == 0 &&
+ rgi->current_gtid.seq_no == 100) {
+ debug_sync_set_action(thd,
+ STRING_WITH_LEN("now SIGNAL scheduled_gtid_0_x_100"));
+ }
+ });
+
in_event_group= true;
/*
If the standalone flag is set, then this event group consists of a
@@ -656,8 +682,12 @@ handle_rpl_parallel_thread(void *arg)
mysql_mutex_lock(&entry->LOCK_parallel_entry);
if (!gco->installed)
{
- if (gco->prev_gco)
- gco->prev_gco->next_gco= gco;
+ group_commit_orderer *prev_gco= gco->prev_gco;
+ if (prev_gco)
+ {
+ prev_gco->last_sub_id= gco->prior_sub_id;
+ prev_gco->next_gco= gco;
+ }
gco->installed= true;
}
wait_count= gco->wait_count;
@@ -674,6 +704,8 @@ handle_rpl_parallel_thread(void *arg)
if (thd->check_killed() && !rgi->worker_error)
{
DEBUG_SYNC(thd, "rpl_parallel_start_waiting_for_prior_killed");
+ thd->clear_error();
+ thd->get_stmt_da()->reset_diagnostics_area();
thd->send_kill_message();
slave_output_error_info(rgi, thd);
signal_error_to_sql_driver_thread(thd, rgi, 1);
@@ -690,18 +722,6 @@ handle_rpl_parallel_thread(void *arg)
} while (wait_count > entry->count_committing_event_groups);
}
- if ((tmp_gco= gco->prev_gco))
- {
- /*
- Now all the event groups in the previous batch have entered their
- commit phase, and will no longer access their gco. So we can free
- it here.
- */
- DBUG_ASSERT(!tmp_gco->prev_gco);
- gco->prev_gco= NULL;
- rpt->loc_free_gco(tmp_gco);
- }
-
if (entry->force_abort && wait_count > entry->stop_count)
{
/*
@@ -765,6 +785,7 @@ handle_rpl_parallel_thread(void *arg)
{
DEBUG_SYNC(thd, "rpl_parallel_before_mark_start_commit");
rgi->mark_start_commit();
+ DEBUG_SYNC(thd, "rpl_parallel_after_mark_start_commit");
}
/*
@@ -785,6 +806,7 @@ handle_rpl_parallel_thread(void *arg)
thd->get_stmt_da()->reset_diagnostics_area();
my_error(ER_LOCK_DEADLOCK, MYF(0));
err= 1;
+ DEBUG_SYNC(thd, "rpl_parallel_simulate_temp_err_xid");
});
if (!err)
#endif
@@ -824,7 +846,7 @@ handle_rpl_parallel_thread(void *arg)
if (end_of_group)
{
in_event_group= false;
- finish_event_group(thd, event_gtid_sub_id, entry, rgi);
+ finish_event_group(rpt, event_gtid_sub_id, entry, rgi);
rpt->loc_free_rgi(rgi);
thd->rgi_slave= group_rgi= rgi= NULL;
skip_event_group= false;
@@ -865,7 +887,7 @@ handle_rpl_parallel_thread(void *arg)
*/
mysql_mutex_unlock(&rpt->LOCK_rpl_thread);
signal_error_to_sql_driver_thread(thd, group_rgi, 1);
- finish_event_group(thd, group_rgi->gtid_sub_id,
+ finish_event_group(rpt, group_rgi->gtid_sub_id,
group_rgi->parallel_entry, group_rgi);
in_event_group= false;
mysql_mutex_lock(&rpt->LOCK_rpl_thread);
@@ -914,7 +936,6 @@ handle_rpl_parallel_thread(void *arg)
static void
dealloc_gco(group_commit_orderer *gco)
{
- DBUG_ASSERT(!gco->prev_gco /* Must only free after dealloc previous */);
mysql_cond_destroy(&gco->COND_group_commit_orderer);
my_free(gco);
}
@@ -1123,9 +1144,9 @@ rpl_parallel_thread::inuse_relaylog_refcount_update()
inuse_relaylog *ir= accumulated_ir_last;
if (ir)
{
- my_atomic_rwlock_wrlock(&ir->rli->inuse_relaylog_atomic_lock);
+ my_atomic_rwlock_wrlock(&ir->inuse_relaylog_atomic_lock);
my_atomic_add64(&ir->dequeued_count, accumulated_ir_count);
- my_atomic_rwlock_wrunlock(&ir->rli->inuse_relaylog_atomic_lock);
+ my_atomic_rwlock_wrunlock(&ir->inuse_relaylog_atomic_lock);
accumulated_ir_count= 0;
accumulated_ir_last= NULL;
}
@@ -1295,7 +1316,8 @@ rpl_parallel_thread::free_rgi(rpl_group_info *rgi)
group_commit_orderer *
-rpl_parallel_thread::get_gco(uint64 wait_count, group_commit_orderer *prev)
+rpl_parallel_thread::get_gco(uint64 wait_count, group_commit_orderer *prev,
+ uint64 prior_sub_id)
{
group_commit_orderer *gco;
mysql_mutex_assert_owner(&LOCK_rpl_thread);
@@ -1311,6 +1333,7 @@ rpl_parallel_thread::get_gco(uint64 wait_count, group_commit_orderer *prev)
gco->wait_count= wait_count;
gco->prev_gco= prev;
gco->next_gco= NULL;
+ gco->prior_sub_id= prior_sub_id;
gco->installed= false;
return gco;
}
@@ -1319,7 +1342,6 @@ rpl_parallel_thread::get_gco(uint64 wait_count, group_commit_orderer *prev)
void
rpl_parallel_thread::loc_free_gco(group_commit_orderer *gco)
{
- DBUG_ASSERT(!gco->prev_gco /* Must not free until wait has completed. */);
if (!loc_gco_list)
loc_gco_last_ptr_ptr= &gco->next_gco;
else
@@ -1526,8 +1548,12 @@ static void
free_rpl_parallel_entry(void *element)
{
rpl_parallel_entry *e= (rpl_parallel_entry *)element;
- if (e->current_gco)
+ while (e->current_gco)
+ {
+ group_commit_orderer *prev_gco= e->current_gco->prev_gco;
dealloc_gco(e->current_gco);
+ e->current_gco= prev_gco;
+ }
mysql_cond_destroy(&e->COND_parallel_entry);
mysql_mutex_destroy(&e->LOCK_parallel_entry);
my_free(e);
@@ -1999,7 +2025,7 @@ rpl_parallel::do_event(rpl_group_info *serial_rgi, Log_event *ev,
uint64 count= e->count_queued_event_groups;
group_commit_orderer *gco;
- if (!(gco= cur_thread->get_gco(count, e->current_gco)))
+ if (!(gco= cur_thread->get_gco(count, e->current_gco, e->current_sub_id)))
{
cur_thread->free_rgi(rgi);
cur_thread->free_qev(qev);
diff --git a/sql/rpl_parallel.h b/sql/rpl_parallel.h
index 239818855b8..2604cd98527 100644
--- a/sql/rpl_parallel.h
+++ b/sql/rpl_parallel.h
@@ -39,9 +39,12 @@ struct inuse_relaylog;
rpl_parallel_entry::count_committing_event_groups has reached
gco->next_gco->wait_count.
- - When gco->wait_count is reached for a worker and the wait completes,
- the worker frees gco->prev_gco; at this point it is guaranteed not to
- be needed any longer.
+ - The gco lives until all its event groups have completed their commit.
+ This is detected by rpl_parallel_entry::last_committed_sub_id being
+ greater than or equal gco->last_sub_id. Once this happens, the gco is
+ freed. Note that since update of last_committed_sub_id can happen
+ out-of-order, the thread that frees a given gco can be for any later
+ event group, not necessarily an event group from the gco being freed.
*/
struct group_commit_orderer {
/* Wakeup condition, used with rpl_parallel_entry::LOCK_parallel_entry. */
@@ -49,6 +52,16 @@ struct group_commit_orderer {
uint64 wait_count;
group_commit_orderer *prev_gco;
group_commit_orderer *next_gco;
+ /*
+ The sub_id of last event group in this the previous GCO.
+ Only valid if prev_gco != NULL.
+ */
+ uint64 prior_sub_id;
+ /*
+ The sub_id of the last event group in this GCO. Only valid when next_gco
+ is non-NULL.
+ */
+ uint64 last_sub_id;
bool installed;
};
@@ -168,7 +181,8 @@ struct rpl_parallel_thread {
LOCK_rpl_thread mutex.
*/
void free_rgi(rpl_group_info *rgi);
- group_commit_orderer *get_gco(uint64 wait_count, group_commit_orderer *prev);
+ group_commit_orderer *get_gco(uint64 wait_count, group_commit_orderer *prev,
+ uint64 first_sub_id);
/*
Put a gco on the local free list, to be later released to the global free
list by batch_free().
diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc
index d21ebd494c1..a751dd16650 100644
--- a/sql/rpl_rli.cc
+++ b/sql/rpl_rli.cc
@@ -986,11 +986,11 @@ void Relay_log_info::inc_group_relay_log_pos(ulonglong log_pos,
if (rgi->is_parallel_exec)
{
/* In case of parallel replication, do not update the position backwards. */
- int cmp= strcmp(group_relay_log_name, event_relay_log_name);
+ int cmp= strcmp(group_relay_log_name, rgi->event_relay_log_name);
if (cmp < 0)
{
group_relay_log_pos= rgi->future_event_relay_log_pos;
- strmake_buf(group_relay_log_name, event_relay_log_name);
+ strmake_buf(group_relay_log_name, rgi->event_relay_log_name);
notify_group_relay_log_name_update();
} else if (cmp == 0 && group_relay_log_pos < rgi->future_event_relay_log_pos)
group_relay_log_pos= rgi->future_event_relay_log_pos;
@@ -1717,6 +1717,11 @@ void rpl_group_info::cleanup_context(THD *thd, bool error)
trans_rollback_stmt(thd); // if a "statement transaction"
/* trans_rollback() also resets OPTION_GTID_BEGIN */
trans_rollback(thd); // if a "real transaction"
+ /*
+ Now that we have rolled back the transaction, make sure we do not
+ errorneously update the GTID position.
+ */
+ gtid_pending= false;
}
m_table_map.clear_tables();
slave_close_thread_tables(thd);
@@ -1844,11 +1849,20 @@ void rpl_group_info::slave_close_thread_tables(THD *thd)
static void
-mark_start_commit_inner(rpl_parallel_entry *e, group_commit_orderer *gco)
+mark_start_commit_inner(rpl_parallel_entry *e, group_commit_orderer *gco,
+ rpl_group_info *rgi)
{
+ group_commit_orderer *tmp;
uint64 count= ++e->count_committing_event_groups;
- if (gco->next_gco && gco->next_gco->wait_count == count)
- mysql_cond_broadcast(&gco->next_gco->COND_group_commit_orderer);
+ /* Signal any following GCO whose wait_count has been reached now. */
+ tmp= gco;
+ while ((tmp= tmp->next_gco))
+ {
+ uint64 wait_count= tmp->wait_count;
+ if (wait_count > count)
+ break;
+ mysql_cond_broadcast(&tmp->COND_group_commit_orderer);
+ }
}
@@ -1857,7 +1871,7 @@ rpl_group_info::mark_start_commit_no_lock()
{
if (did_mark_start_commit)
return;
- mark_start_commit_inner(parallel_entry, gco);
+ mark_start_commit_inner(parallel_entry, gco, this);
did_mark_start_commit= true;
}
@@ -1872,7 +1886,7 @@ rpl_group_info::mark_start_commit()
e= this->parallel_entry;
mysql_mutex_lock(&e->LOCK_parallel_entry);
- mark_start_commit_inner(e, gco);
+ mark_start_commit_inner(e, gco, this);
mysql_mutex_unlock(&e->LOCK_parallel_entry);
did_mark_start_commit= true;
}
diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h
index 9885417aa3f..fb4e3261468 100644
--- a/sql/rpl_rli.h
+++ b/sql/rpl_rli.h
@@ -563,6 +563,10 @@ struct rpl_group_info
(When we execute in parallel the transactions that group committed
together on the master, we still need to wait for any prior transactions
to have reached the commit stage).
+
+ The pointed-to gco is only valid for as long as
+ gtid_sub_id < parallel_entry->last_committed_sub_id. After that, it can
+ be freed by another thread.
*/
group_commit_orderer *gco;
diff --git a/sql/slave.cc b/sql/slave.cc
index c569499fdcf..237c1c57ccc 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -5608,7 +5608,7 @@ static int queue_event(Master_info* mi,const char* buf, ulong event_len)
char str_buf[128];
String str(str_buf, sizeof(str_buf), system_charset_info);
mi->rli.until_gtid_pos.to_string(&str);
- sql_print_information("Slave IO thread stops because it reached its"
+ sql_print_information("Slave I/O thread stops because it reached its"
" UNTIL master_gtid_pos %s", str.c_ptr_safe());
mi->abort_slave= true;
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 41639ea352f..5ab03388f01 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -3293,6 +3293,14 @@ public:
mysql_mutex_unlock(&LOCK_thd_data);
}
}
+ inline void reset_kill_query()
+ {
+ if (killed < KILL_CONNECTION)
+ {
+ reset_killed();
+ mysys_var->abort= 0;
+ }
+ }
inline void send_kill_message() const
{
int err= killed_errno();
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index c842bed9ac2..77a45cbae59 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1762,7 +1762,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
table->file->adjust_next_insert_id_after_explicit_value(table->next_number_field->val_int());
}
- else
+ else if (prev_insert_id_for_cur_row)
{
table->file->restore_auto_increment(prev_insert_id_for_cur_row);
}
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index b389e591bc0..87810a65b0f 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1297,6 +1297,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->enable_slow_log= TRUE;
thd->query_plan_flags= QPLAN_INIT;
thd->lex->sql_command= SQLCOM_END; /* to avoid confusing VIEW detectors */
+ thd->reset_kill_query();
DEBUG_SYNC(thd,"dispatch_command_before_set_time");
@@ -5560,11 +5561,7 @@ finish:
if (! thd->get_stmt_da()->is_set())
thd->send_kill_message();
}
- if (thd->killed < KILL_CONNECTION)
- {
- thd->reset_killed();
- thd->mysys_var->abort= 0;
- }
+ thd->reset_kill_query();
}
if (thd->is_error() || (thd->variables.option_bits & OPTION_MASTER_SQL_ERROR))
trans_rollback_stmt(thd);
@@ -6456,6 +6453,115 @@ bool check_global_access(THD *thd, ulong want_access, bool no_errors)
#endif
}
+
+/**
+ Checks foreign key's parent table access.
+
+ @param thd [in] Thread handler
+ @param create_info [in] Create information (like MAX_ROWS, ENGINE or
+ temporary table flag)
+ @param alter_info [in] Initial list of columns and indexes for the
+ table to be created
+
+ @retval
+ false ok.
+ @retval
+ true error or access denied. Error is sent to client in this case.
+*/
+bool check_fk_parent_table_access(THD *thd,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info)
+{
+ Key *key;
+ List_iterator<Key> key_iterator(alter_info->key_list);
+
+ while ((key= key_iterator++))
+ {
+ if (key->type == Key::FOREIGN_KEY)
+ {
+ TABLE_LIST parent_table;
+ bool is_qualified_table_name;
+ Foreign_key *fk_key= (Foreign_key *)key;
+ LEX_STRING db_name;
+ LEX_STRING table_name= { fk_key->ref_table.str,
+ fk_key->ref_table.length };
+ const ulong privileges= (SELECT_ACL | INSERT_ACL | UPDATE_ACL |
+ DELETE_ACL | REFERENCES_ACL);
+
+ // Check if tablename is valid or not.
+ DBUG_ASSERT(table_name.str != NULL);
+ if (check_table_name(table_name.str, table_name.length, false))
+ {
+ my_error(ER_WRONG_TABLE_NAME, MYF(0), table_name.str);
+ return true;
+ }
+
+ if (fk_key->ref_db.str)
+ {
+ is_qualified_table_name= true;
+ db_name.str= (char *) thd->memdup(fk_key->ref_db.str,
+ fk_key->ref_db.length+1);
+ db_name.length= fk_key->ref_db.length;
+
+ // Check if database name is valid or not.
+ if (fk_key->ref_db.str && check_db_name(&db_name))
+ {
+ my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
+ return true;
+ }
+ }
+ else if (thd->lex->copy_db_to(&db_name.str, &db_name.length))
+ return true;
+ else
+ is_qualified_table_name= false;
+
+ // if lower_case_table_names is set then convert tablename to lower case.
+ if (lower_case_table_names)
+ {
+ table_name.str= (char *) thd->memdup(fk_key->ref_table.str,
+ fk_key->ref_table.length+1);
+ table_name.length= my_casedn_str(files_charset_info, table_name.str);
+ }
+
+ parent_table.init_one_table(db_name.str, db_name.length,
+ table_name.str, table_name.length,
+ table_name.str, TL_IGNORE);
+
+ /*
+ Check if user has any of the "privileges" at table level on
+ "parent_table".
+ Having privilege on any of the parent_table column is not
+ enough so checking whether user has any of the "privileges"
+ at table level only here.
+ */
+ if (check_some_access(thd, privileges, &parent_table) ||
+ parent_table.grant.want_privilege)
+ {
+ if (is_qualified_table_name)
+ {
+ const size_t qualified_table_name_len= NAME_LEN + 1 + NAME_LEN + 1;
+ char *qualified_table_name= (char *) thd->alloc(qualified_table_name_len);
+
+ my_snprintf(qualified_table_name, qualified_table_name_len, "%s.%s",
+ db_name.str, table_name.str);
+ table_name.str= qualified_table_name;
+ }
+
+ my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
+ "REFERENCES",
+ thd->security_ctx->priv_user,
+ thd->security_ctx->host_or_ip,
+ table_name.str);
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
/****************************************************************************
Check stack size; Send error if there isn't enough stack to continue
****************************************************************************/
@@ -7995,7 +8101,7 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user,
host.str[0] == '%' means that host name was not given. See sql_yacc.yy
*/
if (((user->host.str[0] == '%' && !user->host.str[1]) ||
- !strcmp(tmp->security_ctx->host, user->host.str)) &&
+ !strcmp(tmp->security_ctx->host_or_ip, user->host.str)) &&
!strcmp(tmp->security_ctx->user, user->user.str))
{
if (!(thd->security_ctx->master_access & SUPER_ACL) &&
@@ -8651,7 +8757,9 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
goto err;
}
- error= FALSE;
+
+ if (check_fk_parent_table_access(thd, &lex->create_info, &lex->alter_info))
+ goto err;
/*
For CREATE TABLE we should not open the table even if it exists.
@@ -8659,6 +8767,8 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
*/
lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB;
+ error= FALSE;
+
err:
DBUG_RETURN(error);
}
diff --git a/sql/sql_parse.h b/sql/sql_parse.h
index 773ede9edee..5e46c881510 100644
--- a/sql/sql_parse.h
+++ b/sql/sql_parse.h
@@ -46,6 +46,9 @@ bool delete_precheck(THD *thd, TABLE_LIST *tables);
bool insert_precheck(THD *thd, TABLE_LIST *tables);
bool create_table_precheck(THD *thd, TABLE_LIST *tables,
TABLE_LIST *create_table);
+bool check_fk_parent_table_access(THD *thd,
+ HA_CREATE_INFO *create_info,
+ Alter_info *alter_info);
bool parse_sql(THD *thd, Parser_state *parser_state,
Object_creation_ctx *creation_ctx, bool do_pfs_digest=false);
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 34ad2ff2615..b5a849f7fd4 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -3967,7 +3967,7 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
we copy string values to a plugin's memroot.
*/
if (mysqld_server_started &&
- ((o->flags & (PLUGIN_VAR_STR | PLUGIN_VAR_NOCMDOPT |
+ ((o->flags & (PLUGIN_VAR_TYPEMASK | PLUGIN_VAR_NOCMDOPT |
PLUGIN_VAR_MEMALLOC)) == PLUGIN_VAR_STR))
{
sysvar_str_t* str= (sysvar_str_t *)o;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 8f68c929a9e..ed56191c32b 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -291,18 +291,18 @@ static double table_cond_selectivity(JOIN *join, uint idx, JOIN_TAB *s,
void dbug_serve_apcs(THD *thd, int n_calls)
{
const char *save_proc_info= thd->proc_info;
- /* This is so that mysqltest knows we're ready to serve requests: */
- thd_proc_info(thd, "show_explain_trap");
/* Busy-wait for n_calls APC requests to arrive and be processed */
int n_apcs= thd->apc_target.n_calls_processed + n_calls;
while (thd->apc_target.n_calls_processed < n_apcs)
{
- my_sleep(300);
+ /* This is so that mysqltest knows we're ready to serve requests: */
+ thd_proc_info(thd, "show_explain_trap");
+ my_sleep(30000);
+ thd_proc_info(thd, save_proc_info);
if (thd->check_killed())
break;
}
- thd_proc_info(thd, save_proc_info);
}
@@ -3032,6 +3032,7 @@ void JOIN::exec_inner()
const ha_rows select_limit_arg=
select_options & OPTION_FOUND_ROWS
? HA_POS_ERROR : unit->select_limit_cnt;
+ curr_join->filesort_found_rows= filesort_limit_arg != HA_POS_ERROR;
DBUG_PRINT("info", ("has_group_by %d "
"curr_join->table_count %d "
@@ -3079,7 +3080,8 @@ void JOIN::exec_inner()
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
error= do_select(curr_join, curr_fields_list, NULL, procedure);
thd->limit_found_rows= curr_join->send_records;
- if (curr_join->order && curr_join->filesort_found_rows)
+ if (curr_join->order && curr_join->sortorder &&
+ curr_join->filesort_found_rows)
{
/* Use info provided by filesort. */
DBUG_ASSERT(curr_join->table_count > curr_join->const_tables);
@@ -9989,7 +9991,7 @@ bool generate_derived_keys_for_table(KEYUSE *keyuse, uint count, uint keys)
else
{
/* Mark keyuses for this key to be excluded */
- for (KEYUSE *curr=save_first_keyuse; curr < first_keyuse; curr++)
+ for (KEYUSE *curr=save_first_keyuse; curr < keyuse; curr++)
{
curr->key= MAX_KEY;
}
@@ -12213,8 +12215,8 @@ public:
{ TRASH(ptr, size); }
Item *and_level;
- Item_func *cmp_func;
- COND_CMP(Item *a,Item_func *b) :and_level(a),cmp_func(b) {}
+ Item_bool_func2 *cmp_func;
+ COND_CMP(Item *a,Item_bool_func2 *b) :and_level(a),cmp_func(b) {}
};
/**
@@ -13603,6 +13605,75 @@ static void update_const_equal_items(COND *cond, JOIN_TAB *tab, bool const_key)
}
+/**
+ Check if
+ WHERE expr=value AND expr=const
+ can be rewritten as:
+ WHERE const=value AND expr=const
+
+ @param target - the target operator whose "expr" argument will be
+ replaced to "const".
+ @param target_expr - the target's "expr" which will be replaced to "const".
+ @param target_value - the target's second argument, it will remain unchanged.
+ @param source - the equality expression ("=" or "<=>") that
+ can be used to rewrite the "target" part
+ (under certain conditions, see the code).
+ @param source_expr - the source's "expr". It should be exactly equal to
+ the target's "expr" to make condition rewrite possible.
+ @param source_const - the source's "const" argument, it will be inserted
+ into "target" instead of "expr".
+*/
+static bool
+can_change_cond_ref_to_const(Item_bool_func2 *target,
+ Item *target_expr, Item *target_value,
+ Item_bool_func2 *source,
+ Item *source_expr, Item *source_const)
+{
+ if (!target_expr->eq(source_expr,0) ||
+ target_value == source_const ||
+ target_expr->cmp_context != source_expr->cmp_context)
+ return false;
+ if (target_expr->cmp_context == STRING_RESULT)
+ {
+ /*
+ In this example:
+ SET NAMES utf8 COLLATE utf8_german2_ci;
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1 (a CHAR(10) CHARACTER SET utf8);
+ INSERT INTO t1 VALUES ('o-umlaut'),('oe');
+ SELECT * FROM t1 WHERE a='oe' COLLATE utf8_german2_ci AND a='oe';
+
+ the query should return only the row with 'oe'.
+ It should not return 'o-umlaut', because 'o-umlaut' does not match
+ the right part of the condition: a='oe'
+ ('o-umlaut' is not equal to 'oe' in utf8_general_ci,
+ which is the collation of the field "a").
+
+ If we change the right part from:
+ ... AND a='oe'
+ to
+ ... AND 'oe' COLLATE utf8_german2_ci='oe'
+ it will be evalulated to TRUE and removed from the condition,
+ so the overall query will be simplified to:
+
+ SELECT * FROM t1 WHERE a='oe' COLLATE utf8_german2_ci;
+
+ which will erroneously start to return both 'oe' and 'o-umlaut'.
+ So changing "expr" to "const" is not possible if the effective
+ collations of "target" and "source" are not exactly the same.
+
+ Note, the code before the fix for MDEV-7152 only checked that
+ collations of "source_const" and "target_value" are the same.
+ This was not enough, as the bug report demonstrated.
+ */
+ return
+ target->compare_collation() == source->compare_collation() &&
+ target_value->collation.collation == source_const->collation.collation;
+ }
+ return true; // Non-string comparison
+}
+
+
/*
change field = field to field = const for each found field = const in the
and_level
@@ -13611,6 +13682,7 @@ static void update_const_equal_items(COND *cond, JOIN_TAB *tab, bool const_key)
static void
change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
Item *and_father, Item *cond,
+ Item_bool_func2 *field_value_owner,
Item *field, Item *value)
{
if (cond->type() == Item::COND_ITEM)
@@ -13621,7 +13693,7 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
Item *item;
while ((item=li++))
change_cond_ref_to_const(thd, save_list,and_level ? cond : item, item,
- field, value);
+ field_value_owner, field, value);
return;
}
if (cond->eq_cmp_result() == Item::COND_OK)
@@ -13633,11 +13705,8 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
Item *right_item= args[1];
Item_func::Functype functype= func->functype();
- if (right_item->eq(field,0) && left_item != value &&
- right_item->cmp_context == field->cmp_context &&
- (left_item->result_type() != STRING_RESULT ||
- value->result_type() != STRING_RESULT ||
- left_item->collation.collation == value->collation.collation))
+ if (can_change_cond_ref_to_const(func, right_item, left_item,
+ field_value_owner, field, value))
{
Item *tmp=value->clone_item();
if (tmp)
@@ -13656,11 +13725,8 @@ change_cond_ref_to_const(THD *thd, I_List<COND_CMP> *save_list,
func->set_cmp_func();
}
}
- else if (left_item->eq(field,0) && right_item != value &&
- left_item->cmp_context == field->cmp_context &&
- (right_item->result_type() != STRING_RESULT ||
- value->result_type() != STRING_RESULT ||
- right_item->collation.collation == value->collation.collation))
+ else if (can_change_cond_ref_to_const(func, left_item, right_item,
+ field_value_owner, field, value))
{
Item *tmp= value->clone_item();
if (tmp)
@@ -13709,7 +13775,8 @@ propagate_cond_constants(THD *thd, I_List<COND_CMP> *save_list,
Item **args= cond_cmp->cmp_func->arguments();
if (!args[0]->const_item())
change_cond_ref_to_const(thd, &save,cond_cmp->and_level,
- cond_cmp->and_level, args[0], args[1]);
+ cond_cmp->and_level,
+ cond_cmp->cmp_func, args[0], args[1]);
}
}
}
@@ -13731,14 +13798,14 @@ propagate_cond_constants(THD *thd, I_List<COND_CMP> *save_list,
resolve_const_item(thd, &args[1], args[0]);
func->update_used_tables();
change_cond_ref_to_const(thd, save_list, and_father, and_father,
- args[0], args[1]);
+ func, args[0], args[1]);
}
else if (left_const)
{
resolve_const_item(thd, &args[0], args[1]);
func->update_used_tables();
change_cond_ref_to_const(thd, save_list, and_father, and_father,
- args[1], args[0]);
+ func, args[1], args[0]);
}
}
}
@@ -18835,7 +18902,8 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
records are read. Because of optimization in some cases it can
provide only select_limit_cnt+1 records.
*/
- if (join->order && join->filesort_found_rows &&
+ if (join->order && join->sortorder &&
+ join->filesort_found_rows &&
join->select_options & OPTION_FOUND_ROWS)
{
DBUG_PRINT("info", ("filesort NESTED_LOOP_QUERY_LIMIT"));
@@ -18857,8 +18925,9 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
/* Join over all rows in table; Return number of found rows */
TABLE *table=jt->table;
- join->select_options ^= OPTION_FOUND_ROWS;
- if (join->filesort_found_rows)
+ join->select_options ^= OPTION_FOUND_ROWS;
+ if (table->sort.record_pointers ||
+ (table->sort.io_cache && my_b_inited(table->sort.io_cache)))
{
/* Using filesort */
join->send_records= table->sort.found_records;
@@ -20689,11 +20758,7 @@ create_sort_index(THD *thd, JOIN *join, ORDER *order,
select, filesort_limit, 0,
&examined_rows, &found_rows);
table->sort.found_records= filesort_retval;
- if (found_rows != HA_POS_ERROR)
- {
- tab->records= found_rows; // For SQL_CALC_ROWS
- join->filesort_found_rows= true;
- }
+ tab->records= found_rows; // For SQL_CALC_ROWS
if (quick_created)
{
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 490d8c91a9e..7d53731b558 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -1341,7 +1341,6 @@ public:
emb_sjm_nest= NULL;
sjm_lookup_tables= 0;
- filesort_found_rows= false;
exec_saved_explain= false;
/*
The following is needed because JOIN::cleanup(true) may be called for
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index c6f18fa2a3c..841f67239b4 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -4312,7 +4312,7 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys,
Again we don't do this for SHOW COLUMNS/KEYS because
of backward compatibility.
*/
- if (!is_show_fields_or_keys && result && thd->is_error() &&
+ if (!is_show_fields_or_keys && result &&
(thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE ||
thd->get_stmt_da()->sql_errno() == ER_WRONG_OBJECT))
{
@@ -5319,12 +5319,11 @@ err:
column with the error text, and clear the error so that the operation
can continue.
*/
- const char *error= thd->is_error() ? thd->get_stmt_da()->message() : "";
+ const char *error= thd->get_stmt_da()->message();
table->field[20]->store(error, strlen(error), cs);
push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
- thd->get_stmt_da()->sql_errno(),
- thd->get_stmt_da()->message());
+ thd->get_stmt_da()->sql_errno(), error);
thd->clear_error();
}
@@ -8118,12 +8117,13 @@ bool get_schema_tables_result(JOIN *join,
THD *thd= join->thd;
LEX *lex= thd->lex;
bool result= 0;
- const char *old_proc_info;
+ PSI_stage_info org_stage;
DBUG_ENTER("get_schema_tables_result");
Warnings_only_error_handler err_handler;
thd->push_internal_handler(&err_handler);
- old_proc_info= thd_proc_info(thd, "Filling schema table");
+ thd->enter_stage(&stage_filling_schema_table, &org_stage, __func__, __FILE__,
+ __LINE__);
JOIN_TAB *tab;
for (tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS, WITH_CONST_TABLES);
@@ -8227,7 +8227,7 @@ bool get_schema_tables_result(JOIN *join,
}
else if (result)
my_error(ER_UNKNOWN_ERROR, MYF(0));
- thd_proc_info(thd, old_proc_info);
+ THD_STAGE_INFO(thd, org_stage);
DBUG_RETURN(result);
}
diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc
index d368145ca73..4ce1f3ec22a 100644
--- a/sql/sql_statistics.cc
+++ b/sql/sql_statistics.cc
@@ -2355,9 +2355,15 @@ int collect_statistics_for_index(THD *thd, TABLE *table, uint index)
int rc= 0;
KEY *key_info= &table->key_info[index];
ha_rows rows= 0;
- Index_prefix_calc index_prefix_calc(table, key_info);
+
DBUG_ENTER("collect_statistics_for_index");
+ /* No statistics for FULLTEXT indexes. */
+ if (key_info->flags & HA_FULLTEXT)
+ DBUG_RETURN(rc);
+
+ Index_prefix_calc index_prefix_calc(table, key_info);
+
DEBUG_SYNC(table->in_use, "statistics_collection_start1");
DEBUG_SYNC(table->in_use, "statistics_collection_start2");
@@ -2391,7 +2397,7 @@ int collect_statistics_for_index(THD *thd, TABLE *table, uint index)
if (!rc)
index_prefix_calc.get_avg_frequency();
- DBUG_RETURN(rc);
+ DBUG_RETURN(rc);
}
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 34515d655e5..cf3f588b323 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -8450,9 +8450,21 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
/*
- If this is an ALTER TABLE and no explicit row type specified reuse
- the table's row type.
- Note : this is the same as if the row type was specified explicitly.
+ If foreign key is added then check permission to access parent table.
+
+ In function "check_fk_parent_table_access", create_info->db_type is used
+ to identify whether engine supports FK constraint or not. Since
+ create_info->db_type is set here, check to parent table access is delayed
+ till this point for the alter operation.
+ */
+ if ((alter_info->flags & Alter_info::ADD_FOREIGN_KEY) &&
+ check_fk_parent_table_access(thd, create_info, alter_info))
+ DBUG_RETURN(true);
+
+ /*
+ If this is an ALTER TABLE and no explicit row type specified reuse
+ the table's row type.
+ Note: this is the same as if the row type was specified explicitly.
*/
if (create_info->row_type == ROW_TYPE_NOT_USED)
{
@@ -9595,12 +9607,12 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
/*
- Recreates tables by calling mysql_alter_table().
+ Recreates one table by calling mysql_alter_table().
SYNOPSIS
mysql_recreate_table()
thd Thread handler
- tables Tables to recreate
+ table_list Table to recreate
table_copy Recreate the table by using
ALTER TABLE COPY algorithm
@@ -9612,13 +9624,15 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy)
{
HA_CREATE_INFO create_info;
Alter_info alter_info;
- DBUG_ENTER("mysql_recreate_table");
- DBUG_ASSERT(!table_list->next_global);
+ TABLE_LIST *next_table= table_list->next_global;
+ DBUG_ENTER("mysql_recreate_table");
/* Set lock type which is appropriate for ALTER TABLE. */
table_list->lock_type= TL_READ_NO_INSERT;
/* Same applies to MDL request. */
table_list->mdl_request.set_type(MDL_SHARED_NO_WRITE);
+ /* hide following tables from open_tables() */
+ table_list->next_global= NULL;
bzero((char*) &create_info, sizeof(create_info));
create_info.row_type=ROW_TYPE_NOT_USED;
@@ -9630,9 +9644,11 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list, bool table_copy)
if (table_copy)
alter_info.requested_algorithm= Alter_info::ALTER_TABLE_ALGORITHM_COPY;
- DBUG_RETURN(mysql_alter_table(thd, NullS, NullS, &create_info,
+ bool res= mysql_alter_table(thd, NullS, NullS, &create_info,
table_list, &alter_info, 0,
- (ORDER *) 0, 0));
+ (ORDER *) 0, 0);
+ table_list->next_global= next_table;
+ DBUG_RETURN(res);
}
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index c9aafbf622c..b86516cbd9c 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -603,7 +603,10 @@ int mysql_update(THD *thd,
if (query_plan.index == MAX_KEY || (select && select->quick))
{
if (init_read_record(&info, thd, table, select, 0, 1, FALSE))
+ {
+ close_cached_file(&tempfile);
goto err;
+ }
}
else
init_read_record_idx(&info, thd, table, 1, query_plan.index, reverse);
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 6a81301a6d9..20a16e3eae1 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -685,7 +685,7 @@ err:
/* number of required parameters for making view */
-static const int required_view_parameters= 14;
+static const int required_view_parameters= 15;
/*
table of VIEW .frm field descriptors
@@ -736,6 +736,9 @@ static File_option view_parameters[]=
{{(char*) STRING_WITH_LEN("view_body_utf8")},
my_offsetof(TABLE_LIST, view_body_utf8),
FILE_OPTIONS_ESTRING},
+ {{ C_STRING_WITH_LEN("mariadb-version")},
+ my_offsetof(TABLE_LIST, mariadb_version),
+ FILE_OPTIONS_ULONGLONG},
{{NullS, 0}, 0,
FILE_OPTIONS_STRING}
};
@@ -836,6 +839,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
version 2 - empty definer_host means a role
*/
view->file_version= 2;
+ view->mariadb_version= MYSQL_VERSION_ID;
view->calc_md5(md5);
if (!(view->md5.str= (char*) thd->memdup(md5, 32)))
{
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index dbbf16e07c1..561e3afcd54 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -3457,13 +3457,13 @@ static Sys_var_bit Sys_log_off(
static bool fix_sql_log_bin_after_update(sys_var *self, THD *thd,
enum_var_type type)
{
- if (type == OPT_SESSION)
- {
- if (thd->variables.sql_log_bin)
- thd->variables.option_bits |= OPTION_BIN_LOG;
- else
- thd->variables.option_bits &= ~OPTION_BIN_LOG;
- }
+ DBUG_ASSERT(type == OPT_SESSION);
+
+ if (thd->variables.sql_log_bin)
+ thd->variables.option_bits |= OPTION_BIN_LOG;
+ else
+ thd->variables.option_bits &= ~OPTION_BIN_LOG;
+
return FALSE;
}
@@ -3485,7 +3485,10 @@ static bool check_sql_log_bin(sys_var *self, THD *thd, set_var *var)
return TRUE;
if (var->type == OPT_GLOBAL)
- return FALSE;
+ {
+ my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), self->name.str, "SESSION");
+ return TRUE;
+ }
if (error_if_in_trans_or_substatement(thd,
ER_STORED_FUNCTION_PREVENTS_SWITCH_SQL_LOG_BIN,
@@ -3496,9 +3499,9 @@ static bool check_sql_log_bin(sys_var *self, THD *thd, set_var *var)
}
static Sys_var_mybool Sys_log_binlog(
- "sql_log_bin", "sql_log_bin",
- SESSION_VAR(sql_log_bin), NO_CMD_LINE,
- DEFAULT(TRUE), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_sql_log_bin),
+ "sql_log_bin", "Controls whether logging to the binary log is done",
+ SESSION_VAR(sql_log_bin), NO_CMD_LINE, DEFAULT(TRUE),
+ NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(check_sql_log_bin),
ON_UPDATE(fix_sql_log_bin_after_update));
static Sys_var_bit Sys_sql_warnings(
@@ -4769,7 +4772,7 @@ static Sys_var_ulong Sys_sp_cache_size(
"The soft upper limit for number of cached stored routines for "
"one connection.",
GLOBAL_VAR(stored_program_cache_size), CMD_LINE(REQUIRED_ARG),
- VALID_RANGE(256, 512 * 1024), DEFAULT(256), BLOCK_SIZE(1));
+ VALID_RANGE(0, 512 * 1024), DEFAULT(256), BLOCK_SIZE(1));
export const char *plugin_maturity_names[]=
{ "unknown", "experimental", "alpha", "beta", "gamma", "stable", 0 };
diff --git a/sql/table.cc b/sql/table.cc
index 69b0faf0a9e..5b8809bbced 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -831,6 +831,24 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
}
+/** ensures that the enum value (read from frm) is within limits
+
+ if not - issues a warning and resets the value to 0
+ (that is, 0 is assumed to be a default value)
+*/
+
+static uint enum_value_with_check(THD *thd, TABLE_SHARE *share,
+ const char *name, uint value, uint limit)
+{
+ if (value < limit)
+ return value;
+
+ sql_print_warning("%s.frm: invalid value %d for the field %s",
+ share->normalized_path.str, value, name);
+ return 0;
+}
+
+
/**
Check if a collation has changed number
@@ -840,8 +858,7 @@ static bool create_key_infos(const uchar *strpos, const uchar *frm_image_end,
@retval new collation number (same as current collation number of no change)
*/
-static uint
-upgrade_collation(ulong mysql_version, uint cs_number)
+static uint upgrade_collation(ulong mysql_version, uint cs_number)
{
if (mysql_version >= 50300 && mysql_version <= 50399)
{
@@ -865,8 +882,6 @@ upgrade_collation(ulong mysql_version, uint cs_number)
}
-
-
/**
Read data from a binary .frm file image into a TABLE_SHARE
@@ -1050,9 +1065,12 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write,
share->incompatible_version|= HA_CREATE_USED_CHARSET;
share->avg_row_length= uint4korr(frm_image+34);
- share->transactional= (ha_choice) (frm_image[39] & 3);
- share->page_checksum= (ha_choice) ((frm_image[39] >> 2) & 3);
- share->row_type= (enum row_type) frm_image[40];
+ share->transactional= (ha_choice)
+ enum_value_with_check(thd, share, "transactional", frm_image[39] & 3, HA_CHOICE_MAX);
+ share->page_checksum= (ha_choice)
+ enum_value_with_check(thd, share, "page_checksum", (frm_image[39] >> 2) & 3, HA_CHOICE_MAX);
+ share->row_type= (enum row_type)
+ enum_value_with_check(thd, share, "row_format", frm_image[40], ROW_TYPE_MAX);
if (cs_new && !(share->table_charset= get_charset(cs_new, MYF(MY_WME))))
goto err;
diff --git a/sql/table.h b/sql/table.h
index 58b78af6836..b978484158b 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -1898,6 +1898,7 @@ struct TABLE_LIST
LEX_STRING timestamp; /* GMT time stamp of last operation */
st_lex_user definer; /* definer of view */
ulonglong file_version; /* version of file's field set */
+ ulonglong mariadb_version; /* version of server on creation */
ulonglong updatable_view; /* VIEW can be updated */
/**
@brief The declared algorithm, if this is a view.