diff options
author | Nirbhay Choubey <nirbhay@mariadb.com> | 2015-01-26 22:54:27 -0500 |
---|---|---|
committer | Nirbhay Choubey <nirbhay@mariadb.com> | 2015-01-26 22:54:27 -0500 |
commit | 7cda4bee0ef7c8a3ec85e94bc1443ceaba3a64e8 (patch) | |
tree | f8c1f556f36f026038015e7215ae88b0cb7b1ce5 /sql | |
parent | ea229eb6bb29ee7b11a7d28a7d4c80a593cb1d3b (diff) | |
parent | fb71449b10100e9a0f887b1585000fbfab294f3c (diff) | |
download | mariadb-git-7cda4bee0ef7c8a3ec85e94bc1443ceaba3a64e8.tar.gz |
maria-10.0.16 merge
bzr merge -r4588 maria/10.0
Diffstat (limited to 'sql')
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, <ime->year, <ime->month, <ime->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. |