diff options
author | Sergei Golubchik <sergii@pisem.net> | 2013-11-23 00:50:54 +0100 |
---|---|---|
committer | Sergei Golubchik <sergii@pisem.net> | 2013-11-23 00:50:54 +0100 |
commit | c6d30805db3a1a2a098c3009fde8a42efd9fb9de (patch) | |
tree | e55f68e5adf40e1a75e08bda91b712ab6a094643 /sql | |
parent | 2c032b990e4ec470fad2e9f61cf6267a68b7e937 (diff) | |
parent | eea310e498f3b7ac95a4492d48f04e08d3009412 (diff) | |
download | mariadb-git-c6d30805db3a1a2a098c3009fde8a42efd9fb9de.tar.gz |
5.5 merge
Diffstat (limited to 'sql')
45 files changed, 641 insertions, 210 deletions
diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index 4a4ae5a732f..07f5a520e64 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -238,6 +238,12 @@ event_scheduler_thread(void *arg) my_free(arg); if (!res) scheduler->run(thd); + else + { + thd->proc_info= "Clearing"; + net_end(&thd->net); + delete thd; + } DBUG_LEAVE; // Against gcc warnings my_thread_end(); @@ -365,26 +371,26 @@ Event_scheduler::~Event_scheduler() } -/* +/** Starts the scheduler (again). Creates a new THD and passes it to a forked thread. Does not wait for acknowledgement from the new thread that it has started. Asynchronous starting. Most of the needed initializations are done in the current thread to minimize the chance of failure in the spawned thread. - SYNOPSIS - Event_scheduler::start() + @param[out] err_no - errno indicating type of error which caused + failure to start scheduler thread. - RETURN VALUE - FALSE OK - TRUE Error (not reported) + @return + @retval false Success. + @retval true Error. */ bool -Event_scheduler::start() +Event_scheduler::start(int *err_no) { THD *new_thd= NULL; - bool ret= FALSE; + bool ret= false; pthread_t th; struct scheduler_param *scheduler_param_value; DBUG_ENTER("Event_scheduler::start"); @@ -397,7 +403,7 @@ Event_scheduler::start() if (!(new_thd= new THD)) { sql_print_error("Event Scheduler: Cannot initialize the scheduler thread"); - ret= TRUE; + ret= true; goto end; } @@ -422,21 +428,30 @@ Event_scheduler::start() DBUG_PRINT("info", ("Setting state go RUNNING")); state= RUNNING; DBUG_PRINT("info", ("Forking new thread for scheduler. THD: 0x%lx", (long) new_thd)); - if (mysql_thread_create(key_thread_event_scheduler, - &th, &connection_attrib, event_scheduler_thread, - (void*)scheduler_param_value)) + if ((*err_no= mysql_thread_create(key_thread_event_scheduler, + &th, &connection_attrib, + event_scheduler_thread, + (void*)scheduler_param_value))) { DBUG_PRINT("error", ("cannot create a new thread")); + sql_print_error("Event scheduler: Failed to start scheduler," + " 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; - ret= TRUE; + delete new_thd; - new_thd->proc_info= "Clearing"; - delete_running_thd(new_thd); + delete scheduler_param_value; + ret= true; } + end: UNLOCK_DATA(); - DBUG_RETURN(ret); } @@ -548,7 +563,20 @@ Event_scheduler::execute_top(Event_queue_element_for_exec *event_name) if ((res= mysql_thread_create(key_thread_event_worker, &th, &connection_attrib, event_worker_thread, event_name))) + { + mysql_mutex_lock(&LOCK_global_system_variables); + Events::opt_event_scheduler= Events::EVENTS_OFF; + mysql_mutex_unlock(&LOCK_global_system_variables); + + 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); + goto error; + } started_events++; executed_events++; // For SHOW STATUS @@ -559,10 +587,8 @@ 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) - { - new_thd->proc_info= "Clearing"; - delete_running_thd(new_thd); - } + delete new_thd; + delete event_name; DBUG_RETURN(TRUE); } diff --git a/sql/event_scheduler.h b/sql/event_scheduler.h index aca4b74dd95..9435ee548d9 100644 --- a/sql/event_scheduler.h +++ b/sql/event_scheduler.h @@ -78,7 +78,7 @@ public: /* State changing methods follow */ bool - start(); + start(int *err_no); bool stop(); diff --git a/sql/events.cc b/sql/events.cc index 46b13056d7d..c449b9009f2 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -791,6 +791,7 @@ Events::init(bool opt_noacl_or_bootstrap) { THD *thd; + int err_no; bool res= FALSE; DBUG_ENTER("Events::init"); @@ -862,7 +863,7 @@ Events::init(bool opt_noacl_or_bootstrap) } if (event_queue->init_queue(thd) || load_events_from_db(thd) || - (opt_event_scheduler == EVENTS_ON && scheduler->start())) + (opt_event_scheduler == EVENTS_ON && scheduler->start(&err_no))) { sql_print_error("Event Scheduler: Error while loading from disk."); res= TRUE; /* fatal error: request unireg_abort */ @@ -1010,9 +1011,9 @@ Events::dump_internal_status() DBUG_VOID_RETURN; } -bool Events::start() +bool Events::start(int *err_no) { - return scheduler->start(); + return scheduler->start(err_no); } bool Events::stop() diff --git a/sql/events.h b/sql/events.h index a94fbc17135..d6fc4b5ea67 100644 --- a/sql/events.h +++ b/sql/events.h @@ -78,7 +78,7 @@ public: /* Protected using LOCK_global_system_variables only. */ static ulong opt_event_scheduler; static bool check_if_system_tables_error(); - static bool start(); + static bool start(int *err_no); static bool stop(); public: diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 1544678de38..075de1e1549 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2006, 2011, Oracle and/or its affiliates. +/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. Copyright (c) 2012, 2013, Monty Proram Ab. This program is free software; you can redistribute it and/or modify @@ -2410,6 +2410,12 @@ add_ndb_binlog_index_err: thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); thd->stmt_da->can_overwrite_status= FALSE; close_thread_tables(thd); + /* + There should be no need for rolling back transaction due to deadlock + (since ndb_binlog_index is non transactional). + */ + DBUG_ASSERT(! thd->transaction_rollback_request); + thd->mdl_context.release_transactional_locks(); ndb_binlog_index= 0; thd->variables.option_bits= saved_options; diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index cdc695c978e..780473e3683 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -6040,7 +6040,7 @@ void ha_partition::get_dynamic_partition_info(PARTITION_STATS *stat_info, uint part_id) { handler *file= m_file[part_id]; - file->info(HA_STATUS_CONST | HA_STATUS_TIME | HA_STATUS_VARIABLE | + file->info(HA_STATUS_TIME | HA_STATUS_VARIABLE | HA_STATUS_VARIABLE_EXTRA | HA_STATUS_NO_LOCK); stat_info->records= file->stats.records; diff --git a/sql/handler.cc b/sql/handler.cc index 5ef40768798..1518f2baaa0 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1579,10 +1579,16 @@ int ha_rollback_trans(THD *thd, bool all) } trans->ha_list= 0; trans->no_2pc=0; - if (is_real_trans && thd->transaction_rollback_request && - thd->transaction.xid_state.xa_state != XA_NOTR) - thd->transaction.xid_state.rm_error= thd->stmt_da->sql_errno(); } + + /* + Thanks to possibility of MDL deadlock rollback request can come even if + transaction hasn't been started in any transactional storage engine. + */ + if (is_real_trans && thd->transaction_rollback_request && + thd->transaction.xid_state.xa_state != XA_NOTR) + thd->transaction.xid_state.rm_error= thd->stmt_da->sql_errno(); + /* Always cleanup. Even if nht==0. There may be savepoints. */ if (is_real_trans) { diff --git a/sql/item.cc b/sql/item.cc index f1968e952be..e665e391042 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -396,6 +396,27 @@ my_decimal *Item::val_decimal_from_time(my_decimal *decimal_value) } +longlong Item::val_int_from_date() +{ + DBUG_ASSERT(fixed == 1); + MYSQL_TIME ltime; + if (get_date(<ime, 0)) + return 0; + longlong v= TIME_to_ulonglong(<ime); + return ltime.neg ? -v : v; +} + + +double Item::val_real_from_date() +{ + DBUG_ASSERT(fixed == 1); + MYSQL_TIME ltime; + if (get_date(<ime, 0)) + return 0; + return TIME_to_double(<ime); +} + + double Item::val_real_from_decimal() { /* Note that fix_fields may not be called for Item_avg_field items */ @@ -8843,6 +8864,18 @@ Item_cache_temporal::Item_cache_temporal(enum_field_types field_type_arg): } +longlong Item_cache_temporal::val_temporal_packed() +{ + DBUG_ASSERT(fixed == 1); + if ((!value_cached && !cache_value()) || null_value) + { + null_value= TRUE; + return 0; + } + return value; +} + + String *Item_cache_temporal::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -8855,6 +8888,42 @@ String *Item_cache_temporal::val_str(String *str) } +my_decimal *Item_cache_temporal::val_decimal(my_decimal *decimal_value) +{ + DBUG_ASSERT(fixed == 1); + if ((!value_cached && !cache_value()) || null_value) + { + null_value= true; + return NULL; + } + return val_decimal_from_date(decimal_value); +} + + +longlong Item_cache_temporal::val_int() +{ + DBUG_ASSERT(fixed == 1); + if ((!value_cached && !cache_value()) || null_value) + { + null_value= true; + return 0; + } + return val_int_from_date(); +} + + +double Item_cache_temporal::val_real() +{ + DBUG_ASSERT(fixed == 1); + if ((!value_cached && !cache_value()) || null_value) + { + null_value= true; + return 0; + } + return val_real_from_date(); +} + + bool Item_cache_temporal::cache_value() { if (!example) diff --git a/sql/item.h b/sql/item.h index 89d46e83e28..ea17668d47d 100644 --- a/sql/item.h +++ b/sql/item.h @@ -960,7 +960,9 @@ public: my_decimal *val_decimal_from_date(my_decimal *decimal_value); my_decimal *val_decimal_from_time(my_decimal *decimal_value); longlong val_int_from_decimal(); + longlong val_int_from_date(); double val_real_from_decimal(); + double val_real_from_date(); int save_time_in_field(Field *field); int save_date_in_field(Field *field); @@ -4166,6 +4168,10 @@ class Item_cache_temporal: public Item_cache_int public: Item_cache_temporal(enum_field_types field_type_arg); String* val_str(String *str); + my_decimal *val_decimal(my_decimal *); + longlong val_int(); + longlong val_temporal_packed(); + double val_real(); bool cache_value(); bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); int save_in_field(Field *field, bool no_conversions); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 276b7a5f90e..0966ab91c45 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -888,10 +888,13 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, enum_field_types f_type= item->cmp_type() == TIME_RESULT ? item->field_type() : warn_item->field_type(); - if (item->result_type() == INT_RESULT && item->cmp_type() == TIME_RESULT) + if (item->result_type() == INT_RESULT && + item->cmp_type() == TIME_RESULT && + item->type() == Item::CACHE_ITEM) { /* it's our Item_cache_temporal, as created below */ - value= item->val_int(); + DBUG_ASSERT(is_temporal_type(((Item_cache *) item)->field_type())); + value= ((Item_cache_temporal*) item)->val_temporal_packed(); } else { @@ -5706,8 +5709,8 @@ Item *Item_bool_rowready_func2::negated_item() */ Item_equal::Item_equal(Item *f1, Item *f2, bool with_const_item) - : Item_bool_func(), eval_item(0), cond_false(0), context_field(NULL), - link_equal_fields(FALSE) + : Item_bool_func(), eval_item(0), cond_false(0), cond_true(0), + context_field(NULL), link_equal_fields(FALSE) { const_item_cache= 0; with_const= with_const_item; @@ -5732,8 +5735,8 @@ Item_equal::Item_equal(Item *f1, Item *f2, bool with_const_item) */ Item_equal::Item_equal(Item_equal *item_equal) - : Item_bool_func(), eval_item(0), cond_false(0), context_field(NULL), - link_equal_fields(FALSE) + : Item_bool_func(), eval_item(0), cond_false(0), cond_true(0), + context_field(NULL), link_equal_fields(FALSE) { const_item_cache= 0; List_iterator_fast<Item> li(item_equal->equal_items); @@ -5800,12 +5803,6 @@ void Item_equal::add_const(Item *c, Item *f) func->quick_fix_field(); cond_false= !func->val_int(); } - /* - TODO: also support the case where Item_equal becomes singular with - this->is_cond_true()=1. When I attempted to mark the item as constant, - the optimizer attempted to remove it, however it is still referenced from - COND_EQUAL and I got a crash. - */ if (cond_false) const_item_cache= 1; } @@ -6010,8 +6007,7 @@ void Item_equal::merge_into_list(List<Item_equal> *list, void Item_equal::sort(Item_field_cmpfunc compare, void *arg) { - if (equal_items.elements > 1) - bubble_sort<Item>(&equal_items, compare, arg); + bubble_sort<Item>(&equal_items, compare, arg); } @@ -6061,6 +6057,9 @@ void Item_equal::update_const() { it.remove(); add_const(item); + if (equal_items.elements == 1) + cond_true= TRUE; + update_used_tables(); } } } @@ -6139,13 +6138,7 @@ bool Item_equal::fix_fields(THD *thd, Item **ref) void Item_equal::update_used_tables() { not_null_tables_cache= used_tables_cache= 0; - /* - TODO: also support the case where Item_equal becomes singular with - this->is_cond_true()=1. When I attempted to mark the item as constant, - the optimizer attempted to remove it, however it is still referenced from - COND_EQUAL and I got a crash. - */ - if ((const_item_cache= cond_false)) + if ((const_item_cache= cond_false || cond_true)) return; Item_equal_fields_iterator it(*this); Item *item; @@ -6194,7 +6187,7 @@ longlong Item_equal::val_int() { if (cond_false) return 0; - if (is_cond_true()) + if (cond_true) return 1; Item *item= get_const(); Item_equal_fields_iterator it(*this); @@ -6220,11 +6213,6 @@ longlong Item_equal::val_int() void Item_equal::fix_length_and_dec() { Item *item= get_first(NO_PARTICULAR_TAB, NULL); - if (!item) - { - DBUG_ASSERT(is_cond_true()); // it should be the only constant - item= equal_items.head(); - } eval_item= cmp_item::get_comparator(item->cmp_type(), item, item->collation.collation); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index a438139bcfe..a0d9b7c20fa 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1794,6 +1794,12 @@ class Item_equal: public Item_bool_func the equal_items should be ignored. */ bool cond_false; + /* + This initially is set to FALSE. It becomes TRUE when this item is evaluated + as being always true. If the flag is TRUE the contents of the list + the equal_items should be ignored. + */ + bool cond_true; /* compare_as_dates=TRUE <-> constants equal to fields from equal_items must be compared as datetimes and not as strings. @@ -1826,7 +1832,6 @@ public: Item_equal(Item_equal *item_equal); /* Currently the const item is always the first in the list of equal items */ inline Item* get_const() { return with_const ? equal_items.head() : NULL; } - inline bool is_cond_true() { return equal_items.elements == 1; } void add_const(Item *c, Item *f = NULL); /** Add a non-constant item to the multiple equality */ void add(Item *f) { equal_items.push_back(f); } diff --git a/sql/item_func.cc b/sql/item_func.cc index ef436ef8dc9..5d9abbb0d8c 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -826,6 +826,8 @@ void Item_num_op::find_num_type(void) cached_result_type= DECIMAL_RESULT; result_precision(); fix_decimals(); + if ((r0 == TIME_RESULT || r1 == TIME_RESULT) && decimals == 0) + cached_result_type= INT_RESULT; } else { diff --git a/sql/item_sum.cc b/sql/item_sum.cc index b4ecb66fc46..9f2c3f074b6 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1266,7 +1266,7 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) void Item_sum_hybrid::setup_hybrid(Item *item, Item *value_arg) { - if (!(value= Item_cache::get_cache(item))) + if (!(value= Item_cache::get_cache(item, item->cmp_type()))) return; value->setup(item); value->store(value_arg); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 51d6d3fa6a6..425008ab43b 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1475,27 +1475,6 @@ String *Item_temporal_func::val_str(String *str) } -longlong Item_temporal_func::val_int() -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if (get_date(<ime, sql_mode)) - return 0; - longlong v= TIME_to_ulonglong(<ime); - return ltime.neg ? -v : v; -} - - -double Item_temporal_func::val_real() -{ - DBUG_ASSERT(fixed == 1); - MYSQL_TIME ltime; - if (get_date(<ime, sql_mode)) - return 0; - return TIME_to_double(<ime); -} - - bool Item_func_from_days::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) { longlong value=args[0]->val_int(); diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index c54490f45af..c086579e34b 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -499,8 +499,8 @@ public: enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } Item_result cmp_type() const { return TIME_RESULT; } String *val_str(String *str); - longlong val_int(); - double val_real(); + longlong val_int() { return val_int_from_date(); } + double val_real() { return val_real_from_date(); } bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date) { DBUG_ASSERT(0); return 1; } my_decimal *val_decimal(my_decimal *decimal_value) { return val_decimal_from_date(decimal_value); } diff --git a/sql/log_event.cc b/sql/log_event.cc index 764144419be..d6c7db1e215 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -5813,6 +5813,8 @@ error: thd->stmt_da->can_overwrite_status= FALSE; close_thread_tables(thd); /* + - If transaction rollback was requested due to deadlock + perform it and release metadata locks. - If inside a multi-statement transaction, defer the release of metadata locks until the current transaction is either committed or rolled back. This prevents @@ -5822,7 +5824,12 @@ error: - If in autocommit mode, or outside a transactional context, automatically release metadata locks of the current statement. */ - if (! thd->in_multi_stmt_transaction_mode()) + if (thd->transaction_rollback_request) + { + trans_rollback_implicit(thd); + thd->mdl_context.release_transactional_locks(); + } + else if (! thd->in_multi_stmt_transaction_mode()) thd->mdl_context.release_transactional_locks(); else thd->mdl_context.release_statement_locks(); @@ -9655,7 +9662,10 @@ static int rows_event_stmt_cleanup(rpl_group_info *rgi, THD * thd) Xid_log_event will come next which will, if some transactional engines are involved, commit the transaction and flush the pending event to the binlog. + If there was a deadlock the transaction should have been rolled back + already. So there should be no need to rollback the transaction. */ + DBUG_ASSERT(! thd->transaction_rollback_request); error|= (error ? trans_rollback_stmt(thd) : trans_commit_stmt(thd)); /* diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index d9de0615eb5..87a31f53d87 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2007, 2012, Oracle and/or its affiliates. +/* Copyright (c) 2007, 2013, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1771,7 +1771,10 @@ int Old_rows_log_event::do_apply_event(rpl_group_info *rgi) Xid_log_event will come next which will, if some transactional engines are involved, commit the transaction and flush the pending event to the binlog. + If there was a deadlock the transaction should have been rolled back + already. So there should be no need to rollback the transaction. */ + DBUG_ASSERT(! thd->transaction_rollback_request); if ((error= (binlog_error ? trans_rollback_stmt(thd) : trans_commit_stmt(thd)))) rli->report(ERROR_LEVEL, error, "Error in %s event: commit of row events failed, " diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 7b1e34fbf25..1d4002df60f 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1650,11 +1650,12 @@ void kill_mysql(void) if (!kill_in_progress) { pthread_t tmp; + int error; abort_loop=1; - if (mysql_thread_create(0, /* Not instrumented */ - &tmp, &connection_attrib, kill_server_thread, - (void*) 0)) - sql_print_error("Can't create thread to kill server"); + if ((error= mysql_thread_create(0, /* Not instrumented */ + &tmp, &connection_attrib, + kill_server_thread, (void*) 0))) + sql_print_error("Can't create thread to kill server (errno= %d).", error); } #endif DBUG_VOID_RETURN; @@ -3190,10 +3191,12 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused))) #endif #ifdef USE_ONE_SIGNAL_HAND pthread_t tmp; - if (mysql_thread_create(0, /* Not instrumented */ - &tmp, &connection_attrib, kill_server_thread, - (void*) &sig)) - sql_print_error("Can't create thread to kill server"); + if ((error= mysql_thread_create(0, /* Not instrumented */ + &tmp, &connection_attrib, + kill_server_thread, + (void*) &sig))) + sql_print_error("Can't create thread to kill server (errno= %d)", + error); #else kill_server((void*) sig); // MIT THREAD has a alarm thread #endif @@ -4746,9 +4749,12 @@ static void create_shutdown_thread() #ifdef __WIN__ hEventShutdown=CreateEvent(0, FALSE, FALSE, shutdown_event_name); pthread_t hThread; - if (mysql_thread_create(key_thread_handle_shutdown, - &hThread, &connection_attrib, handle_shutdown, 0)) - sql_print_warning("Can't create thread to handle shutdown requests"); + int error; + if ((error= mysql_thread_create(key_thread_handle_shutdown, + &hThread, &connection_attrib, + handle_shutdown, 0))) + sql_print_warning("Can't create thread to handle shutdown requests" + " (errno= %d)", error); // On "Stop Service" we have to do regular shutdown Service.SetShutdownEvent(hEventShutdown); @@ -4762,6 +4768,7 @@ static void create_shutdown_thread() static void handle_connections_methods() { pthread_t hThread; + int error; DBUG_ENTER("handle_connections_methods"); if (hPipe == INVALID_HANDLE_VALUE && (!have_tcpip || opt_disable_networking) && @@ -4777,22 +4784,24 @@ static void handle_connections_methods() if (hPipe != INVALID_HANDLE_VALUE) { handler_count++; - if (mysql_thread_create(key_thread_handle_con_namedpipes, - &hThread, &connection_attrib, - handle_connections_namedpipes, 0)) + if ((error= mysql_thread_create(key_thread_handle_con_namedpipes, + &hThread, &connection_attrib, + handle_connections_namedpipes, 0))) { - sql_print_warning("Can't create thread to handle named pipes"); + sql_print_warning("Can't create thread to handle named pipes" + " (errno= %d)", error); handler_count--; } } if (have_tcpip && !opt_disable_networking) { handler_count++; - if (mysql_thread_create(key_thread_handle_con_sockets, - &hThread, &connection_attrib, - handle_connections_sockets_thread, 0)) + if ((error= mysql_thread_create(key_thread_handle_con_sockets, + &hThread, &connection_attrib, + handle_connections_sockets_thread, 0))) { - sql_print_warning("Can't create thread to handle TCP/IP"); + sql_print_warning("Can't create thread to handle TCP/IP", + " (errno= %d)", error); handler_count--; } } @@ -4800,11 +4809,12 @@ static void handle_connections_methods() if (opt_enable_shared_memory) { handler_count++; - if (mysql_thread_create(key_thread_handle_con_sharedmem, - &hThread, &connection_attrib, - handle_connections_shared_memory, 0)) + if ((error= mysql_thread_create(key_thread_handle_con_sharedmem, + &hThread, &connection_attrib, + handle_connections_shared_memory, 0))) { - sql_print_warning("Can't create thread to handle shared memory"); + sql_print_warning("Can't create thread to handle shared memory", + " (errno= %d)", error); handler_count--; } } @@ -4830,6 +4840,42 @@ void decrement_handler_count() #ifndef EMBEDDED_LIBRARY + +LEX_STRING sql_statement_names[(uint) SQLCOM_END + 1]; + +static void init_sql_statement_names() +{ + char *first_com= (char*) offsetof(STATUS_VAR, com_stat[0]); + char *last_com= (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_END]); + int record_size= (char*) offsetof(STATUS_VAR, com_stat[1]) + - (char*) offsetof(STATUS_VAR, com_stat[0]); + char *ptr; + uint i; + uint com_index; + + for (i= 0; i < ((uint) SQLCOM_END + 1); i++) + sql_statement_names[i]= empty_lex_str; + + SHOW_VAR *var= &com_status_vars[0]; + while (var->name != NULL) + { + ptr= var->value; + if ((first_com <= ptr) && (ptr <= last_com)) + { + com_index= ((int)(ptr - first_com))/record_size; + DBUG_ASSERT(com_index < (uint) SQLCOM_END); + sql_statement_names[com_index].str= const_cast<char *>(var->name); + sql_statement_names[com_index].length= strlen(var->name); + } + var++; + } + + DBUG_ASSERT(strcmp(sql_statement_names[(uint) SQLCOM_SELECT].str, "select") == 0); + DBUG_ASSERT(strcmp(sql_statement_names[(uint) SQLCOM_SIGNAL].str, "signal") == 0); + + sql_statement_names[(uint) SQLCOM_END].str= const_cast<char*>("error"); +} + #ifndef DBUG_OFF /* Debugging helper function to keep the locale database @@ -4908,6 +4954,7 @@ int mysqld_main(int argc, char **argv) /* Must be initialized early for comparison of options name */ system_charset_info= &my_charset_utf8_general_ci; + init_sql_statement_names(); sys_var_init(); #ifdef WITH_PERFSCHEMA_STORAGE_ENGINE @@ -5517,11 +5564,14 @@ static void bootstrap(MYSQL_FILE *file) bootstrap_file=file; #ifndef EMBEDDED_LIBRARY // TODO: Enable this - if (mysql_thread_create(key_thread_bootstrap, - &thd->real_id, &connection_attrib, handle_bootstrap, - (void*) thd)) + int error; + if ((error= mysql_thread_create(key_thread_bootstrap, + &thd->real_id, &connection_attrib, + handle_bootstrap, + (void*) thd))) { - sql_print_warning("Can't create thread to handle bootstrap"); + sql_print_warning("Can't create thread to handle bootstrap (errno= %d)", + error); bootstrap_error=-1; DBUG_VOID_RETURN; } diff --git a/sql/mysqld.h b/sql/mysqld.h index 47d399938f6..2e10b0caeb5 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -214,7 +214,6 @@ extern LEX_STRING opt_init_connect, opt_init_slave; extern int bootstrap_error; extern I_List<THD> threads; extern char err_shared_dir[]; -extern TYPELIB thread_handling_typelib; /* THR_MALLOC is a key which will be used to set/get MEM_ROOT** for a thread, diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 410f0d5c0f3..d90c8e88704 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -5334,8 +5334,10 @@ TABLE_READ_PLAN *merge_same_index_scans(PARAM *param, SEL_IMERGE *imerge, bzero((*changed_tree)->keys, sizeof((*changed_tree)->keys[0])*param->keys); (*changed_tree)->keys_map.clear_all(); - key->incr_refs(); - (*tree)->keys[key_idx]->incr_refs(); + if (key) + key->incr_refs(); + if ((*tree)->keys[key_idx]) + (*tree)->keys[key_idx]->incr_refs(); if (((*changed_tree)->keys[key_idx]= key_or(param, key, (*tree)->keys[key_idx]))) (*changed_tree)->keys_map.set_bit(key_idx); diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 78a1f5fb326..32e54d10856 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -1928,11 +1928,25 @@ int pull_out_semijoin_tables(JOIN *join) } } - table_map pulled_tables= 0; + table_map dep_tables= 0; if (have_join_nest_children) goto skip; + /* + Calculate set of tables within this semi-join nest that have + other dependent tables + */ + child_li.rewind(); + while ((tbl= child_li++)) + { + TABLE *const table= tbl->table; + if (table && + (table->reginfo.join_tab->dependent & + sj_nest->nested_join->used_tables)) + dep_tables|= table->reginfo.join_tab->dependent; + } + /* Action #1: Mark the constant tables to be pulled out */ child_li.rewind(); while ((tbl= child_li++)) @@ -1983,7 +1997,8 @@ int pull_out_semijoin_tables(JOIN *join) child_li.rewind(); while ((tbl= child_li++)) { - if (tbl->table && !(pulled_tables & tbl->table->map)) + if (tbl->table && !(pulled_tables & tbl->table->map) && + !(dep_tables & tbl->table->map)) { if (find_eq_ref_candidate(tbl->table, sj_nest->nested_join->used_tables & diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 415e6267ef3..3d33cd7b131 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -2206,11 +2206,10 @@ int partition_info::fix_parser_data(THD *thd) { part_elem= it++; List_iterator<part_elem_value> list_val_it(part_elem->list_val_list); - j= 0; num_elements= part_elem->list_val_list.elements; DBUG_ASSERT(part_type == RANGE_PARTITION ? num_elements == 1U : TRUE); - do + for (j= 0; j < num_elements; j++) { part_elem_value *val= list_val_it++; if (column_list) @@ -2245,7 +2244,7 @@ int partition_info::fix_parser_data(THD *thd) list_val_it.remove(); } } - } while (++j < num_elements); + } } while (++i < num_parts); DBUG_RETURN(FALSE); } diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 3bf6a0cff76..d4db81b79c1 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -1,4 +1,5 @@ -/* Copyright (c) 2006, 2012, Oracle and/or its affiliates. +/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. + Copyright (c) 2011, 2013, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1672,6 +1673,8 @@ void rpl_group_info::slave_close_thread_tables(THD *thd) close_thread_tables(thd); /* + - If transaction rollback was requested due to deadlock + perform it and release metadata locks. - If inside a multi-statement transaction, defer the release of metadata locks until the current transaction is either committed or rolled back. This prevents @@ -1681,7 +1684,12 @@ void rpl_group_info::slave_close_thread_tables(THD *thd) - If in autocommit mode, or outside a transactional context, automatically release metadata locks of the current statement. */ - if (! thd->in_multi_stmt_transaction_mode()) + if (thd->transaction_rollback_request) + { + trans_rollback_implicit(thd); + thd->mdl_context.release_transactional_locks(); + } + else if (! thd->in_multi_stmt_transaction_mode()) thd->mdl_context.release_transactional_locks(); else thd->mdl_context.release_statement_locks(); diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc index 377006253c9..bded28cb542 100644 --- a/sql/rpl_utility.cc +++ b/sql/rpl_utility.cc @@ -1,6 +1,5 @@ -/* - Copyright (c) 2006, 2010, Oracle and/or its affiliates. - Copyright (c) 2011, 2013, Monty Program Ab. +/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. + Copyright (c) 2011, 2013, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -879,6 +878,7 @@ TABLE *table_def::create_conversion_table(THD *thd, Relay_log_info *rli, TABLE * DBUG_ENTER("table_def::create_conversion_table"); List<Create_field> field_list; + TABLE *conv_table= NULL; /* At slave, columns may differ. So we should create min(columns@master, columns@slave) columns in the @@ -920,10 +920,15 @@ TABLE *table_def::create_conversion_table(THD *thd, Relay_log_info *rli, TABLE * break; case MYSQL_TYPE_DECIMAL: - precision= field_metadata(col); - decimals= static_cast<Field_num*>(target_table->field[col])->dec; - max_length= field_metadata(col); - break; + sql_print_error("In RBR mode, Slave received incompatible DECIMAL field " + "(old-style decimal field) from Master while creating " + "conversion table. Please consider changing datatype on " + "Master to new style decimal by executing ALTER command for" + " column Name: %s.%s.%s.", + target_table->s->db.str, + target_table->s->table_name.str, + target_table->field[col]->field_name); + goto err; case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: @@ -951,7 +956,9 @@ TABLE *table_def::create_conversion_table(THD *thd, Relay_log_info *rli, TABLE * field_def->interval= interval; } - TABLE *conv_table= create_virtual_tmp_table(thd, field_list); + conv_table= create_virtual_tmp_table(thd, field_list); + +err: if (conv_table == NULL) rli->report(ERROR_LEVEL, ER_SLAVE_CANT_CREATE_CONVERSION, ER(ER_SLAVE_CANT_CREATE_CONVERSION), diff --git a/sql/slave.cc b/sql/slave.cc index 76fbc10db5b..993b41d2de4 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -792,6 +792,7 @@ int start_slave_thread( { pthread_t th; ulong start_id; + int error; DBUG_ENTER("start_slave_thread"); DBUG_ASSERT(mi->inited); @@ -818,9 +819,10 @@ int start_slave_thread( } start_id= *slave_run_id; DBUG_PRINT("info",("Creating new slave thread")); - if (mysql_thread_create(thread_key, - &th, &connection_attrib, h_func, (void*)mi)) + if ((error = mysql_thread_create(thread_key, + &th, &connection_attrib, h_func, (void*)mi))) { + sql_print_error("Can't create slave thread (errno= %d).", error); if (start_lock) mysql_mutex_unlock(start_lock); DBUG_RETURN(ER_SLAVE_THREAD); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 122b6c9b796..80d4f4c8e9f 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1,5 +1,6 @@ /* - Copyright (c) 2002, 2011, Oracle and/or its affiliates. + Copyright (c) 2002, 2013, Oracle and/or its affiliates. + Copyright (c) 2011, 2013, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -2183,10 +2184,18 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) close_thread_tables(thd); thd_proc_info(thd, 0); - if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode()) - thd->mdl_context.release_transactional_locks(); - else if (! thd->in_sub_stmt) - thd->mdl_context.release_statement_locks(); + if (! thd->in_sub_stmt) + { + if (thd->transaction_rollback_request) + { + trans_rollback_implicit(thd); + thd->mdl_context.release_transactional_locks(); + } + else if (! thd->in_multi_stmt_transaction_mode()) + thd->mdl_context.release_transactional_locks(); + else + thd->mdl_context.release_statement_locks(); + } thd->rollback_item_tree_changes(); @@ -3041,10 +3050,18 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, close_thread_tables(thd); thd_proc_info(thd, 0); - if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode()) - thd->mdl_context.release_transactional_locks(); - else if (! thd->in_sub_stmt) - thd->mdl_context.release_statement_locks(); + if (! thd->in_sub_stmt) + { + if (thd->transaction_rollback_request) + { + trans_rollback_implicit(thd); + thd->mdl_context.release_transactional_locks(); + } + else if (! thd->in_multi_stmt_transaction_mode()) + thd->mdl_context.release_transactional_locks(); + else + thd->mdl_context.release_statement_locks(); + } } //TODO: why is this here if log_slow_query is in sp_instr_stmt_execute? delete_explain_query(m_lex); diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index 0b2a939d0ba..852661d5915 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -980,8 +980,20 @@ send_result_message: } } /* Error path, a admin command failed. */ - trans_commit_stmt(thd); - trans_commit_implicit(thd); + if (thd->transaction_rollback_request) + { + /* + Unlikely, but transaction rollback was requested by one of storage + engines (e.g. due to deadlock). Perform it. + */ + if (trans_rollback_stmt(thd) || trans_rollback_implicit(thd)) + goto err; + } + else + { + if (trans_commit_stmt(thd) || trans_commit_implicit(thd)) + goto err; + } close_thread_tables(thd); thd->mdl_context.release_transactional_locks(); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 34737f0dca0..3f4be0da25d 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. - Copyright (c) 2010, 2011 Monty Program Ab +/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. + Copyright (c) 2010, 2013 Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -3905,7 +3905,8 @@ end_free: /** Open_table_context */ Open_table_context::Open_table_context(THD *thd, uint flags) - :m_failed_table(NULL), + :m_thd(thd), + m_failed_table(NULL), m_start_of_statement_svp(thd->mdl_context.mdl_savepoint()), m_timeout(flags & MYSQL_LOCK_IGNORE_TIMEOUT ? LONG_TIMEOUT : thd->variables.lock_wait_timeout), @@ -3982,6 +3983,7 @@ request_backoff_action(enum_open_table_action action_arg, if (action_arg != OT_REOPEN_TABLES && m_has_locks) { my_error(ER_LOCK_DEADLOCK, MYF(0)); + mark_transaction_to_rollback(m_thd, true); return TRUE; } /* @@ -3991,7 +3993,7 @@ request_backoff_action(enum_open_table_action action_arg, if (table) { DBUG_ASSERT(action_arg == OT_DISCOVER || action_arg == OT_REPAIR); - m_failed_table= (TABLE_LIST*) current_thd->alloc(sizeof(TABLE_LIST)); + m_failed_table= (TABLE_LIST*) m_thd->alloc(sizeof(TABLE_LIST)); if (m_failed_table == NULL) return TRUE; m_failed_table->init_one_table(table->db, table->db_length, @@ -4008,8 +4010,6 @@ request_backoff_action(enum_open_table_action action_arg, /** Recover from failed attempt of open table by performing requested action. - @param thd Thread context - @pre This function should be called only with "action" != OT_NO_ACTION and after having called @sa close_tables_for_reopen(). @@ -4019,7 +4019,7 @@ request_backoff_action(enum_open_table_action action_arg, bool Open_table_context:: -recover_from_failed_open(THD *thd) +recover_from_failed_open() { bool result= FALSE; /* Execute the action. */ @@ -4031,38 +4031,38 @@ recover_from_failed_open(THD *thd) break; case OT_DISCOVER: { - if ((result= lock_table_names(thd, m_failed_table, NULL, + if ((result= lock_table_names(m_thd, m_failed_table, NULL, get_timeout(), MYSQL_OPEN_SKIP_TEMPORARY))) break; - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db, + tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db, m_failed_table->table_name, FALSE); - thd->warning_info->clear_warning_info(thd->query_id); - thd->clear_error(); // Clear error message + m_thd->warning_info->clear_warning_info(m_thd->query_id); + m_thd->clear_error(); // Clear error message if ((result= - !get_table_share(thd, m_failed_table->db, + !get_table_share(m_thd, m_failed_table->db, m_failed_table->table_name, GTS_TABLE | GTS_FORCE_DISCOVERY | GTS_NOLOCK))) break; - thd->mdl_context.release_transactional_locks(); + m_thd->mdl_context.release_transactional_locks(); break; } case OT_REPAIR: { - if ((result= lock_table_names(thd, m_failed_table, NULL, + if ((result= lock_table_names(m_thd, m_failed_table, NULL, get_timeout(), MYSQL_OPEN_SKIP_TEMPORARY))) break; - tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db, + tdc_remove_table(m_thd, TDC_RT_REMOVE_ALL, m_failed_table->db, m_failed_table->table_name, FALSE); - result= auto_repair_table(thd, m_failed_table); - thd->mdl_context.release_transactional_locks(); + result= auto_repair_table(m_thd, m_failed_table); + m_thd->mdl_context.release_transactional_locks(); break; } default: @@ -4981,7 +4981,7 @@ restart: TABLE_LIST element. Altough currently this assumption is valid it may change in future. */ - if (ot_ctx.recover_from_failed_open(thd)) + if (ot_ctx.recover_from_failed_open()) goto err; error= FALSE; @@ -5034,7 +5034,7 @@ restart: { close_tables_for_reopen(thd, start, ot_ctx.start_of_statement_svp()); - if (ot_ctx.recover_from_failed_open(thd)) + if (ot_ctx.recover_from_failed_open()) goto err; error= FALSE; @@ -5473,7 +5473,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, */ thd->mdl_context.rollback_to_savepoint(ot_ctx.start_of_statement_svp()); table_list->mdl_request.ticket= 0; - if (ot_ctx.recover_from_failed_open(thd)) + if (ot_ctx.recover_from_failed_open()) break; } diff --git a/sql/sql_base.h b/sql/sql_base.h index 95d9bf21fe8..7e9992c8994 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -581,7 +581,7 @@ public: }; Open_table_context(THD *thd, uint flags); - bool recover_from_failed_open(THD *thd); + bool recover_from_failed_open(); bool request_backoff_action(enum_open_table_action action_arg, TABLE_LIST *table); @@ -621,6 +621,8 @@ public: } private: + /* THD for which tables are opened. */ + THD *m_thd; /** For OT_DISCOVER and OT_REPAIR actions, the table list element for the table which definition should be re-discovered or which diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index debd06db455..0a2c0528539 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1,4 +1,5 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. +/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. + Copyright (c) 2010, 2013, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -2033,7 +2034,12 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", */ thd->query_cache_is_applicable= 0; // Query can't be cached } - /* End the statement transaction potentially started by engine. */ + /* + End the statement transaction potentially started by engine. + Currently our engines do not request rollback from callbacks. + If this is going to change code needs to be reworked. + */ + DBUG_ASSERT(! thd->transaction_rollback_request); trans_rollback_stmt(thd); goto err_unlock; // Parse query } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 5ead1914af0..d1edec140b6 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -4023,6 +4023,10 @@ extern "C" unsigned long thd_get_thread_id(const MYSQL_THD thd) return((unsigned long)thd->thread_id); } +extern "C" enum_tx_isolation thd_get_trx_isolation(const MYSQL_THD thd) +{ + return thd->tx_isolation; +} /** Check if THD socket is still connected. diff --git a/sql/sql_class.h b/sql/sql_class.h index 13e72f11829..9f091ac2c2b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2000, 2013, Oracle and/or its affiliates. Copyright (c) 2009, 2013, Monty Program Ab This program is free software; you can redistribute it and/or modify @@ -444,6 +444,7 @@ enum killed_type #include "sql_lex.h" /* Must be here */ +extern LEX_STRING sql_statement_names[(uint) SQLCOM_END + 1]; class Delayed_insert; class select_result; class Time_zone; @@ -3065,6 +3066,11 @@ public: */ bool set_db(const char *new_db, size_t new_db_len) { + /* + Acquiring mutex LOCK_thd_data as we either free the memory allocated + for the database and reallocating the memory for the new db or memcpy + the new_db to the db. + */ mysql_mutex_lock(&LOCK_thd_data); /* Do not reallocate memory if current chunk is big enough. */ if (db && new_db && db_length >= new_db_len) diff --git a/sql/sql_db.cc b/sql/sql_db.cc index ec7561594c9..0235ea408b3 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. + Copyright (c) 2000, 2013, Oracle and/or its affiliates. Copyright (c) 2009, 2013, Monty Program Ab. This program is free software; you can redistribute it and/or modify @@ -1253,8 +1253,7 @@ static void mysql_change_db_impl(THD *thd, we just call THD::reset_db(). Since THD::reset_db() does not releases the previous database name, we should do it explicitly. */ - my_free(thd->db); - + thd->set_db(NULL, 0); thd->reset_db(new_db_name->str, new_db_name->length); } diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 3c4804c523a..87ca0f9056d 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -789,7 +789,10 @@ retry: /* Always close statement transaction explicitly, so that the engine doesn't have to count locks. + There should be no need to perform transaction + rollback due to deadlock. */ + DBUG_ASSERT(! thd->transaction_rollback_request); trans_rollback_stmt(thd); mysql_ha_close_table(handler); if (thd->stmt_arena->is_stmt_execute()) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 0d7c81deabb..9941c124f4d 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -197,6 +197,7 @@ init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex) table->map= 1; //To ensure correct calculation of const item table->get_fields_in_item_tree= TRUE; table_list->table= table; + table_list->cacheable_table= false; return FALSE; } @@ -3898,7 +3899,7 @@ void SELECT_LEX::update_used_tables() } for (ORDER *order= group_list.first; order; order= order->next) (*order->item)->update_used_tables(); - if (!master_unit()->is_union()) + if (!master_unit()->is_union() || master_unit()->global_parameters != this) { for (ORDER *order= order_list.first; order; order= order->next) (*order->item)->update_used_tables(); diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc index e9ac55730d3..9d014790929 100644 --- a/sql/sql_manager.cc +++ b/sql/sql_manager.cc @@ -140,9 +140,12 @@ void start_handle_manager() if (flush_time && flush_time != ~(ulong) 0L) { pthread_t hThread; - if (mysql_thread_create(key_thread_handle_manager, - &hThread, &connection_attrib, handle_manager, 0)) - sql_print_warning("Can't create handle_manager thread"); + int error; + if ((error= mysql_thread_create(key_thread_handle_manager, + &hThread, &connection_attrib, + handle_manager, 0))) + sql_print_warning("Can't create handle_manager thread (errno= %d)", + error); } DBUG_VOID_RETURN; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index fccc012429c..7366e4fc10f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. +/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. Copyright (c) 2008, 2013, Monty Program Ab This program is free software; you can redistribute it and/or modify @@ -1264,6 +1264,18 @@ bool dispatch_command(enum enum_server_command command, THD *thd, close_thread_tables(thd); thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + if (thd->transaction_rollback_request) + { + /* + Transaction rollback was requested since MDL deadlock was + discovered while trying to open tables. Rollback transaction + in all storage engines including binary log and release all + locks. + */ + trans_rollback_implicit(thd); + thd->mdl_context.release_transactional_locks(); + } + thd->cleanup_after_query(); break; } @@ -1953,7 +1965,7 @@ err: can free its locks if LOCK TABLES locked some tables before finding that it can't lock a table in its list */ - trans_commit_implicit(thd); + trans_rollback(thd); /* Close tables and release metadata locks. */ close_thread_tables(thd); DBUG_ASSERT(!thd->locked_tables_mode); @@ -2007,6 +2019,13 @@ mysql_execute_command(THD *thd) DBUG_ASSERT(thd->transaction.stmt.is_empty() || thd->in_sub_stmt); /* + Each statement or replication event which might produce deadlock + should handle transaction rollback on its own. So by the start of + the next statement transaction rollback request should be fulfilled + already. + */ + DBUG_ASSERT(! thd->transaction_rollback_request || thd->in_sub_stmt); + /* In many cases first table of main SELECT_LEX have special meaning => check that it is first table in global list and relink it first in queries_tables list if it is necessary (we need such relinking only @@ -2210,8 +2229,8 @@ mysql_execute_command(THD *thd) or triggers as all such statements prohibited there. */ DBUG_ASSERT(! thd->in_sub_stmt); - /* Commit or rollback the statement transaction. */ - thd->is_error() ? trans_rollback_stmt(thd) : trans_commit_stmt(thd); + /* Statement transaction still should not be started. */ + DBUG_ASSERT(thd->transaction.stmt.is_empty()); /* Commit the normal transaction if one is active. */ if (trans_commit_implicit(thd)) goto error; @@ -4840,7 +4859,17 @@ finish: thd->set_binlog_format(orig_binlog_format, orig_current_stmt_binlog_format); - if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END)) + if (! thd->in_sub_stmt && thd->transaction_rollback_request) + { + /* + We are not in sub-statement and transaction rollback was requested by + one of storage engines (e.g. due to deadlock). Rollback transaction in + all storage engines including binary log. + */ + trans_rollback_implicit(thd); + thd->mdl_context.release_transactional_locks(); + } + else if (stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END)) { /* No transaction control allowed in sub-statements. */ DBUG_ASSERT(! thd->in_sub_stmt); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index b4a313e344a..9c37c5d28fb 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2002, 2012, Oracle and/or its affiliates. - Copyright (c) 2008, 2011, Monty Program Ab +/* Copyright (c) 2002, 2013, Oracle and/or its affiliates. + Copyright (c) 2008, 2013, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -115,6 +115,7 @@ When one supplies long data for a placeholder: #endif #include "lock.h" // MYSQL_OPEN_FORCE_SHARED_MDL #include "sql_handler.h" +#include "transaction.h" // trans_rollback_implicit /** A result class used to send cursor rows using the binary protocol. @@ -3407,6 +3408,22 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) close_thread_tables(thd); thd->mdl_context.rollback_to_savepoint(mdl_savepoint); + + /* + Transaction rollback was requested since MDL deadlock was discovered + while trying to open tables. Rollback transaction in all storage + engines including binary log and release all locks. + + Once dynamic SQL is allowed as substatements the below if-statement + has to be adjusted to not do rollback in substatement. + */ + DBUG_ASSERT(! thd->in_sub_stmt); + if (thd->transaction_rollback_request) + { + trans_rollback_implicit(thd); + thd->mdl_context.release_transactional_locks(); + } + lex_end(lex); cleanup_stmt(); thd->restore_backup_statement(this, &stmt_backup); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 246af046d4f..4562ababca0 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -246,7 +246,8 @@ static ORDER *create_distinct_group(THD *thd, Item **ref_pointer_array, List<Item> &all_fields, bool *all_order_by_fields_used); static bool test_if_subpart(ORDER *a,ORDER *b); -static TABLE *get_sort_by_table(ORDER *a,ORDER *b,List<TABLE_LIST> &tables); +static TABLE *get_sort_by_table(ORDER *a,ORDER *b,List<TABLE_LIST> &tables, + table_map const_tables); static void calc_group_buffer(JOIN *join,ORDER *group); static bool make_group_fields(JOIN *main_join, JOIN *curr_join); static bool alloc_group_fields(JOIN *join,ORDER *group); @@ -1323,7 +1324,8 @@ TODO: make view to decide if it is possible to write to WHERE directly or make S goto setup_subq_exit; } error= -1; // Error is sent to client - sort_by_table= get_sort_by_table(order, group_list, select_lex->leaf_tables); + /* get_sort_by_table() call used to be here: */ + MEM_UNDEFINED(&sort_by_table, sizeof(sort_by_table)); /* Calculate how to do the join */ thd_proc_info(thd, "statistics"); @@ -3801,6 +3803,9 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, } } while (join->const_table_map & found_ref && ref_changed); + join->sort_by_table= get_sort_by_table(join->order, join->group_list, + join->select_lex->leaf_tables, + join->const_table_map); /* Update info on indexes that can be used for search lookups as reading const tables may has added new sargable predicates. @@ -10904,7 +10909,23 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) join->group_list ? join->join_tab+join->const_tables : join->get_sort_by_join_tab(); - if (sort_by_tab) + /* + It could be that sort_by_tab==NULL, and the plan is to use filesort() + on the first table. + */ + if (join->order) + { + join->simple_order= 0; + join->need_tmp= 1; + } + + if (join->group && !join->group_optimized_away) + { + join->need_tmp= 1; + join->simple_group= 0; + } + + if (sort_by_tab) { join->need_tmp= 1; join->simple_order= join->simple_group= 0; @@ -14396,16 +14417,44 @@ internal_remove_eq_conds(THD *thd, COND *cond, Item::cond_result *cond_value) { if (cond->type() == Item::COND_ITEM) { - List<Item_equal> new_equalities; bool and_level= ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC; List<Item> *cond_arg_list= ((Item_cond*) cond)->argument_list(); + + if (and_level) + { + /* + Remove multiple equalities that became always true (e.g. after + constant row substitution). + They would be removed later in the function anyway, but the list of + them cond_equal.current_level also must be adjusted correspondingly. + So it's easier to do it at one pass through the list of the equalities. + */ + List<Item_equal> *cond_equalities= + &((Item_cond_and *) cond)->cond_equal.current_level; + cond_arg_list->disjoin((List<Item> *) cond_equalities); + List_iterator<Item_equal> it(*cond_equalities); + Item_equal *eq_item; + while ((eq_item= it++)) + { + if (eq_item->const_item() && eq_item->val_int()) + it.remove(); + } + cond_arg_list->concat((List<Item> *) cond_equalities); + } + + List<Item_equal> new_equalities; List_iterator<Item> li(*cond_arg_list); + bool should_fix_fields= 0; Item::cond_result tmp_cond_value; - bool should_fix_fields=0; - - *cond_value=Item::COND_UNDEF; Item *item; + + /* + If the list cond_arg_list became empty then it consisted only + of always true multiple equalities. + */ + *cond_value= cond_arg_list->elements ? Item::COND_UNDEF : Item::COND_TRUE; + while ((item=li++)) { Item *new_item=internal_remove_eq_conds(thd, item, &tmp_cond_value); @@ -21308,7 +21357,8 @@ test_if_subpart(ORDER *a,ORDER *b) */ static TABLE * -get_sort_by_table(ORDER *a,ORDER *b, List<TABLE_LIST> &tables) +get_sort_by_table(ORDER *a,ORDER *b, List<TABLE_LIST> &tables, + table_map const_tables) { TABLE_LIST *table; List_iterator<TABLE_LIST> ti(tables); @@ -21322,6 +21372,23 @@ get_sort_by_table(ORDER *a,ORDER *b, List<TABLE_LIST> &tables) for (; a && b; a=a->next,b=b->next) { + /* Skip elements of a that are constant */ + while (!((*a->item)->used_tables() & ~const_tables)) + { + if (!(a= a->next)) + break; + } + + /* Skip elements of b that are constant */ + while (!((*b->item)->used_tables() & ~const_tables)) + { + if (!(b= b->next)) + break; + } + + if (!a || !b) + break; + if (!(*a->item)->eq(*b->item,1)) DBUG_RETURN(0); map|=a->item[0]->used_tables(); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index ff5f732108b..bb788175603 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2601,7 +2601,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) { Security_context *tmp_sctx= tmp->security_ctx; struct st_my_thread_var *mysys_var; - const char *val; + const char *val, *db; ulonglong max_counter; if ((!tmp->vio_ok() && !tmp->system_thread) || @@ -2628,13 +2628,13 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) table->field[2]->store(tmp_sctx->host_or_ip, strlen(tmp_sctx->host_or_ip), cs); /* DB */ - if (tmp->db) + mysql_mutex_lock(&tmp->LOCK_thd_data); + if ((db= tmp->db)) { - table->field[3]->store(tmp->db, strlen(tmp->db), cs); + table->field[3]->store(db, strlen(db), cs); table->field[3]->set_notnull(); } - mysql_mutex_lock(&tmp->LOCK_thd_data); if ((mysys_var= tmp->mysys_var)) mysql_mutex_lock(&mysys_var->mutex); /* COMMAND */ diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 602e4faa979..e3e777f06fe 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2038,6 +2038,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool non_tmp_error= 0; bool trans_tmp_table_deleted= 0, non_trans_tmp_table_deleted= 0; bool non_tmp_table_deleted= 0; + bool is_drop_tmp_if_exists_added= 0; String built_query; String built_trans_tmp_query, built_non_trans_tmp_query; DBUG_ENTER("mysql_rm_table_no_locks"); @@ -2066,6 +2067,15 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, table stems from the fact that such drop does not commit an ongoing transaction and changes to non-transactional tables must be written ahead of the transaction in some circumstances. + + 6- Slave SQL thread ignores all replicate-* filter rules + for temporary tables with 'IF EXISTS' clause. (See sql/sql_parse.cc: + mysql_execute_command() for details). These commands will be binlogged + as they are, even if the default database (from USE `db`) is not present + on the Slave. This can cause point in time recovery failures later + when user uses the slave's binlog to re-apply. Hence at the time of binary + logging, these commands will be written with fully qualified table names + and use `db` will be suppressed. */ if (!dont_log_query) { @@ -2089,6 +2099,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, if (thd->is_current_stmt_binlog_format_row() || if_exists) { + is_drop_tmp_if_exists_added= true; built_trans_tmp_query.set_charset(system_charset_info); built_trans_tmp_query.append("DROP TEMPORARY TABLE IF EXISTS "); built_non_trans_tmp_query.set_charset(system_charset_info); @@ -2165,10 +2176,12 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, String *built_ptr_query= (is_trans ? &built_trans_tmp_query : &built_non_trans_tmp_query); /* - Don't write the database name if it is the current one (or if - thd->db is NULL). + Write the database name if it is not the current one or if + thd->db is NULL or 'IF EXISTS' clause is present in 'DROP TEMPORARY' + query. */ - if (thd->db == NULL || strcmp(db,thd->db) != 0) + if (thd->db == NULL || strcmp(db,thd->db) != 0 + || is_drop_tmp_if_exists_added ) { append_identifier(thd, built_ptr_query, db, db_length); built_ptr_query->append("."); @@ -2371,7 +2384,9 @@ err: error |= thd->binlog_query(THD::STMT_QUERY_TYPE, built_non_trans_tmp_query.ptr(), built_non_trans_tmp_query.length(), - FALSE, FALSE, FALSE, 0); + FALSE, FALSE, + is_drop_tmp_if_exists_added, + 0); } if (trans_tmp_table_deleted) { @@ -2381,7 +2396,9 @@ err: error |= thd->binlog_query(THD::STMT_QUERY_TYPE, built_trans_tmp_query.ptr(), built_trans_tmp_query.length(), - TRUE, FALSE, FALSE, 0); + TRUE, FALSE, + is_drop_tmp_if_exists_added, + 0); } if (non_tmp_table_deleted) { @@ -4877,7 +4894,6 @@ mysql_discard_or_import_tablespace(THD *thd, error= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); err: - trans_rollback_stmt(thd); thd->tablespace_op=FALSE; if (error == 0) @@ -7926,6 +7942,12 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, Protocol *protocol= thd->protocol; DBUG_ENTER("mysql_checksum_table"); + /* + CHECKSUM TABLE returns results and rollbacks statement transaction, + so it should not be used in stored function or trigger. + */ + DBUG_ASSERT(! thd->in_sub_stmt); + field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2)); item->maybe_null= 1; field_list.push_back(item= new Item_int("Checksum", (longlong) 1, @@ -7944,7 +7966,6 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, strxmov(table_name, table->db ,".", table->table_name, NullS); t= table->table= open_n_lock_single_table(thd, table, TL_READ, 0); - thd->clear_error(); // these errors shouldn't get client protocol->prepare_for_resend(); protocol->store(table_name, system_charset_info); @@ -7953,7 +7974,6 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, { /* Table didn't exist */ protocol->store_null(); - thd->clear_error(); } else { @@ -8041,9 +8061,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, t->file->ha_rnd_end(); } } - thd->clear_error(); - if (! thd->in_sub_stmt) - trans_rollback_stmt(thd); + trans_rollback_stmt(thd); close_thread_tables(thd); /* Don't release metadata locks, this will be done at @@ -8051,6 +8069,21 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, */ table->table=0; // For query cache } + + if (thd->transaction_rollback_request) + { + /* + If transaction rollback was requested we honor it. To do this we + abort statement and return error as not only CHECKSUM TABLE is + rolled back but the whole transaction in which it was used. + */ + thd->protocol->remove_last_row(); + goto err; + } + + /* Hide errors from client. Return NULL for problematic tables instead. */ + thd->clear_error(); + if (protocol->write()) goto err; } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 278cc1bc567..25986428089 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1433,7 +1433,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, objects of the view. */ if (!(table->view_sctx= (Security_context *) - thd->stmt_arena->alloc(sizeof(Security_context)))) + thd->stmt_arena->calloc(sizeof(Security_context)))) goto err; security_ctx= table->view_sctx; } diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 36f70875abf..08b4953b2e4 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -670,6 +670,7 @@ static bool event_scheduler_check(sys_var *self, THD *thd, set_var *var) } static bool event_scheduler_update(sys_var *self, THD *thd, enum_var_type type) { + int err_no= 0; uint opt_event_scheduler_value= Events::opt_event_scheduler; mysql_mutex_unlock(&LOCK_global_system_variables); /* @@ -689,11 +690,14 @@ static bool event_scheduler_update(sys_var *self, THD *thd, enum_var_type type) for deadlocks. See bug#51160. */ bool ret= opt_event_scheduler_value == Events::EVENTS_ON - ? Events::start() + ? Events::start(&err_no) : Events::stop(); mysql_mutex_lock(&LOCK_global_system_variables); if (ret) - my_error(ER_EVENT_SET_VAR_ERROR, MYF(0), my_errno); + { + Events::opt_event_scheduler= Events::EVENTS_OFF; + my_error(ER_EVENT_SET_VAR_ERROR, MYF(0), err_no); + } return ret; } diff --git a/sql/transaction.cc b/sql/transaction.cc index f14b35352c3..b3bc3bf8838 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -273,6 +273,52 @@ bool trans_rollback(THD *thd) /** + Implicitly rollback the current transaction, typically + after deadlock was discovered. + + @param thd Current thread + + @retval False Success + @retval True Failure + + @note ha_rollback_low() which is indirectly called by this + function will mark XA transaction for rollback by + setting appropriate RM error status if there was + transaction rollback request. +*/ + +bool trans_rollback_implicit(THD *thd) +{ + int res; + DBUG_ENTER("trans_rollback_implict"); + + /* + Always commit/rollback statement transaction before manipulating + with the normal one. + Don't perform rollback in the middle of sub-statement, wait till + its end. + */ + DBUG_ASSERT(thd->transaction.stmt.is_empty() && !thd->in_sub_stmt); + + thd->server_status&= ~SERVER_STATUS_IN_TRANS; + DBUG_PRINT("info", ("clearing SERVER_STATUS_IN_TRANS")); + res= ha_rollback_trans(thd, true); + /* + We don't reset OPTION_BEGIN flag below to simulate implicit start + of new transacton in @@autocommit=1 mode. This is necessary to + preserve backward compatibility. + */ + thd->variables.option_bits&= ~(OPTION_KEEP_LOG); + thd->transaction.all.modified_non_trans_table= false; + + /* Rollback should clear transaction_rollback_request flag. */ + DBUG_ASSERT(! thd->transaction_rollback_request); + + DBUG_RETURN(test(res)); +} + + +/** Commit the single statement transaction. @note Note that if the autocommit is on, then the following call @@ -344,8 +390,6 @@ bool trans_rollback_stmt(THD *thd) if (thd->transaction.stmt.ha_list) { ha_rollback_trans(thd, FALSE); - if (thd->transaction_rollback_request && !thd->in_sub_stmt) - ha_rollback_trans(thd, TRUE); if (! thd->in_active_multi_stmt_transaction()) thd->tx_isolation= (enum_tx_isolation) thd->variables.tx_isolation; } diff --git a/sql/transaction.h b/sql/transaction.h index e002cd4a9dc..abe7823cf9b 100644 --- a/sql/transaction.h +++ b/sql/transaction.h @@ -30,6 +30,7 @@ bool trans_begin(THD *thd, uint flags= 0); bool trans_commit(THD *thd); bool trans_commit_implicit(THD *thd); bool trans_rollback(THD *thd); +bool trans_rollback_implicit(THD *thd); bool trans_commit_stmt(THD *thd); bool trans_rollback_stmt(THD *thd); |