From b6283d4f97628134e9010424cb4748e801a25cb8 Mon Sep 17 00:00:00 2001 From: Venkatesh Duggirala Date: Mon, 5 May 2014 22:22:15 +0530 Subject: Bug#17638477 UNINSTALL AND INSTALL SEMI-SYNC PLUGIN CAUSES SLAVES TO BREAK Problem: Uninstallation of semi sync plugin causes replication to break. Analysis: A semisync enabled replication is mutual agreement between Master and Slave when the connection (I/O thread) is established. Once I/O thread is started and if semisync is enabled on both master and slave, master appends special magic header to events using semisync plugin functions and sends it to slave. And slave expects that each event will have that special magic header format and reads those bytes using semisync plugin functions. When semi sync replication is in use if users execute uninstallation of the plugin on master, slave gets confused while interpreting that event's content because it expects special magic header at the beginning of the event. Slave SQL thread will be stopped with "Missing magic number in the header" error. Similar problem will happen if uninstallation of the plugin happens on slave when semi sync replication is in in use. Master sends the events with magic header and slave does not know about the added magic header and thinks that it received a corrupted event. Hence slave SQL thread stops with "Found corrupted event" error. Fix: Uninstallation of semisync plugin will be blocked when semisync replication is in use and will throw 'ER_UNKNOWN_ERROR' error. To detect that semisync replication is in use, this patch uses semisync status variable values. > On Master, it checks for 'Rpl_semi_sync_master_status' to be OFF before allowing the uninstallation of rpl_semi_sync_master plugin. >> Rpl_semi_sync_master_status is OFF when >>> there is no dump thread running >>> there are no semisync slaves > On Slave, it checks for 'Rpl_semi_sync_slave_status' to be OFF before allowing the uninstallation of rpl_semi_sync_slave plugin. >> Rpl_semi_sync_slave_status is OFF when >>> there is no I/O thread running >>> replication is asynchronous replication. --- sql/sql_plugin.cc | 43 +++++++++++++++++++++++++++++++++++++++++++ sql/sql_show.cc | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ sql/sql_show.h | 1 + 3 files changed, 97 insertions(+) (limited to 'sql') diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index d54326af7aa..9851cb2739a 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1930,6 +1930,49 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name) goto err; } +#ifdef HAVE_REPLICATION + /* Block Uninstallation of semi_sync plugins (Master/Slave) + when they are busy + */ + char buff[20]; + /* + Master: If there are active semi sync slaves for this Master, + then that means it is busy and rpl_semi_sync_master plugin + cannot be uninstalled. To check whether the master + has any semi sync slaves or not, check Rpl_semi_sync_master_cliens + status variable value, if it is not 0, that means it is busy. + */ + if (!strcmp(name->str, "rpl_semi_sync_master") && + get_status_var(thd, + plugin->plugin->status_vars, + "Rpl_semi_sync_master_clients",buff) && + strcmp(buff,"0") ) + { + sql_print_error("Plugin 'rpl_semi_sync_master' cannot be uninstalled now. " + "Stop any active semisynchronous slaves of this master " + "first.\n"); + my_error(ER_UNKNOWN_ERROR, MYF(0), name->str); + goto err; + } + /* Slave: If there is semi sync enabled IO thread active on this Slave, + then that means plugin is busy and rpl_semi_sync_slave plugin + cannot be uninstalled. To check whether semi sync + IO thread is active or not, check Rpl_semi_sync_slave_status status + variable value, if it is ON, that means it is busy. + */ + if (!strcmp(name->str, "rpl_semi_sync_slave") && + get_status_var(thd, plugin->plugin->status_vars, + "Rpl_semi_sync_slave_status", buff) && + !strcmp(buff,"ON") ) + { + sql_print_error("Plugin 'rpl_semi_sync_slave' cannot be uninstalled now. " + "Stop any active semisynchronous I/O threads on this slave " + "first.\n"); + my_error(ER_UNKNOWN_ERROR, MYF(0), name->str); + goto err; + } +#endif + plugin->state= PLUGIN_IS_DELETED; if (plugin->ref_count) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, diff --git a/sql/sql_show.cc b/sql/sql_show.cc index dcae4c63b02..ac63b2aaff9 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2117,6 +2117,59 @@ void free_status_vars() delete_dynamic(&all_status_vars); } +/** + @brief Get the value of given status variable + + @param[in] thd thread handler + @param[in] list list of SHOW_VAR objects in which function should + search + @param[in] name name of the status variable + @param[in/out] value buffer in which value of the status variable + needs to be filled in + + @return status + @retval FALSE if variable is not found in the list + @retval TRUE if variable is found in the list + NOTE: Currently this function is implemented just to support 'bool' status + variables and 'long' status variables *only*. It can be extended very easily + for further show_types in future if required. + TODO: Currently show_status_arary switch case is tightly coupled with + pos, end, buff, value variables and also it stores the values in a 'table'. + Decouple the switch case to fill the buffer value so that it can be used + in show_status_array() and get_status_var() to avoid duplicate code. + */ + +bool get_status_var(THD* thd, SHOW_VAR *list, const char * name, char * const value) +{ + for (; list->name; list++) + { + int res= strcmp(list->name, name); + if (res == 0) + { + /* + if var->type is SHOW_FUNC, call the function. + Repeat as necessary, if new var is again SHOW_FUNC + */ + SHOW_VAR tmp; + for (; list->type == SHOW_FUNC; list= &tmp) + ((mysql_show_var_func)(list->value))(thd, &tmp, value); + switch (list->type) { + case SHOW_BOOL: + strmov(value, *(bool*) list->value ? "ON" : "OFF"); + break; + case SHOW_LONG: + int10_to_str(*(long*) list->value, value, 10); + break; + default: + /* not supported type */ + DBUG_ASSERT(0); + } + return TRUE; + } + } + return FALSE; +} + /* Removes an array of SHOW_VAR entries from the output of SHOW STATUS diff --git a/sql/sql_show.h b/sql/sql_show.h index 406c75d8cbe..b6d520441c7 100644 --- a/sql/sql_show.h +++ b/sql/sql_show.h @@ -113,6 +113,7 @@ int add_status_vars(SHOW_VAR *list); void remove_status_vars(SHOW_VAR *list); void init_status_vars(); void free_status_vars(); +bool get_status_var(THD* thd, SHOW_VAR *list, const char *name, char * const buff); void reset_status_vars(); bool show_create_trigger(THD *thd, const sp_name *trg_name); void view_store_options(THD *thd, TABLE_LIST *table, String *buff); -- cgit v1.2.1 From 548db492101346bf582295544c9a7a42d3308641 Mon Sep 17 00:00:00 2001 From: Mattias Jonsson Date: Tue, 6 May 2014 11:05:37 +0200 Subject: Bug#17909699: WRONG RESULTS WITH PARTITION BY LIST COLUMNS() Typo leading to not including the last list values (partition). Also improved pruning to skip last partition if not used. rb#4762 approved by Aditya and Marko. --- sql/sql_partition.cc | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) (limited to 'sql') diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index e2ff4d07f1a..d7d362dbdfb 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -3304,19 +3304,28 @@ uint32 get_partition_id_cols_list_for_endpoint(partition_info *part_info, uint num_columns= part_info->part_field_list.elements; uint list_index; uint min_list_index= 0; + int cmp; + /* Notice that max_list_index = last_index + 1 here! */ uint max_list_index= part_info->num_list_values; DBUG_ENTER("get_partition_id_cols_list_for_endpoint"); /* Find the matching partition (including taking endpoint into account). */ do { - /* Midpoint, adjusted down, so it can never be > last index. */ + /* Midpoint, adjusted down, so it can never be >= max_list_index. */ list_index= (max_list_index + min_list_index) >> 1; - if (cmp_rec_and_tuple_prune(list_col_array + list_index*num_columns, - nparts, left_endpoint, include_endpoint) > 0) + cmp= cmp_rec_and_tuple_prune(list_col_array + list_index*num_columns, + nparts, left_endpoint, include_endpoint); + if (cmp > 0) + { min_list_index= list_index + 1; + } else + { max_list_index= list_index; + if (cmp == 0) + break; + } } while (max_list_index > min_list_index); list_index= max_list_index; @@ -3333,12 +3342,10 @@ uint32 get_partition_id_cols_list_for_endpoint(partition_info *part_info, nparts, left_endpoint, include_endpoint))); - if (!left_endpoint) - { - /* Set the end after this list tuple if not already after the last. */ - if (list_index < part_info->num_parts) - list_index++; - } + /* Include the right endpoint if not already passed end of array. */ + if (!left_endpoint && include_endpoint && cmp == 0 && + list_index < part_info->num_list_values) + list_index++; DBUG_RETURN(list_index); } @@ -7493,15 +7500,13 @@ static int cmp_rec_and_tuple_prune(part_column_list_val *val, field= val->part_info->part_field_array + n_vals_in_rec; if (!(*field)) { - /* - Full match, if right endpoint and not including the endpoint, - (rec < part) return lesser. - */ - if (!is_left_endpoint && !include_endpoint) - return -4; + /* Full match. Only equal if including endpoint. */ + if (include_endpoint) + return 0; - /* Otherwise they are equal! */ - return 0; + if (is_left_endpoint) + return +4; /* Start of range, part_tuple < rec, return higher. */ + return -4; /* End of range, rec < part_tupe, return lesser. */ } /* The prefix is equal and there are more partition columns to compare. -- cgit v1.2.1 From 8ade414b28bd73dd5fc04d372b61d1c49aba0a8a Mon Sep 17 00:00:00 2001 From: Chaithra Gopalareddy Date: Wed, 7 May 2014 14:59:23 +0530 Subject: Bug#17909656 - WRONG RESULTS FOR A SIMPLE QUERY WITH GROUP BY Problem: If there is a predicate on a column referenced by MIN/MAX and that predicate is not present in all the disjunctions on keyparts earlier in the compound index, Loose Index Scan will not return correct result. Analysis: When loose index scan is chosen, range optimizer currently groups all the predicates that contain group parts separately and minmax parts separately. It therefore applies all the conditions on the group parts first to the fetched row. Then in the call to next_max, it processes the conditions which have min/max keypart. For ex in the following query: Select f1, max(f2) from t1 where (f1 = 10 and f2 = 13) or (f1 = 3) group by f1; Condition (f2 = 13) would be applied even for rows that satisfy (f1 = 3) thereby giving wrong results. Solution: Do not choose loose_index_scan for such cases. So a new rule WA2 is introduced to take care of the same. WA2: "If there are predicates on C, these predicates must be in conjuction to all predicates on all earlier keyparts in I." Todo the same, fix reuses the function get_constant_key_infix(). Since this funciton will fail for all multi-range conditions, it is re-written to recognize that if the sub-conditions are equivalent across the disjuncts: it will now succeed. And to achieve this a new helper function is introduced called all_same(). The fix also moves the test of NGA3 up to the former only caller, get_constant_key_infix(). mysql-test/r/group_min_max_innodb.result: Added test result change for Bug#17909656 mysql-test/t/group_min_max_innodb.test: Added test cases for Bug#17909656 sql/opt_range.cc: Introduced Rule WA2 because of Bug#17909656 --- sql/opt_range.cc | 190 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 137 insertions(+), 53 deletions(-) (limited to 'sql') diff --git a/sql/opt_range.cc b/sql/opt_range.cc index c7a7d2531af..05da107f0ea 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1,4 +1,5 @@ -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights + * reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -304,31 +305,54 @@ public: :min_flag(0),elements(1),use_count(1),left(0),right(0),next_key_part(0), color(BLACK), type(type_arg) {} - inline bool is_same(SEL_ARG *arg) + /** + returns true if a range predicate is equal. Use all_same() + to check for equality of all the predicates on this keypart. + */ + inline bool is_same(const SEL_ARG *arg) const { if (type != arg->type || part != arg->part) - return 0; + return false; if (type != KEY_RANGE) - return 1; + return true; return cmp_min_to_min(arg) == 0 && cmp_max_to_max(arg) == 0; } + /** + returns true if all the predicates in the keypart tree are equal + */ + bool all_same(const SEL_ARG *arg) const + { + if (type != arg->type || part != arg->part) + return false; + if (type != KEY_RANGE) + return true; + if (arg == this) + return true; + const SEL_ARG *cmp_arg= arg->first(); + const SEL_ARG *cur_arg= first(); + for (; cur_arg && cmp_arg && cur_arg->is_same(cmp_arg); + cur_arg= cur_arg->next, cmp_arg= cmp_arg->next); + if (cur_arg || cmp_arg) + return false; + return true; + } inline void merge_flags(SEL_ARG *arg) { maybe_flag|=arg->maybe_flag; } inline void maybe_smaller() { maybe_flag=1; } /* Return true iff it's a single-point null interval */ inline bool is_null_interval() { return maybe_null && max_value[0] == 1; } - inline int cmp_min_to_min(SEL_ARG* arg) + inline int cmp_min_to_min(const SEL_ARG* arg) const { return sel_cmp(field,min_value, arg->min_value, min_flag, arg->min_flag); } - inline int cmp_min_to_max(SEL_ARG* arg) + inline int cmp_min_to_max(const SEL_ARG* arg) const { return sel_cmp(field,min_value, arg->max_value, min_flag, arg->max_flag); } - inline int cmp_max_to_max(SEL_ARG* arg) + inline int cmp_max_to_max(const SEL_ARG* arg) const { return sel_cmp(field,max_value, arg->max_value, max_flag, arg->max_flag); } - inline int cmp_max_to_min(SEL_ARG* arg) + inline int cmp_max_to_min(const SEL_ARG* arg) const { return sel_cmp(field,max_value, arg->min_value, max_flag, arg->min_flag); } @@ -507,6 +531,7 @@ public: void test_use_count(SEL_ARG *root); #endif SEL_ARG *first(); + const SEL_ARG *first() const; SEL_ARG *last(); void make_root(); inline bool simple_key() @@ -583,6 +608,18 @@ public: SEL_ARG *clone_tree(RANGE_OPT_PARAM *param); }; +/** + Helper function to compare two SEL_ARG's. +*/ +static bool all_same(const SEL_ARG *sa1, const SEL_ARG *sa2) +{ + if (sa1 == NULL && sa2 == NULL) + return true; + if ((sa1 != NULL && sa2 == NULL) || (sa1 == NULL && sa2 != NULL)) + return false; + return sa1->all_same(sa2); +} + class SEL_IMERGE; @@ -1770,6 +1807,13 @@ SEL_ARG *SEL_ARG::clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent, return tmp; } +/** + This gives the first SEL_ARG in the interval list, and the minimal element + in the red-black tree + + @return + SEL_ARG first SEL_ARG in the interval list +*/ SEL_ARG *SEL_ARG::first() { SEL_ARG *next_arg=this; @@ -1780,6 +1824,11 @@ SEL_ARG *SEL_ARG::first() return next_arg; } +const SEL_ARG *SEL_ARG::first() const +{ + return const_cast(this)->first(); +} + SEL_ARG *SEL_ARG::last() { SEL_ARG *next_arg=this; @@ -9356,6 +9405,8 @@ void QUICK_ROR_UNION_SELECT::add_keys_and_lengths(String *key_names, static inline uint get_field_keypart(KEY *index, Field *field); static inline SEL_ARG * get_index_range_tree(uint index, SEL_TREE* range_tree, PARAM *param, uint *param_idx); +static bool get_sel_arg_for_keypart(Field *field, SEL_ARG *index_range_tree, + SEL_ARG **cur_range); static bool get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree, KEY_PART_INFO *first_non_group_part, KEY_PART_INFO *min_max_arg_part, @@ -9451,6 +9502,8 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts, above tests. By transitivity then it also follows that each WA_i participates in the index I (if this was already tested for GA, NGA and C). + WA2. If there is a predicate on C, then it must be in conjunction + to all predicates on all earlier keyparts in I. C) Overall query form: SELECT EXPR([A_1,...,A_k], [B_1,...,B_m], [MIN(C)], [MAX(C)]) @@ -9856,6 +9909,25 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time) } } + /** + Test WA2:If there are conditions on a column C participating in + MIN/MAX, those conditions must be conjunctions to all earlier + keyparts. Otherwise, Loose Index Scan cannot be used. + */ + if (tree && min_max_arg_item) + { + uint dummy; + SEL_ARG *index_range_tree= get_index_range_tree(cur_index, tree, param, + &dummy); + SEL_ARG *cur_range= NULL; + if (get_sel_arg_for_keypart(min_max_arg_part->field, + index_range_tree, &cur_range) || + (cur_range && cur_range->type != SEL_ARG::KEY_RANGE)) + { + goto next_index; + } + } + /* If we got to this point, cur_index_info passes the test. */ key_infix_parts= cur_key_infix_len ? (uint) (first_non_infix_part - first_non_group_part) : 0; @@ -10099,73 +10171,75 @@ check_group_min_max_predicates(COND *cond, Item_field *min_max_arg_item, /* - Get SEL_ARG tree, if any, for the keypart covering non grouping - attribute (NGA) field 'nga_field'. + Get the SEL_ARG tree 'tree' for the keypart covering 'field', if + any. 'tree' must be a unique conjunction to ALL predicates in earlier + keyparts of 'keypart_tree'. + + E.g., if 'keypart_tree' is for a composite index (kp1,kp2) and kp2 + covers 'field', all these conditions satisfies the requirement: - This function enforces the NGA3 test: If 'keypart_tree' contains a - condition for 'nga_field', there can only be one range. In the - opposite case, this function returns with error and 'cur_range' - should not be used. + 1. "(kp1=2 OR kp1=3) AND kp2=10" => returns "kp2=10" + 2. "(kp1=2 AND kp2=10) OR (kp1=3 AND kp2=10)" => returns "kp2=10" + 3. "(kp1=2 AND (kp2=10 OR kp2=11)) OR (kp1=3 AND (kp2=10 OR kp2=11))" + => returns "kp2=10 OR kp2=11" - Note that the NGA1 and NGA2 requirements, like whether or not the - range predicate for 'nga_field' is equality, is not tested by this - function. + whereas these do not + 1. "(kp1=2 AND kp2=10) OR kp1=3" + 2. "(kp1=2 AND kp2=10) OR (kp1=3 AND kp2=11)" + 3. "(kp1=2 AND kp2=10) OR (kp1=3 AND (kp2=10 OR kp2=11))" - @param[in] nga_field The NGA field we want the SEL_ARG tree for + This function effectively tests requirement WA2. In combination with + a test that the returned tree has no more than one range it is also + a test of NGA3. + + @param[in] field The field we want the SEL_ARG tree for @param[in] keypart_tree Root node of the SEL_ARG* tree for the index @param[out] cur_range The SEL_ARG tree, if any, for the keypart covering field 'keypart_field' - @retval true 'keypart_tree' contained a predicate for 'nga_field' but - multiple ranges exists. 'cur_range' should not be used. + @retval true 'keypart_tree' contained a predicate for 'field' that + is not conjunction to all predicates on earlier keyparts @retval false otherwise */ static bool -get_sel_arg_for_keypart(Field *nga_field, +get_sel_arg_for_keypart(Field *field, SEL_ARG *keypart_tree, SEL_ARG **cur_range) { - if(keypart_tree == NULL) + if (keypart_tree == NULL) return false; - if(keypart_tree->field->eq(nga_field)) + if (keypart_tree->field->eq(field)) { - /* - Enforce NGA3: If a condition for nga_field has been found, only - a single range is allowed. - */ - if (keypart_tree->prev || keypart_tree->next) - return true; // There are multiple ranges - *cur_range= keypart_tree; return false; } - SEL_ARG *found_tree= NULL; + SEL_ARG *tree_first_range= NULL; SEL_ARG *first_kp= keypart_tree->first(); - for (SEL_ARG *cur_kp= first_kp; cur_kp && !found_tree; - cur_kp= cur_kp->next) + for (SEL_ARG *cur_kp= first_kp; cur_kp; cur_kp= cur_kp->next) { + SEL_ARG *curr_tree= NULL; if (cur_kp->next_key_part) { - if (get_sel_arg_for_keypart(nga_field, + if (get_sel_arg_for_keypart(field, cur_kp->next_key_part, - &found_tree)) + &curr_tree)) return true; - } /* - Enforce NGA3: If a condition for nga_field has been found,only - a single range is allowed. - */ - if (found_tree && first_kp->next) - return true; // There are multiple ranges + Check if the SEL_ARG tree for 'field' is identical for all ranges in + 'keypart_tree + */ + if (cur_kp == first_kp) + tree_first_range= curr_tree; + else if (!all_same(tree_first_range, curr_tree)) + return true; } - *cur_range= found_tree; + *cur_range= tree_first_range; return false; } - /* Extract a sequence of constants from a conjunction of equality predicates. @@ -10188,7 +10262,8 @@ get_sel_arg_for_keypart(Field *nga_field, (const_ci = NG_i).. In addition, there can only be one range when there is such a gap. Thus all the NGF_i attributes must fill the 'gap' between the last group-by - attribute and the MIN/MAX attribute in the index (if present). If these + attribute and the MIN/MAX attribute in the index (if present). Also ensure + that there is only a single range on NGF_i (NGA3). If these conditions hold, copy each constant from its corresponding predicate into key_infix, in the order its NG_i attribute appears in the index, and update key_infix_len with the total length of the key parts in key_infix. @@ -10197,7 +10272,6 @@ get_sel_arg_for_keypart(Field *nga_field, TRUE if the index passes the test FALSE o/w */ - static bool get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree, KEY_PART_INFO *first_non_group_part, @@ -10217,32 +10291,42 @@ get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree, { cur_range= NULL; /* - Find the range tree for the current keypart. We assume that - index_range_tree points to the first keypart in the index. + Check NGA3: + 1. get_sel_arg_for_keypart gets the range tree for the 'field' and also + checks for a unique conjunction of this tree with all the predicates + on the earlier keyparts in the index. + 2. Check for multiple ranges on the found keypart tree. + + We assume that index_range_tree points to the leftmost keypart in + the index. */ - if(get_sel_arg_for_keypart(cur_part->field, index_range_tree, &cur_range)) + if (get_sel_arg_for_keypart(cur_part->field, index_range_tree, + &cur_range)) + return false; + + if (cur_range && cur_range->elements > 1) return false; if (!cur_range) { if (min_max_arg_part) - return FALSE; /* The current keypart has no range predicates at all. */ + return false; /* The current keypart has no range predicates at all. */ else { *first_non_infix_part= cur_part; - return TRUE; + return true; } } if ((cur_range->min_flag & NO_MIN_RANGE) || (cur_range->max_flag & NO_MAX_RANGE) || (cur_range->min_flag & NEAR_MIN) || (cur_range->max_flag & NEAR_MAX)) - return FALSE; + return false; uint field_length= cur_part->store_length; if (cur_range->maybe_null && cur_range->min_value[0] && cur_range->max_value[0]) - { + { /* cur_range specifies 'IS NULL'. In this case the argument points to a "null value" (is_null_string) that may not always be long @@ -10261,7 +10345,7 @@ get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree, *key_infix_len+= field_length; } else - return FALSE; + return false; } if (!min_max_arg_part && (cur_part == last_part)) -- cgit v1.2.1 From e1da25f62117c8171ddb7e187ac3aedcacbe4088 Mon Sep 17 00:00:00 2001 From: Chaithra Gopalareddy Date: Wed, 7 May 2014 16:55:03 +0530 Subject: Fixing compilation error. Post push fix for Bug#17909656 --- sql/opt_range.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 05da107f0ea..f7d8d0009d4 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -331,7 +331,7 @@ public: const SEL_ARG *cmp_arg= arg->first(); const SEL_ARG *cur_arg= first(); for (; cur_arg && cmp_arg && cur_arg->is_same(cmp_arg); - cur_arg= cur_arg->next, cmp_arg= cmp_arg->next); + cur_arg= cur_arg->next, cmp_arg= cmp_arg->next) ; if (cur_arg || cmp_arg) return false; return true; -- cgit v1.2.1 From ee3c555ad9abd0f98434910ce7892819607c06a3 Mon Sep 17 00:00:00 2001 From: mithun Date: Thu, 8 May 2014 14:49:53 +0530 Subject: Bug #17059925: UNIONS COMPUTES ROWS_EXAMINED INCORRECTLY ISSUE: ------ For UNION of selects, rows examined by the query will be sum of rows examined by individual select operations and rows examined for union operation. The value of session level global counter that is used to count the rows examined by a select statement should be accumulated and reset before it is used for next select statement. But we have missed to reset the same. Because of this examined row count of a select query is accounted more than once. SOLUTION: --------- In union reset the session level global counter used to accumulate count of examined rows after its value is saved. mysql-test/r/union.result: Expected output of testcase added. mysql-test/t/union.test: Test to verify examined row count of Union operations. sql/sql_union.cc: Reset the value of thd->examined_row_count after accumulating the value. --- sql/sql_union.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_union.cc b/sql/sql_union.cc index d0660e8f117..d230b903d2c 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -557,7 +557,13 @@ bool st_select_lex_unit::exec() 0); if (!saved_error) { + /* + Save the current examined row count locally and clear the global + counter, so that we can accumulate the number of evaluated rows for + the current query block. + */ examined_rows+= thd->examined_row_count; + thd->examined_row_count= 0; if (union_result->flush()) { thd->lex->current_select= lex_select_save; -- cgit v1.2.1 From 2870bd7423b5bb00360d5e24f44ded071c40be07 Mon Sep 17 00:00:00 2001 From: Venkatesh Duggirala Date: Thu, 8 May 2014 18:13:01 +0530 Subject: Bug#17283409 4-WAY DEADLOCK: ZOMBIES, PURGING BINLOGS, SHOW PROCESSLIST, SHOW BINLOGS Problem: A deadlock was occurring when 4 threads were involved in acquiring locks in the following way Thread 1: Dump thread ( Slave is reconnecting, so on Master, a new dump thread is trying kill zombie dump threads. It acquired thread's LOCK_thd_data and it is about to acquire mysys_var->current_mutex ( which LOCK_log) Thread 2: Application thread is executing show binlogs and acquired LOCK_log and it is about to acquire LOCK_index. Thread 3: Application thread is executing Purge binary logs and acquired LOCK_index and it is about to acquire LOCK_thread_count. Thread 4: Application thread is executing show processlist and acquired LOCK_thread_count and it is about to acquire zombie dump thread's LOCK_thd_data. Deadlock Cycle: Thread 1 -> Thread 2 -> Thread 3-> Thread 4 ->Thread 1 The same above deadlock was observed even when thread 4 is executing 'SELECT * FROM information_schema.processlist' command and acquired LOCK_thread_count and it is about to acquire zombie dump thread's LOCK_thd_data. Analysis: There are four locks involved in the deadlock. LOCK_log, LOCK_thread_count, LOCK_index and LOCK_thd_data. LOCK_log, LOCK_thread_count, LOCK_index are global mutexes where as LOCK_thd_data is local to a thread. We can divide these four locks in two groups. Group 1 consists of LOCK_log and LOCK_index and the order should be LOCK_log followed by LOCK_index. Group 2 consists of other two mutexes LOCK_thread_count, LOCK_thd_data and the order should be LOCK_thread_count followed by LOCK_thd_data. Unfortunately, there is no specific predefined lock order defined to follow in the MySQL system when it comes to locks across these two groups. In the above problematic example, there is no problem in the way we are acquiring the locks if you see each thread individually. But If you combine all 4 threads, they end up in a deadlock. Fix: Since everything seems to be fine in the way threads are taking locks, In this patch We are changing the duration of the locks in Thread 4 to break the deadlock. i.e., before the patch, Thread 4 ('show processlist' command) mysqld_list_processes() function acquires LOCK_thread_count for the complete duration of the function and it also acquires/releases each thread's LOCK_thd_data. LOCK_thread_count is used to protect addition and deletion of threads in global threads list. While show process list is looping through all the existing threads, it will be a problem if a thread is exited but there is no problem if a new thread is added to the system. Hence a new mutex is introduced "LOCK_thd_remove" which will protect deletion of a thread from global threads list. All threads which are getting exited should acquire LOCK_thd_remove followed by LOCK_thread_count. (It should take LOCK_thread_count also because other places of the code still thinks that exit thread is protected with LOCK_thread_count. In this fix, we are changing only 'show process list' query logic ) (Eg: unlink_thd logic will be protected with LOCK_thd_remove). Logic of mysqld_list_processes(or file_schema_processlist) will now be protected with 'LOCK_thd_remove' instead of 'LOCK_thread_count'. Now the new locking order after this patch is: LOCK_thd_remove -> LOCK_thd_data -> LOCK_log -> LOCK_index -> LOCK_thread_count --- sql/event_scheduler.cc | 8 +++++--- sql/log.cc | 16 ++++++++-------- sql/mysqld.cc | 17 ++++++++++++++--- sql/mysqld.h | 6 ++++-- sql/scheduler.cc | 3 ++- sql/slave.cc | 6 +++++- sql/sql_class.cc | 29 ++++++++++++++++++++++++++++- sql/sql_insert.cc | 4 +++- sql/sql_repl.cc | 5 +++-- sql/sql_show.cc | 42 ++++++++++++++++++++++++++++++++---------- 10 files changed, 104 insertions(+), 32 deletions(-) (limited to 'sql') diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index 52eae66b603..2c397f15357 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2006, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,6 +22,7 @@ #include "event_db_repository.h" #include "sql_connect.h" // init_new_connection_handler_thread #include "sql_acl.h" // SUPER_ACL +extern void delete_thd(THD *); // Used in deinit_event_thread() /** @addtogroup Event_Scheduler @@ -156,12 +157,13 @@ deinit_event_thread(THD *thd) DBUG_ASSERT(thd->net.buff != 0); net_end(&thd->net); DBUG_PRINT("exit", ("Event thread finishing")); + mysql_mutex_lock(&LOCK_thd_remove); mysql_mutex_lock(&LOCK_thread_count); - thread_count--; dec_thread_running(); - delete thd; + delete_thd(thd); mysql_cond_broadcast(&COND_thread_count); mysql_mutex_unlock(&LOCK_thread_count); + mysql_mutex_unlock(&LOCK_thd_remove); } diff --git a/sql/log.cc b/sql/log.cc index b318780ea41..7327015deb5 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -3427,6 +3427,13 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd) ha_reset_logs(thd); + /* + We need to get both locks to be sure that no one is trying to + write to the index log file. + */ + mysql_mutex_lock(&LOCK_log); + mysql_mutex_lock(&LOCK_index); + /* The following mutex is needed to ensure that no threads call 'delete thd' as we would then risk missing a 'rollback' from this @@ -3435,13 +3442,6 @@ bool MYSQL_BIN_LOG::reset_logs(THD* thd) */ mysql_mutex_lock(&LOCK_thread_count); - /* - We need to get both locks to be sure that no one is trying to - write to the index log file. - */ - mysql_mutex_lock(&LOCK_log); - mysql_mutex_lock(&LOCK_index); - /* Save variables so that we can reopen the log */ save_name=name; name=0; // Protect against free diff --git a/sql/mysqld.cc b/sql/mysqld.cc index b61e64e627b..1489e3cdc31 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -642,7 +642,7 @@ SHOW_COMP_OPTION have_profiling; pthread_key(MEM_ROOT**,THR_MALLOC); pthread_key(THD*, THR_THD); mysql_mutex_t LOCK_thread_created; -mysql_mutex_t LOCK_thread_count; +mysql_mutex_t LOCK_thread_count, LOCK_thd_remove; mysql_mutex_t LOCK_status, LOCK_error_log, LOCK_uuid_generator, LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create, @@ -1607,6 +1607,7 @@ static void clean_up_mutexes() mysql_mutex_destroy(&LOCK_prepared_stmt_count); mysql_mutex_destroy(&LOCK_error_messages); mysql_cond_destroy(&COND_thread_count); + mysql_mutex_destroy(&LOCK_thd_remove); mysql_cond_destroy(&COND_thread_cache); mysql_cond_destroy(&COND_flush_thread_cache); mysql_cond_destroy(&COND_manager); @@ -2160,6 +2161,8 @@ void dec_connection_count() void delete_thd(THD *thd) { + mysql_mutex_assert_owner(&LOCK_thread_count); + mysql_mutex_assert_owner(&LOCK_thd_remove); thread_count--; delete thd; } @@ -2173,7 +2176,7 @@ void delete_thd(THD *thd) thd Thread handler NOTES - LOCK_thread_count is locked and left locked + LOCK_thread_count, LOCK_thd_remove are locked and left locked */ void unlink_thd(THD *thd) @@ -2183,6 +2186,7 @@ void unlink_thd(THD *thd) thd_cleanup(thd); dec_connection_count(); + mysql_mutex_lock(&LOCK_thd_remove); mysql_mutex_lock(&LOCK_thread_count); /* Used by binlog_reset_master. It would be cleaner to use @@ -2295,6 +2299,7 @@ bool one_thread_per_connection_end(THD *thd, bool put_in_cache) { DBUG_ENTER("one_thread_per_connection_end"); unlink_thd(thd); + mysql_mutex_unlock(&LOCK_thd_remove); if (put_in_cache) put_in_cache= cache_thread(); mysql_mutex_unlock(&LOCK_thread_count); @@ -3589,6 +3594,8 @@ static int init_thread_environment() mysql_mutex_init(key_LOCK_thread_created, &LOCK_thread_created, MY_MUTEX_INIT_FAST); mysql_mutex_init(key_LOCK_thread_count, &LOCK_thread_count, MY_MUTEX_INIT_FAST); mysql_mutex_init(key_LOCK_status, &LOCK_status, MY_MUTEX_INIT_FAST); + mysql_mutex_init(key_LOCK_thd_remove, + &LOCK_thd_remove, MY_MUTEX_INIT_FAST); mysql_mutex_init(key_LOCK_delayed_insert, &LOCK_delayed_insert, MY_MUTEX_INIT_FAST); mysql_mutex_init(key_LOCK_delayed_status, @@ -5080,9 +5087,11 @@ void create_thread_to_handle_connection(THD *thd) ER_THD(thd, ER_CANT_CREATE_THREAD), error); net_send_error(thd, ER_CANT_CREATE_THREAD, error_message_buff, NULL); close_connection(thd); + mysql_mutex_lock(&LOCK_thd_remove); mysql_mutex_lock(&LOCK_thread_count); delete thd; mysql_mutex_unlock(&LOCK_thread_count); + mysql_mutex_unlock(&LOCK_thd_remove); return; /* purecov: end */ } @@ -7873,6 +7882,7 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_prep_xids, key_structure_guard_mutex, key_TABLE_SHARE_LOCK_ha_data, key_LOCK_error_messages, key_LOG_INFO_lock, key_LOCK_thread_count, key_PARTITION_LOCK_auto_inc; +PSI_mutex_key key_LOCK_thd_remove; PSI_mutex_key key_RELAYLOG_LOCK_index; PSI_mutex_key key_LOCK_thread_created; @@ -7928,7 +7938,8 @@ static PSI_mutex_info all_server_mutexes[]= { &key_LOG_INFO_lock, "LOG_INFO::lock", 0}, { &key_LOCK_thread_count, "LOCK_thread_count", PSI_FLAG_GLOBAL}, { &key_PARTITION_LOCK_auto_inc, "HA_DATA_PARTITION::LOCK_auto_inc", 0}, - { &key_LOCK_thread_created, "LOCK_thread_created", PSI_FLAG_GLOBAL } + { &key_LOCK_thread_created, "LOCK_thread_created", PSI_FLAG_GLOBAL }, + { &key_LOCK_thd_remove, "LOCK_thd_remove", PSI_FLAG_GLOBAL} }; PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger, diff --git a/sql/mysqld.h b/sql/mysqld.h index f500a0abf3f..bd93264d50d 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2006, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -252,7 +252,8 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_prep_xids, key_relay_log_info_log_space_lock, key_relay_log_info_run_lock, key_relay_log_info_sleep_lock, key_structure_guard_mutex, key_TABLE_SHARE_LOCK_ha_data, - key_LOCK_error_messages, key_LOCK_thread_count, key_PARTITION_LOCK_auto_inc; + key_LOCK_error_messages, key_LOCK_thread_count, key_PARTITION_LOCK_auto_inc, + key_LOCK_thd_remove; extern PSI_mutex_key key_RELAYLOG_LOCK_index; extern PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger, @@ -346,6 +347,7 @@ extern mysql_mutex_t LOCK_global_system_variables, LOCK_user_conn, LOCK_prepared_stmt_count, LOCK_error_messages, LOCK_connection_count; extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_thread_count; +extern MYSQL_PLUGIN_IMPORT mysql_mutex_t LOCK_thd_remove; #ifdef HAVE_OPENSSL extern mysql_mutex_t LOCK_des_key_file; #endif diff --git a/sql/scheduler.cc b/sql/scheduler.cc index a2abc7c14bf..fa2c43c3b65 100644 --- a/sql/scheduler.cc +++ b/sql/scheduler.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -36,6 +36,7 @@ static bool no_threads_end(THD *thd, bool put_in_cache) { unlink_thd(thd); mysql_mutex_unlock(&LOCK_thread_count); + mysql_mutex_unlock(&LOCK_thd_remove); return 1; // Abort handle_one_connection } diff --git a/sql/slave.cc b/sql/slave.cc index 0e23b1af606..23460c1af63 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -3119,10 +3119,12 @@ err: change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE); DBUG_ASSERT(thd->net.buff != 0); net_end(&thd->net); // destructor will not free it, because net.vio is 0 + mysql_mutex_lock(&LOCK_thd_remove); mysql_mutex_lock(&LOCK_thread_count); THD_CHECK_SENTRY(thd); delete thd; mysql_mutex_unlock(&LOCK_thread_count); + mysql_mutex_unlock(&LOCK_thd_remove); mi->abort_slave= 0; mi->slave_running= 0; mi->io_thd= 0; @@ -3518,10 +3520,12 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ THD_CHECK_SENTRY(thd); rli->sql_thd= 0; set_thd_in_use_temporary_tables(rli); // (re)set sql_thd in use for saved temp tables + mysql_mutex_lock(&LOCK_thd_remove); mysql_mutex_lock(&LOCK_thread_count); THD_CHECK_SENTRY(thd); delete thd; mysql_mutex_unlock(&LOCK_thread_count); + mysql_mutex_unlock(&LOCK_thd_remove); /* Note: the order of the broadcast and unlock calls below (first broadcast, then unlock) is important. Otherwise a killer_thread can execute between the calls and diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 93f1d8eb3bb..136c6012514 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -332,6 +332,26 @@ void thd_unlock_thread_count(THD *) mysql_mutex_unlock(&LOCK_thread_count); } +/** + Lock thread removal mutex + + @param thd THD object +*/ +void thd_lock_thread_remove(THD *) +{ + mysql_mutex_lock(&LOCK_thd_remove); +} + +/** + Unlock thread removal mutex + + @param thd THD object +*/ +void thd_unlock_thread_remove(THD *) +{ + mysql_mutex_unlock(&LOCK_thd_remove); +} + /** Close the socket used by this connection @@ -1561,6 +1581,13 @@ void THD::awake(THD::killed_state state_to_set) */ if (mysys_var->current_cond && mysys_var->current_mutex) { + DBUG_EXECUTE_IF("before_dump_thread_acquires_current_mutex", + { + const char act[]= + "now signal dump_thread_signal wait_for go"; + DBUG_ASSERT(!debug_sync_set_action(current_thd, + STRING_WITH_LEN(act))); + };); mysql_mutex_lock(mysys_var->current_mutex); mysql_cond_broadcast(mysys_var->current_cond); mysql_mutex_unlock(mysys_var->current_mutex); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 0f432779305..d6a83b9c35e 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1933,6 +1933,7 @@ public: close_thread_tables(&thd); thd.mdl_context.release_transactional_locks(); } + mysql_mutex_lock(&LOCK_thd_remove); mysql_mutex_lock(&LOCK_thread_count); mysql_mutex_destroy(&mutex); mysql_cond_destroy(&cond); @@ -1944,6 +1945,7 @@ public: thread_count--; delayed_insert_threads--; mysql_mutex_unlock(&LOCK_thread_count); + mysql_mutex_unlock(&LOCK_thd_remove); mysql_cond_broadcast(&COND_thread_count); /* Tell main we are ready */ } diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index ec1ef72cd73..343e128af7a 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -248,7 +248,7 @@ bool log_in_use(const char* log_name) size_t log_name_len = strlen(log_name) + 1; THD *tmp; bool result = 0; - + DEBUG_SYNC(current_thd,"purge_logs_after_lock_index_before_thread_count"); mysql_mutex_lock(&LOCK_thread_count); I_List_iterator it(threads); @@ -1965,6 +1965,7 @@ bool show_binlogs(THD* thd) DBUG_RETURN(TRUE); mysql_mutex_lock(mysql_bin_log.get_log_lock()); + DEBUG_SYNC(thd, "show_binlogs_after_lock_log_before_lock_index"); mysql_bin_log.lock_index(); index_file=mysql_bin_log.get_index_file(); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index ac63b2aaff9..f02ea6bead7 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1814,13 +1814,23 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_VOID_RETURN; - mysql_mutex_lock(&LOCK_thread_count); // For unlink from list + if (!thd->killed) { + /* + Acquire only LOCK_thd_remove and not LOCK_thread_count. + i.e., we allow new threads to be added to the list while processing + the list but we will not allow deletions from the list (Note that unlink + a thread is protected by both LOCK_thd_remove and LOCK_thread_count + mutexes). + */ + mysql_mutex_lock(&LOCK_thd_remove); I_List_iterator it(threads); THD *tmp; + DEBUG_SYNC(thd,"before_one_element_read_from_threads_iterator"); while ((tmp=it++)) { + DEBUG_SYNC(thd,"after_one_element_read_from_threads_iterator"); Security_context *tmp_sctx= tmp->security_ctx; struct st_my_thread_var *mysys_var; if ((tmp->vio_ok() || tmp->system_thread) && @@ -1845,6 +1855,11 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) tmp_sctx->get_host()->length() ? tmp_sctx->get_host()->ptr() : ""); thd_info->command=(int) tmp->command; + DBUG_EXECUTE_IF("processlist_acquiring_dump_threads_LOCK_thd_data", + { + if (tmp->command == COM_BINLOG_DUMP) + DEBUG_SYNC(thd, "processlist_after_LOCK_thd_count_before_LOCK_thd_data"); + }); mysql_mutex_lock(&tmp->LOCK_thd_data); if ((thd_info->db= tmp->db)) // Safe test thd_info->db= thd->strdup(thd_info->db); @@ -1869,8 +1884,8 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) thread_infos.append(thd_info); } } + mysql_mutex_unlock(&LOCK_thd_remove); } - mysql_mutex_unlock(&LOCK_thread_count); thread_info *thd_info; time_t now= my_time(0); @@ -1910,10 +1925,15 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) user= thd->security_ctx->master_access & PROCESS_ACL ? NullS : thd->security_ctx->priv_user; - mysql_mutex_lock(&LOCK_thread_count); - if (!thd->killed) { + /* + Acquire only LOCK_thd_remove and not LOCK_thread_count. + i.e., we allow new threads to be added to the list while processing + the list but we will not allow deletions from the list (Note that unlink + thread is also protected with LOCK_thd_remove mutex). + */ + mysql_mutex_lock(&LOCK_thd_remove); I_List_iterator it(threads); THD* tmp; @@ -1946,7 +1966,13 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) else table->field[2]->store(tmp_sctx->host_or_ip, strlen(tmp_sctx->host_or_ip), cs); + DBUG_EXECUTE_IF("processlist_acquiring_dump_threads_LOCK_thd_data", + { + if (tmp->command == COM_BINLOG_DUMP) + DEBUG_SYNC(thd, "processlist_after_LOCK_thd_count_before_LOCK_thd_data"); + }); /* DB */ + /* Lock THD mutex that protects its data when looking at it. */ mysql_mutex_lock(&tmp->LOCK_thd_data); if ((db= tmp->db)) { @@ -1974,11 +2000,8 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) if (mysys_var) mysql_mutex_unlock(&mysys_var->mutex); - mysql_mutex_unlock(&tmp->LOCK_thd_data); /* INFO */ - /* Lock THD mutex that protects its data when looking at it. */ - mysql_mutex_lock(&tmp->LOCK_thd_data); if (tmp->query()) { table->field[7]->store(tmp->query(), @@ -1990,13 +2013,12 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) if (schema_table_store_record(thd, table)) { - mysql_mutex_unlock(&LOCK_thread_count); + mysql_mutex_unlock(&LOCK_thd_remove); DBUG_RETURN(1); } } + mysql_mutex_unlock(&LOCK_thd_remove); } - - mysql_mutex_unlock(&LOCK_thread_count); DBUG_RETURN(0); } -- cgit v1.2.1 From f22023351248e260743b6521c7795faf9e67a837 Mon Sep 17 00:00:00 2001 From: mithun Date: Thu, 15 May 2014 11:46:57 +0530 Subject: Bug#17217128 : BAD INTERACTION BETWEEN MIN/MAX AND "HAVING SUM(DISTINCT)": WRONG RESULTS. ISSUE: ------ If a query uses loose index scan and it has both AGG(DISTINCT) and MIN()/MAX()functions. Then, result values of MIN/MAX() is set improperly. When query has AGG(DISTINCT) then end_select is set to end_send_group. "end_send_group" keeps doing aggregation until it sees a record from next group. And, then it will send out the result row of that group. Since query also has MIN()/MAX() and loose index scan is used, values of MIN/MAX() are set as part of loose index scan itself. Setting MIN()/MAX() values as part of loose index scan overwrites values computed in end_send_group. This caused invalid result. For such queries to work loose index scan should stop performing MIN/MAX() aggregation. And, let end_send_group to do the same. But according to current design loose index scan can produce only one row per group key. If we have both MIN() and MAX() then it has to give two records out. This is not possible as interface has to use common buffer record[0]! for both records at a time. SOLUTIONS: ---------- For such queries to work we need a new interface for loose index scan. Hence, do not choose loose_index_scan for such cases. So a new rule SA7 is introduced to take care of the same. SA7: "If Q has both AGG_FUNC(DISTINCT ...) and MIN/MAX() functions then loose index scan access method is not used." mysql-test/r/group_min_max.result: Expected result. mysql-test/t/group_min_max.test: 1. Test with various combination of AGG(DISTINCT) and MIN(), MAX() functions. 2. Corrected the plan for old queries. sql/opt_range.cc: A new rule SA7 is introduced. --- sql/opt_range.cc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'sql') diff --git a/sql/opt_range.cc b/sql/opt_range.cc index f7d8d0009d4..0f2a66a9efe 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -9471,6 +9471,16 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts, never stored after a unique key lookup in the clustered index and furhter index_next/prev calls can not be used. So loose index scan optimization can not be used in this case. + SA7. If Q has both AGG_FUNC(DISTINCT ...) and MIN/MAX() functions then this + access method is not used. + For above queries MIN/MAX() aggregation has to be done at + nested_loops_join (end_send_group). But with current design MIN/MAX() + is always set as part of loose index scan. Because of this mismatch + MIN() and MAX() values will be set incorrectly. For such queries to + work we need a new interface for loose index scan. This new interface + should only fetch records with min and max values and let + end_send_group to do aggregation. Until then do not use + loose_index_scan. GA1. If Q has a GROUP BY clause, then GA is a prefix of I. That is, if G_i = A_j => i = j. GA2. If Q has a DISTINCT clause, then there is a permutation of SA that @@ -9636,6 +9646,13 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree, double read_time) DBUG_RETURN(NULL); } } + + /* Check (SA7). */ + if (is_agg_distinct && (have_max || have_min)) + { + DBUG_RETURN(NULL); + } + /* Check (SA5). */ if (join->select_distinct) { -- cgit v1.2.1 From 10978e0aa9e3dca43cf26c2c63f46181483073af Mon Sep 17 00:00:00 2001 From: Neeraj Bisht Date: Thu, 15 May 2014 15:50:52 +0530 Subject: Bug#18207212 : FILE NAME IS NOT ESCAPED IN BINLOG FOR LOAD DATA INFILE STATEMENT Problem: Load_log_event::print_query() function does not put escape character in file name for "LOAD DATA INFILE" statement. Analysis: When we have "'" in our file name for "LOAD DATA INFILE" statement, Load_log_event::print_query() function does not put escape character in our file name. This one result that when we show binary-log, we get file name without escape character. Solution: To put escape character when we have "'" in file name, for this instead of using simple memcpy() to put file-name, we will use pretty_print_str(). --- sql/log_event.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'sql') diff --git a/sql/log_event.cc b/sql/log_event.cc index fea7c863cbc..40e626f1083 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -4384,7 +4384,7 @@ uint Load_log_event::get_query_buffer_length() return //the DB name may double if we escape the quote character 5 + 2*db_len + 3 + - 18 + fname_len + 2 + // "LOAD DATA INFILE 'file''" + 18 + fname_len*4 + 2 + // "LOAD DATA INFILE 'file''" 11 + // "CONCURRENT " 7 + // LOCAL 9 + // " REPLACE or IGNORE " @@ -4430,9 +4430,9 @@ void Load_log_event::print_query(bool need_db, const char *cs, char *buf, if (check_fname_outside_temp_buf()) pos= strmov(pos, "LOCAL "); - pos= strmov(pos, "INFILE '"); - memcpy(pos, fname, fname_len); - pos= strmov(pos+fname_len, "' "); + pos= strmov(pos, "INFILE "); + pos= pretty_print_str(pos, fname, fname_len); + pos= strmov(pos, " "); if (sql_ex.opt_flags & REPLACE_FLAG) pos= strmov(pos, "REPLACE "); @@ -7339,9 +7339,9 @@ void Execute_load_query_log_event::print(FILE* file, if (local_fname) { my_b_write(&cache, (uchar*) query, fn_pos_start); - my_b_printf(&cache, " LOCAL INFILE \'"); - my_b_printf(&cache, "%s", local_fname); - my_b_printf(&cache, "\'"); + my_b_printf(&cache, " LOCAL INFILE "); + pretty_print_str(&cache, local_fname, strlen(local_fname)); + if (dup_handling == LOAD_DUP_REPLACE) my_b_printf(&cache, " REPLACE"); my_b_printf(&cache, " INTO"); -- cgit v1.2.1 From ab8bd02b9b5283753a31595abfc3798a0970b33a Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Fri, 16 May 2014 10:18:43 +0200 Subject: Bug#18315770 BUG#12368495 FIX IS INCOMPLETE Item_func_ltrim::val_str did not handle multibyte charsets. Fix: factor out common code for Item_func_trim and Item_func_ltrim. --- sql/item_strfunc.cc | 92 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 32 deletions(-) (limited to 'sql') diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index ae93f060956..b7eacc2092a 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -1588,6 +1588,42 @@ String *Item_func_substr_index::val_str(String *str) return (&tmp_value); } + +/** + A helper function for trim(leading ...) for multibyte charsets. + @param res Copy of 'res' in calling functions. + @param ptr Where to start trimming. + @param end End of string to be trimmed. + @param remove_str The string to be removed from [ptr .. end) + @return Pointer to left-trimmed string. + */ +static inline +char *trim_left_mb(String *res, char *ptr, char *end, String *remove_str) +{ + const char * const r_ptr= remove_str->ptr(); + const uint remove_length= remove_str->length(); + + while (ptr + remove_length <= end) + { + uint num_bytes= 0; + while (num_bytes < remove_length) + { + uint len; + if ((len= my_ismbchar(res->charset(), ptr + num_bytes, end))) + num_bytes+= len; + else + ++num_bytes; + } + if (num_bytes != remove_length) + break; + if (memcmp(ptr, r_ptr, remove_length)) + break; + ptr+= remove_length; + } + return ptr; +} + + /* ** The trim functions are extension to ANSI SQL because they trim substrings ** They ltrim() and rtrim() functions are optimized for 1 byte strings @@ -1622,19 +1658,28 @@ String *Item_func_ltrim::val_str(String *str) ptr= (char*) res->ptr(); end= ptr+res->length(); - if (remove_length == 1) +#ifdef USE_MB + if (use_mb(res->charset())) { - char chr=(*remove_str)[0]; - while (ptr != end && *ptr == chr) - ptr++; + ptr= trim_left_mb(res, ptr, end, remove_str); } else +#endif /* USE_MB */ { - const char *r_ptr=remove_str->ptr(); - end-=remove_length; - while (ptr <= end && !memcmp(ptr, r_ptr, remove_length)) - ptr+=remove_length; - end+=remove_length; + if (remove_length == 1) + { + char chr=(*remove_str)[0]; + while (ptr != end && *ptr == chr) + ptr++; + } + else + { + const char *r_ptr=remove_str->ptr(); + end-=remove_length; + while (ptr <= end && !memcmp(ptr, r_ptr, remove_length)) + ptr+=remove_length; + end+=remove_length; + } } if (ptr == res->ptr()) return res; @@ -1728,11 +1773,8 @@ String *Item_func_trim::val_str(String *str) { DBUG_ASSERT(fixed == 1); char buff[MAX_FIELD_WIDTH], *ptr, *end; - const char *r_ptr; String tmp(buff, sizeof(buff), system_charset_info); String *res, *remove_str; - uint remove_length; - LINT_INIT(remove_length); res= args[0]->val_str(str); if ((null_value=args[0]->null_value)) @@ -1745,33 +1787,19 @@ String *Item_func_trim::val_str(String *str) return 0; } - if ((remove_length= remove_str->length()) == 0 || + const uint remove_length= remove_str->length(); + if (remove_length == 0 || remove_length > res->length()) return res; ptr= (char*) res->ptr(); end= ptr+res->length(); - r_ptr= remove_str->ptr(); + const char * const r_ptr= remove_str->ptr(); #ifdef USE_MB if (use_mb(res->charset())) { - while (ptr + remove_length <= end) - { - uint num_bytes= 0; - while (num_bytes < remove_length) - { - uint len; - if ((len= my_ismbchar(res->charset(), ptr + num_bytes, end))) - num_bytes+= len; - else - ++num_bytes; - } - if (num_bytes != remove_length) - break; - if (memcmp(ptr, r_ptr, remove_length)) - break; - ptr+= remove_length; - } + ptr= trim_left_mb(res, ptr, end, remove_str); + char *p=ptr; register uint32 l; loop: -- cgit v1.2.1 From f88e362fbc3b4d0a491643297b68d043658b6f07 Mon Sep 17 00:00:00 2001 From: Tor Didriksen Date: Fri, 6 Jun 2014 16:49:25 +0200 Subject: Bug#18786138 SHA/MD5 HASHING FUNCTIONS DIE WITH "FILENAME" CHARACTER SET For charsets with no binary collation: use my_charset_bin. --- sql/item_strfunc.cc | 50 +++++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 27 deletions(-) (limited to 'sql') diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index b7eacc2092a..bc7d488a41d 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -195,16 +195,27 @@ String *Item_func_md5::val_str_ascii(String *str) } +/* + The MD5()/SHA() functions treat their parameter as being a case sensitive. + Thus we set binary collation on it so different instances of MD5() will be + compared properly. +*/ +static CHARSET_INFO *get_checksum_charset(const char *csname) +{ + CHARSET_INFO *cs= get_charset_by_csname(csname, MY_CS_BINSORT, MYF(0)); + if (!cs) + { + // Charset has no binary collation: use my_charset_bin. + cs= &my_charset_bin; + } + return cs; +} + + void Item_func_md5::fix_length_and_dec() { - /* - The MD5() function treats its parameter as being a case sensitive. Thus - we set binary collation on it so different instances of MD5() will be - compared properly. - */ - args[0]->collation.set( - get_charset_by_csname(args[0]->collation.collation->csname, - MY_CS_BINSORT,MYF(0)), DERIVATION_COERCIBLE); + CHARSET_INFO *cs= get_checksum_charset(args[0]->collation.collation->csname); + args[0]->collation.set(cs, DERIVATION_COERCIBLE); fix_length_and_charset(32, default_charset()); } @@ -239,14 +250,8 @@ String *Item_func_sha::val_str_ascii(String *str) void Item_func_sha::fix_length_and_dec() { - /* - The SHA() function treats its parameter as being a case sensitive. Thus - we set binary collation on it so different instances of MD5() will be - compared properly. - */ - args[0]->collation.set( - get_charset_by_csname(args[0]->collation.collation->csname, - MY_CS_BINSORT,MYF(0)), DERIVATION_COERCIBLE); + CHARSET_INFO *cs= get_checksum_charset(args[0]->collation.collation->csname); + args[0]->collation.set(cs, DERIVATION_COERCIBLE); // size of hex representation of hash fix_length_and_charset(SHA1_HASH_SIZE * 2, default_charset()); } @@ -369,18 +374,9 @@ void Item_func_sha2::fix_length_and_dec() ER(ER_WRONG_PARAMETERS_TO_NATIVE_FCT), "sha2"); } - /* - The SHA2() function treats its parameter as being a case sensitive. - Thus we set binary collation on it so different instances of SHA2() - will be compared properly. - */ + CHARSET_INFO *cs= get_checksum_charset(args[0]->collation.collation->csname); + args[0]->collation.set(cs, DERIVATION_COERCIBLE); - args[0]->collation.set( - get_charset_by_csname( - args[0]->collation.collation->csname, - MY_CS_BINSORT, - MYF(0)), - DERIVATION_COERCIBLE); #else push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, -- cgit v1.2.1 From a4aa1f852d9c9ab5d6ceb8bc33c2de857483ffac Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Wed, 11 Jun 2014 16:23:20 +0400 Subject: MDEV-6329 - Buffer overrun in find_uniq_filename Merged WebScaleSQL fix: https://github.com/webscalesql/webscalesql-5.6/commit/bce9eddc1da87beab17c60d7eb7379841a3d45af --- sql/log.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/log.cc b/sql/log.cc index d129fcbe8a9..6d4d52b3f2e 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2244,7 +2244,7 @@ static int find_uniq_filename(char *name) file_info= dir_info->dir_entry; for (i= dir_info->number_off_files ; i-- ; file_info++) { - if (memcmp(file_info->name, start, length) == 0 && + if (strncmp(file_info->name, start, length) == 0 && test_if_number(file_info->name+length, &number,0)) { set_if_bigger(max_found,(ulong) number); -- cgit v1.2.1 From 14544cefc9c52dbba3cc795490f46c193ef72749 Mon Sep 17 00:00:00 2001 From: Sujatha Sivakumar Date: Mon, 16 Jun 2014 10:06:44 +0530 Subject: Bug#18432495:RBR REPLICATION SLAVE CRASHES WHEN DELETE NON-EXISTS RECORDS Problem: ======== In RBR replication, master deletes a record but the record don't exist on slave. when slave tries to apply the Delete_row_log_event from master, it will result in an assert on slave. Analysis: ======== This problem exists not only with Delete_rows event but also with Update_rows event as well. Trying to update a non existing row on the slave from the master will cause the same assert. This assert occurs only for the tables that doesn't have primary keys and which basically require sequential scan to be done to locate a record. This bug occurs only with innodb engine not with myisam. When update or delete rows is executed on a slave on a table which doesn't have primary key the updated record is stored in a buffer named table->record[0] and the same is copied to table->record[1] so that during sequential scan table->record[0] can reloaded with fetched data from the table and compared against table->record[1]. In a special case where there is no record on the slave side scan will result in EOF in that case we reinit the scan and we try to compare record[0] with record[1] which are basically the same. This comparison is incorrect. Since they both are the same record_compare() will report that record is found and we try to go ahead and try to update/delete non existing row. Ideally if the scan results in EOF means no data found hence no need to do a record_compare() at all. Fix: === Avoid comparision of records on EOF. sql/log_event.cc: Avoid record comparison on end of file. sql/log_event_old.cc: Avoid record comparison on end of file. --- sql/log_event.cc | 1 + sql/log_event_old.cc | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/log_event.cc b/sql/log_event.cc index 40e626f1083..4f55d08933e 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -9962,6 +9962,7 @@ int Rows_log_event::find_row(const Relay_log_info *rli) table->file->print_error(error, MYF(0)); goto err; } + goto restart_rnd_next; } break; diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index 1462b08e993..ed53aec6006 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -2479,6 +2479,7 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli) table->file->print_error(error, MYF(0)); DBUG_RETURN(error); } + goto restart_rnd_next; } break; -- cgit v1.2.1 From 081926f3d8bc0b2ebaac87568a5e6ab6294ffbc0 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 17 Jun 2014 14:10:13 +0200 Subject: MDEV-6188: master_retry_count (ignored if disconnect happens on SET master_heartbeat_period) That particular part of slave connect to master was missing code to handle retry in case of network errors. The same problem is present in MySQL 5.5, but fixed in MySQL 5.6. Fixed with this patch, by adding the code (mostly identical to MySQL 5.6), and also adding a test case. I checked other queries done towards master during slave connect, and they now all seem to handle reconnect in case of network failures. --- sql/slave.cc | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) (limited to 'sql') diff --git a/sql/slave.cc b/sql/slave.cc index 702b6d38cbe..78f152fd2fd 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1630,15 +1630,35 @@ when it try to get the value of TIME_ZONE global variable from master."; llstr((ulonglong) (mi->heartbeat_period*1000000000UL), llbuf); sprintf(query, query_format, llbuf); - if (mysql_real_query(mysql, query, strlen(query)) - && !check_io_slave_killed(mi->io_thd, mi, NULL)) + DBUG_EXECUTE_IF("simulate_slave_heartbeat_network_error", + { static ulong dbug_count= 0; + if (++dbug_count < 3) + goto heartbeat_network_error; + }); + if (mysql_real_query(mysql, query, strlen(query))) { - errmsg= "The slave I/O thread stops because SET @master_heartbeat_period " - "on master failed."; - err_code= ER_SLAVE_FATAL_ERROR; - sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql)); - mysql_free_result(mysql_store_result(mysql)); - goto err; + if (check_io_slave_killed(mi->io_thd, mi, NULL)) + goto slave_killed_err; + + if (is_network_error(mysql_errno(mysql))) + { + IF_DBUG(heartbeat_network_error: , ) + mi->report(WARNING_LEVEL, mysql_errno(mysql), + "SET @master_heartbeat_period to master failed with error: %s", + mysql_error(mysql)); + mysql_free_result(mysql_store_result(mysql)); + goto network_err; + } + else + { + /* Fatal error */ + errmsg= "The slave I/O thread stops because a fatal error is encountered " + "when it tries to SET @master_heartbeat_period on master."; + err_code= ER_SLAVE_FATAL_ERROR; + sprintf(err_buff, "%s Error: %s", errmsg, mysql_error(mysql)); + mysql_free_result(mysql_store_result(mysql)); + goto err; + } } mysql_free_result(mysql_store_result(mysql)); } -- cgit v1.2.1 From 07c0b1d8d075ee5c95ceca98c88d914fff10a8ee Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 22 Jul 2014 15:52:49 +0400 Subject: MDEV-6434: Wrong result (extra rows) with ORDER BY, multiple-column index, InnoDB - Filesort has an optmization where it reads only columns that are needed before the sorting is done. - When ref(_or_null) is picked by the join optimizer, it may remove parts of WHERE clause that are guaranteed to be true. - However, if we use quick select, we must put all of the range columns into the read set. Not doing so will may cause us to fail to detect the end of the range. --- sql/filesort.cc | 3 +++ sql/opt_range.cc | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- sql/opt_range.h | 15 +++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/filesort.cc b/sql/filesort.cc index 34cd11cbcf3..d6cb22ebe2c 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -642,6 +642,9 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, /* Temporary set for register_used_fields and register_field_in_read_map */ sort_form->read_set= &sort_form->tmp_set; register_used_fields(param); + if (quick_select) + select->quick->add_used_key_part_to_set(sort_form->read_set); + Item *sort_cond= !select ? 0 : !select->pre_idx_push_select_cond ? select->cond : select->pre_idx_push_select_cond; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 5db080394eb..80807c01fd0 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -10538,6 +10538,7 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, if (quick->init()) goto err; quick->records= records; + quick->max_used_key_length= ref->key_parts; if ((cp_buffer_from_ref(thd, table, ref) && thd->is_fatal_error) || !(range= new(alloc) QUICK_RANGE())) @@ -10547,7 +10548,7 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, range->min_length= range->max_length= ref->key_length; range->min_keypart_map= range->max_keypart_map= make_prev_keypart_map(ref->key_parts); - range->flag= (ref->key_length == key_info->key_length ? EQ_RANGE : 0); + range->flag= EQ_RANGE; if (!(quick->key_parts=key_part=(KEY_PART *) alloc_root(&quick->alloc,sizeof(KEY_PART)*ref->key_parts))) @@ -11756,6 +11757,60 @@ void QUICK_ROR_UNION_SELECT::add_keys_and_lengths(String *key_names, } +void QUICK_RANGE_SELECT::add_used_key_part_to_set(MY_BITMAP *col_set) +{ + for (uint i=0; i < max_used_key_length; i++) + { + bitmap_set_bit(col_set, key_parts[i].field->field_index); + } +} + + +void QUICK_GROUP_MIN_MAX_SELECT::add_used_key_part_to_set(MY_BITMAP *col_set) +{ + for (uint i=0; i < max_used_key_length; i++) + { + bitmap_set_bit(col_set, index_info->key_part[i].field->field_index); + } +} + + +void QUICK_ROR_INTERSECT_SELECT::add_used_key_part_to_set(MY_BITMAP *col_set) +{ + List_iterator_fast it(quick_selects); + QUICK_SELECT_WITH_RECORD *quick; + while ((quick= it++)) + { + quick->quick->add_used_key_part_to_set(col_set); + } +} + + +void QUICK_INDEX_SORT_SELECT::add_used_key_part_to_set(MY_BITMAP *col_set) +{ + QUICK_RANGE_SELECT *quick; + List_iterator_fast it(quick_selects); + while ((quick= it++)) + { + quick->add_used_key_part_to_set(col_set); + } + if (pk_quick_select) + pk_quick_select->add_used_key_part_to_set(col_set); +} + + +void QUICK_ROR_UNION_SELECT::add_used_key_part_to_set(MY_BITMAP *col_set) +{ + QUICK_SELECT_I *quick; + List_iterator_fast it(quick_selects); + + while ((quick= it++)) + { + quick->add_used_key_part_to_set(col_set); + } +} + + /******************************************************************************* * Implementation of QUICK_GROUP_MIN_MAX_SELECT *******************************************************************************/ diff --git a/sql/opt_range.h b/sql/opt_range.h index fff6e414ad9..b8b46ae5ab1 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -394,6 +394,13 @@ public: Returns a QUICK_SELECT with reverse order of to the index. */ virtual QUICK_SELECT_I *make_reverse(uint used_key_parts_arg) { return NULL; } + + /* + Add the key columns used by the quick select into table's read set. + + This is used by an optimization in filesort. + */ + virtual void add_used_key_part_to_set(MY_BITMAP *col_set)=0; }; @@ -484,6 +491,9 @@ public: #endif virtual void replace_handler(handler *new_file) { file= new_file; } QUICK_SELECT_I *make_reverse(uint used_key_parts_arg); + + virtual void add_used_key_part_to_set(MY_BITMAP *col_set); + private: /* Default copy ctor used by QUICK_SELECT_DESC */ friend class TRP_ROR_INTERSECT; @@ -644,6 +654,8 @@ public: virtual int read_keys_and_merge()= 0; /* used to get rows collected in Unique */ READ_RECORD read_record; + + virtual void add_used_key_part_to_set(MY_BITMAP *col_set); }; @@ -719,6 +731,7 @@ public: void add_keys_and_lengths(String *key_names, String *used_lengths); void add_info_string(String *str); bool is_keys_used(const MY_BITMAP *fields); + void add_used_key_part_to_set(MY_BITMAP *col_set); #ifndef DBUG_OFF void dbug_dump(int indent, bool verbose); #endif @@ -798,6 +811,7 @@ public: void add_keys_and_lengths(String *key_names, String *used_lengths); void add_info_string(String *str); bool is_keys_used(const MY_BITMAP *fields); + void add_used_key_part_to_set(MY_BITMAP *col_set); #ifndef DBUG_OFF void dbug_dump(int indent, bool verbose); #endif @@ -940,6 +954,7 @@ public: bool unique_key_range() { return false; } int get_type() { return QS_TYPE_GROUP_MIN_MAX; } void add_keys_and_lengths(String *key_names, String *used_lengths); + void add_used_key_part_to_set(MY_BITMAP *col_set); #ifndef DBUG_OFF void dbug_dump(int indent, bool verbose); #endif -- cgit v1.2.1 From c714073bfdc2a1e9297823bbfe680c98df6548bd Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 22 Jul 2014 19:32:58 +0400 Subject: MDEV-6434: Wrong result (extra rows) with ORDER BY, multiple-column index, InnoDB - Part #2. Fix obvious problems in the previous patch. --- sql/opt_range.cc | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'sql') diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 80807c01fd0..83fbd83207a 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -10521,6 +10521,7 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, uint part; bool create_err= FALSE; COST_VECT cost; + uint max_used_key_len; old_root= thd->mem_root; /* The following call may change thd->mem_root */ @@ -10538,7 +10539,6 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, if (quick->init()) goto err; quick->records= records; - quick->max_used_key_length= ref->key_parts; if ((cp_buffer_from_ref(thd, table, ref) && thd->is_fatal_error) || !(range= new(alloc) QUICK_RANGE())) @@ -10553,7 +10553,8 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, if (!(quick->key_parts=key_part=(KEY_PART *) alloc_root(&quick->alloc,sizeof(KEY_PART)*ref->key_parts))) goto err; - + + max_used_key_len=0; for (part=0 ; part < ref->key_parts ;part++,key_part++) { key_part->part=part; @@ -10562,7 +10563,12 @@ QUICK_RANGE_SELECT *get_quick_select_for_ref(THD *thd, TABLE *table, key_part->store_length= key_info->key_part[part].store_length; key_part->null_bit= key_info->key_part[part].null_bit; key_part->flag= (uint8) key_info->key_part[part].key_part_flag; + + max_used_key_len +=key_info->key_part[part].store_length; } + + quick->max_used_key_length= max_used_key_len; + if (insert_dynamic(&quick->ranges,(uchar*)&range)) goto err; @@ -11759,18 +11765,24 @@ void QUICK_ROR_UNION_SELECT::add_keys_and_lengths(String *key_names, void QUICK_RANGE_SELECT::add_used_key_part_to_set(MY_BITMAP *col_set) { - for (uint i=0; i < max_used_key_length; i++) + uint key_len; + KEY_PART *part= key_parts; + for (key_len=0; key_len < max_used_key_length; + key_len += (part++)->store_length) { - bitmap_set_bit(col_set, key_parts[i].field->field_index); + bitmap_set_bit(col_set, part->field->field_index); } } void QUICK_GROUP_MIN_MAX_SELECT::add_used_key_part_to_set(MY_BITMAP *col_set) { - for (uint i=0; i < max_used_key_length; i++) + uint key_len; + KEY_PART_INFO *part= index_info->key_part; + for (key_len=0; key_len < max_used_key_length; + key_len += (part++)->store_length) { - bitmap_set_bit(col_set, index_info->key_part[i].field->field_index); + bitmap_set_bit(col_set, part->field->field_index); } } -- cgit v1.2.1 From 3375e137f88b9cfe3cdd10dd968e3f87d169d373 Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Tue, 17 Jun 2014 13:03:26 +0400 Subject: MDEV-6351 - --plugin=force has no effect for built-in plugins mysqld didn't fail to start if a compiled-in plugin failed to initialize (--xxx=FORCE behaving as --xxx=ON) --- sql/sql_plugin.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index fc856f11a89..e1784c1f027 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1612,7 +1612,7 @@ int plugin_init(int *argc, char **argv, int flags) if (plugin_initialize(&tmp_root, plugin_ptr, argc, argv, !is_myisam && (flags & PLUGIN_INIT_SKIP_INITIALIZATION))) { - if (mandatory) + if (plugin_ptr->load_option == PLUGIN_FORCE) goto err_unlock; plugin_ptr->state= PLUGIN_IS_DISABLED; } -- cgit v1.2.1 From 879fec69fced11ca9bdd9379c757396c374b45bd Mon Sep 17 00:00:00 2001 From: Jon Olav Hauglid Date: Thu, 19 Jun 2014 16:47:41 +0200 Subject: WL#7436: Deprecate and remove timed_mutexes system variable This is the 5.5/5.6 version of the patch. Add deprecation warning for timed_mutexes. --- sql/sys_vars.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'sql') diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 63f478be5f0..f8b2c022ba5 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -2241,9 +2241,10 @@ static Sys_var_ulonglong Sys_tmp_table_size( static Sys_var_mybool Sys_timed_mutexes( "timed_mutexes", - "Specify whether to time mutexes (only InnoDB mutexes are currently " - "supported)", - GLOBAL_VAR(timed_mutexes), CMD_LINE(OPT_ARG), DEFAULT(0)); + "Specify whether to time mutexes. Deprecated, has no effect.", + GLOBAL_VAR(timed_mutexes), CMD_LINE(OPT_ARG), DEFAULT(0), + NO_MUTEX_GUARD, NOT_IN_BINLOG, ON_CHECK(NULL), ON_UPDATE(NULL), + DEPRECATED("")); static char *server_version_ptr; static Sys_var_charptr Sys_version( -- cgit v1.2.1 From 01fd5d0d0e527ed5da5d6d2dcf91f74162bb4ad1 Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Mon, 23 Jun 2014 19:59:15 +0400 Subject: Bug #18978946: BACKPORT TO 5.6: BUGFIX FOR 18017820 "BISON 3 BREAKS MYSQL BUILD" Backport of the fix: : Bug 18017820: BISON 3 BREAKS MYSQL BUILD : ======================================== : : The source of the reported problem is a removal of a few deprecated : things from Bison 3.x: : * YYPARSE_PARAM macro (use the %parse-param bison directive instead), : * YYLEX_PARAM macro (use %lex-param instead), : : The fix removes obsolete macro calls and introduces use of : %parse-param and %lex-param directives. --- sql/sql_lex.cc | 19 +++++++++---------- sql/sql_lex.h | 5 ++--- sql/sql_parse.cc | 4 ++-- sql/sql_yacc.yy | 18 +++++++----------- 4 files changed, 20 insertions(+), 26 deletions(-) (limited to 'sql') diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 9113f31c76c..17446778034 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ #include "sp.h" #include "sp_head.h" -static int lex_one_token(void *arg, void *yythd); +static int lex_one_token(YYSTYPE *yylval, THD *thd); /* We are using pointer to this variable for distinguishing between assignment @@ -864,16 +864,17 @@ bool consume_comment(Lex_input_stream *lip, int remaining_recursions_permitted) /* MYSQLlex remember the following states from the following MYSQLlex() + @param yylval [out] semantic value of the token being parsed (yylval) + @param thd THD + - MY_LEX_EOQ Found end of query - MY_LEX_OPERATOR_OR_IDENT Last state was an ident, text or number (which can't be followed by a signed number) */ -int MYSQLlex(void *arg, void *yythd) +int MYSQLlex(YYSTYPE *yylval, THD *thd) { - THD *thd= (THD *)yythd; Lex_input_stream *lip= & thd->m_parser_state->m_lip; - YYSTYPE *yylval=(YYSTYPE*) arg; int token; if (lip->lookahead_token >= 0) @@ -889,7 +890,7 @@ int MYSQLlex(void *arg, void *yythd) return token; } - token= lex_one_token(arg, yythd); + token= lex_one_token(yylval, thd); switch(token) { case WITH: @@ -900,7 +901,7 @@ int MYSQLlex(void *arg, void *yythd) to transform the grammar into a LALR(1) grammar, which sql_yacc.yy can process. */ - token= lex_one_token(arg, yythd); + token= lex_one_token(yylval, thd); switch(token) { case CUBE_SYM: return WITH_CUBE_SYM; @@ -923,17 +924,15 @@ int MYSQLlex(void *arg, void *yythd) return token; } -int lex_one_token(void *arg, void *yythd) +static int lex_one_token(YYSTYPE *yylval, THD *thd) { reg1 uchar c= 0; bool comment_closed; int tokval, result_state; uint length; enum my_lex_states state; - THD *thd= (THD *)yythd; Lex_input_stream *lip= & thd->m_parser_state->m_lip; LEX *lex= thd->lex; - YYSTYPE *yylval=(YYSTYPE*) arg; CHARSET_INFO *cs= thd->charset(); uchar *state_map= cs->state_map; uchar *ident_map= cs->ident_map; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 40fea173a7a..c5886b3c142 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1,5 +1,4 @@ -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. reserved. - reserved. +/* Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -2794,7 +2793,7 @@ extern void lex_init(void); extern void lex_free(void); extern void lex_start(THD *thd); extern void lex_end(LEX *lex); -extern int MYSQLlex(void *arg, void *yythd); +extern int MYSQLlex(union YYSTYPE *yylval, class THD *thd); extern void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index fe9f564672a..ea63d23d182 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -7359,7 +7359,7 @@ bool check_host_name(LEX_STRING *str) } -extern int MYSQLparse(void *thd); // from sql_yacc.cc +extern int MYSQLparse(class THD *thd); // from sql_yacc.cc /** diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 3af964db731..bd5d4dc5604 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -22,13 +22,9 @@ */ %{ -/* thd is passed as an argument to yyparse(), and subsequently to yylex(). -** The type will be void*, so it must be cast to (THD*) when used. -** Use the YYTHD macro for this. +/* +Note: YYTHD is passed as an argument to yyparse(), and subsequently to yylex(). */ -#define YYPARSE_PARAM yythd -#define YYLEX_PARAM yythd -#define YYTHD ((THD *)yythd) #define YYLIP (& YYTHD->m_parser_state->m_lip) #define YYPS (& YYTHD->m_parser_state->m_yacc) @@ -76,7 +72,7 @@ int yylex(void *yylval, void *yythd); ulong val= *(F); \ if (my_yyoverflow((B), (D), &val)) \ { \ - yyerror((char*) (A)); \ + yyerror(YYTHD, (char*) (A)); \ return 2; \ } \ else \ @@ -174,10 +170,8 @@ void my_parse_error(const char *s) to abort from the parser. */ -void MYSQLerror(const char *s) +void MYSQLerror(THD *thd, const char *s) { - THD *thd= current_thd; - /* Restore the original LEX if it was replaced when parsing a stored procedure. We must ensure that a parsing error @@ -787,7 +781,9 @@ static bool add_create_index (LEX *lex, Key::Keytype type, bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %} -%pure_parser /* We have threads */ +%parse-param { class THD *YYTHD } +%lex-param { class THD *YYTHD } +%pure-parser /* We have threads */ /* Currently there are 168 shift/reduce conflicts. We should not introduce new conflicts any more. -- cgit v1.2.1 From 24756e8e3fc4461d2818ebfdd5929e171c625a0a Mon Sep 17 00:00:00 2001 From: Nisha Gopalakrishnan Date: Tue, 24 Jun 2014 10:15:53 +0530 Subject: BUG#18618561: FAILED ALTER TABLE ENGINE CHANGE WITH PARTITIONS CORRUPTS FRM Analysis: --------- ALTER TABLE on a partitioned table resulted in the wrong engine being written into the table's FRM file and displayed in SHOW CREATE TABLE. The prep_alter_part_table() modifies the partition_info object for TABLE instance representing the old version of table. If the ALTER TABLE ENGINE statement fails, the partition_info object for the TABLE contains the altered storage engine name. The SHOW CREATE TABLE uses the TABLE object to display the table information, hence displays incorrect storage engine for the table. Also a subsequent successful ALTER TABLE operation will write the incorrect engine information into the FRM file. Fix: --- A copy of the partition_info object is created before modification so that any changes would not cause the the original partition_info object to be modified if the ALTER TABLE fails.(Backported part of the code provided as fix for bug#14156617 in mysql-5.6.6). --- sql/sql_partition.cc | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'sql') diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index d7d362dbdfb..c615ee96d03 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -5573,7 +5573,9 @@ the generated partition syntax in a correct manner. There was no partitioning before and no partitioning defined. Obviously no work needed. */ - if (table->part_info) + partition_info *tab_part_info= table->part_info; + + if (tab_part_info) { if (alter_info->flags & ALTER_REMOVE_PARTITIONING) { @@ -5581,7 +5583,7 @@ the generated partition syntax in a correct manner. if (!(create_info->used_fields & HA_CREATE_USED_ENGINE)) { DBUG_PRINT("info", ("No explicit engine used")); - create_info->db_type= table->part_info->default_engine_type; + create_info->db_type= tab_part_info->default_engine_type; } DBUG_PRINT("info", ("New engine type: %s", ha_resolve_storage_engine_name(create_info->db_type))); @@ -5593,16 +5595,20 @@ the generated partition syntax in a correct manner. /* Retain partitioning but possibly with a new storage engine beneath. + + Create a copy of TABLE::part_info to be able to modify it freely. */ - thd->work_part_info= table->part_info; + if (!(tab_part_info= tab_part_info->get_clone())) + DBUG_RETURN(TRUE); + thd->work_part_info= tab_part_info; if (create_info->used_fields & HA_CREATE_USED_ENGINE && - create_info->db_type != table->part_info->default_engine_type) + create_info->db_type != tab_part_info->default_engine_type) { /* Make sure change of engine happens to all partitions. */ DBUG_PRINT("info", ("partition changed")); - if (table->part_info->is_auto_partitioned) + if (tab_part_info->is_auto_partitioned) { /* If the user originally didn't specify partitioning to be @@ -5630,7 +5636,7 @@ the generated partition syntax in a correct manner. Need to cater for engine types that can handle partition without using the partition handler. */ - if (part_info != table->part_info) + if (part_info != tab_part_info) { if (part_info->fix_parser_data(thd)) { @@ -5658,8 +5664,8 @@ the generated partition syntax in a correct manner. part_info->default_engine_type= create_info->db_type; else { - if (table->part_info) - part_info->default_engine_type= table->part_info->default_engine_type; + if (tab_part_info) + part_info->default_engine_type= tab_part_info->default_engine_type; else part_info->default_engine_type= create_info->db_type; } -- cgit v1.2.1 From ec4f1d197d01382a80306dc629548171e31100b9 Mon Sep 17 00:00:00 2001 From: Sergey Vojtovich Date: Tue, 24 Jun 2014 11:41:35 +0400 Subject: MDEV-6313 - mysqld --log-bin=no-such-dir/master crashes during server initialization ER() macro was used during server initialization. It refers to current_thd, which is not available that early. Print error to error log in "lc-messages" locale. Avoid duplicate error message during server initialization. --- sql/log.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/log.cc b/sql/log.cc index 6d4d52b3f2e..e860d030be3 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -2523,9 +2523,10 @@ int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name) { if (find_uniq_filename(new_name)) { - my_printf_error(ER_NO_UNIQUE_LOGFILE, ER(ER_NO_UNIQUE_LOGFILE), - MYF(ME_FATALERROR), log_name); - sql_print_error(ER(ER_NO_UNIQUE_LOGFILE), log_name); + if (current_thd) + my_printf_error(ER_NO_UNIQUE_LOGFILE, ER(ER_NO_UNIQUE_LOGFILE), + MYF(ME_FATALERROR), log_name); + sql_print_error(ER_DEFAULT(ER_NO_UNIQUE_LOGFILE), log_name); return 1; } } -- cgit v1.2.1 From 119984db0c1e406c48fda48680c2d97c0d4cd177 Mon Sep 17 00:00:00 2001 From: Gopal Shankar Date: Wed, 25 Jun 2014 09:50:17 +0530 Subject: Bug#18776592 INNODB: FAILING ASSERTION: PRIMARY_KEY_NO == -1 || PRIMARY_KEY_NO == 0 This bug is a backport of the following revision of 5.6 source tree: # committer: Gopal Shankar # branch nick: priKey56 # timestamp: Wed 2013-05-29 11:11:46 +0530 # message: # Bug#16368875 INNODB: FAILING ASSERTION: --- sql/sql_table.cc | 68 +++++++++++++++++++++++++++++++------------------------- sql/table.cc | 24 +++++++++++++++----- 2 files changed, 56 insertions(+), 36 deletions(-) (limited to 'sql') diff --git a/sql/sql_table.cc b/sql/sql_table.cc index bfed754a90c..1664f94515a 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3383,7 +3383,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, CHARSET_INFO *ft_key_charset=0; // for FULLTEXT for (uint column_nr=0 ; (column=cols++) ; column_nr++) { - uint length; Key_part_spec *dup_column; it.rewind(); @@ -3515,30 +3514,40 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, key_part_info->fieldnr= field; key_part_info->offset= (uint16) sql_field->offset; key_part_info->key_type=sql_field->pack_flag; - length= sql_field->key_length; + uint key_part_length= sql_field->key_length; if (column->length) { if (f_is_blob(sql_field->pack_flag)) { - if ((length=column->length) > max_key_length || - length > file->max_key_part_length()) + key_part_length= column->length; + /* + There is a possibility that the given prefix length is less + than the engine max key part length, but still greater + than the BLOB field max size. We handle this case + using the max_field_size variable below. + */ + uint max_field_size= sql_field->key_length * sql_field->charset->mbmaxlen; + if ((max_field_size && key_part_length > max_field_size) || + key_part_length > max_key_length || + key_part_length > file->max_key_part_length()) { - length=min(max_key_length, file->max_key_part_length()); + // Given prefix length is too large, adjust it. + key_part_length= min(max_key_length, file->max_key_part_length()); + if (max_field_size) + key_part_length= min(key_part_length, max_field_size); if (key->type == Key::MULTIPLE) { /* not a critical problem */ - char warn_buff[MYSQL_ERRMSG_SIZE]; - my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY), - length); - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_TOO_LONG_KEY, warn_buff); + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TOO_LONG_KEY, ER(ER_TOO_LONG_KEY), + key_part_length); /* Align key length to multibyte char boundary */ - length-= length % sql_field->charset->mbmaxlen; + key_part_length-= key_part_length % sql_field->charset->mbmaxlen; } else { - my_error(ER_TOO_LONG_KEY,MYF(0),length); + my_error(ER_TOO_LONG_KEY, MYF(0), key_part_length); DBUG_RETURN(TRUE); } } @@ -3546,9 +3555,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, // Catch invalid use of partial keys else if (!f_is_geom(sql_field->pack_flag) && // is the key partial? - column->length != length && + column->length != key_part_length && // is prefix length bigger than field length? - (column->length > length || + (column->length > key_part_length || // can the field have a partial key? !Field::type_can_have_key_part (sql_field->sql_type) || // a packed field can't be used in a partial key @@ -3557,43 +3566,42 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, ((file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS) && // and is this a 'unique' key? (key_info->flags & HA_NOSAME)))) - { + { my_message(ER_WRONG_SUB_KEY, ER(ER_WRONG_SUB_KEY), MYF(0)); DBUG_RETURN(TRUE); } else if (!(file->ha_table_flags() & HA_NO_PREFIX_CHAR_KEYS)) - length=column->length; + key_part_length= column->length; } - else if (length == 0) + else if (key_part_length == 0) { my_error(ER_WRONG_KEY_COLUMN, MYF(0), column->field_name.str); DBUG_RETURN(TRUE); } - if (length > file->max_key_part_length() && key->type != Key::FULLTEXT) + if (key_part_length > file->max_key_part_length() && + key->type != Key::FULLTEXT) { - length= file->max_key_part_length(); + key_part_length= file->max_key_part_length(); if (key->type == Key::MULTIPLE) { /* not a critical problem */ - char warn_buff[MYSQL_ERRMSG_SIZE]; - my_snprintf(warn_buff, sizeof(warn_buff), ER(ER_TOO_LONG_KEY), - length); - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_TOO_LONG_KEY, warn_buff); + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_TOO_LONG_KEY, ER(ER_TOO_LONG_KEY), + key_part_length); /* Align key length to multibyte char boundary */ - length-= length % sql_field->charset->mbmaxlen; + key_part_length-= key_part_length % sql_field->charset->mbmaxlen; } else { - my_error(ER_TOO_LONG_KEY,MYF(0),length); + my_error(ER_TOO_LONG_KEY, MYF(0), key_part_length); DBUG_RETURN(TRUE); } } - key_part_info->length=(uint16) length; + key_part_info->length= (uint16) key_part_length; /* Use packed keys for long strings on the first column */ if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) && !((create_info->table_options & HA_OPTION_NO_PACK_KEYS)) && - (length >= KEY_DEFAULT_PACK_LENGTH && + (key_part_length >= KEY_DEFAULT_PACK_LENGTH && (sql_field->sql_type == MYSQL_TYPE_STRING || sql_field->sql_type == MYSQL_TYPE_VARCHAR || sql_field->pack_flag & FIELDFLAG_BLOB))) @@ -3605,10 +3613,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, key_info->flags|= HA_PACK_KEY; } /* Check if the key segment is partial, set the key flag accordingly */ - if (length != sql_field->key_length) + if (key_part_length != sql_field->key_length) key_info->flags|= HA_KEY_HAS_PART_KEY_SEG; - key_length+=length; + key_length+= key_part_length; key_part_info++; /* Create the key name based on the first column (if not given) */ diff --git a/sql/table.cc b/sql/table.cc index 67f7922a2e1..ba1839bb6ec 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1525,13 +1525,25 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, primary_key=key; for (i=0 ; i < keyinfo->key_parts ;i++) { - uint fieldnr= key_part[i].fieldnr; - if (!fieldnr || - share->field[fieldnr-1]->null_ptr || - share->field[fieldnr-1]->key_length() != - key_part[i].length) + DBUG_ASSERT(key_part[i].fieldnr > 0); + // Table field corresponding to the i'th key part. + Field *table_field= share->field[key_part[i].fieldnr - 1]; + + /* + If the key column is of NOT NULL BLOB type, then it + will definitly have key prefix. And if key part prefix size + is equal to the BLOB column max size, then we can promote + it to primary key. + */ + if (!table_field->real_maybe_null() && + table_field->type() == MYSQL_TYPE_BLOB && + table_field->field_length == key_part[i].length) + continue; + + if (table_field->real_maybe_null() || + table_field->key_length() != key_part[i].length) { - primary_key=MAX_KEY; // Can't be used + primary_key= MAX_KEY; // Can't be used break; } } -- cgit v1.2.1 From d63645c890550cc190416e2c90585c1c6903b529 Mon Sep 17 00:00:00 2001 From: Nisha Gopalakrishnan Date: Wed, 25 Jun 2014 16:33:04 +0530 Subject: BUG#18405221: SHOW CREATE VIEW OUTPUT INCORRECT Fix: --- The issue reported is same as the BUG#14117018. Hence backporting the patch from mysql-trunk to mysql-5.5 and mysql-5.6 --- sql/sql_view.cc | 60 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 14 deletions(-) (limited to 'sql') diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 4a10c397508..6bbc475565b 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2004, 2014, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -163,27 +163,61 @@ err: Check if auto generated column names are conforming and possibly generate a conforming name for them if not. - @param item_list List of Items which should be checked + @param lex Lex for this thread. + + @retval false Operation was a success. + @retval true An error occurred. */ -static void make_valid_column_names(List &item_list) +static bool make_valid_column_names(LEX *lex) { Item *item; uint name_len; - List_iterator_fast it(item_list); char buff[NAME_LEN]; + uint column_no= 1; DBUG_ENTER("make_valid_column_names"); - for (uint column_no= 1; (item= it++); column_no++) + for (SELECT_LEX *sl= &lex->select_lex; sl; sl= sl->next_select()) { - if (!item->is_autogenerated_name || !check_column_name(item->name)) - continue; - name_len= my_snprintf(buff, NAME_LEN, "Name_exp_%u", column_no); - item->orig_name= item->name; - item->set_name(buff, name_len, system_charset_info); + for (List_iterator_fast it(sl->item_list); (item= it++); column_no++) + { + if (!item->is_autogenerated_name || !check_column_name(item->name)) + continue; + name_len= my_snprintf(buff, NAME_LEN, "Name_exp_%u", column_no); + item->orig_name= item->name; + item->set_name(buff, name_len, system_charset_info); + } + + /* + There is a possibility of generating same name for column in more than + one SELECT_LEX. For Example: + + CREATE TABLE t1 (Name_exp_1 INT, Name_exp_2 INT, Name_exp_3 INT); + CREATE TABLE t2 (Name_exp_1 INT, Name_exp_2 INT, Name_exp_3 INT); + + CREATE VIEW v1 AS SELECT '', t1.Name_exp_2 AS Name_exp_2 FROM t1 + UNION + SELECT '', t2.Name_exp_1 AS Name_exp_1 from t2; + + But, column names of the first SELECT_LEX is considered + for the output. + + mysql> SELECT * FROM v1; + +------------+------------+ + | Name_exp_1 | Name_exp_2 | + +------------+------------+ + | | 2 | + | | 3 | + +------------+------------+ + + So, checking for duplicate names in only "sl", current + SELECT_LEX. + */ + if (check_duplicate_names(sl->item_list, 1)) + DBUG_RETURN(true); } - DBUG_VOID_RETURN; + DBUG_RETURN(false); } @@ -590,9 +624,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, } /* Check if the auto generated column names are conforming. */ - make_valid_column_names(select_lex->item_list); - - if (check_duplicate_names(select_lex->item_list, 1)) + if (make_valid_column_names(lex)) { res= TRUE; goto err; -- cgit v1.2.1 From cdf72d51c631aecb46505ff94cdb9ef667d4444c Mon Sep 17 00:00:00 2001 From: Raghav Kapoor Date: Wed, 25 Jun 2014 18:06:28 +0530 Subject: BUG#17665767 - FAILING ASSERTION: PRIMARY_KEY_NO == -1 || PRIMARY_KEY_NO == 0 BACKGROUND: This bug is a followup on Bug#16368875. The assertion failure happens because in SQL layer the key does not get promoted to PRIMARY KEY but InnoDB takes it as PRIMARY KEY. ANALYSIS: Here we are trying to create an index on POINT (GEOMETRY) data type which is a type of BLOB (since GEOMETRY is a subclass of BLOB). In general, we can't create an index over GEOMETRY family type field unless we specify the length of the keypart (similar to BLOB fields). Only exception is the POINT field type. The POINT column max size is 25. The problem is that the field is not treated as PRIMARY KEY when we create a index on POINT column using its max column size as key part prefix. The fix would allow index on POINT column to be treated as PRIMARY KEY. FIX: Patch for Bug#16368875 is extended to take into account GEOMETRY datatype, POINT in particular to consider it as PRIMARY KEY in SQL layer. --- sql/sql_table.cc | 2 +- sql/sql_table.h | 3 +++ sql/sql_yacc.yy | 3 ++- sql/table.cc | 11 +++++++++++ 4 files changed, 17 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 1664f94515a..8b4873cb834 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -3459,7 +3459,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } if (f_is_geom(sql_field->pack_flag) && sql_field->geom_type == Field::GEOM_POINT) - column->length= 25; + column->length= MAX_LEN_GEOM_POINT_FIELD; if (!column->length) { my_error(ER_BLOB_KEY_WITHOUT_LENGTH, MYF(0), column->field_name.str); diff --git a/sql/sql_table.h b/sql/sql_table.h index 6f09db12bb9..82f6dfb9658 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -105,6 +105,9 @@ enum enum_explain_filename_mode EXPLAIN_PARTITIONS_AS_COMMENT }; +/* Maximum length of GEOM_POINT Field */ +#define MAX_LEN_GEOM_POINT_FIELD 25 + /* depends on errmsg.txt Database `db`, Table `t` ... */ #define EXPLAIN_FILENAME_MAX_EXTRA_LENGTH 63 diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index bd5d4dc5604..74a21c9f6b6 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -5603,7 +5603,8 @@ spatial_type: | GEOMETRYCOLLECTION { $$= Field::GEOM_GEOMETRYCOLLECTION; } | POINT_SYM { - Lex->length= (char*)"25"; + Lex->length= const_cast(STRINGIFY_ARG + (MAX_LEN_GEOM_POINT_FIELD)); $$= Field::GEOM_POINT; } | MULTIPOINT { $$= Field::GEOM_MULTIPOINT; } diff --git a/sql/table.cc b/sql/table.cc index ba1839bb6ec..b24e03fbe1a 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1539,6 +1539,17 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, table_field->type() == MYSQL_TYPE_BLOB && table_field->field_length == key_part[i].length) continue; + /* + If the key column is of NOT NULL GEOMETRY type, specifically POINT + type whose length is known internally (which is 25). And key part + prefix size is equal to the POINT column max size, then we can + promote it to primary key. + */ + if (!table_field->real_maybe_null() && + table_field->type() == MYSQL_TYPE_GEOMETRY && + table_field->get_geometry_type() == Field::GEOM_POINT && + key_part[i].length == MAX_LEN_GEOM_POINT_FIELD) + continue; if (table_field->real_maybe_null() || table_field->key_length() != key_part[i].length) -- cgit v1.2.1 From 14aa44bb8f3deb4268dfb7c8d235422b133d4403 Mon Sep 17 00:00:00 2001 From: Praveenkumar Hulakund Date: Fri, 27 Jun 2014 17:04:08 +0530 Subject: Bug#18903155: BACKPORT BUG-18008907 TO 5.5+ VERSIONS. Backporting patch committed for bug 18008907 to 5.5 and 5.6. --- sql/sql_plugin.cc | 28 ++++++++++++++++++++++++---- sql/sql_plugin.h | 2 ++ sql/sql_show.cc | 16 ++++++++++++++-- 3 files changed, 40 insertions(+), 6 deletions(-) (limited to 'sql') diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 9851cb2739a..9ec0390483a 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -33,6 +33,7 @@ #include "sql_audit.h" #include #include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT +#include "debug_sync.h" #define REPORT_TO_LOG 1 #define REPORT_TO_USER 2 @@ -131,6 +132,12 @@ static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]= #include "sql_plugin_services.h" +/* + A mutex LOCK_plugin_delete must be acquired before calling plugin_del + function. +*/ +mysql_mutex_t LOCK_plugin_delete; + /* A mutex LOCK_plugin must be acquired before accessing the following variables/structures. @@ -949,6 +956,7 @@ static void plugin_del(struct st_plugin_int *plugin) { DBUG_ENTER("plugin_del(plugin)"); mysql_mutex_assert_owner(&LOCK_plugin); + mysql_mutex_assert_owner(&LOCK_plugin_delete); /* Free allocated strings before deleting the plugin. */ mysql_rwlock_wrlock(&LOCK_system_variables_hash); mysql_del_sys_var_chain(plugin->system_vars); @@ -996,11 +1004,14 @@ static void reap_plugins(void) while ((plugin= *(--list))) plugin_deinitialize(plugin, true); + mysql_mutex_lock(&LOCK_plugin_delete); mysql_mutex_lock(&LOCK_plugin); while ((plugin= *(--reap))) plugin_del(plugin); + mysql_mutex_unlock(&LOCK_plugin_delete); + my_afree(reap); } @@ -1205,10 +1216,12 @@ static inline void convert_underscore_to_dash(char *str, int len) #ifdef HAVE_PSI_INTERFACE static PSI_mutex_key key_LOCK_plugin; +static PSI_mutex_key key_LOCK_plugin_delete; static PSI_mutex_info all_plugin_mutexes[]= { - { &key_LOCK_plugin, "LOCK_plugin", PSI_FLAG_GLOBAL} + { &key_LOCK_plugin, "LOCK_plugin", PSI_FLAG_GLOBAL}, + { &key_LOCK_plugin_delete, "LOCK_plugin_delete", PSI_FLAG_GLOBAL} }; static void init_plugin_psi_keys(void) @@ -1259,6 +1272,7 @@ int plugin_init(int *argc, char **argv, int flags) mysql_mutex_init(key_LOCK_plugin, &LOCK_plugin, MY_MUTEX_INIT_FAST); + mysql_mutex_init(key_LOCK_plugin_delete, &LOCK_plugin_delete, MY_MUTEX_INIT_FAST); if (my_init_dynamic_array(&plugin_dl_array, sizeof(struct st_plugin_dl *),16,16) || @@ -1401,8 +1415,10 @@ int plugin_init(int *argc, char **argv, int flags) plugin_ptr->load_option == PLUGIN_FORCE_PLUS_PERMANENT) reaped_mandatory_plugin= TRUE; plugin_deinitialize(plugin_ptr, true); + mysql_mutex_lock(&LOCK_plugin_delete); mysql_mutex_lock(&LOCK_plugin); plugin_del(plugin_ptr); + mysql_mutex_unlock(&LOCK_plugin_delete); } mysql_mutex_unlock(&LOCK_plugin); @@ -1692,10 +1708,11 @@ void plugin_shutdown(void) } /* - It's perfectly safe not to lock LOCK_plugin, as there're no - concurrent threads anymore. But some functions called from here - use mysql_mutex_assert_owner(), so we lock the mutex to satisfy it + It's perfectly safe not to lock LOCK_plugin, LOCK_plugin_delete, as + there're no concurrent threads anymore. But some functions called from + here use mysql_mutex_assert_owner(), so we lock the mutex to satisfy it */ + mysql_mutex_lock(&LOCK_plugin_delete); mysql_mutex_lock(&LOCK_plugin); /* @@ -1718,9 +1735,11 @@ void plugin_shutdown(void) cleanup_variables(NULL, &global_system_variables); cleanup_variables(NULL, &max_system_variables); mysql_mutex_unlock(&LOCK_plugin); + mysql_mutex_unlock(&LOCK_plugin_delete); initialized= 0; mysql_mutex_destroy(&LOCK_plugin); + mysql_mutex_destroy(&LOCK_plugin_delete); my_afree(plugins); } @@ -1795,6 +1814,7 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, const LEX_STRING *dl mysql_audit_acquire_plugins(thd, MYSQL_AUDIT_GENERAL_CLASS); mysql_mutex_lock(&LOCK_plugin); + DEBUG_SYNC(thd, "acquired_LOCK_plugin"); mysql_rwlock_wrlock(&LOCK_system_variables_hash); if (my_load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv, NULL)) diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index da59e6ee58a..848b608b64e 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -40,6 +40,8 @@ extern const char *global_plugin_typelib_names[]; #include +extern mysql_mutex_t LOCK_plugin_delete; + #ifdef DBUG_OFF #define plugin_ref_to_int(A) A #define plugin_int_to_ref(A) A diff --git a/sql/sql_show.cc b/sql/sql_show.cc index f02ea6bead7..ccbf1f41126 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -6292,6 +6292,7 @@ int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond) int fill_variables(THD *thd, TABLE_LIST *tables, COND *cond) { DBUG_ENTER("fill_variables"); + SHOW_VAR *sys_var_array; int res= 0; LEX *lex= thd->lex; const char *wild= lex->wild ? lex->wild->ptr() : NullS; @@ -6305,10 +6306,21 @@ int fill_variables(THD *thd, TABLE_LIST *tables, COND *cond) schema_table_idx == SCH_GLOBAL_VARIABLES) option_type= OPT_GLOBAL; + /* + Lock LOCK_plugin_delete to avoid deletion of any plugins while creating + SHOW_VAR array and hold it until all variables are stored in the table. + */ + mysql_mutex_lock(&LOCK_plugin_delete); + // Lock LOCK_system_variables_hash to prepare SHOW_VARs array. mysql_rwlock_rdlock(&LOCK_system_variables_hash); - res= show_status_array(thd, wild, enumerate_sys_vars(thd, sorted_vars, option_type), - option_type, NULL, "", tables->table, upper_case_names, cond); + DEBUG_SYNC(thd, "acquired_LOCK_system_variables_hash"); + sys_var_array= enumerate_sys_vars(thd, sorted_vars, option_type); mysql_rwlock_unlock(&LOCK_system_variables_hash); + + res= show_status_array(thd, wild, sys_var_array, option_type, NULL, "", + tables->table, upper_case_names, cond); + + mysql_mutex_unlock(&LOCK_plugin_delete); DBUG_RETURN(res); } -- cgit v1.2.1 From 9406108356a4955728b3a0af4330ae769701f122 Mon Sep 17 00:00:00 2001 From: Venkata Sidagam Date: Mon, 30 Jun 2014 19:24:25 +0530 Subject: Bug #17357528 BACKPORT BUG#16513435 TO 5.5 AND 5.6 Description: Backporting BUG#16513435 to 5.5 and 5.6 This is a fix for REMOTE PREAUTH USER ENUMERATION FLAW bug --- sql/sql_acl.cc | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index ff2871f7f1d..2bb074da1f7 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -222,6 +222,7 @@ public: const char *ssl_cipher, *x509_issuer, *x509_subject; LEX_STRING plugin; LEX_STRING auth_string; + bool can_authenticate; ACL_USER *copy(MEM_ROOT *root) { @@ -811,6 +812,16 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) { ACL_USER user; bzero(&user, sizeof(user)); + + /* + All accounts can authenticate per default. This will change when + we add a new field to the user table. + + Currently this flag is only set to false when authentication is attempted + using an unknown user name. + */ + user.can_authenticate= true; + update_hostname(&user.host, get_field(&mem, table->field[0])); user.user= get_field(&mem, table->field[1]); if (check_no_resolve && hostname_requires_resolving(user.host.hostname)) @@ -1465,6 +1476,14 @@ static void acl_insert_user(const char *user, const char *host, ACL_USER acl_user; mysql_mutex_assert_owner(&acl_cache->lock); + /* + All accounts can authenticate per default. This will change when + we add a new field to the user table. + + Currently this flag is only set to false when authentication is attempted + using an unknown user name. + */ + acl_user.can_authenticate= true; acl_user.user=*user ? strdup_root(&mem,user) : 0; update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0); @@ -8000,6 +8019,10 @@ struct MPVIO_EXT :public MYSQL_PLUGIN_VIO char *host; Thd_charset_adapter *charset_adapter; LEX_STRING acl_user_plugin; + bool can_authenticate() + { + return (acl_user && acl_user->can_authenticate); + } }; /** @@ -8301,6 +8324,34 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio, } #ifndef NO_EMBEDDED_ACCESS_CHECKS + +/** + When authentication is attempted using an unknown username a dummy user + account with no authentication capabilites is assigned to the connection. + This is done increase the cost of enumerating user accounts based on + authentication protocol. +*/ + +ACL_USER *decoy_user(const LEX_STRING &username, + MEM_ROOT *mem) +{ + ACL_USER *user= (ACL_USER *) alloc_root(mem, sizeof(ACL_USER)); + user->can_authenticate= false; + user->user= strmake_root(mem, username.str, username.length); + user->auth_string= empty_lex_str; + user->ssl_cipher= empty_c_string; + user->x509_issuer= empty_c_string; + user->x509_subject= empty_c_string; + user->salt_len= 0; + + /* + For now the common default account is used. Improvements might involve + mapping a consistent hash of a username to a range of plugins. + */ + user->plugin= *default_auth_plugin_name; + return user; +} + /** Finds acl entry in user database for authentication purposes. @@ -8342,8 +8393,14 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio) if (!mpvio->acl_user) { - login_failed_error(mpvio, mpvio->auth_info.password_used); - DBUG_RETURN (1); + /* + Pretend the user exists; let the plugin decide how to handle + bad credentials. + */ + LEX_STRING usr= { mpvio->auth_info.user_name, + mpvio->auth_info.user_name_length }; + mpvio->acl_user= decoy_user(usr, mpvio->mem_root); + mpvio->acl_user_plugin= mpvio->acl_user->plugin; } /* user account requires non-default plugin and the client is too old */ @@ -8465,7 +8522,9 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) #ifndef NO_EMBEDDED_ACCESS_CHECKS if (find_mpvio_user(mpvio)) + { DBUG_RETURN(1); + } char *client_plugin; if (mpvio->client_capabilities & CLIENT_PLUGIN_AUTH) @@ -9450,6 +9509,8 @@ acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len) if (parse_com_change_user_packet(&mpvio, com_change_user_pkt_len)) { + if (!thd->is_error()) + login_failed_error(&mpvio, mpvio.auth_info.password_used); server_mpvio_update_thd(thd, &mpvio); DBUG_RETURN(1); } @@ -9515,6 +9576,11 @@ acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len) mpvio.db.str ? mpvio.db.str : (char*) ""); } + if (res == CR_OK && !mpvio.can_authenticate()) + { + res= CR_ERROR; + } + if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS) { DBUG_ASSERT(mpvio.status == MPVIO_EXT::FAILURE); -- cgit v1.2.1 From e892e7196997e3c7c385b5e0e714440ff90c0d90 Mon Sep 17 00:00:00 2001 From: Ashish Agarwal Date: Thu, 17 Jul 2014 19:21:56 +0530 Subject: WL#7219: Pushing it to release 5.5.39-release branch --- sql/sql_audit.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc index bf672b6ea48..6ccdcefe8eb 100644 --- a/sql/sql_audit.cc +++ b/sql/sql_audit.cc @@ -353,7 +353,7 @@ int initialize_audit_plugin(st_plugin_int *plugin) return 1; } - if (plugin->plugin->init && plugin->plugin->init(NULL)) + if (plugin->plugin->init && plugin->plugin->init(plugin)) { sql_print_error("Plugin '%s' init function returned error.", plugin->name.str); -- cgit v1.2.1 From e9b2f5bf15281583b38a56b12f9ae2420c46a6d1 Mon Sep 17 00:00:00 2001 From: Ashish Agarwal Date: Sat, 19 Jul 2014 11:24:21 +0530 Subject: WL#7219: Reverting the wl#7219 patch in mysql-5.5.39-release branch --- sql/sql_audit.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_audit.cc b/sql/sql_audit.cc index 6ccdcefe8eb..bf672b6ea48 100644 --- a/sql/sql_audit.cc +++ b/sql/sql_audit.cc @@ -353,7 +353,7 @@ int initialize_audit_plugin(st_plugin_int *plugin) return 1; } - if (plugin->plugin->init && plugin->plugin->init(plugin)) + if (plugin->plugin->init && plugin->plugin->init(NULL)) { sql_print_error("Plugin '%s' init function returned error.", plugin->name.str); -- cgit v1.2.1 From 80708da138913deb3a096da0e761ff247766bf96 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Wed, 23 Jul 2014 13:38:48 +0400 Subject: MDEV-5750 Assertion `ltime->year == 0' fails on a query with EXTRACT DAY_MINUTE and TIME column Item_func_min_max::get_date() did not clear ltime->year when returning a TIME value. --- sql/item_func.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/item_func.cc b/sql/item_func.cc index fc69fa711c2..8df0564af15 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -2646,7 +2646,7 @@ bool Item_func_min_max::get_date(MYSQL_TIME *ltime, uint fuzzy_date) { ltime->time_type= MYSQL_TIMESTAMP_TIME; ltime->hour+= (ltime->month * 32 + ltime->day) * 24; - ltime->month= ltime->day= 0; + ltime->year= ltime->month= ltime->day= 0; if (adjust_time_range_with_warn(ltime, min(decimals, TIME_SECOND_PART_DIGITS))) return (null_value= true); -- cgit v1.2.1 From 6b353dd1ded71c1abaa4e9c64c152546a44922df Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 23 Jul 2014 19:53:29 +0400 Subject: MDEV-6289 : Unexpected results when querying information_schema - When traversing JOIN_TABs with first_linear_tab/next_linear_tab(), don't forget to enter the semi-join nest when it is the first table in the join order. Failure to do so could cause e.g. I_S tables not to be filled. --- sql/item_subselect.cc | 5 +++-- sql/sql_delete.cc | 3 ++- sql/sql_select.cc | 61 +++++++++++++++++++++++++++++++++------------------ sql/sql_select.h | 4 +++- sql/sql_show.cc | 5 +++-- 5 files changed, 51 insertions(+), 27 deletions(-) (limited to 'sql') diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 65ce50c1e3c..62df666b71f 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -3200,8 +3200,9 @@ int subselect_single_select_engine::exec() pushed down into the subquery. Those optimizations are ref[_or_null] acceses. Change them to be full table scans. */ - for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_CONST_TABLES); tab; - tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) + JOIN_TAB *tab; + for (tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); + tab; tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) { if (tab && tab->keyuse) { diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 64fd33bbc2c..97d3d10c21c 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -673,7 +673,8 @@ multi_delete::initialize_tables(JOIN *join) walk= delete_tables; - for (JOIN_TAB *tab= first_linear_tab(join, WITH_CONST_TABLES); + for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS, + WITH_CONST_TABLES); tab; tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index e6ae3b05c1d..084cbb4645d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1332,7 +1332,8 @@ JOIN::optimize() Perform the optimization on fields evaluation mentioned above for all on expressions. */ - for (JOIN_TAB *tab= first_linear_tab(this, WITHOUT_CONST_TABLES); tab; + JOIN_TAB *tab; + for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) { if (*tab->on_expr_ref) @@ -1355,7 +1356,7 @@ JOIN::optimize() Perform the optimization on fields evaliation mentioned above for all used ref items. */ - for (JOIN_TAB *tab= first_linear_tab(this, WITHOUT_CONST_TABLES); tab; + for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) { uint key_copy_index=0; @@ -1980,7 +1981,8 @@ bool JOIN::setup_subquery_caches() if (conds) conds= conds->transform(&Item::expr_cache_insert_transformer, (uchar*) thd); - for (JOIN_TAB *tab= first_linear_tab(this, WITHOUT_CONST_TABLES); + JOIN_TAB *tab; + for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) { if (tab->select_cond) @@ -2143,7 +2145,8 @@ JOIN::reinit() /* need to reset ref access state (see join_read_key) */ if (join_tab) { - for (JOIN_TAB *tab= first_linear_tab(this, WITH_CONST_TABLES); tab; + JOIN_TAB *tab; + for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITH_CONST_TABLES); tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) { tab->ref.key_err= TRUE; @@ -2907,8 +2910,9 @@ JOIN::destroy() { if (join_tab != tmp_join->join_tab) { - for (JOIN_TAB *tab= first_linear_tab(this, WITH_CONST_TABLES); tab; - tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) + JOIN_TAB *tab; + for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITH_CONST_TABLES); + tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) { tab->cleanup(); } @@ -7520,14 +7524,24 @@ JOIN_TAB *next_top_level_tab(JOIN *join, JOIN_TAB *tab) } -JOIN_TAB *first_linear_tab(JOIN *join, enum enum_with_const_tables const_tbls) +JOIN_TAB *first_linear_tab(JOIN *join, + enum enum_with_bush_roots include_bush_roots, + enum enum_with_const_tables const_tbls) { JOIN_TAB *first= join->join_tab; if (const_tbls == WITHOUT_CONST_TABLES) first+= join->const_tables; - if (first < join->join_tab + join->top_join_tab_count) - return first; - return NULL; /* All tables were const tables */ + + if (first >= join->join_tab + join->top_join_tab_count) + return NULL; /* All are const tables */ + + if (first->bush_children && include_bush_roots == WITHOUT_BUSH_ROOTS) + { + /* This JOIN_TAB is a SJM nest; Start from first table in nest */ + return first->bush_children->start; + } + + return first; } @@ -8364,9 +8378,10 @@ inline void add_cond_and_fix(THD *thd, Item **e1, Item *e2) static void add_not_null_conds(JOIN *join) { + JOIN_TAB *tab; DBUG_ENTER("add_not_null_conds"); - for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_CONST_TABLES); + for (tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); tab; tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) { @@ -8537,7 +8552,7 @@ make_outerjoin_info(JOIN *join) tab->table->pos_in_table_list being set. */ JOIN_TAB *tab; - for (tab= first_linear_tab(join, WITHOUT_CONST_TABLES); + for (tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); tab; tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) { @@ -8549,7 +8564,7 @@ make_outerjoin_info(JOIN *join) } } - for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_CONST_TABLES); tab; + for (JOIN_TAB *tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); tab; tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) { TABLE *table= tab->table; @@ -9309,7 +9324,7 @@ bool generate_derived_keys(DYNAMIC_ARRAY *keyuse_array) void JOIN::drop_unused_derived_keys() { JOIN_TAB *tab; - for (tab= first_linear_tab(this, WITHOUT_CONST_TABLES); + for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) { @@ -9997,7 +10012,7 @@ void check_join_cache_usage_for_tables(JOIN *join, ulonglong options, JOIN_TAB *tab; JOIN_TAB *prev_tab; - for (tab= first_linear_tab(join, WITHOUT_CONST_TABLES); + for (tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); tab; tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) { @@ -10005,7 +10020,7 @@ void check_join_cache_usage_for_tables(JOIN *join, ulonglong options, } uint idx= join->const_tables; - for (tab= first_linear_tab(join, WITHOUT_CONST_TABLES); + for (tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); tab; tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) { @@ -10099,7 +10114,8 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) tab->partial_join_cardinality= 1; JOIN_TAB *prev_tab= NULL; - for (tab= first_linear_tab(join, WITHOUT_CONST_TABLES), i= join->const_tables; + i= join->const_tables; + for (tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); tab; prev_tab=tab, tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) { @@ -10124,7 +10140,7 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) check_join_cache_usage_for_tables(join, options, no_jbuf_after); JOIN_TAB *first_tab; - for (tab= first_tab= first_linear_tab(join, WITHOUT_CONST_TABLES); + for (tab= first_tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITHOUT_CONST_TABLES); tab; tab= next_linear_tab(join, tab, WITH_BUSH_ROOTS)) { @@ -10800,7 +10816,8 @@ void JOIN::cleanup(bool full) } if (full) { - JOIN_TAB *sort_tab= first_linear_tab(this, WITHOUT_CONST_TABLES); + JOIN_TAB *sort_tab= first_linear_tab(this, WITH_BUSH_ROOTS, + WITHOUT_CONST_TABLES); if (pre_sort_join_tab) { if (sort_tab && sort_tab->select == pre_sort_join_tab->select) @@ -10847,7 +10864,7 @@ void JOIN::cleanup(bool full) } else { - for (tab= first_linear_tab(this, WITH_CONST_TABLES); tab; + for (tab= first_linear_tab(this, WITH_BUSH_ROOTS, WITH_CONST_TABLES); tab; tab= next_linear_tab(this, tab, WITH_BUSH_ROOTS)) { if (tab->table) @@ -11009,7 +11026,9 @@ only_eq_ref_tables(JOIN *join,ORDER *order,table_map tables) static void update_depend_map(JOIN *join) { - for (JOIN_TAB *join_tab= first_linear_tab(join, WITH_CONST_TABLES); join_tab; + JOIN_TAB *join_tab; + for (join_tab= first_linear_tab(join, WITH_BUSH_ROOTS, WITH_CONST_TABLES); + join_tab; join_tab= next_linear_tab(join, join_tab, WITH_BUSH_ROOTS)) { TABLE_REF *ref= &join_tab->ref; diff --git a/sql/sql_select.h b/sql/sql_select.h index 30d2f4abe56..ce57376a3ec 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1458,7 +1458,9 @@ private: enum enum_with_bush_roots { WITH_BUSH_ROOTS, WITHOUT_BUSH_ROOTS}; enum enum_with_const_tables { WITH_CONST_TABLES, WITHOUT_CONST_TABLES}; -JOIN_TAB *first_linear_tab(JOIN *join, enum enum_with_const_tables const_tbls); +JOIN_TAB *first_linear_tab(JOIN *join, + enum enum_with_bush_roots include_bush_roots, + enum enum_with_const_tables const_tbls); JOIN_TAB *next_linear_tab(JOIN* join, JOIN_TAB* tab, enum enum_with_bush_roots include_bush_roots); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 6e89a871c7a..afbda8dd0b1 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -7708,8 +7708,9 @@ bool get_schema_tables_result(JOIN *join, Warnings_only_error_handler err_handler; thd->push_internal_handler(&err_handler); old_proc_info= thd_proc_info(thd, "Filling schema table"); - - for (JOIN_TAB *tab= first_linear_tab(join, WITH_CONST_TABLES); + + JOIN_TAB *tab; + for (tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS, WITH_CONST_TABLES); tab; tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) { -- cgit v1.2.1 From 06655a192d7d3216fccc18c1f25601c4ecff09d4 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 23 Jul 2014 22:48:31 +0400 Subject: MDEV-6322: The PARTITION engine can return wrong query results MySQL Bug#71095: Wrong results with PARTITION BY LIST COLUMNS() MySQL Bug#72803: Wrong "Impossible where" with LIST partitioning MDEV-6240: Wrong "Impossible where" with LIST partitioning - Backprot the fix from MySQL Bug#71095. --- sql/sql_partition.cc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'sql') diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 357bd8efc27..3eff6ee5cf4 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -3261,11 +3261,16 @@ uint32 get_partition_id_cols_list_for_endpoint(partition_info *part_info, nparts, left_endpoint, include_endpoint))); - if (!left_endpoint) + if (!left_endpoint && list_index < part_info->num_list_values) { - /* Set the end after this list tuple if not already after the last. */ - if (list_index < part_info->num_parts) - list_index++; + /* + Set the end after this list tuple if it is not already after the last + and it matches. ??? + */ + int cmp = cmp_rec_and_tuple_prune(list_col_array + list_index*num_columns, + nparts, left_endpoint, include_endpoint); + if (cmp >= 0) + list_index++; } DBUG_RETURN(list_index); -- cgit v1.2.1 From 1907bf042a38c162740fb729b8a93b4c6f72f411 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 23 Jul 2014 12:01:05 +0200 Subject: MDEV-6409 CREATE VIEW replication problem if error occurs in mysql_register_view fix by Sriram Patil --- sql/sql_view.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 7be370b8e63..25bada09fba 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -39,8 +39,7 @@ const LEX_STRING view_type= { C_STRING_WITH_LEN("VIEW") }; -static int mysql_register_view(THD *thd, TABLE_LIST *view, - enum_view_create_mode mode); +static int mysql_register_view(THD *, TABLE_LIST *, enum_view_create_mode); /* Make a unique name for an anonymous view column @@ -670,7 +669,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, res= mysql_register_view(thd, view, mode); - if (mysql_bin_log.is_open()) + if (!res && mysql_bin_log.is_open()) { String buff; const LEX_STRING command[3]= -- cgit v1.2.1 From 4e6e720160d983752465323477d74a1c90bad0cd Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 23 Jul 2014 19:36:15 +0200 Subject: MDEV-6290 Crash in KILL HARD QUERY USER x@y when slave threads are running KILL USER should ignore system threads where sctx->user=sctx->host=NULL --- sql/sql_parse.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2c833761e74..43b10a6fb9e 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -7365,7 +7365,7 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user, I_List_iterator it(threads); while ((tmp=it++)) { - if (tmp->command == COM_DAEMON) + if (!tmp->security_ctx->user) continue; /* Check that hostname (if given) and user name matches. -- cgit v1.2.1 From be667b8c42f9d67f61409be594603d73f007d405 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Fri, 25 Jul 2014 14:15:33 +0200 Subject: MDEV-5706 MariaDB does not build on hurd-i386 backport from 10.0 the fix for: MDEV-5756 CMake option to build without thread pool --- sql/CMakeLists.txt | 10 +++++++++- sql/scheduler.h | 5 ++--- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'sql') diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 376f9773ab2..22cc9ac4be7 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -30,7 +30,15 @@ ${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h SET_SOURCE_FILES_PROPERTIES(${GEN_SOURCES} PROPERTIES GENERATED 1) -ADD_DEFINITIONS(-DMYSQL_SERVER -DHAVE_EVENT_SCHEDULER -DHAVE_POOL_OF_THREADS) +ADD_DEFINITIONS(-DMYSQL_SERVER -DHAVE_EVENT_SCHEDULER) + +IF (CMAKE_SYSTEM_NAME MATCHES "Linux" OR + CMAKE_SYSTEM_NAME MATCHES "Windows" OR + CMAKE_SYSTEM_NAME MATCHES "SunOS" OR + HAVE_KQUEUE) + ADD_DEFINITIONS(-DHAVE_POOL_OF_THREADS) +ENDIF() + IF(SSL_DEFINES) ADD_DEFINITIONS(${SSL_DEFINES}) ENDIF() diff --git a/sql/scheduler.h b/sql/scheduler.h index 4e200e86d74..f7aff377eac 100644 --- a/sql/scheduler.h +++ b/sql/scheduler.h @@ -99,14 +99,13 @@ public: void *data; /* scheduler-specific data structure */ }; -#if !defined(EMBEDDED_LIBRARY) -#define HAVE_POOL_OF_THREADS 1 +#ifdef HAVE_POOL_OF_THREADS void pool_of_threads_scheduler(scheduler_functions* func, ulong *arg_max_connections, uint *arg_connection_count); #else #define pool_of_threads_scheduler(A,B,C) \ one_thread_per_connection_scheduler(A, B, C) -#endif +#endif /*HAVE_POOL_OF_THREADS*/ #endif /* SCHEDULER_INCLUDED */ -- cgit v1.2.1 From a6071cc596fe37ef02e5639042de124f0c11b3b3 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 27 Jul 2014 21:02:00 +0200 Subject: MDEV-6082 Assertion `0' fails in TC_LOG_DUMMY::log_and_order on DML after installing TokuDB at runtime on server with disabled InnoDB We don't support changing tc_log implementation at run time. If the first XA-capable engine is loaded with INSTALL PLUGIN - disable its XA capabilities with a warning --- sql/handler.cc | 12 ++++++++++++ sql/log.h | 9 ++++++++- sql/mysqld.cc | 7 +++---- 3 files changed, 23 insertions(+), 5 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 2e20944a96b..efecd478019 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -543,7 +543,19 @@ int ha_initialize_handlerton(st_plugin_int *plugin) savepoint_alloc_size+= tmp; hton2plugin[hton->slot]=plugin; if (hton->prepare) + { total_ha_2pc++; + if (tc_log && tc_log != get_tc_log_implementation()) + { + total_ha_2pc--; + hton->prepare= 0; + push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_UNKNOWN_ERROR, + "Cannot enable tc-log at run-time. " + "XA features of %s are disabled", + plugin->name.str); + } + } break; } /* fall through */ diff --git a/sql/log.h b/sql/log.h index e388df61b38..4eb23d4878f 100644 --- a/sql/log.h +++ b/sql/log.h @@ -954,6 +954,13 @@ end: DBUG_RETURN(error); } - +static inline TC_LOG *get_tc_log_implementation() +{ + if (total_ha_2pc <= 1) + return &tc_log_dummy; + if (opt_bin_log) + return &mysql_bin_log; + return &tc_log_mmap; +} #endif /* LOG_H */ diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 193c1af0f99..0559afa7616 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4327,6 +4327,8 @@ a file name for --log-bin-index option", opt_binlog_index_name); if (ha_init_errors()) DBUG_RETURN(1); + tc_log= 0; // ha_initialize_handlerton() needs that + if (plugin_init(&remaining_argc, remaining_argv, (opt_noacl ? PLUGIN_INIT_SKIP_PLUGIN_TABLE : 0) | (opt_abort ? PLUGIN_INIT_SKIP_INITIALIZATION : 0))) @@ -4469,10 +4471,7 @@ a file name for --log-bin-index option", opt_binlog_index_name); internal_tmp_table_max_key_segments= myisam_max_key_segments(); #endif - tc_log= (total_ha_2pc > 1 ? (opt_bin_log ? - (TC_LOG *) &mysql_bin_log : - (TC_LOG *) &tc_log_mmap) : - (TC_LOG *) &tc_log_dummy); + tc_log= get_tc_log_implementation(); if (tc_log->open(opt_bin_log ? opt_bin_logname : opt_tc_log_file)) { -- cgit v1.2.1 From c57c5be12a2d7d066cc508043da87cf51eb75cd3 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 28 Jul 2014 12:47:14 +0400 Subject: MDEV-5745 analyze MySQL fix for bug#12368495 --- sql/item.cc | 2 +- sql/item_strfunc.cc | 54 +++++++++++++++++------------------------------------ sql/item_strfunc.h | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 38 deletions(-) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index b4d7bffae7b..d58e4d285c5 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2074,7 +2074,7 @@ bool agg_item_collations(DTCollation &c, const char *fname, bool unknown_cs= 0; c.set(av[0]->collation); - for (i= 1, arg= &av[item_sep]; i < count; i++, arg++) + for (i= 1, arg= &av[item_sep]; i < count; i++, arg+= item_sep) { if (c.aggregate((*arg)->collation, flags)) { diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 861da69a9be..b0d7a346e83 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1624,7 +1624,7 @@ String *Item_func_ltrim::val_str(String *str) if ((remove_length= remove_str->length()) == 0 || remove_length > res->length()) - return res; + return non_trimmed_value(res); ptr= (char*) res->ptr(); end= ptr+res->length(); @@ -1643,9 +1643,8 @@ String *Item_func_ltrim::val_str(String *str) end+=remove_length; } if (ptr == res->ptr()) - return res; - tmp_value.set(*res,(uint) (ptr - res->ptr()),(uint) (end-ptr)); - return &tmp_value; + return non_trimmed_value(res); + return trimmed_value(res, (uint32) (ptr - res->ptr()), (uint32) (end - ptr)); } @@ -1671,7 +1670,7 @@ String *Item_func_rtrim::val_str(String *str) if ((remove_length= remove_str->length()) == 0 || remove_length > res->length()) - return res; + return non_trimmed_value(res); ptr= (char*) res->ptr(); end= ptr+res->length(); @@ -1683,11 +1682,11 @@ String *Item_func_rtrim::val_str(String *str) { char chr=(*remove_str)[0]; #ifdef USE_MB - if (use_mb(res->charset())) + if (use_mb(collation.collation)) { while (ptr < end) { - if ((l=my_ismbchar(res->charset(), ptr,end))) ptr+=l,p=ptr; + if ((l= my_ismbchar(collation.collation, ptr, end))) ptr+= l, p=ptr; else ++ptr; } ptr=p; @@ -1700,12 +1699,12 @@ String *Item_func_rtrim::val_str(String *str) { const char *r_ptr=remove_str->ptr(); #ifdef USE_MB - if (use_mb(res->charset())) + if (use_mb(collation.collation)) { loop: while (ptr + remove_length < end) { - if ((l=my_ismbchar(res->charset(), ptr,end))) ptr+=l; + if ((l= my_ismbchar(collation.collation, ptr, end))) ptr+= l; else ++ptr; } if (ptr + remove_length == end && !memcmp(ptr,r_ptr,remove_length)) @@ -1724,9 +1723,8 @@ String *Item_func_rtrim::val_str(String *str) } } if (end == res->ptr()+res->length()) - return res; - tmp_value.set(*res,0,(uint) (end-res->ptr())); - return &tmp_value; + return non_trimmed_value(res); + return trimmed_value(res, 0, (uint32) (end - res->ptr())); } @@ -1753,37 +1751,22 @@ String *Item_func_trim::val_str(String *str) if ((remove_length= remove_str->length()) == 0 || remove_length > res->length()) - return res; + return non_trimmed_value(res); ptr= (char*) res->ptr(); end= ptr+res->length(); r_ptr= remove_str->ptr(); + while (ptr+remove_length <= end && !memcmp(ptr,r_ptr,remove_length)) + ptr+=remove_length; #ifdef USE_MB - if (use_mb(res->charset())) + if (use_mb(collation.collation)) { - while (ptr + remove_length <= end) - { - uint num_bytes= 0; - while (num_bytes < remove_length) - { - uint len; - if ((len= my_ismbchar(res->charset(), ptr + num_bytes, end))) - num_bytes+= len; - else - ++num_bytes; - } - if (num_bytes != remove_length) - break; - if (memcmp(ptr, r_ptr, remove_length)) - break; - ptr+= remove_length; - } char *p=ptr; register uint32 l; loop: while (ptr + remove_length < end) { - if ((l= my_ismbchar(res->charset(), ptr,end))) + if ((l= my_ismbchar(collation.collation, ptr, end))) ptr+= l; else ++ptr; @@ -1799,16 +1782,13 @@ String *Item_func_trim::val_str(String *str) else #endif /* USE_MB */ { - while (ptr+remove_length <= end && !memcmp(ptr,r_ptr,remove_length)) - ptr+=remove_length; while (ptr + remove_length <= end && !memcmp(end-remove_length,r_ptr,remove_length)) end-=remove_length; } if (ptr == res->ptr() && end == ptr+res->length()) - return res; - tmp_value.set(*res,(uint) (ptr - res->ptr()),(uint) (end-ptr)); - return &tmp_value; + return non_trimmed_value(res); + return trimmed_value(res, (uint32) (ptr - res->ptr()), (uint32) (end - ptr)); } void Item_func_trim::fix_length_and_dec() diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 4579a6949c7..fcc1991774c 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -270,6 +270,21 @@ class Item_func_trim :public Item_str_func protected: String tmp_value; String remove; + String *trimmed_value(String *res, uint32 offset, uint32 length) + { + tmp_value.set(*res, offset, length); + /* + Make sure to return correct charset and collation: + TRIM(0x000000 FROM _ucs2 0x0061) + should set charset to "binary" rather than to "ucs2". + */ + tmp_value.set_charset(collation.collation); + return &tmp_value; + } + String *non_trimmed_value(String *res) + { + return trimmed_value(res, 0, res->length()); + } public: Item_func_trim(Item *a,Item *b) :Item_str_func(a,b) {} Item_func_trim(Item *a) :Item_str_func(a) {} -- cgit v1.2.1 From 5b452ae0279935164b6a2bee8861bf2e830508b5 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 28 Jul 2014 13:47:55 +0400 Subject: MDEV-4511 Assertion `scale <= precision' fails on GROUP BY TIMEDIFF with incorrect types MDEV-6302 Wrong result set when using GROUP BY FROM_UNIXTIME(...)+0 Fixed. --- sql/field.h | 23 ++++++++++++++ sql/item.h | 38 ++++++++++++++++++++++ sql/item_func.cc | 91 +++++++++++++++++++++++------------------------------ sql/item_func.h | 17 +++------- sql/item_timefunc.h | 10 ++---- 5 files changed, 107 insertions(+), 72 deletions(-) (limited to 'sql') diff --git a/sql/field.h b/sql/field.h index f422d0c8997..e7e7545aacf 100644 --- a/sql/field.h +++ b/sql/field.h @@ -65,6 +65,29 @@ inline bool is_temporal_type(enum_field_types type) return mysql_type_to_time_type(type) != MYSQL_TIMESTAMP_ERROR; } + +/** + Tests if field type is temporal and has time part, + i.e. represents TIME, DATETIME or TIMESTAMP types in SQL. + + @param type Field type, as returned by field->type(). + @retval true If field type is temporal type with time part. + @retval false If field type is not temporal type with time part. +*/ +inline bool is_temporal_type_with_time(enum_field_types type) +{ + switch (type) + { + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + case MYSQL_TYPE_TIMESTAMP: + return true; + default: + return false; + } +} + + /* Virtual_column_info is the class to contain additional characteristics that is specific for a virtual/computed diff --git a/sql/item.h b/sql/item.h index acef5729f66..33296b619db 100644 --- a/sql/item.h +++ b/sql/item.h @@ -909,9 +909,47 @@ public: virtual cond_result eq_cmp_result() const { return COND_OK; } inline uint float_length(uint decimals_par) const { return decimals != NOT_FIXED_DEC ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;} + /* Returns total number of decimal digits */ virtual uint decimal_precision() const; + /* Returns the number of integer part digits only */ inline int decimal_int_part() const { return my_decimal_int_part(decimal_precision(), decimals); } + /* + Returns the number of fractional digits only. + NOT_FIXED_DEC is replaced to the maximum possible number + of fractional digits, taking into account the data type. + */ + uint decimal_scale() const + { + return decimals < NOT_FIXED_DEC ? decimals : + is_temporal_type_with_time(field_type()) ? + TIME_SECOND_PART_DIGITS : + min(max_length, DECIMAL_MAX_SCALE); + } + /* + Returns how many digits a divisor adds into a division result. + This is important when the integer part of the divisor can be 0. + In this example: + SELECT 1 / 0.000001; -> 1000000.0000 + the divisor adds 5 digits into the result precision. + + Currently this method only replaces NOT_FIXED_DEC to + TIME_SECOND_PART_DIGITS for temporal data types. + This method can be made virtual, to create more efficient (smaller) + data types for division results. + For example, in + SELECT 1/1.000001; + the divisor could provide no additional precision into the result, + so could any other items that are know to return a result + with non-zero integer part. + */ + uint divisor_precision_increment() const + { + return decimals < NOT_FIXED_DEC ? decimals : + is_temporal_type_with_time(field_type()) ? + TIME_SECOND_PART_DIGITS : + decimals; + } /** TIME or DATETIME precision of the item: 0..6 */ diff --git a/sql/item_func.cc b/sql/item_func.cc index 8df0564af15..f2b07a37575 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -569,7 +569,7 @@ my_decimal *Item_real_func::val_decimal(my_decimal *decimal_value) } -void Item_func::fix_num_length_and_dec() +void Item_udf_func::fix_num_length_and_dec() { uint fl_length= 0; decimals=0; @@ -587,11 +587,6 @@ void Item_func::fix_num_length_and_dec() } -void Item_func_numhybrid::fix_num_length_and_dec() -{} - - - /** Count max_length and decimals for temporal functions. @@ -777,9 +772,9 @@ bool Item_func_connection_id::fix_fields(THD *thd, Item **ref) function of two arguments. */ -void Item_num_op::find_num_type(void) +void Item_num_op::fix_length_and_dec(void) { - DBUG_ENTER("Item_num_op::find_num_type"); + DBUG_ENTER("Item_num_op::fix_length_and_dec"); DBUG_PRINT("info", ("name %s", func_name())); DBUG_ASSERT(arg_count == 2); Item_result r0= args[0]->cast_to_int_type(); @@ -823,22 +818,26 @@ void Item_num_op::find_num_type(void) type depends only on the first argument) */ -void Item_func_num1::find_num_type() +void Item_func_num1::fix_length_and_dec() { - DBUG_ENTER("Item_func_num1::find_num_type"); + DBUG_ENTER("Item_func_num1::fix_length_and_dec"); DBUG_PRINT("info", ("name %s", func_name())); switch (cached_result_type= args[0]->cast_to_int_type()) { case INT_RESULT: + max_length= args[0]->max_length; unsigned_flag= args[0]->unsigned_flag; break; case STRING_RESULT: case REAL_RESULT: cached_result_type= REAL_RESULT; + decimals= args[0]->decimals; // Preserve NOT_FIXED_DEC max_length= float_length(decimals); break; case TIME_RESULT: cached_result_type= DECIMAL_RESULT; case DECIMAL_RESULT: + decimals= args[0]->decimal_scale(); // Do not preserve NOT_FIXED_DEC + max_length= args[0]->max_length; break; case ROW_RESULT: case IMPOSSIBLE_RESULT: @@ -853,20 +852,6 @@ void Item_func_num1::find_num_type() } -void Item_func_num1::fix_num_length_and_dec() -{ - decimals= args[0]->decimals; - max_length= args[0]->max_length; -} - - -void Item_func_numhybrid::fix_length_and_dec() -{ - fix_num_length_and_dec(); - find_num_type(); -} - - String *Item_func_hybrid_result_type::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -1455,11 +1440,14 @@ my_decimal *Item_func_plus::decimal_op(my_decimal *decimal_value) */ void Item_func_additive_op::result_precision() { - decimals= max(args[0]->decimals, args[1]->decimals); - int arg1_int= args[0]->decimal_precision() - args[0]->decimals; - int arg2_int= args[1]->decimal_precision() - args[1]->decimals; + decimals= max(args[0]->decimal_scale(), args[1]->decimal_scale()); + int arg1_int= args[0]->decimal_precision() - args[0]->decimal_scale(); + int arg2_int= args[1]->decimal_precision() - args[1]->decimal_scale(); int precision= max(arg1_int, arg2_int) + 1 + decimals; + DBUG_ASSERT(arg1_int >= 0); + DBUG_ASSERT(arg2_int >= 0); + /* Integer operations keep unsigned_flag if one of arguments is unsigned */ if (result_type() == INT_RESULT) unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; @@ -1568,7 +1556,8 @@ void Item_func_mul::result_precision() unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; else unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag; - decimals= min(args[0]->decimals + args[1]->decimals, DECIMAL_MAX_SCALE); + decimals= min(args[0]->decimal_scale() + args[1]->decimal_scale(), + DECIMAL_MAX_SCALE); uint est_prec = args[0]->decimal_precision() + args[1]->decimal_precision(); uint precision= min(est_prec, DECIMAL_MAX_PRECISION); max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, @@ -1618,8 +1607,20 @@ my_decimal *Item_func_div::decimal_op(my_decimal *decimal_value) void Item_func_div::result_precision() { + /* + We need to add args[1]->divisor_precision_increment(), + to properly handle the cases like this: + SELECT 5.05 / 0.014; -> 360.714286 + i.e. when the divisor has a zero integer part + and non-zero digits appear only after the decimal point. + Precision in this example is calculated as + args[0]->decimal_precision() + // 3 + args[1]->divisor_precision_increment() + // 3 + prec_increment // 4 + which gives 10 decimals digits. + */ uint precision=min(args[0]->decimal_precision() + - args[1]->decimals + prec_increment, + args[1]->divisor_precision_increment() + prec_increment, DECIMAL_MAX_PRECISION); /* Integer operations keep unsigned_flag if one of arguments is unsigned */ @@ -1627,7 +1628,7 @@ void Item_func_div::result_precision() unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; else unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag; - decimals= min(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE); + decimals= min(args[0]->decimal_scale() + prec_increment, DECIMAL_MAX_SCALE); max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, unsigned_flag); } @@ -1776,7 +1777,7 @@ my_decimal *Item_func_mod::decimal_op(my_decimal *decimal_value) void Item_func_mod::result_precision() { - decimals= max(args[0]->decimals, args[1]->decimals); + decimals= max(args[0]->decimal_scale(), args[1]->decimal_scale()); max_length= max(args[0]->max_length, args[1]->max_length); } @@ -1818,18 +1819,12 @@ my_decimal *Item_func_neg::decimal_op(my_decimal *decimal_value) } -void Item_func_neg::fix_num_length_and_dec() -{ - decimals= args[0]->decimals; - /* 1 add because sign can appear */ - max_length= args[0]->max_length + 1; -} - - void Item_func_neg::fix_length_and_dec() { DBUG_ENTER("Item_func_neg::fix_length_and_dec"); Item_func_num1::fix_length_and_dec(); + /* 1 add because sign can appear */ + max_length= args[0]->max_length + 1; /* If this is in integer context keep the context as integer if possible @@ -2119,8 +2114,12 @@ void Item_func_integer::fix_length_and_dec() decimals=0; } -void Item_func_int_val::fix_num_length_and_dec() + +void Item_func_int_val::fix_length_and_dec() { + DBUG_ENTER("Item_func_int_val::fix_length_and_dec"); + DBUG_PRINT("info", ("name %s", func_name())); + ulonglong tmp_max_length= (ulonglong ) args[0]->max_length - (args[0]->decimals ? args[0]->decimals + 1 : 0) + 2; max_length= tmp_max_length > (ulonglong) max_field_size ? @@ -2128,13 +2127,7 @@ void Item_func_int_val::fix_num_length_and_dec() uint tmp= float_length(decimals); set_if_smaller(max_length,tmp); decimals= 0; -} - -void Item_func_int_val::find_num_type() -{ - DBUG_ENTER("Item_func_int_val::find_num_type"); - DBUG_PRINT("info", ("name %s", func_name())); switch (cached_result_type= args[0]->cast_to_int_type()) { case STRING_RESULT: @@ -3569,12 +3562,6 @@ String *Item_func_udf_decimal::val_str(String *str) } -void Item_func_udf_decimal::fix_length_and_dec() -{ - fix_num_length_and_dec(); -} - - /* Default max_length is max argument length */ void Item_func_udf_str::fix_length_and_dec() diff --git a/sql/item_func.h b/sql/item_func.h index 1586c6e9c63..deaaee2daa0 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -145,7 +145,6 @@ public: virtual void print(String *str, enum_query_type query_type); void print_op(String *str, enum_query_type query_type); void print_args(String *str, uint from, enum_query_type query_type); - virtual void fix_num_length_and_dec(); void count_only_length(Item **item, uint nitems); void count_real_length(); void count_decimal_length(); @@ -450,9 +449,6 @@ public: Item_func_numhybrid(List &list) :Item_func_hybrid_result_type(list) {} - void fix_length_and_dec(); - void fix_num_length_and_dec(); - virtual void find_num_type()= 0; /* To be called from fix_length_and_dec */ String *str_op(String *str) { DBUG_ASSERT(0); return 0; } bool date_op(MYSQL_TIME *ltime, uint fuzzydate) { DBUG_ASSERT(0); return true; } }; @@ -464,9 +460,7 @@ class Item_func_num1: public Item_func_numhybrid public: Item_func_num1(Item *a) :Item_func_numhybrid(a) {} Item_func_num1(Item *a, Item *b) :Item_func_numhybrid(a, b) {} - - void fix_num_length_and_dec(); - void find_num_type(); + void fix_length_and_dec(); }; @@ -482,7 +476,7 @@ class Item_num_op :public Item_func_numhybrid print_op(str, query_type); } - void find_num_type(); + void fix_length_and_dec(); }; @@ -698,7 +692,6 @@ public: const char *func_name() const { return "-"; } enum Functype functype() const { return NEG_FUNC; } void fix_length_and_dec(); - void fix_num_length_and_dec(); uint decimal_precision() const { return args[0]->decimal_precision(); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} @@ -857,8 +850,7 @@ class Item_func_int_val :public Item_func_num1 { public: Item_func_int_val(Item *a) :Item_func_num1(a) {} - void fix_num_length_and_dec(); - void find_num_type(); + void fix_length_and_dec(); }; @@ -1267,6 +1259,7 @@ public: fixed= 1; return res; } + void fix_num_length_and_dec(); void update_used_tables() { /* @@ -1380,7 +1373,7 @@ public: my_decimal *val_decimal(my_decimal *); String *val_str(String *str); enum Item_result result_type () const { return DECIMAL_RESULT; } - void fix_length_and_dec(); + void fix_length_and_dec() { fix_num_length_and_dec(); } }; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 7d865df8fea..1e8d876ec94 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -365,16 +365,15 @@ protected: public: Item_func_seconds_hybrid() :Item_func_numhybrid() {} Item_func_seconds_hybrid(Item *a) :Item_func_numhybrid(a) {} - void fix_num_length_and_dec() + void fix_length_and_dec() { if (arg_count) decimals= args[0]->temporal_precision(arg0_expected_type()); set_if_smaller(decimals, TIME_SECOND_PART_DIGITS); max_length=17 + (decimals ? decimals + 1 : 0); maybe_null= true; + cached_result_type= decimals ? DECIMAL_RESULT : INT_RESULT; } - void find_num_type() - { cached_result_type= decimals ? DECIMAL_RESULT : INT_RESULT; } double real_op() { DBUG_ASSERT(0); return 0; } String *str_op(String *str) { DBUG_ASSERT(0); return 0; } bool date_op(MYSQL_TIME *ltime, uint fuzzydate) { DBUG_ASSERT(0); return true; } @@ -421,11 +420,6 @@ protected: public: Item_func_time_to_sec(Item *item) :Item_func_seconds_hybrid(item) {} const char *func_name() const { return "time_to_sec"; } - void fix_num_length_and_dec() - { - maybe_null= true; - Item_func_seconds_hybrid::fix_num_length_and_dec(); - } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} bool check_valid_arguments_processor(uchar *int_arg) -- cgit v1.2.1 From c1c6f6f161ce5f87dc78e30ce7eee615ed2b935c Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 29 Jul 2014 12:05:58 +0200 Subject: MDEV-5924 MariaDB could crash after changing the query_cache size with SET GLOBAL * remove incorrect assertion (it didn't take into account concurrent clients) * fix the comment --- sql/sql_cache.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index d558572f3a2..e6a795ba56e 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1669,7 +1669,7 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length) DBUG_ENTER("Query_cache::send_result_to_client"); /* - Testing 'query_cache_size' without a lock here is safe: the thing + Testing without a lock here is safe: the thing we may loose is that the query won't be served from cache, but we save on mutex locking in the case when query cache is disabled. @@ -1689,8 +1689,6 @@ Query_cache::send_result_to_client(THD *thd, char *org_sql, uint query_length) goto err; } - DBUG_ASSERT(query_cache_size != 0); // otherwise cache would be disabled - thd->query_cache_is_applicable= 1; sql= org_sql; sql_end= sql + query_length; -- cgit v1.2.1 From a270e8abc4344654aaac1a6881cbb169165a3cf0 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 31 Jul 2014 10:11:10 +0300 Subject: MDEV-6441: memory leak mysql_derived_prepare() was executed on the statement memory. Now it is executed on the runtime memory. All bugs induced by this were fixed. --- sql/sql_derived.cc | 6 +----- sql/sql_lex.cc | 2 +- sql/table.cc | 3 ++- 3 files changed, 4 insertions(+), 7 deletions(-) (limited to 'sql') diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index be258c16356..004cccb41a9 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -614,6 +614,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) SELECT_LEX_UNIT *unit= derived->get_unit(); DBUG_ENTER("mysql_derived_prepare"); bool res= FALSE; + DBUG_PRINT("enter", ("unit 0x%lx", (ulong) unit)); // Skip already prepared views/DT if (!unit || unit->prepared || @@ -623,9 +624,6 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) thd->lex->sql_command == SQLCOM_DELETE_MULTI)))) DBUG_RETURN(FALSE); - Query_arena *arena, backup; - arena= thd->activate_stmt_arena_if_needed(&backup); - SELECT_LEX *first_select= unit->first_select(); /* prevent name resolving out of derived table */ @@ -743,8 +741,6 @@ exit: if (derived->outer_join) table->maybe_null= 1; } - if (arena) - thd->restore_active_arena(arena, &backup); DBUG_RETURN(res); } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 8dab2191224..b9f84ac542d 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3298,7 +3298,7 @@ static void fix_prepare_info_in_table_list(THD *thd, TABLE_LIST *tbl) { for (; tbl; tbl= tbl->next_local) { - if (tbl->on_expr) + if (tbl->on_expr && !tbl->prep_on_expr) { thd->check_and_register_item_tree(&tbl->prep_on_expr, &tbl->on_expr); tbl->on_expr= tbl->on_expr->copy_andor_structure(thd); diff --git a/sql/table.cc b/sql/table.cc index c431f8dcc02..aa63f79c9bd 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -4140,7 +4140,8 @@ bool TABLE_LIST::create_field_translation(THD *thd) while ((item= it++)) { - transl[field_count].name= item->name; + DBUG_ASSERT(item->name && item->name[0]); + transl[field_count].name= thd->strdup(item->name); transl[field_count++].item= item; } field_translation= transl; -- cgit v1.2.1 From 50e192a04f1e0d6cbc98d5935e775e619b0fbff1 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 3 Aug 2014 12:45:14 +0200 Subject: Bug#17638477 UNINSTALL AND INSTALL SEMI-SYNC PLUGIN CAUSES SLAVES TO BREAK Fix the bug properly (plugin cannot be unloaded as long as it's locked). Enable and fix the test case. Significantly reduce number of LOCK_plugin locks for semisync (practically all locks were removed) --- sql/replication.h | 14 ++++++++++ sql/rpl_handler.cc | 79 +++++++++++++----------------------------------------- sql/rpl_handler.h | 5 +--- sql/slave.cc | 3 ++- 4 files changed, 35 insertions(+), 66 deletions(-) (limited to 'sql') diff --git a/sql/replication.h b/sql/replication.h index 8027c4830ec..9492c54fabd 100644 --- a/sql/replication.h +++ b/sql/replication.h @@ -16,6 +16,20 @@ #ifndef REPLICATION_H #define REPLICATION_H +/*************************************************************************** + NOTE: plugin locking. + This API was created specifically for the semisync plugin and its locking + logic is also matches semisync plugin usage pattern. In particular, a plugin + is locked on Binlog_transmit_observer::transmit_start and is unlocked after + Binlog_transmit_observer::transmit_stop. All other master observable events + happen between these two and don't lock the plugin at all. This works well + for the semisync_master plugin. + + Also a plugin is locked on Binlog_relay_IO_observer::thread_start + and unlocked after Binlog_relay_IO_observer::thread_stop. This works well for + the semisync_slave plugin. +***************************************************************************/ + #include typedef struct st_mysql MYSQL; diff --git a/sql/rpl_handler.cc b/sql/rpl_handler.cc index 9267190605c..9480a9e0454 100644 --- a/sql/rpl_handler.cc +++ b/sql/rpl_handler.cc @@ -170,40 +170,16 @@ void delegates_destroy() /* This macro is used by almost all the Delegate methods to iterate over all the observers running given callback function of the - delegate . - - Add observer plugins to the thd->lex list, after each statement, all - plugins add to thd->lex will be automatically unlocked. + delegate. */ -#define FOREACH_OBSERVER(r, f, thd, args) \ +#define FOREACH_OBSERVER(r, f, do_lock, args) \ param.server_id= thd->server_id; \ - /* - Use a struct to make sure that they are allocated adjacent, check - delete_dynamic(). - */ \ - struct { \ - DYNAMIC_ARRAY plugins; \ - /* preallocate 8 slots */ \ - plugin_ref plugins_buffer[8]; \ - } s; \ - DYNAMIC_ARRAY *plugins= &s.plugins; \ - plugin_ref *plugins_buffer= s.plugins_buffer; \ - my_init_dynamic_array2(plugins, sizeof(plugin_ref), \ - plugins_buffer, 8, 8); \ read_lock(); \ Observer_info_iterator iter= observer_info_iter(); \ Observer_info *info= iter++; \ for (; info; info= iter++) \ { \ - plugin_ref plugin= \ - my_plugin_lock(0, info->plugin); \ - if (!plugin) \ - { \ - /* plugin is not intialized or deleted, this is not an error */ \ - r= 0; \ - break; \ - } \ - insert_dynamic(plugins, (uchar *)&plugin); \ + if (do_lock) plugin_lock(thd, plugin_int_to_ref(info->plugin_int)); \ if (((Observer *)info->observer)->f \ && ((Observer *)info->observer)->f args) \ { \ @@ -213,17 +189,7 @@ void delegates_destroy() break; \ } \ } \ - unlock(); \ - /* - Unlock plugins should be done after we released the Delegate lock - to avoid possible deadlock when this is the last user of the - plugin, and when we unlock the plugin, it will try to - deinitialize the plugin, which will try to lock the Delegate in - order to remove the observers. - */ \ - plugin_unlock_list(0, (plugin_ref*)plugins->buffer, \ - plugins->elements); \ - delete_dynamic(plugins) + unlock(); int Trans_delegate::after_commit(THD *thd, bool all) @@ -240,7 +206,7 @@ int Trans_delegate::after_commit(THD *thd, bool all) param.log_pos= log_info ? log_info->log_pos : 0; int ret= 0; - FOREACH_OBSERVER(ret, after_commit, thd, (¶m)); + FOREACH_OBSERVER(ret, after_commit, false, (¶m)); /* This is the end of a real transaction or autocommit statement, we @@ -268,7 +234,7 @@ int Trans_delegate::after_rollback(THD *thd, bool all) param.log_pos= log_info ? log_info->log_pos : 0; int ret= 0; - FOREACH_OBSERVER(ret, after_rollback, thd, (¶m)); + FOREACH_OBSERVER(ret, after_rollback, false, (¶m)); /* This is the end of a real transaction or autocommit statement, we @@ -307,7 +273,7 @@ int Binlog_storage_delegate::after_flush(THD *thd, log_info->log_pos = log_pos; int ret= 0; - FOREACH_OBSERVER(ret, after_flush, thd, + FOREACH_OBSERVER(ret, after_flush, false, (¶m, log_info->log_file, log_info->log_pos, flags)); return ret; } @@ -321,7 +287,7 @@ int Binlog_transmit_delegate::transmit_start(THD *thd, ushort flags, param.flags= flags; int ret= 0; - FOREACH_OBSERVER(ret, transmit_start, thd, (¶m, log_file, log_pos)); + FOREACH_OBSERVER(ret, transmit_start, true, (¶m, log_file, log_pos)); return ret; } @@ -331,7 +297,7 @@ int Binlog_transmit_delegate::transmit_stop(THD *thd, ushort flags) param.flags= flags; int ret= 0; - FOREACH_OBSERVER(ret, transmit_stop, thd, (¶m)); + FOREACH_OBSERVER(ret, transmit_stop, false, (¶m)); return ret; } @@ -356,13 +322,6 @@ int Binlog_transmit_delegate::reserve_header(THD *thd, ushort flags, Observer_info *info= iter++; for (; info; info= iter++) { - plugin_ref plugin= - my_plugin_lock(thd, info->plugin); - if (!plugin) - { - ret= 1; - break; - } hlen= 0; if (((Observer *)info->observer)->reserve_header && ((Observer *)info->observer)->reserve_header(¶m, @@ -371,10 +330,8 @@ int Binlog_transmit_delegate::reserve_header(THD *thd, ushort flags, &hlen)) { ret= 1; - plugin_unlock(thd, plugin); break; } - plugin_unlock(thd, plugin); if (hlen == 0) continue; if (hlen > RESERVE_HEADER_SIZE || packet->append((char *)header, hlen)) @@ -396,7 +353,7 @@ int Binlog_transmit_delegate::before_send_event(THD *thd, ushort flags, param.flags= flags; int ret= 0; - FOREACH_OBSERVER(ret, before_send_event, thd, + FOREACH_OBSERVER(ret, before_send_event, false, (¶m, (uchar *)packet->c_ptr(), packet->length(), log_file+dirname_length(log_file), log_pos)); @@ -410,7 +367,7 @@ int Binlog_transmit_delegate::after_send_event(THD *thd, ushort flags, param.flags= flags; int ret= 0; - FOREACH_OBSERVER(ret, after_send_event, thd, + FOREACH_OBSERVER(ret, after_send_event, false, (¶m, packet->c_ptr(), packet->length())); return ret; } @@ -422,7 +379,7 @@ int Binlog_transmit_delegate::after_reset_master(THD *thd, ushort flags) param.flags= flags; int ret= 0; - FOREACH_OBSERVER(ret, after_reset_master, thd, (¶m)); + FOREACH_OBSERVER(ret, after_reset_master, false, (¶m)); return ret; } @@ -443,7 +400,7 @@ int Binlog_relay_IO_delegate::thread_start(THD *thd, Master_info *mi) init_param(¶m, mi); int ret= 0; - FOREACH_OBSERVER(ret, thread_start, thd, (¶m)); + FOREACH_OBSERVER(ret, thread_start, true, (¶m)); return ret; } @@ -455,7 +412,7 @@ int Binlog_relay_IO_delegate::thread_stop(THD *thd, Master_info *mi) init_param(¶m, mi); int ret= 0; - FOREACH_OBSERVER(ret, thread_stop, thd, (¶m)); + FOREACH_OBSERVER(ret, thread_stop, false, (¶m)); return ret; } @@ -467,7 +424,7 @@ int Binlog_relay_IO_delegate::before_request_transmit(THD *thd, init_param(¶m, mi); int ret= 0; - FOREACH_OBSERVER(ret, before_request_transmit, thd, (¶m, (uint32)flags)); + FOREACH_OBSERVER(ret, before_request_transmit, false, (¶m, (uint32)flags)); return ret; } @@ -480,7 +437,7 @@ int Binlog_relay_IO_delegate::after_read_event(THD *thd, Master_info *mi, init_param(¶m, mi); int ret= 0; - FOREACH_OBSERVER(ret, after_read_event, thd, + FOREACH_OBSERVER(ret, after_read_event, false, (¶m, packet, len, event_buf, event_len)); return ret; } @@ -498,7 +455,7 @@ int Binlog_relay_IO_delegate::after_queue_event(THD *thd, Master_info *mi, flags |= BINLOG_STORAGE_IS_SYNCED; int ret= 0; - FOREACH_OBSERVER(ret, after_queue_event, thd, + FOREACH_OBSERVER(ret, after_queue_event, false, (¶m, event_buf, event_len, flags)); return ret; } @@ -510,7 +467,7 @@ int Binlog_relay_IO_delegate::after_reset_slave(THD *thd, Master_info *mi) init_param(¶m, mi); int ret= 0; - FOREACH_OBSERVER(ret, after_reset_slave, thd, (¶m)); + FOREACH_OBSERVER(ret, after_reset_slave, false, (¶m)); return ret; } #endif /* HAVE_REPLICATION */ diff --git a/sql/rpl_handler.h b/sql/rpl_handler.h index 4743fffb9a0..362f3c74a4b 100644 --- a/sql/rpl_handler.h +++ b/sql/rpl_handler.h @@ -26,13 +26,10 @@ class Observer_info { public: void *observer; st_plugin_int *plugin_int; - plugin_ref plugin; Observer_info(void *ob, st_plugin_int *p) :observer(ob), plugin_int(p) - { - plugin= plugin_int_to_ref(plugin_int); - } + { } }; class Delegate { diff --git a/sql/slave.cc b/sql/slave.cc index 78f152fd2fd..f224de2e1ff 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -3388,9 +3388,10 @@ err_during_init: DBUG_ASSERT(thd->net.buff != 0); net_end(&thd->net); // destructor will not free it, because net.vio is 0 mysql_mutex_lock(&LOCK_thread_count); + thd->unlink(); + mysql_mutex_unlock(&LOCK_thread_count); THD_CHECK_SENTRY(thd); delete thd; - mysql_mutex_unlock(&LOCK_thread_count); mi->abort_slave= 0; mi->slave_running= 0; mi->io_thd= 0; -- cgit v1.2.1