diff options
Diffstat (limited to 'sql')
49 files changed, 953 insertions, 727 deletions
diff --git a/sql/contributors.h b/sql/contributors.h index 0359ec54022..3a771e2b493 100644 --- a/sql/contributors.h +++ b/sql/contributors.h @@ -37,16 +37,18 @@ struct show_table_contributors_st { struct show_table_contributors_st show_table_contributors[]= { /* MariaDB foundation sponsors, in contribution, size , time order */ - {"Booking.com", "http://www.booking.com", "Founding member, Platinum Sponsor of the MariaDB Foundation"}, + {"Booking.com", "https://www.booking.com", "Founding member, Platinum Sponsor of the MariaDB Foundation"}, + {"Alibaba Cloud", "https://intl.aliyun.com", "Platinum Sponsor of the MariaDB Foundation"}, {"MariaDB Corporation", "https://mariadb.com", "Founding member, Gold Sponsor of the MariaDB Foundation"}, - {"Visma", "http://visma.com", "Gold Sponsor of the MariaDB Foundation"}, - {"DBS", "http://dbs.com", "Gold Sponsor of the MariaDB Foundation"}, + {"Visma", "https://visma.com", "Gold Sponsor of the MariaDB Foundation"}, + {"DBS", "https://dbs.com", "Gold Sponsor of the MariaDB Foundation"}, {"Nexedi", "https://www.nexedi.com", "Silver Sponsor of the MariaDB Foundation"}, {"Acronis", "http://www.acronis.com", "Silver Sponsor of the MariaDB Foundation"}, {"Auttomattic", "https://automattic.com", "Bronze Sponsor of the MariaDB Foundation"}, - {"Verkkokauppa.com", "https://virtuozzo.com", "Bronze Sponsor of the MariaDB Foundation"}, - {"Virtuozzo", "https://virtuozzo.com/", "Bronze Sponsor of the MariaDB Foundation"}, - {"Tencent Game DBA", "http://tencentdba.com/about/", "Bronze Sponsor of the MariaDB Foundation"}, + {"Verkkokauppa.com", "https://www.verkkokauppa.com", "Bronze Sponsor of the MariaDB Foundation"}, + {"Virtuozzo", "https://virtuozzo.com", "Bronze Sponsor of the MariaDB Foundation"}, + {"Tencent Game DBA", "http://tencentdba.com/about", "Bronze Sponsor of the MariaDB Foundation"}, + {"Tencent TDSQL", "http://tdsql.org", "Bronze Sponsor of the MariaDB Foundation"}, /* Sponsors of important features */ {"Google", "USA", "Sponsoring encryption, parallel replication and GTID"}, diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index 5c4926c830c..2423fac4ca6 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -133,12 +133,6 @@ post_init_event_thread(THD *thd) thd->cleanup(); return TRUE; } - - thread_safe_increment32(&thread_count, &thread_count_lock); - mysql_mutex_lock(&LOCK_thread_count); - threads.append(thd); - mysql_mutex_unlock(&LOCK_thread_count); - inc_thread_running(); return FALSE; } @@ -157,7 +151,13 @@ deinit_event_thread(THD *thd) thd->proc_info= "Clearing"; DBUG_PRINT("exit", ("Event thread finishing")); - delete_running_thd(thd); + mysql_mutex_lock(&LOCK_thread_count); + thd->unlink(); + mysql_mutex_unlock(&LOCK_thread_count); + + delete thd; + thread_safe_decrement32(&thread_count, &thread_count_lock); + signal_thd_deleted(); } @@ -191,8 +191,10 @@ pre_init_event_thread(THD* thd) thd->net.read_timeout= slave_net_timeout; thd->variables.option_bits|= OPTION_AUTO_IS_NULL; thd->client_capabilities|= CLIENT_MULTI_RESULTS; + thread_safe_increment32(&thread_count, &thread_count_lock); mysql_mutex_lock(&LOCK_thread_count); thd->thread_id= thd->variables.pseudo_thread_id= thread_id++; + threads.append(thd); mysql_mutex_unlock(&LOCK_thread_count); /* @@ -240,13 +242,8 @@ event_scheduler_thread(void *arg) my_free(arg); if (!res) scheduler->run(thd); - else - { - thd->proc_info= "Clearing"; - net_end(&thd->net); - delete thd; - } + deinit_event_thread(thd); DBUG_LEAVE; // Against gcc warnings my_thread_end(); return 0; @@ -310,6 +307,7 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event) DBUG_ENTER("Event_worker_thread::run"); DBUG_PRINT("info", ("Time is %ld, THD: 0x%lx", (long) my_time(0), (long) thd)); + inc_thread_running(); if (res) goto end; @@ -338,6 +336,7 @@ end: event->name.str)); delete event; + dec_thread_running(); deinit_event_thread(thd); DBUG_VOID_RETURN; @@ -442,13 +441,9 @@ Event_scheduler::start(int *err_no) " Can not create thread for event scheduler (errno=%d)", *err_no); - new_thd->proc_info= "Clearing"; - DBUG_ASSERT(new_thd->net.buff != 0); - net_end(&new_thd->net); - state= INITIALIZED; scheduler_thd= NULL; - delete new_thd; + deinit_event_thread(new_thd); delete scheduler_param_value; ret= true; @@ -515,7 +510,6 @@ Event_scheduler::run(THD *thd) } LOCK_DATA(); - deinit_event_thread(thd); scheduler_thd= NULL; state= INITIALIZED; DBUG_PRINT("info", ("Broadcasting COND_state back to the stoppers")); @@ -575,10 +569,7 @@ Event_scheduler::execute_top(Event_queue_element_for_exec *event_name) sql_print_error("Event_scheduler::execute_top: Can not create event worker" " thread (errno=%d). Stopping event scheduler", res); - new_thd->proc_info= "Clearing"; - DBUG_ASSERT(new_thd->net.buff != 0); - net_end(&new_thd->net); - + deinit_event_thread(new_thd); goto error; } @@ -590,9 +581,6 @@ Event_scheduler::execute_top(Event_queue_element_for_exec *event_name) error: DBUG_PRINT("error", ("Event_scheduler::execute_top() res: %d", res)); - if (new_thd) - delete new_thd; - delete event_name; DBUG_RETURN(TRUE); } diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index af81962904f..e854045661b 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -7279,6 +7279,7 @@ int ha_partition::reset(void) result= tmp; } bitmap_clear_all(&m_partitions_to_reset); + m_extra_prepare_for_update= FALSE; DBUG_RETURN(result); } diff --git a/sql/handler.cc b/sql/handler.cc index 79649316e73..47c5889431d 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -3441,6 +3441,7 @@ void handler::print_error(int error, myf errflag) textno=ER_FILE_USED; break; case ENOENT: + case ENOTDIR: textno=ER_FILE_NOT_FOUND; break; case ENOSPC: @@ -3919,8 +3920,7 @@ int handler::delete_table(const char *name) for (const char **ext=bas_ext(); *ext ; ext++) { - fn_format(buff, name, "", *ext, MY_UNPACK_FILENAME|MY_APPEND_EXT); - if (mysql_file_delete_with_symlink(key_file_misc, buff, MYF(0))) + if (my_handler_delete_with_symlink(key_file_misc, name, *ext, 0)) { if (my_errno != ENOENT) { @@ -4279,7 +4279,7 @@ enum_alter_inplace_result handler::check_if_supported_inplace_alter(TABLE *altered_table, Alter_inplace_info *ha_alter_info) { - DBUG_ENTER("check_if_supported_alter"); + DBUG_ENTER("handler::check_if_supported_inplace_alter"); HA_CREATE_INFO *create_info= ha_alter_info->create_info; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index e0dbf2c5a73..192e06566ff 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -798,7 +798,7 @@ int Arg_comparator::set_cmp_func(Item_result_field *owner_arg, Item **a1, Item **a2, Item_result type) { - thd= current_thd; + THD *thd= current_thd; owner= owner_arg; set_null= set_null && owner_arg; a= a1; @@ -868,7 +868,6 @@ Item** Arg_comparator::cache_converted_constant(THD *thd_arg, Item **value, void Arg_comparator::set_datetime_cmp_func(Item_result_field *owner_arg, Item **a1, Item **b1) { - thd= current_thd; owner= owner_arg; a= a1; b= b1; @@ -943,12 +942,10 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, if (cache_arg && item->const_item() && !(item->type() == Item::CACHE_ITEM && item->cmp_type() == TIME_RESULT)) { - Query_arena backup; - Query_arena *save_arena= thd->switch_to_arena_for_cached_items(&backup); - Item_cache_temporal *cache= new Item_cache_temporal(f_type); - if (save_arena) - thd->set_query_arena(save_arena); + if (!thd) + thd= current_thd; + Item_cache_temporal *cache= new Item_cache_temporal(f_type); cache->store_packed(value, item); *cache_arg= cache; *item_arg= cache_arg; @@ -983,12 +980,12 @@ int Arg_comparator::compare_datetime() owner->null_value= 1; /* Get DATE/DATETIME/TIME value of the 'a' item. */ - a_value= get_datetime_value(thd, &a, &a_cache, *b, &a_is_null); + a_value= get_datetime_value(0, &a, &a_cache, *b, &a_is_null); if (a_is_null) return -1; /* Get DATE/DATETIME/TIME value of the 'b' item. */ - b_value= get_datetime_value(thd, &b, &b_cache, *a, &b_is_null); + b_value= get_datetime_value(0, &b, &b_cache, *a, &b_is_null); if (b_is_null) return -1; @@ -1006,10 +1003,10 @@ int Arg_comparator::compare_e_datetime() longlong a_value, b_value; /* Get DATE/DATETIME/TIME value of the 'a' item. */ - a_value= get_datetime_value(thd, &a, &a_cache, *b, &a_is_null); + a_value= get_datetime_value(0, &a, &a_cache, *b, &a_is_null); /* Get DATE/DATETIME/TIME value of the 'b' item. */ - b_value= get_datetime_value(thd, &b, &b_cache, *a, &b_is_null); + b_value= get_datetime_value(0, &b, &b_cache, *a, &b_is_null); return a_is_null || b_is_null ? a_is_null == b_is_null : a_value == b_value; } @@ -3663,7 +3660,7 @@ void in_datetime::set(uint pos,Item *item) bool is_null; struct packed_longlong *buff= &((packed_longlong*) base)[pos]; - buff->val= get_datetime_value(thd, &tmp_item, 0, warn_item, &is_null); + buff->val= get_datetime_value(0, &tmp_item, 0, warn_item, &is_null); buff->unsigned_flag= 1L; } @@ -3671,7 +3668,7 @@ uchar *in_datetime::get_value(Item *item) { bool is_null; Item **tmp_item= lval_cache ? &lval_cache : &item; - tmp.val= get_datetime_value(thd, &tmp_item, &lval_cache, warn_item, &is_null); + tmp.val= get_datetime_value(0, &tmp_item, &lval_cache, warn_item, &is_null); if (item->null_value) return 0; tmp.unsigned_flag= 1L; @@ -3915,7 +3912,7 @@ void cmp_item_datetime::store_value(Item *item) { bool is_null; Item **tmp_item= lval_cache ? &lval_cache : &item; - value= get_datetime_value(thd, &tmp_item, &lval_cache, warn_item, &is_null); + value= get_datetime_value(0, &tmp_item, &lval_cache, warn_item, &is_null); } @@ -3924,7 +3921,7 @@ int cmp_item_datetime::cmp(Item *arg) bool is_null; Item **tmp_item= &arg; return value != - get_datetime_value(thd, &tmp_item, 0, warn_item, &is_null); + get_datetime_value(0, &tmp_item, 0, warn_item, &is_null); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 9e83b732dc7..b84cb26fb9c 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -45,7 +45,6 @@ class Arg_comparator: public Sql_alloc Arg_comparator *comparators; // used only for compare_row() double precision; /* Fields used in DATE/DATETIME comparison. */ - 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); @@ -61,10 +60,10 @@ public: /* Allow owner function to use string buffers. */ String value1, value2; - Arg_comparator(): set_null(TRUE), comparators(0), thd(0), + Arg_comparator(): set_null(TRUE), comparators(0), a_cache(0), b_cache(0) {}; Arg_comparator(Item **a1, Item **a2): a(a1), b(a2), set_null(TRUE), - comparators(0), thd(0), a_cache(0), b_cache(0) {}; + comparators(0), a_cache(0), b_cache(0) {}; int set_cmp_func(Item_result_field *owner_arg, Item **a1, Item **a2, @@ -963,15 +962,13 @@ public: class in_datetime :public in_longlong { public: - THD *thd; /* An item used to issue warnings. */ Item *warn_item; /* Cache for the left item. */ Item *lval_cache; in_datetime(Item *warn_item_arg, uint elements) - :in_longlong(elements), thd(current_thd), warn_item(warn_item_arg), - lval_cache(0) {}; + :in_longlong(elements), warn_item(warn_item_arg), lval_cache(0) {}; void set(uint pos,Item *item); uchar *get_value(Item *item); Item* create_item() @@ -1131,14 +1128,13 @@ class cmp_item_datetime :public cmp_item { longlong value; public: - THD *thd; /* Item used for issuing warnings. */ Item *warn_item; /* Cache for the left item. */ Item *lval_cache; cmp_item_datetime(Item *warn_item_arg) - :thd(current_thd), warn_item(warn_item_arg), lval_cache(0) {} + : warn_item(warn_item_arg), lval_cache(0) {} void store_value(Item *item); int cmp(Item *arg); int compare(cmp_item *ci); diff --git a/sql/item_func.cc b/sql/item_func.cc index 07acaacc862..ef164d060d2 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2865,7 +2865,6 @@ void Item_func_min_max::fix_length_and_dec() decimals=0; max_length=0; maybe_null=0; - thd= current_thd; cmp_type=args[0]->result_type(); for (uint i=0 ; i < arg_count ; i++) @@ -2938,13 +2937,11 @@ bool Item_func_min_max::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) { Item **arg= args + i; bool is_null; - longlong res= get_datetime_value(thd, &arg, 0, compare_as_dates, &is_null); + longlong res= get_datetime_value(0, &arg, 0, compare_as_dates, &is_null); /* Check if we need to stop (because of error or KILL) and stop the loop */ - if (thd->is_error() || args[i]->null_value) - { + if (args[i]->null_value) return (null_value= 1); - } if (i == 0 || (res < min_max ? cmp_sign : -cmp_sign) > 0) min_max= res; @@ -3973,12 +3970,7 @@ longlong Item_master_pos_wait::val_int() else connection_name= thd->variables.default_master_connection; - mysql_mutex_lock(&LOCK_active_mi); - if (master_info_index) // master_info_index is set to NULL on shutdown. - mi= master_info_index->get_master_info(&connection_name, - Sql_condition::WARN_LEVEL_WARN); - mysql_mutex_unlock(&LOCK_active_mi); - if (!mi) + if (!(mi= get_master_info(&connection_name, Sql_condition::WARN_LEVEL_WARN))) goto err; if ((event_count = mi->rli.wait_for_pos(thd, log_name, pos, timeout)) == -2) @@ -3986,6 +3978,7 @@ longlong Item_master_pos_wait::val_int() null_value = 1; event_count=0; } + mi->release(); #endif return event_count; diff --git a/sql/item_func.h b/sql/item_func.h index 3d0cdca4472..0141619dced 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1077,7 +1077,6 @@ class Item_func_min_max :public Item_func int cmp_sign; /* An item used for issuing warnings while string to DATETIME conversion. */ Item *compare_as_dates; - THD *thd; protected: enum_field_types cached_field_type; public: diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index f3a98785a0b..8cff6a1e6c8 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -894,7 +894,7 @@ void Item_subselect::update_used_tables() if (!forced_const) { recalc_used_tables(parent_select, FALSE); - if (!engine->uncacheable()) + if (!(engine->uncacheable() & ~UNCACHEABLE_EXPLAIN)) { // did all used tables become static? if (!(used_tables_cache & ~engine->upper_select_const_tables())) diff --git a/sql/log.cc b/sql/log.cc index 02e6ef98a65..3f3e0201c0c 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2835,16 +2835,16 @@ int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name) void MYSQL_QUERY_LOG::reopen_file() { char *save_name; - DBUG_ENTER("MYSQL_LOG::reopen_file"); + + mysql_mutex_lock(&LOCK_log); if (!is_open()) { DBUG_PRINT("info",("log is closed")); + mysql_mutex_unlock(&LOCK_log); DBUG_VOID_RETURN; } - mysql_mutex_lock(&LOCK_log); - save_name= name; name= 0; // Don't free name close(LOG_CLOSE_TO_BE_OPENED); @@ -3003,13 +3003,6 @@ bool MYSQL_QUERY_LOG::write(THD *thd, time_t current_time, DBUG_ENTER("MYSQL_QUERY_LOG::write"); mysql_mutex_lock(&LOCK_log); - - if (!is_open()) - { - mysql_mutex_unlock(&LOCK_log); - DBUG_RETURN(0); - } - if (is_open()) { // Safety agains reopen int tmp_errno= 0; @@ -3233,7 +3226,9 @@ void MYSQL_BIN_LOG::cleanup() } inited= 0; + mysql_mutex_lock(&LOCK_log); close(LOG_CLOSE_INDEX|LOG_CLOSE_STOP_EVENT); + mysql_mutex_unlock(&LOCK_log); delete description_event_for_queue; delete description_event_for_exec; @@ -3390,10 +3385,11 @@ bool MYSQL_BIN_LOG::open(const char *log_name, { File file= -1; xid_count_per_binlog *new_xid_list_entry= NULL, *b; - DBUG_ENTER("MYSQL_BIN_LOG::open"); DBUG_PRINT("enter",("log_type: %d",(int) log_type_arg)); + mysql_mutex_assert_owner(&LOCK_log); + if (!is_relay_log) { if (!binlog_state_recover_done) @@ -4286,7 +4282,7 @@ void MYSQL_BIN_LOG::wait_for_last_checkpoint_event() int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included) { - int error; + int error, errcode; char *to_purge_if_included= NULL; inuse_relaylog *ir; ulonglong log_space_reclaimed= 0; @@ -4357,7 +4353,8 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included) } /* Store where we are in the new file for the execution thread */ - flush_relay_log_info(rli); + if (flush_relay_log_info(rli)) + error= LOG_INFO_IO; DBUG_EXECUTE_IF("crash_before_purge_logs", DBUG_SUICIDE();); @@ -4373,11 +4370,13 @@ int MYSQL_BIN_LOG::purge_first_log(Relay_log_info* rli, bool included) * Need to update the log pos because purge logs has been called * after fetching initially the log pos at the begining of the method. */ - if((error=find_log_pos(&rli->linfo, rli->event_relay_log_name, 0))) + if ((errcode= find_log_pos(&rli->linfo, rli->event_relay_log_name, 0))) { char buff[22]; + if (!error) + error= errcode; sql_print_error("next log error: %d offset: %s log: %s included: %d", - error, + errcode, llstr(rli->linfo.index_file_offset,buff), rli->group_relay_log_name, included); @@ -4996,21 +4995,21 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) bool delay_close= false; File old_file; LINT_INIT(old_file); - DBUG_ENTER("MYSQL_BIN_LOG::new_file_impl"); + + if (need_lock) + mysql_mutex_lock(&LOCK_log); + mysql_mutex_assert_owner(&LOCK_log); + if (!is_open()) { DBUG_PRINT("info",("log is closed")); + mysql_mutex_unlock(&LOCK_log); DBUG_RETURN(error); } - if (need_lock) - mysql_mutex_lock(&LOCK_log); mysql_mutex_lock(&LOCK_index); - mysql_mutex_assert_owner(&LOCK_log); - mysql_mutex_assert_owner(&LOCK_index); - /* Reuse old name if not binlog and not update log */ new_name_ptr= name; @@ -5143,9 +5142,9 @@ end: new_name_ptr, errno); } + mysql_mutex_unlock(&LOCK_index); if (need_lock) mysql_mutex_unlock(&LOCK_log); - mysql_mutex_unlock(&LOCK_index); DBUG_RETURN(error); } @@ -7974,9 +7973,11 @@ int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd, void MYSQL_BIN_LOG::close(uint exiting) { // One can't set log_type here! bool failed_to_save_state= false; - DBUG_ENTER("MYSQL_BIN_LOG::close"); DBUG_PRINT("enter",("exiting: %d", (int) exiting)); + + mysql_mutex_assert_owner(&LOCK_log); + if (log_state == LOG_OPENED) { #ifdef HAVE_REPLICATION diff --git a/sql/log_event.cc b/sql/log_event.cc index d2f3b2c60f6..1ae65a02f15 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -6273,9 +6273,11 @@ bool Rotate_log_event::write(IO_CACHE* file) @retval 0 ok + 1 error */ int Rotate_log_event::do_update_pos(rpl_group_info *rgi) { + int error= 0; Relay_log_info *rli= rgi->rli; DBUG_ENTER("Rotate_log_event::do_update_pos"); #ifndef DBUG_OFF @@ -6327,7 +6329,7 @@ int Rotate_log_event::do_update_pos(rpl_group_info *rgi) (ulong) rli->group_master_log_pos)); mysql_mutex_unlock(&rli->data_lock); rpl_global_gtid_slave_state->record_and_update_gtid(thd, rgi); - flush_relay_log_info(rli); + error= flush_relay_log_info(rli); /* Reset thd->variables.option_bits and sql_mode etc, because this could @@ -6345,8 +6347,7 @@ int Rotate_log_event::do_update_pos(rpl_group_info *rgi) else rgi->inc_event_relay_log_pos(); - - DBUG_RETURN(0); + DBUG_RETURN(error); } @@ -6944,6 +6945,7 @@ Gtid_list_log_event::do_apply_event(rpl_group_info *rgi) rli->abort_slave= true; rli->stop_for_until= true; } + free_root(thd->mem_root, MYF(MY_KEEP_PREALLOC)); return ret; } @@ -8220,6 +8222,7 @@ void Stop_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) int Stop_log_event::do_update_pos(rpl_group_info *rgi) { + int error= 0; Relay_log_info *rli= rgi->rli; DBUG_ENTER("Stop_log_event::do_update_pos"); /* @@ -8235,9 +8238,10 @@ int Stop_log_event::do_update_pos(rpl_group_info *rgi) { rpl_global_gtid_slave_state->record_and_update_gtid(thd, rgi); rli->inc_group_relay_log_pos(0, rgi); - flush_relay_log_info(rli); + if (flush_relay_log_info(rli)) + error= 1; } - DBUG_RETURN(0); + DBUG_RETURN(error); } #endif /* !MYSQL_CLIENT */ @@ -10273,8 +10277,8 @@ int Rows_log_event::do_update_pos(rpl_group_info *rgi) { Relay_log_info *rli= rgi->rli; - DBUG_ENTER("Rows_log_event::do_update_pos"); int error= 0; + DBUG_ENTER("Rows_log_event::do_update_pos"); DBUG_PRINT("info", ("flags: %s", get_flags(STMT_END_F) ? "STMT_END_F " : "")); @@ -10286,7 +10290,7 @@ Rows_log_event::do_update_pos(rpl_group_info *rgi) Step the group log position if we are not in a transaction, otherwise increase the event log position. */ - rli->stmt_done(log_pos, thd, rgi); + error= rli->stmt_done(log_pos, thd, rgi); /* Clear any errors in thd->net.last_err*. It is not known if this is needed or not. It is believed that any errors that may exist in diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 22ef970dab9..8bdb81f5283 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1843,8 +1843,8 @@ int Old_rows_log_event::do_update_pos(rpl_group_info *rgi) { Relay_log_info *rli= rgi->rli; - DBUG_ENTER("Old_rows_log_event::do_update_pos"); int error= 0; + DBUG_ENTER("Old_rows_log_event::do_update_pos"); DBUG_PRINT("info", ("flags: %s", get_flags(STMT_END_F) ? "STMT_END_F " : "")); @@ -1856,7 +1856,7 @@ Old_rows_log_event::do_update_pos(rpl_group_info *rgi) Step the group log position if we are not in a transaction, otherwise increase the event log position. */ - rli->stmt_done(log_pos, thd, rgi); + error= rli->stmt_done(log_pos, thd, rgi); /* Clear any errors in thd->net.last_err*. It is not known if this is needed or not. It is believed that any errors that may exist in diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc index c23a20ebac9..de799874a8f 100644 --- a/sql/mysql_install_db.cc +++ b/sql/mysql_install_db.cc @@ -386,8 +386,8 @@ static int register_service() CloseServiceHandle(sc_manager); die("CreateService failed (%u)", GetLastError()); } - - SERVICE_DESCRIPTION sd= { "MariaDB database server" }; + char description[] = "MariaDB database server"; + SERVICE_DESCRIPTION sd= { description }; ChangeServiceConfig2(sc_service, SERVICE_CONFIG_DESCRIPTION, &sd); CloseServiceHandle(sc_service); CloseServiceHandle(sc_manager); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index f252d19f50b..349971e86fa 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -21,7 +21,7 @@ #ifndef __WIN__ #include <netdb.h> // getservbyname, servent #endif -#include "sql_parse.h" // test_if_data_home_dir +#include "sql_parse.h" // path_starts_from_data_home_dir #include "sql_cache.h" // query_cache, query_cache_* #include "sql_locale.h" // MY_LOCALES, my_locales, my_locale_by_name #include "sql_show.h" // free_status_vars, add_status_vars, @@ -721,12 +721,15 @@ mysql_mutex_t LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_global_system_variables, - LOCK_user_conn, LOCK_slave_list, LOCK_active_mi, + LOCK_user_conn, LOCK_slave_list, LOCK_connection_count, LOCK_error_messages, LOCK_slave_init; mysql_mutex_t LOCK_stats, LOCK_global_user_client_stats, LOCK_global_table_stats, LOCK_global_index_stats; +/* This protects against changes in master_info_index */ +mysql_mutex_t LOCK_active_mi; + /** The below lock protects access to two global server variables: max_prepared_stmt_count and prepared_stmt_count. These variables @@ -896,7 +899,7 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_LOCK_system_variables_hash, key_LOCK_thd_data, key_LOCK_user_conn, key_LOCK_uuid_short_generator, key_LOG_LOCK_log, key_master_info_data_lock, key_master_info_run_lock, - key_master_info_sleep_lock, + key_master_info_sleep_lock, key_master_info_start_stop_lock, key_mutex_slave_reporting_capability_err_lock, key_relay_log_info_data_lock, key_rpl_group_info_sleep_lock, key_relay_log_info_log_space_lock, key_relay_log_info_run_lock, @@ -972,6 +975,7 @@ static PSI_mutex_info all_server_mutexes[]= { &key_LOCK_uuid_short_generator, "LOCK_uuid_short_generator", PSI_FLAG_GLOBAL}, { &key_LOG_LOCK_log, "LOG::LOCK_log", 0}, { &key_master_info_data_lock, "Master_info::data_lock", 0}, + { &key_master_info_start_stop_lock, "Master_info::start_stop_lock", 0}, { &key_master_info_run_lock, "Master_info::run_lock", 0}, { &key_master_info_sleep_lock, "Master_info::sleep_lock", 0}, { &key_mutex_slave_reporting_capability_err_lock, "Slave_reporting_capability::err_lock", 0}, @@ -1483,7 +1487,7 @@ ulong query_cache_min_res_unit= QUERY_CACHE_MIN_RESULT_DATA_SIZE; Query_cache query_cache; #endif #ifdef HAVE_SMEM -char *shared_memory_base_name= default_shared_memory_base_name; +const char *shared_memory_base_name= default_shared_memory_base_name; my_bool opt_enable_shared_memory; HANDLE smem_event_connect_request= 0; #endif @@ -1725,7 +1729,7 @@ static void close_connections(void) mysql_mutex_unlock(&LOCK_thread_count); // For unlink from list Events::deinit(); - end_slave(); + slave_prepare_for_shutdown(); /* Give threads time to die. @@ -1801,6 +1805,7 @@ static void close_connections(void) DBUG_PRINT("quit",("Unlocking LOCK_thread_count")); mysql_mutex_unlock(&LOCK_thread_count); } + end_slave(); /* All threads has now been aborted */ DBUG_PRINT("quit",("Waiting for threads to die (count=%u)",thread_count)); mysql_mutex_lock(&LOCK_thread_count); @@ -2802,23 +2807,6 @@ void dec_connection_count(THD *thd) /* - Delete THD and decrement thread counters, including thread_running -*/ - -void delete_running_thd(THD *thd) -{ - mysql_mutex_lock(&LOCK_thread_count); - thd->unlink(); - mysql_mutex_unlock(&LOCK_thread_count); - - delete thd; - dec_thread_running(); - thread_safe_decrement32(&thread_count, &thread_count_lock); - signal_thd_deleted(); -} - - -/* Send a signal to unblock close_conneciton() if there is no more threads running with a THD attached @@ -5252,9 +5240,17 @@ static int init_server_components() unireg_abort(1); } - if (opt_bin_log && mysql_bin_log.open(opt_bin_logname, LOG_BIN, 0, - WRITE_CACHE, max_binlog_size, 0, TRUE)) - unireg_abort(1); + if (opt_bin_log) + { + int error; + mysql_mutex_t *log_lock= mysql_bin_log.get_log_lock(); + mysql_mutex_lock(log_lock); + error= mysql_bin_log.open(opt_bin_logname, LOG_BIN, 0, + WRITE_CACHE, max_binlog_size, 0, TRUE); + mysql_mutex_unlock(log_lock); + if (error) + unireg_abort(1); + } #ifdef HAVE_REPLICATION if (opt_bin_log && expire_logs_days) @@ -8025,17 +8021,14 @@ static int show_slave_running(THD *thd, SHOW_VAR *var, char *buff) var->type= SHOW_MY_BOOL; var->value= buff; - mysql_mutex_lock(&LOCK_active_mi); - if (master_info_index) + + if ((mi= get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_NOTE))) { - mi= master_info_index-> - get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_NOTE); - if (mi) - tmp= (my_bool) (mi->slave_running == MYSQL_SLAVE_RUN_READING && - mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN); + tmp= (my_bool) (mi->slave_running == MYSQL_SLAVE_RUN_READING && + mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN); + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); if (mi) *((my_bool *)buff)= tmp; else @@ -8067,38 +8060,26 @@ static int show_slaves_running(THD *thd, SHOW_VAR *var, char *buff) { var->type= SHOW_LONGLONG; var->value= buff; - mysql_mutex_lock(&LOCK_active_mi); - if (master_info_index) - *((longlong *)buff)= master_info_index->any_slave_sql_running(); - else - *((longlong *)buff)= 0; + *((longlong *)buff)= any_slave_sql_running(); - mysql_mutex_unlock(&LOCK_active_mi); return 0; } static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff) { - Master_info *mi= NULL; - longlong tmp; - LINT_INIT(tmp); + Master_info *mi; var->type= SHOW_LONGLONG; var->value= buff; - mysql_mutex_lock(&LOCK_active_mi); - if (master_info_index) + + if ((mi= get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_NOTE))) { - mi= master_info_index-> - get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_NOTE); - if (mi) - tmp= mi->received_heartbeats; + *((longlong *)buff)= mi->received_heartbeats; + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); - if (mi) - *((longlong *)buff)= tmp; else var->type= SHOW_UNDEF; return 0; @@ -8108,23 +8089,16 @@ static int show_slave_received_heartbeats(THD *thd, SHOW_VAR *var, char *buff) static int show_heartbeat_period(THD *thd, SHOW_VAR *var, char *buff) { Master_info *mi= NULL; - float tmp; - LINT_INIT(tmp); var->type= SHOW_CHAR; var->value= buff; - mysql_mutex_lock(&LOCK_active_mi); - if (master_info_index) + + if ((mi= get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_NOTE))) { - mi= master_info_index-> - get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_NOTE); - if (mi) - tmp= mi->heartbeat_period; + sprintf(buff, "%.3f", mi->heartbeat_period); + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); - if (mi) - sprintf(buff, "%.3f", tmp); else var->type= SHOW_UNDEF; return 0; @@ -8965,7 +8939,7 @@ static int mysql_init_variables(void) mysql_home[0]= pidfile_name[0]= log_error_file[0]= 0; #if defined(HAVE_REALPATH) && !defined(HAVE_valgrind) && !defined(HAVE_BROKEN_REALPATH) /* We can only test for sub paths if my_symlink.c is using realpath */ - myisam_test_invalid_symlink= test_if_data_home_dir; + mysys_test_invalid_symlink= path_starts_from_data_home_dir; #endif opt_log= opt_slow_log= 0; opt_bin_log= opt_bin_log_used= 0; diff --git a/sql/mysqld.h b/sql/mysqld.h index c68cab26374..731391490e0 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -59,7 +59,6 @@ void kill_mysql(void); void close_connection(THD *thd, uint sql_errno= 0); void handle_connection_in_main_thread(THD *thd); void create_thread_to_handle_connection(THD *thd); -void delete_running_thd(THD *thd); void signal_thd_deleted(); void unlink_thd(THD *thd); bool one_thread_per_connection_end(THD *thd, bool put_in_cache); @@ -118,7 +117,8 @@ extern my_bool sp_automatic_privileges, opt_noacl; extern ulong use_stat_tables; extern my_bool opt_old_style_user_limits, trust_function_creators; extern uint opt_crash_binlog_innodb; -extern char *shared_memory_base_name, *mysqld_unix_port; +extern const char *shared_memory_base_name; +extern char *mysqld_unix_port; extern my_bool opt_enable_shared_memory; extern ulong opt_replicate_events_marked_for_skip; extern char *default_tz_name; @@ -269,7 +269,7 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_LOCK_thd_data, key_LOCK_user_conn, key_LOG_LOCK_log, key_master_info_data_lock, key_master_info_run_lock, - key_master_info_sleep_lock, + key_master_info_sleep_lock, key_master_info_start_stop_lock, key_mutex_slave_reporting_capability_err_lock, key_relay_log_info_data_lock, key_relay_log_info_log_space_lock, key_relay_log_info_run_lock, key_rpl_group_info_sleep_lock, diff --git a/sql/nt_servc.cc b/sql/nt_servc.cc index d6a8eac7ed5..e05e43a0a59 100644 --- a/sql/nt_servc.cc +++ b/sql/nt_servc.cc @@ -508,7 +508,7 @@ BOOL NTService::IsService(LPCSTR ServiceName) } /* ------------------------------------------------------------------------ -------------------------------------------------------------------------- */ -BOOL NTService::got_service_option(char **argv, char *service_option) +BOOL NTService::got_service_option(char **argv, const char *service_option) { char *option; for (option= argv[1]; *option; option++) diff --git a/sql/nt_servc.h b/sql/nt_servc.h index 949499d8d7f..6781fe0ddfa 100644 --- a/sql/nt_servc.h +++ b/sql/nt_servc.h @@ -61,7 +61,7 @@ class NTService BOOL SeekStatus(LPCSTR szInternName, int OperationType); BOOL Remove(LPCSTR szInternName); BOOL IsService(LPCSTR ServiceName); - BOOL got_service_option(char **argv, char *service_option); + BOOL got_service_option(char **argv, const char *service_option); BOOL is_super_user(); /* diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 5d6891a1edf..8f9d5abfa4d 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -8067,8 +8067,15 @@ static SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param,COND *cond) if (cond_func->functype() == Item_func::BETWEEN || cond_func->functype() == Item_func::IN_FUNC) inv= ((Item_func_opt_neg *) cond_func)->negated; - else if (cond_func->select_optimize() == Item_func::OPTIMIZE_NONE) - DBUG_RETURN(0); + else + { + MEM_ROOT *tmp_root= param->mem_root; + param->thd->mem_root= param->old_root; + Item_func::optimize_type opt_res= cond_func->select_optimize(); + param->thd->mem_root= tmp_root; + if (opt_res == Item_func::OPTIMIZE_NONE) + DBUG_RETURN(0); + } param->cond= cond; @@ -9923,6 +9930,13 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2) if (!tmp->next_key_part) { + if (key2->use_count) + { + SEL_ARG *key2_cpy= new SEL_ARG(*key2); + if (key2_cpy) + return 0; + key2= key2_cpy; + } /* tmp->next_key_part is empty: cut the range that is covered by tmp from key2. @@ -9954,13 +9968,6 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2) key2: [---] tmp: [---------] */ - if (key2->use_count) - { - SEL_ARG *key2_cpy= new SEL_ARG(*key2); - if (key2_cpy) - return 0; - key2= key2_cpy; - } key2->copy_max_to_min(tmp); continue; } diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index a81b091461f..564a108c766 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -4044,13 +4044,13 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd) share->db_plugin= ha_lock_engine(0, TMP_ENGINE_HTON); table->file= get_new_handler(share, &table->mem_root, share->db_type()); - DBUG_ASSERT(uniq_tuple_length_arg <= table->file->max_key_length()); } else { share->db_plugin= ha_lock_engine(0, heap_hton); table->file= get_new_handler(share, &table->mem_root, share->db_type()); + DBUG_ASSERT(uniq_tuple_length_arg <= table->file->max_key_length()); } if (!table->file) goto err; diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 1607b1937df..1eee5df2bc5 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -27,7 +27,7 @@ #include "sql_partition.h" // partition_info.h: LIST_PART_ENTRY // NOT_A_PARTITION_ID #include "partition_info.h" -#include "sql_parse.h" // test_if_data_home_dir +#include "sql_parse.h" #include "sql_acl.h" // *_ACL #include "sql_base.h" // fill_record diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index c9a9ba3fc6d..f1508024c22 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -40,7 +40,9 @@ Master_info::Master_info(LEX_STRING *connection_name_arg, sync_counter(0), heartbeat_period(0), received_heartbeats(0), master_id(0), prev_master_id(0), using_gtid(USE_GTID_NO), events_queued_since_last_gtid(0), - gtid_reconnect_event_skip_count(0), gtid_event_seen(false) + gtid_reconnect_event_skip_count(0), gtid_event_seen(false), + in_start_all_slaves(0), in_stop_all_slaves(0), + users(0), killed(0) { host[0] = 0; user[0] = 0; password[0] = 0; ssl_ca[0]= 0; ssl_capath[0]= 0; ssl_cert[0]= 0; @@ -78,6 +80,8 @@ Master_info::Master_info(LEX_STRING *connection_name_arg, bzero((char*) &file, sizeof(file)); mysql_mutex_init(key_master_info_run_lock, &run_lock, MY_MUTEX_INIT_FAST); mysql_mutex_init(key_master_info_data_lock, &data_lock, MY_MUTEX_INIT_FAST); + mysql_mutex_init(key_master_info_start_stop_lock, &start_stop_lock, + MY_MUTEX_INIT_SLOW); mysql_mutex_setflags(&run_lock, MYF_NO_DEADLOCK_DETECTION); mysql_mutex_setflags(&data_lock, MYF_NO_DEADLOCK_DETECTION); mysql_mutex_init(key_master_info_sleep_lock, &sleep_lock, MY_MUTEX_INIT_FAST); @@ -87,6 +91,24 @@ Master_info::Master_info(LEX_STRING *connection_name_arg, mysql_cond_init(key_master_info_sleep_cond, &sleep_cond, NULL); } + +/** + Wait until no one is using Master_info +*/ + +void Master_info::wait_until_free() +{ + mysql_mutex_lock(&sleep_lock); + killed= 1; + while (users) + mysql_cond_wait(&sleep_cond, &sleep_lock); + mysql_mutex_unlock(&sleep_lock); +} + +/** + Delete master_info +*/ + Master_info::~Master_info() { #ifdef WITH_WSREP @@ -103,6 +125,7 @@ Master_info::~Master_info() mysql_mutex_destroy(&run_lock); mysql_mutex_destroy(&data_lock); mysql_mutex_destroy(&sleep_lock); + mysql_mutex_destroy(&start_stop_lock); mysql_cond_destroy(&data_cond); mysql_cond_destroy(&start_cond); mysql_cond_destroy(&stop_cond); @@ -713,12 +736,28 @@ uchar *get_key_master_info(Master_info *mi, size_t *length, return (uchar*) mi->cmp_connection_name.str; } +/* + Delete a master info + + Called from my_hash_delete(&master_info_hash) + Stops associated slave threads and frees master_info +*/ + void free_key_master_info(Master_info *mi) { DBUG_ENTER("free_key_master_info"); + mysql_mutex_unlock(&LOCK_active_mi); + + /* Ensure that we are not in reset_slave while this is done */ + mi->lock_slave_threads(); terminate_slave_threads(mi,SLAVE_FORCE_ALL); + /* We use 2 here instead of 1 just to make it easier when debugging */ + mi->killed= 2; end_master_info(mi); + mi->unlock_slave_threads(); delete mi; + + mysql_mutex_lock(&LOCK_active_mi); DBUG_VOID_RETURN; } @@ -874,9 +913,28 @@ Master_info_index::Master_info_index() index_file.file= -1; } + +/** + Free all connection threads + + This is done during early stages of shutdown + to give connection threads and slave threads time + to die before ~Master_info_index is called +*/ + +void Master_info_index::free_connections() +{ + mysql_mutex_assert_owner(&LOCK_active_mi); + my_hash_reset(&master_info_hash); +} + + +/** + Free all connection threads and free structures +*/ + Master_info_index::~Master_info_index() { - /* This will close connection for all objects in the cache */ my_hash_free(&master_info_hash); end_io_cache(&index_file); if (index_file.file >= 0) @@ -899,7 +957,6 @@ bool Master_info_index::init_all_master_info() File index_file_nr; DBUG_ENTER("init_all_master_info"); - mysql_mutex_assert_owner(&LOCK_active_mi); DBUG_ASSERT(master_info_index); if ((index_file_nr= my_open(index_file_name, @@ -947,7 +1004,6 @@ bool Master_info_index::init_all_master_info() DBUG_RETURN(1); } - lock_slave_threads(mi); init_thread_mask(&thread_mask,mi,0 /*not inverse*/); create_logfile_name_with_suffix(buf_master_info_file, @@ -962,6 +1018,7 @@ bool Master_info_index::init_all_master_info() sql_print_information("Reading Master_info: '%s' Relay_info:'%s'", buf_master_info_file, buf_relay_log_info_file); + mi->lock_slave_threads(); if (init_master_info(mi, buf_master_info_file, buf_relay_log_info_file, 0, thread_mask)) { @@ -975,14 +1032,15 @@ bool Master_info_index::init_all_master_info() if (master_info_index->add_master_info(mi, FALSE)) DBUG_RETURN(1); succ_num++; - unlock_slave_threads(mi); + mi->unlock_slave_threads(); } else { /* Master_info already in HASH */ sql_print_error(ER(ER_CONNECTION_ALREADY_EXISTS), + (int) connection_name.length, connection_name.str, (int) connection_name.length, connection_name.str); - unlock_slave_threads(mi); + mi->unlock_slave_threads(); delete mi; } continue; @@ -998,8 +1056,9 @@ bool Master_info_index::init_all_master_info() { /* Master_info was already registered */ sql_print_error(ER(ER_CONNECTION_ALREADY_EXISTS), + (int) connection_name.length, connection_name.str, (int) connection_name.length, connection_name.str); - unlock_slave_threads(mi); + mi->unlock_slave_threads(); delete mi; continue; } @@ -1008,7 +1067,6 @@ bool Master_info_index::init_all_master_info() if (master_info_index->add_master_info(mi, FALSE)) DBUG_RETURN(1); succ_num++; - unlock_slave_threads(mi); if (!opt_skip_slave_start) { @@ -1029,6 +1087,7 @@ bool Master_info_index::init_all_master_info() (int) connection_name.length, connection_name.str); } + mi->unlock_slave_threads(); } } @@ -1080,6 +1139,71 @@ bool Master_info_index::write_master_name_to_index_file(LEX_STRING *name, /** + Get Master_info for a connection and lock the object from deletion + + @param + connection_name Connection name + warning WARN_LEVEL_NOTE -> Don't print anything + WARN_LEVEL_WARN -> Issue warning if not exists + WARN_LEVEL_ERROR-> Issue error if not exists +*/ + +Master_info *get_master_info(LEX_STRING *connection_name, + Sql_condition::enum_warning_level warning) +{ + Master_info *mi; + DBUG_ENTER("get_master_info"); + + /* Protect against inserts into hash */ + mysql_mutex_lock(&LOCK_active_mi); + /* + The following can only be true during shutdown when slave has been killed + but some other threads are still trying to access slave statistics. + */ + if (unlikely(!master_info_index)) + { + if (warning != Sql_condition::WARN_LEVEL_NOTE) + my_error(WARN_NO_MASTER_INFO, + MYF(warning == Sql_condition::WARN_LEVEL_WARN ? + ME_JUST_WARNING : 0), + (int) connection_name->length, connection_name->str); + mysql_mutex_unlock(&LOCK_active_mi); + DBUG_RETURN(0); + } + if ((mi= master_info_index->get_master_info(connection_name, warning))) + { + /* + We have to use sleep_lock here. If we would use LOCK_active_mi + then we would take locks in wrong order in Master_info::release() + */ + mysql_mutex_lock(&mi->sleep_lock); + mi->users++; + DBUG_PRINT("info",("users: %d", mi->users)); + mysql_mutex_unlock(&mi->sleep_lock); + } + mysql_mutex_unlock(&LOCK_active_mi); + DBUG_RETURN(mi); +} + + +/** + Release master info. + Signals ~Master_info that it's now safe to delete it +*/ + +void Master_info::release() +{ + mysql_mutex_lock(&sleep_lock); + if (!--users && killed) + { + /* Signal ~Master_info that it's ok to now free it */ + mysql_cond_signal(&sleep_cond); + } + mysql_mutex_unlock(&sleep_lock); +} + + +/** Get Master_info for a connection @param @@ -1101,8 +1225,6 @@ Master_info_index::get_master_info(LEX_STRING *connection_name, ("connection_name: '%.*s'", (int) connection_name->length, connection_name->str)); - mysql_mutex_assert_owner(&LOCK_active_mi); - /* Make name lower case for comparison */ res= strmake(buff, connection_name->str, connection_name->length); my_casedn_str(system_charset_info, buff); @@ -1168,7 +1290,12 @@ bool Master_info_index::check_duplicate_master_info(LEX_STRING *name_arg, /* Add a Master_info class to Hash Table */ bool Master_info_index::add_master_info(Master_info *mi, bool write_to_file) { - if (!my_hash_insert(&master_info_hash, (uchar*) mi)) + /* + We have to protect against shutdown to ensure we are not calling + my_hash_insert() while my_hash_free() is in progress + */ + if (unlikely(shutdown_in_progress) || + !my_hash_insert(&master_info_hash, (uchar*) mi)) { if (global_system_variables.log_warnings > 1) sql_print_information("Added new Master_info '%.*s' to hash table", @@ -1194,105 +1321,131 @@ bool Master_info_index::add_master_info(Master_info *mi, bool write_to_file) atomic */ -bool Master_info_index::remove_master_info(LEX_STRING *name) +bool Master_info_index::remove_master_info(Master_info *mi) { - Master_info* mi; DBUG_ENTER("remove_master_info"); + mysql_mutex_assert_owner(&LOCK_active_mi); - if ((mi= get_master_info(name, Sql_condition::WARN_LEVEL_WARN))) + // Delete Master_info and rewrite others to file + if (!my_hash_delete(&master_info_hash, (uchar*) mi)) { - // Delete Master_info and rewrite others to file - if (!my_hash_delete(&master_info_hash, (uchar*) mi)) + File index_file_nr; + + // Close IO_CACHE and FILE handler fisrt + end_io_cache(&index_file); + my_close(index_file.file, MYF(MY_WME)); + + // Reopen File and truncate it + if ((index_file_nr= my_open(index_file_name, + O_RDWR | O_CREAT | O_TRUNC | O_BINARY , + MYF(MY_WME))) < 0 || + init_io_cache(&index_file, index_file_nr, + IO_SIZE, WRITE_CACHE, + my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)), + 0, MYF(MY_WME | MY_WAIT_IF_FULL))) { - File index_file_nr; - - // Close IO_CACHE and FILE handler fisrt - end_io_cache(&index_file); - my_close(index_file.file, MYF(MY_WME)); - - // Reopen File and truncate it - if ((index_file_nr= my_open(index_file_name, - O_RDWR | O_CREAT | O_TRUNC | O_BINARY , - MYF(MY_WME))) < 0 || - init_io_cache(&index_file, index_file_nr, - IO_SIZE, WRITE_CACHE, - my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)), - 0, MYF(MY_WME | MY_WAIT_IF_FULL))) - { - int error= my_errno; - if (index_file_nr >= 0) - my_close(index_file_nr,MYF(0)); - - sql_print_error("Create of Master Info Index file '%s' failed with " - "error: %M", - index_file_name, error); - DBUG_RETURN(TRUE); - } + int error= my_errno; + if (index_file_nr >= 0) + my_close(index_file_nr,MYF(0)); - // Rewrite Master_info.index - for (uint i= 0; i< master_info_hash.records; ++i) - { - Master_info *tmp_mi; - tmp_mi= (Master_info *) my_hash_element(&master_info_hash, i); - write_master_name_to_index_file(&tmp_mi->connection_name, 0); - } - my_sync(index_file_nr, MYF(MY_WME)); + sql_print_error("Create of Master Info Index file '%s' failed with " + "error: %M", + index_file_name, error); + DBUG_RETURN(TRUE); + } + + // Rewrite Master_info.index + for (uint i= 0; i< master_info_hash.records; ++i) + { + Master_info *tmp_mi; + tmp_mi= (Master_info *) my_hash_element(&master_info_hash, i); + write_master_name_to_index_file(&tmp_mi->connection_name, 0); } + if (my_sync(index_file_nr, MYF(MY_WME))) + DBUG_RETURN(TRUE); } DBUG_RETURN(FALSE); } /** - Master_info_index::give_error_if_slave_running() + give_error_if_slave_running() + + @param + already_locked 0 if we need to lock, 1 if we have LOCK_active_mi_locked @return TRUE If some slave is running. An error is printed FALSE No slave is running */ -bool Master_info_index::give_error_if_slave_running() +bool give_error_if_slave_running(bool already_locked) { + bool ret= 0; DBUG_ENTER("give_error_if_slave_running"); - mysql_mutex_assert_owner(&LOCK_active_mi); - for (uint i= 0; i< master_info_hash.records; ++i) + if (!already_locked) + mysql_mutex_lock(&LOCK_active_mi); + if (!master_info_index) { - Master_info *mi; - mi= (Master_info *) my_hash_element(&master_info_hash, i); - if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN) + my_error(ER_SERVER_SHUTDOWN, MYF(0)); + ret= 1; + } + else + { + HASH *hash= &master_info_index->master_info_hash; + for (uint i= 0; i< hash->records; ++i) { - my_error(ER_SLAVE_MUST_STOP, MYF(0), (int) mi->connection_name.length, - mi->connection_name.str); - DBUG_RETURN(TRUE); + Master_info *mi; + mi= (Master_info *) my_hash_element(hash, i); + if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN) + { + my_error(ER_SLAVE_MUST_STOP, MYF(0), (int) mi->connection_name.length, + mi->connection_name.str); + ret= 1; + break; + } } } - DBUG_RETURN(FALSE); + if (!already_locked) + mysql_mutex_unlock(&LOCK_active_mi); + DBUG_RETURN(ret); } /** - Master_info_index::any_slave_sql_running() - - The LOCK_active_mi must be held while calling this function. + any_slave_sql_running() @return 0 No Slave SQL thread is running # Number of slave SQL thread running + + Note that during shutdown we return 1. This is needed to ensure we + don't try to resize thread pool during shutdown as during shutdown + master_info_hash may be freeing the hash and during that time + hash entries can't be accessed. */ -uint Master_info_index::any_slave_sql_running() +uint any_slave_sql_running() { uint count= 0; + HASH *hash; DBUG_ENTER("any_slave_sql_running"); - mysql_mutex_assert_owner(&LOCK_active_mi); - for (uint i= 0; i< master_info_hash.records; ++i) + mysql_mutex_lock(&LOCK_active_mi); + if (unlikely(shutdown_in_progress || !master_info_index)) + { + mysql_mutex_unlock(&LOCK_active_mi); + DBUG_RETURN(1); + } + hash= &master_info_index->master_info_hash; + for (uint i= 0; i< hash->records; ++i) { - Master_info *mi= (Master_info *)my_hash_element(&master_info_hash, i); + Master_info *mi= (Master_info *)my_hash_element(hash, i); if (mi->rli.slave_running != MYSQL_SLAVE_NOT_RUN) count++; } + mysql_mutex_unlock(&LOCK_active_mi); DBUG_RETURN(count); } @@ -1305,15 +1458,25 @@ uint Master_info_index::any_slave_sql_running() @return TRUE Error FALSE Everything ok. + + This code is written so that we don't keep LOCK_active_mi active + while we are starting a slave. */ bool Master_info_index::start_all_slaves(THD *thd) { bool result= FALSE; - DBUG_ENTER("warn_if_slave_running"); + DBUG_ENTER("start_all_slaves"); mysql_mutex_assert_owner(&LOCK_active_mi); - for (uint i= 0; i< master_info_hash.records; ++i) + for (uint i= 0; i< master_info_hash.records; i++) + { + Master_info *mi; + mi= (Master_info *) my_hash_element(&master_info_hash, i); + mi->in_start_all_slaves= 0; + } + + for (uint i= 0; i< master_info_hash.records; ) { int error; Master_info *mi; @@ -1323,25 +1486,40 @@ bool Master_info_index::start_all_slaves(THD *thd) Try to start all slaves that are configured (host is defined) and are not already running */ - if ((mi->slave_running == MYSQL_SLAVE_NOT_RUN || - !mi->rli.slave_running) && *mi->host) + if (!((mi->slave_running == MYSQL_SLAVE_NOT_RUN || + !mi->rli.slave_running) && *mi->host) || + mi->in_start_all_slaves) { - if ((error= start_slave(thd, mi, 1))) - { - my_error(ER_CANT_START_STOP_SLAVE, MYF(0), - "START", - (int) mi->connection_name.length, - mi->connection_name.str); - result= 1; - if (error < 0) // fatal error - break; - } - else - push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_SLAVE_STARTED, ER(ER_SLAVE_STARTED), - (int) mi->connection_name.length, - mi->connection_name.str); + i++; + continue; } + mi->in_start_all_slaves= 1; + + mysql_mutex_lock(&mi->sleep_lock); + mi->users++; // Mark used + mysql_mutex_unlock(&mi->sleep_lock); + mysql_mutex_unlock(&LOCK_active_mi); + error= start_slave(thd, mi, 1); + mi->release(); + mysql_mutex_lock(&LOCK_active_mi); + if (error) + { + my_error(ER_CANT_START_STOP_SLAVE, MYF(0), + "START", + (int) mi->connection_name.length, + mi->connection_name.str); + result= 1; + if (error < 0) // fatal error + break; + } + else + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_SLAVE_STARTED, ER(ER_SLAVE_STARTED), + (int) mi->connection_name.length, + mi->connection_name.str); + /* Restart from first element as master_info_hash may have changed */ + i= 0; + continue; } DBUG_RETURN(result); } @@ -1355,38 +1533,63 @@ bool Master_info_index::start_all_slaves(THD *thd) @return TRUE Error FALSE Everything ok. + + This code is written so that we don't keep LOCK_active_mi active + while we are stopping a slave. */ bool Master_info_index::stop_all_slaves(THD *thd) { bool result= FALSE; - DBUG_ENTER("warn_if_slave_running"); + DBUG_ENTER("stop_all_slaves"); mysql_mutex_assert_owner(&LOCK_active_mi); - for (uint i= 0; i< master_info_hash.records; ++i) + for (uint i= 0; i< master_info_hash.records; i++) + { + Master_info *mi; + mi= (Master_info *) my_hash_element(&master_info_hash, i); + mi->in_stop_all_slaves= 0; + } + + for (uint i= 0; i< master_info_hash.records ;) { int error; Master_info *mi; mi= (Master_info *) my_hash_element(&master_info_hash, i); - if ((mi->slave_running != MYSQL_SLAVE_NOT_RUN || - mi->rli.slave_running)) + if (!(mi->slave_running != MYSQL_SLAVE_NOT_RUN || + mi->rli.slave_running) || + mi->in_stop_all_slaves) { - if ((error= stop_slave(thd, mi, 1))) - { - my_error(ER_CANT_START_STOP_SLAVE, MYF(0), - "STOP", - (int) mi->connection_name.length, - mi->connection_name.str); - result= 1; - if (error < 0) // Fatal error - break; - } - else - push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_SLAVE_STOPPED, ER(ER_SLAVE_STOPPED), - (int) mi->connection_name.length, - mi->connection_name.str); + i++; + continue; } + mi->in_stop_all_slaves= 1; // Protection for loops + + mysql_mutex_lock(&mi->sleep_lock); + mi->users++; // Mark used + mysql_mutex_unlock(&mi->sleep_lock); + mysql_mutex_unlock(&LOCK_active_mi); + error= stop_slave(thd, mi, 1); + mi->release(); + mysql_mutex_lock(&LOCK_active_mi); + if (error) + { + my_error(ER_CANT_START_STOP_SLAVE, MYF(0), + "STOP", + (int) mi->connection_name.length, + mi->connection_name.str); + result= 1; + if (error < 0) // Fatal error + break; + } + else + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_SLAVE_STOPPED, ER(ER_SLAVE_STOPPED), + (int) mi->connection_name.length, + mi->connection_name.str); + /* Restart from first element as master_info_hash may have changed */ + i= 0; + continue; } DBUG_RETURN(result); } diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h index e58df0150f5..5c42378ca2c 100644 --- a/sql/rpl_mi.h +++ b/sql/rpl_mi.h @@ -79,6 +79,10 @@ class Master_info : public Slave_reporting_capability { return opt_slave_parallel_threads > 0; } + void release(); + void wait_until_free(); + void lock_slave_threads(); + void unlock_slave_threads(); /* the variables below are needed because we can change masters on the fly */ char master_log_name[FN_REFLEN+6]; /* Room for multi-*/ @@ -97,7 +101,7 @@ class Master_info : public Slave_reporting_capability File fd; // we keep the file open, so we need to remember the file pointer IO_CACHE file; - mysql_mutex_t data_lock, run_lock, sleep_lock; + mysql_mutex_t data_lock, run_lock, sleep_lock, start_stop_lock; mysql_cond_t data_cond, start_cond, stop_cond, sleep_cond; THD *io_thd; MYSQL* mysql; @@ -182,7 +186,11 @@ class Master_info : public Slave_reporting_capability uint64 gtid_reconnect_event_skip_count; /* gtid_event_seen is false until we receive first GTID event from master. */ bool gtid_event_seen; + bool in_start_all_slaves, in_stop_all_slaves; + uint users; /* Active user for object */ + uint killed; }; + int init_master_info(Master_info* mi, const char* master_info_fname, const char* slave_info_fname, bool abort_if_no_master_info_file, @@ -218,13 +226,12 @@ public: bool check_duplicate_master_info(LEX_STRING *connection_name, const char *host, uint port); bool add_master_info(Master_info *mi, bool write_to_file); - bool remove_master_info(LEX_STRING *connection_name); + bool remove_master_info(Master_info *mi); Master_info *get_master_info(LEX_STRING *connection_name, Sql_condition::enum_warning_level warning); - bool give_error_if_slave_running(); - uint any_slave_sql_running(); bool start_all_slaves(THD *thd); bool stop_all_slaves(THD *thd); + void free_connections(); }; @@ -237,6 +244,8 @@ public: }; +Master_info *get_master_info(LEX_STRING *connection_name, + Sql_condition::enum_warning_level warning); bool check_master_connection_name(LEX_STRING *name); void create_logfile_name_with_suffix(char *res_file_name, size_t length, const char *info_file, @@ -246,7 +255,8 @@ void create_logfile_name_with_suffix(char *res_file_name, size_t length, uchar *get_key_master_info(Master_info *mi, size_t *length, my_bool not_used __attribute__((unused))); void free_key_master_info(Master_info *mi); - +uint any_slave_sql_running(); +bool give_error_if_slave_running(bool already_lock); #endif /* HAVE_REPLICATION */ #endif /* RPL_MI_H */ diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index 23f61a82a90..44f60e4482b 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -1253,39 +1253,16 @@ handle_rpl_parallel_thread(void *arg) */ rpt->batch_free(); - for (;;) + if ((events= rpt->event_queue) != NULL) { - if ((events= rpt->event_queue) != NULL) - { - /* - Take next group of events from the replication pool. - This is faster than having to wakeup the pool manager thread to give - us a new event. - */ - rpt->dequeue1(events); - mysql_mutex_unlock(&rpt->LOCK_rpl_thread); - goto more_events; - } - if (!rpt->pause_for_ftwrl || - (in_event_group && !group_rgi->parallel_entry->force_abort)) - break; /* - We are currently in the delicate process of pausing parallel - replication while FLUSH TABLES WITH READ LOCK is starting. We must - not de-allocate the thread (setting rpt->current_owner= NULL) until - rpl_unpause_after_ftwrl() has woken us up. + Take next group of events from the replication pool. + This is faster than having to wakeup the pool manager thread to give + us a new event. */ - mysql_mutex_lock(&rpt->current_entry->LOCK_parallel_entry); + rpt->dequeue1(events); mysql_mutex_unlock(&rpt->LOCK_rpl_thread); - if (rpt->pause_for_ftwrl) - mysql_cond_wait(&rpt->current_entry->COND_parallel_entry, - &rpt->current_entry->LOCK_parallel_entry); - mysql_mutex_unlock(&rpt->current_entry->LOCK_parallel_entry); - mysql_mutex_lock(&rpt->LOCK_rpl_thread); - /* - Now loop to check again for more events available, since we released - and re-aquired the LOCK_rpl_thread mutex. - */ + goto more_events; } rpt->inuse_relaylog_refcount_update(); @@ -1312,11 +1289,36 @@ handle_rpl_parallel_thread(void *arg) } if (!in_event_group) { + /* If we are in a FLUSH TABLES FOR READ LOCK, wait for it */ + while (rpt->current_entry && rpt->pause_for_ftwrl) + { + /* + We are currently in the delicate process of pausing parallel + replication while FLUSH TABLES WITH READ LOCK is starting. We must + not de-allocate the thread (setting rpt->current_owner= NULL) until + rpl_unpause_after_ftwrl() has woken us up. + */ + rpl_parallel_entry *e= rpt->current_entry; + /* + Ensure that we will unblock rpl_pause_for_ftrwl() + e->pause_sub_id may be LONGLONG_MAX if rpt->current_entry has changed + */ + DBUG_ASSERT(e->pause_sub_id == (uint64)ULONGLONG_MAX || + e->last_committed_sub_id >= e->pause_sub_id); + mysql_mutex_lock(&e->LOCK_parallel_entry); + mysql_mutex_unlock(&rpt->LOCK_rpl_thread); + if (rpt->pause_for_ftwrl) + mysql_cond_wait(&e->COND_parallel_entry, &e->LOCK_parallel_entry); + mysql_mutex_unlock(&e->LOCK_parallel_entry); + mysql_mutex_lock(&rpt->LOCK_rpl_thread); + } + rpt->current_owner= NULL; /* Tell wait_for_done() that we are done, if it is waiting. */ if (likely(rpt->current_entry) && unlikely(rpt->current_entry->force_abort)) mysql_cond_broadcast(&rpt->COND_rpl_thread_stop); + rpt->current_entry= NULL; if (!rpt->stop) rpt->pool->release_thread(rpt); @@ -1355,10 +1357,24 @@ dealloc_gco(group_commit_orderer *gco) my_free(gco); } +/** + Change thread count for global parallel worker threads + + @param pool parallel thread pool + @param new_count Number of threads to be in pool. 0 in shutdown + @param force Force thread count to new_count even if slave + threads are running + + By default we don't resize pool of there are running threads. + However during shutdown we will always do it. + This is needed as any_slave_sql_running() returns 1 during shutdown + as we don't want to access master_info while + Master_info_index::free_connections are running. +*/ static int rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool, - uint32 new_count) + uint32 new_count, bool force) { uint32 i; rpl_parallel_thread **new_list= NULL; @@ -1369,6 +1385,28 @@ rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool, if ((res= pool_mark_busy(pool, current_thd))) return res; + /* Protect against parallel pool resizes */ + if (pool->count == new_count) + { + pool_mark_not_busy(pool); + return 0; + } + + /* + If we are about to delete pool, do an extra check that there are no new + slave threads running since we marked pool busy + */ + if (!new_count && !force) + { + if (any_slave_sql_running()) + { + DBUG_PRINT("warning", + ("SQL threads running while trying to reset parallel pool")); + pool_mark_not_busy(pool); + return 0; // Ok to not resize pool + } + } + /* Allocate the new list of threads up-front. That way, if we fail half-way, we only need to free whatever we managed @@ -1382,7 +1420,7 @@ rpl_parallel_change_thread_count(rpl_parallel_thread_pool *pool, { my_error(ER_OUTOFMEMORY, MYF(0), (int(new_count*sizeof(*new_list) + new_count*sizeof(*rpt_array)))); - goto err;; + goto err; } for (i= 0; i < new_count; ++i) @@ -1503,12 +1541,26 @@ err: return 1; } +/* + Deactivate the parallel replication thread pool, if there are now no more + SQL threads running. +*/ + +int rpl_parallel_resize_pool_if_no_slaves(void) +{ + /* master_info_index is set to NULL on shutdown */ + if (opt_slave_parallel_threads > 0 && !any_slave_sql_running()) + return rpl_parallel_inactivate_pool(&global_rpl_thread_pool); + return 0; +} + int rpl_parallel_activate_pool(rpl_parallel_thread_pool *pool) { if (!pool->count) - return rpl_parallel_change_thread_count(pool, opt_slave_parallel_threads); + return rpl_parallel_change_thread_count(pool, opt_slave_parallel_threads, + 0); return 0; } @@ -1516,7 +1568,7 @@ rpl_parallel_activate_pool(rpl_parallel_thread_pool *pool) int rpl_parallel_inactivate_pool(rpl_parallel_thread_pool *pool) { - return rpl_parallel_change_thread_count(pool, 0); + return rpl_parallel_change_thread_count(pool, 0, 0); } @@ -1795,7 +1847,7 @@ rpl_parallel_thread_pool::destroy() { if (!inited) return; - rpl_parallel_change_thread_count(this, 0); + rpl_parallel_change_thread_count(this, 0, 1); mysql_mutex_destroy(&LOCK_rpl_thread_pool); mysql_cond_destroy(&COND_rpl_thread_pool); inited= false; @@ -1814,6 +1866,7 @@ rpl_parallel_thread_pool::get_thread(rpl_parallel_thread **owner, { rpl_parallel_thread *rpt; + DBUG_ASSERT(count > 0); mysql_mutex_lock(&LOCK_rpl_thread_pool); while (unlikely(busy) || !(rpt= free_list)) mysql_cond_wait(&COND_rpl_thread_pool, &LOCK_rpl_thread_pool); @@ -2042,6 +2095,11 @@ rpl_parallel::find(uint32 domain_id) return e; } +/** + Wait until all sql worker threads has stopped processing + + This is called when sql thread has been killed/stopped +*/ void rpl_parallel::wait_for_done(THD *thd, Relay_log_info *rli) diff --git a/sql/rpl_parallel.h b/sql/rpl_parallel.h index a02c1af3b3e..0d7cd4f2e9b 100644 --- a/sql/rpl_parallel.h +++ b/sql/rpl_parallel.h @@ -342,6 +342,7 @@ struct rpl_parallel { extern struct rpl_parallel_thread_pool global_rpl_thread_pool; +extern int rpl_parallel_resize_pool_if_no_slaves(void); extern int rpl_parallel_activate_pool(rpl_parallel_thread_pool *pool); extern int rpl_parallel_inactivate_pool(rpl_parallel_thread_pool *pool); extern bool process_gtid_for_restart_pos(Relay_log_info *rli, rpl_gtid *gtid); diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index c570105cdf6..928fbd3d7c1 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -208,6 +208,7 @@ a file name for --relay-log-index option", opt_relaylog_index_name); Master_info* mi= rli->mi; char buf_relay_logname[FN_REFLEN], buf_relaylog_index_name_buff[FN_REFLEN]; char *buf_relaylog_index_name= opt_relaylog_index_name; + mysql_mutex_t *log_lock; create_logfile_name_with_suffix(buf_relay_logname, sizeof(buf_relay_logname), @@ -227,14 +228,18 @@ a file name for --relay-log-index option", opt_relaylog_index_name); note, that if open() fails, we'll still have index file open but a destructor will take care of that */ + log_lock= rli->relay_log.get_log_lock(); + mysql_mutex_lock(log_lock); if (rli->relay_log.open_index_file(buf_relaylog_index_name, ln, TRUE) || rli->relay_log.open(ln, LOG_BIN, 0, SEQ_READ_APPEND, mi->rli.max_relay_log_size, 1, TRUE)) { + mysql_mutex_unlock(log_lock); mysql_mutex_unlock(&rli->data_lock); sql_print_error("Failed when trying to open logs for '%s' in init_relay_log_info(). Error: %M", ln, my_errno); DBUG_RETURN(1); } + mysql_mutex_unlock(log_lock); } /* if file does not exist */ @@ -432,7 +437,7 @@ Failed to open the existing relay log info file '%s' (errno %d)", } rli->inited= 1; mysql_mutex_unlock(&rli->data_lock); - DBUG_RETURN(error); + DBUG_RETURN(0); err: sql_print_error("%s", msg); @@ -1304,9 +1309,10 @@ bool Relay_log_info::is_until_satisfied(THD *thd, Log_event *ev) } -void Relay_log_info::stmt_done(my_off_t event_master_log_pos, THD *thd, +bool Relay_log_info::stmt_done(my_off_t event_master_log_pos, THD *thd, rpl_group_info *rgi) { + int error= 0; DBUG_ENTER("Relay_log_info::stmt_done"); DBUG_ASSERT(rgi->rli == this); @@ -1358,10 +1364,11 @@ void Relay_log_info::stmt_done(my_off_t event_master_log_pos, THD *thd, } DBUG_EXECUTE_IF("inject_crash_before_flush_rli", DBUG_SUICIDE();); if (mi->using_gtid == Master_info::USE_GTID_NO) - flush_relay_log_info(this); + if (flush_relay_log_info(this)) + error= 1; DBUG_EXECUTE_IF("inject_crash_after_flush_rli", DBUG_SUICIDE();); } - DBUG_VOID_RETURN; + DBUG_RETURN(error); } diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index efcec83b880..7fc41786957 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -416,7 +416,7 @@ public: relay log info and used to produce information for <code>SHOW SLAVE STATUS</code>. */ - void stmt_done(my_off_t event_log_pos, THD *thd, rpl_group_info *rgi); + bool stmt_done(my_off_t event_log_pos, THD *thd, rpl_group_info *rgi); int alloc_inuse_relaylog(const char *name); void free_inuse_relaylog(inuse_relaylog *ir); void reset_inuse_relaylog(); diff --git a/sql/slave.cc b/sql/slave.cc index ae335a24811..694e9a2e673 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -231,16 +231,14 @@ void init_thread_mask(int* mask,Master_info* mi,bool inverse) /* - lock_slave_threads() + lock_slave_threads() against other threads doing STOP, START or RESET SLAVE + */ -void lock_slave_threads(Master_info* mi) +void Master_info::lock_slave_threads() { DBUG_ENTER("lock_slave_threads"); - - //TODO: see if we can do this without dual mutex - mysql_mutex_lock(&mi->run_lock); - mysql_mutex_lock(&mi->rli.run_lock); + mysql_mutex_lock(&start_stop_lock); DBUG_VOID_RETURN; } @@ -249,13 +247,10 @@ void lock_slave_threads(Master_info* mi) unlock_slave_threads() */ -void unlock_slave_threads(Master_info* mi) +void Master_info::unlock_slave_threads() { DBUG_ENTER("unlock_slave_threads"); - - //TODO: see if we can do this without dual mutex - mysql_mutex_unlock(&mi->rli.run_lock); - mysql_mutex_unlock(&mi->run_lock); + mysql_mutex_unlock(&start_stop_lock); DBUG_VOID_RETURN; } @@ -377,7 +372,6 @@ int init_slave() accepted. However bootstrap may conflict with us if it does START SLAVE. So it's safer to take the lock. */ - mysql_mutex_lock(&LOCK_active_mi); if (pthread_key_create(&RPL_MASTER_INFO, NULL)) goto err; @@ -386,7 +380,6 @@ int init_slave() if (!master_info_index || master_info_index->init_all_master_info()) { sql_print_error("Failed to initialize multi master structures"); - mysql_mutex_unlock(&LOCK_active_mi); DBUG_RETURN(1); } if (!(active_mi= new Master_info(&default_master_connection_name, @@ -444,7 +437,6 @@ int init_slave() } end: - mysql_mutex_unlock(&LOCK_active_mi); DBUG_RETURN(error); err: @@ -617,6 +609,7 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock) if (!mi->inited) DBUG_RETURN(0); /* successfully do nothing */ int error,force_all = (thread_mask & SLAVE_FORCE_ALL); + int retval= 0; mysql_mutex_t *sql_lock = &mi->rli.run_lock, *io_lock = &mi->run_lock; mysql_mutex_t *log_lock= mi->rli.relay_log.get_log_lock(); @@ -636,24 +629,19 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock) skip_lock)) && !force_all) DBUG_RETURN(error); + retval= error; mysql_mutex_lock(log_lock); DBUG_PRINT("info",("Flushing relay-log info file.")); if (current_thd) THD_STAGE_INFO(current_thd, stage_flushing_relay_log_info_file); - if (flush_relay_log_info(&mi->rli)) - DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS); - - if (my_sync(mi->rli.info_fd, MYF(MY_WME))) - DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS); + if (flush_relay_log_info(&mi->rli) || + my_sync(mi->rli.info_fd, MYF(MY_WME))) + retval= ER_ERROR_DURING_FLUSH_LOGS; mysql_mutex_unlock(log_lock); } - if (opt_slave_parallel_threads > 0 && - master_info_index &&// master_info_index is set to NULL on server shutdown - !master_info_index->any_slave_sql_running()) - rpl_parallel_inactivate_pool(&global_rpl_thread_pool); if (thread_mask & (SLAVE_IO|SLAVE_FORCE_ALL)) { DBUG_PRINT("info",("Terminating IO thread")); @@ -664,25 +652,26 @@ int terminate_slave_threads(Master_info* mi,int thread_mask,bool skip_lock) skip_lock)) && !force_all) DBUG_RETURN(error); + if (!retval) + retval= error; mysql_mutex_lock(log_lock); DBUG_PRINT("info",("Flushing relay log and master info file.")); if (current_thd) THD_STAGE_INFO(current_thd, stage_flushing_relay_log_and_master_info_repository); - if (flush_master_info(mi, TRUE, FALSE)) - DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS); - + if (likely(mi->fd >= 0)) + { + if (flush_master_info(mi, TRUE, FALSE) || my_sync(mi->fd, MYF(MY_WME))) + retval= ER_ERROR_DURING_FLUSH_LOGS; + } if (mi->rli.relay_log.is_open() && my_sync(mi->rli.relay_log.get_log_file()->file, MYF(MY_WME))) - DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS); - - if (my_sync(mi->fd, MYF(MY_WME))) - DBUG_RETURN(ER_ERROR_DURING_FLUSH_LOGS); + retval= ER_ERROR_DURING_FLUSH_LOGS; mysql_mutex_unlock(log_lock); } - DBUG_RETURN(0); + DBUG_RETURN(retval); } @@ -845,6 +834,15 @@ int start_slave_thread( mysql_mutex_unlock(start_lock); DBUG_RETURN(ER_SLAVE_THREAD); } + + /* + In the following loop we can't check for thd->killed as we have to + wait until THD structures for the slave thread are created + before we can return. + This should be ok as there is no major work done in the slave + threads before they signal that we can stop waiting. + */ + if (start_cond && cond_lock) // caller has cond_lock { THD* thd = current_thd; @@ -862,16 +860,9 @@ int start_slave_thread( registered, we could otherwise go waiting though thd->killed is set. */ - if (!thd->killed) - mysql_cond_wait(start_cond, cond_lock); + mysql_cond_wait(start_cond, cond_lock); thd->EXIT_COND(& saved_stage); mysql_mutex_lock(cond_lock); // re-acquire it as exit_cond() released - if (thd->killed) - { - if (start_lock) - mysql_mutex_unlock(start_lock); - DBUG_RETURN(thd->killed_errno()); - } } } if (start_lock) @@ -959,10 +950,7 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start, mi); if (!error && (thread_mask & SLAVE_SQL)) { - if (opt_slave_parallel_threads > 0) - error= rpl_parallel_activate_pool(&global_rpl_thread_pool); - if (!error) - error= start_slave_thread( + error= start_slave_thread( #ifdef HAVE_PSI_INTERFACE key_thread_slave_sql, #endif @@ -978,10 +966,18 @@ int start_slave_threads(bool need_slave_mutex, bool wait_for_start, /* - Release slave threads at time of executing shutdown. + Kill slaves preparing for shutdown +*/ - SYNOPSIS - end_slave() +void slave_prepare_for_shutdown() +{ + mysql_mutex_lock(&LOCK_active_mi); + master_info_index->free_connections(); + mysql_mutex_unlock(&LOCK_active_mi); +} + +/* + Release slave threads at time of executing shutdown. */ void end_slave() @@ -999,7 +995,10 @@ void end_slave() startup parameter to the server was wrong. */ mysql_mutex_lock(&LOCK_active_mi); - /* This will call terminate_slave_threads() on all connections */ + /* + master_info_index should not have any threads anymore as they where + killed as part of slave_prepare_for_shutdown() + */ delete master_info_index; master_info_index= 0; active_mi= 0; @@ -2663,7 +2662,9 @@ static bool send_show_master_info_data(THD *thd, Master_info *mi, bool full, mysql_mutex_lock(&mi->data_lock); mysql_mutex_lock(&mi->rli.data_lock); + /* err_lock is to protect mi->last_error() */ mysql_mutex_lock(&mi->err_lock); + /* err_lock is to protect mi->rli.last_error() */ mysql_mutex_lock(&mi->rli.err_lock); protocol->store(mi->host, &my_charset_bin); protocol->store(mi->user, &my_charset_bin); @@ -4532,6 +4533,16 @@ pthread_handler_t handle_slave_sql(void *arg) rli->slave_running= MYSQL_SLAVE_RUN_NOT_CONNECT; pthread_detach_this_thread(); + + if (opt_slave_parallel_threads > 0 && + rpl_parallel_activate_pool(&global_rpl_thread_pool)) + { + mysql_cond_broadcast(&rli->start_cond); + rli->report(ERROR_LEVEL, ER_SLAVE_FATAL_ERROR, NULL, + "Failed during parallel slave pool activation"); + goto err_during_init; + } + if (init_slave_thread(thd, mi, SLAVE_THD_SQL)) { /* @@ -4840,8 +4851,15 @@ log '%s' at position %s, relay log '%s' position: %s%s", RPL_LOG_NAME, if (rli->mi->using_gtid != Master_info::USE_GTID_NO) { ulong domain_count; + my_bool save_log_all_errors= thd->log_all_errors; + /* + We don't need to check return value for flush_relay_log_info() + as any errors should be logged to stderr + */ + thd->log_all_errors= 1; flush_relay_log_info(rli); + thd->log_all_errors= save_log_all_errors; if (mi->using_parallel()) { /* @@ -4950,17 +4968,7 @@ err_during_init: DBUG_EXECUTE_IF("simulate_slave_delay_at_terminate_bug38694", sleep(5);); mysql_mutex_unlock(&rli->run_lock); // tell the world we are done - /* - Deactivate the parallel replication thread pool, if there are now no more - SQL threads running. Do this here, when we have released all locks, but - while our THD (and current_thd) is still valid. - */ - mysql_mutex_lock(&LOCK_active_mi); - if (opt_slave_parallel_threads > 0 && - master_info_index &&// master_info_index is set to NULL on server shutdown - !master_info_index->any_slave_sql_running()) - rpl_parallel_inactivate_pool(&global_rpl_thread_pool); - mysql_mutex_unlock(&LOCK_active_mi); + rpl_parallel_resize_pool_if_no_slaves(); mysql_mutex_lock(&LOCK_thread_count); delete thd; @@ -6070,6 +6078,7 @@ err: void end_relay_log_info(Relay_log_info* rli) { + mysql_mutex_t *log_lock; DBUG_ENTER("end_relay_log_info"); if (!rli->inited) @@ -6087,8 +6096,11 @@ void end_relay_log_info(Relay_log_info* rli) rli->cur_log_fd = -1; } rli->inited = 0; + log_lock= rli->relay_log.get_log_lock(); + mysql_mutex_lock(log_lock); rli->relay_log.close(LOG_CLOSE_INDEX | LOG_CLOSE_STOP_EVENT); rli->relay_log.harvest_bytes_written(&rli->log_space_total); + mysql_mutex_unlock(log_lock); /* Delete the slave's temporary tables from memory. In the future there will be other actions than this, to ensure persistance @@ -6240,7 +6252,7 @@ static int connect_to_master(THD* thd, MYSQL* mysql, Master_info* mi, suppress_warnings= 0; mi->report(ERROR_LEVEL, last_errno, NULL, "error %s to master '%s@%s:%d'" - " - retry-time: %d retries: %lu message: %s", + " - retry-time: %d maximum-retries: %lu message: %s", (reconnect ? "reconnecting" : "connecting"), mi->user, mi->host, mi->port, mi->connect_retry, master_retry_count, @@ -6804,9 +6816,12 @@ static Log_event* next_event(rpl_group_info *rgi, ulonglong *event_size) } rli->event_relay_log_pos = BIN_LOG_HEADER_SIZE; strmake_buf(rli->event_relay_log_name,rli->linfo.log_file_name); - flush_relay_log_info(rli); + if (flush_relay_log_info(rli)) + { + errmsg= "error flushing relay log"; + goto err; + } } - /* Now we want to open this next log. To know if it's a hot log (the one being written by the I/O thread now) or a cold log, we can use diff --git a/sql/slave.h b/sql/slave.h index c6b78b96aca..abcb4df984b 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -213,13 +213,12 @@ bool rpl_master_erroneous_autoinc(THD* thd); const char *print_slave_db_safe(const char *db); void skip_load_data_infile(NET* net); +void slave_prepare_for_shutdown(); void end_slave(); /* release slave threads */ void close_active_mi(); /* clean up slave threads data */ void clear_until_condition(Relay_log_info* rli); void clear_slave_error(Relay_log_info* rli); void end_relay_log_info(Relay_log_info* rli); -void lock_slave_threads(Master_info* mi); -void unlock_slave_threads(Master_info* mi); void init_thread_mask(int* mask,Master_info* mi,bool inverse); Format_description_log_event * read_relay_log_description_event(IO_CACHE *cur_log, ulonglong start_pos, diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index be8b0c5a022..d4b23a9fbc2 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -8975,13 +8975,13 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, case USER_ACL: acl_user->user.str= strdup_root(&acl_memroot, user_to->user.str); acl_user->user.length= user_to->user.length; - acl_user->host.hostname= strdup_root(&acl_memroot, user_to->host.str); - acl_user->hostname_length= user_to->host.length; + update_hostname(&acl_user->host, strdup_root(&acl_memroot, user_to->host.str)); + acl_user->hostname_length= strlen(acl_user->host.hostname); break; case DB_ACL: acl_db->user= strdup_root(&acl_memroot, user_to->user.str); - acl_db->host.hostname= strdup_root(&acl_memroot, user_to->host.str); + update_hostname(&acl_db->host, strdup_root(&acl_memroot, user_to->host.str)); break; case COLUMN_PRIVILEGES_HASH: diff --git a/sql/sql_alter.h b/sql/sql_alter.h index 526442e83e2..7114694124b 100644 --- a/sql/sql_alter.h +++ b/sql/sql_alter.h @@ -70,59 +70,56 @@ public: // Set for DISABLE KEYS | ENABLE KEYS static const uint ALTER_KEYS_ONOFF = 1L << 9; - // Set for CONVERT TO CHARACTER SET - static const uint ALTER_CONVERT = 1L << 10; - // Set for FORCE // Set for ENGINE(same engine) // Set by mysql_recreate_table() - static const uint ALTER_RECREATE = 1L << 11; + static const uint ALTER_RECREATE = 1L << 10; // Set for ADD PARTITION - static const uint ALTER_ADD_PARTITION = 1L << 12; + static const uint ALTER_ADD_PARTITION = 1L << 11; // Set for DROP PARTITION - static const uint ALTER_DROP_PARTITION = 1L << 13; + static const uint ALTER_DROP_PARTITION = 1L << 12; // Set for COALESCE PARTITION - static const uint ALTER_COALESCE_PARTITION = 1L << 14; + static const uint ALTER_COALESCE_PARTITION = 1L << 13; // Set for REORGANIZE PARTITION ... INTO - static const uint ALTER_REORGANIZE_PARTITION = 1L << 15; + static const uint ALTER_REORGANIZE_PARTITION = 1L << 14; // Set for partition_options - static const uint ALTER_PARTITION = 1L << 16; + static const uint ALTER_PARTITION = 1L << 15; // Set for LOAD INDEX INTO CACHE ... PARTITION // Set for CACHE INDEX ... PARTITION - static const uint ALTER_ADMIN_PARTITION = 1L << 17; + static const uint ALTER_ADMIN_PARTITION = 1L << 16; // Set for REORGANIZE PARTITION - static const uint ALTER_TABLE_REORG = 1L << 18; + static const uint ALTER_TABLE_REORG = 1L << 17; // Set for REBUILD PARTITION - static const uint ALTER_REBUILD_PARTITION = 1L << 19; + static const uint ALTER_REBUILD_PARTITION = 1L << 18; // Set for partitioning operations specifying ALL keyword - static const uint ALTER_ALL_PARTITION = 1L << 20; + static const uint ALTER_ALL_PARTITION = 1L << 19; // Set for REMOVE PARTITIONING - static const uint ALTER_REMOVE_PARTITIONING = 1L << 21; + static const uint ALTER_REMOVE_PARTITIONING = 1L << 20; // Set for ADD FOREIGN KEY - static const uint ADD_FOREIGN_KEY = 1L << 22; + static const uint ADD_FOREIGN_KEY = 1L << 21; // Set for DROP FOREIGN KEY - static const uint DROP_FOREIGN_KEY = 1L << 23; + static const uint DROP_FOREIGN_KEY = 1L << 22; // Set for EXCHANGE PARITION - static const uint ALTER_EXCHANGE_PARTITION = 1L << 24; + static const uint ALTER_EXCHANGE_PARTITION = 1L << 23; // Set by Sql_cmd_alter_table_truncate_partition::execute() - static const uint ALTER_TRUNCATE_PARTITION = 1L << 25; + static const uint ALTER_TRUNCATE_PARTITION = 1L << 24; // Set for ADD [COLUMN] FIRST | AFTER - static const uint ALTER_COLUMN_ORDER = 1L << 26; + static const uint ALTER_COLUMN_ORDER = 1L << 25; enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE }; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index efe9ac6f7f4..2c7f3147901 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -6672,7 +6672,7 @@ find_field_in_tables(THD *thd, Item_ident *item, if (!table_ref->belong_to_view && !table_ref->belong_to_derived) { - SELECT_LEX *current_sel= thd->lex->current_select; + SELECT_LEX *current_sel= item->context->select_lex; SELECT_LEX *last_select= table_ref->select_lex; bool all_merged= TRUE; for (SELECT_LEX *sl= current_sel; sl && sl!=last_select; @@ -8758,9 +8758,7 @@ fill_record(THD * thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, /* Update virtual fields*/ thd->abort_on_warning= FALSE; if (vcol_table && vcol_table->vfield && - update_virtual_fields(thd, vcol_table, - vcol_table->triggers ? VCOL_UPDATE_ALL : - VCOL_UPDATE_FOR_WRITE)) + update_virtual_fields(thd, vcol_table, VCOL_UPDATE_FOR_WRITE)) goto err; thd->abort_on_warning= save_abort_on_warning; thd->no_errors= save_no_errors; @@ -8821,9 +8819,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields, if (item_field && item_field->field && table && table->vfield) { DBUG_ASSERT(table == item_field->field->table); - result= update_virtual_fields(thd, table, - table->triggers ? VCOL_UPDATE_ALL : - VCOL_UPDATE_FOR_WRITE); + result= update_virtual_fields(thd, table, VCOL_UPDATE_FOR_WRITE); } } } @@ -8908,9 +8904,7 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values, /* Update virtual fields*/ thd->abort_on_warning= FALSE; if (table->vfield && - update_virtual_fields(thd, table, - table->triggers ? VCOL_UPDATE_ALL : - VCOL_UPDATE_FOR_WRITE)) + update_virtual_fields(thd, table, VCOL_UPDATE_FOR_WRITE)) goto err; thd->abort_on_warning= abort_on_warning_saved; DBUG_RETURN(thd->is_error()); @@ -8961,9 +8955,7 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, Field **ptr, { DBUG_ASSERT(table == (*ptr)->table); if (table->vfield) - result= update_virtual_fields(thd, table, - table->triggers ? VCOL_UPDATE_ALL : - VCOL_UPDATE_FOR_WRITE); + result= update_virtual_fields(thd, table, VCOL_UPDATE_FOR_WRITE); } return result; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index b210a4d32dc..57c228900fe 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1272,7 +1272,6 @@ THD::THD() m_internal_handler= NULL; m_binlog_invoker= INVOKER_NONE; - arena_for_cached_items= 0; memset(&invoker_user, 0, sizeof(invoker_user)); memset(&invoker_host, 0, sizeof(invoker_host)); prepare_derived_at_open= FALSE; @@ -6849,7 +6848,13 @@ wait_for_commit::reinit() So in this case, do a re-init of the mutex. In release builds, we want to avoid the overhead of a re-init though. + + To ensure that no one is locking the mutex, we take a lock of it first. + For full explanation, see wait_for_commit::~wait_for_commit() */ + mysql_mutex_lock(&LOCK_wait_commit); + mysql_mutex_unlock(&LOCK_wait_commit); + mysql_mutex_destroy(&LOCK_wait_commit); mysql_mutex_init(key_LOCK_wait_commit, &LOCK_wait_commit, MY_MUTEX_INIT_FAST); #endif diff --git a/sql/sql_class.h b/sql/sql_class.h index aed75d94972..beef22a8140 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -3702,26 +3702,7 @@ public: } } -private: - /* - This reference points to the table arena when the expression - for a virtual column is being evaluated - */ - Query_arena *arena_for_cached_items; - public: - void reset_arena_for_cached_items(Query_arena *new_arena) - { - arena_for_cached_items= new_arena; - } - Query_arena *switch_to_arena_for_cached_items(Query_arena *backup) - { - if (!arena_for_cached_items) - return 0; - set_n_backup_active_arena(arena_for_cached_items, backup); - return backup; - } - void clear_wakeup_ready() { wakeup_ready= false; } /* Sleep waiting for others to wake us up with signal_wakeup_ready(). diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 0a3ff64113f..3f43a33ab7c 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -815,7 +815,8 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) if there exists a table with the name 'db', so let's just do it separately. We know this file exists and needs to be deleted anyway. */ - if (my_delete_with_symlink(path, MYF(0)) && my_errno != ENOENT) + if (my_handler_delete_with_symlink(key_file_misc, path, "", MYF(0)) && + my_errno != ENOENT) { my_error(EE_DELETE, MYF(0), path, my_errno); DBUG_RETURN(true); @@ -1116,9 +1117,9 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp, strxmov(filePath, path, "/", file->name, NullS); /* We ignore ENOENT error in order to skip files that was deleted - by concurrently running statement like REAPIR TABLE ... + by concurrently running statement like REPAIR TABLE ... */ - if (my_delete_with_symlink(filePath, MYF(0)) && + if (my_handler_delete_with_symlink(key_file_misc, filePath, "", MYF(0)) && my_errno != ENOENT) { my_error(EE_DELETE, MYF(0), filePath, my_errno); @@ -1234,7 +1235,7 @@ long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path) continue; } strxmov(filePath, org_path, "/", file->name, NullS); - if (mysql_file_delete_with_symlink(key_file_misc, filePath, MYF(MY_WME))) + if (my_handler_delete_with_symlink(key_file_misc, filePath, "", MYF(MY_WME))) { goto err; } diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 9ac7e64ec23..ad5bc23a31b 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -548,9 +548,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ! thd->is_error()) { if (table->vfield) - update_virtual_fields(thd, table, - table->triggers ? VCOL_UPDATE_ALL : - VCOL_UPDATE_FOR_READ); + update_virtual_fields(thd, table, VCOL_UPDATE_FOR_READ); thd->inc_examined_row_count(1); // thd->is_error() is tested to disallow delete row on error if (!select || select->skip_record(thd) > 0) @@ -1309,4 +1307,3 @@ bool multi_delete::send_eof() } return 0; } - diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index f2674cb8dab..af5b016df9d 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -447,6 +447,9 @@ bool mysql_derived_merge(THD *thd, LEX *lex, TABLE_LIST *derived) { Item *expr= derived->on_expr; expr= and_conds(expr, dt_select->join ? dt_select->join->conds : 0); + if (expr) + expr->top_level_item(); + if (expr && (derived->prep_on_expr || expr != derived->on_expr)) { derived->on_expr= expr; diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index f701c424f63..7584b42c904 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -589,6 +589,11 @@ void JOIN_CACHE::create_remaining_fields() { MY_BITMAP *rem_field_set; TABLE *table= tab->table; +#if MYSQL_VERSION_ID < 100204 + empty_record(table); +#else +#error remove +#endif if (all_read_fields) rem_field_set= table->read_set; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index c906a941867..3db0f37974a 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2119,12 +2119,12 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, #endif case SCH_COLUMNS: case SCH_STATISTICS: - { #ifdef DONT_ALLOW_SHOW_COMMANDS my_message(ER_NOT_ALLOWED_COMMAND, ER(ER_NOT_ALLOWED_COMMAND), MYF(0)); /* purecov: inspected */ DBUG_RETURN(1); #else + { DBUG_ASSERT(table_ident); TABLE_LIST **query_tables_last= lex->query_tables_last; schema_select_lex= new SELECT_LEX(); @@ -2424,7 +2424,7 @@ static bool wsrep_is_show_query(enum enum_sql_command command) int mysql_execute_command(THD *thd) { - int res= FALSE; + int res= 0; int up_result= 0; LEX *lex= thd->lex; /* first SELECT_LEX (have special meaning for many of non-SELECTcommands) */ @@ -2983,10 +2983,17 @@ case SQLCOM_PREPARE: if (check_global_access(thd, SUPER_ACL)) goto error; + /* + In this code it's ok to use LOCK_active_mi as we are adding new things + into master_info_index + */ mysql_mutex_lock(&LOCK_active_mi); - if (!master_info_index) + { + mysql_mutex_unlock(&LOCK_active_mi); + my_error(ER_SERVER_SHUTDOWN, MYF(0)); goto error; + } mi= master_info_index->get_master_info(&lex_mi->connection_name, Sql_condition::WARN_LEVEL_NOTE); @@ -3015,7 +3022,7 @@ case SQLCOM_PREPARE: If new master was not added, we still need to free mi. */ if (master_info_added) - master_info_index->remove_master_info(&lex_mi->connection_name); + master_info_index->remove_master_info(mi); else delete mi; } @@ -3033,22 +3040,24 @@ case SQLCOM_PREPARE: /* Accept one of two privileges */ if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL)) goto error; - mysql_mutex_lock(&LOCK_active_mi); if (lex->verbose) + { + mysql_mutex_lock(&LOCK_active_mi); res= show_all_master_info(thd); + mysql_mutex_unlock(&LOCK_active_mi); + } else { LEX_MASTER_INFO *lex_mi= &thd->lex->mi; Master_info *mi; - mi= master_info_index->get_master_info(&lex_mi->connection_name, - Sql_condition::WARN_LEVEL_ERROR); - if (mi != NULL) + if ((mi= get_master_info(&lex_mi->connection_name, + Sql_condition::WARN_LEVEL_ERROR))) { res= show_master_info(thd, mi, 0); + mi->release(); } } - mysql_mutex_unlock(&LOCK_active_mi); break; } case SQLCOM_SHOW_MASTER_STAT: @@ -3383,22 +3392,23 @@ end_with_restore_list: load_error= rpl_load_gtid_slave_state(thd); - mysql_mutex_lock(&LOCK_active_mi); - - if ((mi= (master_info_index-> - get_master_info(&lex_mi->connection_name, - Sql_condition::WARN_LEVEL_ERROR)))) + /* + We don't need to ensure that only one user is using master_info + as start_slave is protected against simultaneous usage + */ + if ((mi= get_master_info(&lex_mi->connection_name, + Sql_condition::WARN_LEVEL_ERROR))) { if (load_error) { /* - We cannot start a slave using GTID if we cannot load the GTID position - from the mysql.gtid_slave_pos table. But we can allow non-GTID - replication (useful eg. during upgrade). + We cannot start a slave using GTID if we cannot load the + GTID position from the mysql.gtid_slave_pos table. But we + can allow non-GTID replication (useful eg. during upgrade). */ if (mi->using_gtid != Master_info::USE_GTID_NO) { - mysql_mutex_unlock(&LOCK_active_mi); + mi->release(); break; } else @@ -3406,8 +3416,8 @@ end_with_restore_list: } if (!start_slave(thd, mi, 1 /* net report*/)) my_ok(thd); + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); break; } case SQLCOM_SLAVE_STOP: @@ -3437,13 +3447,17 @@ end_with_restore_list: } lex_mi= &thd->lex->mi; - mysql_mutex_lock(&LOCK_active_mi); - if ((mi= (master_info_index-> - get_master_info(&lex_mi->connection_name, - Sql_condition::WARN_LEVEL_ERROR)))) - if (!stop_slave(thd, mi, 1/* net report*/)) + if ((mi= get_master_info(&lex_mi->connection_name, + Sql_condition::WARN_LEVEL_ERROR))) + { + if (stop_slave(thd, mi, 1/* net report*/)) + res= 1; + mi->release(); + if (rpl_parallel_resize_pool_if_no_slaves()) + res= 1; + if (!res) my_ok(thd); - mysql_mutex_unlock(&LOCK_active_mi); + } break; } case SQLCOM_SLAVE_ALL_START: @@ -4744,11 +4758,13 @@ end_with_restore_list: reload_acl_and_cache binlog interactions failed */ res= 1; - } + } if (!res) my_ok(thd); } + else + res= 1; // reload_acl_and_cache failed #ifdef HAVE_REPLICATION if (lex->type & REFRESH_READ_LOCK) rpl_unpause_after_ftwrl(thd); @@ -7123,7 +7139,7 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, #endif /* WITH_WSREP */ /* - When you modify mysql_parse(), you may need to mofify + When you modify mysql_parse(), you may need to modify mysql_test_parse_for_slave() in this same file. */ @@ -9113,48 +9129,24 @@ bool check_ident_length(LEX_STRING *ident) } -C_MODE_START - /* Check if path does not contain mysql data home directory SYNOPSIS - test_if_data_home_dir() - dir directory + path_starts_from_data_home_dir() + dir directory, with all symlinks resolved RETURN VALUES 0 ok 1 error ; Given path contains data directory */ +extern "C" { -int test_if_data_home_dir(const char *dir) +int path_starts_from_data_home_dir(const char *path) { - char path[FN_REFLEN]; - int dir_len; - DBUG_ENTER("test_if_data_home_dir"); - - if (!dir) - DBUG_RETURN(0); + int dir_len= strlen(path); + DBUG_ENTER("path_starts_from_data_home_dir"); - /* - data_file_name and index_file_name include the table name without - extension. Mostly this does not refer to an existing file. When - comparing data_file_name or index_file_name against the data - directory, we try to resolve all symbolic links. On some systems, - we use realpath(3) for the resolution. This returns ENOENT if the - resolved path does not refer to an existing file. my_realpath() - does then copy the requested path verbatim, without symlink - resolution. Thereafter the comparison can fail even if the - requested path is within the data directory. E.g. if symlinks to - another file system are used. To make realpath(3) return the - resolved path, we strip the table name and compare the directory - path only. If the directory doesn't exist either, table creation - will fail anyway. - */ - - (void) fn_format(path, dir, "", "", - (MY_RETURN_REAL_PATH|MY_RESOLVE_SYMLINKS)); - dir_len= strlen(path); if (mysql_unpacked_real_data_home_len<= dir_len) { if (dir_len > mysql_unpacked_real_data_home_len && @@ -9182,7 +9174,31 @@ int test_if_data_home_dir(const char *dir) DBUG_RETURN(0); } -C_MODE_END +} + +/* + Check if path does not contain mysql data home directory + + SYNOPSIS + test_if_data_home_dir() + dir directory + + RETURN VALUES + 0 ok + 1 error ; Given path contains data directory +*/ + +int test_if_data_home_dir(const char *dir) +{ + char path[FN_REFLEN]; + DBUG_ENTER("test_if_data_home_dir"); + + if (!dir) + DBUG_RETURN(0); + + (void) fn_format(path, dir, "", "", MY_RETURN_REAL_PATH); + DBUG_RETURN(path_starts_from_data_home_dir(path)); +} int error_if_data_home_dir(const char *path, const char *what) diff --git a/sql/sql_parse.h b/sql/sql_parse.h index 0e60a5cc884..368bba91c20 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -33,7 +33,8 @@ enum enum_mysql_completiontype { COMMIT_RELEASE=-1, COMMIT=0, COMMIT_AND_CHAIN=6 }; -extern "C" int test_if_data_home_dir(const char *dir); +extern "C" int path_starts_from_data_home_dir(const char *dir); +int test_if_data_home_dir(const char *dir); int error_if_data_home_dir(const char *path, const char *what); bool multi_update_precheck(THD *thd, TABLE_LIST *tables); diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc index a83e91680da..704e2f84437 100644 --- a/sql/sql_reload.cc +++ b/sql/sql_reload.cc @@ -181,24 +181,20 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options, slave is not likely to have the same connection names. */ tmp_write_to_binlog= 0; - mysql_mutex_lock(&LOCK_active_mi); - if (master_info_index) + + if (!(mi= (get_master_info(&connection_name, + Sql_condition::WARN_LEVEL_ERROR)))) { - if (!(mi= (master_info_index-> - get_master_info(&connection_name, - Sql_condition::WARN_LEVEL_ERROR)))) - { - result= 1; - } - else - { - mysql_mutex_lock(&mi->data_lock); - if (rotate_relay_log(mi)) - *write_to_binlog= -1; - mysql_mutex_unlock(&mi->data_lock); - } + result= 1; + } + else + { + mysql_mutex_lock(&mi->data_lock); + if (rotate_relay_log(mi)) + *write_to_binlog= -1; + mysql_mutex_unlock(&mi->data_lock); + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); #endif } #ifdef HAVE_QUERY_CACHE @@ -377,27 +373,33 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options, LEX_MASTER_INFO* lex_mi= &thd->lex->mi; Master_info *mi; tmp_write_to_binlog= 0; - mysql_mutex_lock(&LOCK_active_mi); - if (master_info_index) + + if (!(mi= get_master_info(&lex_mi->connection_name, + Sql_condition::WARN_LEVEL_ERROR))) { - if (!(mi= (master_info_index-> - get_master_info(&lex_mi->connection_name, - Sql_condition::WARN_LEVEL_ERROR)))) - { - result= 1; - } - else if (reset_slave(thd, mi)) + result= 1; + } + else + { + /* The following will fail if slave is running */ + if (reset_slave(thd, mi)) { + mi->release(); /* NOTE: my_error() has been already called by reset_slave(). */ result= 1; } else if (mi->connection_name.length && thd->lex->reset_slave_info.all) { /* If not default connection and 'all' is used */ - master_info_index->remove_master_info(&mi->connection_name); + mi->release(); + mysql_mutex_lock(&LOCK_active_mi); + if (master_info_index->remove_master_info(mi)) + result= 1; + mysql_mutex_unlock(&LOCK_active_mi); } + else + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); } #endif if (options & REFRESH_USER_RESOURCES) diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 4ee55b59c11..ece137aa380 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -2833,7 +2833,16 @@ int start_slave(THD* thd , Master_info* mi, bool net_report) relay_log_info_file, 0, &mi->cmp_connection_name); - lock_slave_threads(mi); // this allows us to cleanly read slave_running + mi->lock_slave_threads(); + if (mi->killed) + { + /* connection was deleted while we waited for lock_slave_threads */ + mi->unlock_slave_threads(); + my_error(WARN_NO_MASTER_INFO, mi->connection_name.length, + mi->connection_name.str); + DBUG_RETURN(-1); + } + // Get a mask of _stopped_ threads init_thread_mask(&thread_mask,mi,1 /* inverse */); @@ -2968,7 +2977,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report) ER(ER_UNTIL_COND_IGNORED)); if (!slave_errno) - slave_errno = start_slave_threads(0 /*no mutex */, + slave_errno = start_slave_threads(1, 1 /* wait for start */, mi, master_info_file_tmp, @@ -2984,7 +2993,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report) } err: - unlock_slave_threads(mi); + mi->unlock_slave_threads(); #ifdef WITH_WSREP if (WSREP(thd)) @@ -3033,8 +3042,12 @@ int stop_slave(THD* thd, Master_info* mi, bool net_report ) DBUG_RETURN(-1); THD_STAGE_INFO(thd, stage_killing_slave); int thread_mask; - lock_slave_threads(mi); - // Get a mask of _running_ threads + mi->lock_slave_threads(); + /* + Get a mask of _running_ threads. + We don't have to test for mi->killed as the thread_mask will take care + of checking if threads exists + */ init_thread_mask(&thread_mask,mi,0 /* not inverse*/); /* Below we will stop all running threads. @@ -3047,8 +3060,7 @@ int stop_slave(THD* thd, Master_info* mi, bool net_report ) if (thread_mask) { - slave_errno= terminate_slave_threads(mi,thread_mask, - 1 /*skip lock */); + slave_errno= terminate_slave_threads(mi,thread_mask, 0 /* get lock */); } else { @@ -3057,7 +3069,8 @@ int stop_slave(THD* thd, Master_info* mi, bool net_report ) push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SLAVE_WAS_NOT_RUNNING, ER(ER_SLAVE_WAS_NOT_RUNNING)); } - unlock_slave_threads(mi); + + mi->unlock_slave_threads(); if (slave_errno) { @@ -3092,11 +3105,20 @@ int reset_slave(THD *thd, Master_info* mi) char relay_log_info_file_tmp[FN_REFLEN]; DBUG_ENTER("reset_slave"); - lock_slave_threads(mi); + mi->lock_slave_threads(); + if (mi->killed) + { + /* connection was deleted while we waited for lock_slave_threads */ + mi->unlock_slave_threads(); + my_error(WARN_NO_MASTER_INFO, mi->connection_name.length, + mi->connection_name.str); + DBUG_RETURN(-1); + } + init_thread_mask(&thread_mask,mi,0 /* not inverse */); if (thread_mask) // We refuse if any slave thread is running { - unlock_slave_threads(mi); + mi->unlock_slave_threads(); my_error(ER_SLAVE_MUST_STOP, MYF(0), (int) mi->connection_name.length, mi->connection_name.str); DBUG_RETURN(ER_SLAVE_MUST_STOP); @@ -3161,7 +3183,7 @@ int reset_slave(THD *thd, Master_info* mi) RUN_HOOK(binlog_relay_io, after_reset_slave, (thd, mi)); err: - unlock_slave_threads(mi); + mi->unlock_slave_threads(); if (error) my_error(sql_errno, MYF(0), errmsg); DBUG_RETURN(error); @@ -3274,8 +3296,8 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added) LEX_MASTER_INFO* lex_mi= &thd->lex->mi; DBUG_ENTER("change_master"); - mysql_mutex_assert_owner(&LOCK_active_mi); DBUG_ASSERT(master_info_index); + mysql_mutex_assert_owner(&LOCK_active_mi); *master_info_added= false; /* @@ -3295,7 +3317,16 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added) lex_mi->port)) DBUG_RETURN(TRUE); - lock_slave_threads(mi); + mi->lock_slave_threads(); + if (mi->killed) + { + /* connection was deleted while we waited for lock_slave_threads */ + mi->unlock_slave_threads(); + my_error(WARN_NO_MASTER_INFO, mi->connection_name.length, + mi->connection_name.str); + DBUG_RETURN(TRUE); + } + init_thread_mask(&thread_mask,mi,0 /*not inverse*/); if (thread_mask) // We refuse if any slave thread is running { @@ -3597,12 +3628,13 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added) in-memory value at restart (thus causing errors, as the old relay log does not exist anymore). */ - flush_relay_log_info(&mi->rli); + if (flush_relay_log_info(&mi->rli)) + ret= 1; mysql_cond_broadcast(&mi->data_cond); mysql_mutex_unlock(&mi->rli.data_lock); err: - unlock_slave_threads(mi); + mi->unlock_slave_threads(); if (ret == FALSE) my_ok(thd); DBUG_RETURN(ret); @@ -3655,7 +3687,6 @@ bool mysql_show_binlog_events(THD* thd) int old_max_allowed_packet= thd->variables.max_allowed_packet; Master_info *mi= 0; LOG_INFO linfo; - DBUG_ENTER("mysql_show_binlog_events"); Log_event::init_show_field_list(&field_list); @@ -3683,13 +3714,9 @@ bool mysql_show_binlog_events(THD* thd) } else /* showing relay log contents */ { - mysql_mutex_lock(&LOCK_active_mi); - if (!master_info_index || - !(mi= master_info_index-> - get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_ERROR))) + if (!(mi= get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_ERROR))) { - mysql_mutex_unlock(&LOCK_active_mi); DBUG_RETURN(TRUE); } binary_log= &(mi->rli.relay_log); @@ -3709,7 +3736,7 @@ bool mysql_show_binlog_events(THD* thd) if (mi) { /* We can unlock the mutex as we have a lock on the file */ - mysql_mutex_unlock(&LOCK_active_mi); + mi->release(); mi= 0; } @@ -3731,6 +3758,7 @@ bool mysql_show_binlog_events(THD* thd) goto err; } + /* These locks is here to enable syncronization with log_in_use() */ mysql_mutex_lock(&LOCK_thread_count); thd->current_linfo = &linfo; mysql_mutex_unlock(&LOCK_thread_count); @@ -3808,7 +3836,7 @@ bool mysql_show_binlog_events(THD* thd) mysql_mutex_unlock(log_lock); } else if (mi) - mysql_mutex_unlock(&LOCK_active_mi); + mi->release(); // Check that linfo is still on the function scope. DEBUG_SYNC(thd, "after_show_binlog_events"); @@ -3829,8 +3857,9 @@ err: else my_eof(thd); + /* These locks is here to enable syncronization with log_in_use() */ mysql_mutex_lock(&LOCK_thread_count); - thd->current_linfo = 0; + thd->current_linfo= 0; mysql_mutex_unlock(&LOCK_thread_count); thd->variables.max_allowed_packet= old_max_allowed_packet; DBUG_RETURN(ret); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 7850c7a0dcf..d459d0b88bf 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8510,8 +8510,6 @@ get_best_combination(JOIN *join) form=join->table[tablenr]=j->table; used_tables|= form->map; form->reginfo.join_tab=j; - if (!*j->on_expr_ref) - form->reginfo.not_exists_optimize=0; // Only with LEFT JOIN DBUG_PRINT("info",("type: %d", j->type)); if (j->type == JT_CONST) goto loop_end; // Handled in make_join_stat.. @@ -9355,7 +9353,10 @@ make_outerjoin_info(JOIN *join) tab->cond_equal= tbl->cond_equal; if (embedding && !embedding->is_active_sjm()) tab->first_upper= embedding->nested_join->first_nested; - } + } + else if (!embedding) + tab->table->reginfo.not_exists_optimize= 0; + for ( ; embedding ; embedding= embedding->embedding) { if (embedding->is_active_sjm()) @@ -9365,7 +9366,10 @@ make_outerjoin_info(JOIN *join) } /* Ignore sj-nests: */ if (!(embedding->on_expr && embedding->outer_join)) + { + tab->table->reginfo.not_exists_optimize= 0; continue; + } NESTED_JOIN *nested_join= embedding->nested_join; if (!nested_join->counter) { @@ -9381,17 +9385,10 @@ make_outerjoin_info(JOIN *join) } if (!tab->first_inner) tab->first_inner= nested_join->first_nested; - if (tab->table->reginfo.not_exists_optimize) - tab->first_inner->table->reginfo.not_exists_optimize= 1; if (++nested_join->counter < nested_join->n_tables) break; /* Table tab is the last inner table for nested join. */ nested_join->first_nested->last_inner= tab; - if (tab->first_inner->table->reginfo.not_exists_optimize) - { - for (JOIN_TAB *join_tab= tab->first_inner; join_tab <= tab; join_tab++) - join_tab->table->reginfo.not_exists_optimize= 1; - } } } DBUG_RETURN(FALSE); @@ -15510,7 +15507,9 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, if (new_field) new_field->init(table); - if (copy_func && item->real_item()->is_result_field()) + if (copy_func && + (item->is_result_field() || + (item->real_item()->is_result_field()))) *((*copy_func)++) = item; // Save for copy_funcs if (modify_item) item->set_result_field(new_field); @@ -17982,32 +17981,41 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, first_unmatched->found= 1; for (JOIN_TAB *tab= first_unmatched; tab <= join_tab; tab++) { + /* + Check whether 'not exists' optimization can be used here. + If tab->table->reginfo.not_exists_optimize is set to true + then WHERE contains a conjunctive predicate IS NULL over + a non-nullable field of tab. When activated this predicate + will filter out all records with matches for the left part + of the outer join whose inner tables start from the + first_unmatched table and include table tab. To safely use + 'not exists' optimization we have to check that the + IS NULL predicate is really activated, i.e. all guards + that wrap it are in the 'open' state. + */ + bool not_exists_opt_is_applicable= + tab->table->reginfo.not_exists_optimize; + for (JOIN_TAB *first_upper= first_unmatched->first_upper; + not_exists_opt_is_applicable && first_upper; + first_upper= first_upper->first_upper) + { + if (!first_upper->found) + not_exists_opt_is_applicable= false; + } /* Check all predicates that has just been activated. */ /* Actually all predicates non-guarded by first_unmatched->found will be re-evaluated again. It could be fixed, but, probably, it's not worth doing now. */ - /* - not_exists_optimize has been created from a - select_cond containing 'is_null'. This 'is_null' - predicate is still present on any 'tab' with - 'not_exists_optimize'. Furthermore, the usual rules - for condition guards also applies for - 'not_exists_optimize' -> When 'is_null==false' we - know all cond. guards are open and we can apply - the 'not_exists_optimize'. - */ - DBUG_ASSERT(!(tab->table->reginfo.not_exists_optimize && - !tab->select_cond)); - if (tab->select_cond && !tab->select_cond->val_int()) { /* The condition attached to table tab is false */ - if (tab == join_tab) { found= 0; + if (not_exists_opt_is_applicable) + DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS); } else { @@ -18016,21 +18024,10 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, not to the last table of the current nest level. */ join->return_tab= tab; - } - - if (tab->table->reginfo.not_exists_optimize) - { - /* - When not_exists_optimize is set: No need to further - explore more rows of 'tab' for this partial result. - Any found 'tab' matches are known to evaluate to 'false'. - Returning .._NO_MORE_ROWS will skip rem. 'tab' rows. - */ - DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS); - } - else if (tab != join_tab) - { - DBUG_RETURN(NESTED_LOOP_OK); + if (not_exists_opt_is_applicable) + DBUG_RETURN(NESTED_LOOP_NO_MORE_ROWS); + else + DBUG_RETURN(NESTED_LOOP_OK); } } } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index aec81e17bd3..0732d6b74f0 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4149,7 +4149,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, Field::utype type= (Field::utype) MTYP_TYPENR(sql_field->unireg_check); if (thd->variables.sql_mode & MODE_NO_ZERO_DATE && - !sql_field->def && + !sql_field->def && !sql_field->vcol_info && is_timestamp_type(sql_field->sql_type) && (sql_field->flags & NOT_NULL_FLAG) && (type == Field::NONE || type == Field::TIMESTAMP_UN_FIELD)) @@ -6492,6 +6492,9 @@ static bool fill_alter_inplace_info(THD *thd, new_key->user_defined_key_parts)) goto index_changed; + if (table_key->block_size != new_key->block_size) + goto index_changed; + if (engine_options_differ(table_key->option_struct, new_key->option_struct, table->file->ht->index_options)) goto index_changed; diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index fec90d7ddf3..327b5bb0260 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -2230,6 +2230,9 @@ void Table_triggers_list::mark_fields_used(trg_event_type event) bitmap_set_bit(trigger_table->read_set, trg_field->field_idx); if (trg_field->get_settable_routine_parameter()) bitmap_set_bit(trigger_table->write_set, trg_field->field_idx); + if (trigger_table->field[trg_field->field_idx]->vcol_info) + trigger_table->mark_virtual_col(trigger_table-> + field[trg_field->field_idx]); } } } diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 65a94d1da60..1a136e5158b 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -629,9 +629,7 @@ int mysql_update(THD *thd, while (!(error=info.read_record(&info)) && !thd->killed) { if (table->vfield) - update_virtual_fields(thd, table, - table->triggers ? VCOL_UPDATE_ALL : - VCOL_UPDATE_FOR_READ); + update_virtual_fields(thd, table, VCOL_UPDATE_FOR_READ); thd->inc_examined_row_count(1); if (!select || (error= select->skip_record(thd)) > 0) { @@ -744,9 +742,7 @@ int mysql_update(THD *thd, while (!(error=info.read_record(&info)) && !thd->killed) { if (table->vfield) - update_virtual_fields(thd, table, - table->triggers ? VCOL_UPDATE_ALL : - VCOL_UPDATE_FOR_READ); + update_virtual_fields(thd, table, VCOL_UPDATE_FOR_READ); thd->inc_examined_row_count(1); if (!select || select->skip_record(thd) > 0) { @@ -2407,8 +2403,7 @@ int multi_update::do_updates() if (table->default_field && (error= table->update_default_fields())) goto err2; if (table->vfield && - update_virtual_fields(thd, table, - (table->triggers ? VCOL_UPDATE_ALL : VCOL_UPDATE_FOR_WRITE))) + update_virtual_fields(thd, table, VCOL_UPDATE_FOR_WRITE)) goto err2; if ((error= cur_table->view_check_option(thd, ignore)) != VIEW_CHECK_OK) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index a7408f7bf50..b5d9604fd41 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -7649,7 +7649,7 @@ alter_list_item: LEX *lex= Lex; if (lex->create_info.add_alter_list_item_convert_to_charset($5)) MYSQL_YYABORT; - lex->alter_info.flags|= Alter_info::ALTER_CONVERT; + lex->alter_info.flags|= Alter_info::ALTER_OPTIONS; } | create_table_options_space_separated { diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 379294fa7b2..6457a8597bd 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1548,7 +1548,6 @@ bool Sys_var_gtid_slave_pos::do_check(THD *thd, set_var *var) { String str, *res; - bool running; DBUG_ASSERT(var->type == OPT_GLOBAL); @@ -1559,11 +1558,7 @@ Sys_var_gtid_slave_pos::do_check(THD *thd, set_var *var) return true; } - mysql_mutex_lock(&LOCK_active_mi); - running= (!master_info_index || - master_info_index->give_error_if_slave_running()); - mysql_mutex_unlock(&LOCK_active_mi); - if (running) + if (give_error_if_slave_running(0)) return true; if (!(res= var->value->val_str(&str))) return true; @@ -1601,7 +1596,7 @@ Sys_var_gtid_slave_pos::global_update(THD *thd, set_var *var) mysql_mutex_unlock(&LOCK_global_system_variables); mysql_mutex_lock(&LOCK_active_mi); - if (!master_info_index || master_info_index->give_error_if_slave_running()) + if (give_error_if_slave_running(1)) err= true; else err= rpl_gtid_pos_update(thd, var->save_result.string_value.str, @@ -1787,16 +1782,7 @@ Sys_var_last_gtid::session_value_ptr(THD *thd, LEX_STRING *base) static bool check_slave_parallel_threads(sys_var *self, THD *thd, set_var *var) { - bool running; - - mysql_mutex_lock(&LOCK_active_mi); - running= (!master_info_index || - master_info_index->give_error_if_slave_running()); - mysql_mutex_unlock(&LOCK_active_mi); - if (running) - return true; - - return false; + return give_error_if_slave_running(0); } static bool @@ -1805,10 +1791,7 @@ fix_slave_parallel_threads(sys_var *self, THD *thd, enum_var_type type) bool err; mysql_mutex_unlock(&LOCK_global_system_variables); - mysql_mutex_lock(&LOCK_active_mi); - err= (!master_info_index || - master_info_index->give_error_if_slave_running()); - mysql_mutex_unlock(&LOCK_active_mi); + err= give_error_if_slave_running(0); mysql_mutex_lock(&LOCK_global_system_variables); return err; @@ -1831,16 +1814,7 @@ static Sys_var_ulong Sys_slave_parallel_threads( static bool check_slave_domain_parallel_threads(sys_var *self, THD *thd, set_var *var) { - bool running; - - mysql_mutex_lock(&LOCK_active_mi); - running= (!master_info_index || - master_info_index->give_error_if_slave_running()); - mysql_mutex_unlock(&LOCK_active_mi); - if (running) - return true; - - return false; + return give_error_if_slave_running(0); } static bool @@ -1849,13 +1823,10 @@ fix_slave_domain_parallel_threads(sys_var *self, THD *thd, enum_var_type type) bool running; mysql_mutex_unlock(&LOCK_global_system_variables); - mysql_mutex_lock(&LOCK_active_mi); - running= (!master_info_index || - master_info_index->give_error_if_slave_running()); - mysql_mutex_unlock(&LOCK_active_mi); + running= give_error_if_slave_running(0); mysql_mutex_lock(&LOCK_global_system_variables); - return running ? true : false; + return running; } @@ -1886,16 +1857,7 @@ static Sys_var_ulong Sys_slave_parallel_max_queued( static bool check_gtid_ignore_duplicates(sys_var *self, THD *thd, set_var *var) { - bool running; - - mysql_mutex_lock(&LOCK_active_mi); - running= (!master_info_index || - master_info_index->give_error_if_slave_running()); - mysql_mutex_unlock(&LOCK_active_mi); - if (running) - return true; - - return false; + return give_error_if_slave_running(0); } static bool @@ -1904,13 +1866,10 @@ fix_gtid_ignore_duplicates(sys_var *self, THD *thd, enum_var_type type) bool running; mysql_mutex_unlock(&LOCK_global_system_variables); - mysql_mutex_lock(&LOCK_active_mi); - running= (!master_info_index || - master_info_index->give_error_if_slave_running()); - mysql_mutex_unlock(&LOCK_active_mi); + running= give_error_if_slave_running(0); mysql_mutex_lock(&LOCK_global_system_variables); - return running ? true : false; + return running; } @@ -2858,10 +2817,8 @@ Sys_var_replicate_events_marked_for_skip::global_update(THD *thd, set_var *var) DBUG_ENTER("Sys_var_replicate_events_marked_for_skip::global_update"); mysql_mutex_unlock(&LOCK_global_system_variables); - mysql_mutex_lock(&LOCK_active_mi); - if (master_info_index && !master_info_index->give_error_if_slave_running()) + if (!give_error_if_slave_running(0)) result= Sys_var_enum::global_update(thd, var); - mysql_mutex_unlock(&LOCK_active_mi); mysql_mutex_lock(&LOCK_global_system_variables); DBUG_RETURN(result); } @@ -4137,19 +4094,16 @@ bool Sys_var_rpl_filter::global_update(THD *thd, set_var *var) Master_info *mi; mysql_mutex_unlock(&LOCK_global_system_variables); - mysql_mutex_lock(&LOCK_active_mi); if (!var->base.length) // no base name { - mi= master_info_index-> - get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_ERROR); + mi= get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_ERROR); } else // has base name { - mi= master_info_index-> - get_master_info(&var->base, - Sql_condition::WARN_LEVEL_WARN); + mi= get_master_info(&var->base, + Sql_condition::WARN_LEVEL_WARN); } if (mi) @@ -4157,17 +4111,17 @@ bool Sys_var_rpl_filter::global_update(THD *thd, set_var *var) if (mi->rli.slave_running) { my_error(ER_SLAVE_MUST_STOP, MYF(0), - mi->connection_name.length, - mi->connection_name.str); + mi->connection_name.length, + mi->connection_name.str); result= true; } else { result= set_filter_value(var->save_result.string_value.str, mi); } + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); mysql_mutex_lock(&LOCK_global_system_variables); return result; } @@ -4175,8 +4129,10 @@ bool Sys_var_rpl_filter::global_update(THD *thd, set_var *var) bool Sys_var_rpl_filter::set_filter_value(const char *value, Master_info *mi) { bool status= true; - Rpl_filter* rpl_filter= mi ? mi->rpl_filter : global_rpl_filter; + Rpl_filter* rpl_filter= mi->rpl_filter; + /* Proctect against other threads */ + mysql_mutex_lock(&LOCK_active_mi); switch (opt_id) { case OPT_REPLICATE_DO_DB: status= rpl_filter->set_do_db(value); @@ -4197,7 +4153,7 @@ bool Sys_var_rpl_filter::set_filter_value(const char *value, Master_info *mi) status= rpl_filter->set_wild_ignore_table(value); break; } - + mysql_mutex_unlock(&LOCK_active_mi); return status; } @@ -4210,29 +4166,24 @@ uchar *Sys_var_rpl_filter::global_value_ptr(THD *thd, LEX_STRING *base) Rpl_filter *rpl_filter; mysql_mutex_unlock(&LOCK_global_system_variables); - mysql_mutex_lock(&LOCK_active_mi); if (!base->length) // no base name { - mi= master_info_index-> - get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_ERROR); + mi= get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_ERROR); } else // has base name - { - mi= master_info_index-> - get_master_info(base, - Sql_condition::WARN_LEVEL_WARN); - } - mysql_mutex_lock(&LOCK_global_system_variables); + mi= get_master_info(base, Sql_condition::WARN_LEVEL_WARN); if (!mi) { - mysql_mutex_unlock(&LOCK_active_mi); + mysql_mutex_lock(&LOCK_global_system_variables); return 0; } + rpl_filter= mi->rpl_filter; tmp.length(0); + mysql_mutex_lock(&LOCK_active_mi); switch (opt_id) { case OPT_REPLICATE_DO_DB: rpl_filter->get_do_db(&tmp); @@ -4253,9 +4204,12 @@ uchar *Sys_var_rpl_filter::global_value_ptr(THD *thd, LEX_STRING *base) rpl_filter->get_wild_ignore_table(&tmp); break; } + mysql_mutex_unlock(&LOCK_active_mi); + mysql_mutex_lock(&LOCK_global_system_variables); + + mi->release(); ret= (uchar *) thd->strmake(tmp.ptr(), tmp.length()); - mysql_mutex_unlock(&LOCK_active_mi); return ret; } @@ -4326,17 +4280,12 @@ get_master_info_ulonglong_value(THD *thd, ptrdiff_t offset) Master_info *mi; ulonglong res= 0; // Default value mysql_mutex_unlock(&LOCK_global_system_variables); - mysql_mutex_lock(&LOCK_active_mi); - mi= master_info_index-> - get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_WARN); - if (mi) + if ((mi= get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_WARN))) { - mysql_mutex_lock(&mi->rli.data_lock); res= *((ulonglong*) (((uchar*) mi) + master_info_offset)); - mysql_mutex_unlock(&mi->rli.data_lock); + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); mysql_mutex_lock(&LOCK_global_system_variables); return res; } @@ -4351,19 +4300,16 @@ bool update_multi_source_variable(sys_var *self_var, THD *thd, if (type == OPT_GLOBAL) mysql_mutex_unlock(&LOCK_global_system_variables); - mysql_mutex_lock(&LOCK_active_mi); - mi= master_info_index-> - get_master_info(&thd->variables.default_master_connection, - Sql_condition::WARN_LEVEL_ERROR); - if (mi) + if ((mi= (get_master_info(&thd->variables.default_master_connection, + Sql_condition::WARN_LEVEL_ERROR)))) { mysql_mutex_lock(&mi->rli.run_lock); mysql_mutex_lock(&mi->rli.data_lock); result= self->update_variable(thd, mi); mysql_mutex_unlock(&mi->rli.data_lock); mysql_mutex_unlock(&mi->rli.run_lock); + mi->release(); } - mysql_mutex_unlock(&LOCK_active_mi); if (type == OPT_GLOBAL) mysql_mutex_lock(&LOCK_global_system_variables); return result; diff --git a/sql/table.cc b/sql/table.cc index 57effed81ff..60334740790 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -572,7 +572,7 @@ enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, uint flags) { DBUG_ASSERT(flags & GTS_TABLE); DBUG_ASSERT(flags & GTS_USE_DISCOVERY); - mysql_file_delete_with_symlink(key_file_frm, path, MYF(0)); + my_handler_delete_with_symlink(key_file_frm, path, "", MYF(0)); file= -1; } else @@ -6689,11 +6689,9 @@ bool is_simple_order(ORDER *order) @details The function computes the values of the virtual columns of the table and stores them in the table record buffer. - If vcol_update_mode is set to VCOL_UPDATE_ALL then all virtual column are - computed. Otherwise, only fields from vcol_set are computed: all of them, - if vcol_update_mode is set to VCOL_UPDATE_FOR_WRITE, and, only those with - the stored_in_db flag set to false, if vcol_update_mode is equal to - VCOL_UPDATE_FOR_READ. + Only fields from vcol_set are computed: all of them, if vcol_update_mode is + set to VCOL_UPDATE_FOR_WRITE, and, only those with the stored_in_db flag + set to false, if vcol_update_mode is equal to VCOL_UPDATE_FOR_READ. @retval 0 Success @@ -6709,15 +6707,16 @@ int update_virtual_fields(THD *thd, TABLE *table, int error __attribute__ ((unused))= 0; DBUG_ASSERT(table && table->vfield); - thd->reset_arena_for_cached_items(table->expr_arena); + Query_arena backup_arena; + thd->set_n_backup_active_arena(table->expr_arena, &backup_arena); + /* Iterate over virtual fields in the table */ for (vfield_ptr= table->vfield; *vfield_ptr; vfield_ptr++) { vfield= (*vfield_ptr); DBUG_ASSERT(vfield->vcol_info && vfield->vcol_info->expr_item); - if ((bitmap_is_set(table->vcol_set, vfield->field_index) && - (vcol_update_mode == VCOL_UPDATE_FOR_WRITE || !vfield->stored_in_db)) || - vcol_update_mode == VCOL_UPDATE_ALL) + if (bitmap_is_set(table->vcol_set, vfield->field_index) && + (vcol_update_mode == VCOL_UPDATE_FOR_WRITE || !vfield->stored_in_db)) { /* Compute the actual value of the virtual fields */ error= vfield->vcol_info->expr_item->save_in_field(vfield, 0); @@ -6728,7 +6727,7 @@ int update_virtual_fields(THD *thd, TABLE *table, DBUG_PRINT("info", ("field '%s' - skipped", vfield->field_name)); } } - thd->reset_arena_for_cached_items(0); + thd->restore_active_arena(table->expr_arena, &backup_arena); DBUG_RETURN(0); } diff --git a/sql/table.h b/sql/table.h index 39faa8b9765..bf98f08842a 100644 --- a/sql/table.h +++ b/sql/table.h @@ -315,8 +315,7 @@ enum release_type { RELEASE_NORMAL, RELEASE_WAIT_FOR_DROP }; enum enum_vcol_update_mode { VCOL_UPDATE_FOR_READ= 0, - VCOL_UPDATE_FOR_WRITE, - VCOL_UPDATE_ALL + VCOL_UPDATE_FOR_WRITE }; class Filesort_info |