diff options
Diffstat (limited to 'sql')
68 files changed, 1390 insertions, 347 deletions
diff --git a/sql/field.cc b/sql/field.cc index 6e1851d89d1..2084c661602 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1,6 +1,6 @@ /* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2008-2011 Monty Program Ab + Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2008, 2011, 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 diff --git a/sql/field.h b/sql/field.h index c86c0975117..f22bab0409d 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1,7 +1,7 @@ #ifndef FIELD_INCLUDED #define FIELD_INCLUDED -/* Copyright (c) 2000, 2011 Oracle and/or its affiliates. - Copyright (c) 2008-2011 Monty Program Ab +/* Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2008, 2011, 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 @@ -63,6 +63,9 @@ enum Derivation #define my_charset_numeric my_charset_latin1 #define MY_REPERTOIRE_NUMERIC MY_REPERTOIRE_ASCII +/* The length of the header part for each virtual column in the .frm file */ +#define FRM_VCOL_HEADER_SIZE(b) (3 + test(b)) + struct ha_field_option_struct; struct st_cache_field; @@ -2244,6 +2247,10 @@ public: { return (flags & (BINCMP_FLAG | BINARY_FLAG)) != 0; } + uint virtual_col_expr_maxlen() + { + return 255 - FRM_VCOL_HEADER_SIZE(interval != NULL); + } private: const String empty_set_string; }; diff --git a/sql/field_conv.cc b/sql/field_conv.cc index 20da18a129c..6685b334d06 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -1,5 +1,7 @@ /* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. + Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2010, 2012, 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 diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index b4181fc6d7f..78adbeff623 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -2076,6 +2076,133 @@ partition_element *ha_partition::find_partition_element(uint part_id) return NULL; } +uint ha_partition::count_query_cache_dependant_tables(uint8 *tables_type) +{ + DBUG_ENTER("ha_partition::count_query_cache_dependant_tables"); + /* Here we rely on the fact that all tables are of the same type */ + uint8 type= m_file[0]->table_cache_type(); + (*tables_type)|= type; + DBUG_PRINT("info", ("cnt: %u", (uint)m_tot_parts)); + /* + We need save underlying tables only for HA_CACHE_TBL_ASKTRANSACT: + HA_CACHE_TBL_NONTRANSACT - because all changes goes through partition table + HA_CACHE_TBL_NOCACHE - because will not be cached + HA_CACHE_TBL_TRANSACT - QC need to know that such type present + */ + DBUG_RETURN(type == HA_CACHE_TBL_ASKTRANSACT ? m_tot_parts : 0); +} + +my_bool ha_partition::reg_query_cache_dependant_table(THD *thd, + char *key, uint key_len, + uint8 type, + Query_cache *cache, + Query_cache_block_table **block_table, + handler *file, + uint *n) +{ + DBUG_ENTER("ha_partition::reg_query_cache_dependant_table"); + qc_engine_callback engine_callback; + ulonglong engine_data; + /* ask undelying engine */ + if (!file->register_query_cache_table(thd, key, + key_len, + &engine_callback, + &engine_data)) + { + DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s", + key, + key + table_share->db.length + 1)); + /* + As this can change from call to call, don't reset set + thd->lex->safe_to_cache_query + */ + thd->query_cache_is_applicable= 0; // Query can't be cached + DBUG_RETURN(TRUE); + } + (++(*block_table))->n= ++(*n); + if (!cache->insert_table(key_len, + key, (*block_table), + table_share->db.length, + type, + engine_callback, engine_data, + FALSE)) + DBUG_RETURN(TRUE); + DBUG_RETURN(FALSE); +} + + +my_bool ha_partition::register_query_cache_dependant_tables(THD *thd, + Query_cache *cache, + Query_cache_block_table **block_table, + uint *n) +{ + char *name; + uint prefix_length= table_share->table_cache_key.length + 3; + uint num_parts= m_part_info->num_parts; + uint num_subparts= m_part_info->num_subparts; + uint i= 0; + List_iterator<partition_element> part_it(m_part_info->partitions); + char key[FN_REFLEN]; + + DBUG_ENTER("ha_partition::register_query_cache_dependant_tables"); + + /* see ha_partition::count_query_cache_dependant_tables */ + if (m_file[0]->table_cache_type() != HA_CACHE_TBL_ASKTRANSACT) + DBUG_RETURN(FALSE); // nothing to register + + /* prepare static part of the key */ + memmove(key, table_share->table_cache_key.str, + table_share->table_cache_key.length); + + name= key + table_share->table_cache_key.length - 1; + name[0]= name[2]= '#'; + name[1]= 'P'; + name+= 3; + + do + { + partition_element *part_elem= part_it++; + uint part_len= strmov(name, part_elem->partition_name) - name; + if (m_is_sub_partitioned) + { + List_iterator<partition_element> subpart_it(part_elem->subpartitions); + partition_element *sub_elem; + char *sname= name + part_len; + uint j= 0, part; + sname[0]= sname[3]= '#'; + sname[1]= 'S'; + sname[2]= 'P'; + sname += 4; + do + { + sub_elem= subpart_it++; + part= i * num_subparts + j; + uint spart_len= strmov(sname, sub_elem->partition_name) - name + 1; + if (reg_query_cache_dependant_table(thd, key, + prefix_length + part_len + 4 + + spart_len, + m_file[part]->table_cache_type(), + cache, + block_table, m_file[part], + n)) + DBUG_RETURN(TRUE); + } while (++j < num_subparts); + } + else + { + if (reg_query_cache_dependant_table(thd, key, + prefix_length + part_len + 1, + m_file[i]->table_cache_type(), + cache, + block_table, m_file[i], + n)) + DBUG_RETURN(TRUE); + } + } while (++i < num_parts); + DBUG_PRINT("info", ("cnt: %u", (uint)m_tot_parts)); + DBUG_RETURN(FALSE); +} + /* Set up table share object before calling create on underlying handler diff --git a/sql/ha_partition.h b/sql/ha_partition.h index aa9179f9f69..0f922394ec5 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -544,22 +544,20 @@ public: virtual int extra(enum ha_extra_function operation); virtual int extra_opt(enum ha_extra_function operation, ulong cachesize); virtual int reset(void); - /* - Do not allow caching of partitioned tables, since we cannot return - a callback or engine_data that would work for a generic engine. - */ - virtual my_bool register_query_cache_table(THD *thd, char *table_key, - uint key_length, - qc_engine_callback - *engine_callback, - ulonglong *engine_data) - { - *engine_callback= NULL; - *engine_data= 0; - return FALSE; - } + virtual uint count_query_cache_dependant_tables(uint8 *tables_type); + virtual my_bool + register_query_cache_dependant_tables(THD *thd, + Query_cache *cache, + Query_cache_block_table **block, + uint *n); private: + my_bool reg_query_cache_dependant_table(THD *thd, + char *key, uint key_len, uint8 type, + Query_cache *cache, + Query_cache_block_table + **block_table, + handler *file, uint *n); static const uint NO_CURRENT_PART_ID; int loop_extra(enum ha_extra_function operation); void late_extra_cache(uint partition_id); diff --git a/sql/handler.cc b/sql/handler.cc index 8b3573eb23e..cb220783e33 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -507,10 +507,6 @@ int ha_initialize_handlerton(st_plugin_int *plugin) "Assigning value %d.", plugin->plugin->name, idx); hton->db_type= (enum legacy_db_type) idx; } - installed_htons[hton->db_type]= hton; - tmp= hton->savepoint_offset; - hton->savepoint_offset= savepoint_alloc_size; - savepoint_alloc_size+= tmp; /* In case a plugin is uninstalled and re-installed later, it should @@ -1301,6 +1297,7 @@ int ha_commit_trans(THD *thd, bool all) need_prepare_ordered|= (ht->prepare_ordered != NULL); need_commit_ordered|= (ht->commit_ordered != NULL); } + DEBUG_SYNC(thd, "ha_commit_trans_after_prepare"); DBUG_EXECUTE_IF("crash_commit_after_prepare", DBUG_SUICIDE();); #ifdef WITH_WSREP diff --git a/sql/handler.h b/sql/handler.h index a2277f8df90..47593845ad6 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1694,6 +1694,8 @@ public: virtual ~handler_add_index() {} }; +class Query_cache; +struct Query_cache_block_table; /** The handler class is the interface for dynamically loadable storage engines. Do not add ifdefs and take care when adding or @@ -2527,6 +2529,46 @@ public: return TRUE; } + /* + Count tables invisible from all tables list on which current one built + (like myisammrg and partitioned tables) + + tables_type mask for the tables should be added herdde + + returns number of such tables + */ + + virtual uint count_query_cache_dependant_tables(uint8 *tables_type + __attribute__((unused))) + { + return 0; + } + + /* + register tables invisible from all tables list on which current one built + (like myisammrg and partitioned tables). + + @note they should be counted by method above + + cache Query cache pointer + block Query cache block to write the table + n Number of the table + + @retval FALSE - OK + @retval TRUE - Error + */ + + virtual my_bool + register_query_cache_dependant_tables(THD *thd + __attribute__((unused)), + Query_cache *cache + __attribute__((unused)), + Query_cache_block_table **block + __attribute__((unused)), + uint *n __attribute__((unused))) + { + return FALSE; + } /* Check if the primary key (if there is one) is a clustered and a diff --git a/sql/item.cc b/sql/item.cc index 3af415fe5c2..3031e90c9b2 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1265,7 +1265,7 @@ err: bool Item::get_seconds(ulonglong *sec, ulong *sec_part) { - if (result_type() == INT_RESULT) + if (decimals == 0) { // optimize for an important special case longlong val= val_int(); bool neg= val < 0 && !unsigned_flag; diff --git a/sql/item.h b/sql/item.h index a9c1153d236..f7f3edda384 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1395,21 +1395,21 @@ public: { return cmp_context == IMPOSSIBLE_RESULT || item->cmp_context == cmp_context; } - /* + /** Test whether an expression is expensive to compute. Used during optimization to avoid computing expensive expressions during this phase. Also used to force temp tables when sorting on expensive functions. - TODO: + @todo Normally we should have a method: cost Item::execution_cost(), where 'cost' is either 'double' or some structure of various cost parameters. - NOTE - This function is now used to prevent evaluation of materialized IN - subquery predicates before it is allowed. grep for - DontEvaluateMaterializedSubqueryTooEarly to see the uses. + @note + This function is now used to prevent evaluation of expensive subquery + predicates during the optimization phase. It also prevents evaluation + of predicates that are not computable at this moment. */ virtual bool is_expensive() { diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 5f1a863d8fd..2a0ca19a4e9 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1504,6 +1504,7 @@ bool Item_in_optimizer::fix_fields(THD *thd, Item **ref) } if (args[1]->maybe_null) maybe_null=1; + with_subselect= 1; with_sum_func= with_sum_func || args[1]->with_sum_func; with_field= with_field || args[1]->with_field; used_tables_cache|= args[1]->used_tables(); @@ -3032,6 +3033,11 @@ void Item_func_case::fix_length_and_dec() nagg++; if (!(found_types= collect_cmp_types(agg, nagg))) return; + + Item *date_arg= 0; + if (found_types & (1 << TIME_RESULT)) + date_arg= find_date_time_item(args, arg_count, 0); + if (found_types & (1 << STRING_RESULT)) { /* @@ -3071,16 +3077,12 @@ void Item_func_case::fix_length_and_dec() change_item_tree_if_needed(thd, &args[nagg * 2], agg[nagg + 1]); } - Item *date_arg= 0; for (i= 0; i <= (uint)TIME_RESULT; i++) { if (found_types & (1 << i) && !cmp_items[i]) { DBUG_ASSERT((Item_result)i != ROW_RESULT); - if ((Item_result)i == TIME_RESULT) - date_arg= find_date_time_item(args, arg_count, 0); - if (!(cmp_items[i]= cmp_item::get_comparator((Item_result)i, date_arg, cmp_collation.collation))) @@ -4051,15 +4053,15 @@ void Item_func_in::fix_length_and_dec() } else { + if (found_types & (1 << TIME_RESULT)) + date_arg= find_date_time_item(args, arg_count, 0); + if (found_types & (1 << STRING_RESULT) && + agg_arg_charsets_for_comparison(cmp_collation, args, arg_count)) + return; for (i= 0; i <= (uint) TIME_RESULT; i++) { if (found_types & (1 << i) && !cmp_items[i]) { - if ((Item_result)i == STRING_RESULT && - agg_arg_charsets_for_comparison(cmp_collation, args, arg_count)) - return; - if ((Item_result)i == TIME_RESULT) - date_arg= find_date_time_item(args, arg_count, 0); if (!cmp_items[i] && !(cmp_items[i]= cmp_item::get_comparator((Item_result)i, date_arg, cmp_collation.collation))) @@ -4255,6 +4257,22 @@ Item_cond::fix_fields(THD *thd, Item **ref) if (abort_on_null) item->top_level_item(); + /* + replace degraded condition: + was: <field> + become: <field> = 1 + */ + if (item->type() == FIELD_ITEM) + { + Query_arena backup, *arena; + Item *new_item; + arena= thd->activate_stmt_arena_if_needed(&backup); + if ((new_item= new Item_func_ne(item, new Item_int(0, 1)))) + li.replace(item= new_item); + if (arena) + thd->restore_active_arena(arena, &backup); + } + // item can be substituted in fix_fields if ((!item->fixed && item->fix_fields(thd, li.ref())) || @@ -4939,6 +4957,7 @@ Item_func_regex::fix_fields(THD *thd, Item **ref) return TRUE; /* purecov: inspected */ with_sum_func=args[0]->with_sum_func || args[1]->with_sum_func; with_field= args[0]->with_field || args[1]->with_field; + with_subselect|= args[0]->with_subselect | args[1]->with_subselect; max_length= 1; decimals= 0; @@ -5308,6 +5327,28 @@ Item *Item_func_not::neg_transformer(THD *thd) /* NOT(x) -> x */ } +bool Item_func_not::fix_fields(THD *thd, Item **ref) +{ + if (args[0]->type() == FIELD_ITEM) + { + /* replace "NOT <field>" with "<filed> == 0" */ + Query_arena backup, *arena; + Item *new_item; + bool rc= TRUE; + arena= thd->activate_stmt_arena_if_needed(&backup); + if ((new_item= new Item_func_eq(args[0], new Item_int(0, 1)))) + { + new_item->name= name; + rc= (*ref= new_item)->fix_fields(thd, ref); + } + if (arena) + thd->restore_active_arena(arena, &backup); + return rc; + } + return Item_func::fix_fields(thd, ref); +} + + Item *Item_bool_rowready_func2::neg_transformer(THD *thd) { Item *item= negated_item(); @@ -5540,7 +5581,15 @@ void Item_equal::add_const(Item *c, Item *f) else { Item_func_eq *func= new Item_func_eq(c, const_item); - func->set_cmp_func(); + if (func->set_cmp_func()) + { + /* + Setting a comparison function fails when trying to compare + incompatible charsets. Charset compatibility is checked earlier, + except for constant subqueries where we may do it here. + */ + return; + } func->quick_fix_field(); cond_false= !func->val_int(); } @@ -5730,6 +5779,7 @@ bool Item_equal::fix_fields(THD *thd, Item **ref) used_tables_cache|= item->used_tables(); tmp_table_map= item->not_null_tables(); not_null_tables_cache|= tmp_table_map; + DBUG_ASSERT(!item->with_sum_func && !item->with_subselect); if (item->maybe_null) maybe_null= 1; if (!item->get_item_equal()) diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index de62bc49930..34d1a0bd0ae 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -371,9 +371,9 @@ public: Item_bool_func2(Item *a,Item *b) :Item_int_func(a,b), cmp(tmp_arg, tmp_arg+1), abort_on_null(FALSE) {} void fix_length_and_dec(); - void set_cmp_func() + int set_cmp_func() { - cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, TRUE); + return cmp.set_cmp_func(this, tmp_arg, tmp_arg+1, TRUE); } optimize_type select_optimize() const { return OPTIMIZE_OP; } virtual enum Functype rev_functype() const { return UNKNOWN_FUNC; } @@ -442,6 +442,7 @@ public: enum Functype functype() const { return NOT_FUNC; } const char *func_name() const { return "not"; } Item *neg_transformer(THD *thd); + bool fix_fields(THD *, Item **); virtual void print(String *str, enum_query_type query_type); }; @@ -508,6 +509,8 @@ public: longlong val_int(); enum Functype functype() const { return NOT_ALL_FUNC; } const char *func_name() const { return "<not>"; } + bool fix_fields(THD *thd, Item **ref) + {return Item_func::fix_fields(thd, ref);} virtual void print(String *str, enum_query_type query_type); void set_sum_test(Item_sum_hybrid *item) { test_sum_item= item; }; void set_sub_test(Item_maxmin_subselect *item) { test_sub_item= item; }; diff --git a/sql/item_func.cc b/sql/item_func.cc index b9bab372500..4691ee10e49 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3350,6 +3350,7 @@ udf_handler::fix_fields(THD *thd, Item_result_field *func, func->maybe_null=1; func->with_sum_func= func->with_sum_func || item->with_sum_func; func->with_field= func->with_field || item->with_field; + func->with_subselect|= item->with_subselect; used_tables_cache|=item->used_tables(); const_item_cache&=item->const_item(); f_args.arg_type[i]=item->result_type(); diff --git a/sql/item_func.h b/sql/item_func.h index f91107c5445..cb9c1929d7d 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -1941,6 +1941,11 @@ public: return str; } + void update_null_value() + { + execute(); + } + virtual bool change_context_processor(uchar *cntx) { context= (Name_resolution_context *)cntx; return FALSE; } @@ -1955,6 +1960,7 @@ public: { return sp_result_field; } + bool check_vcol_func_processor(uchar *int_arg) { return trace_unsupported_by_check_vcol_func_processor(func_name()); diff --git a/sql/item_row.cc b/sql/item_row.cc index 530a40c55dc..2c4a628075e 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -95,6 +95,7 @@ bool Item_row::fix_fields(THD *thd, Item **ref) maybe_null|= item->maybe_null; with_sum_func= with_sum_func || item->with_sum_func; with_field= with_field || item->with_field; + with_subselect|= item->with_subselect; } fixed= 1; return FALSE; diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index ad854b02765..9ed2627a518 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -829,7 +829,7 @@ public: { DBUG_ASSERT(args[0]->fixed); conv_charset= cs; - if (cache_if_const && args[0]->const_item() && !args[0]->with_subselect) + if (cache_if_const && args[0]->const_item() && !args[0]->is_expensive()) { uint errors= 0; String tmp, *str= args[0]->val_str(&tmp); @@ -854,6 +854,30 @@ public: } } String *val_str(String *); + longlong val_int() + { + if (args[0]->result_type() == STRING_RESULT) + return Item_str_func::val_int(); + return args[0]->val_int(); + } + double val_real() + { + if (args[0]->result_type() == STRING_RESULT) + return Item_str_func::val_real(); + return args[0]->val_real(); + } + my_decimal *val_decimal(my_decimal *d) + { + if (args[0]->result_type() == STRING_RESULT) + return Item_str_func::val_decimal(d); + return args[0]->val_decimal(d); + } + bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) + { + if (args[0]->result_type() == STRING_RESULT) + return Item_str_func::get_date(ltime, fuzzydate); + return args[0]->get_date(ltime, fuzzydate); + } void fix_length_and_dec(); const char *func_name() const { return "convert"; } virtual void print(String *str, enum_query_type query_type); diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 5458a2fb968..49d232d31be 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2002, 2011, Oracle and/or its affiliates. - Copyright (c) 2010, 2011, Monty Program Ab +/* Copyright (c) 2002, 2012, Oracle and/or its affiliates. + Copyright (c) 2010, 2012, 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 @@ -287,7 +287,7 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) else goto end; - if ((uncacheable= engine->uncacheable())) + if ((uncacheable= engine->uncacheable() & ~UNCACHEABLE_EXPLAIN)) { const_item_cache= 0; if (uncacheable & UNCACHEABLE_RAND) @@ -523,6 +523,48 @@ void Item_subselect::recalc_used_tables(st_select_lex *new_parent, */ } + +/** + Determine if a subquery is expensive to execute during query optimization. + + @details The cost of execution of a subquery is estimated based on an + estimate of the number of rows the subquery will access during execution. + This measure is used instead of JOIN::read_time, because it is considered + to be much more reliable than the cost estimate. + + @return true if the subquery is expensive + @return false otherwise +*/ +bool Item_subselect::is_expensive() +{ + double examined_rows= 0; + + for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) + { + JOIN *cur_join= sl->join; + if (!cur_join) + continue; + + /* If a subquery is not optimized we cannot estimate its cost. */ + if (!cur_join->join_tab) + return true; + + if (sl->first_inner_unit()) + { + /* + Subqueries that contain subqueries are considered expensive. + @todo: accumulate the cost of subqueries. + */ + return true; + } + + examined_rows+= cur_join->get_examined_rows(); + } + + return (examined_rows > thd->variables.expensive_subquery_limit); +} + + bool Item_subselect::walk(Item_processor processor, bool walk_subquery, uchar *argument) { @@ -904,6 +946,15 @@ void Item_maxmin_subselect::print(String *str, enum_query_type query_type) void Item_maxmin_subselect::no_rows_in_result() { + /* + Subquery predicates outside of the SELECT list must be evaluated in order + to possibly filter the special result row generated for implicit grouping + if the subquery is in the HAVING clause. + If the predicate is constant, we need its actual value in the only result + row for queries with implicit grouping. + */ + if (parsing_place != SELECT_LIST || const_item()) + return; value= Item_cache::get_cache(new Item_null()); null_value= 0; was_values= 0; @@ -913,6 +964,15 @@ void Item_maxmin_subselect::no_rows_in_result() void Item_singlerow_subselect::no_rows_in_result() { + /* + Subquery predicates outside of the SELECT list must be evaluated in order + to possibly filter the special result row generated for implicit grouping + if the subquery is in the HAVING clause. + If the predicate is constant, we need its actual value in the only result + row for queries with implicit grouping. + */ + if (parsing_place != SELECT_LIST || const_item()) + return; value= Item_cache::get_cache(new Item_null()); reset(); make_const(); @@ -1375,6 +1435,15 @@ Item* Item_exists_subselect::expr_cache_insert_transformer(uchar *thd_arg) void Item_exists_subselect::no_rows_in_result() { + /* + Subquery predicates outside of the SELECT list must be evaluated in order + to possibly filter the special result row generated for implicit grouping + if the subquery is in the HAVING clause. + If the predicate is constant, we need its actual value in the only result + row for queries with implicit grouping. + */ + if (parsing_place != SELECT_LIST || const_item()) + return; value= 0; null_value= 0; make_const(); @@ -2719,6 +2788,15 @@ void Item_allany_subselect::print(String *str, enum_query_type query_type) void Item_allany_subselect::no_rows_in_result() { + /* + Subquery predicates outside of the SELECT list must be evaluated in order + to possibly filter the special result row generated for implicit grouping + if the subquery is in the HAVING clause. + If the predicate is constant, we need its actual value in the only result + row for queries with implicit grouping. + */ + if (parsing_place != SELECT_LIST || const_item()) + return; value= 0; null_value= 0; was_null= 0; diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 0e0f61aedd9..05c4528490f 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -159,8 +159,8 @@ public: null_value= 1; } /** - Set the subquery result to the default value for the predicate when the - subquery is known to produce an empty result. + Set the subquery result to a default value consistent with the semantics of + the result row produced for queries with implicit grouping. */ void no_rows_in_result()= 0; virtual bool select_transformer(JOIN *join); @@ -209,7 +209,7 @@ public: */ bool is_evaluated() const; bool is_uncacheable() const; - bool is_expensive() { return TRUE; } + bool is_expensive(); /* Used by max/min subquery to initialize value presence registration @@ -235,7 +235,7 @@ public: @retval TRUE if the predicate is expensive @retval FALSE otherwise */ - bool is_expensive_processor(uchar *arg) { return TRUE; } + bool is_expensive_processor(uchar *arg) { return is_expensive(); } /** Get the SELECT_LEX structure associated with this Item. @@ -581,6 +581,10 @@ public: bool fix_fields(THD *thd, Item **ref); void fix_length_and_dec(); void fix_after_pullout(st_select_lex *new_parent, Item **ref); + bool const_item() const + { + return Item_subselect::const_item() && left_expr->const_item(); + } void update_used_tables(); bool setup_mat_engine(); bool init_left_expr_cache(); diff --git a/sql/item_sum.cc b/sql/item_sum.cc index afe4f34bd05..0b21ba92558 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1135,6 +1135,7 @@ Item_sum_num::fix_fields(THD *thd, Item **ref) if (args[i]->fix_fields(thd, args + i) || args[i]->check_cols(1)) return TRUE; set_if_bigger(decimals, args[i]->decimals); + with_subselect|= args[i]->with_subselect; } result_field=0; max_length=float_length(decimals); @@ -1165,6 +1166,7 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) (item= args[0])->check_cols(1)) return TRUE; decimals=item->decimals; + with_subselect= args[0]->with_subselect; switch (hybrid_type= item->result_type()) { case INT_RESULT: @@ -3319,6 +3321,7 @@ Item_func_group_concat::fix_fields(THD *thd, Item **ref) args[i]->fix_fields(thd, args + i)) || args[i]->check_cols(1)) return TRUE; + with_subselect|= args[i]->with_subselect; } /* skip charset aggregation for order columns */ diff --git a/sql/log.cc b/sql/log.cc index 2ef6463aa81..3547877bad8 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2628,9 +2628,10 @@ void MYSQL_LOG::init_pthread_objects() SYNOPSIS close() - exiting Bitmask. For the slow and general logs the only used bit is - LOG_CLOSE_TO_BE_OPENED. This is used if we intend to call - open at once after close. + exiting Bitmask. LOG_CLOSE_TO_BE_OPENED is used if we intend to call + open at once after close. LOG_CLOSE_DELAYED_CLOSE is used for + binlog rotation, to delay actual close of the old file until + we have successfully created the new file. NOTES One can do an open on the object at once after doing a close. @@ -2651,7 +2652,8 @@ void MYSQL_LOG::close(uint exiting) sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); } - if (mysql_file_close(log_file.file, MYF(MY_WME)) && ! write_error) + if (!(exiting & LOG_CLOSE_DELAYED_CLOSE) && + mysql_file_close(log_file.file, MYF(MY_WME)) && ! write_error) { write_error= 1; sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); @@ -3353,6 +3355,10 @@ bool MYSQL_BIN_LOG::open(const char *log_name, if (write_file_name_to_index_file) { #ifdef HAVE_REPLICATION +#ifdef ENABLED_DEBUG_SYNC + if (current_thd) + DEBUG_SYNC(current_thd, "binlog_open_before_update_index"); +#endif DBUG_EXECUTE_IF("crash_create_critical_before_update_index", DBUG_SUICIDE();); #endif @@ -4461,6 +4467,10 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) { int error= 0, close_on_error= FALSE; char new_name[FN_REFLEN], *new_name_ptr, *old_name, *file_to_open; + uint close_flag; + bool delay_close= false; + File old_file; + LINT_INIT(old_file); DBUG_ENTER("MYSQL_BIN_LOG::new_file_impl"); if (!is_open()) @@ -4544,7 +4554,20 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) } old_name=name; name=0; // Don't free name - close(LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX); + close_flag= LOG_CLOSE_TO_BE_OPENED | LOG_CLOSE_INDEX; + if (!is_relay_log) + { + /* + We need to keep the old binlog file open (and marked as in-use) until + the new one is fully created and synced to disk and index. Otherwise we + leave a window where if we crash, there is no binlog file marked as + crashed for server restart to detect the need for recovery. + */ + old_file= log_file.file; + close_flag|= LOG_CLOSE_DELAYED_CLOSE; + delay_close= true; + } + close(close_flag); if (log_type == LOG_BIN && checksum_alg_reset != BINLOG_CHECKSUM_ALG_UNDEF) { DBUG_ASSERT(!is_relay_log); @@ -4587,6 +4610,12 @@ int MYSQL_BIN_LOG::new_file_impl(bool need_lock) end: + if (delay_close) + { + clear_inuse_flag_when_closing(old_file); + mysql_file_close(old_file, MYF(MY_WME)); + } + if (error && close_on_error /* rotate or reopen failed */) { /* @@ -6493,6 +6522,8 @@ int MYSQL_BIN_LOG::wait_for_update_bin_log(THD* thd, - LOG_CLOSE_TO_BE_OPENED : if we intend to call open at once after close. - LOG_CLOSE_STOP_EVENT : write a 'stop' event to the log + - LOG_CLOSE_DELAYED_CLOSE : do not yet close the file and clear the + LOG_EVENT_BINLOG_IN_USE_F flag @note One can do an open on the object at once after doing a close. @@ -6522,12 +6553,11 @@ void MYSQL_BIN_LOG::close(uint exiting) #endif /* HAVE_REPLICATION */ /* don't pwrite in a file opened with O_APPEND - it doesn't work */ - if (log_file.type == WRITE_CACHE && log_type == LOG_BIN) + if (log_file.type == WRITE_CACHE && log_type == LOG_BIN + && !(exiting & LOG_CLOSE_DELAYED_CLOSE)) { - my_off_t offset= BIN_LOG_HEADER_SIZE + FLAGS_OFFSET; my_off_t org_position= mysql_file_tell(log_file.file, MYF(0)); - uchar flags= 0; // clearing LOG_EVENT_BINLOG_IN_USE_F - mysql_file_pwrite(log_file.file, &flags, 1, offset, MYF(0)); + clear_inuse_flag_when_closing(log_file.file); /* Restore position so that anything we have in the IO_cache is written to the correct position. @@ -6562,6 +6592,18 @@ void MYSQL_BIN_LOG::close(uint exiting) } +/* + Clear the LOG_EVENT_BINLOG_IN_USE_F; this marks the binlog file as cleanly + closed and not needing crash recovery. +*/ +void MYSQL_BIN_LOG::clear_inuse_flag_when_closing(File file) +{ + my_off_t offset= BIN_LOG_HEADER_SIZE + FLAGS_OFFSET; + uchar flags= 0; // clearing LOG_EVENT_BINLOG_IN_USE_F + mysql_file_pwrite(file, &flags, 1, offset, MYF(0)); +} + + void MYSQL_BIN_LOG::set_max_size(ulong max_size_arg) { /* diff --git a/sql/log.h b/sql/log.h index 2bc4d0e49d7..55782952d21 100644 --- a/sql/log.h +++ b/sql/log.h @@ -202,6 +202,7 @@ extern TC_LOG_DUMMY tc_log_dummy; #define LOG_CLOSE_INDEX 1 #define LOG_CLOSE_TO_BE_OPENED 2 #define LOG_CLOSE_STOP_EVENT 4 +#define LOG_CLOSE_DELAYED_CLOSE 8 /* Maximum unique log filename extension. @@ -222,6 +223,11 @@ class Relay_log_info; extern PSI_mutex_key key_LOG_INFO_lock; #endif +/* + Note that we destroy the lock mutex in the desctructor here. + This means that object instances cannot be destroyed/go out of scope, + until we have reset thd->current_linfo to NULL; + */ typedef struct st_log_info { char log_file_name[FN_REFLEN]; @@ -477,8 +483,8 @@ class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG void mark_xids_active(uint xid_count); public: - MYSQL_LOG::generate_name; - MYSQL_LOG::is_open; + using MYSQL_LOG::generate_name; + using MYSQL_LOG::is_open; /* This is relay log */ bool is_relay_log; @@ -666,6 +672,7 @@ public: bool need_mutex); bool reset_logs(THD* thd); void close(uint exiting); + void clear_inuse_flag_when_closing(File file); // iterating through the log index file int find_log_pos(LOG_INFO* linfo, const char* log_name, diff --git a/sql/log_event.cc b/sql/log_event.cc index 95ac3fc278c..a679249ae24 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1,5 +1,6 @@ /* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. + Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2010, 2012, 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 @@ -5843,11 +5844,12 @@ void Intvar_log_event::print(FILE* file, PRINT_EVENT_INFO* print_event_info) #endif +#if defined(HAVE_REPLICATION)&& !defined(MYSQL_CLIENT) + /* Intvar_log_event::do_apply_event() */ -#if defined(HAVE_REPLICATION)&& !defined(MYSQL_CLIENT) int Intvar_log_event::do_apply_event(Relay_log_info const *rli) { /* @@ -5856,6 +5858,9 @@ int Intvar_log_event::do_apply_event(Relay_log_info const *rli) */ const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT); + if (rli->deferred_events_collecting) + return rli->deferred_events->add(this); + switch (type) { case LAST_INSERT_ID_EVENT: thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 1; @@ -5962,6 +5967,9 @@ int Rand_log_event::do_apply_event(Relay_log_info const *rli) */ const_cast<Relay_log_info*>(rli)->set_flag(Relay_log_info::IN_STMT); + if (rli->deferred_events_collecting) + return rli->deferred_events->add(this); + thd->rand.seed1= (ulong) seed1; thd->rand.seed2= (ulong) seed2; return 0; @@ -5988,6 +5996,29 @@ Rand_log_event::do_shall_skip(Relay_log_info *rli) return continue_group(rli); } +/** + Exec deferred Int-, Rand- and User- var events prefixing + a Query-log-event event. + + @param thd THD handle + + @return false on success, true if a failure in an event applying occurred. +*/ +bool slave_execute_deferred_events(THD *thd) +{ + bool res= false; + Relay_log_info *rli= thd->rli_slave; + + DBUG_ASSERT(rli && (!rli->deferred_events_collecting || rli->deferred_events)); + + if (!rli->deferred_events_collecting || rli->deferred_events->is_empty()) + return res; + + res= rli->deferred_events->execute(rli); + + return res; +} + #endif /* !MYSQL_CLIENT */ @@ -6426,6 +6457,10 @@ int User_var_log_event::do_apply_event(Relay_log_info const *rli) { Item *it= 0; CHARSET_INFO *charset; + + if (rli->deferred_events_collecting) + return rli->deferred_events->add(this); + if (!(charset= get_charset(charset_number, MYF(MY_WME)))) return 1; LEX_STRING user_var_name; diff --git a/sql/log_event.h b/sql/log_event.h index 78dccb3cac1..cccab93e0d5 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -4258,6 +4258,14 @@ private: const char* log_ident; uint ident_len; }; + +/** + The function is called by slave applier in case there are + active table filtering rules to force gathering events associated + with Query-log-event into an array to execute + them once the fate of the Query is determined for execution. +*/ +bool slave_execute_deferred_events(THD *thd); #endif int append_query_string(THD *thd, CHARSET_INFO *csinfo, diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 040b35d28f1..8f2c515e11c 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. +/* Copyright (c) 2007, 2012, 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 diff --git a/sql/mysql_install_db.cc b/sql/mysql_install_db.cc index 364dca9120a..6e2c6ec07f3 100644 --- a/sql/mysql_install_db.cc +++ b/sql/mysql_install_db.cc @@ -119,8 +119,8 @@ static void die(const char *fmt, ...) fprintf(stderr, "http://kb.askmonty.org/v/installation-issues-on-windows contains some help\n" "for solving the most common problems. If this doesn't help you, please\n" - "leave a comment in the knowledge base or file a bug report at\n" - "https://bugs.launchpad.net/maria"); + "leave a comment in the Knowledgebase or file a bug report at\n" + "http://mariadb.org/jira"); } fflush(stderr); va_end(args); @@ -247,7 +247,7 @@ static char *init_bootstrap_command_line(char *cmdline, size_t size) "\"\"%s\" --no-defaults --bootstrap" " \"--language=%s\\share\\english\"" " --basedir=. --datadir=. --default-storage-engine=myisam" - " --max_allowed_packet=9M --loose-skip-innodb --loose-skip-pbxt" + " --max_allowed_packet=9M --loose-skip-innodb" " --net-buffer-length=16k\"", mysqld_path, basedir); return cmdline; } diff --git a/sql/mysqld.cc b/sql/mysqld.cc index eaaff295ae0..dfdf1c41c0f 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -110,7 +110,7 @@ ulong wsrep_running_threads = 0; // # of currently running wsrep threads /* We have HAVE_valgrind below as this speeds up the shutdown of MySQL */ -#if defined(HAVE_DEC_3_2_THREADS) || defined(SIGNALS_DONT_BREAK_READ) || defined(HAVE_valgrind) && defined(__linux__) +#if defined(SIGNALS_DONT_BREAK_READ) || defined(HAVE_valgrind) && defined(__linux__) #define HAVE_CLOSE_SERVER_SOCK 1 #endif @@ -694,6 +694,7 @@ mysql_cond_t COND_wsrep_rollback; wsrep_aborting_thd_t wsrep_aborting_thd= NULL; mysql_mutex_t LOCK_wsrep_replaying; mysql_cond_t COND_wsrep_replaying; +mysql_mutex_t LOCK_wsrep_slave_threads; int wsrep_replaying= 0; static void wsrep_close_threads(THD* thd); #endif @@ -768,7 +769,8 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_prep_xids, #ifdef WITH_WSREP PSI_mutex_key key_LOCK_wsrep_rollback, key_LOCK_wsrep_thd, key_LOCK_wsrep_replaying, key_LOCK_wsrep_ready, key_LOCK_wsrep_sst, - key_LOCK_wsrep_sst_thread, key_LOCK_wsrep_sst_init; + key_LOCK_wsrep_sst_thread, key_LOCK_wsrep_sst_init, + key_LOCK_wsrep_slave_threads; #endif PSI_mutex_key key_RELAYLOG_LOCK_index; @@ -846,6 +848,7 @@ static PSI_mutex_info all_server_mutexes[]= { &key_LOCK_wsrep_rollback, "LOCK_wsrep_rollback", PSI_FLAG_GLOBAL}, { &key_LOCK_wsrep_thd, "THD::LOCK_wsrep_thd", 0}, { &key_LOCK_wsrep_replaying, "LOCK_wsrep_replaying", PSI_FLAG_GLOBAL}, + { &key_LOCK_wsrep_slave_threads, "LOCK_wsrep_slave_threads", PSI_FLAG_GLOBAL}, #endif { &key_PARTITION_LOCK_auto_inc, "HA_DATA_PARTITION::LOCK_auto_inc", 0} }; @@ -2047,6 +2050,7 @@ static void clean_up_mutexes() (void) mysql_cond_destroy(&COND_wsrep_rollback); (void) mysql_mutex_destroy(&LOCK_wsrep_replaying); (void) mysql_cond_destroy(&COND_wsrep_replaying); + (void) mysql_mutex_destroy(&LOCK_wsrep_slave_threads); #endif mysql_mutex_destroy(&LOCK_server_started); mysql_cond_destroy(&COND_server_started); @@ -3051,11 +3055,9 @@ static void start_signal_handler(void) DBUG_ENTER("start_signal_handler"); (void) pthread_attr_init(&thr_attr); -#if !defined(HAVE_DEC_3_2_THREADS) pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_SYSTEM); (void) pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED); (void) my_setstacksize(&thr_attr,my_thread_stack_size); -#endif mysql_mutex_lock(&LOCK_thread_count); if ((error= mysql_thread_create(key_thread_signal_hand, @@ -3542,14 +3544,23 @@ static int init_common_variables() return 1; #ifdef HAVE_TZNAME - { - struct tm tm_tmp; - localtime_r(&server_start_time,&tm_tmp); - strmake(system_time_zone, tzname[tm_tmp.tm_isdst != 0 ? 1 : 0], - sizeof(system_time_zone)-1); + struct tm tm_tmp; + localtime_r(&server_start_time,&tm_tmp); + const char *tz_name= tzname[tm_tmp.tm_isdst != 0 ? 1 : 0]; +#ifdef _WIN32 + /* + Time zone name may be localized and contain non-ASCII characters, + Convert from ANSI encoding to UTF8. + */ + wchar_t wtz_name[sizeof(system_time_zone)]; + mbstowcs(wtz_name, tz_name, sizeof(system_time_zone)-1); + WideCharToMultiByte(CP_UTF8,0, wtz_name, -1, system_time_zone, + sizeof(system_time_zone) - 1, NULL, NULL); +#else + strmake(system_time_zone, tz_name, sizeof(system_time_zone)-1); +#endif /* _WIN32 */ +#endif /* HAVE_TZNAME */ - } -#endif /* We set SYSTEM time zone as reasonable default and also for failure of my_tz_init() and bootstrap mode. @@ -4065,6 +4076,8 @@ static int init_thread_environment() mysql_mutex_init(key_LOCK_wsrep_replaying, &LOCK_wsrep_replaying, MY_MUTEX_INIT_FAST); mysql_cond_init(key_COND_wsrep_replaying, &COND_wsrep_replaying, NULL); + mysql_mutex_init(key_LOCK_wsrep_slave_threads, + &LOCK_wsrep_slave_threads, MY_MUTEX_INIT_FAST); #endif return 0; } @@ -5002,9 +5015,7 @@ void wsrep_close_client_connections(my_bool wait_to_end) void wsrep_close_applier(THD *thd) { - if (wsrep_debug) - WSREP_INFO("closing applier %ld", thd->thread_id); - + WSREP_DEBUG("closing applier %ld", thd->thread_id); wsrep_close_thread(thd); } @@ -5021,10 +5032,30 @@ static void wsrep_close_threads(THD *thd) /* We skip slave threads & scheduler on this first loop through. */ if (tmp->wsrep_applier && tmp != thd) { - if (wsrep_debug) - WSREP_INFO("closing wsrep thread %ld", tmp->thread_id); + WSREP_DEBUG("closing wsrep thread %ld", tmp->thread_id); wsrep_close_thread (tmp); + } + } + mysql_mutex_unlock(&LOCK_thread_count); +} + +void wsrep_close_applier_threads(int count) +{ + THD *tmp; + mysql_mutex_lock(&LOCK_thread_count); // For unlink from list + + I_List_iterator<THD> it(threads); + while ((tmp=it++) && count) + { + DBUG_PRINT("quit",("Informing thread %ld that it's time to die", + tmp->thread_id)); + /* We skip slave threads & scheduler on this first loop through. */ + if (tmp->wsrep_applier) + { + WSREP_DEBUG("closing wsrep applier thread %ld", tmp->thread_id); + tmp->wsrep_applier_closing= TRUE; + count--; } } diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 0390ac1101e..da328063e56 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -2008,7 +2008,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) if (reuse_handler) { DBUG_PRINT("info", ("Reusing handler 0x%lx", (long) file)); - if (init() || reset()) + if (init()) { DBUG_RETURN(1); } @@ -2043,7 +2043,7 @@ int QUICK_RANGE_SELECT::init_ror_merged_scan(bool reuse_handler) if (file->ha_external_lock(thd, F_RDLCK)) goto failure; - if (init() || reset()) + if (init()) { file->ha_external_lock(thd, F_UNLCK); file->ha_close(); @@ -2090,7 +2090,18 @@ end: head->key_read= org_key_read; bitmap_copy(&column_bitmap, head->read_set); head->column_bitmaps_set(&column_bitmap, &column_bitmap); - + + if (reset()) + { + if (!reuse_handler) + { + file->ha_external_lock(thd, F_UNLCK); + file->ha_close(); + goto failure; + } + else + DBUG_RETURN(1); + } DBUG_RETURN(0); failure: diff --git a/sql/opt_range.h b/sql/opt_range.h index 545e9e3c7b8..efb45f96345 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -990,7 +990,7 @@ class SQL_SELECT :public Sql_alloc { key_map quick_keys; // Possible quick keys key_map needed_reg; // Possible quick keys after prev tables. table_map const_tables,read_tables; - bool free_cond; + bool free_cond; /* Currently not used and always FALSE */ SQL_SELECT(); ~SQL_SELECT(); diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index a5a68d0d306..8d1cbeba5f4 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -4894,7 +4894,43 @@ static void remove_subq_pushed_predicates(JOIN *join, Item **where) bool JOIN::optimize_unflattened_subqueries() { - return select_lex->optimize_unflattened_subqueries(); + return select_lex->optimize_unflattened_subqueries(false); +} + +/** + Optimize all constant subqueries of a query that were not flattened into + a semijoin. + + @details + Similar to other constant conditions, constant subqueries can be used in + various constant optimizations. Having optimized constant subqueries before + these constant optimizations, makes it possible to estimate if a subquery + is "cheap" enough to be executed during the optimization phase. + + Constant subqueries can be optimized and evaluated independent of the outer + query, therefore if const_only = true, this method can be called early in + the optimization phase of the outer query. + + @return Operation status + @retval FALSE success. + @retval TRUE error occurred. +*/ + +bool JOIN::optimize_constant_subqueries() +{ + ulonglong save_options= select_lex->options; + bool res; + /* + Constant subqueries may be executed during the optimization phase. + In EXPLAIN mode the optimizer doesn't initialize many of the data structures + needed for execution. In order to make it possible to execute subqueries + during optimization, constant subqueries must be optimized for execution, + not for EXPLAIN. + */ + select_lex->options&= ~SELECT_DESCRIBE; + res= select_lex->optimize_unflattened_subqueries(true); + select_lex->options= save_options; + return res; } @@ -5295,7 +5331,14 @@ bool JOIN::choose_subquery_plan(table_map join_tables) by the IN predicate. */ outer_join= unit->outer_select() ? unit->outer_select()->join : NULL; - if (outer_join && outer_join->table_count > 0) + /* + Get the cost of the outer join if: + (1) It has at least one table, and + (2) It has been already optimized (if there is no join_tab, then the + outer join has not been optimized yet). + */ + if (outer_join && outer_join->table_count > 0 && // (1) + outer_join->join_tab) // (2) { /* TODO: diff --git a/sql/password.c b/sql/password.c index 1a12a81f7c6..947620ddf7a 100644 --- a/sql/password.c +++ b/sql/password.c @@ -1,5 +1,6 @@ /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. + Copyright (c) 2012, 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 diff --git a/sql/rpl_rli.cc b/sql/rpl_rli.cc index 940fc201bae..252b4f3f5b9 100644 --- a/sql/rpl_rli.cc +++ b/sql/rpl_rli.cc @@ -52,8 +52,8 @@ Relay_log_info::Relay_log_info(bool is_slave_recovery) inited(0), abort_slave(0), slave_running(0), until_condition(UNTIL_NONE), until_log_pos(0), retried_trans(0), tables_to_lock(0), tables_to_lock_count(0), - last_event_start_time(0), m_flags(0), row_stmt_start_timestamp(0), - long_find_row_note_printed(false), + last_event_start_time(0), deferred_events(NULL),m_flags(0), + row_stmt_start_timestamp(0), long_find_row_note_printed(false), m_annotate_event(0) { DBUG_ENTER("Relay_log_info::Relay_log_info"); diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index 958002561bc..b989283deb4 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -389,6 +389,41 @@ public: */ time_t last_event_start_time; + /* + A container to hold on Intvar-, Rand-, Uservar- log-events in case + the slave is configured with table filtering rules. + The withhold events are executed when their parent Query destiny is + determined for execution as well. + */ + Deferred_log_events *deferred_events; + + /* + State of the container: true stands for IRU events gathering, + false does for execution, either deferred or direct. + */ + bool deferred_events_collecting; + + /* + Returns true if the argument event resides in the containter; + more specifically, the checking is done against the last added event. + */ + bool is_deferred_event(Log_event * ev) + { + return deferred_events_collecting ? deferred_events->is_last(ev) : false; + }; + /* The general cleanup that slave applier may need at the end of query. */ + inline void cleanup_after_query() + { + if (deferred_events) + deferred_events->rewind(); + }; + /* The general cleanup that slave applier may need at the end of session. */ + void cleanup_after_session() + { + if (deferred_events) + delete deferred_events; + }; + /** Helper function to do after statement completion. diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc index 71fa5c8909c..388a6c9f9bb 100644 --- a/sql/rpl_utility.cc +++ b/sql/rpl_utility.cc @@ -19,6 +19,7 @@ #ifndef MYSQL_CLIENT #include "unireg.h" // REQUIRED by later includes #include "rpl_rli.h" +#include "log_event.h" #include "sql_select.h" /** @@ -1057,6 +1058,7 @@ table_def::~table_def() #endif } + /** @param even_buf point to the buffer containing serialized event @param event_len length of the event accounting possible checksum alg @@ -1112,3 +1114,68 @@ bool event_checksum_test(uchar *event_buf, ulong event_len, uint8 alg) } return DBUG_EVALUATE_IF("simulate_checksum_test_failure", TRUE, res); } + + +#if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION) + +Deferred_log_events::Deferred_log_events(Relay_log_info *rli) : last_added(NULL) +{ + my_init_dynamic_array(&array, sizeof(Log_event *), 32, 16); +} + +Deferred_log_events::~Deferred_log_events() +{ + delete_dynamic(&array); +} + +int Deferred_log_events::add(Log_event *ev) +{ + last_added= ev; + insert_dynamic(&array, (uchar*) &ev); + return 0; +} + +bool Deferred_log_events::is_empty() +{ + return array.elements == 0; +} + +bool Deferred_log_events::execute(Relay_log_info *rli) +{ + bool res= false; + + DBUG_ASSERT(rli->deferred_events_collecting); + + rli->deferred_events_collecting= false; + for (uint i= 0; !res && i < array.elements; i++) + { + Log_event *ev= (* (Log_event **) + dynamic_array_ptr(&array, i)); + res= ev->apply_event(rli); + } + rli->deferred_events_collecting= true; + return res; +} + +void Deferred_log_events::rewind() +{ + /* + Reset preceeding Query log event events which execution was + deferred because of slave side filtering. + */ + if (!is_empty()) + { + for (uint i= 0; i < array.elements; i++) + { + Log_event *ev= *(Log_event **) dynamic_array_ptr(&array, i); + delete ev; + } + if (array.elements > array.max_element) + freeze_size(&array); + reset_dynamic(&array); + } + last_added= NULL; +} + +#endif + diff --git a/sql/rpl_utility.h b/sql/rpl_utility.h index 1f5577d8b8b..9046891e27f 100644 --- a/sql/rpl_utility.h +++ b/sql/rpl_utility.h @@ -29,7 +29,7 @@ #include "mysql_com.h" class Relay_log_info; - +class Log_event; /** A table definition from the master. @@ -262,6 +262,24 @@ CPP_UNNAMED_NS_START }; CPP_UNNAMED_NS_END + +class Deferred_log_events +{ +private: + DYNAMIC_ARRAY array; + Log_event *last_added; + +public: + Deferred_log_events(Relay_log_info *rli); + ~Deferred_log_events(); + /* queue for exection at Query-log-event time prior the Query */; + int add(Log_event *ev); + bool is_empty(); + bool execute(Relay_log_info *rli); + void rewind(); + bool is_last(Log_event *ev) { return ev == last_added; }; +}; + #endif // NB. number of printed bit values is limited to sizeof(buf) - 1 diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 140220dfa9c..923e8e73bc7 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6501,6 +6501,9 @@ ER_TABLE_IN_FK_CHECK ER_UNUSED_1 eng "You should never see it" +ER_BINLOG_UNSAFE_AUTOINC_NOT_FIRST + eng "INSERT into autoincrement field which is not the first part in the composed primary key is unsafe." + # # End of 5.5 error messages. # diff --git a/sql/signal_handler.cc b/sql/signal_handler.cc index 37f28844a7c..edc33c4d63b 100644 --- a/sql/signal_handler.cc +++ b/sql/signal_handler.cc @@ -138,23 +138,6 @@ extern "C" sig_handler handle_fatal_signal(int sig) my_safe_printf_stderr("%s", "Hope that's ok; if not, decrease some variables in the equation.\n\n"); -#if defined(HAVE_LINUXTHREADS) -#define UNSAFE_DEFAULT_LINUX_THREADS 200 - if (sizeof(char*) == 4 && thread_count > UNSAFE_DEFAULT_LINUX_THREADS) - { - my_safe_printf_stderr( - "You seem to be running 32-bit Linux and have " - "%d concurrent connections.\n" - "If you have not changed STACK_SIZE in LinuxThreads " - "and built the binary \n" - "yourself, LinuxThreads is quite likely to steal " - "a part of the global heap for\n" - "the thread stack. Please read " - "http://dev.mysql.com/doc/mysql/en/linux-installation.html\n\n" - thread_count); - } -#endif /* HAVE_LINUXTHREADS */ - #ifdef HAVE_STACKTRACE thd= current_thd; diff --git a/sql/slave.cc b/sql/slave.cc index 94a485cbe29..2b387012f85 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2797,7 +2797,8 @@ static int exec_relay_log_event(THD* thd, Relay_log_info* rli) /* fall through */ default: DBUG_PRINT("info", ("Deleting the event after it has been executed")); - delete ev; + if (!rli->is_deferred_event(ev)) + delete ev; break; } @@ -3461,6 +3462,12 @@ pthread_handler_t handle_slave_sql(void *arg) goto err_during_init; } thd->init_for_queries(); + thd->rli_slave= rli; + if ((rli->deferred_events_collecting= rpl_filter->is_on())) + { + rli->deferred_events= new Deferred_log_events(rli); + } + thd->temporary_tables = rli->save_temporary_tables; // restore temp tables set_thd_in_use_temporary_tables(rli); // (re)set sql_thd in use for saved temp tables /* diff --git a/sql/spatial.cc b/sql/spatial.cc index 1616f93241d..e82eec26fdb 100644 --- a/sql/spatial.cc +++ b/sql/spatial.cc @@ -1,6 +1,6 @@ /* - Copyright (c) 2002, 2011, Oracle and/or its affiliates. - Copyright (c) 2011, Monty Program Ab + Copyright (c) 2002, 2012, Oracle and/or its affiliates. + Copyright (c) 2011, 2012, 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 diff --git a/sql/spatial.h b/sql/spatial.h index 0d0560656f0..07675d59040 100644 --- a/sql/spatial.h +++ b/sql/spatial.h @@ -554,5 +554,5 @@ public: struct Geometry_buffer : public my_aligned_storage<sizeof(Gis_point), MY_ALIGNOF(Gis_point)> {}; -#endif /*HAVE_SPATAIAL*/ +#endif /*HAVE_SPATIAL*/ #endif diff --git a/sql/sql_base.cc b/sql/sql_base.cc index be30e085b05..96cb4d8a045 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -223,6 +223,7 @@ static bool has_write_table_with_auto_increment(TABLE_LIST *tables); static bool has_write_table_with_auto_increment_and_select(TABLE_LIST *tables); +static bool has_write_table_auto_increment_not_first_in_pk(TABLE_LIST *tables); uint cached_open_tables(void) { @@ -5781,6 +5782,12 @@ bool lock_tables(THD *thd, TABLE_LIST *tables, uint count, if (thd->variables.binlog_format != BINLOG_FORMAT_ROW && tables && has_write_table_with_auto_increment_and_select(tables)) thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_WRITE_AUTOINC_SELECT); + /* Todo: merge all has_write_table_auto_inc with decide_logging_format */ + if (thd->variables.binlog_format != BINLOG_FORMAT_ROW && tables) + { + if (has_write_table_auto_increment_not_first_in_pk(tables)) + thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_AUTOINC_NOT_FIRST); + } /* INSERT...ON DUPLICATE KEY UPDATE on a table with more than one unique keys @@ -9554,6 +9561,32 @@ has_write_table_with_auto_increment_and_select(TABLE_LIST *tables) return(has_select && has_auto_increment_tables); } +/* + Tells if there is a table whose auto_increment column is a part + of a compound primary key while is not the first column in + the table definition. + + @param tables Table list + + @return true if the table exists, fais if does not. +*/ + +static bool +has_write_table_auto_increment_not_first_in_pk(TABLE_LIST *tables) +{ + for (TABLE_LIST *table= tables; table; table= table->next_global) + { + /* we must do preliminary checks as table->table may be NULL */ + if (!table->placeholder() && + table->table->found_next_number_field && + (table->lock_type >= TL_WRITE_ALLOW_WRITE) + && table->table->s->next_number_keypart != 0) + return 1; + } + + return 0; +} + /* diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 96814562757..7edd28446a2 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1533,7 +1533,7 @@ def_week_frmt: %lu, in_trans: %d, autocommit: %d", unlock(); goto end; } - if (!register_all_tables(query_block, tables_used, local_tables)) + if (!register_all_tables(thd, query_block, tables_used, local_tables)) { refused++; DBUG_PRINT("warning", ("tables list including failed")); @@ -3203,6 +3203,7 @@ Query_cache::invalidate_query_block_list(THD *thd, SYNOPSIS Query_cache::register_tables_from_list + thd thread handle tables_used given table list counter number current position in table of tables of block block_table pointer to current position in tables table of block @@ -3213,24 +3214,24 @@ Query_cache::invalidate_query_block_list(THD *thd, */ TABLE_COUNTER_TYPE -Query_cache::register_tables_from_list(TABLE_LIST *tables_used, +Query_cache::register_tables_from_list(THD *thd, TABLE_LIST *tables_used, TABLE_COUNTER_TYPE counter, - Query_cache_block_table *block_table) + Query_cache_block_table **block_table) { TABLE_COUNTER_TYPE n; DBUG_ENTER("Query_cache::register_tables_from_list"); for (n= counter; tables_used; - tables_used= tables_used->next_global, n++, block_table++) + tables_used= tables_used->next_global, n++, (*block_table)++) { if (tables_used->is_anonymous_derived_table()) { DBUG_PRINT("qcache", ("derived table skipped")); n--; - block_table--; + (*block_table)--; continue; } - block_table->n= n; + (*block_table)->n= n; if (tables_used->view) { char key[MAX_DBKEY_LENGTH]; @@ -3243,9 +3244,9 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used, /* There are not callback function for for VIEWs */ - if (!insert_table(key_length, key, block_table, + if (!insert_table(key_length, key, (*block_table), tables_used->view_db.length + 1, - HA_CACHE_TBL_NONTRANSACT, 0, 0)) + HA_CACHE_TBL_NONTRANSACT, 0, 0, TRUE)) DBUG_RETURN(0); /* We do not need to register view tables here because they are already @@ -3264,42 +3265,17 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used, if (!insert_table(tables_used->table->s->table_cache_key.length, tables_used->table->s->table_cache_key.str, - block_table, + (*block_table), tables_used->db_length, tables_used->table->file->table_cache_type(), tables_used->callback_func, - tables_used->engine_data)) + tables_used->engine_data, + TRUE)) DBUG_RETURN(0); -#ifdef WITH_MYISAMMRG_STORAGE_ENGINE - /* - XXX FIXME: Some generic mechanism is required here instead of this - MYISAMMRG-specific implementation. - */ - if (tables_used->table->s->db_type()->db_type == DB_TYPE_MRG_MYISAM) - { - ha_myisammrg *handler = (ha_myisammrg *) tables_used->table->file; - MYRG_INFO *file = handler->myrg_info(); - for (MYRG_TABLE *table = file->open_tables; - table != file->end_table ; - table++) - { - char key[MAX_DBKEY_LENGTH]; - uint32 db_length; - uint key_length= filename_2_table_key(key, table->table->filename, - &db_length); - (++block_table)->n= ++n; - /* - There are not callback function for for MyISAM, and engine data - */ - if (!insert_table(key_length, key, block_table, - db_length, - tables_used->table->file->table_cache_type(), - 0, 0)) - DBUG_RETURN(0); - } - } -#endif + if (tables_used->table->file-> + register_query_cache_dependant_tables(thd, this, block_table, &n)) + DBUG_RETURN(0); } } DBUG_RETURN(n - counter); @@ -3310,12 +3286,14 @@ Query_cache::register_tables_from_list(TABLE_LIST *tables_used, SYNOPSIS register_all_tables() + thd Thread handle block Store tables in this block tables_used List if used tables tables_arg Not used ? */ -my_bool Query_cache::register_all_tables(Query_cache_block *block, +my_bool Query_cache::register_all_tables(THD *thd, + Query_cache_block *block, TABLE_LIST *tables_used, TABLE_COUNTER_TYPE tables_arg) { @@ -3326,7 +3304,7 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block, Query_cache_block_table *block_table = block->table(0); - n= register_tables_from_list(tables_used, 0, block_table); + n= register_tables_from_list(thd, tables_used, 0, &block_table); if (n==0) { @@ -3335,6 +3313,8 @@ my_bool Query_cache::register_all_tables(Query_cache_block *block, tmp != block_table; tmp++) unlink_table(tmp); + if (block_table->parent) + unlink_table(block_table); } return test(n); } @@ -3353,7 +3333,8 @@ Query_cache::insert_table(uint key_len, char *key, Query_cache_block_table *node, uint32 db_length, uint8 cache_type, qc_engine_callback callback, - ulonglong engine_data) + ulonglong engine_data, + my_bool hash) { DBUG_ENTER("Query_cache::insert_table"); DBUG_PRINT("qcache", ("insert table node 0x%lx, len %d", @@ -3361,8 +3342,10 @@ Query_cache::insert_table(uint key_len, char *key, THD *thd= current_thd; - Query_cache_block *table_block= - (Query_cache_block *) my_hash_search(&tables, (uchar*) key, key_len); + Query_cache_block *table_block= + (hash ? + (Query_cache_block *) my_hash_search(&tables, (uchar*) key, key_len) : + NULL); if (table_block && table_block->table()->engine_data() != engine_data) @@ -3412,7 +3395,8 @@ Query_cache::insert_table(uint key_len, char *key, */ list_root->next= list_root->prev= list_root; - if (my_hash_insert(&tables, (const uchar *) table_block)) + if (hash && + my_hash_insert(&tables, (const uchar *) table_block)) { DBUG_PRINT("qcache", ("Can't insert table to hash")); // write_block_data return locked block @@ -3425,6 +3409,7 @@ Query_cache::insert_table(uint key_len, char *key, header->type(cache_type); header->callback(callback); header->engine_data(engine_data); + header->set_hashed(hash); /* We insert this table without the assumption that it isn't refrenenced by @@ -3478,7 +3463,9 @@ void Query_cache::unlink_table(Query_cache_block_table *node) Query_cache_block *table_block= neighbour->block(); double_linked_list_exclude(table_block, &tables_blocks); - my_hash_delete(&tables,(uchar *) table_block); + Query_cache_table *header= table_block->table(); + if (header->is_hashed()) + my_hash_delete(&tables,(uchar *) table_block); free_memory_block(table_block); } DBUG_VOID_RETURN; @@ -3951,6 +3938,9 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used, table_alias_charset used here because it depends of lower_case_table_names variable */ + table_count+= tables_used->table->file-> + count_query_cache_dependant_tables(tables_type); + if (tables_used->table->s->tmp_table != NO_TMP_TABLE || (*tables_type & HA_CACHE_TBL_NOCACHE) || (tables_used->db_length == 5 && @@ -3963,18 +3953,6 @@ Query_cache::process_and_count_tables(THD *thd, TABLE_LIST *tables_used, "other non-cacheable table(s)")); DBUG_RETURN(0); } -#ifdef WITH_MYISAMMRG_STORAGE_ENGINE - /* - XXX FIXME: Some generic mechanism is required here instead of this - MYISAMMRG-specific implementation. - */ - if (tables_used->table->s->db_type()->db_type == DB_TYPE_MRG_MYISAM) - { - ha_myisammrg *handler = (ha_myisammrg *)tables_used->table->file; - MYRG_INFO *file = handler->myrg_info(); - table_count+= (file->end_table - file->open_tables); - } -#endif } } DBUG_RETURN(table_count); diff --git a/sql/sql_cache.h b/sql/sql_cache.h index 87291e80a85..7444d444cf9 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -200,6 +200,10 @@ struct Query_cache_table The number of queries depending of this table. */ int32 m_cached_query_count; + /** + If table included in the table hash to be found by other queries + */ + my_bool hashed; inline char *db() { return (char *) data(); } inline char *table() { return tbl; } @@ -212,6 +216,8 @@ struct Query_cache_table inline void callback(qc_engine_callback fn){ callback_func= fn; } inline ulonglong engine_data() { return engine_data_buff; } inline void engine_data(ulonglong data_arg){ engine_data_buff= data_arg; } + inline my_bool is_hashed() { return hashed; } + inline void set_hashed(my_bool hash) { hashed= hash; } inline uchar* data() { return (uchar*)(((uchar*)this)+ @@ -343,10 +349,6 @@ protected: static void double_linked_list_join(Query_cache_block *head_tail, Query_cache_block *tail_head); - /* Table key generation */ - static uint filename_2_table_key (char *key, const char *filename, - uint32 *db_langth); - /* The following functions require that structure_guard_mutex is locked */ void flush_cache(); my_bool free_old_query(); @@ -363,17 +365,12 @@ protected: Query_cache_block_table *list_root); TABLE_COUNTER_TYPE - register_tables_from_list(TABLE_LIST *tables_used, + register_tables_from_list(THD *thd, TABLE_LIST *tables_used, TABLE_COUNTER_TYPE counter, - Query_cache_block_table *block_table); - my_bool register_all_tables(Query_cache_block *block, + Query_cache_block_table **block_table); + my_bool register_all_tables(THD *thd, Query_cache_block *block, TABLE_LIST *tables_used, TABLE_COUNTER_TYPE tables); - my_bool insert_table(uint key_len, char *key, - Query_cache_block_table *node, - uint32 db_length, uint8 cache_type, - qc_engine_callback callback, - ulonglong engine_data); void unlink_table(Query_cache_block_table *node); Query_cache_block *get_free_block (ulong len, my_bool not_less, ulong min); @@ -491,6 +488,12 @@ protected: const char *packet, ulong length, unsigned pkt_nr); + my_bool insert_table(uint key_len, char *key, + Query_cache_block_table *node, + uint32 db_length, uint8 cache_type, + qc_engine_callback callback, + ulonglong engine_data, + my_bool hash); void end_of_result(THD *thd); void abort(Query_cache_tls *query_cache_tls); @@ -513,6 +516,10 @@ protected: const char *name); my_bool in_blocks(Query_cache_block * point); + /* Table key generation */ + static uint filename_2_table_key (char *key, const char *filename, + uint32 *db_langth); + enum Cache_try_lock_mode {WAIT, TIMEOUT, TRY}; bool try_lock(THD *thd, Cache_try_lock_mode mode= WAIT); void lock(THD *thd); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 5fb809c4da3..99d54e437d2 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1,6 +1,6 @@ /* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2008-2012 Monty Program Ab + Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2008, 2012, 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 @@ -710,7 +710,7 @@ extern "C" bool wsrep_thd_is_wsrep_on(THD *thd) extern "C" bool wsrep_consistency_check(void *thd) { - return ((THD*)thd)->wsrep_consistency_check; + return ((THD*)thd)->wsrep_consistency_check == CONSISTENCY_CHECK_RUNNING; } extern "C" void wsrep_thd_set_exec_mode(THD *thd, enum wsrep_exec_mode mode) @@ -864,7 +864,7 @@ THD::THD() #endif :Statement(&main_lex, &main_mem_root, STMT_CONVENTIONAL_EXECUTION, /* statement id */ 0), - rli_fake(0), + rli_fake(0), rli_slave(NULL), in_sub_stmt(0), log_all_errors(0), binlog_unsafe_warning_flags(0), binlog_table_maps(0), @@ -890,6 +890,7 @@ THD::THD() spcont(NULL), #ifdef WITH_WSREP wsrep_applier(is_applier), + wsrep_applier_closing(FALSE), wsrep_client_thread(0), #endif m_parser_state(NULL), @@ -1003,7 +1004,7 @@ THD::THD() wsrep_retry_query = NULL; wsrep_retry_query_len = 0; wsrep_retry_command = COM_CONNECT; - wsrep_consistency_check = false; + wsrep_consistency_check = NO_CONSISTENCY_CHECK; #endif /* Call to init() below requires fully initialized Open_tables_state. */ reset_open_tables_state(this); @@ -1356,7 +1357,7 @@ void THD::init(void) wsrep_rli= NULL; wsrep_PA_safe= true; wsrep_seqno_changed= false; - wsrep_consistency_check = false; + wsrep_consistency_check = NO_CONSISTENCY_CHECK; #endif if (variables.sql_log_bin) variables.option_bits|= OPTION_BIN_LOG; @@ -1593,6 +1594,8 @@ THD::~THD() } mysql_audit_free_thd(this); + if (rli_slave) + rli_slave->cleanup_after_session(); #endif free_root(&main_mem_root, MYF(0)); @@ -1979,6 +1982,11 @@ void THD::cleanup_after_query() //wsrep_trx_seqno = 0; #endif /* WITH_WSREP */ +#ifndef EMBEDDED_LIBRARY + if (rli_slave) + rli_slave->cleanup_after_query(); +#endif + DBUG_VOID_RETURN; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 1dc38539670..844f56fac6f 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1,6 +1,6 @@ /* - Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2009-2012, Monty Program Ab + Copyright (c) 2000, 2012, Oracle and/or its affiliates. + Copyright (c) 2009, 2012, 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 @@ -45,6 +45,11 @@ REPLAYING, RETRY_AUTOCOMMIT, }; + enum wsrep_consistency_check_mode { + NO_CONSISTENCY_CHECK, + CONSISTENCY_CHECK_DECLARED, + CONSISTENCY_CHECK_RUNNING, + }; #endif #ifdef USE_PRAGMA_INTERFACE @@ -517,6 +522,7 @@ typedef struct system_variables ulonglong group_concat_max_len; ha_rows select_limit; ha_rows max_join_size; + ha_rows expensive_subquery_limit; ulong auto_increment_increment, auto_increment_offset; ulong lock_wait_timeout; ulong join_cache_level; @@ -1590,6 +1596,8 @@ public: /* Used to execute base64 coded binlog events in MySQL server */ Relay_log_info* rli_fake; + /* Slave applier execution context */ + Relay_log_info* rli_slave; void reset_for_next_command(bool calculate_userstat); /* @@ -2368,6 +2376,7 @@ public: #ifdef WITH_WSREP const bool wsrep_applier; /* dedicated slave applier thread */ + bool wsrep_applier_closing; /* applier marked to close */ bool wsrep_client_thread; /* to identify client threads*/ enum wsrep_exec_mode wsrep_exec_mode; query_id_t wsrep_last_query_id; @@ -2389,7 +2398,8 @@ public: char* wsrep_retry_query; size_t wsrep_retry_query_len; enum enum_server_command wsrep_retry_command; - bool wsrep_consistency_check; + enum wsrep_consistency_check_mode + wsrep_consistency_check; #endif /* WITH_WSREP */ /** Internal parser state. @@ -3106,7 +3116,7 @@ public: if (global_system_variables.log_warnings > threshold) { Security_context *sctx= &main_security_ctx; - sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), + sql_print_warning(ER_THD(this, ER_NEW_ABORTING_CONNECTION), thread_id, (db ? db : "unconnected"), sctx->user ? sctx->user : "unauthenticated", sctx->host_or_ip, reason); @@ -3603,7 +3613,8 @@ public: if (copy_field) /* Fix for Intel compiler */ { delete [] copy_field; - save_copy_field= copy_field= 0; + save_copy_field= copy_field= NULL; + save_copy_field_end= copy_field_end= NULL; } } }; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index bde7a082f42..0afd8c2ac3c 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -120,7 +120,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } /* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */ - if (select_lex->optimize_unflattened_subqueries()) + if (select_lex->optimize_unflattened_subqueries(false)) DBUG_RETURN(TRUE); const_cond= (!conds || conds->const_item()); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index a4eb1e8996f..230384d0a43 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2785,9 +2785,6 @@ pthread_handler_t handle_delayed_insert(void *arg) { int error; mysql_audit_release(thd); -#if defined(HAVE_BROKEN_COND_TIMEDWAIT) - error= mysql_cond_wait(&di->cond, &di->mutex); -#else error= mysql_cond_timedwait(&di->cond, &di->mutex, &abstime); #ifdef EXTRA_DEBUG if (error && error != EINTR && error != ETIMEDOUT) @@ -2797,7 +2794,6 @@ pthread_handler_t handle_delayed_insert(void *arg) error)); } #endif -#endif if (error == ETIMEDOUT || error == ETIME) thd->killed= KILL_CONNECTION; } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 3ffdb1384c1..6efb36c2d7d 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -68,7 +68,8 @@ Query_tables_list::binlog_stmt_unsafe_errcode[BINLOG_STMT_UNSAFE_COUNT] = ER_BINLOG_UNSAFE_CREATE_REPLACE_SELECT, ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC, ER_BINLOG_UNSAFE_UPDATE_IGNORE, - ER_BINLOG_UNSAFE_INSERT_TWO_KEYS + ER_BINLOG_UNSAFE_INSERT_TWO_KEYS, + ER_BINLOG_UNSAFE_AUTOINC_NOT_FIRST }; @@ -1556,7 +1557,7 @@ int lex_one_token(void *arg, void *yythd) if (version == 99997 && thd->wsrep_exec_mode == LOCAL_STATE) { WSREP_DEBUG("consistency check: %s", thd->query()); - thd->wsrep_consistency_check= TRUE; + thd->wsrep_consistency_check= CONSISTENCY_CHECK_DECLARED; lip->yySkipn(5); lip->set_echo(TRUE); state=MY_LEX_START; @@ -3417,7 +3418,23 @@ bool st_select_lex::add_index_hint (THD *thd, char *str, uint length) } -bool st_select_lex::optimize_unflattened_subqueries() +/** + Optimize all subqueries that have not been flattened into semi-joins. + + @details + This functionality is a method of SELECT_LEX instead of JOIN because + SQL statements as DELETE/UPDATE do not have a corresponding JOIN object. + + @see JOIN::optimize_unflattened_subqueries + + @param const_only Restrict subquery optimization to constant subqueries + + @return Operation status + @retval FALSE success. + @retval TRUE error occurred. +*/ + +bool st_select_lex::optimize_unflattened_subqueries(bool const_only) { for (SELECT_LEX_UNIT *un= first_inner_unit(); un; un= un->next_unit()) { @@ -3427,12 +3444,19 @@ bool st_select_lex::optimize_unflattened_subqueries() { if (subquery_predicate->substype() == Item_subselect::IN_SUBS) { - Item_in_subselect *in_subs=(Item_in_subselect*)subquery_predicate; + Item_in_subselect *in_subs= (Item_in_subselect*) subquery_predicate; if (in_subs->is_jtbm_merged) continue; } + if (const_only && !subquery_predicate->const_item()) + { + /* Skip non-constant subqueries if the caller asked so. */ + continue; + } + bool empty_union_result= true; + bool is_correlated_unit= false; /* If the subquery is a UNION, optimize all the subqueries in the UNION. If there is no UNION, then the loop will execute once for the subquery. @@ -3457,6 +3481,8 @@ bool st_select_lex::optimize_unflattened_subqueries() inner_join->select_options|= SELECT_DESCRIBE; } res= inner_join->optimize(); + sl->update_correlated_cache(); + is_correlated_unit|= sl->is_correlated; inner_join->select_options= save_options; un->thd->lex->current_select= save_select; if (empty_union_result) @@ -3472,6 +3498,9 @@ bool st_select_lex::optimize_unflattened_subqueries() } if (empty_union_result) subquery_predicate->no_rows_in_result(); + if (!is_correlated_unit) + un->uncacheable&= ~UNCACHEABLE_DEPENDENT; + subquery_predicate->is_correlated= is_correlated_unit; } } return FALSE; @@ -3841,6 +3870,61 @@ void SELECT_LEX::update_used_tables() /** + @brief + Update is_correlated cache for this select + + @details +*/ + +void st_select_lex::update_correlated_cache() +{ + TABLE_LIST *tl; + List_iterator<TABLE_LIST> ti(leaf_tables); + + is_correlated= false; + + while ((tl= ti++)) + { + if (tl->on_expr) + is_correlated|= test(tl->on_expr->used_tables() & OUTER_REF_TABLE_BIT); + for (TABLE_LIST *embedding= tl->embedding ; embedding ; + embedding= embedding->embedding) + { + if (embedding->on_expr) + is_correlated|= test(embedding->on_expr->used_tables() & + OUTER_REF_TABLE_BIT); + } + } + + if (join->conds) + is_correlated|= test(join->conds->used_tables() & OUTER_REF_TABLE_BIT); + + if (join->having) + is_correlated|= test(join->having->used_tables() & OUTER_REF_TABLE_BIT); + + if (join->tmp_having) + is_correlated|= test(join->tmp_having->used_tables() & OUTER_REF_TABLE_BIT); + + Item *item; + List_iterator_fast<Item> it(join->fields_list); + while ((item= it++)) + is_correlated|= test(item->used_tables() & OUTER_REF_TABLE_BIT); + + for (ORDER *order= group_list.first; order; order= order->next) + is_correlated|= test((*order->item)->used_tables() & OUTER_REF_TABLE_BIT); + + if (!master_unit()->is_union()) + { + for (ORDER *order= order_list.first; order; order= order->next) + is_correlated|= test((*order->item)->used_tables() & OUTER_REF_TABLE_BIT); + } + + if (!is_correlated) + uncacheable&= ~UNCACHEABLE_DEPENDENT; +} + + +/** Set the EXPLAIN type for this subquery. */ diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 7da0cc48298..2f3214646de 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -997,12 +997,7 @@ public: void clear_index_hints(void) { index_hints= NULL; } bool is_part_of_union() { return master_unit()->is_union(); } - /* - Optimize all subqueries that have not been flattened into semi-joins. - This functionality is a method of SELECT_LEX instead of JOIN because - some SQL statements as DELETE do not have a corresponding JOIN object. - */ - bool optimize_unflattened_subqueries(); + bool optimize_unflattened_subqueries(bool const_only); /* Set the EXPLAIN type for this subquery. */ void set_explain_type(); bool handle_derived(LEX *lex, uint phases); @@ -1023,6 +1018,7 @@ public: void mark_as_belong_to_derived(TABLE_LIST *derived); void increase_derived_records(ha_rows records); void update_used_tables(); + void update_correlated_cache(); void mark_const_derived(bool empty); bool save_leaf_tables(THD *thd); @@ -1433,6 +1429,12 @@ public: */ BINLOG_STMT_UNSAFE_INSERT_TWO_KEYS, + /** + INSERT into auto-inc field which is not the first part of composed + primary key. + */ + BINLOG_STMT_UNSAFE_AUTOINC_NOT_FIRST, + /* The last element of this enumeration type. */ BINLOG_STMT_UNSAFE_COUNT }; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index cce1aaf05d4..0ca412be17d 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2401,6 +2401,11 @@ mysql_execute_command(THD *thd) } DBUG_RETURN(0); } + /* + Execute deferred events first + */ + if (slave_execute_deferred_events(thd)) + DBUG_RETURN(-1); } else { @@ -2447,12 +2452,33 @@ mysql_execute_command(THD *thd) * allow SET and SHOW queries */ if (thd->variables.wsrep_on && !thd->wsrep_applier && !wsrep_ready && - lex->sql_command != SQLCOM_SET_OPTION && - !is_show_query(lex->sql_command)) - { + lex->sql_command != SQLCOM_SET_OPTION && + !is_show_query(lex->sql_command)) + { +#if DIRTY_HACK + /* Dirty hack for lp:1002714 - trying to recognize mysqldump connection + * and allow it to continue. Actuall mysqldump_magic_str may be longer + * and is obviously version dependent and may be issued by any client + * connection after which connection becomes non-replicating. */ + static char const mysqldump_magic_str[]= +"SELECT LOGFILE_GROUP_NAME, FILE_NAME, TOTAL_EXTENTS, INITIAL_SIZE, ENGINE, EXTRA FROM INFORMATION_SCHEMA.FILES WHERE FILE_TYPE = 'UNDO LOG' AND FILE_NAME IS NOT NULL"; + static const size_t mysqldump_magic_str_len= sizeof(mysqldump_magic_str) -1; + if (SQLCOM_SELECT != lex->sql_command || + thd->query_length() < mysqldump_magic_str_len || + strncmp(thd->query(), mysqldump_magic_str, mysqldump_magic_str_len)) + { +#endif /* DIRTY_HACK */ my_error(ER_UNKNOWN_COM_ERROR, MYF(0), "WSREP has not yet prepared node for application use"); goto error; +#if DIRTY_HACK + } + else + { + /* mysqldump connection, allow all further queries to pass */ + thd->variables.wsrep_on= FALSE; + } +#endif /* DIRTY_HACK */ } } #endif /* WITH_WSREP */ @@ -3074,7 +3100,7 @@ end_with_restore_list: goto error; #else { - if (check_global_access(thd, SUPER_ACL)) + if (check_global_access(thd, SUPER_ACL | REPL_CLIENT_ACL)) goto error; res = show_binlogs(thd); break; @@ -3348,8 +3374,9 @@ end_with_restore_list: break; #ifdef WITH_WSREP if (lex->sql_command == SQLCOM_INSERT_SELECT && - thd->wsrep_consistency_check) + thd->wsrep_consistency_check == CONSISTENCY_CHECK_DECLARED) { + thd->wsrep_consistency_check = CONSISTENCY_CHECK_RUNNING; WSREP_TO_ISOLATION_BEGIN(first_table->db, first_table->table_name, NULL); } @@ -4978,7 +5005,7 @@ finish: close_thread_tables(thd); #ifdef WITH_WSREP WSREP_TO_ISOLATION_END - thd->wsrep_consistency_check= FALSE; + thd->wsrep_consistency_check= NO_CONSISTENCY_CHECK; #endif /* WITH_WSREP */ thd_proc_info(thd, 0); @@ -6669,6 +6696,7 @@ TABLE_LIST *st_select_lex::end_nested_join(THD *thd) embedded->embedding= embedding; join_list->push_front(embedded); ptr= embedded; + embedded->lifted= 1; } else if (nested_join->join_list.elements == 0) { @@ -8408,11 +8436,16 @@ int wsrep_abort_thd(void *bf_thd_ptr, void *victim_thd_ptr, my_bool signal) THD *bf_thd = (THD *) bf_thd_ptr; DBUG_ENTER("wsrep_abort_thd"); - if (WSREP(bf_thd) && victim_thd) + if ( (WSREP(bf_thd) || + (WSREP_ON && bf_thd->wsrep_exec_mode == TOTAL_ORDER)) && victim_thd) { WSREP_DEBUG("wsrep_abort_thd, by: %llu, victim: %llu", (bf_thd) ? (long long)bf_thd->real_id : 0, (long long)victim_thd->real_id); ha_wsrep_abort_transaction(bf_thd, victim_thd, signal); + } + else + { + WSREP_DEBUG("wsrep_abort_thd not effective: %p %p", bf_thd, victim_thd); } DBUG_RETURN(1); diff --git a/sql/sql_profile.h b/sql/sql_profile.h index 2e93912fb25..7705f6ca476 100644 --- a/sql/sql_profile.h +++ b/sql/sql_profile.h @@ -287,5 +287,5 @@ public: int fill_statistics_info(THD *thd, TABLE_LIST *tables, Item *cond); }; -# endif /* HAVE_PROFILING */ +# endif /* ENABLED_PROFILING */ #endif /* _SQL_PROFILE_H */ diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 816ae57c828..a85e0f2a997 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1897,6 +1897,8 @@ bool mysql_show_binlog_events(THD* thd) File file = -1; MYSQL_BIN_LOG *binary_log= NULL; int old_max_allowed_packet= thd->variables.max_allowed_packet; + LOG_INFO linfo; + DBUG_ENTER("mysql_show_binlog_events"); Log_event::init_show_field_list(&field_list); @@ -1939,7 +1941,6 @@ bool mysql_show_binlog_events(THD* thd) char search_file_name[FN_REFLEN], *name; const char *log_file_name = lex_mi->log_file_name; mysql_mutex_t *log_lock = binary_log->get_log_lock(); - LOG_INFO linfo; Log_event* ev; unit->set_limit(thd->lex->current_select); @@ -2036,6 +2037,8 @@ bool mysql_show_binlog_events(THD* thd) mysql_mutex_unlock(log_lock); } + // Check that linfo is still on the function scope. + DEBUG_SYNC(thd, "after_show_binlog_events"); ret= FALSE; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 223e5c44fcf..0ed351b720c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2010 Oracle and/or its affiliates. - 2009-2011 Monty Program Ab +/* Copyright (c) 2000, 2012 Oracle and/or its affiliates. + Copyright (c) 2009, 2012, 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 @@ -271,6 +271,8 @@ Item_equal *find_item_equal(COND_EQUAL *cond_equal, Field *field, bool *inherited_fl); JOIN_TAB *first_depth_first_tab(JOIN* join); JOIN_TAB *next_depth_first_tab(JOIN* join, JOIN_TAB* tab); +JOIN_TAB *first_breadth_first_tab(JOIN *join); +JOIN_TAB *next_breadth_first_tab(JOIN *join, JOIN_TAB *tab); /** This handles SELECT with and without UNION. @@ -987,7 +989,10 @@ JOIN::optimize() } eval_select_list_used_tables(); - + + if (optimize_constant_subqueries()) + DBUG_RETURN(1); + table_count= select_lex->leaf_tables.elements; if (setup_ftfuncs(select_lex)) /* should be after having->fix_fields */ @@ -1274,6 +1279,12 @@ JOIN::optimize() { conds= substitute_for_best_equal_field(NO_PARTICULAR_TAB, conds, cond_equal, map2table); + if (thd->is_error()) + { + error= 1; + DBUG_PRINT("error",("Error from substitute_for_best_equal")); + DBUG_RETURN(1); + } conds->update_used_tables(); DBUG_EXECUTE("where", print_where(conds, @@ -1294,6 +1305,12 @@ JOIN::optimize() *tab->on_expr_ref, tab->cond_equal, map2table); + if (thd->is_error()) + { + error= 1; + DBUG_PRINT("error",("Error from substitute_for_best_equal")); + DBUG_RETURN(1); + } (*tab->on_expr_ref)->update_used_tables(); } } @@ -6615,6 +6632,32 @@ void JOIN::get_prefix_cost_and_fanout(uint n_tables, /** + Estimate the number of rows that query execution will read. + + @todo This is a very pessimistic upper bound. Use join selectivity + when available to produce a more realistic number. +*/ + +double JOIN::get_examined_rows() +{ + ha_rows examined_rows; + double prev_fanout= 1; + JOIN_TAB *tab= first_breadth_first_tab(this); + JOIN_TAB *prev_tab= tab; + + examined_rows= tab->get_examined_rows(); + + while ((tab= next_breadth_first_tab(this, tab))) + { + prev_fanout *= prev_tab->records_read; + examined_rows+= tab->get_examined_rows() * prev_fanout; + prev_tab= tab; + } + return examined_rows; +} + + +/** Find a good, possibly optimal, query execution plan (QEP) by a possibly exhaustive search. @@ -8061,36 +8104,15 @@ JOIN::make_simple_join(JOIN *parent, TABLE *temp_table) row_limit= unit->select_limit_cnt; do_send_rows= row_limit ? 1 : 0; - join_tab->use_join_cache= FALSE; - join_tab->cache=0; /* No caching */ + bzero(join_tab, sizeof(JOIN_TAB)); join_tab->table=temp_table; - join_tab->cache_select= 0; - join_tab->select=0; - join_tab->select_cond= 0; // Avoid valgrind warning join_tab->set_select_cond(NULL, __LINE__); - join_tab->quick=0; join_tab->type= JT_ALL; /* Map through all records */ join_tab->keys.init(); join_tab->keys.set_all(); /* test everything in quick */ - join_tab->info=0; - join_tab->on_expr_ref=0; - join_tab->last_inner= 0; - join_tab->first_unmatched= 0; join_tab->ref.key = -1; - join_tab->not_used_in_distinct=0; join_tab->read_first_record= join_init_read_record; - join_tab->preread_init_done= FALSE; join_tab->join= this; - join_tab->ref.key_parts= 0; - join_tab->keep_current_rowid= FALSE; - join_tab->flush_weedout_table= join_tab->check_weed_out_table= NULL; - join_tab->do_firstmatch= NULL; - join_tab->loosescan_match_tab= NULL; - join_tab->emb_sj_nest= NULL; - join_tab->pre_idx_push_select_cond= NULL; - join_tab->bush_root_tab= NULL; - join_tab->bush_children= NULL; - join_tab->last_leaf_in_bush= FALSE; bzero((char*) &join_tab->read_record,sizeof(join_tab->read_record)); temp_table->status=0; temp_table->null_row=0; @@ -10288,6 +10310,51 @@ double JOIN_TAB::scan_time() return res; } + +/** + Estimate the number of rows that a an access method will read from a table. + + @todo: why not use JOIN_TAB::found_records +*/ + +ha_rows JOIN_TAB::get_examined_rows() +{ + ha_rows examined_rows; + + if (select && select->quick) + examined_rows= select->quick->records; + else if (type == JT_NEXT || type == JT_ALL || + type == JT_HASH || type ==JT_HASH_NEXT) + { + if (limit) + { + /* + @todo This estimate is wrong, a LIMIT query may examine much more rows + than the LIMIT itself. + */ + examined_rows= limit; + } + else + { + if (table->is_filled_at_execution()) + examined_rows= records; + else + { + /* + handler->info(HA_STATUS_VARIABLE) has been called in + make_join_statistics() + */ + examined_rows= table->file->stats.records; + } + } + } + else + examined_rows= (ha_rows) records_read; + + return examined_rows; +} + + /** Initialize the join_tab before reading. Currently only derived table/view materialization is done here. @@ -10591,6 +10658,22 @@ void JOIN::cleanup(bool full) tmp_join->tmp_table_param.save_copy_field= 0; } tmp_table_param.cleanup(); + + if (!join_tab) + { + List_iterator<TABLE_LIST> li(*join_list); + TABLE_LIST *table_ref; + while ((table_ref= li++)) + { + if (table_ref->table && + table_ref->jtbm_subselect && + table_ref->jtbm_subselect->is_jtbm_const_tab) + { + free_tmp_table(thd, table_ref->table); + table_ref->table= NULL; + } + } + } } DBUG_VOID_RETURN; } @@ -11267,9 +11350,9 @@ static bool check_simple_equality(Item *left_item, Item *right_item, if (!item) { Item_func_eq *eq_item; - if ((eq_item= new Item_func_eq(orig_left_item, orig_right_item))) + if (!(eq_item= new Item_func_eq(orig_left_item, orig_right_item)) || + eq_item->set_cmp_func()) return FALSE; - eq_item->set_cmp_func(); eq_item->quick_fix_field(); item= eq_item; } @@ -11362,9 +11445,9 @@ static bool check_row_equality(THD *thd, Item *left_row, Item_row *right_row, if (!is_converted) { Item_func_eq *eq_item; - if (!(eq_item= new Item_func_eq(left_item, right_item))) + if (!(eq_item= new Item_func_eq(left_item, right_item)) || + eq_item->set_cmp_func()) return FALSE; - eq_item->set_cmp_func(); eq_item->quick_fix_field(); eq_list->push_back(eq_item); } @@ -12050,9 +12133,8 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, eq_item= new Item_func_eq(field_item->real_item(), head_item); - if (!eq_item) + if (!eq_item || eq_item->set_cmp_func()) return 0; - eq_item->set_cmp_func(); eq_item->quick_fix_field(); } current_sjm= field_sjm; @@ -12139,7 +12221,7 @@ Item *eliminate_item_equal(COND *cond, COND_EQUAL *upper_levels, Item_equal::get_first() for details. @return - The transformed condition + The transformed condition, or NULL in case of error */ static COND* substitute_for_best_equal_field(JOIN_TAB *context_tab, @@ -18594,8 +18676,6 @@ check_reverse_order: join_read_first:join_read_last; tab->type=JT_NEXT; // Read with index_first(), index_next() - if (table->covering_keys.is_set(best_key) && ! table->key_read) - table->enable_keyread(); if (tab->pre_idx_push_select_cond) { tab->set_cond(tab->pre_idx_push_select_cond); @@ -18606,6 +18686,7 @@ check_reverse_order: orig_cond= 0; orig_cond_saved= false; } + table->file->ha_index_or_rnd_end(); if (tab->join->select_options & SELECT_DESCRIBE) { @@ -18613,6 +18694,7 @@ check_reverse_order: tab->ref.key_parts= 0; if (select_limit < table->file->stats.records) tab->limit= select_limit; + table->disable_keyread(); } } else if (tab->type != JT_ALL) @@ -20214,6 +20296,8 @@ copy_fields(TMP_TABLE_PARAM *param) Copy_field *ptr=param->copy_field; Copy_field *end=param->copy_field_end; + DBUG_ASSERT((ptr != NULL && end >= ptr) || (ptr == NULL && end == NULL)); + for (; ptr != end; ptr++) (*ptr->do_copy)(ptr); @@ -21354,10 +21438,17 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, } else { - TABLE_LIST *real_table= table->pos_in_table_list; - item_list.push_back(new Item_string(real_table->alias, - strlen(real_table->alias), - cs)); + TABLE_LIST *real_table= table->pos_in_table_list; + /* + Internal temporary tables have no corresponding table reference + object. Such a table may appear in EXPLAIN when a subquery that needs + a temporary table has been executed, and JOIN::exec replaced the + original JOIN with a plan to access the data in the temp table + (made by JOIN::make_simple_join). + */ + const char *tab_name= real_table ? real_table->alias : + "internal_tmp_table"; + item_list.push_back(new Item_string(tab_name, strlen(tab_name), cs)); } /* "partitions" column */ if (join->thd->lex->describe & DESCRIBE_PARTITIONS) @@ -21515,32 +21606,8 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, } else { - ha_rows examined_rows; - if (tab->select && tab->select->quick) - examined_rows= tab->select->quick->records; - else if (tab->type == JT_NEXT || tab->type == JT_ALL || is_hj) - { - if (tab->limit) - examined_rows= tab->limit; - else - { - if (tab->table->is_filled_at_execution()) - { - examined_rows= tab->records; - } - else - { - /* - handler->info(HA_STATUS_VARIABLE) has been called in - make_join_statistics() - */ - examined_rows= tab->table->file->stats.records; - } - } - } - else - examined_rows=(ha_rows)tab->records_read; - + ha_rows examined_rows= tab->get_examined_rows(); + item_list.push_back(new Item_int((longlong) (ulonglong) examined_rows, MY_INT64_NUM_DECIMAL_DIGITS)); diff --git a/sql/sql_select.h b/sql/sql_select.h index c4553148cc6..0ed976ac36a 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -512,6 +512,7 @@ typedef struct st_join_table { return (is_hash_join_key_no(key) ? hj_key : table->key_info+key); } double scan_time(); + ha_rows get_examined_rows(); bool preread_init(); bool is_sjm_nest() { return test(bush_children); } @@ -1281,6 +1282,7 @@ public: bool alloc_func_list(); bool flatten_subqueries(); bool optimize_unflattened_subqueries(); + bool optimize_constant_subqueries(); bool make_sum_func_list(List<Item> &all_fields, List<Item> &send_fields, bool before_group_by, bool recompute= FALSE); @@ -1380,6 +1382,7 @@ public: void get_prefix_cost_and_fanout(uint n_tables, double *read_time_arg, double *record_count_arg); + double get_examined_rows(); /* defined in opt_subselect.cc */ bool transform_max_min_subquery(); /* True if this JOIN is a subquery under an IN predicate. */ diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 82647df6756..5eb8c8da039 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2012, Oracle and/or its affiliates. - Copyright (c) 2009, 2011, Monty Program Ab + Copyright (c) 2009, 2012, 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 diff --git a/sql/sql_table.cc b/sql/sql_table.cc index ef05a472ab7..04cf6a479ec 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -238,10 +238,17 @@ uint explain_filename(THD* thd, { part_name_len= tmp_p - part_name - 1; subpart_name= tmp_p + 3; + tmp_p+= 3; + } + else if ((tmp_p[1] == 'Q' || tmp_p[1] == 'q') && + (tmp_p[2] == 'L' || tmp_p[2] == 'l') && + tmp_p[3] == '-') + { + name_type= TEMP; + tmp_p+= 4; /* sql- prefix found */ } else res= 2; - tmp_p+= 3; break; case 'T': case 't': @@ -1937,6 +1944,49 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, /** + Find the comment in the query. + That's auxiliary function to be used handling DROP TABLE [comment]. + + @param thd Thread handler + @param comment_pos How many characters to skip before the comment. + Can be either 9 for DROP TABLE or + 17 for DROP TABLE IF EXISTS + @param comment_start returns the beginning of the comment if found. + + @retval 0 no comment found + @retval >0 the lenght of the comment found + +*/ +static uint32 comment_length(THD *thd, uint32 comment_pos, + const char **comment_start) +{ + const char *query= thd->query(); + const char *query_end= query + thd->query_length(); + const uchar *const state_map= thd->charset()->state_map; + + for (; query < query_end; query++) + { + if (state_map[*query] == MY_LEX_SKIP) + continue; + if (comment_pos-- == 0) + break; + } + if (query > query_end - 3 /* comment can't be shorter than 4 */ || + state_map[*query] != MY_LEX_LONG_COMMENT || query[1] != '*') + return 0; + + *comment_start= query; + + for (query+= 3; query < query_end; query++) + { + if (query[-1] == '*' && query[0] == '/') + return query - *comment_start + 1; + } + return 0; +} + + +/** Execute the drop of a normal or temporary table. @param thd Thread handler @@ -2011,11 +2061,20 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, { if (!drop_temporary) { + const char *comment_start; + uint32 comment_len; + built_query.set_charset(system_charset_info); if (if_exists) built_query.append("DROP TABLE IF EXISTS "); else built_query.append("DROP TABLE "); + + if ((comment_len= comment_length(thd, if_exists ? 17:9, &comment_start))) + { + built_query.append(comment_start, comment_len); + built_query.append(" "); + } } if (thd->is_current_stmt_binlog_format_row() || if_exists) @@ -6935,21 +6994,47 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, (void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP); } else if (mysql_rename_table(new_db_type, new_db, tmp_name, new_db, - new_alias, FN_FROM_IS_TMP) || - ((new_name != table_name || new_db != db) && // we also do rename - (need_copy_table != ALTER_TABLE_METADATA_ONLY || - mysql_rename_table(save_old_db_type, db, table_name, new_db, - new_alias, NO_FRM_RENAME)) && - Table_triggers_list::change_table_name(thd, db, alias, table_name, - new_db, new_alias))) + new_alias, FN_FROM_IS_TMP)) { /* Try to get everything back. */ - error=1; - (void) quick_rm_table(new_db_type,new_db,new_alias, 0); + error= 1; (void) quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP); (void) mysql_rename_table(old_db_type, db, old_name, db, alias, FN_FROM_IS_TMP); } + else if (new_name != table_name || new_db != db) + { + if (need_copy_table == ALTER_TABLE_METADATA_ONLY && + mysql_rename_table(save_old_db_type, db, table_name, new_db, + new_alias, NO_FRM_RENAME)) + { + /* Try to get everything back. */ + error= 1; + (void) quick_rm_table(new_db_type, new_db, new_alias, 0); + (void) mysql_rename_table(old_db_type, db, old_name, db, alias, + FN_FROM_IS_TMP); + } + else if (Table_triggers_list::change_table_name(thd, db, alias, + table_name, new_db, + new_alias)) + { + /* Try to get everything back. */ + error= 1; + (void) quick_rm_table(new_db_type, new_db, new_alias, 0); + (void) mysql_rename_table(old_db_type, db, old_name, db, + alias, FN_FROM_IS_TMP); + /* + If we were performing "fast"/in-place ALTER TABLE we also need + to restore old name of table in storage engine as a separate + step, as the above rename affects .FRM only. + */ + if (need_copy_table == ALTER_TABLE_METADATA_ONLY) + { + (void) mysql_rename_table(save_old_db_type, new_db, new_alias, + db, table_name, NO_FRM_RENAME); + } + } + } if (! error) (void) quick_rm_table(old_db_type, db, old_name, FN_IS_TMP); @@ -7369,7 +7454,10 @@ err: thd_progress_next_stage(thd); if (error > 0) + { + /* We are going to drop the temporary table */ to->file->extra(HA_EXTRA_PREPARE_FOR_DROP); + } if (errpos >= 3 && to->file->ha_end_bulk_insert() && error <= 0) { to->file->print_error(my_errno,MYF(0)); diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 53abf1bbe15..2a24f6d2bdf 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -368,7 +368,7 @@ int mysql_update(THD *thd, } /* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */ - if (select_lex->optimize_unflattened_subqueries()) + if (select_lex->optimize_unflattened_subqueries(false)) DBUG_RETURN(TRUE); if (select_lex->inner_refs_list.elements && diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e968dd12ca0..4e3629080be 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -9823,7 +9823,9 @@ table_factor: lex->nest_level--; } else if (($3->select_lex && - $3->select_lex->master_unit()->is_union()) || $5) + $3->select_lex->master_unit()->is_union() && + ($3->select_lex->master_unit()->first_select() == + $3->select_lex || !$3->lifted)) || $5) { /* simple nested joins cannot have aliases or unions */ my_parse_error(ER(ER_SYNTAX_ERROR)); diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 49fc526a16f..b7e5feaa8a2 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -2246,7 +2246,7 @@ static char *system_time_zone_ptr; static Sys_var_charptr Sys_system_time_zone( "system_time_zone", "The server system time zone", READ_ONLY GLOBAL_VAR(system_time_zone_ptr), NO_CMD_LINE, - IN_FS_CHARSET, DEFAULT(system_time_zone)); + IN_SYSTEM_CHARSET, DEFAULT(system_time_zone)); static Sys_var_ulong Sys_table_def_size( "table_definition_cache", @@ -3603,12 +3603,13 @@ static Sys_var_charptr Sys_wsrep_cluster_name( ON_CHECK(wsrep_cluster_name_check), ON_UPDATE(wsrep_cluster_name_update)); +static PolyLock_mutex PLock_wsrep_slave_threads(&LOCK_wsrep_slave_threads); static Sys_var_charptr Sys_wsrep_cluster_address ( "wsrep_cluster_address", "Address to initially connect to cluster", GLOBAL_VAR(wsrep_cluster_address), CMD_LINE(REQUIRED_ARG, OPT_WSREP_CLUSTER_ADDRESS), IN_FS_CHARSET, DEFAULT(wsrep_cluster_address), - NO_MUTEX_GUARD, NOT_IN_BINLOG, + &PLock_wsrep_slave_threads, NOT_IN_BINLOG, ON_CHECK(wsrep_cluster_address_check), ON_UPDATE(wsrep_cluster_address_update)); @@ -3637,7 +3638,10 @@ static Sys_var_charptr Sys_wsrep_node_incoming_address( static Sys_var_ulong Sys_wsrep_slave_threads( "wsrep_slave_threads", "Number of slave appliers to launch", GLOBAL_VAR(wsrep_slave_threads), CMD_LINE(REQUIRED_ARG), - VALID_RANGE(1, 512), DEFAULT(1), BLOCK_SIZE(1)); + VALID_RANGE(1, 512), DEFAULT(1), BLOCK_SIZE(1), + &PLock_wsrep_slave_threads, NOT_IN_BINLOG, + ON_CHECK(wsrep_slave_threads_check), + ON_UPDATE(wsrep_slave_threads_update)); static Sys_var_charptr Sys_wsrep_dbug_option( "wsrep_dbug_option", "DBUG options to provider library", @@ -3674,7 +3678,7 @@ static Sys_var_mybool Sys_wsrep_drupal_282555_workaround( CMD_LINE(OPT_ARG), DEFAULT(FALSE)); static Sys_var_charptr sys_wsrep_sst_method( - "wsrep_sst_method", "Snapshot transfer method", + "wsrep_sst_method", "State snapshot transfer method", GLOBAL_VAR(wsrep_sst_method),CMD_LINE(REQUIRED_ARG), IN_FS_CHARSET, DEFAULT(wsrep_sst_method), NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(wsrep_sst_method_check), @@ -3704,6 +3708,12 @@ static Sys_var_charptr Sys_wsrep_sst_donor( ON_CHECK(wsrep_sst_donor_check), ON_UPDATE(wsrep_sst_donor_update)); +static Sys_var_mybool Sys_wsrep_sst_donor_rejects_queries( + "wsrep_sst_donor_rejects_queries", "Reject client queries " + "when donating state snapshot transfer", + GLOBAL_VAR(wsrep_sst_donor_rejects_queries), + CMD_LINE(OPT_ARG), DEFAULT(FALSE)); + static Sys_var_mybool Sys_wsrep_on ( "wsrep_on", "To enable wsrep replication ", SESSION_VAR(wsrep_on), @@ -3971,4 +3981,9 @@ static Sys_var_ulong Sys_debug_binlog_fsync_sleep( CMD_LINE(REQUIRED_ARG), VALID_RANGE(0, UINT_MAX), DEFAULT(0), BLOCK_SIZE(1)); #endif - +static Sys_var_harows Sys_expensive_subquery_limit( + "expensive_subquery_limit", + "The maximum number of rows a subquery may examine in order to be " + "executed during optimization and used for constant optimization", + SESSION_VAR(expensive_subquery_limit), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, HA_POS_ERROR), DEFAULT(100), BLOCK_SIZE(1)); diff --git a/sql/table.cc b/sql/table.cc index 40304dc6fdc..b8161093e9a 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1439,25 +1439,33 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, { /* Get virtual column data stored in the .frm file as follows: - byte 1 = 1 (always 1 to allow for future extensions) + byte 1 = 1 | 2 byte 2 = sql_type byte 3 = flags (as of now, 0 - no flags, 1 - field is physically stored) - byte 4-... = virtual column expression (text data) + [byte 4] = optional interval_id for sql_type (only if byte 1 == 2) + next byte ... = virtual column expression (text data) */ vcol_info= new Virtual_column_info(); - if ((uint)vcol_screen_pos[0] != 1) + bool opt_interval_id= (uint)vcol_screen_pos[0] == 2; + field_type= (enum_field_types) (uchar) vcol_screen_pos[1]; + if (opt_interval_id) + interval_nr= (uint)vcol_screen_pos[3]; + else if ((uint)vcol_screen_pos[0] != 1) { error= 4; goto err; } - field_type= (enum_field_types) (uchar) vcol_screen_pos[1]; fld_stored_in_db= (bool) (uint) vcol_screen_pos[2]; - vcol_expr_length= vcol_info_length-(uint)FRM_VCOL_HEADER_SIZE; + vcol_expr_length= vcol_info_length - + (uint)(FRM_VCOL_HEADER_SIZE(opt_interval_id)); if (!(vcol_info->expr_str.str= (char *)memdup_root(&share->mem_root, - vcol_screen_pos+(uint)FRM_VCOL_HEADER_SIZE, + vcol_screen_pos + + (uint) FRM_VCOL_HEADER_SIZE(opt_interval_id), vcol_expr_length))) goto err; + if (opt_interval_id) + interval_nr= (uint) vcol_screen_pos[3]; vcol_info->expr_str.length= vcol_expr_length; vcol_screen_pos+= vcol_info_length; share->vfields++; @@ -4536,7 +4544,14 @@ bool TABLE_LIST::check_single_table(TABLE_LIST **table_arg, tbl; tbl= tbl->next_local) { - if (tbl->table) + /* + Merged view has also temporary table attached (in 5.2 if it has table + then it was real table), so we have filter such temporary tables out + by checking that it is not merged view + */ + if (tbl->table && + !(tbl->is_view() && + tbl->is_merged_derived())) { if (tbl->table->map & map) { @@ -5985,7 +6000,13 @@ void TABLE::use_index(int key_to_save) bool TABLE::is_filled_at_execution() { - return test(pos_in_table_list->jtbm_subselect || + /* + pos_in_table_list == NULL for internal temporary tables because they + do not have a corresponding table reference. Such tables are filled + during execution. + */ + return test(!pos_in_table_list || + pos_in_table_list->jtbm_subselect || pos_in_table_list->is_active_sjm()); } diff --git a/sql/table.h b/sql/table.h index f3f9d5ac036..87affe984fc 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1800,6 +1800,8 @@ struct TABLE_LIST struct st_nested_join *nested_join; /* if the element is a nested join */ TABLE_LIST *embedding; /* nested join containing the table */ List<TABLE_LIST> *join_list;/* join list the table belongs to */ + bool lifted; /* set to true when the table is moved to + the upper level at the parsing stage */ bool cacheable_table; /* stop PS caching */ /* used in multi-upd/views privilege check */ bool table_in_first_from_clause; diff --git a/sql/unireg.cc b/sql/unireg.cc index c9b0f91d9f7..edcfe9eb934 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -726,18 +726,19 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, } if (field->vcol_info) { + uint col_expr_maxlen= field->virtual_col_expr_maxlen(); tmp_len= system_charset_info->cset->charpos(system_charset_info, field->vcol_info->expr_str.str, field->vcol_info->expr_str.str + field->vcol_info->expr_str.length, - VIRTUAL_COLUMN_EXPRESSION_MAXLEN); + col_expr_maxlen); if (tmp_len < field->vcol_info->expr_str.length) { my_error(ER_WRONG_STRING_LENGTH, MYF(0), field->vcol_info->expr_str.str,"VIRTUAL COLUMN EXPRESSION", - (uint) VIRTUAL_COLUMN_EXPRESSION_MAXLEN); + col_expr_maxlen); DBUG_RETURN(1); } /* @@ -746,7 +747,7 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, expressions saved in the frm file for virtual columns. */ vcol_info_length+= field->vcol_info->expr_str.length+ - (uint)FRM_VCOL_HEADER_SIZE; + FRM_VCOL_HEADER_SIZE(field->interval!=NULL); } totlength+= field->length; @@ -949,8 +950,9 @@ static bool pack_fields(File file, List<Create_field> &create_fields, the additional data saved for the virtual field */ buff[12]= cur_vcol_expr_len= field->vcol_info->expr_str.length + - (uint)FRM_VCOL_HEADER_SIZE; - vcol_info_length+= cur_vcol_expr_len+(uint)FRM_VCOL_HEADER_SIZE; + FRM_VCOL_HEADER_SIZE(field->interval!=NULL); + vcol_info_length+= cur_vcol_expr_len + + FRM_VCOL_HEADER_SIZE(field->interval!=NULL); buff[13]= (uchar) MYSQL_TYPE_VIRTUAL; } int2store(buff+15, field->comment.length); @@ -1055,17 +1057,20 @@ static bool pack_fields(File file, List<Create_field> &create_fields, { /* Pack each virtual field as follows: - byte 1 = 1 (always 1 to allow for future extensions) + byte 1 = interval_id == 0 ? 1 : 2 byte 2 = sql_type byte 3 = flags (as of now, 0 - no flags, 1 - field is physically stored) - byte 4-... = virtual column expression (text data) + [byte 4] = possible interval_id for sql_type + next byte ... = virtual column expression (text data) */ if (field->vcol_info && field->vcol_info->expr_str.length) { - buff[0]= (uchar)1; + buff[0]= (uchar)(1 + test(field->interval_id)); buff[1]= (uchar) field->sql_type; buff[2]= (uchar) field->stored_in_db; - if (my_write(file, buff, 3, MYF_RW)) + if (field->interval_id) + buff[3]= (uchar) field->interval_id; + if (my_write(file, buff, 3 + test(field->interval_id), MYF_RW)) DBUG_RETURN(1); if (my_write(file, (uchar*) field->vcol_info->expr_str.str, diff --git a/sql/unireg.h b/sql/unireg.h index f8317a89c8c..da510bb4e6d 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -161,12 +161,6 @@ typedef struct st_ha_create_information HA_CREATE_INFO; #define DEFAULT_KEY_CACHE_NAME "default" -/* The length of the header part for each virtual column in the .frm file */ -#define FRM_VCOL_HEADER_SIZE 3 - -/* Maximum length of the defining expression for a virtual columns */ -#define VIRTUAL_COLUMN_EXPRESSION_MAXLEN 255 - FRM_VCOL_HEADER_SIZE - /* Include prototypes for unireg */ diff --git a/sql/wsrep_hton.cc b/sql/wsrep_hton.cc index 49b3ea5c0bf..219e0e8a244 100644 --- a/sql/wsrep_hton.cc +++ b/sql/wsrep_hton.cc @@ -216,7 +216,7 @@ wsrep_run_wsrep_commit( if (thd->wsrep_exec_mode != LOCAL_STATE) { DBUG_RETURN(WSREP_TRX_OK); } - if (thd->wsrep_consistency_check) { + if (thd->wsrep_consistency_check == CONSISTENCY_CHECK_RUNNING) { WSREP_DEBUG("commit for consistency check: %s", thd->query()); DBUG_RETURN(WSREP_TRX_OK); } diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 0bf0c2294d3..a5d7134a201 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -326,15 +326,27 @@ out: local_status.set(new_status, view); } +void wsrep_ready_set (my_bool x) +{ + WSREP_DEBUG("Setting wsrep_ready to %d", x); + if (mysql_mutex_lock (&LOCK_wsrep_ready)) abort(); + if (wsrep_ready != x) + { + wsrep_ready= x; + mysql_cond_signal (&COND_wsrep_ready); + } + mysql_mutex_unlock (&LOCK_wsrep_ready); +} + // Wait until wsrep has reached ready state void wsrep_ready_wait () { if (mysql_mutex_lock (&LOCK_wsrep_ready)) abort(); while (!wsrep_ready) - { - WSREP_INFO("Waiting to reach ready state"); - mysql_cond_wait (&COND_wsrep_ready, &LOCK_wsrep_ready); - } + { + WSREP_INFO("Waiting to reach ready state"); + mysql_cond_wait (&COND_wsrep_ready, &LOCK_wsrep_ready); + } WSREP_INFO("ready state reached"); mysql_mutex_unlock (&LOCK_wsrep_ready); } @@ -1127,17 +1139,17 @@ void wsrep_to_isolation_end(THD *thd) { } } -#define WSREP_MDL_LOG(severity, msg, req, gra) \ - WSREP_##severity( \ - "%s\n" \ - "request: (%lu \tseqno %lld \tmode %d \tQstate \t%d cmd %d %d \t%s)\n" \ - "granted: (%lu \tseqno %lld \tmode %d \tQstate \t%d cmd %d %d \t%s)", \ - msg, \ - req->thread_id, (long long)req->wsrep_trx_seqno, \ - req->wsrep_exec_mode, req->wsrep_query_state, \ - req->command, req->lex->sql_command, req->query(), \ - gra->thread_id, (long long)gra->wsrep_trx_seqno, \ - gra->wsrep_exec_mode, gra->wsrep_query_state, \ +#define WSREP_MDL_LOG(severity, msg, req, gra) \ + WSREP_##severity( \ + "%s\n" \ + "request: (%lu \tseqno %lld \twsrep (%d, %d, %d) cmd %d %d \t%s)\n" \ + "granted: (%lu \tseqno %lld \twsrep (%d, %d, %d) cmd %d %d \t%s)", \ + msg, \ + req->thread_id, (long long)req->wsrep_trx_seqno, \ + req->wsrep_exec_mode, req->wsrep_query_state, req->wsrep_conflict_state, \ + req->command, req->lex->sql_command, req->query(), \ + gra->thread_id, (long long)gra->wsrep_trx_seqno, \ + gra->wsrep_exec_mode, gra->wsrep_query_state, gra->wsrep_conflict_state, \ gra->command, gra->lex->sql_command, gra->query()); bool @@ -1154,7 +1166,7 @@ wsrep_grant_mdl_exception(MDL_context *requestor_ctx, request_thd->wsrep_exec_mode == REPL_RECV) { mysql_mutex_unlock(&request_thd->LOCK_wsrep_thd); - WSREP_MDL_LOG(DEBUG, "MDL conflict", request_thd, granted_thd); + WSREP_MDL_LOG(DEBUG, "MDL conflict ", request_thd, granted_thd); mysql_mutex_lock(&granted_thd->LOCK_wsrep_thd); if (granted_thd->wsrep_exec_mode == TOTAL_ORDER || @@ -1183,7 +1195,7 @@ wsrep_grant_mdl_exception(MDL_context *requestor_ctx, mysql_mutex_unlock(&granted_thd->LOCK_wsrep_thd); wsrep_abort_thd((void*)request_thd, (void*)granted_thd, 1); return FALSE; - } + } else { WSREP_MDL_LOG(DEBUG, "MDL conflict-> BF abort", request_thd, granted_thd); diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index ebea44c9151..968281a2c98 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -50,6 +50,7 @@ extern const char* wsrep_sst_method; extern const char* wsrep_sst_receive_address; extern char* wsrep_sst_auth; extern const char* wsrep_sst_donor; +extern my_bool wsrep_sst_donor_rejects_queries; extern const char* wsrep_start_position; extern long long wsrep_max_ws_size; extern long wsrep_max_ws_rows; @@ -129,6 +130,8 @@ extern void wsrep_sst_auth_init INIT_ARGS; extern bool wsrep_sst_donor_check CHECK_ARGS; extern bool wsrep_sst_donor_update UPDATE_ARGS; +extern bool wsrep_slave_threads_check CHECK_ARGS; +extern bool wsrep_slave_threads_update UPDATE_ARGS; extern bool wsrep_init_first(); // initialize wsrep before storage // engines (true) or after (false) @@ -142,7 +145,8 @@ extern void wsrep_init_startup(bool first); extern void wsrep_close_client_connections(my_bool wait_to_end); extern void wsrep_close_applier(THD *thd); -extern void wsrep_wait_appliers_close(THD *thd); +extern void wsrep_wait_appliers_close(THD *thd); +extern void wsrep_close_applier_threads(int count); extern void wsrep_create_appliers(long threads = wsrep_slave_threads); extern void wsrep_create_rollbacker(); extern void wsrep_kill_mysql(THD *thd); @@ -250,6 +254,7 @@ extern long long wsrep_max_ws_size; extern long wsrep_max_ws_rows; extern int wsrep_to_isolation; extern my_bool wsrep_certify_nonPK; +extern mysql_mutex_t LOCK_wsrep_slave_threads; extern PSI_mutex_key key_LOCK_wsrep_ready; extern PSI_mutex_key key_COND_wsrep_ready; @@ -263,6 +268,7 @@ extern PSI_mutex_key key_LOCK_wsrep_rollback; extern PSI_cond_key key_COND_wsrep_rollback; extern PSI_mutex_key key_LOCK_wsrep_replaying; extern PSI_cond_key key_COND_wsrep_replaying; +extern PSI_mutex_key key_LOCK_wsrep_slave_threads; struct TABLE_LIST; int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_, diff --git a/sql/wsrep_priv.h b/sql/wsrep_priv.h index 4db8abf68de..32d0cebfd33 100644 --- a/sql/wsrep_priv.h +++ b/sql/wsrep_priv.h @@ -26,6 +26,8 @@ #include <pthread.h> #include <cstdio> +extern void wsrep_ready_set (my_bool x); + extern ssize_t wsrep_sst_prepare (void** msg); extern int wsrep_sst_donate_cb (void* app_ctx, void* recv_ctx, @@ -38,8 +40,8 @@ extern int wsrep_sst_donate_cb (void* app_ctx, extern size_t default_ip (char* buf, size_t buf_len); extern size_t default_address(char* buf, size_t buf_len); -extern wsrep_uuid_t local_uuid; -extern wsrep_seqno_t local_seqno; +extern wsrep_uuid_t local_uuid; +extern wsrep_seqno_t local_seqno; /*! SST thread signals init thread about sst completion */ extern void wsrep_sst_complete(wsrep_uuid_t* uuid, wsrep_seqno_t seqno, bool); @@ -212,7 +214,7 @@ class thd public: - thd(); + thd(my_bool wsrep_on); ~thd(); THD* const ptr; }; diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc index 37779c4b96d..df39db3caa4 100644 --- a/sql/wsrep_sst.cc +++ b/sql/wsrep_sst.cc @@ -39,6 +39,8 @@ const char* wsrep_sst_donor = ""; // container for real auth string static const char* sst_auth_real = NULL; +my_bool wsrep_sst_donor_rejects_queries = FALSE; + static const char *sst_methods[] = { "mysqldump", "rsync", @@ -597,6 +599,13 @@ static int sst_run_shell (const char* cmd_str, int max_tries) return -ret; } +static void sst_reject_queries(my_bool close_conn) +{ + wsrep_ready_set (FALSE); // this will be resotred when donor becomes synced + WSREP_INFO("Rejecting client queries for the duration of SST."); + if (TRUE == close_conn) wsrep_close_client_connections(FALSE); +} + static int sst_mysqldump_check_addr (const char* user, const char* pswd, const char* host, const char* port) { @@ -654,6 +663,8 @@ static int sst_donate_mysqldump (const char* addr, size_t cmd_len= 1024; char cmd_str[cmd_len]; + if (!bypass && wsrep_sst_donor_rejects_queries) sst_reject_queries(TRUE); + snprintf (cmd_str, cmd_len, "wsrep_sst_mysqldump '%s' '%s' '%s' '%s' '%u' '%s' '%lld' '%d'", user, pswd, host, port, mysqld_port, uuid_str, (long long)seqno, @@ -781,7 +792,8 @@ static void* sst_donor_thread (void* a) wsrep_uuid_t ret_uuid= WSREP_UUID_UNDEFINED; wsrep_seqno_t ret_seqno= WSREP_SEQNO_UNDEFINED; // seqno of complete SST - wsp::thd thd; + wsp::thd thd(FALSE); // we turn off wsrep_on for this THD so that it can + // operate with wsrep_ready == OFF wsp::process proc(arg->cmd, "r"); err= proc.error(); @@ -880,6 +892,8 @@ static int sst_donate_other (const char* method, return (ret < 0 ? ret : -EMSGSIZE); } + if (!bypass && wsrep_sst_donor_rejects_queries) sst_reject_queries(FALSE); + pthread_t tmp; sst_thread_arg arg(cmd_str); mysql_mutex_lock (&arg.lock); diff --git a/sql/wsrep_utils.cc b/sql/wsrep_utils.cc index a6d7dcdbfc8..00919b3163e 100644 --- a/sql/wsrep_utils.cc +++ b/sql/wsrep_utils.cc @@ -289,13 +289,14 @@ process::wait () return err_; } -thd::thd () : init(), ptr(new THD) +thd::thd (my_bool won) : init(), ptr(new THD) { if (ptr) { ptr->thread_stack= (char*) &ptr; ptr->store_globals(); ptr->variables.option_bits&= ~OPTION_BIN_LOG; // disable binlog + ptr->variables.wsrep_on = won; ptr->security_ctx->master_access= ~(ulong)0; lex_start(ptr); } diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc index 66f0f05c006..fb5c3b339cd 100644 --- a/sql/wsrep_var.cc +++ b/sql/wsrep_var.cc @@ -35,6 +35,7 @@ const char* wsrep_node_name = glob_hostname; static char node_address[256] = { 0, }; const char* wsrep_node_address = node_address; ulong wsrep_OSU_method_options; +static int wsrep_thread_change = 0; int wsrep_init_vars() { @@ -419,6 +420,27 @@ void wsrep_node_address_init (const char* value) wsrep_node_address = (value) ? my_strdup(value, MYF(0)) : NULL; } +bool wsrep_slave_threads_check (sys_var *self, THD* thd, set_var* var) +{ + mysql_mutex_lock(&LOCK_wsrep_slave_threads); + wsrep_thread_change = var->value->val_int() - wsrep_slave_threads; + mysql_mutex_unlock(&LOCK_wsrep_slave_threads); + + return 0; +} + +bool wsrep_slave_threads_update (sys_var *self, THD* thd, enum_var_type type) +{ + if (wsrep_thread_change > 0) + { + wsrep_create_appliers(wsrep_thread_change); + } + else if (wsrep_thread_change < 0) + { + wsrep_close_applier_threads(-wsrep_thread_change); + } + return false; +} /* * Status variables stuff below */ |