From deb3b9a17498d101468fc12a633245fc74730133 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Wed, 25 May 2011 16:01:56 -0700 Subject: Downported InnoDB support of Index Condition Pushdown from MySQL-5.6 code line. --- sql/opt_index_cond_pushdown.cc | 65 ++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 27 deletions(-) (limited to 'sql') diff --git a/sql/opt_index_cond_pushdown.cc b/sql/opt_index_cond_pushdown.cc index e0a2d3b1f30..639ceedf693 100644 --- a/sql/opt_index_cond_pushdown.cc +++ b/sql/opt_index_cond_pushdown.cc @@ -83,15 +83,44 @@ bool uses_index_fields_only(Item *item, TABLE *tbl, uint keyno, case Item::FIELD_ITEM: { Item_field *item_field= (Item_field*)item; - if (item_field->field->table != tbl) + Field *field= item_field->field; + if (field->table != tbl) return TRUE; /* The below is probably a repetition - the first part checks the other two, but let's play it safe: */ - return item_field->field->part_of_key.is_set(keyno) && - item_field->field->type() != MYSQL_TYPE_GEOMETRY && - item_field->field->type() != MYSQL_TYPE_BLOB; + if(!field->part_of_key.is_set(keyno) || + field->type() == MYSQL_TYPE_GEOMETRY || + field->type() == MYSQL_TYPE_BLOB) + return FALSE; + KEY *key_info= tbl->key_info + keyno; + KEY_PART_INFO *key_part= key_info->key_part; + KEY_PART_INFO *key_part_end= key_part + key_info->key_parts; + for ( ; key_part < key_part_end; key_part++) + { + if (field->eq(key_part->field)) + return !(key_part->key_part_flag & HA_PART_KEY_SEG); + } + if ((tbl->file->ha_table_flags() & HA_PRIMARY_KEY_IN_READ_INDEX) && + tbl->s->primary_key != MAX_KEY && + tbl->s->primary_key != keyno) + { + key_info= tbl->key_info + tbl->s->primary_key; + key_part= key_info->key_part; + key_part_end= key_part + key_info->key_parts; + for ( ; key_part < key_part_end; key_part++) + { + /* + It does not make sense to use the fact that the engine can read in + a full field if the key if the index is built only over a part + of this field. + */ + if (field->eq(key_part->field)) + return !(key_part->key_part_flag & HA_PART_KEY_SEG); + } + } + return FALSE; } case Item::REF_ITEM: return uses_index_fields_only(item->real_item(), tbl, keyno, @@ -288,30 +317,12 @@ void push_index_cond(JOIN_TAB *tab, uint keyno) { DBUG_ENTER("push_index_cond"); Item *idx_cond; - bool do_index_cond_pushdown= - ((tab->table->file->index_flags(keyno, 0, 1) & - HA_DO_INDEX_COND_PUSHDOWN) && - optimizer_flag(tab->join->thd, OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN)); - - /* - Do not try index condition pushdown on indexes which have partially-covered - columns. Unpacking from a column prefix into index tuple is not a supported - operation in some engines, see e.g. MySQL BUG#42991. - TODO: a better solution would be not to consider partially-covered columns - as parts of the index and still produce/check index condition for - fully-covered index columns. - */ - KEY *key_info= tab->table->key_info + keyno; - for (uint kp= 0; kp < key_info->key_parts; kp++) - { - if ((key_info->key_part[kp].key_part_flag & HA_PART_KEY_SEG)) - { - do_index_cond_pushdown= FALSE; - break; - } - } - if (do_index_cond_pushdown) + if ((tab->table->file->index_flags(keyno, 0, 1) & + HA_DO_INDEX_COND_PUSHDOWN) && + optimizer_flag(tab->join->thd, OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN) && + tab->join->thd->lex->sql_command != SQLCOM_UPDATE_MULTI && + tab->join->thd->lex->sql_command != SQLCOM_DELETE_MULTI) { DBUG_EXECUTE("where", print_where(tab->select_cond, "full cond", QT_ORDINARY);); -- cgit v1.2.1 From d86ad1861a57558e63dc68bf372eff6bdb22cb6d Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 27 May 2011 15:20:19 -0700 Subject: Backported the test case for bug 43617 fixed by the patch for bug 42580. Backported the test case for bug 49906 fixed by the patch for LP bug 625841. Slightly optimized the code of the fix for LP bug 625841. --- sql/filesort.cc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'sql') diff --git a/sql/filesort.cc b/sql/filesort.cc index 6e3bf27afcc..b9cda236b0c 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -547,12 +547,11 @@ 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 (select && select->cond) - select->cond->walk(&Item::register_field_in_read_map, 1, - (uchar*) sort_form); - if (select && select->pre_idx_push_select_cond) - select->pre_idx_push_select_cond->walk(&Item::register_field_in_read_map, - 1, (uchar*) sort_form); + Item *sort_cond= !select ? + 0 : !select->pre_idx_push_select_cond ? + select->cond : select->pre_idx_push_select_cond; + if (sort_cond) + sort_cond->walk(&Item::register_field_in_read_map, 1, (uchar*) sort_form); sort_form->column_bitmaps_set(&sort_form->tmp_set, &sort_form->tmp_set, &sort_form->tmp_set); -- cgit v1.2.1 From adc1f2f4c939c15ad5efd37633332560456cd4fd Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 2 Jun 2011 14:03:02 -0700 Subject: Applied the patch for bug 58837 (for the mysql-5.6 code line). This patch fixed the crash in innodb_bug59307. --- sql/handler.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sql') diff --git a/sql/handler.h b/sql/handler.h index 4707aabbd52..96cea68e1fb 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1716,6 +1716,7 @@ public: DBUG_ENTER("ha_rnd_init"); DBUG_ASSERT(inited==NONE || (inited==RND && scan)); inited= (result= rnd_init(scan)) ? NONE: RND; + end_range= NULL; DBUG_RETURN(result); } int ha_rnd_end() @@ -1723,6 +1724,7 @@ public: DBUG_ENTER("ha_rnd_end"); DBUG_ASSERT(inited==RND); inited=NONE; + end_range= NULL; DBUG_RETURN(rnd_end()); } int ha_rnd_init_with_error(bool scan) __attribute__ ((warn_unused_result)); -- cgit v1.2.1 From be03fe9c29b1622ea3d1ee6f47c5826d09529a61 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Mon, 15 Aug 2011 23:18:36 -0700 Subject: Fixed LP bug #824463. When merging a view / derived table the function SELECT_LEX::merge_subquery incorrectly updated the list SELECT_LEX::leaf_tables. Erroneously it appended the leaf_tables list of the merged object L and then removed the reference to the merged object T from the SELECT_LEX::leaf_tables list. A correct implementation should insert the list L into the SELECT_LEX::leaf_tables list in place of the element of the list that refers to T. The bug could lead to wrong results or even crashes for queries with nested outer joins over views / derived tables. --- sql/sql_lex.cc | 25 ++++++++++--------------- sql/sql_lex.h | 2 +- 2 files changed, 11 insertions(+), 16 deletions(-) (limited to 'sql') diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 021e7a3b5e8..acc06a74327 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3220,18 +3220,19 @@ void st_select_lex::append_table_to_list(TABLE_LIST *TABLE_LIST::*link, tl->*link= table; } + /* @brief - Remove given table from the leaf_tables list. + Replace given table from the leaf_tables list for a list of tables - @param link Offset to which list in table structure to use - @param table Table to remove + @param table Table to replace + @param list List to substititute the table for @details - Remove 'table' from the leaf_tables list using the 'link' offset. + Replace 'table' from the leaf_tables list for a list of tables 'tbl_list'. */ -void st_select_lex::remove_table_from_list(TABLE_LIST *table) +void st_select_lex::replace_leaf_table(TABLE_LIST *table, List &tbl_list) { TABLE_LIST *tl; List_iterator ti(leaf_tables); @@ -3239,7 +3240,7 @@ void st_select_lex::remove_table_from_list(TABLE_LIST *table) { if (tl == table) { - ti.remove(); + ti.replace(tbl_list); break; } } @@ -3344,8 +3345,6 @@ bool SELECT_LEX::merge_subquery(THD *thd, TABLE_LIST *derived, uint table_no, table_map map) { derived->wrap_into_nested_join(subq_select->top_join_list); - /* Reconnect the next_leaf chain. */ - leaf_tables.concat(&subq_select->leaf_tables); ftfunc_list->concat(subq_select->ftfunc_list); if (join || @@ -3361,18 +3360,14 @@ bool SELECT_LEX::merge_subquery(THD *thd, TABLE_LIST *derived, in_subq->emb_on_expr_nest= derived; } } - /* - Remove merged table from chain. - When merge_subquery is called at a subquery-to-semijoin transformation - the derived isn't in the leaf_tables list, so in this case the call of - remove_table_from_list does not cause any actions. - */ - remove_table_from_list(derived); /* Walk through child's tables and adjust table map, tablenr, * parent_lex */ subq_select->remap_tables(derived, map, table_no, this); subq_select->merged_into= this; + + replace_leaf_table(derived, subq_select->leaf_tables); + return FALSE; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index fe79e6e2908..0da628b7ca0 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -888,7 +888,7 @@ public: bool handle_derived(struct st_lex *lex, uint phases); void append_table_to_list(TABLE_LIST *TABLE_LIST::*link, TABLE_LIST *table); bool get_free_table_map(table_map *map, uint *tablenr); - void remove_table_from_list(TABLE_LIST *table); + void replace_leaf_table(TABLE_LIST *table, List &tbl_list); void remap_tables(TABLE_LIST *derived, table_map map, uint tablenr, st_select_lex *parent_lex); bool merge_subquery(THD *thd, TABLE_LIST *derived, st_select_lex *subq_lex, -- cgit v1.2.1 From 9e60b55fd0ff37527ca83e590d1c5386dfa788d6 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 Aug 2011 00:00:13 +0300 Subject: Fix bug lp:825095 Analysis: Partial matching is used even when there are no NULLs in a materialized subquery, as long as the left NOT IN operand may contain NULL values. This case was not handled correctly in two different places. First, the implementation of parital matching did not clear the set of matching columns when the merge process advanced to the next row. Second, there is no need to perform partial matching at all when the left operand has no NULLs. Solution: First fix subselect_rowid_merge_engine::partial_match() to properly cleanup the bitmap of matching keys when advancing to the next row. Second, change subselect_partial_match_engine::exec() so that when the materialized subquery doesn't contain any NULLs, and the left operand of [NOT] IN doesn't contain NULLs either, the method returns without doing any unnecessary partial matching. The correct result in this case is in Item::in_value. --- sql/item_subselect.cc | 36 ++++++++++++++++++++++++------------ sql/item_subselect.h | 14 ++++++++++---- 2 files changed, 34 insertions(+), 16 deletions(-) (limited to 'sql') diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 12f6c26b57f..204279115da 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -3898,6 +3898,8 @@ subselect_hash_sj_engine::get_strategy_using_data() } if (result_sink->get_null_count_of_col(i) == tmp_table->file->stats.records) ++count_null_only_columns; + if (result_sink->get_null_count_of_col(i)) + ++count_columns_with_nulls; } /* If no column contains NULLs use regular hash index lookups. */ @@ -4659,6 +4661,7 @@ int subselect_hash_sj_engine::exec() count_pm_keys, has_covering_null_row, has_covering_null_columns, + count_columns_with_nulls, item, result, semi_join_conds->argument_list()); if (!pm_engine || @@ -4684,7 +4687,8 @@ int subselect_hash_sj_engine::exec() item, result, semi_join_conds->argument_list(), has_covering_null_row, - has_covering_null_columns))) + has_covering_null_columns, + count_columns_with_nulls))) { /* This is an irrecoverable error. */ res= 1; @@ -5121,43 +5125,48 @@ subselect_partial_match_engine::subselect_partial_match_engine( select_result_interceptor *result_arg, List *equi_join_conds_arg, bool has_covering_null_row_arg, - bool has_covering_null_columns_arg) + bool has_covering_null_columns_arg, + uint count_columns_with_nulls_arg) :subselect_engine(thd_arg, item_arg, result_arg), tmp_table(tmp_table_arg), lookup_engine(engine_arg), equi_join_conds(equi_join_conds_arg), has_covering_null_row(has_covering_null_row_arg), - has_covering_null_columns(has_covering_null_columns_arg) + has_covering_null_columns(has_covering_null_columns_arg), + count_columns_with_nulls(count_columns_with_nulls_arg) {} int subselect_partial_match_engine::exec() { Item_in_subselect *item_in= (Item_in_subselect *) item; - int res; + int copy_res, lookup_res; /* Try to find a matching row by index lookup. */ - res= lookup_engine->copy_ref_key_simple(); - if (res == -1) + copy_res= lookup_engine->copy_ref_key_simple(); + if (copy_res == -1) { /* The result is FALSE based on the outer reference. */ item_in->value= 0; item_in->null_value= 0; return 0; } - else if (res == 0) + else if (copy_res == 0) { /* Search for a complete match. */ - if ((res= lookup_engine->index_lookup())) + if ((lookup_res= lookup_engine->index_lookup())) { /* An error occured during lookup(). */ item_in->value= 0; item_in->null_value= 0; - return res; + return lookup_res; } - else if (item_in->value) + else if (item_in->value || !count_columns_with_nulls) { /* A complete match was found, the result of IN is TRUE. + If no match was found, and there are no NULLs in the materialized + subquery, then the result is guaranteed to be false because this + branch is executed when the outer reference has no NULLs as well. Notice: (this->item == lookup_engine->item) */ return 0; @@ -5608,6 +5617,7 @@ bool subselect_rowid_merge_engine::partial_match() { min_key= cur_key; min_row_num= cur_row_num; + bitmap_clear_all(&matching_keys); bitmap_set_bit(&matching_keys, min_key->get_keyid()); bitmap_union(&matching_keys, &matching_outer_cols); } @@ -5641,11 +5651,13 @@ subselect_table_scan_engine::subselect_table_scan_engine( select_result_interceptor *result_arg, List *equi_join_conds_arg, bool has_covering_null_row_arg, - bool has_covering_null_columns_arg) + bool has_covering_null_columns_arg, + uint count_columns_with_nulls_arg) :subselect_partial_match_engine(thd_arg, engine_arg, tmp_table_arg, item_arg, result_arg, equi_join_conds_arg, has_covering_null_row_arg, - has_covering_null_columns_arg) + has_covering_null_columns_arg, + count_columns_with_nulls_arg) {} diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 2c1e3bddb2d..f9e7a5ef568 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -870,7 +870,7 @@ public: tmp_table(NULL), is_materialized(FALSE), materialize_engine(old_engine), materialize_join(NULL), semi_join_conds(NULL), lookup_engine(NULL), count_partial_match_columns(0), count_null_only_columns(0), - strategy(UNDEFINED) + count_columns_with_nulls(0), strategy(UNDEFINED) {} ~subselect_hash_sj_engine(); @@ -908,6 +908,7 @@ protected: MY_BITMAP partial_match_key_parts; uint count_partial_match_columns; uint count_null_only_columns; + uint count_columns_with_nulls; /* Possible execution strategies that can be used to compute hash semi-join.*/ enum exec_strategy { UNDEFINED, @@ -1145,6 +1146,7 @@ protected: guaranteed partial match. */ bool has_covering_null_columns; + uint count_columns_with_nulls; protected: virtual bool partial_match()= 0; @@ -1155,7 +1157,8 @@ public: select_result_interceptor *result_arg, List *equi_join_conds_arg, bool has_covering_null_row_arg, - bool has_covering_null_columns_arg); + bool has_covering_null_columns_arg, + uint count_columns_with_nulls_arg); int prepare() { return 0; } int exec(); void fix_length_and_dec(Item_cache**) {} @@ -1245,13 +1248,15 @@ public: TABLE *tmp_table_arg, uint merge_keys_count_arg, bool has_covering_null_row_arg, bool has_covering_null_columns_arg, + uint count_columns_with_nulls_arg, Item_subselect *item_arg, select_result_interceptor *result_arg, List *equi_join_conds_arg) :subselect_partial_match_engine(thd_arg, engine_arg, tmp_table_arg, item_arg, result_arg, equi_join_conds_arg, has_covering_null_row_arg, - has_covering_null_columns_arg), + has_covering_null_columns_arg, + count_columns_with_nulls_arg), merge_keys_count(merge_keys_count_arg), non_null_key(NULL) {} ~subselect_rowid_merge_engine(); @@ -1272,7 +1277,8 @@ public: select_result_interceptor *result_arg, List *equi_join_conds_arg, bool has_covering_null_row_arg, - bool has_covering_null_columns_arg); + bool has_covering_null_columns_arg, + uint count_columns_with_nulls_arg); void cleanup(); virtual enum_engine_type engine_type() { return TABLE_SCAN_ENGINE; } }; -- cgit v1.2.1 From c1a6dbe5b26946fd40a73dfd4b72c3f6f6f6ae5f Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 Aug 2011 15:39:15 +0300 Subject: Fixed bug lp:825018 Analysis: During the first execution of the query through the stored procedure, the optimization phase calls substitute_for_best_equal_field(), which calls Item_in_optimizer::transform(). The latter replaces Item_in_subselect::left_expr with args[0] via assignment. In this test case args[0] is an Item_outer_ref which is created/deallocated for each re-execution. As a result, during the second execution Item_in_subselect::left_expr pointed to freed memory, which resulted in a crash. Solution: The solution is to use change_item_tree(), so that the origianal left expression is restored after each execution. --- sql/item_cmpfunc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 446d5f18c7f..f06a2f627e9 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1804,7 +1804,7 @@ Item *Item_in_optimizer::transform(Item_transformer transformer, uchar *argument Item_subselect::ANY_SUBS)); Item_in_subselect *in_arg= (Item_in_subselect*)args[1]; - in_arg->left_expr= args[0]; + current_thd->change_item_tree(&in_arg->left_expr, args[0]); } return (this->*transformer)(argument); } -- cgit v1.2.1 From 2df1914791030714196c3d829187891a97be54dc Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 27 Aug 2011 00:40:29 +0300 Subject: Fix bug lp:827416 Analysis: Constant table optimization of the outer query finds that the right side of the equality is a constant that can be used for an eq_ref access to fetch one row from t1, and substitute t1 with a constant. Thus constant optimization triggers evaluation of the subquery during the optimize phase of the outer query. The innermost subquery requires a plan with a temporary table because with InnoDB tables the exact count of rows is not known, and the empty tables cannot be optimzied way. JOIN::exec for the innermost subquery substitutes the subquery tables with a temporary table. When EXPLAIN gets to print the tables in the innermost subquery, EXPLAIN needs to print the name of each table through the corresponding TABLE_LIST object. However, the temporary table created during execution doesn't have a corresponding TABLE_LIST, so we get a null pointer exception. Solution: The solution is to forbid using expensive constant expressions for eq_ref access for contant table optimization. Notice that eq_ref with a subquery providing the value is still possible during regular execution. --- sql/sql_select.cc | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 6d2c7400d3d..54c1215164e 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2993,6 +2993,7 @@ make_join_statistics(JOIN *join, List &tables_list, uint i,table_count,const_count,key; table_map found_const_table_map, all_table_map, found_ref, refs; key_map const_ref, eq_part; + bool has_expensive_keyparts; TABLE **table_vector; JOIN_TAB *stat,*stat_end,*s,**stat_ref; KEYUSE *keyuse,*start_keyuse; @@ -3314,12 +3315,17 @@ make_join_statistics(JOIN *join, List &tables_list, refs=0; const_ref.clear_all(); eq_part.clear_all(); + has_expensive_keyparts= false; do { if (keyuse->val->type() != Item::NULL_ITEM && !keyuse->optimize) { if (!((~found_const_table_map) & keyuse->used_tables)) + { const_ref.set_bit(keyuse->keypart); + if (keyuse->val->is_expensive()) + has_expensive_keyparts= true; + } else refs|=keyuse->used_tables; eq_part.set_bit(keyuse->keypart); @@ -3341,6 +3347,7 @@ make_join_statistics(JOIN *join, List &tables_list, if (table->key_info[key].flags & HA_NOSAME) { if (const_ref == eq_part && + !has_expensive_keyparts && !((outer_join & table->map) && (*s->on_expr_ref)->is_expensive())) { // Found everything for ref. -- cgit v1.2.1 From 282c303746d0653a927b0fcd16ecdc9b690bbb00 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Mon, 29 Aug 2011 18:14:14 +0300 Subject: Added MariaDB executable comment syntax: /*M!##### */ client/mysql.cc: Don't remove MariaDB executable comments. mysql-test/r/comments.result: Test MariaDB executable comments. mysql-test/r/mysql.result: Test MariaDB executable comments. mysql-test/t/comments.test: Test MariaDB executable comments. mysql-test/t/mysql.test: Test MariaDB executable comments. sql/sql_cache.cc: Don't delete MariaDB executable comments. sql/sql_lex.cc: Handle MariaDB executable comments --- sql/sql_cache.cc | 5 +++-- sql/sql_lex.cc | 34 +++++++++++++++++----------------- 2 files changed, 20 insertions(+), 19 deletions(-) (limited to 'sql') diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index d7040a825fb..5378390ebb4 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -491,11 +491,12 @@ static void make_base_query(String *new_query, continue; // Continue with next symbol case '/': // Start of comment ? /* - Comment of format /#!number #/, must be skipped. + Comment of format /#!number #/ or /#M!number #/, must be skipped. These may include '"' and other comments, but it should be safe to parse the content as a normal string. */ - if (query[0] != '*' || query[1] == '!') + if (query[0] != '*' || query[1] == '!' || + (query[1] == 'M' && query[2] == '!')) break; query++; // skip "/" diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index fff1504b969..af3dad0945d 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1272,39 +1272,39 @@ int MYSQLlex(void *arg, void *yythd) lip->save_in_comment_state(); + if (lip->yyPeekn(2) == 'M' && lip->yyPeekn(3) == '!') + { + /* Skip MariaDB unique marker */ + lip->set_echo(FALSE); + lip->yySkip(); + /* The following if will be true */ + } if (lip->yyPeekn(2) == '!') { lip->in_comment= DISCARD_COMMENT; /* Accept '/' '*' '!', but do not keep this marker. */ lip->set_echo(FALSE); - lip->yySkip(); - lip->yySkip(); - lip->yySkip(); + lip->yySkipn(3); /* The special comment format is very strict: - '/' '*' '!', followed by exactly + '/' '*' '!', followed by an optional 'M' and exactly 1 digit (major), 2 digits (minor), then 2 digits (dot). 32302 -> 3.23.02 50032 -> 5.0.32 50114 -> 5.1.14 */ - char version_str[6]; - version_str[0]= lip->yyPeekn(0); - version_str[1]= lip->yyPeekn(1); - version_str[2]= lip->yyPeekn(2); - version_str[3]= lip->yyPeekn(3); - version_str[4]= lip->yyPeekn(4); - version_str[5]= 0; - if ( my_isdigit(cs, version_str[0]) - && my_isdigit(cs, version_str[1]) - && my_isdigit(cs, version_str[2]) - && my_isdigit(cs, version_str[3]) - && my_isdigit(cs, version_str[4]) + if ( my_isdigit(cs, lip->yyPeekn(0)) + && my_isdigit(cs, lip->yyPeekn(1)) + && my_isdigit(cs, lip->yyPeekn(2)) + && my_isdigit(cs, lip->yyPeekn(3)) + && my_isdigit(cs, lip->yyPeekn(4)) ) { ulong version; - version=strtol(version_str, NULL, 10); + char *end_ptr= (char*) lip->get_ptr()+5; + int error; + version= (ulong) my_strtoll10(lip->get_ptr(), &end_ptr, &error); if (version <= MYSQL_VERSION_ID) { -- cgit v1.2.1 From 945a595aa36ccf90f89cf1ca8cddefe47b394dd3 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Mon, 29 Aug 2011 19:57:41 +0400 Subject: BUG#834534: Assertion `0' failed in replace_where_subcondition with semijoin subquery in HAVING - The problem was that the code that made the check whether the subquery is an AND-part of the WHERE clause didn't work correctly for nested subqueries. In particular, grand-child subquery in HAVING was treated as if it was in the WHERE, which eventually caused an assert when replace_where_subcondition looked for the subquery predicate in the WHERE and couldn't find it there. - The fix: Removed implementation of "thd_marker approach". thd->thd_marker was used to determine the location of subquery predicate: setup_conds() would set accordingly it when making the {where|on_expr}->fix_fields(...) call so that AND-parts of the WHERE/ON clauses can determine they are the AND-parts. Item_cond_or::fix_fields(), Item_func::fix_fields(), Item_subselect::fix_fields (this one was missed), and all other items-that-contain-items had to reset thd->thd_marker before calling fix_fields() for their children items, so that the children can see they are not AND-parts of WHERE/ON. - The "thd_marker approach" required that a lot of code in different locations maintains correct value of thd->thd_marker, so it was replaced with: - The new approach with mark_as_condition_AND_part does not keep context in thd->thd_marker. Instead, setup_conds() now calls {where|on_expr}->mark_as_condition_AND_part() and implementations of that function make sure that: - parts of AND-expressions get the mark_as_condition_AND_part() call - Item_in_subselect objects record that they are AND-parts of WHERE/ON --- sql/item.h | 2 ++ sql/item_cmpfunc.cc | 15 +++++++++++---- sql/item_cmpfunc.h | 1 + sql/item_func.cc | 3 --- sql/item_subselect.cc | 9 +++++---- sql/item_subselect.h | 14 ++++++++++---- sql/opt_subselect.cc | 6 ++---- sql/sql_base.cc | 7 ++----- sql/sql_class.h | 9 --------- sql/sql_parse.cc | 2 -- sql/sql_prepare.cc | 1 - 11 files changed, 33 insertions(+), 36 deletions(-) (limited to 'sql') diff --git a/sql/item.h b/sql/item.h index 319135ede9d..99eabdd81ab 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1292,6 +1292,8 @@ public: be defined for Item_func. */ virtual void get_cache_parameters(List ¶meters) { }; + + virtual void mark_as_condition_AND_part(TABLE_LIST *embedding) {}; }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index f06a2f627e9..3e699c30caa 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -4076,15 +4076,12 @@ Item_cond::fix_fields(THD *thd, Item **ref) DBUG_ASSERT(fixed == 0); List_iterator li(list); Item *item; - TABLE_LIST *save_emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest; #ifndef EMBEDDED_LIBRARY uchar buff[sizeof(char*)]; // Max local vars in function #endif not_null_tables_cache= used_tables_cache= 0; const_item_cache= 1; - if (functype() != COND_AND_FUNC) - thd->thd_marker.emb_on_expr_nest= NULL; /* and_table_cache is the value that Item_cond_or() returns for not_null_tables() @@ -4144,7 +4141,6 @@ Item_cond::fix_fields(THD *thd, Item **ref) maybe_null=1; } thd->lex->current_select->cond_count+= list.elements; - thd->thd_marker.emb_on_expr_nest= save_emb_on_expr_nest; fix_length_and_dec(); fixed= 1; return FALSE; @@ -4414,6 +4410,17 @@ void Item_cond::neg_arguments(THD *thd) } +void Item_cond_and::mark_as_condition_AND_part(TABLE_LIST *embedding) +{ + List_iterator li(list); + Item *item; + while ((item=li++)) + { + item->mark_as_condition_AND_part(embedding); + } +} + + /** Evaluation of AND(expr, expr, expr ...). diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index e70e7d80391..acbb7413459 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1816,6 +1816,7 @@ public: return item; } Item *neg_transformer(THD *thd); + void mark_as_condition_AND_part(TABLE_LIST *embedding); }; inline bool is_cond_and(Item *item) diff --git a/sql/item_func.cc b/sql/item_func.cc index 033537092d8..b907d1f432e 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -152,11 +152,9 @@ Item_func::fix_fields(THD *thd, Item **ref) { DBUG_ASSERT(fixed == 0); Item **arg,**arg_end; - TABLE_LIST *save_emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest; #ifndef EMBEDDED_LIBRARY // Avoid compiler warning uchar buff[STACK_BUFF_ALLOC]; // Max argument in function #endif - thd->thd_marker.emb_on_expr_nest= NULL; used_tables_cache= not_null_tables_cache= 0; const_item_cache=1; @@ -210,7 +208,6 @@ Item_func::fix_fields(THD *thd, Item **ref) if (thd->is_error()) // An error inside fix_length_and_dec occured return TRUE; fixed= 1; - thd->thd_marker.emb_on_expr_nest= save_emb_on_expr_nest; return FALSE; } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 204279115da..0c077e56394 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -229,7 +229,7 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) if (check_stack_overrun(thd, STACK_MIN_SIZE, (uchar*)&res)) return TRUE; - + if (!(res= engine->prepare())) { // all transformation is done (used by prepared statements) @@ -1165,8 +1165,9 @@ bool Item_in_subselect::test_limit(st_select_lex_unit *unit_arg) Item_in_subselect::Item_in_subselect(Item * left_exp, st_select_lex *select_lex): - Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE), - optimizer(0), pushed_cond_guards(NULL), in_strategy(0), + Item_exists_subselect(), + left_expr_cache(0), first_execution(TRUE), + optimizer(0), pushed_cond_guards(NULL), emb_on_expr_nest(NULL), in_strategy(0), is_jtbm_merged(FALSE), is_flattenable_semijoin(FALSE), is_registered_semijoin(FALSE), upper_item(0) @@ -2395,6 +2396,7 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref) uint outer_cols_num; List *inner_cols; + if (in_strategy & SUBS_SEMI_JOIN) return !( (*ref)= new Item_int(1)); @@ -2449,7 +2451,6 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref) return TRUE; if (Item_subselect::fix_fields(thd_arg, ref)) return TRUE; - fixed= TRUE; return FALSE; } diff --git a/sql/item_subselect.h b/sql/item_subselect.h index f9e7a5ef568..85ce7745751 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -428,7 +428,6 @@ public: join nest pointer - the predicate is an AND-part of ON expression of a join nest NULL - for all other locations - See also THD::emb_on_expr_nest. */ TABLE_LIST *emb_on_expr_nest; /* @@ -447,7 +446,7 @@ public: /* A bitmap of possible execution strategies for an IN predicate. */ uchar in_strategy; - + bool is_jtbm_merged; /* @@ -459,7 +458,7 @@ public: TRUE<=>registered in the list of semijoins in outer select */ bool is_registered_semijoin; - + /* Used to determine how this subselect item is represented in the item tree, in case there is a need to locate it there and replace with something else. @@ -489,7 +488,8 @@ public: Item_in_subselect() :Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE), abort_on_null(0), optimizer(0), - pushed_cond_guards(NULL), func(NULL), in_strategy(SUBS_NOT_TRANSFORMED), + pushed_cond_guards(NULL), func(NULL), emb_on_expr_nest(NULL), + in_strategy(SUBS_NOT_TRANSFORMED), is_jtbm_merged(FALSE), upper_item(0) {} @@ -533,6 +533,12 @@ public: user. */ int get_identifier(); + + void mark_as_condition_AND_part(TABLE_LIST *embedding) + { + emb_on_expr_nest= embedding; + } + friend class Item_ref_null_helper; friend class Item_is_not_null_test; friend class Item_in_optimizer; diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 6c5e177fe1d..a0b7aa242c2 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -340,7 +340,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join) !select_lex->is_part_of_union() && // 2 !select_lex->group_list.elements && !join->order && // 3 !join->having && !select_lex->with_sum_func && // 4 - thd->thd_marker.emb_on_expr_nest && // 5 + in_subs->emb_on_expr_nest && // 5 select_lex->outer_select()->join && // 6 parent_unit->first_select()->leaf_tables.elements && // 7 !in_subs->in_strategy && // 8 @@ -353,7 +353,6 @@ int check_and_do_in_subquery_rewrites(JOIN *join) (void)subquery_types_allow_materialization(in_subs); - in_subs->emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest; in_subs->is_flattenable_semijoin= TRUE; /* Register the subquery for further processing in flatten_subqueries() */ @@ -434,10 +433,9 @@ int check_and_do_in_subquery_rewrites(JOIN *join) If the subquery is an AND-part of WHERE register for being processed with jtbm strategy */ - if (thd->thd_marker.emb_on_expr_nest == NO_JOIN_NEST && + if (in_subs->emb_on_expr_nest == NO_JOIN_NEST && optimizer_flag(thd, OPTIMIZER_SWITCH_SEMIJOIN)) { - in_subs->emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest; in_subs->is_flattenable_semijoin= FALSE; if (!in_subs->is_registered_semijoin) { diff --git a/sql/sql_base.cc b/sql/sql_base.cc index c025011e8e5..afd4ef57ac8 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8276,7 +8276,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List &leaves, SELECT_LEX *select_lex= thd->lex->current_select; Query_arena *arena= thd->stmt_arena, backup; TABLE_LIST *table= NULL; // For HP compilers - TABLE_LIST *save_emb_on_expr_nest= thd->thd_marker.emb_on_expr_nest; List_iterator ti(leaves); /* it_is_update set to TRUE when tables of primary SELECT_LEX (SELECT_LEX @@ -8317,7 +8316,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List &leaves, goto err_no_arena; } - thd->thd_marker.emb_on_expr_nest= NO_JOIN_NEST; if (*conds) { thd->where="where clause"; @@ -8331,11 +8329,11 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List &leaves, */ if ((*conds)->type() == Item::FIELD_ITEM && !derived) wrap_ident(thd, conds); + (*conds)->mark_as_condition_AND_part(NO_JOIN_NEST); if ((!(*conds)->fixed && (*conds)->fix_fields(thd, conds)) || (*conds)->check_cols(1)) goto err_no_arena; } - thd->thd_marker.emb_on_expr_nest= save_emb_on_expr_nest; /* Apply fix_fields() to all ON clauses at all levels of nesting, @@ -8351,8 +8349,8 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List &leaves, if (embedded->on_expr) { /* Make a join an a expression */ - thd->thd_marker.emb_on_expr_nest= embedded; thd->where="on clause"; + embedded->on_expr->mark_as_condition_AND_part(embedded); if ((!embedded->on_expr->fixed && embedded->on_expr->fix_fields(thd, &embedded->on_expr)) || embedded->on_expr->check_cols(1)) @@ -8376,7 +8374,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, List &leaves, } } } - thd->thd_marker.emb_on_expr_nest= save_emb_on_expr_nest; if (!thd->stmt_arena->is_conventional()) { diff --git a/sql/sql_class.h b/sql/sql_class.h index c97cc34e166..7cc8cfc5147 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1600,15 +1600,6 @@ public: /* container for handler's private per-connection data */ Ha_data ha_data[MAX_HA]; - /* Place to store various things */ - union - { - /* - Used by subquery optimizations, see Item_in_subselect::emb_on_expr_nest. - */ - TABLE_LIST *emb_on_expr_nest; - } thd_marker; - bool prepare_derived_at_open; /* diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 204bc62cde3..f9d9536f11b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5052,7 +5052,6 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) param->select_limit= new Item_int((ulonglong) thd->variables.select_limit); } - thd->thd_marker.emb_on_expr_nest= NULL; if (!(res= open_and_lock_tables(thd, all_tables))) { if (lex->describe) @@ -5826,7 +5825,6 @@ void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat) thd->query_plan_flags= QPLAN_INIT; thd->query_plan_fsort_passes= 0; - thd->thd_marker.emb_on_expr_nest= NULL; /* Because we come here only for start of top-statements, binlog format is diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 5b5ed004006..74ff1f0ebe1 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1413,7 +1413,6 @@ static int mysql_test_select(Prepared_statement *stmt, goto error; thd->used_tables= 0; // Updated by setup_fields - thd->thd_marker.emb_on_expr_nest= 0; /* JOIN::prepare calls -- cgit v1.2.1 From 8b7a63b17f838c012f870e997a63d72c8d696fcf Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Mon, 29 Aug 2011 20:38:21 +0300 Subject: Added logging of all messages (also system warnings) one gets during a MyISAM recovery or auto-recovery. sql/mysqld.cc: Log errors if thd->log_all_errors is set sql/sql_class.cc: Add log_all_errors sql/sql_class.h: Add log_all_errors storage/myisam/ha_myisam.cc: Write db and table name for all logged errors Log errors also during auto_recovery During auto_recovery, set thd->log_all_errors if log_warnings >2 to ensure that system errors are also logged to file --- sql/mysqld.cc | 2 +- sql/sql_class.cc | 2 +- sql/sql_class.h | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 3c47fe446ab..a24d60da596 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3155,7 +3155,7 @@ to_error_log: /* When simulating OOM, skip writing to error log to avoid mtr errors */ DBUG_EXECUTE_IF("simulate_out_of_memory", DBUG_RETURN(0);); - if (!thd || (MyFlags & ME_NOREFRESH)) + if (!thd || thd->log_all_errors || (MyFlags & ME_NOREFRESH)) (*func)("%s: %s", my_progname_short, str); /* purecov: inspected */ DBUG_RETURN(0); } diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 1ba6b7131d9..cf800384b17 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -662,7 +662,7 @@ THD::THD() Open_tables_state(refresh_version), rli_fake(0), lock_id(&main_lock_id), user_time(0), in_sub_stmt(0), - sql_log_bin_toplevel(false), + sql_log_bin_toplevel(false), log_all_errors(0), binlog_table_maps(0), binlog_flags(0UL), table_map_for_update(0), arg_of_last_insert_id_function(FALSE), diff --git a/sql/sql_class.h b/sql/sql_class.h index ddad3f4dfc9..92de010c4b4 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1467,6 +1467,8 @@ public: bool sql_log_bin_toplevel; /* True when opt_userstat_running is set at start of query */ bool userstat_running; + /* True if we want to log all errors */ + bool log_all_errors; /* container for handler's private per-connection data */ Ha_data ha_data[MAX_HA]; -- cgit v1.2.1 From 2e6ae6ebf4ec03c3d979c1c5a0304e181d445fae Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Mon, 29 Aug 2011 21:54:16 +0400 Subject: BUG##836491: Crash in Item_field::Item_field from add_ref_to_table_cond() with semijoin+materialization - Let create_tmp_table set KEY_PART_INFO::fieldnr. It is needed in add_ref_to_table_cond(), and possibly other places. --- sql/sql_select.cc | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 54c1215164e..8a7babcecdd 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -13716,6 +13716,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List &fields, bool maybe_null=(*cur_group->item)->maybe_null; key_part_info->null_bit=0; key_part_info->field= field; + key_part_info->fieldnr= field->field_index + 1; if (cur_group == group) field->key_start.set_bit(0); key_part_info->offset= field->offset(table->record[0]); @@ -13839,6 +13840,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List &fields, key_part_info->field->init(table); key_part_info->key_type=FIELDFLAG_BINARY; key_part_info->type= HA_KEYTYPE_BINARY; + key_part_info->fieldnr= key_part_info->field->field_index + 1; key_part_info++; } /* Create a distinct key over the columns we are going to return */ @@ -13856,6 +13858,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List &fields, key_part_info->offset= (*reg_field)->offset(table->record[0]); key_part_info->length= (uint16) (*reg_field)->pack_length(); + key_part_info->fieldnr= (*reg_field)->field_index + 1; /* TODO: The below method of computing the key format length of the key part is a copy/paste from opt_range.cc, and table.cc. -- cgit v1.2.1 From 11ebbabb087a7bd6c315e6412d8e8ee677c6217b Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 1 Sep 2011 14:23:03 +0400 Subject: sec_to_time() in the integer context was losing the sign of the result --- sql/item_timefunc.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index b080f2e9707..f69d77ad6b5 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1370,7 +1370,8 @@ longlong Item_temporal_func::val_int() MYSQL_TIME ltime; if (get_date(<ime, TIME_FUZZY_DATE)) return 0; - return (longlong)TIME_to_ulonglong(<ime); + longlong v= TIME_to_ulonglong(<ime); + return ltime.neg ? -v : v; } -- cgit v1.2.1 From 4692537f608bb69e8d02c4b102b4fb888f7bec12 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Thu, 1 Sep 2011 21:18:29 +0300 Subject: Fixed non critical buffer overflow bug in open_binary_frm() that could cause ASSERT Added more printing of errors to myisamchk. mysys/mf_iocache.c: Write error message if failed seek. sql/table.cc: Fixed buffer overflow bug: - It's not enough to check for mysql_version to to detect partion indicator as the version may have been updated by mysql_upgrade. storage/myisam/ha_myisam.cc: Don't log same error twice. Don't reset log_all_errors if it's set storage/myisam/mi_check.c: Fixed bug that caused repair() to not report error if called twice (as when doing retry) More printing of errors. storage/myisam/sort.c: Set my_errno in case of out of memory errors. --- sql/table.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/table.cc b/sql/table.cc index dbb5d9ec499..21575e419da 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -989,14 +989,13 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, #endif next_chunk+= 5 + partition_info_len; } - if (share->mysql_version >= 50110) + if (share->mysql_version >= 50110 && next_chunk < buff_end) { /* New auto_partitioned indicator introduced in 5.1.11 */ #ifdef WITH_PARTITION_STORAGE_ENGINE share->auto_partitioned= *next_chunk; #endif next_chunk++; - DBUG_ASSERT(next_chunk <= buff_end); } keyinfo= share->key_info; for (i= 0; i < keys; i++, keyinfo++) -- cgit v1.2.1 From ea8aa329099ee28ec6f1266d8d01a6fc664259cf Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 1 Sep 2011 23:53:12 +0300 Subject: Fix for bug lp:834492 Analysis: In the test query semi-join merges the inner-most subquery into the outer subquery, and the optimization of the merged subquery finds some new index access methods. Later the IN-EXISTS transformation is applied to the unmerged subquery. Since the optimizer is instructed to not consider materialization, it reoptimizes the plan in-place to take into account the new IN-EXISTS conditions. Just before reoptimization JOIN::choose_subquery_plan resets the query plan, which also resets the access methods found during the semi-join merge. Then reoptimization discovers there are no new access methods, but it leaves the query plan in its reset state. Later semi-join crashes because it assumes these access methods are present. Solution: When reoptimizing in-place, reset the query plan only after new access methods were discovered. If no new access methods were discovered, leave the current plan as it was. --- sql/opt_subselect.cc | 15 +++++---------- sql/sql_select.cc | 18 +++++++++++++++++- sql/sql_select.h | 1 + 3 files changed, 23 insertions(+), 11 deletions(-) (limited to 'sql') diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 6c5e177fe1d..b06d80860c5 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -4641,17 +4641,12 @@ bool JOIN::choose_subquery_plan(table_map join_tables) const_tables != table_count) { /* - The subquery was not reoptimized either because the user allowed only - the IN-EXISTS strategy, or because materialization was not possible - based on semantic analysis. Cleanup the original plan and reoptimize. + The subquery was not reoptimized with the newly injected IN-EXISTS + conditions either because the user allowed only the IN-EXISTS strategy, + or because materialization was not possible based on semantic analysis. */ - for (uint i= 0; i < table_count; i++) - { - join_tab[i].keyuse= NULL; - join_tab[i].checked_keys.clear_all(); - } - if ((reopt_result= reoptimize(in_to_exists_where, join_tables, NULL)) == - REOPT_ERROR) + reopt_result= reoptimize(in_to_exists_where, join_tables, NULL); + if (reopt_result == REOPT_ERROR) return TRUE; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 54c1215164e..d01c54d1f22 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -21546,6 +21546,19 @@ void JOIN::save_query_plan(Join_plan_state *save_to) } +/** + Reset a query execution plan so that it can be reoptimized in-place. +*/ +void JOIN::reset_query_plan() +{ + for (uint i= 0; i < table_count; i++) + { + join_tab[i].keyuse= NULL; + join_tab[i].checked_keys.clear_all(); + } +} + + /** Restore a query execution plan previously saved by the caller. @@ -21579,7 +21592,8 @@ void JOIN::restore_query_plan(Join_plan_state *restore_from) @param added_where An extra conjunct to the WHERE clause to reoptimize with @param join_tables The set of tables to reoptimize - @param save_to If != NULL, save here the state of the current query plan + @param save_to If != NULL, save here the state of the current query plan, + otherwise reuse the existing query plan structures. @notes Given a query plan that was already optimized taking into account some WHERE @@ -21623,6 +21637,8 @@ JOIN::reoptimize(Item *added_where, table_map join_tables, if (save_to) save_query_plan(save_to); + else + reset_query_plan(); if (!keyuse.buffer && my_init_dynamic_array(&keyuse, sizeof(KEYUSE), 20, 64)) diff --git a/sql/sql_select.h b/sql/sql_select.h index bbf390aaf7e..9b8e60e9cc1 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -698,6 +698,7 @@ protected: enum_reopt_result reoptimize(Item *added_where, table_map join_tables, Join_plan_state *save_to); void save_query_plan(Join_plan_state *save_to); + void reset_query_plan(); void restore_query_plan(Join_plan_state *restore_from); /* Choose a subquery plan for a table-less subquery. */ bool choose_tableless_subquery_plan(); -- cgit v1.2.1 From 31c8c95bb204e74431412d07970a8133a352984f Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Fri, 2 Sep 2011 01:22:34 +0300 Subject: Added logging of all errors from my_read/my_write/my_pread/my_pwrite/my_open & my_malloc to mysqld error log if one sets log-warning to 10 or 11 The idea is that my_global_flags is ored to the MyFlags parameter for the above functions if the MY_WME flag is not set. As the my_global_flags has ME_JUST_INFO (mark error as 'note') and possible ME_NOREFRESH (write error to log) this will force mysqld to log the not critical error to the log as a note. include/my_sys.h: Moved MY_SYNC_DIR to ensure it never clashes with ME_JUST_INFO Added my_global_flags mysql-test/Makefile.am: Removed not used bugs directory mysys/my_init.c: Added my_global_flags, a variable that is ored to MyFlags in a those mysys functions we want extra logging. mysys/my_malloc.c: Added support for my_global_flags mysys/my_open.c: Added support for my_global_flags mysys/my_pread.c: Added support for my_global_flags mysys/my_read.c: Added support for my_global_flags mysys/my_static.c: Added my_global_flags mysys/my_write.c: Added support for my_global_flags sql/mysqld.cc: Set my_global_flags for warning levels 10 & 11 sql/sql_base.cc: Don't increment unhandled errors for notes or warnings. --- sql/mysqld.cc | 18 +++++++++++++++--- sql/sql_base.cc | 5 +++-- 2 files changed, 18 insertions(+), 5 deletions(-) (limited to 'sql') diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a24d60da596..7e656bbd3bd 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3032,7 +3032,7 @@ int my_message_sql(uint error, const char *str, myf MyFlags) MYSQL_ERROR::enum_warning_level level; sql_print_message_func func; DBUG_ENTER("my_message_sql"); - DBUG_PRINT("error", ("error: %u message: '%s'", error, str)); + DBUG_PRINT("error", ("error: %u message: '%s' Flag: %d", error, str, MyFlags)); DBUG_ASSERT(str != NULL); /* @@ -3076,7 +3076,10 @@ int my_message_sql(uint error, const char *str, myf MyFlags) this could be improved by having a common stack of handlers. */ if (thd->handle_error(error, str, level)) + { + DBUG_PRINT("info", ("error handled by handle_error()")); DBUG_RETURN(0); + } if (level == MYSQL_ERROR::WARN_LEVEL_WARN) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, error, str); @@ -6495,8 +6498,7 @@ each time the SQL thread starts.", "log and this option just turns on --log-bin instead.", &opt_update_logname, &opt_update_logname, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, - {"log-warnings", 'W', "Log some not critical warnings to the general log " - "file.", + {"log-warnings", 'W', "Log some not critical warnings to the general log file. Value can be between 0-6; The higher value, the more warnings", &global_system_variables.log_warnings, &max_system_variables.log_warnings, 0, GET_ULONG, OPT_ARG, 1, 0, 0, 0, 0, 0}, @@ -9419,6 +9421,16 @@ static int get_options(int *argc,char **argv) myisam_block_size=(uint) 1 << my_bit_log2(opt_myisam_block_size); my_crc_dbug_check= opt_my_crc_dbug_check; + /* + Log mysys errors when we don't have a thd or thd->log_all_errors is set (recovery) to + the log. This is mainly useful for debugging strange system errors. + */ + if (global_system_variables.log_warnings >= 10) + my_global_flags= MY_WME | ME_JUST_INFO; + /* Log all errors not handled by thd->handle_error() to my_message_sql() */ + if (global_system_variables.log_warnings >= 11) + my_global_flags|= ME_NOREFRESH; + /* long_query_time is in microseconds */ global_system_variables.long_query_time= max_system_variables.long_query_time= (longlong) (long_query_time * 1000000.0); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 0874ee16127..4826603c720 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -61,7 +61,7 @@ private: bool Prelock_error_handler::handle_error(uint sql_errno, const char * /* message */, - MYSQL_ERROR::enum_warning_level /* level */, + MYSQL_ERROR::enum_warning_level level, THD * /* thd */) { if (sql_errno == ER_NO_SUCH_TABLE) @@ -70,7 +70,8 @@ Prelock_error_handler::handle_error(uint sql_errno, return TRUE; } - m_unhandled_errors++; + if (level == MYSQL_ERROR::WARN_LEVEL_ERROR) + m_unhandled_errors++; return FALSE; } -- cgit v1.2.1 From 37a8497d494ea256ff4b13a89e62150e06a17dae Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 2 Sep 2011 10:11:13 +0300 Subject: LP BUG#823169 fix. For ANY subqueries NULLs should be ignored (if there is other values) when finding max min. For ALL subqueries NULLs should be saved if they found. Optimisation for ALL suqbueries if NULL is possible in the SELECT list with max/min aggregate function switched off. Some test changed where NULL is not used but optimization with max/min aggregate function important so NOT NULL added. mysql-test/r/explain.result: Forced old optimization. mysql-test/r/subselect.result: Forced old optimization. New test suite. mysql-test/t/explain.test: Forced old optimization. mysql-test/t/subselect.test: Forced old optimization. New test suite. sql/item_subselect.cc: Store converted subquery type. Switch off aggregate function optimisation for ALL and nulls. sql/sql_class.cc: Fixed NULL comparison. sql/sql_class.h: Store converted subquery type. --- sql/item_subselect.cc | 24 +++++++++++++++------ sql/sql_class.cc | 60 ++++++++++++++++++++++++++++++--------------------- sql/sql_class.h | 6 ++++-- 3 files changed, 58 insertions(+), 32 deletions(-) (limited to 'sql') diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 02f708cdf91..415d81a5b58 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -390,7 +390,10 @@ Item_maxmin_subselect::Item_maxmin_subselect(THD *thd_param, { DBUG_ENTER("Item_maxmin_subselect::Item_maxmin_subselect"); max= max_arg; - init(select_lex, new select_max_min_finder_subselect(this, max_arg)); + init(select_lex, + new select_max_min_finder_subselect(this, max_arg, + parent->substype() == + Item_subselect::ALL_SUBS)); max_columns= 1; maybe_null= 1; max_columns= 1; @@ -1008,11 +1011,20 @@ Item_in_subselect::single_value_transformer(JOIN *join, } Item *subs; - if (!select_lex->group_list.elements && - !select_lex->having && - !select_lex->with_sum_func && - !(select_lex->next_select()) && - select_lex->table_list.elements) + /* + Check if optimization with aggregate min/max possible + 1 There is no aggregate in the subquery + 2 It is not UNION + 3 There is tables + 4 It is not ALL subquery with possible NULLs in the SELECT list + */ + if (!select_lex->group_list.elements && /*1*/ + !select_lex->having && /*1*/ + !select_lex->with_sum_func && /*1*/ + !(select_lex->next_select()) && /*2*/ + select_lex->table_list.elements && /*3*/ + (!select_lex->ref_pointer_array[0]->maybe_null || /*4*/ + substype() != Item_subselect::ALL_SUBS)) /*4*/ { Item_sum_hybrid *item; nesting_map save_allow_sum_func; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index cf800384b17..0b6a3ebf85e 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -2580,26 +2580,32 @@ bool select_max_min_finder_subselect::cmp_real() { Item *maxmin= ((Item_singlerow_subselect *)item)->element_index(0); double val1= cache->val_real(), val2= maxmin->val_real(); + + /* Ignore NULLs for ANY and keep them for ALL subqueries */ + if (cache->null_value) + return (is_all && !maxmin->null_value) || (!is_all && maxmin->null_value); + if (maxmin->null_value) + return !is_all; + if (fmax) - return (cache->null_value && !maxmin->null_value) || - (!cache->null_value && !maxmin->null_value && - val1 > val2); - return (maxmin->null_value && !cache->null_value) || - (!cache->null_value && !maxmin->null_value && - val1 < val2); + return(val1 > val2); + return (val1 < val2); } bool select_max_min_finder_subselect::cmp_int() { Item *maxmin= ((Item_singlerow_subselect *)item)->element_index(0); longlong val1= cache->val_int(), val2= maxmin->val_int(); + + /* Ignore NULLs for ANY and keep them for ALL subqueries */ + if (cache->null_value) + return (is_all && !maxmin->null_value) || (!is_all && maxmin->null_value); + if (maxmin->null_value) + return !is_all; + if (fmax) - return (cache->null_value && !maxmin->null_value) || - (!cache->null_value && !maxmin->null_value && - val1 > val2); - return (maxmin->null_value && !cache->null_value) || - (!cache->null_value && !maxmin->null_value && - val1 < val2); + return(val1 > val2); + return (val1 < val2); } bool select_max_min_finder_subselect::cmp_decimal() @@ -2607,13 +2613,16 @@ bool select_max_min_finder_subselect::cmp_decimal() Item *maxmin= ((Item_singlerow_subselect *)item)->element_index(0); my_decimal cval, *cvalue= cache->val_decimal(&cval); my_decimal mval, *mvalue= maxmin->val_decimal(&mval); + + /* Ignore NULLs for ANY and keep them for ALL subqueries */ + if (cache->null_value) + return (is_all && !maxmin->null_value) || (!is_all && maxmin->null_value); + if (maxmin->null_value) + return !is_all; + if (fmax) - return (cache->null_value && !maxmin->null_value) || - (!cache->null_value && !maxmin->null_value && - my_decimal_cmp(cvalue, mvalue) > 0) ; - return (maxmin->null_value && !cache->null_value) || - (!cache->null_value && !maxmin->null_value && - my_decimal_cmp(cvalue,mvalue) < 0); + return (my_decimal_cmp(cvalue, mvalue) > 0) ; + return (my_decimal_cmp(cvalue,mvalue) < 0); } bool select_max_min_finder_subselect::cmp_str() @@ -2626,13 +2635,16 @@ bool select_max_min_finder_subselect::cmp_str() */ val1= cache->val_str(&buf1); val2= maxmin->val_str(&buf1); + + /* Ignore NULLs for ANY and keep them for ALL subqueries */ + if (cache->null_value) + return (is_all && !maxmin->null_value) || (!is_all && maxmin->null_value); + if (maxmin->null_value) + return !is_all; + if (fmax) - return (cache->null_value && !maxmin->null_value) || - (!cache->null_value && !maxmin->null_value && - sortcmp(val1, val2, cache->collation.collation) > 0) ; - return (maxmin->null_value && !cache->null_value) || - (!cache->null_value && !maxmin->null_value && - sortcmp(val1, val2, cache->collation.collation) < 0); + return (sortcmp(val1, val2, cache->collation.collation) > 0) ; + return (sortcmp(val1, val2, cache->collation.collation) < 0); } int select_exists_subselect::send_data(List &items) diff --git a/sql/sql_class.h b/sql/sql_class.h index 92de010c4b4..53c1db1a527 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2931,9 +2931,11 @@ class select_max_min_finder_subselect :public select_subselect Item_cache *cache; bool (select_max_min_finder_subselect::*op)(); bool fmax; + bool is_all; public: - select_max_min_finder_subselect(Item_subselect *item_arg, bool mx) - :select_subselect(item_arg), cache(0), fmax(mx) + select_max_min_finder_subselect(Item_subselect *item_arg, bool mx, + bool all) + :select_subselect(item_arg), cache(0), fmax(mx), is_all(all) {} void cleanup(); int send_data(List &items); -- cgit v1.2.1 From 13e4d54795c2e49e4a8654964de1ab899eae2b23 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Fri, 2 Sep 2011 12:41:20 +0300 Subject: Fixed lp:814238 "safe_mutex issues must be assertions in debug binary" Added --debug-assert-on-error variable which, if set, will cause safe_mutex to assert if it founds an error. include/my_sys.h: Added my_assert_on_error mysys/my_static.c: Added my_assert_on_error mysys/thr_mutex.c: Assert when found wrong mutex usage if my_assert_on_error is set sql/mysqld.cc: Added setting of my_assert_on_error --- sql/mysqld.cc | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 7e656bbd3bd..7131188b306 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -6068,7 +6068,9 @@ enum options_mysqld OPT_SECURE_FILE_PRIV, OPT_MIN_EXAMINED_ROW_LIMIT, OPT_LOG_SLOW_SLAVE_STATEMENTS, - OPT_DEBUG_CRC, OPT_DEBUG_ON, OPT_DEBUG_ASSERT_IF_CRASHED_TABLE, OPT_OLD_MODE, + OPT_DEBUG_CRC, OPT_DEBUG_ON, OPT_DEBUG_ASSERT_IF_CRASHED_TABLE, + OPT_DEBUG_ASSERT_ON_ERROR, + OPT_OLD_MODE, OPT_TEST_IGNORE_WRONG_OPTIONS, OPT_TEST_RESTART, #if defined(ENABLED_DEBUG_SYNC) OPT_DEBUG_SYNC_TIMEOUT, @@ -6243,6 +6245,10 @@ struct my_option my_long_options[] = "Do an assert in handler::print_error() if we get a crashed table", &debug_assert_if_crashed_table, &debug_assert_if_crashed_table, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"debug-assert-on-error", OPT_DEBUG_ASSERT_ON_ERROR, + "Do an assert in various functions if we get a fatal error", + &my_assert_on_error, &my_assert_on_error, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #endif {"default-character-set", OPT_DEFAULT_CHARACTER_SET_OLD, "Set the default character set (deprecated option, use --character-set-server instead).", @@ -9422,14 +9428,17 @@ static int get_options(int *argc,char **argv) my_crc_dbug_check= opt_my_crc_dbug_check; /* - Log mysys errors when we don't have a thd or thd->log_all_errors is set (recovery) to - the log. This is mainly useful for debugging strange system errors. + Log mysys errors when we don't have a thd or thd->log_all_errors is set + (recovery) to the log. This is mainly useful for debugging strange system + errors. */ if (global_system_variables.log_warnings >= 10) my_global_flags= MY_WME | ME_JUST_INFO; /* Log all errors not handled by thd->handle_error() to my_message_sql() */ if (global_system_variables.log_warnings >= 11) my_global_flags|= ME_NOREFRESH; + if (my_assert_on_error) + debug_assert_if_crashed_table= 1; /* long_query_time is in microseconds */ global_system_variables.long_query_time= max_system_variables.long_query_time= -- cgit v1.2.1 From da61ecccee7b8147e6fd9406671f33de51b4ca95 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Fri, 2 Sep 2011 22:43:35 +0400 Subject: BUG#836523: Crash in JOIN::get_partial_cost_and_fanout with semijoin+materialization - Make JOIN::get_partial_cost_and_fanout() be able to handle join plans with semi-join-materialization nests. --- sql/opt_subselect.cc | 16 +----------- sql/sql_select.cc | 70 ++++++++++++++++++++++++++++++---------------------- sql/sql_select.h | 2 +- 3 files changed, 43 insertions(+), 45 deletions(-) (limited to 'sql') diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 579d7b2d9bc..c9dff4f0f67 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -4478,20 +4478,6 @@ bool JOIN::choose_subquery_plan(table_map join_tables) outer_join= unit->outer_select() ? unit->outer_select()->join : NULL; if (outer_join && outer_join->table_count > 0) { - /* - The index of the last JOIN_TAB in the outer JOIN where in_subs is - attached (pushed to). - */ - uint max_outer_join_tab_idx; - /* - Make_cond_for_table is called for predicates only in the WHERE/ON - clauses. In all other cases, predicates are not pushed to any - JOIN_TAB, and their join_tab_idx remains MAX_TABLES. Such predicates - are evaluated for each complete row of the outer join. - */ - max_outer_join_tab_idx= (in_subs->get_join_tab_idx() == MAX_TABLES) ? - outer_join->table_count - 1: - in_subs->get_join_tab_idx(); /* TODO: Currently outer_lookup_keys is computed as the number of rows in @@ -4504,7 +4490,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables) If the join order: t1, t2, the number of unique lookup keys is ~ to the number of unique values t2.c2 in the partial join t1 join t2. */ - outer_join->get_partial_cost_and_fanout(max_outer_join_tab_idx, + outer_join->get_partial_cost_and_fanout(in_subs->get_join_tab_idx(), table_map(-1), &dummy, &outer_lookup_keys); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 80abfd2c8e5..9aab58c1742 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -175,14 +175,14 @@ int join_read_always_key_or_null(JOIN_TAB *tab); int join_read_next_same_or_null(READ_RECORD *info); static COND *make_cond_for_table(THD *thd, Item *cond,table_map table, table_map used_table, - uint join_tab_idx_arg, + int join_tab_idx_arg, bool exclude_expensive_cond, bool retain_ref_cond); static COND *make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, table_map tables, table_map used_table, - uint join_tab_idx_arg, + int join_tab_idx_arg, bool exclude_expensive_cond, bool retain_ref_cond); @@ -1089,7 +1089,7 @@ JOIN::optimize() if (conds && !(thd->lex->describe & DESCRIBE_EXTENDED)) { COND *table_independent_conds= - make_cond_for_table(thd, conds, PSEUDO_TABLE_BITS, 0, MAX_TABLES, + make_cond_for_table(thd, conds, PSEUDO_TABLE_BITS, 0, -1, FALSE, FALSE); DBUG_EXECUTE("where", print_where(table_independent_conds, @@ -2536,7 +2536,7 @@ JOIN::exec() Item* sort_table_cond= make_cond_for_table(thd, curr_join->tmp_having, used_tables, - (table_map)0, MAX_TABLES, + (table_map)0, -1, FALSE, FALSE); if (sort_table_cond) { @@ -2574,7 +2574,7 @@ JOIN::exec() QT_ORDINARY);); curr_join->tmp_having= make_cond_for_table(thd, curr_join->tmp_having, ~ (table_map) 0, - ~used_tables, MAX_TABLES, + ~used_tables, -1, FALSE, FALSE); DBUG_EXECUTE("where",print_where(curr_join->tmp_having, "having after sort", @@ -6048,7 +6048,7 @@ greedy_search(JOIN *join, read_time_arg and record_count_arg contain the computed cost and fanout */ -void JOIN::get_partial_cost_and_fanout(uint end_tab_idx, +void JOIN::get_partial_cost_and_fanout(int end_tab_idx, table_map filter_map, double *read_time_arg, double *record_count_arg) @@ -6058,14 +6058,14 @@ void JOIN::get_partial_cost_and_fanout(uint end_tab_idx, double sj_inner_fanout= 1.0; JOIN_TAB *end_tab= NULL; JOIN_TAB *tab; - uint i; - uint last_sj_table= MAX_TABLES; + int i; + int last_sj_table= MAX_TABLES; /* Handle a special case where the join is degenerate, and produces no records */ - if (table_count == 0) + if (table_count == const_tables) { *read_time_arg= 0.0; /* @@ -6075,6 +6075,7 @@ void JOIN::get_partial_cost_and_fanout(uint end_tab_idx, calculations. */ *record_count_arg=1.0; + return; } for (tab= first_depth_first_tab(this), i= const_tables; @@ -6087,19 +6088,17 @@ void JOIN::get_partial_cost_and_fanout(uint end_tab_idx, } for (tab= first_depth_first_tab(this), i= const_tables; - (i <= end_tab_idx && tab); + ; tab= next_depth_first_tab(this, tab), i++) { - /* - We've entered the SJM nest that contains the end_tab. The caller is - actually - - interested in fanout inside the nest (because that's how many times - we'll invoke the attached WHERE conditions) - - not interested in cost - */ if (end_tab->bush_root_tab && end_tab->bush_root_tab == tab) { - /* Ok, end_tab is inside SJM nest and we're entering that nest now */ + /* + We've entered the SJM nest that contains the end_tab. The caller is + - interested in fanout inside the nest (because that's how many times + we'll invoke the attached WHERE conditions) + - not interested in cost + */ record_count= 1.0; read_time= 0.0; } @@ -6113,8 +6112,18 @@ void JOIN::get_partial_cost_and_fanout(uint end_tab_idx, sj_inner_fanout= 1.0; last_sj_table= i + tab->n_sj_tables; } - - if (tab->records_read && (tab->table->map & filter_map)) + + table_map cur_table_map; + if (tab->table) + cur_table_map= tab->table->map; + else + { + /* This is a SJ-Materialization nest. Check all of its tables */ + TABLE *first_child= tab->bush_children->start->table; + TABLE_LIST *sjm_nest= first_child->pos_in_table_list->embedding; + cur_table_map= sjm_nest->nested_join->used_tables; + } + if (tab->records_read && (cur_table_map & filter_map)) { record_count *= tab->records_read; read_time += tab->read_time; @@ -6128,6 +6137,9 @@ void JOIN::get_partial_cost_and_fanout(uint end_tab_idx, sj_inner_fanout= 1.0; last_sj_table= MAX_TABLES; } + + if (tab == end_tab) + break; } *read_time_arg= read_time;// + record_count / TIME_FOR_COMPARE; *record_count_arg= record_count; @@ -6616,7 +6628,7 @@ int JOIN_TAB::make_scan_filter() if (cond && (tmp= make_cond_for_table(join->thd, cond, join->const_table_map | table->map, - table->map, MAX_TABLES, FALSE, TRUE))) + table->map, -1, FALSE, TRUE))) { DBUG_EXECUTE("where",print_where(tmp,"cache", QT_ORDINARY);); if (!(cache_select= @@ -7902,7 +7914,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) join->exec_const_cond= make_cond_for_table(thd, cond, join->const_table_map, - (table_map) 0, MAX_TABLES, FALSE, FALSE); + (table_map) 0, -1, FALSE, FALSE); /* Add conditions added by add_not_null_conds(). */ for (uint i= 0 ; i < join->const_tables ; i++) add_cond_and_fix(thd, &join->exec_const_cond, @@ -7921,7 +7933,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) COND *outer_ref_cond= make_cond_for_table(thd, cond, OUTER_REF_TABLE_BIT, OUTER_REF_TABLE_BIT, - MAX_TABLES, FALSE, FALSE); + -1, FALSE, FALSE); if (outer_ref_cond) { add_cond_and_fix(thd, &outer_ref_cond, join->outer_ref_cond); @@ -8091,7 +8103,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) { COND *push_cond= make_cond_for_table(thd, tmp, current_map, current_map, - MAX_TABLES, FALSE, FALSE); + -1, FALSE, FALSE); if (push_cond) { /* Push condition to handler */ @@ -8263,7 +8275,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) JOIN_TAB *cond_tab= join_tab->first_inner; COND *tmp= make_cond_for_table(thd, *join_tab->on_expr_ref, join->const_table_map, - (table_map) 0, MAX_TABLES, FALSE, FALSE); + (table_map) 0, -1, FALSE, FALSE); if (!tmp) continue; tmp= new Item_func_trig_cond(tmp, &cond_tab->not_null_compl); @@ -8309,10 +8321,10 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) current_map= tab->table->map; used_tables2|= current_map; /* - psergey: have put the MAX_TABLES below. It's bad, will need to fix it. + psergey: have put the -1 below. It's bad, will need to fix it. */ COND *tmp_cond= make_cond_for_table(thd, on_expr, used_tables2, - current_map, /*(tab - first_tab)*/ MAX_TABLES, + current_map, /*(tab - first_tab)*/ -1, FALSE, FALSE); if (tab == first_inner_tab && tab->on_precond) add_cond_and_fix(thd, &tmp_cond, tab->on_precond); @@ -16683,7 +16695,7 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item) static Item * make_cond_for_table(THD *thd, Item *cond, table_map tables, table_map used_table, - uint join_tab_idx_arg, + int join_tab_idx_arg, bool exclude_expensive_cond __attribute__((unused)), bool retain_ref_cond) { @@ -16697,7 +16709,7 @@ make_cond_for_table(THD *thd, Item *cond, table_map tables, static Item * make_cond_for_table_from_pred(THD *thd, Item *root_cond, Item *cond, table_map tables, table_map used_table, - uint join_tab_idx_arg, + int join_tab_idx_arg, bool exclude_expensive_cond __attribute__ ((unused)), bool retain_ref_cond) diff --git a/sql/sql_select.h b/sql/sql_select.h index 9b8e60e9cc1..9b53c53f690 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1154,7 +1154,7 @@ public: max_allowed_join_cache_level > JOIN_CACHE_HASHED_BIT; } bool choose_subquery_plan(table_map join_tables); - void get_partial_cost_and_fanout(uint end_tab_idx, + void get_partial_cost_and_fanout(int end_tab_idx, table_map filter_map, double *read_time_arg, double *record_count_arg); -- cgit v1.2.1 From 28a70509123888f3bce4f754124acc7963dca8b4 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Fri, 2 Sep 2011 23:44:28 +0400 Subject: BUG#836507: Crash in setup_sj_materialization_part1() with semijoin+materialization - setup_sj_materialization() code failed to take into account that it can be that the first [in join order ordering] table inside semi-join-materialization nest is also an inner table wrt an outer join (that is embedded in the semi-join). This can happen when all of the tables that are inside the semi-join but not inside the outer join are constant. - Made a trivial to not assume that table's embedding join nest is the semi-join nest: instead, walk up the outer join nests until we reach the semi-join nest. --- sql/opt_subselect.cc | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'sql') diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index c9dff4f0f67..dc924ec05e9 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -2935,6 +2935,11 @@ bool setup_sj_materialization_part1(JOIN_TAB *sjm_tab) DBUG_ENTER("setup_sj_materialization"); JOIN_TAB *tab= sjm_tab->bush_children->start; TABLE_LIST *emb_sj_nest= tab->table->pos_in_table_list->embedding; + + /* Walk out of outer join nests until we reach the semi-join nest we're in */ + while (!emb_sj_nest->sj_mat_info) + emb_sj_nest= emb_sj_nest->embedding; + SJ_MATERIALIZATION_INFO *sjm= emb_sj_nest->sj_mat_info; THD *thd= tab->join->thd; /* First the calls come to the materialization function */ @@ -2983,6 +2988,9 @@ bool setup_sj_materialization_part2(JOIN_TAB *sjm_tab) DBUG_ENTER("setup_sj_materialization_part2"); JOIN_TAB *tab= sjm_tab->bush_children->start; TABLE_LIST *emb_sj_nest= tab->table->pos_in_table_list->embedding; + /* Walk out of outer join nests until we reach the semi-join nest we're in */ + while (!emb_sj_nest->sj_mat_info) + emb_sj_nest= emb_sj_nest->embedding; SJ_MATERIALIZATION_INFO *sjm= emb_sj_nest->sj_mat_info; THD *thd= tab->join->thd; uint i; -- cgit v1.2.1 From 949984beed26b2536d2f24d3d63e8a06e4dfa089 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Sat, 3 Sep 2011 11:50:56 +0300 Subject: Fixed lp:828514 "Assertion `! is_set()' failed in Diagnostics_area::set_ok_status with derived table + subquery + concurrent DML" sql/item_subselect.cc: Added check of error condtions (safety) sql/sql_join_cache.cc: Added DBUG to some functions. Added error checking for calls to check_match(); This fixed the bug. sql/sql_select.cc: Moved variable assignment to be close to where it's used (cleanup) --- sql/item_subselect.cc | 5 +++-- sql/sql_join_cache.cc | 38 ++++++++++++++++++++++++-------------- sql/sql_select.cc | 8 +++++--- 3 files changed, 32 insertions(+), 19 deletions(-) (limited to 'sql') diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 92e311feb8d..4dbaa4c8c4e 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -2973,7 +2973,7 @@ int subselect_single_select_engine::exec() executed= 1; thd->where= save_where; thd->lex->current_select= save_select; - DBUG_RETURN(join->error||thd->is_fatal_error); + DBUG_RETURN(join->error || thd->is_fatal_error || thd->is_error()); } thd->where= save_where; thd->lex->current_select= save_select; @@ -4578,7 +4578,8 @@ int subselect_hash_sj_engine::exec() /* The subquery should be optimized, and materialized only once. */ DBUG_ASSERT(materialize_join->optimized && !is_materialized); materialize_join->exec(); - if ((res= test(materialize_join->error || thd->is_fatal_error))) + if ((res= test(materialize_join->error || thd->is_fatal_error || + thd->is_error()))) goto err; /* diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index f292375ee6f..c12d7dc5999 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -2021,6 +2021,7 @@ enum_nested_loop_state JOIN_CACHE::join_records(bool skip_last) JOIN_TAB *tab; enum_nested_loop_state rc= NESTED_LOOP_OK; bool outer_join_first_inner= join_tab->is_first_inner_for_outer_join(); + DBUG_ENTER("JOIN_CACHE::join_records"); if (outer_join_first_inner && !join_tab->first_unmatched) join_tab->not_null_compl= TRUE; @@ -2102,7 +2103,8 @@ enum_nested_loop_state JOIN_CACHE::join_records(bool skip_last) finish: restore_last_record(); reset(TRUE); - return rc; + DBUG_PRINT("exit", ("rc: %d", rc)); + DBUG_RETURN(rc); } @@ -2164,10 +2166,11 @@ enum_nested_loop_state JOIN_CACHE::join_matching_records(bool skip_last) join_tab->table->null_row= 0; bool check_only_first_match= join_tab->check_only_first_match(); bool outer_join_first_inner= join_tab->is_first_inner_for_outer_join(); + DBUG_ENTER("JOIN_CACHE::join_matching_records"); /* Return at once if there are no records in the join buffer */ if (!records) - return NESTED_LOOP_OK; + DBUG_RETURN(NESTED_LOOP_OK); /* When joining we read records from the join buffer back into record buffers. @@ -2241,7 +2244,7 @@ finish: rc= error < 0 ? NESTED_LOOP_NO_MORE_ROWS: NESTED_LOOP_ERROR; finish2: join_tab_scan->close(); - return rc; + DBUG_RETURN(rc); } @@ -2323,6 +2326,7 @@ bool JOIN_CACHE::set_match_flag_if_none(JOIN_TAB *first_inner, enum_nested_loop_state JOIN_CACHE::generate_full_extensions(uchar *rec_ptr) { enum_nested_loop_state rc= NESTED_LOOP_OK; + DBUG_ENTER("JOIN_CACHE::generate_full_extensions"); /* Check whether the extended partial join record meets @@ -2340,16 +2344,18 @@ enum_nested_loop_state JOIN_CACHE::generate_full_extensions(uchar *rec_ptr) if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS) { reset(TRUE); - return rc; + DBUG_RETURN(rc); } } if (res == -1) { rc= NESTED_LOOP_ERROR; - return rc; + DBUG_RETURN(rc); } } - return rc; + else if (join->thd->is_error()) + rc= NESTED_LOOP_ERROR; + DBUG_RETURN(rc); } @@ -2374,16 +2380,20 @@ enum_nested_loop_state JOIN_CACHE::generate_full_extensions(uchar *rec_ptr) RETURN VALUE TRUE there is a match FALSE there is no match + In this case the caller must also check thd->is_error() to see + if there was a fatal error for the query. */ inline bool JOIN_CACHE::check_match(uchar *rec_ptr) { /* Check whether pushdown conditions are satisfied */ + DBUG_ENTER("JOIN_CACHE:check_match"); + if (join_tab->select && join_tab->select->skip_record(join->thd) <= 0) - return FALSE; + DBUG_RETURN(FALSE); if (!join_tab->is_last_inner_table()) - return TRUE; + DBUG_RETURN(TRUE); /* This is the last inner table of an outer join, @@ -2396,7 +2406,7 @@ inline bool JOIN_CACHE::check_match(uchar *rec_ptr) set_match_flag_if_none(first_inner, rec_ptr); if (first_inner->check_only_first_match() && !join_tab->first_inner) - return TRUE; + DBUG_RETURN(TRUE); /* This is the first match for the outer table row. The function set_match_flag_if_none has turned the flag @@ -2410,13 +2420,12 @@ inline bool JOIN_CACHE::check_match(uchar *rec_ptr) for (JOIN_TAB *tab= first_inner; tab <= join_tab; tab++) { if (tab->select && tab->select->skip_record(join->thd) <= 0) - return FALSE; + DBUG_RETURN(FALSE); } } while ((first_inner= first_inner->first_upper) && first_inner->last_inner == join_tab); - - return TRUE; + DBUG_RETURN(TRUE); } @@ -2451,10 +2460,11 @@ enum_nested_loop_state JOIN_CACHE::join_null_complements(bool skip_last) ulonglong cnt; enum_nested_loop_state rc= NESTED_LOOP_OK; bool is_first_inner= join_tab == join_tab->first_unmatched; + DBUG_ENTER("JOIN_CACHE::join_null_complements"); /* Return at once if there are no records in the join buffer */ if (!records) - return NESTED_LOOP_OK; + DBUG_RETURN(NESTED_LOOP_OK); cnt= records - (is_key_access() ? 0 : test(skip_last)); @@ -2484,7 +2494,7 @@ enum_nested_loop_state JOIN_CACHE::join_null_complements(bool skip_last) } finish: - return rc; + DBUG_RETURN(rc); } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 80abfd2c8e5..a2c2ae6a51f 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2385,7 +2385,6 @@ JOIN::exec() thd_proc_info(thd, "Copying to group table"); DBUG_PRINT("info", ("%s", thd->proc_info)); - tmp_error= -1; if (curr_join != this) { if (sum_funcs2) @@ -2410,6 +2409,7 @@ JOIN::exec() JOIN_TAB *first_tab= curr_join->join_tab + curr_join->const_tables; first_tab->sorted= test(first_tab->loosescan_match_tab); } + tmp_error= -1; if (setup_sum_funcs(curr_join->thd, curr_join->sum_funcs) || (tmp_error= do_select(curr_join, (List *) 0, curr_tmp_table, 0))) @@ -14839,10 +14839,12 @@ sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool end_of_records) { enum_nested_loop_state rc; JOIN_CACHE *cache= join_tab->cache; - DBUG_ENTER("sub_select_cache"); - /* This function cannot be called if join_tab has no associated join buffer */ + /* + This function cannot be called if join_tab has no associated join + buffer + */ DBUG_ASSERT(cache != NULL); join_tab->cache->reset_join(join); -- cgit v1.2.1 From 5be4148e587ba651e5e1ba807b830216484012f7 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Sat, 3 Sep 2011 17:05:05 +0400 Subject: BUG#834514: Assertion `!table || (!table->read_set || bitmap_is_set(...' with aggregates - Make find_all_keys() not to rely on table->tmp_set remaining constant during execution quick_index_merge_select->reset() may change it. --- sql/filesort.cc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/filesort.cc b/sql/filesort.cc index 559f4f1dcf6..b3bb15bd7f0 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -624,15 +624,21 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, SQL_SELECT::skip_record evaluates this condition. it may include a correlated subquery predicate, such that some field in the subquery refers to 'sort_form'. + + PSergey-todo: discuss the above with Timour. */ + MY_BITMAP *tmp_read_set= sort_form->read_set; + MY_BITMAP *tmp_write_set= sort_form->write_set; + MY_BITMAP *tmp_vcol_set= sort_form->vcol_set; + if (select->cond->with_subselect) sort_form->column_bitmaps_set(save_read_set, save_write_set, save_vcol_set); write_record= (select->skip_record(thd) > 0); if (select->cond->with_subselect) - sort_form->column_bitmaps_set(&sort_form->tmp_set, - &sort_form->tmp_set, - &sort_form->tmp_set); + sort_form->column_bitmaps_set(tmp_read_set, + tmp_write_set, + tmp_vcol_set); } else write_record= true; -- cgit v1.2.1 From 55cde3b428a66808359f5dfd1458f84c26793bac Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Sun, 4 Sep 2011 16:35:37 +0400 Subject: BUG#836532: Crash in Item_equal_fields_iterator::get_curr_field with semijoin+materialization - Item_in_subselect::inject_in_to_exists_cond() should not call ((Item_cond*)join->conds)->argument_list()->concat(join->cond_equal->current_level) as that makes two lists share their tail, and the cond_equal list will end up containing non-Item_equal objects when substitute_for_best_equal_field() walks through join->conds and replaces all Item_equal objects with Item_func_eq objects. - So, instead of using List::concat(), manually copy entries from one list to another. --- sql/item_subselect.cc | 7 ++++++- sql/sql_list.h | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 92e311feb8d..0e23be77fbf 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -2264,7 +2264,12 @@ bool Item_in_subselect::inject_in_to_exists_cond(JOIN *join_arg) { /* The argument list of the top-level AND may change after fix fields. */ and_args= ((Item_cond*) join_arg->conds)->argument_list(); - and_args->concat((List *) &join_arg->cond_equal->current_level); + List_iterator li(join_arg->cond_equal->current_level); + Item_equal *elem; + while ((elem= li++)) + { + and_args->push_back(elem); + } } } diff --git a/sql/sql_list.h b/sql/sql_list.h index 50673921aeb..0189ad88597 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -310,6 +310,26 @@ public: friend class error_list; friend class error_list_iterator; + /* + Debugging help: return N-th element in the list, or NULL if the list has + less than N elements. + */ + inline void *nth_element(int n) + { + list_node *node= first; + void *data; + for (int i=0; i <= n; i++) + { + if (node == &end_of_list) + { + data= NULL; + break; + } + data= node->info; + node= node->next; + } + return data; + } #ifdef LIST_EXTRA_DEBUG /* Check list invariants and print results into trace. Invariants are: @@ -488,6 +508,7 @@ public: } empty(); } + inline T *nth_element(int n) { return (T*)base_list::nth_element(n); } }; -- cgit v1.2.1 From 8b062c1fefd14cfa22e625d098b92c8ac3c1b28f Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 5 Sep 2011 09:29:49 +0300 Subject: Fix of LP BUG#780386. ALL subquery should return TRUE if subquery rowa set is empty independently of left part. The problem was that Item_func_(eq,ne,gt,ge,lt,le) do not call execution of second argument if first is NULL no in this case subquery will not be executed and when Item_func_not_all calls any_value() of the subquery or aggregation function which report that there was rows. So for NULL < ALL (SELECT...) result was FALSE instead of TRUE. Fix is just swapping of arguments of Item_func_(eq,ne,gt,ge,lt,le) (with changing the operation if it is needed) so that result will be the same (for examole a < b is equal to b > a). This fix exploit the fact that first argument will be executed in any case. --- sql/item_cmpfunc.cc | 25 +++++++++++++++++++++++++ sql/item_cmpfunc.h | 13 +++++++++++++ sql/item_subselect.cc | 7 +++++-- 3 files changed, 43 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 282d0cd6abd..c4d56a93af6 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -245,36 +245,61 @@ Item_bool_func2* Eq_creator::create(Item *a, Item *b) const return new Item_func_eq(a, b); } +Item_bool_func2* Eq_creator::create_swap(Item *a, Item *b) const +{ + return new Item_func_eq(b, a); +} Item_bool_func2* Ne_creator::create(Item *a, Item *b) const { return new Item_func_ne(a, b); } +Item_bool_func2* Ne_creator::create_swap(Item *a, Item *b) const +{ + return new Item_func_ne(b, a); +} Item_bool_func2* Gt_creator::create(Item *a, Item *b) const { return new Item_func_gt(a, b); } +Item_bool_func2* Gt_creator::create_swap(Item *a, Item *b) const +{ + return new Item_func_lt(b, a); +} Item_bool_func2* Lt_creator::create(Item *a, Item *b) const { return new Item_func_lt(a, b); } +Item_bool_func2* Lt_creator::create_swap(Item *a, Item *b) const +{ + return new Item_func_gt(b, a); +} Item_bool_func2* Ge_creator::create(Item *a, Item *b) const { return new Item_func_ge(a, b); } +Item_bool_func2* Ge_creator::create_swap(Item *a, Item *b) const +{ + return new Item_func_le(b, a); +} Item_bool_func2* Le_creator::create(Item *a, Item *b) const { return new Item_func_le(a, b); } +Item_bool_func2* Le_creator::create_swap(Item *a, Item *b) const +{ + return new Item_func_ge(b, a); +} + /* Test functions Most of these returns 0LL if false and 1LL if true and diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 2cacd92bc8a..0278ad201af 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -270,7 +270,14 @@ class Comp_creator public: Comp_creator() {} /* Remove gcc warning */ virtual ~Comp_creator() {} /* Remove gcc warning */ + /** + Create operation with given arguments. + */ virtual Item_bool_func2* create(Item *a, Item *b) const = 0; + /** + Create operation with given arguments in swap order. + */ + virtual Item_bool_func2* create_swap(Item *a, Item *b) const = 0; virtual const char* symbol(bool invert) const = 0; virtual bool eqne_op() const = 0; virtual bool l_op() const = 0; @@ -282,6 +289,7 @@ public: Eq_creator() {} /* Remove gcc warning */ virtual ~Eq_creator() {} /* Remove gcc warning */ virtual Item_bool_func2* create(Item *a, Item *b) const; + virtual Item_bool_func2* create_swap(Item *a, Item *b) const; virtual const char* symbol(bool invert) const { return invert? "<>" : "="; } virtual bool eqne_op() const { return 1; } virtual bool l_op() const { return 0; } @@ -293,6 +301,7 @@ public: Ne_creator() {} /* Remove gcc warning */ virtual ~Ne_creator() {} /* Remove gcc warning */ virtual Item_bool_func2* create(Item *a, Item *b) const; + virtual Item_bool_func2* create_swap(Item *a, Item *b) const; virtual const char* symbol(bool invert) const { return invert? "=" : "<>"; } virtual bool eqne_op() const { return 1; } virtual bool l_op() const { return 0; } @@ -304,6 +313,7 @@ public: Gt_creator() {} /* Remove gcc warning */ virtual ~Gt_creator() {} /* Remove gcc warning */ virtual Item_bool_func2* create(Item *a, Item *b) const; + virtual Item_bool_func2* create_swap(Item *a, Item *b) const; virtual const char* symbol(bool invert) const { return invert? "<=" : ">"; } virtual bool eqne_op() const { return 0; } virtual bool l_op() const { return 0; } @@ -315,6 +325,7 @@ public: Lt_creator() {} /* Remove gcc warning */ virtual ~Lt_creator() {} /* Remove gcc warning */ virtual Item_bool_func2* create(Item *a, Item *b) const; + virtual Item_bool_func2* create_swap(Item *a, Item *b) const; virtual const char* symbol(bool invert) const { return invert? ">=" : "<"; } virtual bool eqne_op() const { return 0; } virtual bool l_op() const { return 1; } @@ -326,6 +337,7 @@ public: Ge_creator() {} /* Remove gcc warning */ virtual ~Ge_creator() {} /* Remove gcc warning */ virtual Item_bool_func2* create(Item *a, Item *b) const; + virtual Item_bool_func2* create_swap(Item *a, Item *b) const; virtual const char* symbol(bool invert) const { return invert? "<" : ">="; } virtual bool eqne_op() const { return 0; } virtual bool l_op() const { return 0; } @@ -337,6 +349,7 @@ public: Le_creator() {} /* Remove gcc warning */ virtual ~Le_creator() {} /* Remove gcc warning */ virtual Item_bool_func2* create(Item *a, Item *b) const; + virtual Item_bool_func2* create_swap(Item *a, Item *b) const; virtual const char* symbol(bool invert) const { return invert? ">" : "<="; } virtual bool eqne_op() const { return 0; } virtual bool l_op() const { return 1; } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 415d81a5b58..6f0a38c1625 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -1076,8 +1076,11 @@ Item_in_subselect::single_value_transformer(JOIN *join, if (upper_item) upper_item->set_sub_test(item); } - /* fix fields is already called for left expression */ - substitution= func->create(left_expr, subs); + /* + The swap is needed for expressions of type 'f1 < ALL ( SELECT ....)' + where we want to evaluate the sub query even if f1 would be null. + */ + substitution= func->create_swap(left_expr, subs); DBUG_RETURN(RES_OK); } -- cgit v1.2.1 From 6035d0d755a2465421da3ac845ab970c504c90d7 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Mon, 5 Sep 2011 19:28:22 +0400 Subject: BUG#834758: Wrong result with innner join, LooseScan, two-column IN() predicate - get_bound_sj_equalities() would produce incorrect bitmap when non-first equality was bound, which resulted in invalid LooseScan plans. --- sql/opt_subselect.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'sql') diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index dc924ec05e9..40a0195e554 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -2639,6 +2639,7 @@ ulonglong get_bound_sj_equalities(TABLE_LIST *sj_nest, { res |= 1ULL << i; } + i++; } return res; } -- cgit v1.2.1 From e1435a51786f7975da79f62fc430fc3b3bf1a45f Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Mon, 5 Sep 2011 20:51:37 +0400 Subject: BUG#834739: Wrong result with 3-way inner join, LooseScan,multipart keys - Don't use join buffering for tables that are within ranges that are covered by LooseScan strategy. --- sql/opt_subselect.cc | 2 ++ sql/sql_select.cc | 10 ++++++++++ sql/sql_select.h | 3 +++ 3 files changed, 15 insertions(+) (limited to 'sql') diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 40a0195e554..9ea55390fc5 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -3827,6 +3827,8 @@ int setup_semijoin_dups_elimination(JOIN *join, ulonglong options, { /* We jump from the last table to the first one */ tab->loosescan_match_tab= tab + pos->n_sj_tables - 1; + for (uint j= i; j < pos->n_sj_tables; j++) + join->join_tab[j].inside_loosescan_range= TRUE; /* Calculate key length */ keylen= 0; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 9aab58c1742..6ddb350db1d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7100,6 +7100,7 @@ get_best_combination(JOIN *join) goto loop_end; // Handled in make_join_stat.. j->loosescan_match_tab= NULL; //non-nulls will be set later + j->inside_loosescan_range= FALSE; j->ref.key = -1; j->ref.key_parts=0; @@ -8984,6 +8985,15 @@ uint check_join_cache_usage(JOIN_TAB *tab, if (tab->use_quick == 2) goto no_join_cache; + + /* + Don't use join cache if we're inside a join tab range covered by LooseScan + strategy (TODO: LooseScan is very similar to FirstMatch so theoretically it + should be possible to use join buffering in the same way we're using it for + multi-table firstmatch ranges). + */ + if (tab->inside_loosescan_range) + goto no_join_cache; if (tab->is_inner_table_of_semi_join_with_first_match() && !join->allowed_semijoin_with_cache) diff --git a/sql/sql_select.h b/sql/sql_select.h index 9b53c53f690..15711dca7b3 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -347,6 +347,9 @@ typedef struct st_join_table { NULL - Not doing a loose scan on this join tab. */ struct st_join_table *loosescan_match_tab; + + /* TRUE <=> we are inside LooseScan range */ + bool inside_loosescan_range; /* Buffer to save index tuple to be able to skip duplicates */ uchar *loosescan_buf; -- cgit v1.2.1 From fc6b6435b4c3f5203e6ffc3de289f0413a7ef33f Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 6 Sep 2011 17:06:04 +0400 Subject: BUG#823930: Wrong result with semijoin materialization and blob fields - Make subquery_types_allow_materialization() detect a case where create_tmp_table() would create a blob column which would make it impossible to use materialization Non-semi-join materialization worked because it detected that this case and felt back to use IN->EXISTS. Semi-join Materialization cannot easily fallback, so we have to detect this case early. --- sql/item.cc | 4 ++++ sql/opt_subselect.cc | 10 ++++++++++ 2 files changed, 14 insertions(+) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index ca4d41fcbdb..4fcffcfbc2a 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -5191,6 +5191,10 @@ Field *Item::make_string_field(TABLE *table) { Field *field; DBUG_ASSERT(collation.collation); + /* + Note: the following check is repeated in + subquery_types_allow_materialization(): + */ if (max_length/collation.collation->mbmaxlen > CONVERT_IF_BIGGER_TO_BLOB) field= new Field_blob(max_length, maybe_null, name, collation.collation); diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 9ea55390fc5..3cb13b0b88c 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -559,6 +559,16 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs) if (inner->field_type() == MYSQL_TYPE_BLOB || inner->field_type() == MYSQL_TYPE_GEOMETRY) DBUG_RETURN(FALSE); + /* + Materialization also is unable to work when create_tmp_table() will + create a blob column because item->max_length is too big. + The following check is copied from Item::make_string_field(): + */ + if (inner->max_length / inner->collation.collation->mbmaxlen > + CONVERT_IF_BIGGER_TO_BLOB) + { + DBUG_RETURN(FALSE); + } break; case TIME_RESULT: if (mysql_type_to_time_type(outer->field_type()) != -- cgit v1.2.1 From da59130ec18a5801080a034a3c1cb442f1ad4498 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Tue, 6 Sep 2011 07:17:39 -0700 Subject: Fixed LP bug #838633. For any query JOIN::optimize() should call the method SELECT::save_leaf_tables after the last transformation that utilizes the statement memory rather than the execution memory. --- sql/sql_select.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 54c1215164e..d9c6a1b668e 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -899,9 +899,6 @@ JOIN::optimize() /* dump_TABLE_LIST_graph(select_lex, select_lex->leaf_tables); */ select_lex->update_used_tables(); - /* Save this info for the next executions */ - if (select_lex->save_leaf_tables(thd)) - DBUG_RETURN(1); } eval_select_list_used_tables(); @@ -961,6 +958,8 @@ JOIN::optimize() /* Convert all outer joins to inner joins if possible */ conds= simplify_joins(this, join_list, conds, TRUE, FALSE); + if (select_lex->save_leaf_tables(thd)) + DBUG_RETURN(1); build_bitmap_for_nested_joins(join_list, 0); sel->prep_where= conds ? conds->copy_andor_structure(thd) : 0; -- cgit v1.2.1 From 528598c478141a6f32738351cb815fc13e229103 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 6 Sep 2011 20:52:36 +0400 Subject: Fix typo bug --- sql/sql_list.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_list.h b/sql/sql_list.h index 0189ad88597..4655b4e3577 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -317,7 +317,7 @@ public: inline void *nth_element(int n) { list_node *node= first; - void *data; + void *data= NULL; for (int i=0; i <= n; i++) { if (node == &end_of_list) -- cgit v1.2.1 From 3e97aeafd65b2b05a7a33ba21420bfa85208e820 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Wed, 7 Sep 2011 20:39:47 +0200 Subject: lp:839387 Assertion `(Item_result)i != TIME_RESULT' failed with CASE + datetime remove incorrect DBUG_ASSERT(). Fix incorrectly used cmp_item::get_comparator() in Item_func_case and Item_equal --- sql/item_cmpfunc.cc | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'sql') diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 3e699c30caa..91a656b645a 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2763,7 +2763,7 @@ Item *Item_func_case::find_item(String *str) { if (args[i]->real_item()->type() == NULL_ITEM) continue; - cmp_type= item_cmp_type(left_result_type, args[i]->result_type()); + cmp_type= item_cmp_type(left_result_type, args[i]->cmp_type()); DBUG_ASSERT(cmp_type != ROW_RESULT); DBUG_ASSERT(cmp_items[(uint)cmp_type]); if (!(value_added_map & (1<<(uint)cmp_type))) @@ -2928,7 +2928,7 @@ void Item_func_case::fix_length_and_dec() { uint i; agg[0]= args[first_expr_num]; - left_result_type= agg[0]->result_type(); + left_result_type= agg[0]->cmp_type(); for (nagg= 0; nagg < ncases/2 ; nagg++) agg[nagg+1]= args[nagg*2]; @@ -2946,17 +2946,21 @@ void Item_func_case::fix_length_and_dec() found_types |= (1 << item_cmp_type(left_result_type, STRING_RESULT)); } + Item *date_arg= 0; for (i= 0; i <= (uint)TIME_RESULT; i++) { if (found_types & (1 << i) && !cmp_items[i]) { DBUG_ASSERT((Item_result)i != ROW_RESULT); - DBUG_ASSERT((Item_result)i != TIME_RESULT); if ((Item_result)i == STRING_RESULT && agg_arg_charsets(cmp_collation, agg, nagg, MY_COLL_CMP_CONV, 1)) return; + + if ((Item_result)i == TIME_RESULT) + date_arg= find_date_time_item(args, arg_count, 0); + if (!(cmp_items[i]= - cmp_item::get_comparator((Item_result)i, 0, + cmp_item::get_comparator((Item_result)i, date_arg, cmp_collation.collation))) return; } @@ -3547,10 +3551,13 @@ void cmp_item_row::store_value(Item *item) for (uint i=0; i < n; i++) { if (!comparators[i]) + { + DBUG_ASSERT(item->element_index(i)->cmp_type() != TIME_RESULT); if (!(comparators[i]= cmp_item::get_comparator(item->element_index(i)->result_type(), 0, item->element_index(i)->collation.collation))) break; // new failed + } comparators[i]->store_value(item->element_index(i)); item->null_value|= item->element_index(i)->null_value; } @@ -5664,7 +5671,7 @@ longlong Item_equal::val_int() void Item_equal::fix_length_and_dec() { Item *item= get_first(NULL); - eval_item= cmp_item::get_comparator(item->result_type(), 0, + eval_item= cmp_item::get_comparator(item->cmp_type(), item, item->collation.collation); } -- cgit v1.2.1 From b80641b36cb43206a118ee84fccd90d08fbb8df4 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 8 Sep 2011 16:57:46 +0300 Subject: LP BUG#813418 fix. The problem was that optimization code did not take into account later feature when instad of NOT before BETWEEN it has negated flag into the Item_func_between inherited from Item_func_neg_opt. So optimizer tried process NOT BETWEEN as BETWEEN. The patch just switches off the optimisation for NOT BETWEEN as it was before when NOT function was really used. --- sql/opt_sum.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sql') diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 0ed31675f24..eaa3e88abbb 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -657,6 +657,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, case Item_func::GE_FUNC: break; case Item_func::BETWEEN: + if (((Item_func_between*) cond)->negated) + DBUG_RETURN(FALSE); between= 1; break; case Item_func::MULT_EQUAL_FUNC: -- cgit v1.2.1 From 5673aa41c3e0fe909689cea70b1ca46f10c38831 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 8 Sep 2011 19:48:14 +0400 Subject: BUG#830993: Crash in end_read_record with derived table - Let join buffering code correctly take into account rowids needed by DuplicateElimination when it is calculating minimum record sizes. - In JOIN_CACHE::write_record_data, added asserts that prevent us from writing beyond the end of the buffer. --- sql/sql_join_cache.cc | 19 ++++++++++++++++--- sql/sql_select.cc | 18 ++++++++++++------ sql/sql_select.h | 10 ---------- 3 files changed, 28 insertions(+), 19 deletions(-) (limited to 'sql') diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index f292375ee6f..bbc7e5cd26c 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -225,8 +225,6 @@ void JOIN_CACHE::calc_record_fields() flag_fields+= test(tab->table->maybe_null); fields+= tab->used_fields; blobs+= tab->used_blobs; - - fields+= tab->check_rowid_field(); } if ((with_match_flag= join_tab->use_match_flag())) flag_fields++; @@ -620,7 +618,12 @@ void JOIN_CACHE::create_remaining_fields() copy->type= CACHE_ROWID; copy->field= 0; copy->referenced_field_no= 0; - length+= copy->length; + /* + Note: this may seem odd, but at this point we have + table->file->ref==NULL while table->file->ref_length is already set + to correct value. + */ + length += table->file->ref_length; data_field_count++; copy++; } @@ -1297,6 +1300,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) if (with_length) { rec_len_ptr= cp; + DBUG_ASSERT(cp + size_of_rec_len <= buff + buff_size); cp+= size_of_rec_len; } @@ -1306,6 +1310,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) */ if (prev_cache) { + DBUG_ASSERT(cp + prev_cache->get_size_of_rec_offset() <= buff + buff_size); cp+= prev_cache->get_size_of_rec_offset(); prev_cache->store_rec_ref(cp, link); } @@ -1322,6 +1327,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) flags_pos= cp; for ( ; copy < copy_end; copy++) { + DBUG_ASSERT(cp + copy->length <= buff + buff_size); memcpy(cp, copy->str, copy->length); cp+= copy->length; } @@ -1348,6 +1354,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) { last_rec_blob_data_is_in_rec_buff= 1; /* Put down the length of the blob and the pointer to the data */ + DBUG_ASSERT(cp + copy->length + sizeof(char*) <= buff + buff_size); blob_field->get_image(cp, copy->length+sizeof(char*), blob_field->charset()); cp+= copy->length+sizeof(char*); @@ -1357,6 +1364,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) /* First put down the length of the blob and then copy the data */ blob_field->get_image(cp, copy->length, blob_field->charset()); + DBUG_ASSERT(cp + copy->length + copy->blob_length <= buff + buff_size); memcpy(cp+copy->length, copy->str, copy->blob_length); cp+= copy->length+copy->blob_length; } @@ -1367,12 +1375,14 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) case CACHE_VARSTR1: /* Copy the significant part of the short varstring field */ len= (uint) copy->str[0] + 1; + DBUG_ASSERT(cp + len <= buff + buff_size); memcpy(cp, copy->str, len); cp+= len; break; case CACHE_VARSTR2: /* Copy the significant part of the long varstring field */ len= uint2korr(copy->str) + 2; + DBUG_ASSERT(cp + len <= buff + buff_size); memcpy(cp, copy->str, len); cp+= len; break; @@ -1387,6 +1397,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) end > str && end[-1] == ' '; end--) ; len=(uint) (end-str); + DBUG_ASSERT(cp + len + 2 <= buff + buff_size); int2store(cp, len); memcpy(cp+2, str, len); cp+= len+2; @@ -1406,6 +1417,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) /* fall through */ default: /* Copy the entire image of the field from the record buffer */ + DBUG_ASSERT(cp + copy->length <= buff + buff_size); memcpy(cp, copy->str, copy->length); cp+= copy->length; } @@ -1425,6 +1437,7 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) cnt++; } } + DBUG_ASSERT(cp + size_of_fld_ofs*cnt <= buff + buff_size); cp+= size_of_fld_ofs*cnt; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 6ddb350db1d..ad683ea67bb 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -6572,6 +6572,16 @@ void JOIN_TAB::calc_used_field_length(bool max_fl) rec_length+=(table->s->null_fields+7)/8; if (table->maybe_null) rec_length+=sizeof(my_bool); + + /* Take into account that DuplicateElimination may need to store rowid */ + uint rowid_add_size= 0; + if (keep_current_rowid) + { + rowid_add_size= table->file->ref_length; + rec_length += rowid_add_size; + fields++; + } + if (max_fl) { // TODO: to improve this estimate for max expected length @@ -6585,13 +6595,9 @@ void JOIN_TAB::calc_used_field_length(bool max_fl) } max_used_fieldlength= rec_length; } - else if (table->file->stats.mean_rec_length) - set_if_smaller(rec_length, table->file->stats.mean_rec_length); + else if (table->file->stats.mean_rec_length) + set_if_smaller(rec_length, table->file->stats.mean_rec_length + rowid_add_size); - /* - TODO: why we don't count here rowid that we might need to store when - using DuplicateElimination? - */ used_fields=fields; used_fieldlength=rec_length; used_blobs=blobs; diff --git a/sql/sql_select.h b/sql/sql_select.h index 15711dca7b3..676cc7452f4 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -288,7 +288,6 @@ typedef struct st_join_table { ulong max_used_fieldlength; uint used_blobs; uint used_null_fields; - uint used_rowid_fields; uint used_uneven_bit_fields; enum join_type type; bool cached_eq_ref_table,eq_ref_table,not_used_in_distinct; @@ -387,15 +386,6 @@ typedef struct st_join_table { (select->quick->get_type() == QUICK_SELECT_I::QS_TYPE_GROUP_MIN_MAX)); } - bool check_rowid_field() - { - if (keep_current_rowid && !used_rowid_fields) - { - used_rowid_fields= 1; - used_fieldlength+= table->file->ref_length; - } - return test(used_rowid_fields); - } bool is_inner_table_of_semi_join_with_first_match() { return first_sj_inner_tab != NULL; -- cgit v1.2.1 From 3769841d9e706ee018d5273d2901954b9a281c3e Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 8 Sep 2011 23:24:47 +0400 Subject: BUG#833600: Wrong result with view + outer join + uncorrelated subquery (non-semijoin) - The bug was caused by outer join being incorrectly converted into inner because of invalid return values of Item_direct_view_ref::not_null_tables(). - Provided a correct Item_direct_view_ref::not_null_tables() function. --- sql/item.cc | 6 ++++++ sql/item.h | 1 + 2 files changed, 7 insertions(+) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index 4fcffcfbc2a..d0992a379e0 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -9106,6 +9106,12 @@ table_map Item_direct_view_ref::used_tables() const (view->merged ? (*ref)->used_tables() : view->table->map); } +table_map Item_direct_view_ref::not_null_tables() const +{ + return get_depended_from() ? + 0 : + (view->merged ? (*ref)->not_null_tables() : view->table->map); +} /* we add RAND_TABLE_BIT to prevent moving this item from HAVING to WHERE diff --git a/sql/item.h b/sql/item.h index 99eabdd81ab..8cacd231fbf 100644 --- a/sql/item.h +++ b/sql/item.h @@ -2919,6 +2919,7 @@ public: Item *equal_fields_propagator(uchar *arg); Item *replace_equal_field(uchar *arg); table_map used_tables() const; + table_map not_null_tables() const; bool walk(Item_processor processor, bool walk_subquery, uchar *arg) { return (*ref)->walk(processor, walk_subquery, arg) || -- cgit v1.2.1 From 8fb10c24d74cbddbfd6da0b5f4ea9409f8f88e57 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Fri, 9 Sep 2011 19:44:07 +0300 Subject: Fixed that automatic killing of delayed insert thread (in flush, alter table etc) will not abort auto-repair of MyISAM table. Give more information when finding an error in a MyISAM table. When killing system thread, use KILL_SYSTEM_THREAD instead of KILL_CONNECTION to make it easier to ignore the signal in sensitive context (like auto-repair) Added new kill level: KILL_SERVER that will in the future to be used to signal killed by shutdown. Add more warnings about killed connections when warning level > 3 include/myisamchk.h: Added counting of printed info/notes mysys/mf_iocache.c: Remove duplicate assignment sql/handler.cc: Added test of KILL_SERVER sql/log.cc: Ignore new 'kill' error ER_NEW_ABORTING_CONNECTION when requesting query error code. sql/mysqld.cc: Add more warnings for killed connections when warning level > 3 sql/scheduler.cc: Added checks for new kill signals sql/slave.cc: Ignore new kill signal ER_NEW_ABORTING_CONNECTION sql/sp_head.cc: Fixed assignment to bool Added testing of new kill signals sql/sql_base.cc: Use KILL_SYSTEM_THREAD to auto-kill system threads sql/sql_class.cc: Add more warnings for killed connections when warning level > 3 thd_killed() now ignores KILL_BAD_DATA and THD::KILL_SYSTEM_THREAD as these should not abort sensitive operations. sql/sql_class.h: Added KILL_SYSTEM_THREAD and KILL_SERVER sql/sql_connect.cc: Added handling of KILL_SERVER sql/sql_insert.cc: Use KILL_SYSTEM_THREAD to auto-kill system threads Added handling of KILL_SERVER sql/sql_parse.cc: Add more warnings for killed connections when warning level > 3 Added checking that thd->abort_on_warning is reset at end of query. sql/sql_show.cc: Update condition for when a query is 'killed' storage/myisam/ha_myisam.cc: Added counting of info/notes printed storage/myisam/mi_check.c: Always print an an error if we find data errors when checking/repairing a MyISAM table. When a repair was killed, don't retry repair. Added assert if sort_get_next_record() returned an error without an error message. Removed nonsence check "if (sort_param->read_cache.error < 0)" in repair. storage/myisam/myisamchk.c: Added counting of notes printed storage/pbxt/src/thread_xt.cc: Better error message. --- sql/handler.cc | 7 ++++++- sql/log.cc | 3 ++- sql/mysqld.cc | 35 +++++++++++++++++------------------ sql/scheduler.cc | 11 +++++++---- sql/slave.cc | 1 + sql/sp_head.cc | 9 ++++++--- sql/sql_base.cc | 4 ++-- sql/sql_class.cc | 14 +++++++++++++- sql/sql_class.h | 2 ++ sql/sql_connect.cc | 3 ++- sql/sql_insert.cc | 17 +++++++++-------- sql/sql_parse.cc | 17 +++++++++++++++++ sql/sql_show.cc | 8 ++++++-- 13 files changed, 90 insertions(+), 41 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index f5e7de371a4..0e683d38511 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1350,9 +1350,14 @@ int ha_rollback_trans(THD *thd, bool all) slave SQL thread, it would not stop the thread but just be printed in the error log; but we don't want users to wonder why they have this message in the error log, so we don't send it. + + We don't have to test for thd->killed == THD::KILL_SYSTEM_THREAD as + it doesn't matter if a warning is pushed to a system thread or not: + No one will see it... */ if (is_real_trans && thd->transaction.all.modified_non_trans_table && - !thd->slave_thread && thd->killed != THD::KILL_CONNECTION) + !thd->slave_thread && thd->killed != THD::KILL_CONNECTION && + thd->killed != THD::KILL_SERVER) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARNING_NOT_COMPLETE_ROLLBACK, ER(ER_WARNING_NOT_COMPLETE_ROLLBACK)); diff --git a/sql/log.cc b/sql/log.cc index 5f6656f27de..12369640b72 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -4919,7 +4919,8 @@ int query_error_code(THD *thd, bool not_killed) is not set to these errors when specified not_killed by the caller. */ - if (error == ER_SERVER_SHUTDOWN || error == ER_QUERY_INTERRUPTED) + if (error == ER_SERVER_SHUTDOWN || error == ER_QUERY_INTERRUPTED || + error == ER_NEW_ABORTING_CONNECTION) error= 0; } else diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 7131188b306..9c16561e8b3 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1968,6 +1968,17 @@ void close_connection(THD *thd, uint errcode, bool lock) if (lock) (void) pthread_mutex_lock(&LOCK_thread_count); thd->killed= THD::KILL_CONNECTION; + + if (global_system_variables.log_warnings > 3) + { + Security_context *sctx= &thd->main_security_ctx; + sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), + thd->thread_id,(thd->db ? thd->db : "unconnected"), + sctx->user ? sctx->user : "unauthenticated", + sctx->host_or_ip, + (errcode ? ER(errcode) : "CLOSE_CONNECTION")); + } + if ((vio= thd->net.vio) != 0) { if (errcode) @@ -2129,24 +2140,6 @@ void flush_thread_cache() } -#ifdef THREAD_SPECIFIC_SIGPIPE -/** - Aborts a thread nicely. Comes here on SIGPIPE. - - @todo - One should have to fix that thr_alarm know about this thread too. -*/ -extern "C" sig_handler abort_thread(int sig __attribute__((unused))) -{ - THD *thd=current_thd; - DBUG_ENTER("abort_thread"); - if (thd) - thd->killed= THD::KILL_CONNECTION; - DBUG_VOID_RETURN; -} -#endif - - /****************************************************************************** Setup a signal thread with handles all signals. Because Linux doesn't support schemas use a mutex to check that @@ -2681,6 +2674,12 @@ the thread stack. Please read http://dev.mysql.com/doc/mysql/en/linux.html\n\n", case THD::KILL_QUERY: kreason= "KILL_QUERY"; break; + case THD::KILL_SYSTEM_THREAD: + kreason= "KILL_SYSTEM_THREAD"; + break; + case THD::KILL_SERVER: + kreason= "KILL_SERVER"; + break; case THD::KILLED_NO_VALUE: kreason= "KILLED_NO_VALUE"; break; diff --git a/sql/scheduler.cc b/sql/scheduler.cc index 5b8f834aecc..6dd93640dc5 100644 --- a/sql/scheduler.cc +++ b/sql/scheduler.cc @@ -376,7 +376,9 @@ void libevent_kill_thd_callback(int Fd, short, void*) { THD *thd= (THD*)list->data; list= list_rest(list); - if (thd->killed == THD::KILL_CONNECTION) + if (thd->killed == THD::KILL_CONNECTION || + thd->killed == THD::KILL_SYSTEM_THREAD || + thd->killed == THD::KILL_SERVER) { /* Delete from libevent and add to the processing queue. @@ -531,9 +533,10 @@ static void libevent_connection_close(THD *thd) static bool libevent_should_close_connection(THD* thd) { - return thd->net.error || - thd->net.vio == 0 || - thd->killed == THD::KILL_CONNECTION; + return (thd->net.error || + thd->net.vio == 0 || + thd->killed == THD::KILL_CONNECTION || + thd->killed == THD::KILL_SERVER); } diff --git a/sql/slave.cc b/sql/slave.cc index 435f3d8b95f..439b9d8f4d3 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -837,6 +837,7 @@ bool is_network_error(uint errorno) errorno == CR_SERVER_GONE_ERROR || errorno == CR_SERVER_LOST || errorno == ER_CON_COUNT_ERROR || + errorno == ER_NEW_ABORTING_CONNECTION || errorno == ER_SERVER_SHUTDOWN) return TRUE; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index dbaeb9467ae..f4bcf500727 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -386,8 +386,8 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr) thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL; thd->abort_on_warning= - thd->variables.sql_mode & - (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES); + test(thd->variables.sql_mode & + (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES)); thd->transaction.stmt.modified_non_trans_table= FALSE; /* Save the value in the field. Convert the value if needed. */ @@ -1374,7 +1374,10 @@ sp_head::execute(THD *thd) If the DB has changed, the pointer has changed too, but the original thd->db will then have been freed */ - if (cur_db_changed && thd->killed != THD::KILL_CONNECTION) + if (cur_db_changed && + thd->killed != THD::KILL_CONNECTION && + thd->killed != THD::KILL_SERVER && + thd->killed != THD::KILL_SYSTEM_THREAD) { /* Force switching back to the saved current database, because it may be diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 4826603c720..01649c88b6b 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8743,7 +8743,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, { if (!in_use->killed) { - in_use->killed= THD::KILL_CONNECTION; + in_use->killed= THD::KILL_SYSTEM_THREAD; pthread_mutex_lock(&in_use->mysys_var->mutex); if (in_use->mysys_var->current_cond) { @@ -9077,7 +9077,7 @@ void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && ! in_use->killed) { - in_use->killed= THD::KILL_CONNECTION; + in_use->killed= THD::KILL_SYSTEM_THREAD; pthread_mutex_lock(&in_use->mysys_var->mutex); if (in_use->mysys_var->current_cond) { diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 0b6a3ebf85e..41e59489429 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1254,6 +1254,15 @@ void THD::awake(THD::killed_state state_to_set) THD_CHECK_SENTRY(this); safe_mutex_assert_owner(&LOCK_thd_data); + if (global_system_variables.log_warnings > 3) + { + Security_context *sctx= security_ctx; + sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), + thread_id,(db ? db : "unconnected"), + sctx->user ? sctx->user : "unauthenticated", + sctx->host_or_ip, + "KILLED"); + } killed= state_to_set; if (state_to_set != THD::KILL_QUERY) { @@ -3280,7 +3289,10 @@ void THD::restore_backup_open_tables_state(Open_tables_state *backup) */ extern "C" int thd_killed(const MYSQL_THD thd) { - return(thd->killed); + if (thd->killed == THD::NOT_KILLED || thd->killed == THD::KILL_BAD_DATA || + thd->killed == THD::KILL_SYSTEM_THREAD) + return 0; + return thd->killed; } /** diff --git a/sql/sql_class.h b/sql/sql_class.h index 53c1db1a527..57fc3883b1c 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1838,7 +1838,9 @@ public: NOT_KILLED=0, KILL_BAD_DATA=1, KILL_CONNECTION=ER_SERVER_SHUTDOWN, + KILL_SYSTEM_THREAD=ER_NEW_ABORTING_CONNECTION, /* Kill connection nicely */ KILL_QUERY=ER_QUERY_INTERRUPTED, + KILL_SERVER, /* Placeholder for shortdown */ KILLED_NO_VALUE /* means neither of the states */ }; killed_state volatile killed; diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index be53c60e8ca..ec9ea31d0a4 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -1180,7 +1180,8 @@ pthread_handler_t handle_one_connection(void *arg) prepare_new_connection_state(thd); while (!net->error && net->vio != 0 && - !(thd->killed == THD::KILL_CONNECTION)) + thd->killed != THD::KILL_CONNECTION && + thd->killed != THD::KILL_SERVER) { if (do_command(thd)) break; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 1286c56f30e..ca0a7ab8d22 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2314,7 +2314,7 @@ void kill_delayed_threads(void) Delayed_insert *di; while ((di= it++)) { - di->thd.killed= THD::KILL_CONNECTION; + di->thd.killed= THD::KILL_SYSTEM_THREAD; pthread_mutex_lock(&di->thd.LOCK_thd_data); if (di->thd.mysys_var) { @@ -2399,7 +2399,8 @@ static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di) for (;;) { - if (thd->killed == THD::KILL_CONNECTION) + if (thd->killed == THD::KILL_CONNECTION || + thd->killed == THD::KILL_SYSTEM_THREAD || thd->killed == THD::KILL_SERVER) { uint lock_count; /* @@ -2447,7 +2448,7 @@ static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di) break; if (error == ETIMEDOUT || error == ETIME) { - thd->killed= THD::KILL_CONNECTION; + thd->killed= THD::KILL_SYSTEM_THREAD; break; } } @@ -2480,7 +2481,7 @@ static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di) { /* Fatal error */ di->dead= 1; - thd->killed= THD::KILL_CONNECTION; + thd->killed= THD::KILL_SYSTEM_THREAD; } pthread_cond_broadcast(&di->cond_client); } @@ -2490,7 +2491,7 @@ static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di) { /* Some fatal error */ di->dead= 1; - thd->killed= THD::KILL_CONNECTION; + thd->killed= THD::KILL_SYSTEM_THREAD; } } di->status=0; @@ -2550,7 +2551,7 @@ pthread_handler_t handle_delayed_insert(void *arg) thd->thread_id= thd->variables.pseudo_thread_id= thread_id++; thd->set_current_time(); threads.append(thd); - thd->killed=abort_loop ? THD::KILL_CONNECTION : THD::NOT_KILLED; + thd->killed=abort_loop ? THD::KILL_SYSTEM_THREAD : THD::NOT_KILLED; pthread_mutex_unlock(&LOCK_thread_count); /* @@ -2584,7 +2585,7 @@ end: di->table=0; di->dead= 1; // If error - thd->killed= THD::KILL_CONNECTION; // If error + thd->killed= THD::KILL_SYSTEM_THREAD; // If error pthread_mutex_unlock(&di->mutex); close_thread_tables(thd); // Free the table @@ -2663,7 +2664,7 @@ bool Delayed_insert::handle_inserts(void) max_rows= delayed_insert_limit; if (thd.killed || table->needs_reopen_or_name_lock()) { - thd.killed= THD::KILL_CONNECTION; + thd.killed= THD::KILL_SYSTEM_THREAD; max_rows= ULONG_MAX; // Do as much as possible } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0fdc0b1690c..2050350d450 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -789,7 +789,17 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion) if (res < 0) my_error(thd->killed_errno(), MYF(0)); else if ((res == 0) && do_release) + { thd->killed= THD::KILL_CONNECTION; + if (global_system_variables.log_warnings > 3) + { + Security_context *sctx= &thd->main_security_ctx; + sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), + thd->thread_id,(thd->db ? thd->db : "unconnected"), + sctx->user ? sctx->user : "unauthenticated", + sctx->host_or_ip, "RELEASE"); + } + } DBUG_RETURN(res); } @@ -1670,6 +1680,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, thd_proc_info(thd, 0); thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); + + /* Check that some variables are reset properly */ + DBUG_ASSERT(thd->abort_on_warning == 0); DBUG_RETURN(error); } @@ -7258,6 +7271,10 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query) If user of both killer and killee are non-NULL, proceed with slayage if both are string-equal. + + It's ok to also kill DELAYED threads with KILL_CONNECTION instead of + KILL_SYSTEM_THREAD; The difference is that KILL_CONNECTION may be + faster and do a harder kill than KILL_SYSTEM_THREAD; */ if ((thd->security_ctx->master_access & SUPER_ACL) || diff --git a/sql/sql_show.cc b/sql/sql_show.cc index e10488cb87c..0c422632f35 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1935,7 +1935,9 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) pthread_mutex_lock(&tmp->LOCK_thd_data); if ((mysys_var= tmp->mysys_var)) pthread_mutex_lock(&mysys_var->mutex); - thd_info->proc_info= (char*) (tmp->killed == THD::KILL_CONNECTION? "Killed" : 0); + thd_info->proc_info= (char*) (tmp->killed != THD::NOT_KILLED && + tmp->killed != THD::KILL_BAD_DATA ? + "Killed" : 0); #ifndef EMBEDDED_LIBRARY thd_info->state_info= (char*) (tmp->locked ? "Locked" : tmp->net.reading_or_writing ? @@ -2053,7 +2055,9 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) if ((mysys_var= tmp->mysys_var)) pthread_mutex_lock(&mysys_var->mutex); /* COMMAND */ - if ((val= (char *) (tmp->killed == THD::KILL_CONNECTION? "Killed" : 0))) + if ((val= (char *) ((tmp->killed != THD::NOT_KILLED && + tmp->killed != THD::KILL_BAD_DATA ? + "Killed" : 0)))) table->field[4]->store(val, strlen(val), cs); else table->field[4]->store(command_name[tmp->command].str, -- cgit v1.2.1 From 4139bba2817387233e126d9e48084fc0c60718e7 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Tue, 13 Sep 2011 18:46:47 +0300 Subject: Increased version number Give proper error to client on shutdown. configure.in: Increased version number mysql-test/mysql-test-run.pl: Ignore errors that one can get while running with --mysqld=--log-warnings=2 mysql-test/r/variables.result: Remember original value of log_warnings mysql-test/suite/rpl/r/rpl_idempotency.result: Ignore errors that one can get while running with --mysqld=--log-warnings=2 mysql-test/suite/rpl/t/rpl_idempotency.test: Ignore errors that one can get while running with --mysqld=--log-warnings=2 mysql-test/t/variables.test: Remember original value of log_warnings sql/mysqld.cc: Give proper error to close_connection() on shutdown storage/maria/ha_maria.cc: Added missing DBUG_RETURN --- sql/mysqld.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 9c16561e8b3..e841bae43a4 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1079,7 +1079,7 @@ static void close_connections(void) tmp->thread_id, (tmp->main_security_ctx.user ? tmp->main_security_ctx.user : "")); - close_connection(tmp,0,0); + close_connection(tmp,ER_SERVER_SHUTDOWN,0); } #endif DBUG_PRINT("quit",("Unlocking LOCK_thread_count")); @@ -5215,7 +5215,7 @@ void create_thread_to_handle_connection(THD *thd) ER(ER_CANT_CREATE_THREAD), error); net_send_error(thd, ER_CANT_CREATE_THREAD, error_message_buff); (void) pthread_mutex_lock(&LOCK_thread_count); - close_connection(thd,0,0); + close_connection(thd,ER_OUT_OF_RESOURCES,0); delete thd; (void) pthread_mutex_unlock(&LOCK_thread_count); return; @@ -6503,7 +6503,7 @@ each time the SQL thread starts.", "log and this option just turns on --log-bin instead.", &opt_update_logname, &opt_update_logname, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, - {"log-warnings", 'W', "Log some not critical warnings to the general log file. Value can be between 0-6; The higher value, the more warnings", + {"log-warnings", 'W', "Log some not critical warnings to the general log file. Value can be between 0-11; The higher value, the more warnings", &global_system_variables.log_warnings, &max_system_variables.log_warnings, 0, GET_ULONG, OPT_ARG, 1, 0, 0, 0, 0, 0}, -- cgit v1.2.1 From 5ebff8ab788923cfcc25862d6c9e11ea20801fa0 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 13 Sep 2011 23:45:02 +0400 Subject: BUG#730133: Wrong result with jkl = 7, BKA, ICP in maria-5.3 + compound index - Mrr_ordered_index_reader::interrupt_read() and resume_read() should save/restore not just index lookup tuple, but entire index tuple. Key parts that are not used for index lookup can be still used in pushed index condition. Failure to save/restore will cause the index condition to be evaluated over the wrong values. --- sql/item.cc | 15 +++++++++++++++ sql/multi_range_read.cc | 15 ++++++++------- 2 files changed, 23 insertions(+), 7 deletions(-) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index d0992a379e0..02263106fe8 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -9124,7 +9124,22 @@ table_map Item_ref_null_helper::used_tables() const } +/* Debugger help function */ +static char dbug_item_print_buf[256]; +const char *dbug_print_item(Item *item) +{ + char *buf= dbug_item_print_buf; + String str(buf, sizeof(dbug_item_print_buf), &my_charset_bin); + str.length(0); + if (!item) + return "(Item*)NULL"; + item->print(&str ,QT_ORDINARY); + if (str.c_ptr() == buf) + return buf; + else + return "Couldn't fit into buffer"; +} /***************************************************************************** ** Instantiate templates *****************************************************************************/ diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc index f0a1987c383..08e91559fc6 100644 --- a/sql/multi_range_read.cc +++ b/sql/multi_range_read.cc @@ -424,10 +424,10 @@ void Mrr_ordered_index_reader::interrupt_read() { DBUG_ASSERT(support_scan_interruptions); TABLE *table= file->get_table(); + KEY *used_index= &table->key_info[file->active_index]; /* Save the current key value */ key_copy(saved_key_tuple, table->record[0], - &table->key_info[file->active_index], - keypar.key_tuple_length); + used_index, used_index->key_length); if (saved_primary_key) { @@ -452,9 +452,9 @@ void Mrr_ordered_index_reader::position() void Mrr_ordered_index_reader::resume_read() { TABLE *table= file->get_table(); + KEY *used_index= &table->key_info[file->active_index]; key_restore(table->record[0], saved_key_tuple, - &table->key_info[file->active_index], - keypar.key_tuple_length); + used_index, used_index->key_length); if (saved_primary_key) { key_restore(table->record[0], saved_primary_key, @@ -531,7 +531,7 @@ int Mrr_ordered_index_reader::init(handler *h_arg, RANGE_SEQ_IF *seq_funcs, mrr_funcs= *seq_funcs; source_exhausted= FALSE; if (support_scan_interruptions) - bzero(saved_key_tuple, keypar.key_tuple_length); + bzero(saved_key_tuple, key_info->key_length); have_saved_rowid= FALSE; return 0; } @@ -850,10 +850,11 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs, uint pk= h_idx->get_table()->s->primary_key; saved_pk_length= h_idx->get_table()->key_info[pk].key_length; } - + + KEY *used_index= &h_idx->get_table()->key_info[h_idx->active_index]; if (reader_factory.ordered_index_reader. set_interruption_temp_buffer(primary_file->ref_length, - keypar.key_tuple_length, + used_index->key_length, saved_pk_length, &full_buf, full_buf_end)) goto use_default_impl; -- cgit v1.2.1 From 27cd8d7b70cc22693b9dab35ac2d1e720b8fc7a7 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Sat, 17 Sep 2011 23:53:50 +0400 Subject: BUG##849717: Crash in Item_func::fix_fields on second execution of a prepared statement with semijoin - If convert_join_subqueries_to_semijoins() decides to wrap Item_in_subselect in Item_in_optimizer, it should do so in prep_on_expr/prep_where, too, as long as they are present. There seems to be two possibilities of how we arrive in this function: - prep_on_expr/prep_where==NULL, and will be set later by simplify_joins() - prep_on_expr/prep_where!=NULL, and it is a copy_and_or_structure()-made copy of on_expr/where. the latter can happen for some (but not all!) nested joins. This bug was that we didn't handle this case. --- sql/opt_subselect.cc | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'sql') diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 3cb13b0b88c..beeb0ac878e 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -886,12 +886,6 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) do_fix_fields)) DBUG_RETURN(TRUE); in_subq->substitution= NULL; -#if 0 - /* - Don't do the following, because the simplify_join() call is after this - call, and that call will save to prep_wher/prep_on_expr. - */ - /* If this is a prepared statement, repeat the above operation for prep_where (or prep_on_expr). Subquery-to-semijoin conversion is @@ -902,12 +896,15 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) tree= (in_subq->emb_on_expr_nest == NO_JOIN_NEST)? &join->select_lex->prep_where : &(in_subq->emb_on_expr_nest->prep_on_expr); - - if (replace_where_subcondition(join, tree, replace_me, substitute, + /* + prep_on_expr/ prep_where may be NULL in some cases. + If that is the case, do nothing - simplify_joins() will copy + ON/WHERE expression into prep_on_expr/prep_where. + */ + if (*tree && replace_where_subcondition(join, tree, replace_me, substitute, FALSE)) DBUG_RETURN(TRUE); } -#endif /* Revert to the IN->EXISTS strategy in the rare case when the subquery could not be flattened. -- cgit v1.2.1 From f0323a40d8cbc5228015c1565a4800fd05fd61a8 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 20 Sep 2011 20:40:07 +0400 Subject: BUG#849763: Wrong result with second execution of prepared statement with semijoin + view - The problem was that Item_direct_view_ref and its embedded Item_field were getting incorrect value of item->used_tables() after fix_fields() in the second and subsequent EXECUTE. - Made relevant fixes in Item_field::fix_fields() and find_field_in_tables(), so that the Item_field gets the correct attributes. --- sql/item.cc | 1 + sql/sql_base.cc | 16 +++++++++++++++- sql/sql_lex.cc | 41 +++++++++++++++++++++++++++++++++++++++++ sql/sql_lex.h | 2 +- 4 files changed, 58 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index 02263106fe8..a4cd292a5b1 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -4648,6 +4648,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) if (!outer_fixed && cached_table && cached_table->select_lex && context->select_lex && cached_table->select_lex != context->select_lex && + !context->select_lex->is_merged_child_of(cached_table->select_lex) && is_outer_table(cached_table, context->select_lex)) { int ret; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index bca8167f2b9..e2142bc2734 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -6447,11 +6447,25 @@ find_field_in_tables(THD *thd, Item_ident *item, { SELECT_LEX *current_sel= thd->lex->current_select; SELECT_LEX *last_select= table_ref->select_lex; + bool all_merged= TRUE; + for (SELECT_LEX *sl= current_sel; sl && sl!=last_select; + sl=sl->outer_select()) + { + Item *subs= sl->master_unit()->item; + if (subs->type() == Item::SUBSELECT_ITEM && + ((Item_subselect*)subs)->substype() == Item_subselect::IN_SUBS && + ((Item_in_subselect*)subs)->in_strategy & SUBS_SEMI_JOIN) + { + continue; + } + all_merged= FALSE; + break; + } /* If the field was an outer referencee, mark all selects using this sub query as dependent on the outer query */ - if (current_sel != last_select) + if (!all_merged && current_sel != last_select) { mark_select_range_as_dependent(thd, last_select, current_sel, found, *ref, item); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 23662339cff..72cf0179449 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3622,6 +3622,47 @@ bool st_select_lex::save_prep_leaf_tables(THD *thd) } +/* + Return true if this select_lex has been converted into a semi-join nest + within 'ancestor'. + + We need a loop to check this because there could be several nested + subselects, like + + SELECT ... FROM grand_parent + WHERE expr1 IN (SELECT ... FROM parent + WHERE expr2 IN ( SELECT ... FROM child) + + which were converted into: + + SELECT ... + FROM grand_parent SEMI_JOIN (parent JOIN child) + WHERE + expr1 AND expr2 + + In this case, both parent and child selects were merged into the parent. +*/ + +bool st_select_lex::is_merged_child_of(st_select_lex *ancestor) +{ + bool all_merged= TRUE; + for (SELECT_LEX *sl= this; sl && sl!=ancestor; + sl=sl->outer_select()) + { + Item *subs= sl->master_unit()->item; + if (subs && subs->type() == Item::SUBSELECT_ITEM && + ((Item_subselect*)subs)->substype() == Item_subselect::IN_SUBS && + ((Item_in_subselect*)subs)->in_strategy & SUBS_SEMI_JOIN) + { + continue; + } + all_merged= FALSE; + break; + } + return all_merged; +} + + /** A routine used by the parser to decide whether we are specifying a full partitioning or if only partitions to add or to split. diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 0da628b7ca0..4ef24876474 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -907,7 +907,7 @@ public: bool save_leaf_tables(THD *thd); bool save_prep_leaf_tables(THD *thd); - + bool is_merged_child_of(st_select_lex *ancestor); private: /* current index hint kind. used in filling up index_hints */ enum index_hint_type current_index_hint_type; -- cgit v1.2.1 From 42b928ca05fca3ae8858566622a3a483a70aa3fc Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 22 Sep 2011 01:55:17 +0400 Subject: Fix after previous cset: update test results --- sql/item_subselect.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sql') diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index a70d04fccbe..50533c1a7ff 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -2477,6 +2477,8 @@ void Item_in_subselect::fix_after_pullout(st_select_lex *new_parent, Item **ref) { left_expr->fix_after_pullout(new_parent, &left_expr); Item_subselect::fix_after_pullout(new_parent, ref); + //psergey-todo: the above looks odd, why don't we 'aggregate' left_expr with + //the rest? } void Item_in_subselect::update_used_tables() -- cgit v1.2.1 From 3b31fbb5a9eab5247a52e865cdd94c431e6dc12f Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 22 Sep 2011 11:04:00 +0200 Subject: portability fix: avoid anonymous structs and unions in C --- sql/item_strfunc.cc | 144 ++++++++++++++++++++++++++-------------------------- 1 file changed, 72 insertions(+), 72 deletions(-) (limited to 'sql') diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index a2f600d8473..9abbdfcec4f 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -3606,40 +3606,40 @@ void Item_func_dyncol_create::prepare_arguments() DBUG_ASSERT(args[valpos]->field_type() == MYSQL_TYPE_NULL); break; case DYN_COL_INT: - vals[i].long_value= args[valpos]->val_int(); + vals[i].x.long_value= args[valpos]->val_int(); break; case DYN_COL_UINT: - vals[i].ulong_value= args[valpos]->val_int(); + vals[i].x.ulong_value= args[valpos]->val_int(); break; case DYN_COL_DOUBLE: - vals[i].double_value= args[valpos]->val_real(); + vals[i].x.double_value= args[valpos]->val_real(); break; case DYN_COL_STRING: res= args[valpos]->val_str(&tmp); if (res && - (vals[i].string_value.str= my_strndup(res->ptr(), res->length(), + (vals[i].x.string.value.str= my_strndup(res->ptr(), res->length(), MYF(MY_WME)))) { - vals[i].string_value.length= res->length(); - vals[i].charset= res->charset(); + vals[i].x.string.value.length= res->length(); + vals[i].x.string.charset= res->charset(); } else { args[valpos]->null_value= 1; // In case of out of memory - vals[i].string_value.str= NULL; - vals[i].string_value.length= 0; // just to be safe + vals[i].x.string.value.str= NULL; + vals[i].x.string.value.length= 0; // just to be safe } break; case DYN_COL_DECIMAL: if ((dres= args[valpos]->val_decimal(&dtmp))) { dynamic_column_prepare_decimal(&vals[i]); - DBUG_ASSERT(vals[i].decimal_value.len == dres->len); - vals[i].decimal_value.intg= dres->intg; - vals[i].decimal_value.frac= dres->frac; - vals[i].decimal_value.sign= dres->sign(); - memcpy(vals[i].decimal_buffer, dres->buf, - sizeof(vals[i].decimal_buffer)); + DBUG_ASSERT(vals[i].x.decimal.value.len == dres->len); + vals[i].x.decimal.value.intg= dres->intg; + vals[i].x.decimal.value.frac= dres->frac; + vals[i].x.decimal.value.sign= dres->sign(); + memcpy(vals[i].x.decimal.buffer, dres->buf, + sizeof(vals[i].x.decimal.buffer)); } else { @@ -3648,13 +3648,13 @@ void Item_func_dyncol_create::prepare_arguments() } break; case DYN_COL_DATETIME: - args[valpos]->get_date(&vals[i].time_value, TIME_FUZZY_DATE); + args[valpos]->get_date(&vals[i].x.time_value, TIME_FUZZY_DATE); break; case DYN_COL_DATE: - args[valpos]->get_date(&vals[i].time_value, TIME_FUZZY_DATE); + args[valpos]->get_date(&vals[i].x.time_value, TIME_FUZZY_DATE); break; case DYN_COL_TIME: - args[valpos]->get_time(&vals[i].time_value); + args[valpos]->get_time(&vals[i].x.time_value); break; default: DBUG_ASSERT(0); @@ -3663,7 +3663,7 @@ void Item_func_dyncol_create::prepare_arguments() if (vals[i].type != DYN_COL_NULL && args[valpos]->null_value) { if (vals[i].type == DYN_COL_STRING) - my_free(vals[i].string_value.str, MYF(MY_ALLOW_ZERO_PTR)); + my_free(vals[i].x.string.value.str, MYF(MY_ALLOW_ZERO_PTR)); vals[i].type= DYN_COL_NULL; } } @@ -3677,7 +3677,7 @@ void Item_func_dyncol_create::cleanup_arguments() for (i= 0; i < column_count; i++) { if (vals[i].type == DYN_COL_STRING) - my_free(vals[i].string_value.str, MYF(MY_ALLOW_ZERO_PTR)); + my_free(vals[i].x.string.value.str, MYF(MY_ALLOW_ZERO_PTR)); } } @@ -3894,19 +3894,19 @@ String *Item_dyncol_get::val_str(String *str_result) goto null; case DYN_COL_INT: case DYN_COL_UINT: - str_result->set_int(val.long_value, test(val.type == DYN_COL_UINT), + str_result->set_int(val.x.long_value, test(val.type == DYN_COL_UINT), &my_charset_latin1); break; case DYN_COL_DOUBLE: - str_result->set_real(val.double_value, NOT_FIXED_DEC, &my_charset_latin1); + str_result->set_real(val.x.double_value, NOT_FIXED_DEC, &my_charset_latin1); break; case DYN_COL_STRING: - if ((char*) tmp.ptr() <= val.string_value.str && - (char*) tmp.ptr() + tmp.length() >= val.string_value.str) + if ((char*) tmp.ptr() <= val.x.string.value.str && + (char*) tmp.ptr() + tmp.length() >= val.x.string.value.str) { /* value is allocated in tmp buffer; We have to make a copy */ - str_result->copy(val.string_value.str, val.string_value.length, - val.charset); + str_result->copy(val.x.string.value.str, val.x.string.value.length, + val.x.string.charset); } else { @@ -3915,24 +3915,24 @@ String *Item_dyncol_get::val_str(String *str_result) into a field or in a buffer for another item and this buffer is not going to be deleted during expression evaluation */ - str_result->set(val.string_value.str, val.string_value.length, - val.charset); + str_result->set(val.x.string.value.str, val.x.string.value.length, + val.x.string.charset); } break; case DYN_COL_DECIMAL: { int res; int length= - my_decimal_string_length((const my_decimal*)&val.decimal_value); + my_decimal_string_length((const my_decimal*)&val.x.decimal.value); if (str_result->alloc(length)) goto null; - if ((res= decimal2string(&val.decimal_value, (char*) str_result->ptr(), + if ((res= decimal2string(&val.x.decimal.value, (char*) str_result->ptr(), &length, 0, 0, ' ')) != E_DEC_OK) { char buff[40]; int len= sizeof(buff); DBUG_ASSERT(length < (int)sizeof(buff)); - decimal2string(&val.decimal_value, buff, &len, 0, 0, ' '); + decimal2string(&val.x.decimal.value, buff, &len, 0, 0, ' '); decimal_operation_results(res, buff, "CHAR"); } str_result->set_charset(&my_charset_latin1); @@ -3950,7 +3950,7 @@ String *Item_dyncol_get::val_str(String *str_result) asked to return the time argument as a string. */ if (str_result->alloc(MAX_DATE_STRING_REP_LENGTH) || - !(length= my_TIME_to_str(&val.time_value, (char*) str_result->ptr(), + !(length= my_TIME_to_str(&val.x.time_value, (char*) str_result->ptr(), AUTO_SEC_PART_DIGITS))) goto null; str_result->set_charset(&my_charset_latin1); @@ -3980,20 +3980,20 @@ longlong Item_dyncol_get::val_int() goto null; case DYN_COL_UINT: unsigned_flag= 1; // Make it possible for caller to detect sign - return val.long_value; + return val.x.long_value; case DYN_COL_INT: unsigned_flag= 0; // Make it possible for caller to detect sign - return val.long_value; + return val.x.long_value; case DYN_COL_DOUBLE: { bool error; longlong num; - num= double_to_longlong(val.double_value, unsigned_flag, &error); + num= double_to_longlong(val.x.double_value, unsigned_flag, &error); if (error) { char buff[30]; - sprintf(buff, "%lg", val.double_value); + sprintf(buff, "%lg", val.x.double_value); push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_DATA_OVERFLOW, ER(ER_DATA_OVERFLOW), @@ -4006,14 +4006,14 @@ longlong Item_dyncol_get::val_int() { int error; longlong num; - char *end= val.string_value.str + val.string_value.length, *org_end= end; + char *end= val.x.string.value.str + val.x.string.value.length, *org_end= end; - num= my_strtoll10(val.string_value.str, &end, &error); + num= my_strtoll10(val.x.string.value.str, &end, &error); if (end != org_end || error > 0) { char buff[80]; - strmake(buff, val.string_value.str, min(sizeof(buff)-1, - val.string_value.length)); + strmake(buff, val.x.string.value.str, min(sizeof(buff)-1, + val.x.string.value.length)); push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_BAD_DATA, ER(ER_BAD_DATA), @@ -4026,18 +4026,18 @@ longlong Item_dyncol_get::val_int() case DYN_COL_DECIMAL: { longlong num; - my_decimal2int(E_DEC_FATAL_ERROR, &val.decimal_value, unsigned_flag, + my_decimal2int(E_DEC_FATAL_ERROR, &val.x.decimal.value, unsigned_flag, &num); return num; } case DYN_COL_DATETIME: case DYN_COL_DATE: case DYN_COL_TIME: - unsigned_flag= !val.time_value.neg; + unsigned_flag= !val.x.time_value.neg; if (unsigned_flag) - return TIME_to_ulonglong(&val.time_value); + return TIME_to_ulonglong(&val.x.time_value); else - return -(longlong)TIME_to_ulonglong(&val.time_value); + return -(longlong)TIME_to_ulonglong(&val.x.time_value); } null: @@ -4059,24 +4059,24 @@ double Item_dyncol_get::val_real() case DYN_COL_NULL: goto null; case DYN_COL_UINT: - return ulonglong2double(val.ulong_value); + return ulonglong2double(val.x.ulong_value); case DYN_COL_INT: - return (double) val.long_value; + return (double) val.x.long_value; case DYN_COL_DOUBLE: - return (double) val.double_value; + return (double) val.x.double_value; case DYN_COL_STRING: { int error; char *end; - double res= my_strntod(val.charset, (char*) val.string_value.str, - val.string_value.length, &end, &error); + double res= my_strntod(val.x.string.charset, (char*) val.x.string.value.str, + val.x.string.value.length, &end, &error); - if (end != (char*) val.string_value.str + val.string_value.length || + if (end != (char*) val.x.string.value.str + val.x.string.value.length || error) { char buff[80]; - strmake(buff, val.string_value.str, min(sizeof(buff)-1, - val.string_value.length)); + strmake(buff, val.x.string.value.str, min(sizeof(buff)-1, + val.x.string.value.length)); push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_BAD_DATA, ER(ER_BAD_DATA), @@ -4088,13 +4088,13 @@ double Item_dyncol_get::val_real() { double res; /* This will always succeed */ - decimal2double(&val.decimal_value, &res); + decimal2double(&val.x.decimal.value, &res); return res; } case DYN_COL_DATETIME: case DYN_COL_DATE: case DYN_COL_TIME: - return TIME_to_double(&val.time_value); + return TIME_to_double(&val.x.time_value); } null: @@ -4116,22 +4116,22 @@ my_decimal *Item_dyncol_get::val_decimal(my_decimal *decimal_value) case DYN_COL_NULL: goto null; case DYN_COL_UINT: - int2my_decimal(E_DEC_FATAL_ERROR, val.long_value, TRUE, decimal_value); + int2my_decimal(E_DEC_FATAL_ERROR, val.x.long_value, TRUE, decimal_value); break; case DYN_COL_INT: - int2my_decimal(E_DEC_FATAL_ERROR, val.long_value, FALSE, decimal_value); + int2my_decimal(E_DEC_FATAL_ERROR, val.x.long_value, FALSE, decimal_value); break; case DYN_COL_DOUBLE: - double2my_decimal(E_DEC_FATAL_ERROR, val.double_value, decimal_value); + double2my_decimal(E_DEC_FATAL_ERROR, val.x.double_value, decimal_value); break; case DYN_COL_STRING: { int rc; - rc= str2my_decimal(0, val.string_value.str, val.string_value.length, - val.charset, decimal_value); + rc= str2my_decimal(0, val.x.string.value.str, val.x.string.value.length, + val.x.string.charset, decimal_value); char buff[80]; - strmake(buff, val.string_value.str, min(sizeof(buff)-1, - val.string_value.length)); + strmake(buff, val.x.string.value.str, min(sizeof(buff)-1, + val.x.string.value.length)); if (rc != E_DEC_OK) { push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, @@ -4142,14 +4142,14 @@ my_decimal *Item_dyncol_get::val_decimal(my_decimal *decimal_value) break; } case DYN_COL_DECIMAL: - decimal2my_decimal(&val.decimal_value, decimal_value); + decimal2my_decimal(&val.x.decimal.value, decimal_value); break; case DYN_COL_DATETIME: case DYN_COL_DATE: case DYN_COL_TIME: - decimal_value= seconds2my_decimal(val.time_value.neg, - TIME_to_ulonglong(&val.time_value), - val.time_value.second_part, + decimal_value= seconds2my_decimal(val.x.time_value.neg, + TIME_to_ulonglong(&val.x.time_value), + val.x.time_value.second_part, decimal_value); break; } @@ -4178,36 +4178,36 @@ bool Item_dyncol_get::get_date(MYSQL_TIME *ltime, uint fuzzy_date) signed_value= 1; // For error message /* fall_trough */ case DYN_COL_UINT: - if (signed_value || val.ulong_value <= LONGLONG_MAX) + if (signed_value || val.x.ulong_value <= LONGLONG_MAX) { - if (int_to_datetime_with_warn(val.ulong_value, ltime, fuzzy_date, + if (int_to_datetime_with_warn(val.x.ulong_value, ltime, fuzzy_date, 0 /* TODO */)) goto null; return 0; } /* let double_to_datetime_with_warn() issue the warning message */ - val.double_value= static_cast(ULONGLONG_MAX); + val.x.double_value= static_cast(ULONGLONG_MAX); /* fall_trough */ case DYN_COL_DOUBLE: - if (double_to_datetime_with_warn(val.double_value, ltime, fuzzy_date, + if (double_to_datetime_with_warn(val.x.double_value, ltime, fuzzy_date, 0 /* TODO */)) goto null; return 0; case DYN_COL_DECIMAL: - if (decimal_to_datetime_with_warn((my_decimal*)&val.decimal_value, ltime, + if (decimal_to_datetime_with_warn((my_decimal*)&val.x.decimal.value, ltime, fuzzy_date, 0 /* TODO */)) goto null; return 0; case DYN_COL_STRING: - if (str_to_datetime_with_warn(val.string_value.str, - val.string_value.length, + if (str_to_datetime_with_warn(val.x.string.value.str, + val.x.string.value.length, ltime, fuzzy_date) <= MYSQL_TIMESTAMP_ERROR) goto null; return 0; case DYN_COL_DATETIME: case DYN_COL_DATE: case DYN_COL_TIME: - *ltime= val.time_value; + *ltime= val.x.time_value; return 0; } -- cgit v1.2.1 From ebbdb14a02daf36063effc8b734028578f7e4e90 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Fri, 23 Sep 2011 01:25:08 +0400 Subject: BUG#849776: Wrong result with semijoin + "Impossible where" - Provide fix_after_pullout() function for Item_in_optimizer and other Item_XXX classes (basically, all of them that have eval_not_null_tables, which means they have special rules for calculating not_null_tables_cache value) --- sql/item_cmpfunc.cc | 36 ++++++++++++++++++++++++++++++++++++ sql/item_cmpfunc.h | 4 ++++ sql/item_row.cc | 2 ++ sql/item_subselect.cc | 3 +-- 4 files changed, 43 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 91a656b645a..14faed3dcde 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1405,6 +1405,16 @@ bool Item_in_optimizer::is_top_level_item() } +void Item_in_optimizer::fix_after_pullout(st_select_lex *new_parent, Item **ref) +{ + /* This will re-calculate attributes of our Item_in_subselect: */ + Item_bool_func::fix_after_pullout(new_parent, ref); + + /* Then, re-calculate not_null_tables_cache: */ + eval_not_null_tables(NULL); +} + + bool Item_in_optimizer::eval_not_null_tables(uchar *opt_arg) { not_null_tables_cache= 0; @@ -2136,6 +2146,14 @@ bool Item_func_between::eval_not_null_tables(uchar *opt_arg) } +void Item_func_between::fix_after_pullout(st_select_lex *new_parent, Item **ref) +{ + /* This will re-calculate attributes of the arguments */ + Item_func_opt_neg::fix_after_pullout(new_parent, ref); + /* Then, re-calculate not_null_tables_cache according to our special rules */ + eval_not_null_tables(NULL); +} + void Item_func_between::fix_length_and_dec() { THD *thd= current_thd; @@ -2516,6 +2534,16 @@ Item_func_if::eval_not_null_tables(uchar *opt_arg) return 0; } + +void Item_func_if::fix_after_pullout(st_select_lex *new_parent, Item **ref) +{ + /* This will re-calculate attributes of the arguments */ + Item_func::fix_after_pullout(new_parent, ref); + /* Then, re-calculate not_null_tables_cache according to our special rules */ + eval_not_null_tables(NULL); +} + + void Item_func_if::fix_length_and_dec() { @@ -3760,6 +3788,14 @@ Item_func_in::eval_not_null_tables(uchar *opt_arg) } +void Item_func_in::fix_after_pullout(st_select_lex *new_parent, Item **ref) +{ + /* This will re-calculate attributes of the arguments */ + Item_func_opt_neg::fix_after_pullout(new_parent, ref); + /* Then, re-calculate not_null_tables_cache according to our special rules */ + eval_not_null_tables(NULL); +} + static int srtcmp_in(CHARSET_INFO *cs, const String *x,const String *y) { return cs->coll->strnncollsp(cs, diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index acbb7413459..81a18bb594e 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -262,6 +262,7 @@ public: virtual void get_cache_parameters(List ¶meters); bool is_top_level_item(); bool eval_not_null_tables(uchar *opt_arg); + void fix_after_pullout(st_select_lex *new_parent, Item **ref); }; class Comp_creator @@ -674,6 +675,7 @@ public: CHARSET_INFO *compare_collation() { return cmp_collation.collation; } uint decimal_precision() const { return 1; } bool eval_not_null_tables(uchar *opt_arg); + void fix_after_pullout(st_select_lex *new_parent, Item **ref); }; @@ -775,6 +777,7 @@ public: uint decimal_precision() const; const char *func_name() const { return "if"; } bool eval_not_null_tables(uchar *opt_arg); + void fix_after_pullout(st_select_lex *new_parent, Item **ref); }; @@ -1313,6 +1316,7 @@ public: bool is_bool_func() { return 1; } CHARSET_INFO *compare_collation() { return cmp_collation.collation; } bool eval_not_null_tables(uchar *opt_arg); + void fix_after_pullout(st_select_lex *new_parent, Item **ref); }; class cmp_item_row :public cmp_item diff --git a/sql/item_row.cc b/sql/item_row.cc index 09977d71bb7..46d5f13f6fa 100644 --- a/sql/item_row.cc +++ b/sql/item_row.cc @@ -149,11 +149,13 @@ void Item_row::fix_after_pullout(st_select_lex *new_parent, Item **ref) { used_tables_cache= 0; const_item_cache= 1; + not_null_tables_cache= 0; for (uint i= 0; i < arg_count; i++) { items[i]->fix_after_pullout(new_parent, &items[i]); used_tables_cache|= items[i]->used_tables(); const_item_cache&= items[i]->const_item(); + not_null_tables_cache|= items[i]->not_null_tables(); } } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 50533c1a7ff..43df9fe32a9 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -2477,8 +2477,7 @@ void Item_in_subselect::fix_after_pullout(st_select_lex *new_parent, Item **ref) { left_expr->fix_after_pullout(new_parent, &left_expr); Item_subselect::fix_after_pullout(new_parent, ref); - //psergey-todo: the above looks odd, why don't we 'aggregate' left_expr with - //the rest? + used_tables_cache |= left_expr->used_tables(); } void Item_in_subselect::update_used_tables() -- cgit v1.2.1 From 22e793639aa098aac6712256c67f28a49e4b1126 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Fri, 23 Sep 2011 01:13:38 +0300 Subject: Added new options to KILL. New syntax is KILL [HARD|SOFT] [CONNECTION|QUERY] [ID | USER user_name] - If USER is given, all threads for that user is signaled - If SOFT is used then the KILL will not be sent to the handler. This can be used to not interrupt critical things in the handler like 'REPAIR'. Internally added more kill signals. This gives us more information of why a query/connection was killed. - KILL_SERVER is used when server is going down. In this case the users gets ER_SHUTDOWN as the reason connection was killed. - Changed signals to number in correct order, which makes it easier to test how the signal should affect the code. - New error message ER_CONNECTION_KILLED if connection was killed by 'KILL CONNECTION'. Before we got error ER_SHUTDOWN. Changed names of not used parameters KILL_QUERY & KILL_CONNCTION to mysql_kill() to not conflict with defines in the server include/mysql.h.pp: Updated file include/mysql_com.h: Changed names of not used parameters KILL_QUERY & KILL_CONNCTION to mysql_kill() to not conflict with defines in the server mysql-test/r/kill.result: Added test of KILL USER mysql-test/suite/rpl/r/rpl_stm_000001.result: Updated error code mysql-test/suite/rpl/t/rpl_stm_000001.test: Updated error codes mysql-test/t/flush_read_lock_kill.test: Updated error codes mysql-test/t/kill.test: Added test of KILL USER plugin/handler_socket/handlersocket/database.cpp: Removed THD:: from KILL sql/debug_sync.cc: Removed THD:: from KILL sql/event_scheduler.cc: Removed THD:: from KILL sql/filesort.cc: Removed THD:: from KILL sql/ha_ndbcluster_binlog.cc: Removed THD:: from KILL sql/handler.cc: Removed THD:: from KILL Simplify code. sql/lex.h: Added new keywords HARD | SOFT sql/log.cc: Removed THD:: from KILL Added testing of new error ER_CONNECTION_KILLED sql/log_event.cc: Removed THD:: from KILL Added testing of new error ER_CONNECTION_KILLED sql/mysql_priv.h: Added new prototypes sql/mysqld.cc: Removed THD:: from KILL Use KILL_SERVER_HARD signal on shutdown. sql/scheduler.cc: Removed THD:: from KILL Simplify test if connection should be killed sql/share/errmsg.txt: New error message ER_CONNECTION_KILLED sql/slave.cc: Removed THD:: from KILL sql/sp_head.cc: Removed THD:: from KILL sql/sql_base.cc: Removed THD:: from KILL sql/sql_cache.cc: Removed THD:: from KILL sql/sql_class.cc: Removed THD:: from KILL Added killed_errno() Only signal kill to storage engine if HARD bit is set. sql/sql_class.h: Move KILL options out from THD to make them easier to use in sql_yacc.yy sql/sql_connect.cc: Removed THD:: from KILL sql/sql_delete.cc: Removed THD:: from KILL sql/sql_error.cc: Removed THD:: from KILL sql/sql_insert.cc: Removed THD:: from KILL Simplifed testing if thread is killed. sql/sql_lex.h: Added kill options to st_lex sql/sql_load.cc: Removed THD:: from KILL sql/sql_parse.cc: Added kill options to st_lex Simplifed and optimzed testing of thd->killed at end of query Added support for KILL USER Extended sql_kill() to allow use of more kill signals. sql/sql_repl.cc: Removed THD:: from KILL sql/sql_show.cc: Removed THD:: from KILL Simplied testing if query/connection was killed sql/sql_table.cc: Removed THD:: from KILL sql/sql_update.cc: Removed THD:: from KILL sql/sql_yacc.yy: Added support for new KILL syntax: KILL [HARD|SOFT] [CONNECTION|QUERY] [ID | USER user_name] storage/archive/ha_archive.cc: Simplify compilation storage/maria/ha_maria.cc: Removed THD:: from KILL --- sql/debug_sync.cc | 4 +- sql/event_scheduler.cc | 2 +- sql/filesort.cc | 8 +-- sql/ha_ndbcluster_binlog.cc | 2 +- sql/handler.cc | 7 +-- sql/lex.h | 2 + sql/log.cc | 8 +-- sql/log_event.cc | 3 +- sql/mysql_priv.h | 6 +- sql/mysqld.cc | 27 +++++---- sql/scheduler.cc | 9 +-- sql/share/errmsg.txt | 2 + sql/slave.cc | 2 +- sql/sp_head.cc | 9 +-- sql/sql_base.cc | 4 +- sql/sql_cache.cc | 4 +- sql/sql_class.cc | 41 +++++++++++-- sql/sql_class.h | 51 ++++++++++------ sql/sql_connect.cc | 5 +- sql/sql_delete.cc | 14 ++--- sql/sql_error.cc | 2 +- sql/sql_insert.cc | 33 +++++------ sql/sql_lex.h | 3 + sql/sql_load.cc | 10 ++-- sql/sql_parse.cc | 140 +++++++++++++++++++++++++++++++++++++------- sql/sql_repl.cc | 8 +-- sql/sql_show.cc | 6 +- sql/sql_table.cc | 2 +- sql/sql_update.cc | 16 ++--- sql/sql_yacc.yy | 44 +++++++++++--- 30 files changed, 324 insertions(+), 150 deletions(-) (limited to 'sql') diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc index 322db38adf2..5e5e5a72850 100644 --- a/sql/debug_sync.cc +++ b/sql/debug_sync.cc @@ -1078,7 +1078,7 @@ static bool debug_sync_set_action(THD *thd, st_debug_sync_action *action) point decremented it to 0. In this case the following happened: - an error message was reported with my_error() and - - the statement was killed with thd->killed= THD::KILL_QUERY. + - the statement was killed with thd->killed= KILL_QUERY. If a statement reports an error, it must not call send_ok(). The calling functions will not call send_ok(), if we return TRUE @@ -1852,7 +1852,7 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action) { if (!--action->hit_limit) { - thd->killed= THD::KILL_QUERY; + thd->killed= KILL_QUERY; my_error(ER_DEBUG_SYNC_HIT_LIMIT, MYF(0)); } DBUG_PRINT("debug_sync_exec", ("hit_limit: %lu at: '%s'", diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index ecddcb7ca46..240d2df8e65 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -642,7 +642,7 @@ Event_scheduler::stop() sql_print_information("Event Scheduler: Killing the scheduler thread, " "thread id %lu", scheduler_thd->thread_id); - scheduler_thd->awake(THD::KILL_CONNECTION); + scheduler_thd->awake(KILL_CONNECTION); pthread_mutex_unlock(&scheduler_thd->LOCK_thd_data); /* thd could be 0x0, when shutting down */ diff --git a/sql/filesort.cc b/sql/filesort.cc index b3bb15bd7f0..da275c1c14f 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -506,7 +506,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, my_off_t record; TABLE *sort_form; THD *thd= current_thd; - volatile THD::killed_state *killed= &thd->killed; + volatile killed_state *killed= &thd->killed; handler *file; MY_BITMAP *save_read_set, *save_write_set, *save_vcol_set; DBUG_ENTER("find_all_keys"); @@ -1239,9 +1239,9 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, void *first_cmp_arg; element_count dupl_count= 0; uchar *src; - THD::killed_state not_killable; + killed_state not_killable; uchar *unique_buff= param->unique_buff; - volatile THD::killed_state *killed= ¤t_thd->killed; + volatile killed_state *killed= ¤t_thd->killed; DBUG_ENTER("merge_buffers"); status_var_increment(current_thd->status_var.filesort_merge_passes); @@ -1249,7 +1249,7 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, if (param->not_killable) { killed= ¬_killable; - not_killable= THD::NOT_KILLED; + not_killable= NOT_KILLED; } error=0; diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 77851119e34..5a3365b29d2 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -1855,7 +1855,7 @@ static void ndb_binlog_query(THD *thd, Cluster_schema *schema) else thd->server_id= schema->any_value; thd->db= schema->db; - int errcode = query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode = query_error_code(thd, thd->killed == NOT_KILLED); thd->binlog_query(THD::STMT_QUERY_TYPE, schema->query, schema->query_length, FALSE, schema->name[0] == 0 || thd->db[0] == 0, diff --git a/sql/handler.cc b/sql/handler.cc index 905668114dd..08d778022dc 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1404,13 +1404,12 @@ int ha_rollback_trans(THD *thd, bool all) the error log; but we don't want users to wonder why they have this message in the error log, so we don't send it. - We don't have to test for thd->killed == THD::KILL_SYSTEM_THREAD as + We don't have to test for thd->killed == KILL_SYSTEM_THREAD as it doesn't matter if a warning is pushed to a system thread or not: No one will see it... */ if (is_real_trans && thd->transaction.all.modified_non_trans_table && - !thd->slave_thread && thd->killed != THD::KILL_CONNECTION && - thd->killed != THD::KILL_SERVER) + !thd->slave_thread && thd->killed < KILL_CONNECTION) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARNING_NOT_COMPLETE_ROLLBACK, ER(ER_WARNING_NOT_COMPLETE_ROLLBACK)); @@ -2564,7 +2563,7 @@ int handler::update_auto_increment() /* first test if the query was aborted due to strict mode constraints */ - if (thd->killed == THD::KILL_BAD_DATA) + if (killed_mask_hard(thd->killed) == KILL_BAD_DATA) DBUG_RETURN(HA_ERR_AUTOINC_ERANGE); /* diff --git a/sql/lex.h b/sql/lex.h index 6ab234f8d81..3041168e4fb 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -238,6 +238,7 @@ static SYMBOL symbols[] = { { "GRANTS", SYM(GRANTS)}, { "GROUP", SYM(GROUP_SYM)}, { "HANDLER", SYM(HANDLER_SYM)}, + { "HARD", SYM(HARD_SYM)}, { "HASH", SYM(HASH_SYM)}, { "HAVING", SYM(HAVING)}, { "HELP", SYM(HELP_SYM)}, @@ -496,6 +497,7 @@ static SYMBOL symbols[] = { { "SNAPSHOT", SYM(SNAPSHOT_SYM)}, { "SMALLINT", SYM(SMALLINT)}, { "SOCKET", SYM(SOCKET_SYM)}, + { "SOFT", SYM(SOFT_SYM)}, { "SOME", SYM(ANY_SYM)}, { "SONAME", SYM(SONAME_SYM)}, { "SOUNDS", SYM(SOUNDS_SYM)}, diff --git a/sql/log.cc b/sql/log.cc index e53c8d12770..649db6a4c29 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1809,7 +1809,7 @@ static int binlog_savepoint_set(handlerton *hton, THD *thd, void *sv) log_query.append(thd->lex->ident.str, thd->lex->ident.length) || log_query.append("`")) DBUG_RETURN(1); - int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode= query_error_code(thd, thd->killed == NOT_KILLED); Query_log_event qinfo(thd, log_query.ptr(), log_query.length(), TRUE, TRUE, errcode); DBUG_RETURN(mysql_bin_log.write(&qinfo)); @@ -1833,7 +1833,7 @@ static int binlog_savepoint_rollback(handlerton *hton, THD *thd, void *sv) log_query.append(thd->lex->ident.str, thd->lex->ident.length) || log_query.append("`")) DBUG_RETURN(1); - int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode= query_error_code(thd, thd->killed == NOT_KILLED); Query_log_event qinfo(thd, log_query.ptr(), log_query.length(), TRUE, TRUE, errcode); DBUG_RETURN(mysql_bin_log.write(&qinfo)); @@ -5166,7 +5166,7 @@ int query_error_code(THD *thd, bool not_killed) { int error; - if (not_killed || (thd->killed == THD::KILL_BAD_DATA)) + if (not_killed || (killed_mask_hard(thd->killed) == KILL_BAD_DATA)) { error= thd->is_error() ? thd->main_da.sql_errno() : 0; @@ -5176,7 +5176,7 @@ int query_error_code(THD *thd, bool not_killed) caller. */ if (error == ER_SERVER_SHUTDOWN || error == ER_QUERY_INTERRUPTED || - error == ER_NEW_ABORTING_CONNECTION) + error == ER_NEW_ABORTING_CONNECTION || error == ER_CONNECTION_KILLED) error= 0; } else diff --git a/sql/log_event.cc b/sql/log_event.cc index 3dad0c87e00..ae7a37f2b3d 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -437,6 +437,7 @@ inline bool unexpected_error_code(int unexpected_error) case ER_NET_READ_ERROR: case ER_NET_ERROR_ON_WRITE: case ER_QUERY_INTERRUPTED: + case ER_CONNECTION_KILLED: case ER_SERVER_SHUTDOWN: case ER_NEW_ABORTING_CONNECTION: return(TRUE); @@ -3686,7 +3687,7 @@ Default database: '%s'. Query: '%s'", { DBUG_PRINT("info",("error ignored")); clear_all_errors(thd, const_cast(rli)); - thd->killed= THD::NOT_KILLED; + thd->killed= NOT_KILLED; /* When an error is expected and matches the actual error the slave does not report any error and by consequence changes diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b38253c866a..62fdef5aec6 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -118,8 +118,6 @@ char *sql_strmake_with_convert(const char *str, size_t arg_length, CHARSET_INFO *from_cs, size_t max_res_length, CHARSET_INFO *to_cs, size_t *result_length); -uint kill_one_thread(THD *thd, ulong id, bool only_kill_query); -void sql_kill(THD *thd, ulong id, bool only_kill_query); bool net_request_file(NET* net, const char* fname); char* query_table_status(THD *thd,const char *db,const char *table_name); @@ -1074,6 +1072,10 @@ struct Query_cache_query_flags #define query_cache_is_cacheable_query(L) 0 #endif /*HAVE_QUERY_CACHE*/ +uint kill_one_thread(THD *thd, ulong id, killed_state kill_signal); +void sql_kill(THD *thd, ulong id, killed_state kill_signal); +void sql_kill_user(THD *thd, LEX_USER *str, killed_state kill_signal); + /* Error injector Macros to enable easy testing of recovery after failures in various error cases. diff --git a/sql/mysqld.cc b/sql/mysqld.cc index aa394c4b7da..a54f845a762 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1082,7 +1082,7 @@ static void close_connections(void) if (tmp->slave_thread) continue; - tmp->killed= THD::KILL_CONNECTION; + tmp->killed= KILL_SERVER_HARD; thread_scheduler.post_kill_notification(tmp); pthread_mutex_lock(&tmp->LOCK_thd_data); if (tmp->mysys_var) @@ -2033,7 +2033,7 @@ void close_connection(THD *thd, uint errcode, bool lock) errcode ? ER(errcode) : "")); if (lock) (void) pthread_mutex_lock(&LOCK_thread_count); - thd->killed= THD::KILL_CONNECTION; + thd->killed= KILL_CONNECTION; if (global_system_variables.log_warnings > 3) { @@ -2728,27 +2728,30 @@ the thread stack. Please read http://dev.mysql.com/doc/mysql/en/linux.html\n\n", { const char *kreason= "UNKNOWN"; switch (thd->killed) { - case THD::NOT_KILLED: + case NOT_KILLED: + case KILL_HARD_BIT: kreason= "NOT_KILLED"; break; - case THD::KILL_BAD_DATA: + case KILL_BAD_DATA: + case KILL_BAD_DATA_HARD: kreason= "KILL_BAD_DATA"; break; - case THD::KILL_CONNECTION: + case KILL_CONNECTION: + case KILL_CONNECTION_HARD: kreason= "KILL_CONNECTION"; break; - case THD::KILL_QUERY: + case KILL_QUERY: + case KILL_QUERY_HARD: kreason= "KILL_QUERY"; break; - case THD::KILL_SYSTEM_THREAD: + case KILL_SYSTEM_THREAD: + case KILL_SYSTEM_THREAD_HARD: kreason= "KILL_SYSTEM_THREAD"; break; - case THD::KILL_SERVER: + case KILL_SERVER: + case KILL_SERVER_HARD: kreason= "KILL_SERVER"; break; - case THD::KILLED_NO_VALUE: - kreason= "KILLED_NO_VALUE"; - break; } fprintf(stderr, "\nTrying to get some variables.\n" "Some pointers may be invalid and cause the dump to abort.\n"); @@ -5270,7 +5273,7 @@ void create_thread_to_handle_connection(THD *thd) ("Can't create thread to handle request (error %d)", error)); thread_count--; - thd->killed= THD::KILL_CONNECTION; // Safety + thd->killed= KILL_CONNECTION; // Safety (void) pthread_mutex_unlock(&LOCK_thread_count); pthread_mutex_lock(&LOCK_connection_count); diff --git a/sql/scheduler.cc b/sql/scheduler.cc index 6dd93640dc5..5b83bb4753e 100644 --- a/sql/scheduler.cc +++ b/sql/scheduler.cc @@ -376,9 +376,7 @@ void libevent_kill_thd_callback(int Fd, short, void*) { THD *thd= (THD*)list->data; list= list_rest(list); - if (thd->killed == THD::KILL_CONNECTION || - thd->killed == THD::KILL_SYSTEM_THREAD || - thd->killed == THD::KILL_SERVER) + if ((int) thd->killed >= (int) KILL_CONNECTION) { /* Delete from libevent and add to the processing queue. @@ -512,7 +510,7 @@ static void libevent_connection_close(THD *thd) DBUG_ENTER("libevent_connection_close"); DBUG_PRINT("enter", ("thd: %p", thd)); - thd->killed= THD::KILL_CONNECTION; // Avoid error messages + thd->killed= KILL_CONNECTION; // Avoid error messages if (thd->net.vio->sd >= 0) // not already closed { @@ -535,8 +533,7 @@ static bool libevent_should_close_connection(THD* thd) { return (thd->net.error || thd->net.vio == 0 || - thd->killed == THD::KILL_CONNECTION || - thd->killed == THD::KILL_SERVER); + (int) thd->killed >= (int) KILL_CONNECTION); } diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 928a28aba24..5f6931ab155 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -6290,3 +6290,5 @@ ER_QUERY_CACHE_IS_GLOBALY_DISABLED eng "Query cache is globally disabled and you can't enable it only for this session" ER_VIEW_ORDERBY_IGNORED eng "View '%-.192s'.'%-.192s' ORDER BY clause ignored because there is other ORDER BY clause already." +ER_CONNECTION_KILLED 70100 + eng "Connection was killed" diff --git a/sql/slave.cc b/sql/slave.cc index 1ab1caecfb5..25ce31ab47c 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -524,7 +524,7 @@ terminate_slave_thread(THD *thd, IF_DBUG(int err= ) pthread_kill(thd->real_id, thr_client_alarm); DBUG_ASSERT(err != EINVAL); #endif - thd->awake(THD::NOT_KILLED); + thd->awake(KILL_CONNECTION); pthread_mutex_unlock(&thd->LOCK_thd_data); /* diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 6532e6c56f2..fda922f91ae 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1312,7 +1312,7 @@ sp_head::execute(THD *thd) ctx->enter_handler(hip); thd->clear_error(); thd->is_fatal_error= 0; - thd->killed= THD::NOT_KILLED; + thd->killed= NOT_KILLED; thd->mysys_var->abort= 0; continue; } @@ -1362,10 +1362,7 @@ sp_head::execute(THD *thd) If the DB has changed, the pointer has changed too, but the original thd->db will then have been freed */ - if (cur_db_changed && - thd->killed != THD::KILL_CONNECTION && - thd->killed != THD::KILL_SERVER && - thd->killed != THD::KILL_SYSTEM_THREAD) + if (cur_db_changed && thd->killed < KILL_CONNECTION) { /* Force switching back to the saved current database, because it may be @@ -1799,7 +1796,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, thd->options= binlog_save_options; if (thd->binlog_evt_union.unioned_events) { - int errcode = query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode = query_error_code(thd, thd->killed == NOT_KILLED); Query_log_event qinfo(thd, binlog_buf.ptr(), binlog_buf.length(), thd->binlog_evt_union.unioned_events_trans, FALSE, errcode); if (mysql_bin_log.write(&qinfo) && diff --git a/sql/sql_base.cc b/sql/sql_base.cc index e2142bc2734..cacec989518 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8910,7 +8910,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, { if (!in_use->killed) { - in_use->killed= THD::KILL_SYSTEM_THREAD; + in_use->killed= KILL_SYSTEM_THREAD; pthread_mutex_lock(&in_use->mysys_var->mutex); if (in_use->mysys_var->current_cond) { @@ -9244,7 +9244,7 @@ void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && ! in_use->killed) { - in_use->killed= THD::KILL_SYSTEM_THREAD; + in_use->killed= KILL_SYSTEM_THREAD; pthread_mutex_lock(&in_use->mysys_var->mutex); if (in_use->mysys_var->current_cond) { diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 5378390ebb4..8631a13eae7 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -391,7 +391,7 @@ static void debug_wait_for_kill(const char *info) sql_print_information("%s", info); while(!thd->killed) my_sleep(1000); - thd->killed= THD::NOT_KILLED; + thd->killed= NOT_KILLED; /* Remove the set debug variable, to ensure we don't get stuck on it again This is needed as for MyISAM, invalidate_table() may be called twice @@ -4439,7 +4439,7 @@ void Query_cache::wreck(uint line, const char *message) DBUG_PRINT("warning", ("%5d QUERY CACHE WRECK => DISABLED",line)); DBUG_PRINT("warning", ("==================================")); if (thd) - thd->killed= THD::KILL_CONNECTION; + thd->killed= KILL_CONNECTION; cache_dump(); /* check_integrity(0); */ /* Can't call it here because of locks */ bins_dump(); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 16165cdedbb..39edcdca3de 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1266,7 +1266,7 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var, #endif -void THD::awake(THD::killed_state state_to_set) +void THD::awake(killed_state state_to_set) { DBUG_ENTER("THD::awake"); DBUG_PRINT("enter", ("this: 0x%lx", (long) this)); @@ -1283,7 +1283,7 @@ void THD::awake(THD::killed_state state_to_set) "KILLED"); } killed= state_to_set; - if (state_to_set != THD::KILL_QUERY) + if (state_to_set >= KILL_CONNECTION) { thr_alarm_kill(thread_id); if (!slave_thread) @@ -1363,6 +1363,38 @@ void THD::awake(THD::killed_state state_to_set) DBUG_VOID_RETURN; } + +/* + Get error number for killed state + Note that the error message can't have any parameters. + See thd::kill_message() +*/ + +int killed_errno(killed_state killed) +{ + switch (killed) { + case NOT_KILLED: + case KILL_HARD_BIT: + return 0; // Probably wrong usage + case KILL_BAD_DATA: + case KILL_BAD_DATA_HARD: + return 0; // Not a real error + case KILL_CONNECTION: + case KILL_CONNECTION_HARD: + case KILL_SYSTEM_THREAD: + case KILL_SYSTEM_THREAD_HARD: + return ER_CONNECTION_KILLED; + case KILL_QUERY: + case KILL_QUERY_HARD: + return ER_QUERY_INTERRUPTED; + case KILL_SERVER: + case KILL_SERVER_HARD: + return ER_SERVER_SHUTDOWN; + } + return 0; // Keep compiler happy +} + + /* Remember the location of thread info, the structure needed for sql_alloc() and the structure for the net buffer @@ -3397,12 +3429,13 @@ void THD::restore_backup_open_tables_state(Open_tables_state *backup) @param thd user thread @retval 0 the user thread is active @retval 1 the user thread has been killed + + This is used to signal a storage engine if it should be killed. */ extern "C" int thd_killed(const MYSQL_THD thd) { - if (thd->killed == THD::NOT_KILLED || thd->killed == THD::KILL_BAD_DATA || - thd->killed == THD::KILL_SYSTEM_THREAD) + if (!(thd->killed & KILL_HARD_BIT)) return 0; return thd->killed; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 748c2a4a818..17111b9a8c6 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -362,6 +362,38 @@ public: LEX_COLUMN (const String& x,const uint& y ): column (x),rights (y) {} }; + +/* Note: these states are actually bit coded with HARD */ +enum killed_state +{ + NOT_KILLED= 0, + KILL_HARD_BIT= 1, /* Bit for HARD KILL */ + KILL_BAD_DATA= 2, + KILL_BAD_DATA_HARD= 3, + KILL_QUERY= 4, + KILL_QUERY_HARD= 5, + /* + All of the following killed states will kill the connection + KILL_CONNECTION must be the first of these! + */ + KILL_CONNECTION= 6, + KILL_CONNECTION_HARD= 7, + KILL_SYSTEM_THREAD= 8, + KILL_SYSTEM_THREAD_HARD= 9, + KILL_SERVER= 10, + KILL_SERVER_HARD= 11 +}; + +extern int killed_errno(killed_state killed); +#define killed_mask_hard(killed) ((killed_state) ((killed) & ~KILL_HARD_BIT)) + +enum killed_type +{ + KILL_TYPE_ID, + KILL_TYPE_USER +}; + + #include "sql_lex.h" /* Must be here */ class Delayed_insert; @@ -1973,16 +2005,6 @@ public: DYNAMIC_ARRAY user_var_events; /* For user variables replication */ MEM_ROOT *user_var_events_alloc; /* Allocate above array elements here */ - enum killed_state - { - NOT_KILLED=0, - KILL_BAD_DATA=1, - KILL_CONNECTION=ER_SERVER_SHUTDOWN, - KILL_SYSTEM_THREAD=ER_NEW_ABORTING_CONNECTION, /* Kill connection nicely */ - KILL_QUERY=ER_QUERY_INTERRUPTED, - KILL_SERVER, /* Placeholder for shortdown */ - KILLED_NO_VALUE /* means neither of the states */ - }; killed_state volatile killed; /* scramble - random string sent to client on handshake */ @@ -2165,7 +2187,7 @@ public: } void close_active_vio(); #endif - void awake(THD::killed_state state_to_set); + void awake(killed_state state_to_set); #ifndef MYSQL_CLIENT enum enum_binlog_query_type { @@ -2399,18 +2421,13 @@ public: void end_statement(); inline int killed_errno() const { - killed_state killed_val; /* to cache the volatile 'killed' */ - return (killed_val= killed) != KILL_BAD_DATA ? killed_val : 0; + return ::killed_errno(killed); } inline void send_kill_message() const { int err= killed_errno(); if (err) - { - if ((err == KILL_CONNECTION) && !shutdown_in_progress) - err = KILL_QUERY; my_message(err, ER(err), MYF(0)); - } } /* return TRUE if we will abort query if we make a warning now */ inline bool really_abort_on_warning() diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 5b7d392d773..5e0ab339418 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -1095,7 +1095,7 @@ void prepare_new_connection_state(THD* thd) execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect); if (thd->is_error()) { - thd->killed= THD::KILL_CONNECTION; + thd->killed= KILL_CONNECTION; sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), thd->thread_id,(thd->db ? thd->db : "unconnected"), sctx->user ? sctx->user : "unauthenticated", @@ -1181,8 +1181,7 @@ pthread_handler_t handle_one_connection(void *arg) prepare_new_connection_state(thd); while (!net->error && net->vio != 0 && - thd->killed != THD::KILL_CONNECTION && - thd->killed != THD::KILL_SERVER) + thd->killed < KILL_CONNECTION) { if (do_command(thd)) break; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index becc1ada7ae..98d034a4d5c 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -49,7 +49,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, bool triggers_applicable; uint usable_index= MAX_KEY; SELECT_LEX *select_lex= &thd->lex->select_lex; - THD::killed_state killed_status= THD::NOT_KILLED; + killed_state killed_status= NOT_KILLED; DBUG_ENTER("mysql_delete"); bool save_binlog_row_based; @@ -383,7 +383,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, table->file->unlock_row(); // Row failed selection, release lock on it } killed_status= thd->killed; - if (killed_status != THD::NOT_KILLED || thd->is_error()) + if (killed_status != NOT_KILLED || thd->is_error()) error= 1; // Aborted if (will_batch && (loc_error= table->file->end_bulk_delete())) { @@ -450,7 +450,7 @@ cleanup: if (error < 0) thd->clear_error(); else - errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); + errcode= query_error_code(thd, killed_status == NOT_KILLED); /* [binlog]: If 'handler::delete_all_rows()' was called and the @@ -916,7 +916,7 @@ void multi_delete::abort() */ if (mysql_bin_log.is_open()) { - int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode= query_error_code(thd, thd->killed == NOT_KILLED); /* possible error of writing binary log is ignored deliberately */ (void) thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), thd->query_length(), @@ -1066,7 +1066,7 @@ int multi_delete::do_table_deletes(TABLE *table, bool ignore) bool multi_delete::send_eof() { - THD::killed_state killed_status= THD::NOT_KILLED; + killed_state killed_status= NOT_KILLED; thd_proc_info(thd, "deleting from reference tables"); /* Does deletes for the last n - 1 tables, returns 0 if ok */ @@ -1074,7 +1074,7 @@ bool multi_delete::send_eof() /* compute a total error to know if something failed */ local_error= local_error || error; - killed_status= (local_error == 0)? THD::NOT_KILLED : thd->killed; + killed_status= (local_error == 0)? NOT_KILLED : thd->killed; /* reset used flags */ thd_proc_info(thd, "end"); @@ -1094,7 +1094,7 @@ bool multi_delete::send_eof() if (local_error == 0) thd->clear_error(); else - errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); + errcode= query_error_code(thd, killed_status == NOT_KILLED); if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), thd->query_length(), transactional_tables, FALSE, errcode) && diff --git a/sql/sql_error.cc b/sql/sql_error.cc index 835e60cd6ba..1db4a110d01 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -131,7 +131,7 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQL_ERROR::enum_warning_level level, thd->no_warnings_for_error= 1; thd->spcont= NULL; - thd->killed= THD::KILL_BAD_DATA; + thd->killed= KILL_BAD_DATA; my_message(code, msg, MYF(0)); thd->spcont= spcont; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index b840eebf4cc..778805d0eb6 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -943,7 +943,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, thd->clear_error(); } else - errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + errcode= query_error_code(thd, thd->killed == NOT_KILLED); /* bug#22725: @@ -957,7 +957,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, routines did not result in any error due to the KILLED. In such case the flag is ignored for constructing binlog event. */ - DBUG_ASSERT(thd->killed != THD::KILL_BAD_DATA || error > 0); + DBUG_ASSERT(thd->killed != KILL_BAD_DATA || error > 0); if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), thd->query_length(), transactional_table, FALSE, @@ -2362,7 +2362,7 @@ void kill_delayed_threads(void) Delayed_insert *di; while ((di= it++)) { - di->thd.killed= THD::KILL_SYSTEM_THREAD; + di->thd.killed= KILL_SYSTEM_THREAD; pthread_mutex_lock(&di->thd.LOCK_thd_data); if (di->thd.mysys_var) { @@ -2447,8 +2447,7 @@ static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di) for (;;) { - if (thd->killed == THD::KILL_CONNECTION || - thd->killed == THD::KILL_SYSTEM_THREAD || thd->killed == THD::KILL_SERVER) + if (thd->killed >= KILL_CONNECTION) { uint lock_count; /* @@ -2496,7 +2495,7 @@ static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di) break; if (error == ETIMEDOUT || error == ETIME) { - thd->killed= THD::KILL_SYSTEM_THREAD; + thd->killed= KILL_SYSTEM_THREAD; break; } } @@ -2529,7 +2528,7 @@ static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di) { /* Fatal error */ di->dead= 1; - thd->killed= THD::KILL_SYSTEM_THREAD; + thd->killed= KILL_SYSTEM_THREAD; } pthread_cond_broadcast(&di->cond_client); } @@ -2539,7 +2538,7 @@ static void handle_delayed_insert_impl(THD *thd, Delayed_insert *di) { /* Some fatal error */ di->dead= 1; - thd->killed= THD::KILL_SYSTEM_THREAD; + thd->killed= KILL_SYSTEM_THREAD; } } di->status=0; @@ -2599,7 +2598,7 @@ pthread_handler_t handle_delayed_insert(void *arg) thd->thread_id= thd->variables.pseudo_thread_id= thread_id++; thd->set_current_time(); threads.append(thd); - thd->killed=abort_loop ? THD::KILL_SYSTEM_THREAD : THD::NOT_KILLED; + thd->killed=abort_loop ? KILL_SYSTEM_THREAD : NOT_KILLED; pthread_mutex_unlock(&LOCK_thread_count); /* @@ -2633,7 +2632,7 @@ end: di->table=0; di->dead= 1; // If error - thd->killed= THD::KILL_SYSTEM_THREAD; // If error + thd->killed= KILL_SYSTEM_THREAD; // If error pthread_mutex_unlock(&di->mutex); close_thread_tables(thd); // Free the table @@ -2712,7 +2711,7 @@ bool Delayed_insert::handle_inserts(void) max_rows= delayed_insert_limit; if (thd.killed || table->needs_reopen_or_name_lock()) { - thd.killed= THD::KILL_SYSTEM_THREAD; + thd.killed= KILL_SYSTEM_THREAD; max_rows= ULONG_MAX; // Do as much as possible } @@ -2825,7 +2824,7 @@ bool Delayed_insert::handle_inserts(void) /* if the delayed insert was killed, the killed status is ignored while binlogging */ int errcode= 0; - if (thd.killed == THD::NOT_KILLED) + if (thd.killed == NOT_KILLED) errcode= query_error_code(&thd, TRUE); /* @@ -3354,7 +3353,7 @@ bool select_insert::send_eof() bool const trans_table= table->file->has_transactions(); ulonglong id; bool changed; - THD::killed_state killed_status= thd->killed; + killed_state killed_status= thd->killed; DBUG_ENTER("select_insert::send_eof"); DBUG_PRINT("enter", ("trans_table=%d, table_type='%s'", trans_table, table->file->table_type())); @@ -3389,7 +3388,7 @@ bool select_insert::send_eof() if (!error) thd->clear_error(); else - errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); + errcode= query_error_code(thd, killed_status == NOT_KILLED); if (write_to_binlog(trans_table, errcode)) { @@ -3463,7 +3462,7 @@ void select_insert::abort() { { if (mysql_bin_log.is_open()) { - int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode= query_error_code(thd, thd->killed == NOT_KILLED); /* error of writing binary log is ignored */ write_to_binlog(transactional_table, errcode); } @@ -3824,7 +3823,7 @@ select_create::prepare(List &values, SELECT_LEX_UNIT *u) !table->s->tmp_table && !ptr->get_create_info()->table_existed) { - int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode= query_error_code(thd, thd->killed == NOT_KILLED); if (int error= ptr->binlog_show_create_table(tables, count, errcode)) return error; } @@ -3867,7 +3866,7 @@ select_create::prepare(List &values, SELECT_LEX_UNIT *u) create_table->table_name); if (thd->current_stmt_binlog_row_based) { - int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode= query_error_code(thd, thd->killed == NOT_KILLED); binlog_show_create_table(&(create_table->table), 1, errcode); } table= create_table->table; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 4ef24876474..32ad4f414c9 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1735,6 +1735,9 @@ typedef struct st_lex : public Query_tables_list LEX_SERVER_OPTIONS server_options; USER_RESOURCES mqh; ulong type; + /* The following is used by KILL */ + killed_state kill_signal; + killed_type kill_type; /* This variable is used in post-parse stage to declare that sum-functions, or functions which have sense only if GROUP BY is present, are allowed. diff --git a/sql/sql_load.cc b/sql/sql_load.cc index cc0527591e1..3df6305f620 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -130,7 +130,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, bool is_fifo=0; #ifndef EMBEDDED_LIBRARY LOAD_FILE_INFO lf_info; - THD::killed_state killed_status; + killed_state killed_status; #endif char *db = table_list->db; // This is never null /* @@ -471,11 +471,11 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, DBUG_EXECUTE_IF("simulate_kill_bug27571", { error=1; - thd->killed= THD::KILL_QUERY; + thd->killed= KILL_QUERY; };); #ifndef EMBEDDED_LIBRARY - killed_status= (error == 0) ? THD::NOT_KILLED : thd->killed; + killed_status= (error == 0) ? NOT_KILLED : thd->killed; #endif /* @@ -519,7 +519,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, /* If the file was not empty, wrote_create_file is true */ if (lf_info.wrote_create_file) { - int errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); + int errcode= query_error_code(thd, killed_status == NOT_KILLED); /* since there is already an error, the possible error of writing binary log will be ignored */ @@ -570,7 +570,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, read_info.end_io_cache(); if (lf_info.wrote_create_file) { - int errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); + int errcode= query_error_code(thd, killed_status == NOT_KILLED); error= write_execute_load_query_log_event(thd, ex, table_list->db, table_list->table_name, handle_duplicates, ignore, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f850dc9145f..acdd6b76df4 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -793,7 +793,7 @@ int end_trans(THD *thd, enum enum_mysql_completiontype completion) my_error(thd->killed_errno(), MYF(0)); else if ((res == 0) && do_release) { - thd->killed= THD::KILL_CONNECTION; + thd->killed= KILL_CONNECTION; if (global_system_variables.log_warnings > 3) { Security_context *sctx= &thd->main_security_ctx; @@ -1530,7 +1530,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, { status_var_increment(thd->status_var.com_stat[SQLCOM_KILL]); ulong id=(ulong) uint4korr(packet); - sql_kill(thd,id,false); + sql_kill(thd,id, KILL_CONNECTION_HARD); break; } case COM_SET_OPTION: @@ -1572,15 +1572,18 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } /* report error issued during command execution */ - if (thd->killed_errno()) + if (thd->killed) { - if (! thd->main_da.is_set()) - thd->send_kill_message(); - } - if (thd->killed == THD::KILL_QUERY || thd->killed == THD::KILL_BAD_DATA) - { - thd->killed= THD::NOT_KILLED; - thd->mysys_var->abort= 0; + if (thd->killed_errno()) + { + if (! thd->main_da.is_set()) + thd->send_kill_message(); + } + if (thd->killed < KILL_CONNECTION) + { + thd->killed= NOT_KILLED; + thd->mysys_var->abort= 0; + } } /* If commit fails, we should be able to reset the OK status. */ @@ -4038,8 +4041,6 @@ end_with_restore_list: } case SQLCOM_KILL: { - Item *it= (Item *)lex->value_list.head(); - if (lex->table_or_sp_used()) { my_error(ER_NOT_SUPPORTED_YET, MYF(0), "Usage of subqueries or stored " @@ -4047,13 +4048,20 @@ end_with_restore_list: break; } - if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1)) + if (lex->kill_type == KILL_TYPE_ID) { - my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY), - MYF(0)); - goto error; + Item *it= (Item *)lex->value_list.head(); + if ((!it->fixed && it->fix_fields(lex->thd, &it)) || it->check_cols(1)) + { + my_message(ER_SET_CONSTANTS_ONLY, ER(ER_SET_CONSTANTS_ONLY), + MYF(0)); + goto error; + } + sql_kill(thd, (ulong) it->val_int(), lex->kill_signal); } - sql_kill(thd, (ulong)it->val_int(), lex->type & ONLY_KILL_QUERY); + else + sql_kill_user(thd, get_current_user(thd, lex->users_list.head()), + lex->kill_signal); break; } #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -7174,12 +7182,13 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, This is written such that we have a short lock on LOCK_thread_count */ -uint kill_one_thread(THD *thd, ulong id, bool only_kill_query) +uint kill_one_thread(THD *thd, ulong id, killed_state kill_signal) { THD *tmp; uint error=ER_NO_SUCH_THREAD; DBUG_ENTER("kill_one_thread"); - DBUG_PRINT("enter", ("id=%lu only_kill=%d", id, only_kill_query)); + DBUG_PRINT("enter", ("id: %lu signal: %u", id, (uint) kill_signal)); + VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list I_List_iterator it(threads); while ((tmp=it++)) @@ -7219,7 +7228,7 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query) if ((thd->security_ctx->master_access & SUPER_ACL) || thd->security_ctx->user_matches(tmp->security_ctx)) { - tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION); + tmp->awake(kill_signal); error=0; } else @@ -7231,6 +7240,76 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query) } +/** + kill all threads from one user + + @param thd Thread class + @param user_name User name for threads we should kill + @param only_kill_query Should it kill the query or the connection + + @note + This is written such that we have a short lock on LOCK_thread_count + + If we can't kill all threads because of security issues, no threads + are killed. +*/ + +static uint kill_threads_for_user(THD *thd, LEX_USER *user, + killed_state kill_signal, ha_rows *rows) +{ + THD *tmp; + List threads_to_kill; + DBUG_ENTER("kill_threads_for_user"); + + *rows= 0; + + if (thd->is_fatal_error) // If we run out of memory + DBUG_RETURN(ER_OUT_OF_RESOURCES); + + DBUG_PRINT("enter", ("user: %s signal: %u", user->user.str, + (uint) kill_signal)); + + VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list + I_List_iterator it(threads); + while ((tmp=it++)) + { + if (tmp->command == COM_DAEMON) + continue; + /* + Check that hostname (if given) and user name matches. + + host.str[0] == '%' means that host name was not given. See sql_yacc.yy + */ + if (((user->host.str[0] == '%' && !user->host.str[1]) || + !strcmp(tmp->security_ctx->host, user->host.str)) && + !strcmp(tmp->security_ctx->user, user->user.str)) + { + if (!(thd->security_ctx->master_access & SUPER_ACL) && + !thd->security_ctx->user_matches(tmp->security_ctx)) + { + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + DBUG_RETURN(ER_KILL_DENIED_ERROR); + } + if (!threads_to_kill.push_back(tmp, tmp->mem_root)) + pthread_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete + } + } + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + if (!threads_to_kill.is_empty()) + { + List_iterator_fast it(threads_to_kill); + THD *ptr; + while ((ptr= it++)) + { + ptr->awake(kill_signal); + pthread_mutex_unlock(&ptr->LOCK_thd_data); + (*rows)++; + } + } + DBUG_RETURN(0); +} + + /* kills a thread and sends response @@ -7241,16 +7320,33 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query) only_kill_query Should it kill the query or the connection */ -void sql_kill(THD *thd, ulong id, bool only_kill_query) +void sql_kill(THD *thd, ulong id, killed_state state) { uint error; - if (!(error= kill_one_thread(thd, id, only_kill_query))) + if (!(error= kill_one_thread(thd, id, state))) my_ok(thd); else my_error(error, MYF(0), id); } +void sql_kill_user(THD *thd, LEX_USER *user, killed_state state) +{ + uint error; + ha_rows rows; + if (!(error= kill_threads_for_user(thd, user, state, &rows))) + my_ok(thd, rows); + else + { + /* + This is probably ER_OUT_OF_RESOURCES, but in the future we may + want to write the name of the user we tried to kill + */ + my_error(error, MYF(0), user->host.str, user->user.str); + } +} + + /** If pointer is not a null pointer, append filename to it. */ bool append_file_to_dir(THD *thd, const char **filename_ptr, diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index fc472c17e57..80231c1a293 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1277,9 +1277,9 @@ err: idle, then this could last long, and if the slave reconnects, we could have 2 Binlog_dump threads in SHOW PROCESSLIST, until a query is written to the binlog. To avoid this, when the slave reconnects and sends COM_BINLOG_DUMP, - the master kills any existing thread with the slave's server id (if this id is - not zero; it will be true for real slaves, but false for mysqlbinlog when it - sends COM_BINLOG_DUMP to get a remote binlog dump). + the master kills any existing thread with the slave's server id (if this id + is not zero; it will be true for real slaves, but false for mysqlbinlog when + it sends COM_BINLOG_DUMP to get a remote binlog dump). SYNOPSIS kill_zombie_dump_threads() @@ -1311,7 +1311,7 @@ void kill_zombie_dump_threads(uint32 slave_server_id) it will be slow because it will iterate through the list again. We just to do kill the thread ourselves. */ - tmp->awake(THD::KILL_QUERY); + tmp->awake(KILL_QUERY); pthread_mutex_unlock(&tmp->LOCK_thd_data); } } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 2a717ba1572..57d6d585997 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1943,8 +1943,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) pthread_mutex_lock(&tmp->LOCK_thd_data); if ((mysys_var= tmp->mysys_var)) pthread_mutex_lock(&mysys_var->mutex); - thd_info->proc_info= (char*) (tmp->killed != THD::NOT_KILLED && - tmp->killed != THD::KILL_BAD_DATA ? + thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ? "Killed" : 0); #ifndef EMBEDDED_LIBRARY thd_info->state_info= (char*) (tmp->net.reading_or_writing ? @@ -2084,8 +2083,7 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond) if ((mysys_var= tmp->mysys_var)) pthread_mutex_lock(&mysys_var->mutex); /* COMMAND */ - if ((val= (char *) ((tmp->killed != THD::NOT_KILLED && - tmp->killed != THD::KILL_BAD_DATA ? + if ((val= (char *) ((tmp->killed >= KILL_QUERY ? "Killed" : 0)))) table->field[4]->store(val, strlen(val), cs); else diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 299e88107d7..e6ec80bbd8d 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -59,7 +59,7 @@ static void wait_for_kill_signal(THD *thd) while (thd->killed == 0) sleep(1); // Reset signal and continue as if nothing happend - thd->killed= THD::NOT_KILLED; + thd->killed= NOT_KILLED; } #endif diff --git a/sql/sql_update.cc b/sql/sql_update.cc index b516065eff7..80eb823c346 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -221,7 +221,7 @@ int mysql_update(THD *thd, bool need_reopen; ulonglong id; List all_fields; - THD::killed_state killed_status= THD::NOT_KILLED; + killed_state killed_status= NOT_KILLED; DBUG_ENTER("mysql_update"); for ( ; ; ) @@ -788,9 +788,9 @@ int mysql_update(THD *thd, // simulated killing after the loop must be ineffective for binlogging DBUG_EXECUTE_IF("simulate_kill_bug27571", { - thd->killed= THD::KILL_QUERY; + thd->killed= KILL_QUERY; };); - error= (killed_status == THD::NOT_KILLED)? error : 1; + error= (killed_status == NOT_KILLED)? error : 1; if (error && will_batch && @@ -851,7 +851,7 @@ int mysql_update(THD *thd, if (error < 0) thd->clear_error(); else - errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); + errcode= query_error_code(thd, killed_status == NOT_KILLED); if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), thd->query_length(), @@ -1937,7 +1937,7 @@ void multi_update::abort() got caught and if happens later the killed error is written into repl event. */ - int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED); + int errcode= query_error_code(thd, thd->killed == NOT_KILLED); /* the error of binary logging is ignored */ (void)thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), thd->query_length(), @@ -2151,7 +2151,7 @@ bool multi_update::send_eof() { char buff[STRING_BUFFER_USUAL_SIZE]; ulonglong id; - THD::killed_state killed_status= THD::NOT_KILLED; + killed_state killed_status= NOT_KILLED; DBUG_ENTER("multi_update::send_eof"); thd_proc_info(thd, "updating reference tables"); @@ -2164,7 +2164,7 @@ bool multi_update::send_eof() if local_error is not set ON until after do_updates() then later carried out killing should not affect binlogging. */ - killed_status= (local_error == 0)? THD::NOT_KILLED : thd->killed; + killed_status= (local_error == 0) ? NOT_KILLED : thd->killed; thd_proc_info(thd, "end"); /* We must invalidate the query cache before binlog writing and @@ -2193,7 +2193,7 @@ bool multi_update::send_eof() if (local_error == 0) thd->clear_error(); else - errcode= query_error_code(thd, killed_status == THD::NOT_KILLED); + errcode= query_error_code(thd, killed_status == NOT_KILLED); if (thd->binlog_query(THD::ROW_QUERY_TYPE, thd->query(), thd->query_length(), transactional_tables, FALSE, errcode)) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c2fea985aa1..08738f14cca 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -682,10 +682,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %pure_parser /* We have threads */ /* - Currently there are 171 shift/reduce conflicts. + Currently there are 174 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 171 +%expect 174 /* Comments for TOKENS. @@ -901,6 +901,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token GROUP_CONCAT_SYM %token GT_SYM /* OPERATOR */ %token HANDLER_SYM +%token HARD_SYM %token HASH_SYM %token HAVING /* SQL-2003-R */ %token HELP_SYM @@ -1169,6 +1170,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token SMALLINT /* SQL-2003-R */ %token SNAPSHOT_SYM %token SOCKET_SYM +%token SOFT_SYM %token SONAME_SYM %token SOUNDS_SYM %token SOURCE_SYM @@ -1348,7 +1350,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt optional_flush_tables_arguments opt_dyncol_type dyncol_type - opt_time_precision + opt_time_precision kill_type kill_option %type ulong_num real_ulong_num merge_insert_types @@ -1380,7 +1382,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); function_call_keyword function_call_nonkeyword function_call_generic - function_call_conflict + function_call_conflict kill_expr %type NUM_literal @@ -11081,19 +11083,41 @@ purge_option: /* kill threads */ kill: - KILL_SYM kill_option expr + KILL_SYM { LEX *lex=Lex; lex->value_list.empty(); - lex->value_list.push_front($3); + lex->users_list.empty(); lex->sql_command= SQLCOM_KILL; } + kill_type kill_option kill_expr + { + Lex->kill_signal= (killed_state) ($3 | $4); + } ; +kill_type: + /* Empty */ { $$= (int) KILL_HARD_BIT; } + | HARD_SYM { $$= (int) KILL_HARD_BIT; } + | SOFT_SYM { $$= 0; } + kill_option: - /* empty */ { Lex->type= 0; } - | CONNECTION_SYM { Lex->type= 0; } - | QUERY_SYM { Lex->type= ONLY_KILL_QUERY; } + /* empty */ { $$= (int) KILL_CONNECTION; } + | CONNECTION_SYM { $$= (int) KILL_CONNECTION; } + | QUERY_SYM { $$= (int) KILL_QUERY; } + ; + +kill_expr: + expr + { + Lex->value_list.push_front($$); + Lex->kill_type= KILL_TYPE_ID; + } + | USER user + { + Lex->users_list.push_back($2); + Lex->kill_type= KILL_TYPE_USER; + } ; /* change database */ @@ -12208,6 +12232,7 @@ keyword_sp: | GRANTS {} | GLOBAL_SYM {} | HASH_SYM {} + | HARD_SYM {} | HOSTS_SYM {} | HOUR_SYM {} | IDENTIFIED_SYM {} @@ -12339,6 +12364,7 @@ keyword_sp: | SHUTDOWN {} | SLOW_SYM {} | SNAPSHOT_SYM {} + | SOFT_SYM {} | SOUNDS_SYM {} | SOURCE_SYM {} | SQL_CACHE_SYM {} -- cgit v1.2.1 From 14c767ca48d6a86a3d495476bb6b5999036e00af Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Fri, 23 Sep 2011 12:00:52 +0200 Subject: fix typo: binlog_annotate_rows_events -> binlog_annotate_row_events --- sql/handler.cc | 4 ++-- sql/log_event.h | 2 +- sql/mysqld.cc | 14 +++++++------- sql/rpl_rli.h | 8 ++++---- sql/set_var.cc | 4 ++-- sql/slave.cc | 6 +++--- sql/slave.h | 2 +- sql/sql_class.h | 2 +- sql/sql_insert.cc | 2 +- sql/sql_repl.cc | 6 +++--- 10 files changed, 25 insertions(+), 25 deletions(-) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 905668114dd..a9f5b6f20b6 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -4721,7 +4721,7 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table) /** @brief Write table maps for all (manually or automatically) locked tables - to the binary log. Also, if binlog_annotate_rows_events is ON, + to the binary log. Also, if binlog_annotate_row_events is ON, write Annotate_rows event before the first table map. SYNOPSIS @@ -4759,7 +4759,7 @@ static int write_locked_table_maps(THD *thd) locks[0]= thd->extra_lock; locks[1]= thd->lock; locks[2]= thd->locked_tables; - my_bool with_annotate= thd->variables.binlog_annotate_rows_events && + my_bool with_annotate= thd->variables.binlog_annotate_row_events && thd->query() && thd->query_length(); for (uint i= 0 ; i < sizeof(locks)/sizeof(*locks) ; ++i ) diff --git a/sql/log_event.h b/sql/log_event.h index 4053db4e3e7..ab58aac5fd0 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -3082,7 +3082,7 @@ char *str_to_hex(char *to, const char *from, uint len); /** @class Annotate_rows_log_event - In row-based mode, if binlog_annotate_rows_events = ON, each group of + In row-based mode, if binlog_annotate_row_events = ON, each group of Table_map_log_events is preceded by an Annotate_rows_log_event which contains the query which caused the subsequent rows operations. diff --git a/sql/mysqld.cc b/sql/mysqld.cc index aa394c4b7da..7f87ada8011 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -567,7 +567,7 @@ my_bool opt_local_infile, opt_slave_compressed_protocol; my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0; my_bool opt_log_slave_updates= 0; -my_bool opt_replicate_annotate_rows_events= 0; +my_bool opt_replicate_annotate_row_events= 0; bool slave_warning_issued = false; /* @@ -6230,17 +6230,17 @@ struct my_option my_long_options[] = #endif , &opt_binlog_format, &opt_binlog_format, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, - {"binlog-annotate-rows-events", OPT_BINLOG_ANNOTATE_ROWS_EVENTS, + {"binlog-annotate-row-events", OPT_BINLOG_ANNOTATE_ROWS_EVENTS, "Tells the master to annotate RBR events with the statement that " "caused these events.", - (uchar**) &global_system_variables.binlog_annotate_rows_events, - (uchar**) &max_system_variables.binlog_annotate_rows_events, + (uchar**) &global_system_variables.binlog_annotate_row_events, + (uchar**) &max_system_variables.binlog_annotate_row_events, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"replicate-annotate-rows-events", OPT_REPLICATE_ANNOTATE_ROWS_EVENTS, + {"replicate-annotate-row-events", OPT_REPLICATE_ANNOTATE_ROWS_EVENTS, "Tells the slave to write annotate rows events recieved from the master " "to its own binary log. Sensible only in pair with log-slave-updates option.", - (uchar**) &opt_replicate_annotate_rows_events, - (uchar**) &opt_replicate_annotate_rows_events, + (uchar**) &opt_replicate_annotate_row_events, + (uchar**) &opt_replicate_annotate_row_events, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"binlog-do-db", OPT_BINLOG_DO_DB, "Tells the master it should log updates for the specified database, " diff --git a/sql/rpl_rli.h b/sql/rpl_rli.h index 0ea3c23bfd8..a143f5251af 100644 --- a/sql/rpl_rli.h +++ b/sql/rpl_rli.h @@ -425,14 +425,14 @@ public: /** Save pointer to Annotate_rows event and switch on the - binlog_annotate_rows_events for this sql thread. + binlog_annotate_row_events for this sql thread. To be called when sql thread recieves an Annotate_rows event. */ inline void set_annotate_event(Annotate_rows_log_event *event) { free_annotate_event(); m_annotate_event= event; - sql_thd->variables.binlog_annotate_rows_events= 1; + sql_thd->variables.binlog_annotate_row_events= 1; } /** @@ -446,7 +446,7 @@ public: /** Delete saved Annotate_rows event (if any) and switch off the - binlog_annotate_rows_events for this sql thread. + binlog_annotate_row_events for this sql thread. To be called when sql thread has applied the last (i.e. with STMT_END_F flag) rbr event. */ @@ -454,7 +454,7 @@ public: { if (m_annotate_event) { - sql_thd->variables.binlog_annotate_rows_events= 0; + sql_thd->variables.binlog_annotate_row_events= 0; delete m_annotate_event; m_annotate_event= 0; } diff --git a/sql/set_var.cc b/sql/set_var.cc index 0a06b3254f6..a934d0dc4dc 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -189,8 +189,8 @@ static sys_var_const sys_back_log(&vars, "back_log", (uchar*) &back_log); static sys_var_const_os_str sys_basedir(&vars, "basedir", mysql_home); static sys_var_thd_bool -sys_binlog_annotate_rows_events(&vars, "binlog_annotate_rows_events", - &SV::binlog_annotate_rows_events); +sys_binlog_annotate_row_events(&vars, "binlog_annotate_row_events", + &SV::binlog_annotate_row_events); static sys_var_long_ptr sys_binlog_cache_size(&vars, "binlog_cache_size", &binlog_cache_size); static sys_var_thd_binlog_format sys_binlog_format(&vars, "binlog_format", diff --git a/sql/slave.cc b/sql/slave.cc index 1ab1caecfb5..aa8b4ba920b 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2013,7 +2013,7 @@ static int request_dump(MYSQL* mysql, Master_info* mi, *suppress_warnings= FALSE; - if (opt_log_slave_updates && opt_replicate_annotate_rows_events) + if (opt_log_slave_updates && opt_replicate_annotate_row_events) binlog_flags|= BINLOG_SEND_ANNOTATE_ROWS_EVENT; // TODO if big log files: Change next to int8store() @@ -3077,11 +3077,11 @@ pthread_handler_t handle_slave_sql(void *arg) thd->temporary_tables = rli->save_temporary_tables; // restore temp tables set_thd_in_use_temporary_tables(rli); // (re)set sql_thd in use for saved temp tables /* - binlog_annotate_rows_events must be TRUE only after an Annotate_rows event + binlog_annotate_row_events must be TRUE only after an Annotate_rows event has been recieved and only till the last corresponding rbr event has been applied. In all other cases it must be FALSE. */ - thd->variables.binlog_annotate_rows_events= 0; + thd->variables.binlog_annotate_row_events= 0; pthread_mutex_lock(&LOCK_thread_count); threads.append(thd); pthread_mutex_unlock(&LOCK_thread_count); diff --git a/sql/slave.h b/sql/slave.h index 3333e583559..bd4302ff3a1 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -106,7 +106,7 @@ extern MYSQL_PLUGIN_IMPORT char *relay_log_info_file; extern char *opt_relay_logname, *opt_relaylog_index_name; extern my_bool opt_skip_slave_start, opt_reckless_slave; extern my_bool opt_log_slave_updates; -extern my_bool opt_replicate_annotate_rows_events; +extern my_bool opt_replicate_annotate_row_events; extern ulonglong relay_log_space_limit; /* diff --git a/sql/sql_class.h b/sql/sql_class.h index 748c2a4a818..e77d9c0cee2 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -460,7 +460,7 @@ struct system_variables ulong ndb_index_stat_update_freq; ulong binlog_format; // binlog format for this thd (see enum_binlog_format) ulong progress_report_time; - my_bool binlog_annotate_rows_events; + my_bool binlog_annotate_row_events; my_bool binlog_direct_non_trans_update; /* In slave thread we need to know in behalf of which diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index b840eebf4cc..5cb4c094a33 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2010,7 +2010,7 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list) /* Annotating delayed inserts is not supported. */ - di->thd.variables.binlog_annotate_rows_events= 0; + di->thd.variables.binlog_annotate_row_events= 0; di->thd.set_db(table_list->db, (uint) strlen(table_list->db)); di->thd.set_query(my_strdup(table_list->table_name, MYF(MY_WME)), 0); diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index fc472c17e57..02b0fea09a5 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1978,10 +1978,10 @@ static sys_var_const sys_log_slave_updates(&vars, "log_slave_updates", OPT_GLOBAL, SHOW_MY_BOOL, (uchar*) &opt_log_slave_updates); static sys_var_const -sys_replicate_annotate_rows_events(&vars, - "replicate_annotate_rows_events", +sys_replicate_annotate_row_events(&vars, + "replicate_annotate_row_events", OPT_GLOBAL, SHOW_MY_BOOL, - (uchar*) &opt_replicate_annotate_rows_events); + (uchar*) &opt_replicate_annotate_row_events); static sys_var_const sys_relay_log(&vars, "relay_log", OPT_GLOBAL, SHOW_CHAR_PTR, (uchar*) &opt_relay_logname); -- cgit v1.2.1 From f0c6ce9adec82ceec054b0f6e973ab1526a033c4 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Fri, 23 Sep 2011 13:55:01 +0300 Subject: Fixed issue with slow query logging where examined rows where wrong Reset 'examined_rows_count' in union to not count same rows twice mysql-test/r/subselect_mat_cost.result: Test also slow query logging mysql-test/t/subselect_mat_cost.test: Test also slow query logging sql/sql_union.cc: Reset 'examined_rows_count' in union to not count same rows twice --- sql/sql_union.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'sql') diff --git a/sql/sql_union.cc b/sql/sql_union.cc index d2cb21a59a1..50de42d153c 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -645,6 +645,7 @@ bool st_select_lex_unit::exec() if (!saved_error) { 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 4908d27b57ee00dba3694e300a858b0fcdb224ee Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Mon, 26 Sep 2011 13:56:09 +0400 Subject: BUG#858732: Wrong result with semijoin + loosescan + comma join - Fix wrong loop bounds in setup_semijoin_dups_elimination() --- sql/opt_subselect.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index beeb0ac878e..7ac09eaa434 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -3834,7 +3834,7 @@ int setup_semijoin_dups_elimination(JOIN *join, ulonglong options, { /* We jump from the last table to the first one */ tab->loosescan_match_tab= tab + pos->n_sj_tables - 1; - for (uint j= i; j < pos->n_sj_tables; j++) + for (uint j= i; j < i + pos->n_sj_tables; j++) join->join_tab[j].inside_loosescan_range= TRUE; /* Calculate key length */ -- cgit v1.2.1 From 7800d93bc3caca0143334941f626dc6aa3ff2b26 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Mon, 26 Sep 2011 20:26:47 +0300 Subject: Allow one to block an account by using GRANT max_user_connections = -1 One can set @@global.max_user_connections to -1 to block anyone, except SUPER user, to login. If max_user_connection is 0, one can't change it without a restart (needed to get user connections counting to work correctly) mysql-test/r/system_mysql_db.result: Changed max_user_connections to handle negative numbers. mysql-test/r/user_limits-2.result: New test case that one can't change max_user_connection if it was 0 mysql-test/r/user_limits.result: Fixed wrong error messages. mysql-test/r/variables.result: Store / restore max_user_connections (needed as there is now a --master.opt file that changes it) mysql-test/t/subselect_mat_cost-master.opt: Enable slow query log (as this test found some errors in slow query logging) mysql-test/t/user_limits-2.test: New test case that one can't change max_user_connection if it was 0 mysql-test/t/user_limits-master.opt: Set max_user_connections (as one can't change it if it was 0) mysql-test/t/user_limits.test: Test max_user_connections -1 mysql-test/t/variables-master.opt: Set max_user_connections (as one can't change it if it was 0) mysql-test/t/variables.test: Set/restore max_user_connections scripts/Makefile.am: Add a text message to mysql_fix_privilege_tables.sql that it's automaticly generated scripts/mysql_system_tables.sql: Change max_user_connections to signed scripts/mysql_system_tables_fix.sql: Change max_user_connections to signed sql/item_func.cc: Change SHOW_INT to be signed. (Needed for max_user_connections and it's probably a bug that it was not originally signed) sql/log.cc: Remove some code that was not needed (All these variables are reset at start of query) sql/mysql_priv.h: Made max_user_connections signed. Added max_user_connections_checking sql/mysqld.cc: Added max_user_connections_checking so that we know if max_user_connections was 0 at startup (Which means that we will not do connection counting for accounts that don't have user resource limits) Set thd->start_utime at same time as thr_create_utime. (Before start_utime could be < thr_create_utime which lead to wrong query counting) sql/set_var.cc: Don't allow one to change 'max_user_connections' if it was 0 at startup. sql/sql_acl.cc: Change user_connection counting to be negative. sql/sql_connect.cc: If max_user_connections is < 0 then only SUPER user can login. Fixed wrong variable names for error messages. Fixed wrong initial value for questions. Set thd->start_utime and thd->thr_create_utime at startup. Needed to get time_out_user_resource_limits() to work. sql/sql_show.cc: SHOW_INT is now negative sql/sql_yacc.yy: Support negative values for MAX_USER_CONNECTIONS sql/structs.h: Make user connect counting work with signed numbers. --- sql/item_func.cc | 8 ++++++-- sql/log.cc | 11 ----------- sql/mysql_priv.h | 3 ++- sql/mysqld.cc | 14 +++++++++----- sql/set_var.cc | 14 +++++++++++++- sql/sql_acl.cc | 29 ++++++++++++++++++----------- sql/sql_connect.cc | 13 +++++++++---- sql/sql_show.cc | 2 +- sql/sql_yacc.yy | 10 ++++++++-- sql/structs.h | 9 ++++++--- 10 files changed, 72 insertions(+), 41 deletions(-) (limited to 'sql') diff --git a/sql/item_func.cc b/sql/item_func.cc index b907d1f432e..ce43f90be8d 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -5063,12 +5063,16 @@ void Item_func_get_system_var::fix_length_and_dec() switch (var->show_type()) { case SHOW_LONG: - case SHOW_INT: case SHOW_HA_ROWS: unsigned_flag= TRUE; max_length= MY_INT64_NUM_DECIMAL_DIGITS; decimals=0; break; + case SHOW_INT: + unsigned_flag= FALSE; + max_length= MY_INT64_NUM_DECIMAL_DIGITS; + decimals=0; + break; case SHOW_LONGLONG: unsigned_flag= TRUE; max_length= MY_INT64_NUM_DECIMAL_DIGITS; @@ -5209,7 +5213,7 @@ longlong Item_func_get_system_var::val_int() switch (var->show_type()) { - case SHOW_INT: get_sys_var_safe (uint); + case SHOW_INT: get_sys_var_safe (int); case SHOW_LONG: get_sys_var_safe (ulong); case SHOW_LONGLONG: get_sys_var_safe (ulonglong); case SHOW_HA_ROWS: get_sys_var_safe (ha_rows); diff --git a/sql/log.cc b/sql/log.cc index 649db6a4c29..332cdce803e 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -1062,17 +1062,6 @@ bool LOGGER::slow_log_print(THD *thd, const char *query, uint query_length, query_length= command_name[thd->command].length; } - if (!query_length) - { - /* - Not a real query; Reset counts for slow query logging - (QQ: Wonder if this is really needed) - */ - thd->sent_row_count= thd->examined_row_count= 0; - thd->query_plan_flags= QPLAN_INIT; - thd->query_plan_fsort_passes= 0; - } - for (current_handler= slow_log_handler_list; *current_handler ;) error= (*current_handler++)->log_slow(thd, current_time, user_host_buff, user_host_len, diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 62fdef5aec6..bccd5b189ff 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -2151,7 +2151,8 @@ extern MYSQL_PLUGIN_IMPORT ulong max_connections; extern ulong max_connect_errors, connect_timeout; extern ulong extra_max_connections; extern ulong slave_net_timeout, slave_trans_retries; -extern uint max_user_connections; +extern int max_user_connections; +extern bool max_user_connections_checking; extern ulonglong denied_connections; extern ulong what_to_log,flush_time; extern ulong query_buff_size; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a54f845a762..09fc0258c1c 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -669,7 +669,8 @@ ulong extra_max_connections; */ ulong max_long_data_size; -uint max_user_connections= 0; +int max_user_connections= 0; +bool max_user_connections_checking=0; ulonglong denied_connections; /** Limit of the total number of prepared statements in the server. @@ -2142,6 +2143,7 @@ static bool cache_thread() */ thd->mysys_var->abort= 0; thd->thr_create_utime= microsecond_interval_timer(); + thd->start_utime= thd->thr_create_utime; threads.append(thd); return(1); } @@ -5263,7 +5265,7 @@ void create_thread_to_handle_connection(THD *thd) thread_created++; threads.append(thd); DBUG_PRINT("info",(("creating thread %lu"), thd->thread_id)); - thd->prior_thr_create_utime= thd->start_utime= microsecond_interval_timer(); + thd->prior_thr_create_utime= microsecond_interval_timer(); if ((error=pthread_create(&thd->real_id,&connection_attrib, handle_one_connection, (void*) thd))) @@ -7432,9 +7434,9 @@ each time the SQL thread starts.", &max_system_variables.max_tmp_tables, 0, GET_ULONG, REQUIRED_ARG, 32, 1, (longlong) ULONG_MAX, 0, 1, 0}, {"max_user_connections", OPT_MAX_USER_CONNECTIONS, - "The maximum number of active connections for a single user (0 = no limit).", - &max_user_connections, &max_user_connections, 0, GET_UINT, - REQUIRED_ARG, 0, 0, UINT_MAX, 0, 1, 0}, + "The maximum number of active connections for a single user (0 = no limit. In addition global max_user_connections counting and checking is permanently disabled).", + &max_user_connections, &max_user_connections, 0, GET_INT, + REQUIRED_ARG, 0, 0, INT_MAX, 0, 1, 0}, {"max_write_lock_count", OPT_MAX_WRITE_LOCK_COUNT, "After this many write locks, allow some read locks to run in between.", &max_write_lock_count, &max_write_lock_count, 0, GET_ULONG, @@ -9641,6 +9643,8 @@ static int get_options(int *argc,char **argv) if (!max_long_data_size_used) max_long_data_size= global_system_variables.max_allowed_packet; + /* Rember if max_user_connections was 0 at startup */ + max_user_connections_checking= max_user_connections != 0; return 0; } diff --git a/sql/set_var.cc b/sql/set_var.cc index 0a06b3254f6..10f3e20cc86 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -3082,7 +3082,19 @@ void sys_var_thd_time_zone::set_default(THD *thd, enum_var_type type) bool sys_var_max_user_conn::check(THD *thd, set_var *var) { if (var->type == OPT_GLOBAL) + { + if (! max_user_connections_checking) + { + /* + We can't change the value of max_user_connections from 0 as then + connect counting would be wrong. + */ + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), + "--max-user-connections=0"); + return TRUE; + } return sys_var_thd::check(thd, var); + } else { /* @@ -3098,7 +3110,7 @@ bool sys_var_max_user_conn::update(THD *thd, set_var *var) { DBUG_ASSERT(var->type == OPT_GLOBAL); pthread_mutex_lock(&LOCK_global_system_variables); - max_user_connections= (uint)var->save_result.ulonglong_value; + max_user_connections= (int) var->save_result.ulonglong_value; pthread_mutex_unlock(&LOCK_global_system_variables); return 0; } diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 61a6ac0cb87..ae286878cea 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2089,7 +2089,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, table->field[next_field+2]->store((longlong) mqh.conn_per_hour, TRUE); if (table->s->fields >= 36 && (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS)) - table->field[next_field+3]->store((longlong) mqh.user_conn, TRUE); + table->field[next_field+3]->store((longlong) mqh.user_conn, FALSE); mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour; next_field+=4; @@ -4578,7 +4578,8 @@ ulong get_column_grant(THD *thd, GRANT_INFO *grant, /* Help function for mysql_show_grants */ -static void add_user_option(String *grant, ulong value, const char *name) +static void add_user_option(String *grant, long value, const char *name, + my_bool is_signed) { if (value) { @@ -4586,7 +4587,7 @@ static void add_user_option(String *grant, ulong value, const char *name) grant->append(' '); grant->append(name, strlen(name)); grant->append(' '); - p=int10_to_str(value, buff, 10); + p=int10_to_str(value, buff, is_signed ? -10 : 10); grant->append(buff,p-buff); } } @@ -4768,13 +4769,13 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) if (want_access & GRANT_ACL) global.append(STRING_WITH_LEN(" GRANT OPTION")); add_user_option(&global, acl_user->user_resource.questions, - "MAX_QUERIES_PER_HOUR"); + "MAX_QUERIES_PER_HOUR", 0); add_user_option(&global, acl_user->user_resource.updates, - "MAX_UPDATES_PER_HOUR"); + "MAX_UPDATES_PER_HOUR", 0); add_user_option(&global, acl_user->user_resource.conn_per_hour, - "MAX_CONNECTIONS_PER_HOUR"); + "MAX_CONNECTIONS_PER_HOUR", 0); add_user_option(&global, acl_user->user_resource.user_conn, - "MAX_USER_CONNECTIONS"); + "MAX_USER_CONNECTIONS", 1); } protocol->prepare_for_resend(); protocol->store(global.ptr(),global.length(),global.charset()); @@ -8147,10 +8148,16 @@ bool acl_authenticate(THD *thd, uint connect_errors, DBUG_RETURN(1); } - /* Don't allow the user to connect if he has done too many queries */ - if ((acl_user->user_resource.questions || acl_user->user_resource.updates || + /* + Don't allow the user to connect if he has done too many queries. + As we are testing max_user_connections == 0 here, it means that we + can't let the user change max_user_connections from 0 in the server + without a restart as it would lead to wrong connect counting. + */ + if ((acl_user->user_resource.questions || + acl_user->user_resource.updates || acl_user->user_resource.conn_per_hour || - acl_user->user_resource.user_conn || max_user_connections) && + acl_user->user_resource.user_conn || max_user_connections_checking) && get_or_create_user_conn(thd, (opt_old_style_user_limits ? sctx->user : sctx->priv_user), (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host), @@ -8163,7 +8170,7 @@ bool acl_authenticate(THD *thd, uint connect_errors, if (thd->user_connect && (thd->user_connect->user_resources.conn_per_hour || thd->user_connect->user_resources.user_conn || - max_user_connections) && + max_user_connections_checking) && check_for_max_user_connections(thd, thd->user_connect)) { /* Ensure we don't decrement thd->user_connections->connections twice */ diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 5e0ab339418..cd51fd25558 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -113,8 +113,11 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc) DBUG_ENTER("check_for_max_user_connections"); (void) pthread_mutex_lock(&LOCK_user_conn); + + /* Root is not affected by the value of max_user_connections */ if (max_user_connections && !uc->user_resources.user_conn && - max_user_connections < (uint) uc->connections) + max_user_connections < uc->connections && + !(thd->security_ctx->master_access & SUPER_ACL)) { my_error(ER_TOO_MANY_USER_CONNECTIONS, MYF(0), uc->user); goto end; @@ -202,7 +205,7 @@ void time_out_user_resource_limits(THD *thd, USER_CONN *uc) /* If more than a hour since last check, reset resource checking */ if (check_time - uc->reset_utime >= LL(3600000000)) { - uc->questions=1; + uc->questions=0; uc->updates=0; uc->conn_per_hour=0; uc->reset_utime= check_time; @@ -231,7 +234,7 @@ bool check_mqh(THD *thd, uint check_command) if (uc->user_resources.questions && uc->questions++ >= uc->user_resources.questions) { - my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_questions", + my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_queries_per_hour", (long) uc->user_resources.questions); error=1; goto end; @@ -243,7 +246,7 @@ bool check_mqh(THD *thd, uint check_command) (sql_command_flags[check_command] & CF_CHANGES_DATA) && uc->updates++ >= uc->user_resources.updates) { - my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_updates", + my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_updates_per_hour", (long) uc->user_resources.updates); error=1; goto end; @@ -1131,6 +1134,8 @@ pthread_handler_t handle_one_connection(void *arg) THD *thd= (THD*) arg; thd->thr_create_utime= microsecond_interval_timer(); + /* We need to set this because of time_out_user_resource_limits */ + thd->start_utime= thd->thr_create_utime; if (thread_scheduler.init_new_connection_thread()) { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 57d6d585997..062bfc347b3 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2439,7 +2439,7 @@ static bool show_status_array(THD *thd, const char *wild, end= strmov(buff, *(my_bool*) value ? "ON" : "OFF"); break; case SHOW_INT: - end= int10_to_str((long) *(uint32*) value, buff, 10); + end= int10_to_str((long) *(int*) value, buff, -10); break; case SHOW_HAVE: { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 08738f14cca..0d0e0ef98a4 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1350,7 +1350,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt optional_flush_tables_arguments opt_dyncol_type dyncol_type - opt_time_precision kill_type kill_option + opt_time_precision kill_type kill_option int_num %type ulong_num real_ulong_num merge_insert_types @@ -9676,6 +9676,12 @@ delete_limit_clause: } ; +int_num: + NUM { int error; $$= (int) my_strtoll10($1.str, (char**) 0, &error); } + | '-' NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } + | '-' LONG_NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } + ; + ulong_num: NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); } @@ -13434,7 +13440,7 @@ grant_option: lex->mqh.conn_per_hour= $2; lex->mqh.specified_limits|= USER_RESOURCES::CONNECTIONS_PER_HOUR; } - | MAX_USER_CONNECTIONS_SYM ulong_num + | MAX_USER_CONNECTIONS_SYM int_num { LEX *lex=Lex; lex->mqh.user_conn= $2; diff --git a/sql/structs.h b/sql/structs.h index 29ccde3fb03..18f90d5b8b1 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -222,8 +222,11 @@ typedef struct user_resources { uint updates; /* Maximum number of connections established per hour. */ uint conn_per_hour; - /* Maximum number of concurrent connections. */ - uint user_conn; + /* + Maximum number of concurrent connections. If -1 then no new + connections allowed + */ + int user_conn; /* Values of this enum and specified_limits member are used by the parser to store which user limits were specified in GRANT statement. @@ -256,7 +259,7 @@ typedef struct user_conn { /* Total length of the key. */ uint len; /* Current amount of concurrent connections for this account. */ - uint connections; + int connections; /* Current number of connections per hour, number of updating statements per hour and total number of statements per hour for this account. -- cgit v1.2.1 From 9da63b5ed5f7f55ebc86dc5d0335c2b22c64cfe1 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Tue, 27 Sep 2011 17:40:04 +0300 Subject: Fixed test case that changed when max_user_connections was made signed. Threat ER_CONNECTION_KILLED same as ER_SERVER_SHUTDOWN in replication (to get rid of a possible warning in error log) sql/slave.cc: Threat ER_CONNECTION_KILLED same as ER_SERVER_SHUTDOWN --- sql/slave.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'sql') diff --git a/sql/slave.cc b/sql/slave.cc index cb93cf93081..fa7fabb5d99 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -837,6 +837,7 @@ bool is_network_error(uint errorno) errorno == CR_SERVER_GONE_ERROR || errorno == CR_SERVER_LOST || errorno == ER_CON_COUNT_ERROR || + errorno == ER_CONNECTION_KILLED || errorno == ER_NEW_ABORTING_CONNECTION || errorno == ER_SERVER_SHUTDOWN) return TRUE; -- cgit v1.2.1 From 35ecfa90e357a7ab31c7eb18b3c931981ca840a3 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 28 Sep 2011 12:58:01 +0400 Subject: BUG#860300: Second crash with get_fanout_with_deps() with semijoin + materialization - Make get_post_group_estimate() take into account semi-join materialization nests. --- sql/item_subselect.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 43df9fe32a9..a79d035160c 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -4394,7 +4394,13 @@ double get_fanout_with_deps(JOIN *join, table_map tset) for (JOIN_TAB *tab= first_top_level_tab(join, WITHOUT_CONST_TABLES); tab; tab= next_top_level_tab(join, tab)) { - if ((tab->table->map & checked_deps) && !tab->emb_sj_nest && + /* + Ignore SJM nests. They have tab->table==NULL. There is no point to walk + inside them, because GROUP BY clause cannot refer to tables from within + subquery. + */ + if (!tab->is_sjm_nest() && (tab->table->map & checked_deps) && + !tab->emb_sj_nest && tab->records_read != 0) { fanout *= rows2double(tab->records_read); -- cgit v1.2.1 From b53744b79ebb58dfc242638bfb4d9b8c01bb8251 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 28 Sep 2011 17:20:43 +0300 Subject: Fix bug lp:858148. Analysis: The crash is a result of the same cause as all similar bugs (lp:827416, lp:718763, lp:778413, lp:806943, lp:611690). The general pattern is that some optimization requires the evaluation of some condition (e.g. the WHERE clause), and this condition contains a subquery, such that the subquery itself requires a temporary table for its execution. During the subquery execution the original tables in the FROM clause are replaced by the temporary table needed for the final GROUP or ORDER operation. All this happens during optimization of the outer query. Later when EXPLAIN is run for the subquery, explain attempts to print the name of the tables in the FROM clause, but it finds there a temporary table without a corresponding TABLE_LIST object. The attempt to print the name of a NULL table list results in a crash. Solution: This patch extends the fix to bug lp:702301, and dissalows constant substitution of aggregate functions if the filter condition used to check MIN/MAX keys is an expensive condition. --- sql/opt_sum.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sql') diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index dc93548418a..7b4c48497fa 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -625,6 +625,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, /* Condition doesn't restrict the used table */ DBUG_RETURN(!cond->const_item()); } + else if (cond->is_expensive()) + DBUG_RETURN(FALSE); if (cond->type() == Item::COND_ITEM) { if (((Item_cond*) cond)->functype() == Item_func::COND_OR_FUNC) -- cgit v1.2.1 From 134e417895f741223b2a8b68c0b674d14920d316 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 29 Sep 2011 17:03:12 +0400 Subject: BUG#860535: Assertion `keypart_map' failed in mi_rkey with semijoin - are_tables_local() failed to recognize the fact that OUTER_REF_TABLE_BIT is ok for SJ-Materialization. This caused zero-length ref access to be constructed, which led to an assert. --- sql/sql_select.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0c4b1396245..3d9698d6247 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7239,7 +7239,8 @@ static bool are_tables_local(JOIN_TAB *jtab, table_map used_tables) except the const tables. */ table_map local_tables= jtab->emb_sj_nest->nested_join->used_tables | - jtab->join->const_table_map; + jtab->join->const_table_map | + OUTER_REF_TABLE_BIT; return !test(used_tables & ~local_tables); } -- cgit v1.2.1 From 0200ee7003c1cc4183c76bf5a089ad759c363ad9 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 6 Oct 2011 01:11:08 +0400 Subject: BUG#860580: Sporadic crash / valgrind warning in register_field_in_read_map() with semijoin The problem: in an uncorrelated subquery, JOIN execution code may make irreversible cleanups after it has been executed (because the subquery won't be executed again). In particular, JTBM nests and their injected IN-equalities will be invalidated (the materialized table will be dropped and TABLE structure freed). Solution: don't walk into Item_subselect if represents an uncorrelated subselect that has already been executed. The idea is that the subselect will not be executed again (calling Item_subselect_xxx::val_int() will fetch the cached value), so it should not matter what is inside the subselect. --- sql/item_subselect.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'sql') diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index a20e4c4e708..ebc9d47e3a8 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -514,6 +514,20 @@ void Item_subselect::recalc_used_tables(st_select_lex *new_parent, bool Item_subselect::walk(Item_processor processor, bool walk_subquery, uchar *argument) { + if (!(unit->uncacheable & ~UNCACHEABLE_DEPENDENT) && engine->is_executed() && + !unit->describe) + { + /* + The subquery has already been executed (for real, it wasn't EXPLAIN's + fake execution) so it should not matter what it has inside. + + The actual reason for not walking inside is that parts of the subquery + (e.g. JTBM join nests and their IN-equality conditions may have been + invalidated by irreversible cleanups (those happen after an uncorrelated + subquery has been executed). + */ + return FALSE; + } if (walk_subquery) { -- cgit v1.2.1 From 829b1747f9acc3dee0061b6bd4ebaeb38bd7f2f2 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 29 Sep 2011 20:12:57 +0200 Subject: make sure that cast(... as date) returns a valid date, as specified by the caller. make Item::send() request a date according to the current SQL mode limitations. --- sql/item.cc | 2 +- sql/item_timefunc.cc | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index a4cd292a5b1..8710b8f9024 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -5982,7 +5982,7 @@ bool Item::send(Protocol *protocol, String *buffer) case MYSQL_TYPE_TIMESTAMP: { MYSQL_TIME tm; - get_date(&tm, TIME_FUZZY_DATE); + get_date(&tm, TIME_FUZZY_DATE | sql_mode_for_dates()); if (!null_value) { if (f_type == MYSQL_TYPE_DATE) diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index f69d77ad6b5..0f12f6755ed 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2304,7 +2304,17 @@ bool Item_date_typecast::get_date(MYSQL_TIME *ltime, uint fuzzy_date) return 1; ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0; ltime->time_type= MYSQL_TIMESTAMP_DATE; - return 0; + + int unused; + if (check_date(ltime, ltime->year || ltime->month || ltime->day, + fuzzy_date, &unused)) + { + Lazy_string_time str(ltime); + make_truncated_value_warning(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN, + &str, MYSQL_TIMESTAMP_DATE, 0); + return (null_value= 1); + } + return (null_value= 0); } -- cgit v1.2.1 From f5987a0c3ef089439d400217a5784c10fd991f52 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Sat, 1 Oct 2011 00:10:03 +0400 Subject: BUG#860553: Crash in create_ref_for_key with semijoin + materialization - The problem was that JOIN::save/restore_query_plan() did not save/restore parts of the query plan that are located inside SJ_MATERIALIZATION_INFO structures. This could cause parts of one plan to be used with another, which led get_best_combination() to constructing non-sensical join plans (and crash). Fixed by saving/restoring SJM parts of the query plans. - check_and_do_in_subquery_rewrites() will not set SUBS_MATERIALIZATION flag when it records that the subquery predicate is to be converted into semi-join. If convert_join_subqueries_to_semijoins() later decides not to convert to semi-join, let it set SUBS_MATERIALIZATION flag, if appropriate. --- sql/opt_subselect.cc | 125 ++++++++++++++++++++++++++++++++------------------- sql/sql_select.cc | 20 +++++++++ sql/sql_select.h | 1 + 3 files changed, 100 insertions(+), 46 deletions(-) (limited to 'sql') diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 7ac09eaa434..1c317cea808 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -209,6 +209,74 @@ enum_nested_loop_state end_sj_materialize(JOIN *join, JOIN_TAB *join_tab, bool end_of_records); +/* + Check if Materialization strategy is allowed for given subquery predicate. + + @param thd Thread handle + @param in_subs The subquery predicate + @param child_select The select inside predicate (the function will + check it is the only one) + + @return TRUE - Materialization is applicable + FALSE - Otherwise +*/ + +bool is_materialization_applicable(THD *thd, Item_in_subselect *in_subs, + st_select_lex *child_select) +{ + st_select_lex_unit* parent_unit= child_select->master_unit(); + /* + Check if the subquery predicate can be executed via materialization. + The required conditions are: + 0. The materialization optimizer switch was set. + 1. Subquery is a single SELECT (not a UNION). + TODO: this is a limitation that can be fixed + 2. Subquery is not a table-less query. In this case there is no + point in materializing. + 2A The upper query is not a table-less SELECT ... FROM DUAL. We + can't do materialization for SELECT .. FROM DUAL because it + does not call setup_subquery_materialization(). We could make + SELECT ... FROM DUAL call that function but that doesn't seem + to be the case that is worth handling. + 3. Either the subquery predicate is a top-level predicate, or at + least one partial match strategy is enabled. If no partial match + strategy is enabled, then materialization cannot be used for + non-top-level queries because it cannot handle NULLs correctly. + 4. Subquery is non-correlated + TODO: + This condition is too restrictive (limitation). It can be extended to: + (Subquery is non-correlated || + Subquery is correlated to any query outer to IN predicate || + (Subquery is correlated to the immediate outer query && + Subquery !contains {GROUP BY, ORDER BY [LIMIT], + aggregate functions}) && subquery predicate is not under "NOT IN")) + + (*) The subquery must be part of a SELECT statement. The current + condition also excludes multi-table update statements. + A note about prepared statements: we want the if-branch to be taken on + PREPARE and each EXECUTE. The rewrites are only done once, but we need + select_lex->sj_subselects list to be populated for every EXECUTE. + + */ + if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION) && // 0 + !child_select->is_part_of_union() && // 1 + parent_unit->first_select()->leaf_tables.elements && // 2 + thd->lex->sql_command == SQLCOM_SELECT && // * + child_select->outer_select()->leaf_tables.elements && // 2A + subquery_types_allow_materialization(in_subs) && + (in_subs->is_top_level_item() || //3 + optimizer_flag(thd, + OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE) || //3 + optimizer_flag(thd, + OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN)) && //3 + !in_subs->is_correlated) //4 + { + return TRUE; + } + return FALSE; +} + + /* Check if we need JOIN::prepare()-phase subquery rewrites and if yes, do them @@ -381,52 +449,8 @@ int check_and_do_in_subquery_rewrites(JOIN *join) */ if (in_subs) { - /* - Check if the subquery predicate can be executed via materialization. - The required conditions are: - 0. The materialization optimizer switch was set. - 1. Subquery is a single SELECT (not a UNION). - TODO: this is a limitation that can be fixed - 2. Subquery is not a table-less query. In this case there is no - point in materializing. - 2A The upper query is not a table-less SELECT ... FROM DUAL. We - can't do materialization for SELECT .. FROM DUAL because it - does not call setup_subquery_materialization(). We could make - SELECT ... FROM DUAL call that function but that doesn't seem - to be the case that is worth handling. - 3. Either the subquery predicate is a top-level predicate, or at - least one partial match strategy is enabled. If no partial match - strategy is enabled, then materialization cannot be used for - non-top-level queries because it cannot handle NULLs correctly. - 4. Subquery is non-correlated - TODO: - This condition is too restrictive (limitation). It can be extended to: - (Subquery is non-correlated || - Subquery is correlated to any query outer to IN predicate || - (Subquery is correlated to the immediate outer query && - Subquery !contains {GROUP BY, ORDER BY [LIMIT], - aggregate functions}) && subquery predicate is not under "NOT IN")) - - (*) The subquery must be part of a SELECT statement. The current - condition also excludes multi-table update statements. - A note about prepared statements: we want the if-branch to be taken on - PREPARE and each EXECUTE. The rewrites are only done once, but we need - select_lex->sj_subselects list to be populated for every EXECUTE. - - */ - if (optimizer_flag(thd, OPTIMIZER_SWITCH_MATERIALIZATION) && // 0 - !select_lex->is_part_of_union() && // 1 - parent_unit->first_select()->leaf_tables.elements && // 2 - thd->lex->sql_command == SQLCOM_SELECT && // * - select_lex->outer_select()->leaf_tables.elements && // 2A - subquery_types_allow_materialization(in_subs) && - (in_subs->is_top_level_item() || //3 - optimizer_flag(thd, - OPTIMIZER_SWITCH_PARTIAL_MATCH_ROWID_MERGE) || //3 - optimizer_flag(thd, - OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN)) && //3 - !in_subs->is_correlated) //4 - { + if (is_materialization_applicable(thd, in_subs, select_lex)) + { in_subs->in_strategy|= SUBS_MATERIALIZATION; /* @@ -914,6 +938,12 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) check_and_do_in_subquery_rewrites. */ in_subq->in_strategy= SUBS_IN_TO_EXISTS; + if (is_materialization_applicable(thd, in_subq, + in_subq->unit->first_select())) + { + in_subq->in_strategy|= SUBS_MATERIALIZATION; + } + in_subq= li++; } @@ -1766,6 +1796,9 @@ int pull_out_semijoin_tables(JOIN *join) All obtained information is saved and will be used by the main join optimization pass. + + NOTES + Because of Join::reoptimize(), this function may be called multiple times. RETURN FALSE Ok diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 3d9698d6247..7e32c750ab9 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -21576,6 +21576,15 @@ void JOIN::save_query_plan(Join_plan_state *save_to) memcpy((uchar*) save_to->best_positions, (uchar*) best_positions, sizeof(POSITION) * (table_count + 1)); memset(best_positions, 0, sizeof(POSITION) * (table_count + 1)); + + /* Save SJM nests */ + List_iterator it(select_lex->sj_nests); + TABLE_LIST *tlist; + SJ_MATERIALIZATION_INFO **p_info= save_to->sj_mat_info; + while ((tlist= it++)) + { + *(p_info++)= tlist->sj_mat_info; + } } @@ -21616,6 +21625,14 @@ void JOIN::restore_query_plan(Join_plan_state *restore_from) } memcpy((uchar*) best_positions, (uchar*) restore_from->best_positions, sizeof(POSITION) * (table_count + 1)); + /* Restore SJM nests */ + List_iterator it(select_lex->sj_nests); + TABLE_LIST *tlist; + SJ_MATERIALIZATION_INFO **p_info= restore_from->sj_mat_info; + while ((tlist= it++)) + { + tlist->sj_mat_info= *(p_info++); + } } @@ -21705,6 +21722,9 @@ JOIN::reoptimize(Item *added_where, table_map join_tables, return REOPT_ERROR; optimize_keyuse(this, &keyuse); + if (optimize_semijoin_nests(this, join_tables)) + return REOPT_ERROR; + /* Re-run the join optimizer to compute a new query plan. */ if (choose_plan(this, join_tables)) return REOPT_ERROR; diff --git a/sql/sql_select.h b/sql/sql_select.h index 676cc7452f4..0a416966995 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -666,6 +666,7 @@ protected: KEYUSE *join_tab_keyuse[MAX_TABLES]; /* Copies of JOIN_TAB::checked_keys for each JOIN_TAB. */ key_map join_tab_checked_keys[MAX_TABLES]; + SJ_MATERIALIZATION_INFO *sj_mat_info[MAX_TABLES]; public: Join_plan_state() { -- cgit v1.2.1 From b45beca3680bf545728382af90111fa76cd5abdd Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Sat, 1 Oct 2011 00:55:57 +0400 Subject: BUG#861147: Assertion `fixed == 1' failed in Item_func_eq::val_int() with semijoin + materialization - convert_subq_to_jtbm() didn't check that subuqery optimization was successful. If it wasn't (in this example because of @@max_join_size violation), it would proceed further and eventually crash when trying to execute the un-optimized subquery. --- sql/opt_subselect.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 1c317cea808..7c11f9c4aea 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -1425,7 +1425,8 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, DBUG_ENTER("convert_subq_to_jtbm"); subq_pred->in_strategy &= ~SUBS_IN_TO_EXISTS; - subq_pred->optimize(&rows, &read_time); + if (subq_pred->optimize(&rows, &read_time)) + DBUG_RETURN(TRUE); subq_pred->jtbm_read_time= read_time; subq_pred->jtbm_record_count=rows; -- cgit v1.2.1 From 715dc5f99ddf852a5ef45bcb68c6c86298f6a7c3 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 30 Sep 2011 18:55:02 -0700 Subject: Fixed a cost estimation bug introduced into in the function best_access_path of the 5.3 code line after a merge with 5.2 on 2010-10-28 in order not to allow the cost to access a joined table to be equal to 0 ever. Expanded data sets for many test cases to get the same execution plans as before. --- sql/sql_select.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0c4b1396245..41c8a3b0121 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -5177,7 +5177,7 @@ best_access_path(JOIN *join, tmp= table->file->keyread_time(key, 1, (ha_rows) tmp); else tmp= table->file->read_time(key, 1, - (ha_rows) min(tmp,s->worst_seeks)-1); + (ha_rows) min(tmp,s->worst_seeks)); tmp*= record_count; } } @@ -5341,13 +5341,14 @@ best_access_path(JOIN *join, tmp= table->file->keyread_time(key, 1, (ha_rows) tmp); else tmp= table->file->read_time(key, 1, - (ha_rows) min(tmp,s->worst_seeks)-1); + (ha_rows) min(tmp,s->worst_seeks)); tmp*= record_count; } else tmp= best_time; // Do nothing } + DBUG_ASSERT(tmp > 0 || record_count == 0); tmp += s->startup_cost; loose_scan_opt.check_ref_access_part2(key, start_key, records, tmp); } /* not ft_key */ -- cgit v1.2.1 From 3f82e2edb81448452f647a846c1445efb918493f Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 30 Sep 2011 21:53:59 -0700 Subject: The previous correction of the cost estimate to access a joined table in the function best_access_path revealed another bug: currently table scans on NULL keys used for NOT IN subqueries cannot work together with employment of join caches for inner tables of these subqueries. Otherwise the result can be wrong as it could be seen with the result of the test case constructed for bug #37894 in the file subselect3_jcl6.result. --- sql/sql_select.cc | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 41c8a3b0121..11939793158 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -9081,6 +9081,11 @@ uint check_join_cache_usage(JOIN_TAB *tab, case JT_EQ_REF: if (cache_level <=2 || (no_hashed_cache && no_bka_cache)) goto no_join_cache; + for (uint i= 0; i < tab->ref.key_parts; i++) + { + if (tab->ref.cond_guards[i]) + goto no_join_cache; + } if (!tab->is_ref_for_hash_join()) { flags= HA_MRR_NO_NULL_ENDPOINTS | HA_MRR_SINGLE_POINT; -- cgit v1.2.1 From ada0850c0473e21b3909922634d50a2989708827 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 3 Oct 2011 22:48:15 +0300 Subject: Fix bug lp:858038 Analysis: The cause of the bug was the changed meaning of subselect_partial_match_engine::has_covering_null_row. Previously it meant that there is row with NULLs in all nullable fields of the materialized subquery table. Later it was changed to mean a row with NULLs in all fields of this table. At the same time there was a shortcut in subselect_rowid_merge_engine::partial_match() that detected a special case where: - there is no match in any of the columns with NULLs, and - there is no NULL-only row that covers all columns with NULLs. With the change in the meaning of has_covering_null_row, the condition that detected this special case was incomplete. This resulted in an incorrect FALSE, when the result was a partial match. Solution: Expand the condition that detected the special case with the correct test for the existence of a row with NULL values in all columns that contain NULLs (a kind of parially covering NULL-row). --- sql/item_subselect.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index a79d035160c..324cdabf7f6 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -5533,6 +5533,8 @@ bool subselect_rowid_merge_engine::partial_match() Ordered_key *cur_key; rownum_t cur_row_num; uint count_nulls_in_search_key= 0; + uint max_covering_null_row_len= + ((select_materialize_with_stats *) result)->get_max_nulls_in_row(); bool res= FALSE; /* If there is a non-NULL key, it must be the first key in the keys array. */ @@ -5598,8 +5600,16 @@ bool subselect_rowid_merge_engine::partial_match() If there is no NULL (sub)row that covers all NULL columns, and there is no single match for any of the NULL columns, the result is FALSE. */ - if (pq.elements - test(non_null_key) == 0) + if ((pq.elements == 1 && non_null_key && + max_covering_null_row_len < merge_keys_count - 1) || + pq.elements == 0) { + if (pq.elements == 0) + { + DBUG_ASSERT(!non_null_key); /* Must follow from the logic of this method */ + /* This case must be handled by subselect_partial_match_engine::exec() */ + DBUG_ASSERT(max_covering_null_row_len != tmp_table->s->fields); + } res= FALSE; goto end; } -- cgit v1.2.1 From 1bb7a314cd6fbe31a5676ae107ed84f368a00d48 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 4 Oct 2011 02:20:06 +0400 Subject: Fix buildbot failure in previous fix (BUG#861147): - convert_subq_to_jtbm() should always restore the used arena. --- sql/opt_subselect.cc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 7c11f9c4aea..f983e78fdd0 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -858,14 +858,14 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) in_subq->unit->first_select()->join->table_count >= MAX_TABLES) break; if (convert_subq_to_sj(join, in_subq)) - DBUG_RETURN(TRUE); + goto restore_arena_and_fail; } else { if (join->table_count + 1 >= MAX_TABLES) break; if (convert_subq_to_jtbm(join, in_subq, &remove_item)) - DBUG_RETURN(TRUE); + goto restore_arena_and_fail; } if (remove_item) { @@ -874,7 +874,7 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) Item *replace_me= in_subq->original_item(); if (replace_where_subcondition(join, tree, replace_me, new Item_int(1), FALSE)) - DBUG_RETURN(TRUE); /* purecov: inspected */ + goto restore_arena_and_fail; } } //skip_conversion: @@ -951,6 +951,11 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) thd->restore_active_arena(arena, &backup); join->select_lex->sj_subselects.empty(); DBUG_RETURN(FALSE); + +restore_arena_and_fail: + if (arena) + thd->restore_active_arena(arena, &backup); + DBUG_RETURN(TRUE); } -- cgit v1.2.1 From a55f0cabcb332bd6b52350a0f3a2a24eb3822a73 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Mon, 3 Oct 2011 21:36:18 -0700 Subject: Fixed a bad merge. Changed a test case to make its result set platform independent. --- sql/sql_select.cc | 6 ------ 1 file changed, 6 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a16d1bb390b..0252a00dbe3 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -17969,12 +17969,6 @@ skipped_filesort: delete save_quick; save_quick= NULL; } - /* - orig_cond is a part of pre_idx_push_cond, - no need to restore it. - */ - orig_cond= 0; - orig_cond_saved= false; if (orig_cond_saved && !changed_key) tab->set_cond(orig_cond); DBUG_RETURN(1); -- cgit v1.2.1 From 1b7e566683d13906530a2218791bb5f48b6425ec Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 4 Oct 2011 16:51:39 +0200 Subject: tests for feedback plugin, bugfix: garbage in PLUGIN_VAR_STR variables when INSTALL'ing a plugin mysql-test/include/default_mysqld.cnf: disable feedback plugin by default. when enabled - tag is as a test run --- sql/sql_plugin.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'sql') diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index dbbcd7e8c86..8fc02f4b196 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -3176,6 +3176,19 @@ static int construct_options(MEM_ROOT *mem_root, struct st_plugin_int *tmp, opt->name, plugin_name); } } + /* + PLUGIN_VAR_STR command-line options without PLUGIN_VAR_MEMALLOC, point + directly to values in the argv[] array. For plugins started at the + server startup, argv[] array is allocated with load_defaults(), and + freed when the server is shut down. But for plugins loaded with + INSTALL PLUGIN, the memory allocated with load_defaults() is freed with + freed() at the end of mysql_install_plugin(). Which means we cannot + allow any pointers into that area. + Thus, for all plugins loaded after the server was started, + we force all command-line options to be PLUGIN_VAR_MEMALLOC + */ + if (mysqld_server_started && !(opt->flags & PLUGIN_VAR_NOCMDOPT)) + opt->flags|= PLUGIN_VAR_MEMALLOC; break; case PLUGIN_VAR_ENUM: if (!opt->check) -- cgit v1.2.1 From f40f0ff679471948a6366ddfad9377b83abdf345 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 4 Oct 2011 23:57:46 +0300 Subject: Fix bug lp:856152 Analysis: The cause of the bug was that the method subselect_rowid_merge_engine::partial_match() was not designed for re-execution within the same query. Specifically, it didn't cleanup the bitmap of matching keys after completion. The test query requires double execution of the IN predicate because it first checks the predicate as a constant condition. The second execution during regular execution used the bitmap of matching keys produced by the first execution instead of starting with a clean one. Solution: Cleanup the bitmap of matching keys at the end of the partial matching procedure. --- sql/item_subselect.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sql') diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 324cdabf7f6..a20e4c4e708 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -5674,6 +5674,8 @@ bool subselect_rowid_merge_engine::partial_match() DBUG_ASSERT(FALSE); end: + if (!has_covering_null_columns) + bitmap_clear_all(&matching_keys); queue_remove_all(&pq); tmp_table->file->ha_rnd_end(); return res; -- cgit v1.2.1 From 1351bef4df851f7a867c0732d46a322165168b63 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Wed, 5 Oct 2011 16:37:05 +0300 Subject: Fix for issue found in buildbot where mysqld.*.err files was missing Added suppression message for valgrind failure found on OpenSuSE 11.1 mysql-test/mysql-test-run.pl: Fix for issue found in buildbot where mysqld.*.err files was missing Patch by Kristian Nielsen mysql-test/valgrind.supp: Added suppression message for valgrind failure found on OpenSuSE 11.1 sql/mysqld.cc: Added missing space to comment --- sql/mysqld.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 41ca0773782..d92eb15e6a4 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -7523,7 +7523,7 @@ thread is in the relay logs.", "Define threads usage for handling queries: " "one-thread-per-connection" #if HAVE_POOL_OF_THREADS == 1 - ", pool-of-threads" + ", pool-of-threads " #endif "or no-threads.", &opt_thread_handling, &opt_thread_handling, -- cgit v1.2.1 From 54caeee5d6bc06be4cc451aef49ee0a4f7655ce2 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 5 Oct 2011 18:18:00 +0300 Subject: Making subquery cache on by default. --- sql/mysql_priv.h | 3 ++- sql/mysqld.cc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index bccd5b189ff..903f8bd82ff 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -608,7 +608,8 @@ enabled by default, add OPTIMIZER_SWITCH_MATERIALIZATION OPTIMIZER_SWITCH_PARTIAL_MATCH_TABLE_SCAN|\ OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL | \ OPTIMIZER_SWITCH_JOIN_CACHE_HASHED | \ - OPTIMIZER_SWITCH_JOIN_CACHE_BKA) + OPTIMIZER_SWITCH_JOIN_CACHE_BKA | \ + OPTIMIZER_SWITCH_SUBQUERY_CACHE) /* Replication uses 8 bytes to store SQL_MODE in the binary log. The day you diff --git a/sql/mysqld.cc b/sql/mysqld.cc index be66aa957e9..41ec3cde556 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -491,7 +491,7 @@ static const char *optimizer_switch_str="index_merge=on,index_merge_union=on," "semijoin=off," "partial_match_rowid_merge=on," "partial_match_table_scan=on," - "subquery_cache=off," + "subquery_cache=on," "mrr=off," "mrr_cost_based=off," "mrr_sort_keys=off," -- cgit v1.2.1 From 9c32088322f69155d91f6033319ebb03d108f044 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Thu, 6 Oct 2011 16:56:59 +0300 Subject: Fixed that when using a trigger mysql.proc is now accessed Cleanup: Changed procedure type from a int/char to an enum for easier to manage and debug code. mysql-test/r/trigger.result: Test that mysql.proc is not used as part of creating or using a trigger. mysql-test/t/trigger.test: Test that mysql.proc is not used as part of creating or using a trigger. sql/sp.cc: The main bug fix is to not look up triggers in mysql.proc; This is done by ignoreing type == TYPE_ENUM_TRIGGER in sp_add_used_routine() Cleanup: Changed procedure type from a int/char to an enum. sql/sp.h: Cleanup: Changed procedure type from a int/char to an enum. sql/sp_head.h: Cleanup: Changed procedure type from a int/char to an enum. sql/sql_db.cc: Fix include order sql/sql_lex.cc: Fix include order sql/sql_parse.cc: Cleanup: Changed procedure type from a int/char to an enum. sql/sql_show.cc: Fix include order sql/sql_view.cc: Fix include order --- sql/sp.cc | 45 +++++++++++++++++++++++++++------------------ sql/sp.h | 16 +++++++++------- sql/sp_head.h | 22 +++++++++++++--------- sql/sql_db.cc | 1 + sql/sql_lex.cc | 2 +- sql/sql_parse.cc | 18 ++++++++++-------- sql/sql_show.cc | 2 +- sql/sql_view.cc | 2 +- 8 files changed, 63 insertions(+), 45 deletions(-) (limited to 'sql') diff --git a/sql/sp.cc b/sql/sp.cc index d2c732c2100..57dbc04be4e 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -14,8 +14,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mysql_priv.h" -#include "sp.h" #include "sp_head.h" +#include "sp.h" #include "sp_cache.h" #include "sql_trigger.h" @@ -23,7 +23,7 @@ static bool create_string(THD *thd, String *buf, - int sp_type, + stored_procedure_type sp_type, const char *db, ulong dblen, const char *name, ulong namelen, const char *params, ulong paramslen, @@ -33,7 +33,8 @@ create_string(THD *thd, String *buf, const LEX_STRING *definer_user, const LEX_STRING *definer_host); static int -db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, +db_load_routine(THD *thd, stored_procedure_type type, sp_name *name, + sp_head **sphp, ulong sql_mode, const char *params, const char *returns, const char *body, st_sp_chistics &chistics, const char *definer, longlong created, longlong modified, @@ -490,7 +491,8 @@ static TABLE *open_proc_table_for_update(THD *thd) */ static int -db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table) +db_find_routine_aux(THD *thd, stored_procedure_type type, sp_name *name, + TABLE *table) { uchar key[MAX_KEY_LENGTH]; // db, name, optional key length type DBUG_ENTER("db_find_routine_aux"); @@ -543,7 +545,8 @@ db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table) */ static int -db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) +db_find_routine(THD *thd, stored_procedure_type type, sp_name *name, + sp_head **sphp) { TABLE *table; const char *params, *returns, *body; @@ -711,7 +714,8 @@ Silence_deprecated_warning::handle_error(uint sql_errno, const char *message, static int -db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, +db_load_routine(THD *thd, stored_procedure_type type, + sp_name *name, sp_head **sphp, ulong sql_mode, const char *params, const char *returns, const char *body, st_sp_chistics &chistics, const char *definer, longlong created, longlong modified, @@ -890,7 +894,7 @@ sp_returns_type(THD *thd, String &result, sp_head *sp) */ int -sp_create_routine(THD *thd, int type, sp_head *sp) +sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) { int ret; TABLE *table; @@ -906,7 +910,8 @@ sp_create_routine(THD *thd, int type, sp_head *sp) bool save_binlog_row_based; DBUG_ENTER("sp_create_routine"); - DBUG_PRINT("enter", ("type: %d name: %.*s",type, (int) sp->m_name.length, + DBUG_PRINT("enter", ("type: %d name: %.*s", (int) type, + (int) sp->m_name.length, sp->m_name.str)); String retstr(64); retstr.set_charset(system_charset_info); @@ -1151,7 +1156,7 @@ done: */ int -sp_drop_routine(THD *thd, int type, sp_name *name) +sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name) { TABLE *table; int ret; @@ -1211,14 +1216,16 @@ sp_drop_routine(THD *thd, int type, sp_name *name) */ int -sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics) +sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name, + st_sp_chistics *chistics) { TABLE *table; int ret; bool save_binlog_row_based; DBUG_ENTER("sp_update_routine"); DBUG_PRINT("enter", ("type: %d name: %.*s", - type, (int) name->m_name.length, name->m_name.str)); + (int) type, + (int) name->m_name.length, name->m_name.str)); DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || type == TYPE_ENUM_FUNCTION); @@ -1346,7 +1353,7 @@ err: */ bool -sp_show_create_routine(THD *thd, int type, sp_name *name) +sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name) { bool err_status= TRUE; sp_head *sp; @@ -1404,8 +1411,8 @@ sp_show_create_routine(THD *thd, int type, sp_name *name) */ sp_head * -sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp, - bool cache_only) +sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name, + sp_cache **cp, bool cache_only) { sp_head *sp; ulong depth= (type == TYPE_ENUM_PROCEDURE ? @@ -1562,7 +1569,7 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool any) */ int -sp_routine_exists_in_table(THD *thd, int type, sp_name *name) +sp_routine_exists_in_table(THD *thd, stored_procedure_type type, sp_name *name) { TABLE *table; int ret; @@ -1729,7 +1736,7 @@ static bool add_used_routine(LEX *lex, Query_arena *arena, */ void sp_add_used_routine(LEX *lex, Query_arena *arena, - sp_name *rt, char rt_type) + sp_name *rt, enum stored_procedure_type rt_type) { rt->set_routine_type(rt_type); (void)add_used_routine(lex, arena, &rt->m_sroutines_key, 0); @@ -1885,9 +1892,11 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, for (Sroutine_hash_entry *rt= start; rt; rt= rt->next) { sp_name name(thd, rt->key.str, rt->key.length); - int type= rt->key.str[0]; + stored_procedure_type type= (stored_procedure_type) rt->key.str[0]; sp_head *sp; + if (type == TYPE_ENUM_TRIGGER) + continue; if (!(sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ? &thd->sp_func_cache : &thd->sp_proc_cache), &name))) @@ -2076,7 +2085,7 @@ sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex, */ static bool create_string(THD *thd, String *buf, - int type, + stored_procedure_type type, const char *db, ulong dblen, const char *name, ulong namelen, const char *params, ulong paramslen, diff --git a/sql/sp.h b/sql/sp.h index 876287d9704..4e3252392f7 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -39,26 +39,28 @@ int sp_drop_db_routines(THD *thd, char *db); sp_head * -sp_find_routine(THD *thd, int type, sp_name *name, +sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name, sp_cache **cp, bool cache_only); bool sp_exist_routines(THD *thd, TABLE_LIST *procs, bool any); int -sp_routine_exists_in_table(THD *thd, int type, sp_name *name); +sp_routine_exists_in_table(THD *thd, stored_procedure_type type, + sp_name *name); bool -sp_show_create_routine(THD *thd, int type, sp_name *name); +sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name); int -sp_create_routine(THD *thd, int type, sp_head *sp); +sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp); int -sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics); +sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name, + st_sp_chistics *chistics); int -sp_drop_routine(THD *thd, int type, sp_name *name); +sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name); /* Procedures for pre-caching of stored routines and building table list @@ -67,7 +69,7 @@ sp_drop_routine(THD *thd, int type, sp_name *name); void sp_get_prelocking_info(THD *thd, bool *need_prelocking, bool *first_no_prelocking); void sp_add_used_routine(LEX *lex, Query_arena *arena, - sp_name *rt, char rt_type); + sp_name *rt, stored_procedure_type rt_type); void sp_remove_not_own_routines(LEX *lex); bool sp_update_sp_used_routines(HASH *dst, HASH *src); int sp_cache_routines_and_add_tables(THD *thd, LEX *lex, diff --git a/sql/sp_head.h b/sql/sp_head.h index d422adc8927..dc237163716 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -28,11 +28,16 @@ @ingroup Runtime_Environment @{ */ -// Values for the type enum. This reflects the order of the enum declaration -// in the CREATE TABLE command. -#define TYPE_ENUM_FUNCTION 1 -#define TYPE_ENUM_PROCEDURE 2 -#define TYPE_ENUM_TRIGGER 3 +/* + Values for the type enum. This reflects the order of the enum declaration + in the CREATE TABLE command. +*/ +enum stored_procedure_type +{ + TYPE_ENUM_FUNCTION=1, + TYPE_ENUM_PROCEDURE=2, + TYPE_ENUM_TRIGGER=3 +}; Item_result sp_map_result_type(enum enum_field_types type); @@ -134,9 +139,9 @@ public: // Init. the qualified name from the db and name. void init_qname(THD *thd); // thd for memroot allocation - void set_routine_type(char type) + void set_routine_type(stored_procedure_type type) { - m_sroutines_key.str[0]= type; + m_sroutines_key.str[0]= (char) type; } ~sp_name() @@ -170,8 +175,7 @@ public: HAS_SQLCOM_FLUSH= 4096 }; - /** TYPE_ENUM_FUNCTION, TYPE_ENUM_PROCEDURE or TYPE_ENUM_TRIGGER */ - int m_type; + stored_procedure_type m_type; uint m_flags; // Boolean attributes of a stored routine Create_field m_return_field_def; /**< This is used for FUNCTIONs only. */ diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 655268da637..a77ef956925 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -18,6 +18,7 @@ #include "mysql_priv.h" #include +#include "sp_head.h" #include "sp.h" #include "events.h" #include diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index aea9796ba56..f4eb8073d8c 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -21,8 +21,8 @@ #include "item_create.h" #include #include -#include "sp.h" #include "sp_head.h" +#include "sp.h" /* We are using pointer to this variable for distinguishing between assignment diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 2050350d450..1ce0308d251 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4560,9 +4560,10 @@ create_sp_error: */ /* Conditionally writes to binlog */ - int type= lex->sql_command == SQLCOM_ALTER_PROCEDURE ? - TYPE_ENUM_PROCEDURE : - TYPE_ENUM_FUNCTION; + stored_procedure_type type; + type= (lex->sql_command == SQLCOM_ALTER_PROCEDURE ? + TYPE_ENUM_PROCEDURE : + TYPE_ENUM_FUNCTION); sp_result= sp_update_routine(thd, type, @@ -4590,8 +4591,8 @@ create_sp_error: case SQLCOM_DROP_FUNCTION: { int sp_result; - int type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ? - TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); + stored_procedure_type type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ? + TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); sp_result= sp_routine_exists_in_table(thd, type, lex->spname); mysql_reset_errors(thd, 0); @@ -4618,9 +4619,10 @@ create_sp_error: #endif /* Conditionally writes to binlog */ - int type= lex->sql_command == SQLCOM_DROP_PROCEDURE ? - TYPE_ENUM_PROCEDURE : - TYPE_ENUM_FUNCTION; + stored_procedure_type type; + type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ? + TYPE_ENUM_PROCEDURE : + TYPE_ENUM_FUNCTION); sp_result= sp_drop_routine(thd, type, lex->spname); } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 0c422632f35..fb0a6f23261 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -22,8 +22,8 @@ #include "create_options.h" #include "sql_show.h" #include "repl_failsafe.h" -#include "sp.h" #include "sp_head.h" +#include "sp.h" #include "sql_trigger.h" #include "authors.h" #include "contributors.h" diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 0f7b7637744..9c8e8f8c27e 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -18,8 +18,8 @@ #include "mysql_priv.h" #include "sql_select.h" #include "parse_file.h" -#include "sp.h" #include "sp_head.h" +#include "sp.h" #include "sp_cache.h" #define MD5_BUFF_LENGTH 33 -- cgit v1.2.1 From e513652a4ec71daa33cfc96b0c167312711b3b0f Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 6 Oct 2011 23:39:44 +0200 Subject: disable feedback plugin by default. Now on windows too. --- sql/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 5856b795ba8..103074d1747 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -35,7 +35,9 @@ SET_SOURCE_FILES_PROPERTIES(${CMAKE_SOURCE_DIR}/sql/sql_yacc.h PROPERTIES GENERATED 1) ADD_DEFINITIONS(-DMYSQL_SERVER -D_CONSOLE -DHAVE_DLOPEN -DHAVE_EVENT_SCHEDULER) - +IF(WITH_FEEDBACK_STORAGE_ENGINE) + ADD_DEFINITIONS(-DWITH_FEEDBACK_PLUGIN) +ENDIF() SET (SQL_SOURCE ../sql-common/client.c derror.cc des_key_file.cc -- cgit v1.2.1 From 8feff690b6d1f09acb6a68315669e75924da5f80 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Tue, 11 Oct 2011 02:36:08 -0700 Subject: Fixed LP bug #870046. This bug is a consequence of the fix in the function add_ref_to_table_cond for LP bug 826935 that turned out to be not quite correct: it tried to AND the same generated condition with two different other conditions. This patch creates a copy of the generated condition if the condition needs to be ANDed with two different items. --- sql/sql_select.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0252a00dbe3..152030d0581 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -19895,6 +19895,7 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) DBUG_RETURN(FALSE); Item_cond_and *cond=new Item_cond_and(); + Item *cond_copy; TABLE *table=join_tab->table; int error= 0; if (!cond) @@ -19909,13 +19910,14 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) } if (thd->is_fatal_error) DBUG_RETURN(TRUE); - if (!cond->fixed) { Item *tmp_item= (Item*) cond; cond->fix_fields(thd, &tmp_item); DBUG_ASSERT(cond == tmp_item); } + if (join_tab->select->pre_idx_push_select_cond) + cond_copy= cond->copy_andor_structure(thd); if (join_tab->select) { if (join_tab->select->cond) @@ -19923,7 +19925,7 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) join_tab->select->cond= cond; if (join_tab->select->pre_idx_push_select_cond) { - Item *new_cond= and_conds(join_tab->select->pre_idx_push_select_cond, cond); + Item *new_cond= and_conds(cond_copy, join_tab->select->pre_idx_push_select_cond); if (!new_cond->fixed && new_cond->fix_fields(thd, &new_cond)) error= 1; join_tab->select->pre_idx_push_select_cond= new_cond; -- cgit v1.2.1 From 039da95e3d74573a684008c8ea030d285ec98390 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 11 Oct 2011 21:34:00 +0400 Subject: BUG#869012: Wrong result with semijoin + materialization + AND in WHERE - in make_join_select(), use the correct condition to check whether the current table is a SJM nest (the previous condition used to be correct before, but then sj-materialization temp table creation was moved to happen before make_join_select was called) --- sql/sql_select.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0252a00dbe3..8619cb1f53a 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7969,7 +7969,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) */ JOIN_TAB *first_inner_tab= tab->first_inner; - if (tab->table) + if (!tab->bush_children) current_map= tab->table->map; else current_map= tab->bush_children->start->emb_sj_nest->sj_inner_tables; -- cgit v1.2.1 From 2160a25adc7e0ed8ef59dd4426a884610a32802f Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 12 Oct 2011 13:19:37 +0400 Subject: BUG#869001: Wrong result with semijoin + materialization + firstmatch + multipart key - Make advance_sj_state() not to attempt building duplicate removal strategies when we're doing optimization of an SJM-nest. --- sql/opt_subselect.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'sql') diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index f983e78fdd0..08afa4b12fb 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -2179,6 +2179,17 @@ void advance_sj_state(JOIN *join, table_map remaining_tables, pos->sj_strategy= SJ_OPT_NONE; pos->prefix_dups_producing_tables= join->cur_dups_producing_tables; + + /* We're performing optimization inside SJ-Materialization nest */ + if (join->emb_sjm_nest) + { + pos->invalidate_firstmatch_prefix(); + pos->first_loosescan_table= MAX_TABLES; + pos->dupsweedout_tables= 0; + pos->sjm_scan_need_tables= 0; + return; + } + /* Initialize the state or copy it from prev. tables */ if (idx == join->const_tables) { -- cgit v1.2.1 From 42eeb557a6baceef59a25e774e31bb12fb9d7af9 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 12 Oct 2011 14:23:42 +0400 Subject: =?UTF-8?q?Fix=20compile=20error:=20=E2=80=98cond=5Fcopy=E2=80=99?= =?UTF-8?q?=20may=20be=20used=20uninitialized=20in=20this=20function.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sql/sql_select.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index d056c809360..4b4daae3d2d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -19916,10 +19916,11 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) cond->fix_fields(thd, &tmp_item); DBUG_ASSERT(cond == tmp_item); } - if (join_tab->select->pre_idx_push_select_cond) - cond_copy= cond->copy_andor_structure(thd); if (join_tab->select) { + UNINIT_VAR(cond_copy); // used when pre_idx_push_select_cond!=NULL + if (join_tab->select->pre_idx_push_select_cond) + cond_copy= cond->copy_andor_structure(thd); if (join_tab->select->cond) error=(int) cond->add(join_tab->select->cond); join_tab->select->cond= cond; -- cgit v1.2.1 From ae79dbdb958ba49cff46a7b104bf1011b23c3788 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 12 Oct 2011 21:38:40 +0400 Subject: Code cleanup: move variable into branch that uses it --- sql/sql_select.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 4b4daae3d2d..36d2eac6dbc 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -19895,7 +19895,6 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) DBUG_RETURN(FALSE); Item_cond_and *cond=new Item_cond_and(); - Item *cond_copy; TABLE *table=join_tab->table; int error= 0; if (!cond) @@ -19918,6 +19917,7 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) } if (join_tab->select) { + Item *cond_copy; UNINIT_VAR(cond_copy); // used when pre_idx_push_select_cond!=NULL if (join_tab->select->pre_idx_push_select_cond) cond_copy= cond->copy_andor_structure(thd); -- cgit v1.2.1 From 94bf62b65b86803bea8128af2a0b03c149736f53 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 13 Oct 2011 00:15:51 +0400 Subject: Remove garbage comment --- sql/sql_select.cc | 1 - 1 file changed, 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 36d2eac6dbc..61981f87c0a 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -9614,7 +9614,6 @@ void JOIN_TAB::cleanup() table->pos_in_table_list->jtbm_subselect) { end_read_record(&read_record); - //psergey-merge: table->pos_in_table_list->jtbm_subselect->cleanup(); DBUG_VOID_RETURN; } -- cgit v1.2.1 From 9f6e24a05a32c147f292cd6260d63a31cb8cd9db Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 13 Oct 2011 13:44:50 +0200 Subject: lp:817966 int_column IN (string_constant) restore the status quo from before the microsecond patch --- sql/item_cmpfunc.cc | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'sql') diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 14faed3dcde..887067ff8b1 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -29,8 +29,6 @@ #include #include "sql_select.h" -static bool convert_const_to_int(THD *, Item_field *, Item **); - static Item_result item_store_type(Item_result a, Item *item, my_bool unsigned_flag) { @@ -515,7 +513,6 @@ static bool convert_const_to_int(THD *thd, Item_field *field_item, void Item_bool_func2::fix_length_and_dec() { max_length= 1; // Function returns 0 or 1 - THD *thd; /* As some compare functions are generated after sql_yacc, @@ -547,14 +544,14 @@ void Item_bool_func2::fix_length_and_dec() /* Make a special case of compare with fields to get nicer comparisons - of numbers with constant string. + of bigint numbers with constant string. This directly contradicts the manual (number and a string should be compared as doubles), but seems to provide more "intuitive" behavior in some cases (but less intuitive in others). But disable conversion in case of LIKE function. */ - thd= current_thd; + THD *thd= current_thd; if (functype() != LIKE_FUNC && !thd->lex->is_ps_or_view_context_analysis()) { int field; @@ -562,7 +559,8 @@ void Item_bool_func2::fix_length_and_dec() args[field= 1]->real_item()->type() == FIELD_ITEM) { Item_field *field_item= (Item_field*) (args[field]->real_item()); - if (field_item->cmp_type() == INT_RESULT && + if ((field_item->field_type() == MYSQL_TYPE_LONGLONG || + field_item->field_type() == MYSQL_TYPE_YEAR) && convert_const_to_int(thd, field_item, &args[!field])) args[0]->cmp_context= args[1]->cmp_context= INT_RESULT; } @@ -2188,7 +2186,8 @@ void Item_func_between::fix_length_and_dec() !thd->lex->is_ps_or_view_context_analysis()) { Item_field *field_item= (Item_field*) (args[0]->real_item()); - if (field_item->cmp_type() == INT_RESULT) + if (field_item->field_type() == MYSQL_TYPE_LONGLONG || + field_item->field_type() == MYSQL_TYPE_YEAR) { /* The following can't be recoded with || as convert_const_to_int @@ -3894,7 +3893,8 @@ void Item_func_in::fix_length_and_dec() !thd->lex->is_ps_or_view_context_analysis() && cmp_type != INT_RESULT) { Item_field *field_item= (Item_field*) (args[0]->real_item()); - if (field_item->cmp_type() == INT_RESULT) + if (field_item->field_type() == MYSQL_TYPE_LONGLONG || + field_item->field_type() == MYSQL_TYPE_YEAR) { bool all_converted= TRUE; for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++) -- cgit v1.2.1 From 9e6f484788db461b46a365d415ee7043cab65f1e Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 13 Oct 2011 22:39:00 -0700 Subject: Fixed LP bug #872735. This bug happened because the maps of covering keys for mergeable derived tables/views was not recalculated after the derived tables/vies had been merged into the main query. --- sql/item.h | 5 +++++ sql/sql_lex.cc | 15 +++++++++++++++ 2 files changed, 20 insertions(+) (limited to 'sql') diff --git a/sql/item.h b/sql/item.h index 8cacd231fbf..efd358db874 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1816,6 +1816,11 @@ public: bool get_date_result(MYSQL_TIME *ltime,uint fuzzydate); bool is_null() { return field->is_null(); } void update_null_value(); + void update_used_tables() + { + if (field && field->table) + field->table->covering_keys.intersect(field->part_of_key); + } Item *get_tmp_table_item(THD *thd); bool collect_item_field_processor(uchar * arg); bool add_field_to_set_processor(uchar * arg); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 7f39d4ad7f3..64e295d82ea 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3411,6 +3411,21 @@ void SELECT_LEX::update_used_tables() while ((tl= ti++)) { TABLE_LIST *embedding; + if (tl->table) + { + embedding= tl->embedding; + for (embedding= tl->embedding; embedding; embedding=embedding->embedding) + { + if (embedding->is_view_or_derived()) + { + DBUG_ASSERT(embedding->is_merged_derived()); + TABLE *tab= tl->table; + tab->covering_keys= tab->s->keys_for_keyread; + tab->covering_keys.intersect(tab->keys_in_use_for_query); + break; + } + } + } embedding= tl; do { -- cgit v1.2.1 From aed67d25829791d3e3f8dedc83e17179a1043259 Mon Sep 17 00:00:00 2001 From: Vladislav Vaintroub Date: Fri, 14 Oct 2011 17:51:16 +0200 Subject: In crash handler, output session value of the optimizer switch. --- sql/mysqld.cc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 9d87864d939..8a476383d82 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2761,8 +2761,16 @@ the thread stack. Please read http://dev.mysql.com/doc/mysql/en/linux.html\n\n", my_safe_print_str(thd->query(), min(65536,thd->query_length())); fprintf(stderr, "Connection ID (thread ID): %lu\n", (ulong) thd->thread_id); fprintf(stderr, "Status: %s\n", kreason); - fprintf(stderr, "Optimizer switch: %s\n", optimizer_switch_str); - fputc('\n', stderr); + fprintf(stderr, "Optimizer switch: "); + ulonglong optsw= thd->variables.optimizer_switch; + for (uint i= 0; optimizer_switch_names[i+1]; i++, optsw >>= 1) + { + if (i) + fputc(',', stderr); + fprintf(stderr, "%s=%s", + optimizer_switch_names[i], optsw & 1 ? "on" : "off"); + } + fprintf(stderr, "\n\n"); } fprintf(stderr, "\ The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html contains\n\ -- cgit v1.2.1 From e3e60a4102d6918e4231e402bef89b1a6b2a20cb Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Sun, 16 Oct 2011 21:55:53 +0300 Subject: Fixed wrong info message for mysqld --general-log Fixed wrong parameter type for --general-log. Now one can enable it with --general-log= 1 | true | on Fixed that bool parameters can also take 'on' and 'off' as parameters. This is in line with the values assigned to them in mysqld. mysys/my_getopt.c: Fixed that bool parameters can also take 'on' and 'off' as parameters. sql/mysqld.cc: Fixed wrong info message for mysqld --general-log Fixed wrong parameter type for --general-log. Now one can enable it with --general-log= 1 | true | on --- sql/mysqld.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 4e9116874d4..0377d965b16 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -6335,8 +6335,8 @@ struct my_option my_long_options[] = &opt_debugging, &opt_debugging, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"general_log", OPT_GENERAL_LOG, - "Enable/disable general log. Filename can be specified with --general-log-file or --log-basename. Is 'hostname.err' by default.", - &opt_log, &opt_log, 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0}, + "Enable/disable general log. Filename can be specified with --general-log-file or --log-basename. Is 'hostname.log' by default.", + &opt_log, &opt_log, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #ifdef HAVE_LARGE_PAGES {"large-pages", OPT_ENABLE_LARGE_PAGES, "Enable support for large pages. " "Disable with --skip-large-pages.", &opt_large_pages, &opt_large_pages, -- cgit v1.2.1 From d5959780aaa66b229df406e6343d0ef1b4d36750 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Sun, 16 Oct 2011 22:46:11 +0300 Subject: Remove extra MariaDB- from binary tar.gz file name Print server version name to .err file on crash scripts/make_binary_distribution.sh: Remove extra MariaDB- from binary tar.gz file name sql/mysqld.cc: Print server version name to .err file on crash (to ensure people paste also that when they report a crash) --- sql/mysqld.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sql') diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 8a476383d82..1cd4ac182f5 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2685,6 +2685,8 @@ or misconfigured. This error can also be caused by malfunctioning hardware.\n", We will try our best to scrape up some info that will hopefully help diagnose\n\ the problem, but since we have already crashed, something is definitely wrong\n\ and this may fail.\n\n"); + set_server_version(); + fprintf(stderr, "Server version: %s\n", server_version); fprintf(stderr, "key_buffer_size=%lu\n", (ulong) dflt_key_cache->key_cache_mem_size); fprintf(stderr, "read_buffer_size=%ld\n", (long) global_system_variables.read_buff_size); -- cgit v1.2.1 From f5955f87c6aed0af0ffbbab918e953b8c340d466 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Sun, 16 Oct 2011 13:23:57 -0700 Subject: Fixed LP bug #874006. This bug manifested itself with queries containing non-correlated IN subqueries over materialized views/derived tables. The bug happened because the code of the function generate_derived_keys did not take into account that the function could be called twice when the optimizer was deciding whether in-exist transformation should be applied. --- sql/sql_select.cc | 36 +++++++++++++++++++++++++----------- sql/table.cc | 13 ++++++++----- 2 files changed, 33 insertions(+), 16 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 61981f87c0a..24778ef6a98 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8413,17 +8413,17 @@ bool generate_derived_keys_for_table(KEYUSE *keyuse, uint count, uint keys) TABLE *table= keyuse->table; if (table->alloc_keys(keys)) return TRUE; - uint keyno= 0; + uint key_count= 0; KEYUSE *first_keyuse= keyuse; uint prev_part= keyuse->keypart; uint parts= 0; uint i= 0; - for ( ; i < count && keyno < keys; ) + for ( ; i < count && key_count < keys; ) { do { - keyuse->key= keyno; + keyuse->key= table->s->keys; keyuse->keypart_map= (key_part_map) (1 << parts); keyuse++; i++; @@ -8437,14 +8437,14 @@ bool generate_derived_keys_for_table(KEYUSE *keyuse, uint count, uint keys) } else { - if (table->add_tmp_key(keyno, parts, + if (table->add_tmp_key(table->s->keys, parts, get_next_field_for_derived_key, (uchar *) &first_keyuse, FALSE)) return TRUE; - table->reginfo.join_tab->keys.set_bit(keyno); + table->reginfo.join_tab->keys.set_bit(table->s->keys); first_keyuse= keyuse; - keyno++; + key_count++; parts= 0; prev_part= keyuse->keypart; } @@ -8471,12 +8471,23 @@ bool generate_derived_keys(DYNAMIC_ARRAY *keyuse_array) TABLE_LIST *derived= NULL; if (keyuse->table != prev_table) derived= keyuse->table->pos_in_table_list; - while (derived && derived->is_materialized_derived() && - keyuse->key == MAX_KEY) + while (derived && derived->is_materialized_derived()) { if (keyuse->table != prev_table) { prev_table= keyuse->table; + while (keyuse->table == prev_table && keyuse->key != MAX_KEY) + { + keyuse++; + i++; + } + if (keyuse->table != prev_table) + { + keyuse--; + i--; + derived= NULL; + continue; + } first_table_keyuse= keyuse; last_used_tables= keyuse->used_tables; count= 0; @@ -8489,11 +8500,13 @@ bool generate_derived_keys(DYNAMIC_ARRAY *keyuse_array) } count++; keyuse++; + i++; if (keyuse->table != prev_table) { if (generate_derived_keys_for_table(first_table_keyuse, count, ++keys)) return TRUE; keyuse--; + i--; derived= NULL; } } @@ -8524,12 +8537,13 @@ void JOIN::drop_unused_derived_keys() TABLE *table=tab->table; if (!table) continue; - if (!table->pos_in_table_list->is_materialized_derived() || - table->max_keys <= 1) + if (!table->pos_in_table_list->is_materialized_derived()) continue; - table->use_index(tab->ref.key); + if (table->max_keys > 1) + table->use_index(tab->ref.key); if (table->s->keys) tab->ref.key= 0; + tab->keys= (key_map) (table->s->keys ? 1 : 0); } } diff --git a/sql/table.cc b/sql/table.cc index d54135b5620..382de16b81c 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -5220,10 +5220,11 @@ void st_table::mark_virtual_columns_for_write(bool insert_fl) @brief Allocate space for keys - @param key_count number of keys to allocate + @param key_count number of keys to allocate additionally @details - The function allocates memory to fit 'key_count' keys for this table. + The function allocates memory to fit additionally 'key_count' keys + for this table. @return FALSE space was successfully allocated @return TRUE an error occur @@ -5231,9 +5232,11 @@ void st_table::mark_virtual_columns_for_write(bool insert_fl) bool TABLE::alloc_keys(uint key_count) { - key_info= s->key_info= (KEY*) alloc_root(&mem_root, sizeof(KEY)*key_count); - s->keys= 0; - max_keys= key_count; + key_info= (KEY*) alloc_root(&mem_root, sizeof(KEY)*(s->keys+key_count)); + if (s->keys) + memmove(key_info, s->key_info, sizeof(KEY)*s->keys); + s->key_info= key_info; + max_keys= s->keys+key_count; return !(key_info); } -- cgit v1.2.1 From cc80671202181da84689bfe4ff759bf661fd81da Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Mon, 17 Oct 2011 03:42:56 -0700 Subject: Fixed a compiler warning. --- sql/table.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/table.cc b/sql/table.cc index 382de16b81c..72853a30580 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -5381,7 +5381,7 @@ void TABLE::use_index(int key_to_save) /* Drop all keys; */ i= 0; - s->keys= (key_to_save < 0) ? 0 : 1; + s->keys= i; } /** -- cgit v1.2.1 From 255c04aed8b4159ac8402bc80bab3771c0236d40 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 18 Oct 2011 13:44:12 +0300 Subject: Compiler warning about assigned but not used variables fixed. --- sql/item_cmpfunc.cc | 3 --- sql/item_timefunc.cc | 5 ----- sql/multi_range_read.cc | 5 +++-- sql/opt_subselect.cc | 4 +--- sql/sql_show.cc | 4 +++- sql/table.cc | 4 ++-- 6 files changed, 9 insertions(+), 16 deletions(-) (limited to 'sql') diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 887067ff8b1..e1574bdf5de 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -5810,7 +5810,6 @@ Item* Item_equal::get_first(Item *field_item) { Item_equal_fields_iterator it(*this); Item *item; - JOIN_TAB *field_tab; if (!field_item) return (it++); Field *field= ((Item_field *) (field_item->real_item()))->field; @@ -5835,8 +5834,6 @@ Item* Item_equal::get_first(Item *field_item) in presense of SJM nests. */ - field_tab= field->table->reginfo.join_tab; - TABLE_LIST *emb_nest= field->table->pos_in_table_list->embedding; if (emb_nest && emb_nest->sj_mat_info && emb_nest->sj_mat_info->is_used) diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 0f12f6755ed..3a1e3a93f72 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -2436,7 +2436,6 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, uint fuzzy_date) long days, microseconds; longlong seconds; int l_sign= sign, was_cut= 0; - uint dec= decimals; if (is_date) // TIMESTAMP function { @@ -2478,10 +2477,6 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, uint fuzzy_date) ltime->time_type= is_time ? MYSQL_TIMESTAMP_TIME : MYSQL_TIMESTAMP_DATETIME; - if (cached_field_type == MYSQL_TYPE_STRING && - (l_time1.second_part || l_time2.second_part)) - dec= TIME_SECOND_PART_DIGITS; - if (!is_time) { get_date_from_daynr(days,<ime->year,<ime->month,<ime->day); diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc index 08e91559fc6..6e11fe5efa0 100644 --- a/sql/multi_range_read.cc +++ b/sql/multi_range_read.cc @@ -1636,7 +1636,8 @@ bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags, uint *buffer_size, COST_VECT *cost) { ulong max_buff_entries, elem_size; - ha_rows rows_in_full_step, rows_in_last_step; + ha_rows rows_in_full_step; + ha_rows rows_in_last_step; uint n_full_steps; double index_read_cost; @@ -1661,7 +1662,7 @@ bool DsMrr_impl::get_disk_sweep_mrr_cost(uint keynr, ha_rows rows, uint flags, /* Adjust buffer size if we expect to use only part of the buffer */ if (n_full_steps) { - get_sort_and_sweep_cost(table, rows, cost); + get_sort_and_sweep_cost(table, rows_in_full_step, cost); cost->multiply(n_full_steps); } else diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 08afa4b12fb..416dcadc67f 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -3385,7 +3385,7 @@ TABLE *create_duplicate_weedout_tmp_table(THD *thd, bool using_unique_constraint=FALSE; bool use_packed_rows= FALSE; Field *field, *key_field; - uint blob_count, null_pack_length, null_count; + uint null_pack_length, null_count; uchar *null_flags; uchar *pos; DBUG_ENTER("create_duplicate_weedout_tmp_table"); @@ -3466,8 +3466,6 @@ TABLE *create_duplicate_weedout_tmp_table(THD *thd, share->keys_for_keyread.init(); share->keys_in_use.init(); - blob_count= 0; - /* Create the field */ { /* diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 08cdf7459fd..41e1734f346 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1274,7 +1274,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, handler *file= table->file; TABLE_SHARE *share= table->s; HA_CREATE_INFO create_info; - bool show_table_options= FALSE; + bool show_table_options __attribute__ ((unused))= FALSE; bool foreign_db_mode= (thd->variables.sql_mode & (MODE_POSTGRESQL | MODE_ORACLE | MODE_MSSQL | @@ -1507,7 +1507,9 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN("\n)")); if (!(thd->variables.sql_mode & MODE_NO_TABLE_OPTIONS) && !foreign_db_mode) { +#ifdef WITH_PARTITION_STORAGE_ENGINE show_table_options= TRUE; +#endif /* Get possible table space definitions and append them to the CREATE TABLE statement diff --git a/sql/table.cc b/sql/table.cc index d54135b5620..2acd11e3fd2 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -5378,7 +5378,7 @@ void TABLE::use_index(int key_to_save) /* Drop all keys; */ i= 0; - s->keys= (key_to_save < 0) ? 0 : 1; + s->keys= i; } /** @@ -5689,7 +5689,7 @@ int update_virtual_fields(THD *thd, TABLE *table, bool for_write) { DBUG_ENTER("update_virtual_fields"); Field **vfield_ptr, *vfield; - int error= 0; + int error __attribute__ ((unused))= 0; if (!table || !table->vfield) DBUG_RETURN(0); -- cgit v1.2.1 From 945f12cf8fc361844f3553599795beb549fc8c1d Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Wed, 19 Oct 2011 23:28:37 -0700 Subject: Fixed LP bug #877316. This bug happened due to incompleteness of the fix for bug 872735: the occurrences of the fields in the conditions of correlated subqueries were not taken into account when recalculating covering keys bit maps. --- sql/item.cc | 6 ++++++ sql/item.h | 2 ++ sql/item_subselect.cc | 1 + 3 files changed, 9 insertions(+) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index 8710b8f9024..4d516aa33f8 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2172,6 +2172,12 @@ bool Item_field::enumerate_field_refs_processor(uchar *arg) return FALSE; } +bool Item_field::covering_keys_processor(uchar *arg) +{ + if (field && field->table) + field->table->covering_keys.intersect(field->part_of_key); + return FALSE; +} const char *Item_ident::full_name() const { diff --git a/sql/item.h b/sql/item.h index efd358db874..cd79acdf3c3 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1018,6 +1018,7 @@ public: virtual bool mark_as_eliminated_processor(uchar *arg) { return 0; } virtual bool eliminate_subselect_processor(uchar *arg) { return 0; } virtual bool set_fake_select_as_master_processor(uchar *arg) { return 0; } + virtual bool covering_keys_processor(uchar *arg) { return 0; } virtual bool view_used_tables_processor(uchar *arg) { return 0; } virtual bool eval_not_null_tables(uchar *opt_arg) { return 0; } virtual bool clear_sum_processor(uchar *opt_arg) { return 0; } @@ -1832,6 +1833,7 @@ public: bool vcol_in_partition_func_processor(uchar *bool_arg); bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool enumerate_field_refs_processor(uchar *arg); + bool covering_keys_processor(uchar *arg); void cleanup(); Item_equal *get_item_equal() { return item_equal; } void set_item_equal(Item_equal *item_eq) { item_equal= item_eq; } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index ebc9d47e3a8..7dc26adf97e 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -492,6 +492,7 @@ void Item_subselect::recalc_used_tables(st_select_lex *new_parent, upper->item->walk(&Item::enumerate_field_refs_processor, FALSE, (uchar*)&fixer); used_tables_cache |= fixer.used_tables; + upper->item->walk(&Item::covering_keys_processor, FALSE, NULL); /* if (after_pullout) upper->item->fix_after_pullout(new_parent, &(upper->item)); -- cgit v1.2.1 From e7a7e2a036ada1f3b9715fc6e889dbc29768feea Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 20 Oct 2011 04:59:20 -0700 Subject: Fixed LP bug #878199. The function JOIN::drop_unused_derived_keys could erroneously set the value of REF::key to 0 for a joined materialized view/derived table in the case when no REF access to the table was used by the query execution plan. This could cause a crash of the server. --- sql/sql_select.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 24778ef6a98..aa77013ed49 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8541,7 +8541,7 @@ void JOIN::drop_unused_derived_keys() continue; if (table->max_keys > 1) table->use_index(tab->ref.key); - if (table->s->keys) + if (table->s->keys && tab->ref.key >= 0) tab->ref.key= 0; tab->keys= (key_map) (table->s->keys ? 1 : 0); } -- cgit v1.2.1 From fdf789a7eadf864ecc0e617f25f795fafda55026 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Sat, 22 Oct 2011 00:14:27 -0700 Subject: Fixed LP bug #874378. This bug happened for the queries over multi-table mergeable views because the bitmap TABLE::read_set of the underlying tables were not updated after the views had been merged into the query. Now this bitmaps are updated properly. Also the bitmap TABLE::merge_keys now is updated in prevention of future bugs. --- sql/item.cc | 5 ++--- sql/item.h | 19 ++++++++++++++----- sql/item_subselect.cc | 2 +- sql/sql_lex.cc | 5 ++++- 4 files changed, 21 insertions(+), 10 deletions(-) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index 4d516aa33f8..f5ce0fbc7a7 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -2172,10 +2172,9 @@ bool Item_field::enumerate_field_refs_processor(uchar *arg) return FALSE; } -bool Item_field::covering_keys_processor(uchar *arg) +bool Item_field::update_table_bitmaps_processor(uchar *arg) { - if (field && field->table) - field->table->covering_keys.intersect(field->part_of_key); + update_table_bitmaps(); return FALSE; } diff --git a/sql/item.h b/sql/item.h index cd79acdf3c3..bfe0d78ed29 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1018,7 +1018,7 @@ public: virtual bool mark_as_eliminated_processor(uchar *arg) { return 0; } virtual bool eliminate_subselect_processor(uchar *arg) { return 0; } virtual bool set_fake_select_as_master_processor(uchar *arg) { return 0; } - virtual bool covering_keys_processor(uchar *arg) { return 0; } + virtual bool update_table_bitmaps_processor(uchar *arg) { return 0; } virtual bool view_used_tables_processor(uchar *arg) { return 0; } virtual bool eval_not_null_tables(uchar *opt_arg) { return 0; } virtual bool clear_sum_processor(uchar *opt_arg) { return 0; } @@ -1817,11 +1817,20 @@ public: bool get_date_result(MYSQL_TIME *ltime,uint fuzzydate); bool is_null() { return field->is_null(); } void update_null_value(); - void update_used_tables() + void update_table_bitmaps() { if (field && field->table) - field->table->covering_keys.intersect(field->part_of_key); - } + { + TABLE *tab= field->table; + tab->covering_keys.intersect(field->part_of_key); + tab->merge_keys.merge(field->part_of_key); + if (tab->read_set) + bitmap_fast_test_and_set(tab->read_set, field->field_index); + if (field->vcol_info) + tab->mark_virtual_col(field); + } + } + void update_used_tables() { update_table_bitmaps(); } Item *get_tmp_table_item(THD *thd); bool collect_item_field_processor(uchar * arg); bool add_field_to_set_processor(uchar * arg); @@ -1833,7 +1842,7 @@ public: bool vcol_in_partition_func_processor(uchar *bool_arg); bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool enumerate_field_refs_processor(uchar *arg); - bool covering_keys_processor(uchar *arg); + bool update_table_bitmaps_processor(uchar *arg); void cleanup(); Item_equal *get_item_equal() { return item_equal; } void set_item_equal(Item_equal *item_eq) { item_equal= item_eq; } diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 7dc26adf97e..22c62f0a6d7 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -492,7 +492,7 @@ void Item_subselect::recalc_used_tables(st_select_lex *new_parent, upper->item->walk(&Item::enumerate_field_refs_processor, FALSE, (uchar*)&fixer); used_tables_cache |= fixer.used_tables; - upper->item->walk(&Item::covering_keys_processor, FALSE, NULL); + upper->item->walk(&Item::update_table_bitmaps_processor, FALSE, NULL); /* if (after_pullout) upper->item->fix_after_pullout(new_parent, &(upper->item)); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 64e295d82ea..cc10cd5dee3 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3411,7 +3411,7 @@ void SELECT_LEX::update_used_tables() while ((tl= ti++)) { TABLE_LIST *embedding; - if (tl->table) + if (tl->table && !tl->is_view_or_derived()) { embedding= tl->embedding; for (embedding= tl->embedding; embedding; embedding=embedding->embedding) @@ -3422,6 +3422,9 @@ void SELECT_LEX::update_used_tables() TABLE *tab= tl->table; tab->covering_keys= tab->s->keys_for_keyread; tab->covering_keys.intersect(tab->keys_in_use_for_query); + tab->merge_keys.clear_all(); + bitmap_clear_all(tab->read_set); + bitmap_clear_all(tab->vcol_set); break; } } -- cgit v1.2.1 From 2b173bf894b99e7ad5a4e499459141ca08c5027d Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Sat, 22 Oct 2011 07:19:43 -0700 Subject: Fixed LP bug #878769. The method DsMrr_impl::dsmrr_init erroneously tried to get a KEY descriptor for key with number MAX_KEY. This caused valgrind complains. --- sql/multi_range_read.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/multi_range_read.cc b/sql/multi_range_read.cc index 6e11fe5efa0..055a9268417 100644 --- a/sql/multi_range_read.cc +++ b/sql/multi_range_read.cc @@ -848,7 +848,8 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs, if (h_idx->primary_key_is_clustered()) { uint pk= h_idx->get_table()->s->primary_key; - saved_pk_length= h_idx->get_table()->key_info[pk].key_length; + if (pk != MAX_KEY) + saved_pk_length= h_idx->get_table()->key_info[pk].key_length; } KEY *used_index= &h_idx->get_table()->key_info[h_idx->active_index]; -- cgit v1.2.1 From 391c5db1fca4b9d36afa3d131b4f5401fa9189f6 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Sun, 23 Oct 2011 05:46:03 -0700 Subject: Fixed LP bug #879882. This bug happened because the function Item_cond::eval_not_null_tables erroneously did not initialize the value of not_null_tables_cache. --- sql/item_cmpfunc.cc | 1 + 1 file changed, 1 insertion(+) (limited to 'sql') diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index e1574bdf5de..6f8e47264ad 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -4195,6 +4195,7 @@ Item_cond::eval_not_null_tables(uchar *opt_arg) { Item *item; List_iterator li(list); + not_null_tables_cache= (table_map) 0; and_tables_cache= ~(table_map) 0; while ((item=li++)) { -- cgit v1.2.1 From c0a1bd1ed63bb9e0064e99e654b3b57dd3f0c5f4 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Mon, 24 Oct 2011 12:54:28 -0700 Subject: Fixed LP bug #880724. Do not create KEYUSEs for a materialized view over a constant table. --- sql/sql_select.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index aa77013ed49..29db90452a2 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3827,7 +3827,9 @@ add_key_field(JOIN *join, uint optimize= 0; if (eq_func && ((join->is_allowed_hash_join_access() && - field->hash_join_is_possible()) || + field->hash_join_is_possible() && + !(field->table->pos_in_table_list->is_materialized_derived() && + field->table->created)) || (field->table->pos_in_table_list->is_materialized_derived() && !field->table->created))) { -- cgit v1.2.1 From a8f7c03c1e46dd556aa57d45bbd078101c68ec9e Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Tue, 25 Oct 2011 14:18:19 -0700 Subject: Fixed LP bug #881318. If a materialized derived table / view is empty then for this table the value of file->ref is 0. This was not taken into account by the function JOIN_CACHE::write_record_data. As a result a query using an empty materialized derived tables as inner tables of outer joins and IN subqueries in WHERE conditions could cause server crashes when the optimizer employed join caches and duplicate elimination for semi-joins. --- sql/sql_join_cache.cc | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_join_cache.cc b/sql/sql_join_cache.cc index c8933fe69ee..f53bf738b86 100644 --- a/sql/sql_join_cache.cc +++ b/sql/sql_join_cache.cc @@ -1413,12 +1413,22 @@ uint JOIN_CACHE::write_record_data(uchar * link, bool *is_full) TABLE *table= (TABLE *) copy->str; copy->str= table->file->ref; copy->length= table->file->ref_length; + if (!copy->str) + { + /* + If table is an empty inner table of an outer join and it is + a materialized derived table then table->file->ref == NULL. + */ + cp+= copy->length; + break; + } } /* fall through */ default: /* Copy the entire image of the field from the record buffer */ DBUG_ASSERT(cp + copy->length <= buff + buff_size); - memcpy(cp, copy->str, copy->length); + if (copy->str) + memcpy(cp, copy->str, copy->length); cp+= copy->length; } } @@ -1811,6 +1821,13 @@ uint JOIN_CACHE::read_record_field(CACHE_FIELD *copy, bool blob_in_rec_buff) memset(copy->str+len, ' ', copy->length-len); len+= 2; break; + case CACHE_ROWID: + if (!copy->str) + { + len= copy->length; + break; + } + /* fall through */ default: /* Copy the entire image of the field from the record buffer */ len= copy->length; -- cgit v1.2.1 From 8e6440df0b8b08a67829932d62d354f83d29becc Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 26 Oct 2011 02:38:49 +0400 Subject: BUG#877288: Wrong result with semijoin + materialization + multipart key - when create_ref_for_key() is constructing a ref access for a table that's inside a SJ-Materialization nest, it may not use references to fields of tables that are unside the nest (because these fields will not yet have values when ref access will be used) The check was performed in the first of create_ref_for_key's loops (the one which counts how many key parts are usable) but not in the second (the one which actually fills the TABLE_REF structure). --- sql/sql_select.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 29db90452a2..71d3137f1c0 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7360,7 +7360,8 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, while (((~used_tables) & keyuse->used_tables) || (keyuse->keypart != (is_hash_join_key_no(key) ? - keyinfo->key_part[i].field->field_index : i))) + keyinfo->key_part[i].field->field_index : i)) || + !are_tables_local(j, keyuse->val->used_tables())) keyuse++; /* Skip other parts */ uint maybe_null= test(keyinfo->key_part[i].null_bit); -- cgit v1.2.1 From 16942bc5ca0f239c2aa2c2cde4eaba144495e9aa Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Wed, 26 Oct 2011 04:27:09 -0700 Subject: Fixed LP bug #881449. The function SELECT_LEX::update_used_tables first must clean up all bitmaps to be recalculated for all tables that require it and only after this walk through on conditions attached to the tables to update these bitmaps. --- sql/sql_lex.cc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index cc10cd5dee3..a6494c72889 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3408,12 +3408,12 @@ void SELECT_LEX::update_used_tables() { TABLE_LIST *tl; List_iterator ti(leaf_tables); + while ((tl= ti++)) { - TABLE_LIST *embedding; if (tl->table && !tl->is_view_or_derived()) { - embedding= tl->embedding; + TABLE_LIST *embedding= tl->embedding; for (embedding= tl->embedding; embedding; embedding=embedding->embedding) { if (embedding->is_view_or_derived()) @@ -3429,7 +3429,12 @@ void SELECT_LEX::update_used_tables() } } } - embedding= tl; + } + + ti.rewind(); + while ((tl= ti++)) + { + TABLE_LIST *embedding= tl; do { bool maybe_null; @@ -3458,6 +3463,7 @@ void SELECT_LEX::update_used_tables() embedding= tl->embedding; } } + if (join->conds) { join->conds->update_used_tables(); -- cgit v1.2.1 From fa36a7426bf505e83cbd03d8e54e433b3df4d72e Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Wed, 26 Oct 2011 20:25:18 +0300 Subject: Fixed lp:879939 "assertion in ha_maria::enable_indexes with derived_with_keys=on" Honor unique/not unique when creating keys for internal tempory tables. Added new variables to be used to limit how keys are created for internal temporary tables. include/maria.h: Added maria_max_key_length() and maria_max_key_segments() include/myisam.h: Added myisam_max_key_length() and myisam_max_key_segments() mysql-test/r/mysql.result: Drop all used tables mysql-test/r/subselect4.result: Added test case for lp:879939 mysql-test/t/mysql.test: Drop all used tables mysql-test/t/subselect4.test: Added test case for lp:879939 sql/mysql_priv.h: Added internal_tmp_table_max_key_length and internal_tmp_table_max_key_segments to be used to limit how keys for derived tables are created. sql/mysqld.cc: Added internal_tmp_table_max_key_length and internal_tmp_table_max_key_segments to be used to limit how keys for derived tables are created. sql/share/errmsg.txt: Added new error message for internal errors sql/sql_select.cc: Give error if we try to create a wrong key (this error should never happen) Honor unique/not unique when creating keys for internal tempory tables. storage/maria/ha_maria.cc: Added change_table_ptr() to ensure that external_ref points always to the correct table. (Not having this caused an assert in the included test) storage/maria/ha_maria.h: Added change_table_ptr() to ensure that external_ref points always to the correct table. storage/maria/ma_check.c: Fixed bug in Duplicate key error printing (now row position is printed correctly) storage/maria/ma_create.c: maria_max_key_length() -> _ma_max_key_length() storage/maria/ma_info.c: Added extern function maria_max_key_length() to calculate the max key length based on current block size. storage/maria/ma_open.c: maria_max_key_length() -> _ma_max_key_length() storage/maria/maria_def.h: maria_max_key_length() -> _ma_max_key_length() storage/myisam/ha_myisam.cc: Added change_table_ptr() to ensure that external_ref points always to the correct table. (Not having this caused an assert in the included test) storage/myisam/ha_myisam.h: Added change_table_ptr() to ensure that external_ref points always to the correct table. --- sql/mysql_priv.h | 3 +++ sql/mysqld.cc | 9 +++++++++ sql/share/errmsg.txt | 4 ++++ sql/sql_select.cc | 14 +++++++++++--- 4 files changed, 27 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 903f8bd82ff..302a8af7dd3 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -2181,6 +2181,9 @@ extern uint test_flags,select_errors,ha_open_options; extern uint protocol_version, mysqld_port, mysqld_extra_port, dropping_tables; extern uint delay_key_write_options; extern ulong max_long_data_size; +extern uint internal_tmp_table_max_key_length; +extern uint internal_tmp_table_max_key_segments; + #endif /* MYSQL_SERVER */ #if defined MYSQL_SERVER || defined INNODB_COMPATIBILITY_HOOKS extern MYSQL_PLUGIN_IMPORT uint lower_case_table_names; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index df0de7e25b1..57c2fa42ab8 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -669,6 +669,10 @@ ulong extra_max_connections; */ ulong max_long_data_size; +/* Limits for internal temporary tables (MyISAM or Aria) */ +uint internal_tmp_table_max_key_length; +uint internal_tmp_table_max_key_segments; + int max_user_connections= 0; bool max_user_connections_checking=0; ulonglong denied_connections; @@ -4464,6 +4468,11 @@ a file name for --log-bin-index option", opt_binlog_index_name); sql_print_error("Aria engine is not enabled or did not start. The Aria engine must be enabled to continue as mysqld was configured with --with-aria-tmp-tables"); unireg_abort(1); } + internal_tmp_table_max_key_length= maria_max_key_length(); + internal_tmp_table_max_key_segments= maria_max_key_segments(); +#else + internal_tmp_table_max_key_length= myisam_max_key_length(); + internal_tmp_table_max_key_segments= myisam_max_key_segments(); #endif tc_log= (total_ha_2pc > 1 ? (opt_bin_log ? diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 5f6931ab155..035267b0b5d 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -6292,3 +6292,7 @@ ER_VIEW_ORDERBY_IGNORED eng "View '%-.192s'.'%-.192s' ORDER BY clause ignored because there is other ORDER BY clause already." ER_CONNECTION_KILLED 70100 eng "Connection was killed" +ER_INTERNAL_ERROR + eng "Internal error: '%-.192s'" + + diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 71d3137f1c0..efa65f34163 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -14172,6 +14172,13 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, keyinfo->key_parts > table->file->max_key_parts() || share->uniques) { + if (!share->uniques && !(keyinfo->flags & HA_NOSAME)) + { + my_error(ER_INTERNAL_ERROR, MYF(0), + "Using too big key for internal temp tables"); + DBUG_RETURN(1); + } + /* Can't create a key; Make a unique constraint instead of a key */ share->keys= 0; share->uniques= 1; @@ -14190,9 +14197,9 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, } else { - /* Create an unique key */ + /* Create a key */ bzero((char*) &keydef,sizeof(keydef)); - keydef.flag=HA_NOSAME; + keydef.flag= keyinfo->flags & HA_NOSAME; keydef.keysegs= keyinfo->key_parts; keydef.seg= seg; } @@ -14368,7 +14375,8 @@ bool create_internal_tmp_table(TABLE *table, KEY *keyinfo, { /* Create an unique key */ bzero((char*) &keydef,sizeof(keydef)); - keydef.flag=HA_NOSAME | HA_BINARY_PACK_KEY | HA_PACK_KEY; + keydef.flag= ((keyinfo->flags & HA_NOSAME) | HA_BINARY_PACK_KEY | + HA_PACK_KEY); keydef.keysegs= keyinfo->key_parts; keydef.seg= seg; } -- cgit v1.2.1 From 08e9e6a790ea6ba8bbe6fa5c5bd54677f4add072 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Thu, 27 Oct 2011 13:54:28 +0400 Subject: BUG#882472: subselect4.test fails in current 5.3 - The problem was that the value of READ_RECORD::file was not updated when the underlying table was temporary and was converted from heap to myisam. Resolved by eliminating READ_RECORD::file, always use READ_RECORD::table->file --- sql/records.cc | 18 ++++++++---------- sql/sql_select.cc | 15 ++++++--------- sql/sql_show.cc | 4 ++-- sql/structs.h | 2 +- 4 files changed, 17 insertions(+), 22 deletions(-) (limited to 'sql') diff --git a/sql/records.cc b/sql/records.cc index 60c801f8977..b125c201621 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -59,7 +59,6 @@ void init_read_record_idx(READ_RECORD *info, THD *thd, TABLE *table, bzero((char*) info,sizeof(*info)); info->thd= thd; info->table= table; - info->file= table->file; info->record= table->record[0]; info->print_error= print_error; info->unlock_row= rr_unlock_row; @@ -169,7 +168,6 @@ bool init_read_record(READ_RECORD *info,THD *thd, TABLE *table, bzero((char*) info,sizeof(*info)); info->thd=thd; info->table=table; - info->file= table->file; info->forms= &info->table; /* Only one table */ if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE && @@ -291,9 +289,9 @@ void end_read_record(READ_RECORD *info) { filesort_free_buffers(info->table,0); if (info->table->created) - (void) info->file->extra(HA_EXTRA_NO_CACHE); + (void) info->table->file->extra(HA_EXTRA_NO_CACHE); if (info->read_record != rr_quick) // otherwise quick_range does it - (void) info->file->ha_index_or_rnd_end(); + (void) info->table->file->ha_index_or_rnd_end(); info->table=0; } } @@ -352,7 +350,7 @@ static int rr_quick(READ_RECORD *info) static int rr_index_first(READ_RECORD *info) { - int tmp= info->file->ha_index_first(info->record); + int tmp= info->table->file->ha_index_first(info->record); info->read_record= rr_index; if (tmp) tmp= rr_handle_error(info, tmp); @@ -378,7 +376,7 @@ static int rr_index_first(READ_RECORD *info) static int rr_index(READ_RECORD *info) { - int tmp= info->file->ha_index_next(info->record); + int tmp= info->table->file->ha_index_next(info->record); if (tmp) tmp= rr_handle_error(info, tmp); return tmp; @@ -388,7 +386,7 @@ static int rr_index(READ_RECORD *info) int rr_sequential(READ_RECORD *info) { int tmp; - while ((tmp= info->file->ha_rnd_next(info->record))) + while ((tmp= info->table->file->ha_rnd_next(info->record))) { /* rnd_next can return RECORD_DELETED for MyISAM when one thread is @@ -413,7 +411,7 @@ static int rr_from_tempfile(READ_RECORD *info) { if (my_b_read(info->io_cache,info->ref_pos,info->ref_length)) return -1; /* End of file */ - if (!(tmp= info->file->ha_rnd_pos(info->record,info->ref_pos))) + if (!(tmp= info->table->file->ha_rnd_pos(info->record,info->ref_pos))) break; /* The following is extremely unlikely to happen */ if (tmp == HA_ERR_RECORD_DELETED || @@ -464,7 +462,7 @@ static int rr_from_pointers(READ_RECORD *info) cache_pos= info->cache_pos; info->cache_pos+= info->ref_length; - if (!(tmp= info->file->ha_rnd_pos(info->record,cache_pos))) + if (!(tmp= info->table->file->ha_rnd_pos(info->record,cache_pos))) break; /* The following is extremely unlikely to happen */ @@ -597,7 +595,7 @@ static int rr_from_cache(READ_RECORD *info) record=uint3korr(position); position+=3; record_pos=info->cache+record*info->reclength; - if ((error=(int16) info->file->ha_rnd_pos(record_pos,info->ref_pos))) + if ((error=(int16) info->table->file->ha_rnd_pos(record_pos,info->ref_pos))) { record_pos[info->error_offset]=1; shortstore(record_pos,error); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index efa65f34163..af7cd3c0a7a 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -8675,7 +8675,7 @@ void set_join_cache_denial(JOIN_TAB *join_tab) void rr_unlock_row(st_join_table *tab) { READ_RECORD *info= &tab->read_record; - info->file->unlock_row(); + info->table->file->unlock_row(); } @@ -9349,7 +9349,6 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) TABLE *table=tab->table; uint jcl= tab->used_join_cache_level; tab->read_record.table= table; - tab->read_record.file=table->file; tab->read_record.unlock_row= rr_unlock_row; tab->sorted= sorted; sorted= 0; // only first must be sorted @@ -15719,7 +15718,7 @@ int join_read_key2(THD *thd, JOIN_TAB *tab, TABLE *table, TABLE_REF *table_ref) */ if (tab && tab->ref.has_record && tab->ref.use_count == 0) { - tab->read_record.file->unlock_row(); + tab->read_record.table->file->unlock_row(); table_ref->has_record= FALSE; } error=table->file->ha_index_read_map(table->record[0], @@ -15905,7 +15904,7 @@ join_init_quick_read_record(JOIN_TAB *tab) int init_read_record_seq(JOIN_TAB *tab) { tab->read_record.read_record= rr_sequential; - if (tab->read_record.file->ha_rnd_init_with_error(1)) + if (tab->read_record.table->file->ha_rnd_init_with_error(1)) return 1; return (*tab->read_record.read_record)(&tab->read_record); } @@ -15972,7 +15971,6 @@ join_read_first(JOIN_TAB *tab) tab->table->status=0; tab->read_record.read_record=join_read_next; tab->read_record.table=table; - tab->read_record.file=table->file; tab->read_record.index=tab->index; tab->read_record.record=table->record[0]; if (!table->file->inited) @@ -15993,7 +15991,7 @@ static int join_read_next(READ_RECORD *info) { int error; - if ((error= info->file->ha_index_next(info->record))) + if ((error= info->table->file->ha_index_next(info->record))) return report_error(info->table, error); return 0; @@ -16011,7 +16009,6 @@ join_read_last(JOIN_TAB *tab) tab->table->status=0; tab->read_record.read_record=join_read_prev; tab->read_record.table=table; - tab->read_record.file=table->file; tab->read_record.index=tab->index; tab->read_record.record=table->record[0]; if (!table->file->inited) @@ -16029,7 +16026,7 @@ static int join_read_prev(READ_RECORD *info) { int error; - if ((error= info->file->ha_index_prev(info->record))) + if ((error= info->table->file->ha_index_prev(info->record))) return report_error(info->table, error); return 0; } @@ -16063,7 +16060,7 @@ static int join_ft_read_next(READ_RECORD *info) { int error; - if ((error= info->file->ha_ft_read(info->table->record[0]))) + if ((error= info->table->file->ha_ft_read(info->table->record[0]))) return report_error(info->table, error); return 0; } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 41e1734f346..c7cd692000f 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -6747,11 +6747,11 @@ bool get_schema_tables_result(JOIN *join, { result= 1; join->error= 1; - tab->read_record.file= table_list->table->file; + tab->read_record.table->file= table_list->table->file; table_list->schema_table_state= executed_place; break; } - tab->read_record.file= table_list->table->file; + tab->read_record.table->file= table_list->table->file; table_list->schema_table_state= executed_place; } } diff --git a/sql/structs.h b/sql/structs.h index 18f90d5b8b1..61354cbd01c 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -149,7 +149,7 @@ struct READ_RECORD { /* Parameter to read_record */ typedef int (*Read_func)(READ_RECORD*); typedef void (*Unlock_row_func)(st_join_table *); struct st_table *table; /* Head-form */ - handler *file; + // handler *file_; struct st_table **forms; /* head and ref forms */ Read_func read_record; -- cgit v1.2.1 From 28e2777991b238d770529d0438e4f18fbfc3e271 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 27 Oct 2011 15:22:52 +0300 Subject: Fix gcc 4.6 warnings about assigned but not used variables. Fixed my_gethwaddr.c to allow compilation on Mac OS X. --- sql/sql_show.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 884d58d3674..3a9595819fa 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1215,7 +1215,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, handler *file= table->file; TABLE_SHARE *share= table->s; HA_CREATE_INFO create_info; - bool show_table_options= FALSE; + bool show_table_options __attribute__ ((unused))= FALSE; bool foreign_db_mode= (thd->variables.sql_mode & (MODE_POSTGRESQL | MODE_ORACLE | MODE_MSSQL | @@ -1432,7 +1432,9 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN("\n)")); if (!(thd->variables.sql_mode & MODE_NO_TABLE_OPTIONS) && !foreign_db_mode) { +#ifdef WITH_PARTITION_STORAGE_ENGINE show_table_options= TRUE; +#endif /* Get possible table space definitions and append them to the CREATE TABLE statement -- cgit v1.2.1 From 2a3858d9d0bd0cccc6609388ff89096441193d5c Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Thu, 27 Oct 2011 08:32:24 -0700 Subject: Fixed LP bug #874035. The function Item_direct_view_ref::fix_fields erroneously did not correct the value of the flag maybe_null when the view for which the item was being fixed happened to be an inner table of an outer join. --- sql/item.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index f5ce0fbc7a7..2407de1bf30 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -7321,7 +7321,11 @@ bool Item_direct_view_ref::fix_fields(THD *thd, Item **reference) ((*ref)->fix_fields(thd, ref))) return TRUE; - return Item_direct_ref::fix_fields(thd, reference); + if (Item_direct_ref::fix_fields(thd, reference)) + return TRUE; + if (view->table && view->table->maybe_null) + maybe_null= TRUE; + return FALSE; } /* -- cgit v1.2.1 From 26387734fd16e45333b26dabb78740473052cb36 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 27 Oct 2011 19:18:25 +0300 Subject: Fix gcc 4.6 warning after merge with 5.1 --- sql/sql_update.cc | 4 ++-- sql/table.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'sql') diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 70572b406e5..c6f413e754e 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -2081,8 +2081,8 @@ int multi_update::do_updates() err: { thd->fatal_error(); - prepare_record_for_error_message(local_error, table); - table->file->print_error(local_error,MYF(0)); + prepare_record_for_error_message(local_error, err_table); + err_table->file->print_error(local_error,MYF(0)); } err2: diff --git a/sql/table.cc b/sql/table.cc index 21575e419da..bf7265e974f 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -5484,7 +5484,7 @@ int update_virtual_fields(THD *thd, TABLE *table, bool for_write) { DBUG_ENTER("update_virtual_fields"); Field **vfield_ptr, *vfield; - int error= 0; + int error __attribute__ ((unused))= 0; if (!table || !table->vfield) DBUG_RETURN(0); -- cgit v1.2.1 From 3694bb90a47feb463c5a38543650f0f53d1735eb Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Fri, 28 Oct 2011 12:38:36 +0400 Subject: - Let t/myisam_icp.test run include/icp_tests.inc with MRR/ICP turned ON (not OFF) - Fix the compile-time-default value of optimizer_switch printed by mysqld --help --defaults --- sql/mysqld.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 57c2fa42ab8..a7199a4af2a 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -481,7 +481,7 @@ static const char *optimizer_switch_str="index_merge=on,index_merge_union=on," "index_merge_sort_union=on," "index_merge_intersection=on," "index_merge_sort_intersection=off," - "index_condition_pushdown=on," + "index_condition_pushdown=off," "derived_merge=off," "derived_with_keys=off," "firstmatch=off," -- cgit v1.2.1 From 7bc6a83b027aeddbe1d84ceb6647928d5cfb3bca Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Sat, 29 Oct 2011 15:36:24 -0700 Subject: Backported the fix and the test case for bug #58816 from mysql-5.6 code line. --- sql/handler.cc | 3 +++ sql/handler.h | 7 +++++++ sql/sql_select.cc | 4 ++++ 3 files changed, 14 insertions(+) (limited to 'sql') diff --git a/sql/handler.cc b/sql/handler.cc index 66e22aa40dc..6c5d3a580ec 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -4878,6 +4878,9 @@ int handler::ha_reset() /* reset the bitmaps to point to defaults */ table->default_column_bitmaps(); pushed_cond= NULL; + /* Reset information about pushed engine conditions */ + cancel_pushed_idx_cond(); + /* Reset information about pushed index conditions */ DBUG_RETURN(reset()); } diff --git a/sql/handler.h b/sql/handler.h index 46f5beb45d2..1f45b1a1e7d 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -2393,6 +2393,13 @@ public: */ virtual void cond_pop() { return; }; virtual Item *idx_cond_push(uint keyno, Item* idx_cond) { return idx_cond; } + /** Reset information about pushed index conditions */ + virtual void cancel_pushed_idx_cond() + { + pushed_idx_cond= NULL; + pushed_idx_cond_keyno= MAX_KEY; + in_range_check_pushed_down= false; + } virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info, uint table_changes) { return COMPATIBLE_DATA_NO; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index af7cd3c0a7a..770472d4fe6 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -18002,7 +18002,11 @@ use_filesort: select->quick= save_quick; } if (orig_cond_saved) + { + tab->table->file->cancel_pushed_idx_cond(); tab->set_cond(orig_cond); + } + DBUG_RETURN(0); } -- cgit v1.2.1 From 57b3fefa03055e579fbcf664ce71b3b41c6b4558 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Mon, 31 Oct 2011 15:07:43 +0400 Subject: BUG#882994: Crash in QUICK_RANGE_SELECT::reset with derived_with_keys - The bug was caused by the following scenario: = a quick select is created with get_quick_select_for_ref. The quick select refers to temporary (derived) table. It saves table->file, which refers to a ha_heap object. = When temp table is populated, ha_heap reaches max. size and is converted to a ha_myisam. However, quick->file remains pointing to where ha_heap was. = Attempt to use the quick select causes crash. - Fixed by introducing QUICK_SELECT_I::replace_handler(). Note that it will not work for index_merge quick selects. Which is fine, because these quick selects are never created for derived tables. --- sql/opt_range.h | 7 +++++++ sql/sql_select.cc | 2 ++ 2 files changed, 9 insertions(+) (limited to 'sql') diff --git a/sql/opt_range.h b/sql/opt_range.h index 0ad2b7242f2..e498d229dde 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -354,6 +354,12 @@ public: Table record buffer used by this quick select. */ uchar *record; + + virtual void replace_handler(handler *new_file) + { + DBUG_ASSERT(0); /* Only supported in QUICK_RANGE_SELECT */ + } + #ifndef DBUG_OFF /* Print quick select information to DBUG_FILE. Caller is responsible @@ -449,6 +455,7 @@ public: #ifndef DBUG_OFF void dbug_dump(int indent, bool verbose); #endif + virtual void replace_handler(handler *new_file) { file= new_file; } private: /* Default copy ctor used by QUICK_SELECT_DESC */ friend class TRP_ROR_INTERSECT; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index af7cd3c0a7a..c6b5369c356 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -9701,6 +9701,8 @@ bool JOIN_TAB::preread_init() derived, DT_CREATE | DT_FILL)) return TRUE; preread_init_done= TRUE; + if (select && select->quick) + select->quick->replace_handler(table->file); return FALSE; } -- cgit v1.2.1 From acb2d4aad85896ce31380f2c90804f3f1240d1d8 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 1 Nov 2011 12:04:11 +0400 Subject: BUG#884631: Table elimination works 5.3 release builds even if turned off - Make table elimination to actually switch itself on/off in release builds. --- sql/opt_table_elimination.cc | 2 -- 1 file changed, 2 deletions(-) (limited to 'sql') diff --git a/sql/opt_table_elimination.cc b/sql/opt_table_elimination.cc index c8c61720068..56396181619 100644 --- a/sql/opt_table_elimination.cc +++ b/sql/opt_table_elimination.cc @@ -588,10 +588,8 @@ void eliminate_tables(JOIN *join) if (!join->outer_join) DBUG_VOID_RETURN; -#ifndef DBUG_OFF if (!optimizer_flag(thd, OPTIMIZER_SWITCH_TABLE_ELIMINATION)) DBUG_VOID_RETURN; /* purecov: inspected */ -#endif /* Find the tables that are referred to from WHERE/HAVING */ used_tables= (join->conds? join->conds->used_tables() : 0) | -- cgit v1.2.1 From f2b6f4e3df2a41e15b155d6019dfa81982181e91 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Tue, 1 Nov 2011 12:36:43 +0400 Subject: BUG#884184: Wrong result with RIGHT JOIN + derived_merge - Make eliminate_tables_for_list() take into account that it is not possible to eliminate a table if it is used in the upper-side ON expressions. Example: xxx JOIN (t1 LEFT JOIN t2 ON cond ) ON func(t2.columns) Here it would eliminate t2 which is not possible because of use of t2.columns. --- sql/opt_table_elimination.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sql') diff --git a/sql/opt_table_elimination.cc b/sql/opt_table_elimination.cc index fdf818abb8e..65a7d662425 100644 --- a/sql/opt_table_elimination.cc +++ b/sql/opt_table_elimination.cc @@ -695,6 +695,8 @@ eliminate_tables_for_list(JOIN *join, List *join_list, { table_map outside_used_tables= tables_used_elsewhere | tables_used_on_left; + if (on_expr) + outside_used_tables |= on_expr->used_tables(); if (tbl->nested_join) { /* This is "... LEFT JOIN (join_nest) ON cond" */ -- cgit v1.2.1 From 9c5644e6b764667e165fb895c6b9c90601cba06e Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 1 Nov 2011 13:22:09 +0200 Subject: Fix of typo. --- sql/item_cmpfunc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 6f8e47264ad..a33dd090f14 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1792,7 +1792,7 @@ Item *Item_in_optimizer::transform(Item_transformer transformer, uchar *argument if (!new_item) return 0; if (args[1] != new_item) - current_thd->change_item_tree(args, new_item); + current_thd->change_item_tree(args + 1, new_item); } else { -- cgit v1.2.1 From a70f7aa5fe0b05228aea5bf9673233d857524b0d Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Tue, 1 Nov 2011 07:00:55 -0700 Subject: Backported the fix and the test case for bug 12822678 from the mysql-5.6 code line. Fixed a bug in select_describe. Adjusted results for affected test cases. --- sql/opt_index_cond_pushdown.cc | 3 ++- sql/sql_select.cc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'sql') diff --git a/sql/opt_index_cond_pushdown.cc b/sql/opt_index_cond_pushdown.cc index 639ceedf693..d63cc200493 100644 --- a/sql/opt_index_cond_pushdown.cc +++ b/sql/opt_index_cond_pushdown.cc @@ -322,7 +322,8 @@ void push_index_cond(JOIN_TAB *tab, uint keyno) HA_DO_INDEX_COND_PUSHDOWN) && optimizer_flag(tab->join->thd, OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN) && tab->join->thd->lex->sql_command != SQLCOM_UPDATE_MULTI && - tab->join->thd->lex->sql_command != SQLCOM_DELETE_MULTI) + tab->join->thd->lex->sql_command != SQLCOM_DELETE_MULTI && + tab->type != JT_CONST && tab->type != JT_SYSTEM) { DBUG_EXECUTE("where", print_where(tab->select_cond, "full cond", QT_ORDINARY);); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 770472d4fe6..500ed691f2c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -20750,7 +20750,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, keylen_str_buf); tmp3.append(keylen_str_buf, length, cs); } - if (tab->select && tab->select->quick) + if (tab->type != JT_CONST && tab->select && tab->select->quick) tab->select->quick->add_keys_and_lengths(&tmp2, &tmp3); if (key_info || (tab->select && tab->select->quick)) { -- cgit v1.2.1 From b40bc2b3e3afee92a6d33d80840840fcf9baae11 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 1 Nov 2011 17:42:52 +0200 Subject: Fix of LP BUG#872775. The problem was that merged views has its own nest_level numbering => when we compare nest levels we should take into considiration basis (i.e. 0 level), if it is different then nest levels are not comparable. --- sql/item.cc | 4 +++- sql/item.h | 5 ++++- sql/item_subselect.cc | 4 +++- sql/item_sum.cc | 4 +++- sql/sql_lex.cc | 1 + sql/sql_lex.h | 7 +++++++ sql/sql_parse.cc | 1 + 7 files changed, 22 insertions(+), 4 deletions(-) (limited to 'sql') diff --git a/sql/item.cc b/sql/item.cc index f5ce0fbc7a7..7056e1dd5d2 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -755,7 +755,9 @@ bool Item_ident::remove_dependence_processor(uchar * arg) bool Item_ident::collect_outer_ref_processor(uchar *param) { Collect_deps_prm *prm= (Collect_deps_prm *)param; - if (depended_from && depended_from->nest_level < prm->nest_level) + if (depended_from && + depended_from->nest_level_base == prm->nest_level_base && + depended_from->nest_level < prm->nest_level) prm->parameters->add_unique(this, &cmp_items); return FALSE; } diff --git a/sql/item.h b/sql/item.h index bfe0d78ed29..fa6918d8484 100644 --- a/sql/item.h +++ b/sql/item.h @@ -505,6 +505,7 @@ typedef void (*Cond_traverser) (const Item *item, void *arg); class Item_equal; class COND_EQUAL; +class st_select_lex_unit; class Item { Item(const Item &); /* Prevent use of these */ @@ -1159,8 +1160,10 @@ public: } struct Collect_deps_prm { - int nest_level; List *parameters; + /* unit from which we count nest_level */ + st_select_lex_unit *nest_level_base; + int nest_level; }; /** Collect outer references diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 22c62f0a6d7..4ab2e2e2c69 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -599,7 +599,9 @@ bool Item_subselect::exec() void Item_subselect::get_cache_parameters(List ¶meters) { - Collect_deps_prm prm= { unit->first_select()->nest_level, ¶meters }; + Collect_deps_prm prm= {¶meters, + unit->first_select()->nest_level_base, + unit->first_select()->nest_level}; walk(&Item::collect_outer_ref_processor, TRUE, (uchar*)&prm); } diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 65314a0ff91..063406990cb 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -359,7 +359,9 @@ bool Item_sum::collect_outer_ref_processor(uchar *param) { Collect_deps_prm *prm= (Collect_deps_prm *)param; SELECT_LEX *ds; - if ((ds= depended_from()) && ds->nest_level < prm->nest_level) + if ((ds= depended_from()) && + ds->nest_level_base == prm->nest_level_base && + ds->nest_level < prm->nest_level) prm->parameters->add_unique(this, &cmp_items); return FALSE; } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index cc10cd5dee3..8513f73b90c 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -339,6 +339,7 @@ void lex_start(THD *thd) lex->event_parse_data= NULL; lex->profile_options= PROFILE_NONE; lex->nest_level=0 ; + lex->select_lex.nest_level_base= &lex->unit; lex->allow_sum_func= 0; lex->in_sum_func= NULL; lex->protect_against_global_read_lock= FALSE; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 32ad4f414c9..4caf2a1dbb3 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -684,6 +684,13 @@ public: ulong table_join_options; uint in_sum_expr; uint select_number; /* number of select (used for EXPLAIN) */ + + /* + nest_levels are local to the query or VIEW, + and that view merge procedure does not re-calculate them. + So we also have to remember unit against which we count levels. + */ + SELECT_LEX_UNIT *nest_level_base; int nest_level; /* nesting level of select */ Item_sum *inner_sum_func_list; /* list of sum func in nested selects */ uint with_wild; /* item list contain '*' */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 6202b9d888a..a1c373ce426 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5917,6 +5917,7 @@ mysql_new_select(LEX *lex, bool move_down) DBUG_RETURN(1); } select_lex->nest_level= lex->nest_level; + select_lex->nest_level_base= &thd->lex->unit; if (move_down) { SELECT_LEX_UNIT *unit; -- cgit v1.2.1 From 64986873252e6c4fff867407d1b2f92abe24ca88 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 1 Nov 2011 18:19:19 +0200 Subject: Fix bug lp:833702 Analysis: Equality propagation propagated the constant '7' into args[0] of the Item_in_optimizer that stands for the "< ANY" predicate. At the same the min/max subquery rewrite swapped the order of the left and right operands of the "<" predicate, but used Item_in_subselect::left_expr. As a result, when the create_swap(left_expr, subs); + subs= func->create_swap(*(optimizer->get_cache()), subs); thd->change_item_tree(place, subs); if (subs->fix_fields(thd, &subs)) DBUG_RETURN(true); -- cgit v1.2.1 From 47861a657762feeb45ee6b8edea00033bbd0e8ca Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 2 Nov 2011 13:48:41 +0400 Subject: Change the default @@optimizer_switch settings: - semijoin=on - firstmatch=on - loosescan=on --- sql/mysql_priv.h | 5 ++++- sql/mysqld.cc | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'sql') diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 302a8af7dd3..ac8b3663647 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -609,7 +609,10 @@ enabled by default, add OPTIMIZER_SWITCH_MATERIALIZATION OPTIMIZER_SWITCH_JOIN_CACHE_INCREMENTAL | \ OPTIMIZER_SWITCH_JOIN_CACHE_HASHED | \ OPTIMIZER_SWITCH_JOIN_CACHE_BKA | \ - OPTIMIZER_SWITCH_SUBQUERY_CACHE) + OPTIMIZER_SWITCH_SUBQUERY_CACHE |\ + OPTIMIZER_SWITCH_SEMIJOIN | \ + OPTIMIZER_SWITCH_FIRSTMATCH | \ + OPTIMIZER_SWITCH_LOOSE_SCAN ) /* Replication uses 8 bytes to store SQL_MODE in the binary log. The day you diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a7199a4af2a..854bc0b0c78 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -484,11 +484,11 @@ static const char *optimizer_switch_str="index_merge=on,index_merge_union=on," "index_condition_pushdown=off," "derived_merge=off," "derived_with_keys=off," - "firstmatch=off," - "loosescan=off," + "firstmatch=on," + "loosescan=on," "materialization=off," "in_to_exists=on," - "semijoin=off," + "semijoin=on," "partial_match_rowid_merge=on," "partial_match_table_scan=on," "subquery_cache=on," -- cgit v1.2.1 From 9b761df393341bb15d6892ae8def9ae84d07305b Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Wed, 2 Nov 2011 22:05:08 +0400 Subject: BUG#878753: Assertion '0' failed in replace_where_subcondition with derived_merge - Remove the assert in replace_where_subcondition (the patch has explanation why) --- sql/opt_subselect.cc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'sql') diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 416dcadc67f..aa0a8152bbb 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -1026,7 +1026,6 @@ static bool replace_where_subcondition(JOIN *join, Item **expr, Item *old_cond, Item *new_cond, bool do_fix_fields) { - //Item **expr= (emb_nest == (TABLE_LIST*)1)? &join->conds : &emb_nest->on_expr; if (*expr == old_cond) { *expr= new_cond; @@ -1050,9 +1049,15 @@ static bool replace_where_subcondition(JOIN *join, Item **expr, } } } - // If we came here it means there were an error during prerequisites check. - DBUG_ASSERT(0); - return TRUE; + /* + We can come to here when + - we're doing replace operations on both on_expr and prep_on_expr + - on_expr is the same as prep_on_expr, or they share a sub-tree + (so, when we do replace in on_expr, we replace in prep_on_expr, too, + and when we try doing a replace in prep_on_expr, the item we wanted + to replace there has already been replaced) + */ + return FALSE; } static int subq_sj_candidate_cmp(Item_in_subselect* el1, Item_in_subselect* el2, -- cgit v1.2.1 From 6c27730adb3568a986b31716875cac48bf40b148 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 3 Nov 2011 13:00:25 +0100 Subject: rename binlog_dbug_fsync_sleep -> debug_binlog_fsync_sleep --- sql/log.cc | 19 +------------------ sql/mysql_priv.h | 2 +- sql/mysqld.cc | 8 +++++++- sql/set_var.cc | 3 +++ 4 files changed, 12 insertions(+), 20 deletions(-) (limited to 'sql') diff --git a/sql/log.cc b/sql/log.cc index 332cdce803e..e99493945bf 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -70,7 +70,7 @@ static LEX_STRING const write_error_msg= static my_bool opt_optimize_thread_scheduling= TRUE; ulong binlog_checksum_options; #ifndef DBUG_OFF -static ulong opt_binlog_dbug_fsync_sleep= 0; +ulong opt_binlog_dbug_fsync_sleep= 0; #endif static my_bool mutexes_inited; @@ -7067,27 +7067,10 @@ static MYSQL_SYSVAR_ENUM( BINLOG_CHECKSUM_ALG_OFF, &binlog_checksum_typelib); -#ifndef DBUG_OFF -static MYSQL_SYSVAR_ULONG( - dbug_fsync_sleep, - opt_binlog_dbug_fsync_sleep, - PLUGIN_VAR_RQCMDARG, - "Extra sleep (in microseconds) to add to binlog fsync(), for debugging", - NULL, - NULL, - 0, - 0, - ULONG_MAX, - 0); -#endif - static struct st_mysql_sys_var *binlog_sys_vars[]= { MYSQL_SYSVAR(optimize_thread_scheduling), MYSQL_SYSVAR(checksum), -#ifndef DBUG_OFF - MYSQL_SYSVAR(dbug_fsync_sleep), -#endif NULL }; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index ac8b3663647..e335406493c 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -2163,7 +2163,7 @@ extern ulong query_buff_size; extern ulong max_prepared_stmt_count, prepared_stmt_count; extern ulong binlog_cache_size, open_files_limit; extern ulonglong max_binlog_cache_size; -extern ulong max_binlog_size, max_relay_log_size; +extern ulong max_binlog_size, max_relay_log_size, opt_binlog_dbug_fsync_sleep; extern ulong opt_binlog_rows_event_max_size; extern my_bool opt_master_verify_checksum; extern my_bool opt_slave_sql_verify_checksum; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 854bc0b0c78..c0afe0e3717 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -6015,7 +6015,7 @@ enum options_mysqld OPT_SQL_BIN_UPDATE_SAME, OPT_REPLICATE_DO_DB, OPT_REPLICATE_IGNORE_DB, OPT_LOG_SLAVE_UPDATES, OPT_BINLOG_DO_DB, OPT_BINLOG_IGNORE_DB, - OPT_BINLOG_FORMAT, + OPT_BINLOG_FORMAT, OPT_DEBUG_BINLOG_FSYNC_SLEEP, OPT_BINLOG_ANNOTATE_ROWS_EVENTS, OPT_REPLICATE_ANNOTATE_ROWS_EVENTS, #ifndef DBUG_OFF @@ -6241,6 +6241,12 @@ struct my_option my_long_options[] = {"bind-address", OPT_BIND_ADDRESS, "IP address to bind to.", &my_bind_addr_str, &my_bind_addr_str, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#ifndef DBUG_OFF + {"debug-binlog-fsync-sleep", OPT_DEBUG_BINLOG_FSYNC_SLEEP, + "Extra sleep (in microseconds) to add to binlog fsync(), for debugging", + &opt_binlog_dbug_fsync_sleep, &opt_binlog_dbug_fsync_sleep, + 0, GET_ULONG, REQUIRED_ARG, 0, 0, ULONG_MAX, 0, 1, 0}, +#endif {"binlog_format", OPT_BINLOG_FORMAT, "Does not have any effect without '--log-bin'. " "Tell the master the form of binary logging to use: either 'row' for " diff --git a/sql/set_var.cc b/sql/set_var.cc index 289e82f20ee..ed6bc60e3df 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -265,6 +265,9 @@ static sys_var_thd_ulong sys_deadlock_timeout_long(&vars, &SV::wt_timeout_long); #ifndef DBUG_OFF static sys_var_thd_dbug sys_dbug(&vars, "debug"); +static sys_var_long_ptr sys_var_debug_binlog_fsync_sleep(&vars, + "debug_binlog_fsync_sleep", + &opt_binlog_dbug_fsync_sleep); #endif static sys_var_enum sys_delay_key_write(&vars, "delay_key_write", &delay_key_write_options, -- cgit v1.2.1 From ff92a3af8bf262447e5ccee3e6929c4088ccbedc Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Fri, 4 Nov 2011 12:04:12 +0200 Subject: Fixed that test doesn't abort if 'var' points to a deleted directory (common case when using --mem) Better error message if --log-bin is used without --log-bin-index mysql-test/lib/v1/mysql-test-run.pl: Fixed that test doesn't abort if 'var' points to a deleted directory (common case when using --mem) sql/mysqld.cc: Better error message if --log-bin is used without --log-bin-index --- sql/mysqld.cc | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'sql') diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c0afe0e3717..1d8a352b97e 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -707,6 +707,7 @@ char mysql_real_data_home[FN_REFLEN], language[FN_REFLEN], reg_ext[FN_EXTLEN], mysql_charsets_dir[FN_REFLEN], *opt_init_file, *opt_tc_log_file, def_ft_boolean_syntax[sizeof(ft_boolean_syntax)]; +const char *opt_basename; char mysql_unpacked_real_data_home[FN_REFLEN]; int mysql_unpacked_real_data_home_len; uint reg_ext_length; @@ -3509,7 +3510,6 @@ static int init_common_variables(const char *conf_file_name, int argc, char **argv, const char **groups) { char buff[FN_REFLEN], *s; - const char *basename; umask(((~my_umask) & 0666)); tzset(); // Set tzname @@ -3569,13 +3569,13 @@ static int init_common_variables(const char *conf_file_name, int argc, strmake(glob_hostname, STRING_WITH_LEN("localhost")); sql_print_warning("gethostname failed, using '%s' as hostname", glob_hostname); - basename= "mysql"; + opt_basename= "mysql"; } else { - basename= glob_hostname; + opt_basename= glob_hostname; } - strmake(pidfile_name, basename, sizeof(pidfile_name)-5); + strmake(pidfile_name, opt_basename, sizeof(pidfile_name)-5); strmov(fn_ext(pidfile_name),".pid"); // Add proper extension /* @@ -4305,11 +4305,13 @@ a file name for --log-bin-index option", opt_binlog_index_name); require a name. But as we don't want to break many existing setups, we only give warning, not error. */ - sql_print_warning("No argument was provided to --log-bin, and " - "--log-bin-index was not used; so replication " - "may break when this MySQL server acts as a " - "master and has his hostname changed!! Please " - "use '--log-bin=%s' to avoid this problem.", ln); + sql_print_warning("No argument was provided to --log-bin and " + "neither --log-basename or --log-bin-index where " + "used; This may cause repliction to break when this " + "server acts as a master and has its hostname " + "changed!! Please use '--log-basename=%s' or " + "'--log-bin=%s' to avoid this problem.", + opt_basename, ln); } if (ln == buf) { -- cgit v1.2.1 From 928e94fb98b34e511d89c1ca38e35b42656c9313 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 4 Nov 2011 05:39:45 -0700 Subject: Fixed LP bug #885168. The call of the virtual function cancel_pushed_idx_cond in the code of the function test_if_skip_sort_order was misplaced when backporting the fix for bug 58816. --- sql/handler.h | 2 +- sql/sql_select.cc | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'sql') diff --git a/sql/handler.h b/sql/handler.h index 1f45b1a1e7d..66320c7e98f 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1666,7 +1666,7 @@ public: handler(handlerton *ht_arg, TABLE_SHARE *share_arg) :table_share(share_arg), table(0), estimation_rows_to_insert(0), ht(ht_arg), - ref(0), in_range_check_pushed_down(FALSE), + ref(0), end_range(NULL), in_range_check_pushed_down(FALSE), key_used_on_scan(MAX_KEY), active_index(MAX_KEY), ref_length(sizeof(my_off_t)), ft_handler(0), inited(NONE), diff --git a/sql/sql_select.cc b/sql/sql_select.cc index e6fbe3dd4de..775a32d6e7c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -17932,8 +17932,10 @@ check_reverse_order: condition are not relevant anymore */ if (tab->select && tab->select->pre_idx_push_select_cond) + { tab->set_cond(tab->select->pre_idx_push_select_cond); - + tab->table->file->cancel_pushed_idx_cond(); + } /* TODO: update the number of records in join->best_positions[tablenr] */ @@ -17994,6 +17996,9 @@ skipped_filesort: } if (orig_cond_saved && !changed_key) tab->set_cond(orig_cond); + if (!no_changes && changed_key && table->file->pushed_idx_cond) + table->file->cancel_pushed_idx_cond(); + DBUG_RETURN(1); use_filesort: @@ -18004,10 +18009,7 @@ use_filesort: select->quick= save_quick; } if (orig_cond_saved) - { - tab->table->file->cancel_pushed_idx_cond(); tab->set_cond(orig_cond); - } DBUG_RETURN(0); } -- cgit v1.2.1 From e0c1b3f24246d22e6785315f9a8448bd9a590422 Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Sun, 6 Nov 2011 01:23:03 -0700 Subject: Fixed LP bug #886145. The bug happened because in some cases the function JOIN::exec did not save the value of TABLE::pre_idx_push_select_cond in TABLE::select->pre_idx_push_select_cond for the sort table. Noticed and fixed a bug in the function make_cond_remainder that builds the remainder condition after extraction of an index pushdown condition from the where condition. The code erroneously assumed that the function make_cond_for_table left the value of ICP_COND_USES_INDEX_ONLY in sub-condition markers. Adjusted many result files from the regression test suite after this fix . --- sql/opt_index_cond_pushdown.cc | 24 ++++++++++++++++-------- sql/sql_select.cc | 4 +++- 2 files changed, 19 insertions(+), 9 deletions(-) (limited to 'sql') diff --git a/sql/opt_index_cond_pushdown.cc b/sql/opt_index_cond_pushdown.cc index d63cc200493..3a9c813b93c 100644 --- a/sql/opt_index_cond_pushdown.cc +++ b/sql/opt_index_cond_pushdown.cc @@ -239,11 +239,9 @@ Item *make_cond_for_index(Item *cond, TABLE *table, uint keyno, } -Item *make_cond_remainder(Item *cond, bool exclude_index) +Item *make_cond_remainder(Item *cond, TABLE *table, uint keyno, + bool other_tbls_ok, bool exclude_index) { - if (exclude_index && cond->marker == ICP_COND_USES_INDEX_ONLY) - return 0; /* Already checked */ - if (cond->type() == Item::COND_ITEM) { table_map tbl_map= 0; @@ -257,7 +255,8 @@ Item *make_cond_remainder(Item *cond, bool exclude_index) Item *item; while ((item=li++)) { - Item *fix= make_cond_remainder(item, exclude_index); + Item *fix= make_cond_remainder(item, table, keyno, + other_tbls_ok, exclude_index); if (fix) { new_cond->argument_list()->push_back(fix); @@ -284,7 +283,8 @@ Item *make_cond_remainder(Item *cond, bool exclude_index) Item *item; while ((item=li++)) { - Item *fix= make_cond_remainder(item, FALSE); + Item *fix= make_cond_remainder(item, table, keyno, + other_tbls_ok, FALSE); if (!fix) return (COND*) 0; new_cond->argument_list()->push_back(fix); @@ -296,7 +296,14 @@ Item *make_cond_remainder(Item *cond, bool exclude_index) return new_cond; } } - return cond; + else + { + if (exclude_index && + uses_index_fields_only(cond, table, keyno, other_tbls_ok)) + return 0; + else + return cond; + } } @@ -368,7 +375,8 @@ void push_index_cond(JOIN_TAB *tab, uint keyno) tab->ref.disable_cache= TRUE; Item *row_cond= tab->idx_cond_fact_out ? - make_cond_remainder(tab->select_cond, TRUE) : + make_cond_remainder(tab->select_cond, tab->table, keyno, + tab->icp_other_tables_ok, TRUE) : tab->pre_idx_push_select_cond; DBUG_EXECUTE("where", diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 775a32d6e7c..19a29765066 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2565,7 +2565,9 @@ JOIN::exec() if (curr_table->pre_idx_push_select_cond && !curr_table->pre_idx_push_select_cond->fixed) curr_table->pre_idx_push_select_cond->fix_fields(thd, 0); - + + curr_table->select->pre_idx_push_select_cond= + curr_table->pre_idx_push_select_cond; curr_table->set_select_cond(curr_table->select->cond, __LINE__); curr_table->select_cond->top_level_item(); DBUG_EXECUTE("where",print_where(curr_table->select->cond, -- cgit v1.2.1 From 7c7611d728d393dcc39bfd73ab5b3d56491ff9e7 Mon Sep 17 00:00:00 2001 From: Sergey Petrunya Date: Mon, 7 Nov 2011 23:30:03 +0400 Subject: BUG#887026: Wrong result with ICP, outer join, subquery in maria-5.3-icp - Do not push index condition if we're using a triggered ref access. --- sql/sql_select.cc | 41 +++++++++++++++++++++++++---------------- sql/sql_select.h | 1 + 2 files changed, 26 insertions(+), 16 deletions(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 19a29765066..5e1992b8816 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -9101,11 +9101,9 @@ uint check_join_cache_usage(JOIN_TAB *tab, case JT_EQ_REF: if (cache_level <=2 || (no_hashed_cache && no_bka_cache)) goto no_join_cache; - for (uint i= 0; i < tab->ref.key_parts; i++) - { - if (tab->ref.cond_guards[i]) - goto no_join_cache; - } + if (tab->ref.is_access_triggered()) + goto no_join_cache; + if (!tab->is_ref_for_hash_join()) { flags= HA_MRR_NO_NULL_ENDPOINTS | HA_MRR_SINGLE_POINT; @@ -9396,9 +9394,9 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) table->key_read=1; table->file->extra(HA_EXTRA_KEYREAD); } - else if (!jcl || jcl > 4) + else if ((!jcl || jcl > 4) && !tab->ref.is_access_triggered()) push_index_cond(tab, tab->ref.key); - break; + break; case JT_EQ_REF: tab->read_record.unlock_row= join_read_key_unlock_row; /* fall through */ @@ -9408,7 +9406,7 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) table->key_read=1; table->file->extra(HA_EXTRA_KEYREAD); } - else if (!jcl || jcl > 4) + else if ((!jcl || jcl > 4) && !tab->ref.is_access_triggered()) push_index_cond(tab, tab->ref.key); break; case JT_REF_OR_NULL: @@ -9423,7 +9421,7 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) if (table->covering_keys.is_set(tab->ref.key) && !table->no_keyread) table->enable_keyread(); - else if (!jcl || jcl > 4) + else if ((!jcl || jcl > 4) && !tab->ref.is_access_triggered()) push_index_cond(tab, tab->ref.key); break; case JT_ALL: @@ -9780,6 +9778,22 @@ bool TABLE_REF::tmp_table_index_lookup_init(THD *thd, } +/* + Check if ref access uses "Full scan on NULL key" (i.e. it actually alternates + between ref access and full table scan) +*/ + +bool TABLE_REF::is_access_triggered() +{ + for (uint i = 0; i < key_parts; i++) + { + if (cond_guards[i]) + return TRUE; + } + return FALSE; +} + + /** Partially cleanup JOIN after it has executed: close index or rnd read (table cursors), free quick selects. @@ -16656,13 +16670,8 @@ bool test_if_ref(Item *root_cond, Item_field *left_item,Item *right_item) between ref access and full table scan), then no equality can be guaranteed to be true. */ - for (uint i = 0; i < join_tab->ref.key_parts; i++) - { - if (join_tab->ref.cond_guards[i]) - { - return FALSE; - } - } + if (join_tab->ref.is_access_triggered()) + return FALSE; Item *ref_item=part_of_refkey(field->table,field); if (ref_item && (ref_item->eq(right_item,1) || diff --git a/sql/sql_select.h b/sql/sql_select.h index 0a416966995..5476ef9b46c 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -136,6 +136,7 @@ typedef struct st_table_ref bool tmp_table_index_lookup_init(THD *thd, KEY *tmp_key, Item_iterator &it, bool value, uint skip= 0); + bool is_access_triggered(); } TABLE_REF; -- cgit v1.2.1 From 04f3ecf632816a940e2838949e0216d638436fd8 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 8 Nov 2011 23:07:19 +0100 Subject: typos fixed (thanks viva64.com) --- sql/records.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/records.cc b/sql/records.cc index 827450201c9..a3aca6a14ac 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -621,7 +621,7 @@ static int rr_cmp(uchar *a,uchar *b) if (a[4] != b[4]) return (int) a[4] - (int) b[4]; if (a[5] != b[5]) - return (int) a[1] - (int) b[5]; + return (int) a[5] - (int) b[5]; if (a[6] != b[6]) return (int) a[6] - (int) b[6]; return (int) a[7] - (int) b[7]; -- cgit v1.2.1 From b91a6bd88b7ae0db674b60e9b0415f3656b7471d Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Fri, 11 Nov 2011 14:53:26 -0800 Subject: Fixed LP bug #879871. The function add_ref_to_table_cond missed updating the value of join_tab->pre_idx_push_select_cond after having updated the value of join_tab->select->pre_idx_push_select_cond. --- sql/sql_select.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 5e1992b8816..b9088a7ce60 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -19969,7 +19969,8 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) Item *new_cond= and_conds(cond_copy, join_tab->select->pre_idx_push_select_cond); if (!new_cond->fixed && new_cond->fix_fields(thd, &new_cond)) error= 1; - join_tab->select->pre_idx_push_select_cond= new_cond; + join_tab->pre_idx_push_select_cond= + join_tab->select->pre_idx_push_select_cond= new_cond; } join_tab->set_select_cond(cond, __LINE__); } -- cgit v1.2.1 From 1d721d0106db6636170cdf94070a57f40d44a316 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 12 Nov 2011 11:29:12 +0200 Subject: Fix MySQL BUG#12329653 In MariaDB, when running in ONLY_FULL_GROUP_BY mode, the server produced in incorrect error message that there is an aggregate function without GROUP BY, for artificially created MIN/MAX functions during subquery MIN/MAX optimization. The fix introduces a way to distinguish between artifially created MIN/MAX functions as a result of a rewrite, and normal ones present in the query. The test for ONLY_FULL_GROUP_BY violation now tests in addition if a MIN/MAX function was part of a MIN/MAX subquery rewrite. In order to be able to distinguish these MIN/MAX functions, the patch introduces an additional flag in Item_in_subselect::in_strategy - SUBS_STRATEGY_CHOSEN. This flag is set when the optimizer makes its final choice of a subuqery strategy. In order to make the choice consistent, access to Item_in_subselect::in_strategy is provided via new class methods. ****** Fix MySQL BUG#12329653 In MariaDB, when running in ONLY_FULL_GROUP_BY mode, the server produced in incorrect error message that there is an aggregate function without GROUP BY, for artificially created MIN/MAX functions during subquery MIN/MAX optimization. The fix introduces a way to distinguish between artifially created MIN/MAX functions as a result of a rewrite, and normal ones present in the query. The test for ONLY_FULL_GROUP_BY violation now tests in addition if a MIN/MAX function was part of a MIN/MAX subquery rewrite. In order to be able to distinguish these MIN/MAX functions, the patch introduces an additional flag in Item_in_subselect::in_strategy - SUBS_STRATEGY_CHOSEN. This flag is set when the optimizer makes its final choice of a subuqery strategy. In order to make the choice consistent, access to Item_in_subselect::in_strategy is provided via new class methods. --- sql/item_subselect.cc | 31 +++++++++---------- sql/item_subselect.h | 85 ++++++++++++++++++++++++++++++++++++++++++--------- sql/opt_subselect.cc | 56 ++++++++++++++++----------------- sql/sql_base.cc | 4 +-- sql/sql_lex.cc | 4 +-- sql/sql_select.cc | 8 +++-- 6 files changed, 122 insertions(+), 66 deletions(-) (limited to 'sql') diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 64bcd2ad2b0..6134903fee6 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -158,9 +158,11 @@ void Item_in_subselect::cleanup() delete left_expr_cache; left_expr_cache= NULL; } + /* + TODO: This breaks the commented assert in add_strategy(). + in_strategy&= ~SUBS_STRATEGY_CHOSEN; + */ first_execution= TRUE; - if (in_strategy & SUBS_MATERIALIZATION) - in_strategy= 0; pushed_cond_guards= NULL; Item_subselect::cleanup(); DBUG_VOID_RETURN; @@ -176,10 +178,9 @@ void Item_allany_subselect::cleanup() */ for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) - if (in_strategy & SUBS_MAXMIN_INJECTED) + if (test_strategy(SUBS_MAXMIN_INJECTED)) sl->with_sum_func= false; Item_in_subselect::cleanup(); - } @@ -722,7 +723,7 @@ bool Item_in_subselect::exec() - on a cost-based basis, that takes into account the cost of a cache lookup, the cache hit rate, and the savings per cache hit. */ - if (!left_expr_cache && (in_strategy & SUBS_MATERIALIZATION)) + if (!left_expr_cache && (test_strategy(SUBS_MATERIALIZATION))) init_left_expr_cache(); /* @@ -1186,8 +1187,8 @@ bool Item_in_subselect::test_limit(st_select_lex_unit *unit_arg) Item_in_subselect::Item_in_subselect(Item * left_exp, st_select_lex *select_lex): Item_exists_subselect(), - left_expr_cache(0), first_execution(TRUE), - optimizer(0), pushed_cond_guards(NULL), emb_on_expr_nest(NULL), in_strategy(0), + left_expr_cache(0), first_execution(TRUE), in_strategy(SUBS_NOT_TRANSFORMED), + optimizer(0), pushed_cond_guards(NULL), emb_on_expr_nest(NULL), is_jtbm_merged(FALSE), is_flattenable_semijoin(FALSE), is_registered_semijoin(FALSE), upper_item(0) @@ -1608,7 +1609,7 @@ Item_in_subselect::single_value_transformer(JOIN *join) bool Item_allany_subselect::transform_into_max_min(JOIN *join) { DBUG_ENTER("Item_allany_subselect::transform_into_max_min"); - if (!(in_strategy & (SUBS_MAXMIN_INJECTED | SUBS_MAXMIN_ENGINE))) + if (!test_strategy(SUBS_MAXMIN_INJECTED | SUBS_MAXMIN_ENGINE)) DBUG_RETURN(false); Item **place= optimizer->arguments() + 1; THD *thd= join->thd; @@ -1682,7 +1683,7 @@ bool Item_allany_subselect::transform_into_max_min(JOIN *join) Remove other strategies if any (we already changed the query and can't apply other strategy). */ - in_strategy= SUBS_MAXMIN_INJECTED; + set_strategy(SUBS_MAXMIN_INJECTED); } else { @@ -1694,7 +1695,7 @@ bool Item_allany_subselect::transform_into_max_min(JOIN *join) Remove other strategies if any (we already changed the query and can't apply other strategy). */ - in_strategy= SUBS_MAXMIN_ENGINE; + set_strategy(SUBS_MAXMIN_ENGINE); } /* The swap is needed for expressions of type 'f1 < ALL ( SELECT ....)' @@ -2414,7 +2415,7 @@ err: void Item_in_subselect::print(String *str, enum_query_type query_type) { - if (in_strategy & SUBS_IN_TO_EXISTS) + if (test_strategy(SUBS_IN_TO_EXISTS)) str->append(STRING_WITH_LEN("")); else { @@ -2430,8 +2431,7 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref) uint outer_cols_num; List *inner_cols; - - if (in_strategy & SUBS_SEMI_JOIN) + if (test_strategy(SUBS_SEMI_JOIN)) return !( (*ref)= new Item_int(1)); /* @@ -2607,8 +2607,7 @@ Item_allany_subselect::select_transformer(JOIN *join) { DBUG_ENTER("Item_allany_subselect::select_transformer"); DBUG_ASSERT((in_strategy & ~(SUBS_MAXMIN_INJECTED | SUBS_MAXMIN_ENGINE | - SUBS_IN_TO_EXISTS)) == 0); - in_strategy|= SUBS_IN_TO_EXISTS; + SUBS_IN_TO_EXISTS | SUBS_STRATEGY_CHOSEN)) == 0); if (upper_item) upper_item->show= 1; DBUG_RETURN(select_in_like_transformer(join)); @@ -2617,7 +2616,7 @@ Item_allany_subselect::select_transformer(JOIN *join) void Item_allany_subselect::print(String *str, enum_query_type query_type) { - if (in_strategy & SUBS_IN_TO_EXISTS) + if (test_strategy(SUBS_IN_TO_EXISTS)) str->append(STRING_WITH_LEN("")); else { diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 85ce7745751..0ec0969e0ae 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -353,17 +353,19 @@ TABLE_LIST * const NO_JOIN_NEST=(TABLE_LIST*)0x1; based on user-set optimizer switches, semantic analysis and cost comparison. */ #define SUBS_NOT_TRANSFORMED 0 /* No execution method was chosen for this IN. */ -#define SUBS_SEMI_JOIN 1 /* IN was converted to semi-join. */ -#define SUBS_IN_TO_EXISTS 2 /* IN was converted to correlated EXISTS. */ -#define SUBS_MATERIALIZATION 4 /* Execute IN via subquery materialization. */ +/* The Final decision about the strategy is made. */ +#define SUBS_STRATEGY_CHOSEN 1 +#define SUBS_SEMI_JOIN 2 /* IN was converted to semi-join. */ +#define SUBS_IN_TO_EXISTS 4 /* IN was converted to correlated EXISTS. */ +#define SUBS_MATERIALIZATION 8 /* Execute IN via subquery materialization. */ /* Partial matching substrategies of MATERIALIZATION. */ -#define SUBS_PARTIAL_MATCH_ROWID_MERGE 8 -#define SUBS_PARTIAL_MATCH_TABLE_SCAN 16 +#define SUBS_PARTIAL_MATCH_ROWID_MERGE 16 +#define SUBS_PARTIAL_MATCH_TABLE_SCAN 32 /* ALL/ANY will be transformed with max/min optimization */ /* The subquery has not aggregates, transform it into a MAX/MIN query. */ -#define SUBS_MAXMIN_INJECTED 32 +#define SUBS_MAXMIN_INJECTED 64 /* The subquery has aggregates, use a special max/min subselect engine. */ -#define SUBS_MAXMIN_ENGINE 64 +#define SUBS_MAXMIN_ENGINE 128 /** @@ -398,6 +400,8 @@ protected: Item *expr; bool was_null; bool abort_on_null; + /* A bitmap of possible execution strategies for an IN predicate. */ + uchar in_strategy; public: Item_in_optimizer *optimizer; protected: @@ -442,11 +446,7 @@ public: */ bool sjm_scan_allowed; double jtbm_read_time; - double jtbm_record_count; - - /* A bitmap of possible execution strategies for an IN predicate. */ - uchar in_strategy; - + double jtbm_record_count; bool is_jtbm_merged; /* @@ -487,9 +487,8 @@ public: Item_in_subselect(Item * left_expr, st_select_lex *select_lex); Item_in_subselect() :Item_exists_subselect(), left_expr_cache(0), first_execution(TRUE), - abort_on_null(0), optimizer(0), + abort_on_null(0), in_strategy(SUBS_NOT_TRANSFORMED), optimizer(0), pushed_cond_guards(NULL), func(NULL), emb_on_expr_nest(NULL), - in_strategy(SUBS_NOT_TRANSFORMED), is_jtbm_merged(FALSE), upper_item(0) {} @@ -539,6 +538,64 @@ public: emb_on_expr_nest= embedding; } + bool test_strategy(uchar strategy) + { return test(in_strategy & strategy); } + + /** + Test that the IN strategy was chosen for execution. This is so + when the CHOSEN flag is ON, and there is no other strategy. + */ + bool test_set_strategy(uchar strategy) + { + DBUG_ASSERT(strategy == SUBS_SEMI_JOIN || + strategy == SUBS_IN_TO_EXISTS || + strategy == SUBS_MATERIALIZATION || + strategy == SUBS_PARTIAL_MATCH_ROWID_MERGE || + strategy == SUBS_PARTIAL_MATCH_TABLE_SCAN || + strategy == SUBS_MAXMIN_INJECTED || + strategy == SUBS_MAXMIN_ENGINE); + return ((in_strategy & SUBS_STRATEGY_CHOSEN) && + (in_strategy & ~SUBS_STRATEGY_CHOSEN) == strategy); + } + + bool is_set_strategy() + { return test(in_strategy & SUBS_STRATEGY_CHOSEN); } + + bool has_strategy() + { return in_strategy != SUBS_NOT_TRANSFORMED; } + + void add_strategy (uchar strategy) + { + DBUG_ASSERT(strategy != SUBS_NOT_TRANSFORMED); + DBUG_ASSERT(!(strategy & SUBS_STRATEGY_CHOSEN)); + /* + TODO: PS re-execution breaks this condition, because + check_and_do_in_subquery_rewrites() is called for each reexecution + and re-adds the same strategies. + DBUG_ASSERT(!(in_strategy & SUBS_STRATEGY_CHOSEN)); + */ + in_strategy|= strategy; + } + + void reset_strategy(uchar strategy) + { + DBUG_ASSERT(strategy != SUBS_NOT_TRANSFORMED); + in_strategy= strategy; + } + + void set_strategy(uchar strategy) + { + /* Check that only one strategy is set for execution. */ + DBUG_ASSERT(strategy == SUBS_SEMI_JOIN || + strategy == SUBS_IN_TO_EXISTS || + strategy == SUBS_MATERIALIZATION || + strategy == SUBS_PARTIAL_MATCH_ROWID_MERGE || + strategy == SUBS_PARTIAL_MATCH_TABLE_SCAN || + strategy == SUBS_MAXMIN_INJECTED || + strategy == SUBS_MAXMIN_ENGINE); + in_strategy= (SUBS_STRATEGY_CHOSEN | strategy); + } + friend class Item_ref_null_helper; friend class Item_is_not_null_test; friend class Item_in_optimizer; diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index aa0a8152bbb..56815a624e2 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -411,7 +411,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join) in_subs->emb_on_expr_nest && // 5 select_lex->outer_select()->join && // 6 parent_unit->first_select()->leaf_tables.elements && // 7 - !in_subs->in_strategy && // 8 + !in_subs->has_strategy() && // 8 select_lex->outer_select()->leaf_tables.elements && // 9 !((join->select_options | // 10 select_lex->outer_select()->join->select_options) // 10 @@ -451,7 +451,7 @@ int check_and_do_in_subquery_rewrites(JOIN *join) { if (is_materialization_applicable(thd, in_subs, select_lex)) { - in_subs->in_strategy|= SUBS_MATERIALIZATION; + in_subs->add_strategy(SUBS_MATERIALIZATION); /* If the subquery is an AND-part of WHERE register for being processed @@ -479,17 +479,18 @@ int check_and_do_in_subquery_rewrites(JOIN *join) possible. */ if (optimizer_flag(thd, OPTIMIZER_SWITCH_IN_TO_EXISTS) || - !in_subs->in_strategy) - { - in_subs->in_strategy|= SUBS_IN_TO_EXISTS; - } + !in_subs->has_strategy()) + in_subs->add_strategy(SUBS_IN_TO_EXISTS); } /* Check if max/min optimization applicable */ - if (allany_subs) - allany_subs->in_strategy|= (allany_subs->is_maxmin_applicable(join) ? - (SUBS_MAXMIN_INJECTED | SUBS_MAXMIN_ENGINE) : - SUBS_IN_TO_EXISTS); + if (allany_subs && !allany_subs->is_set_strategy()) + { + uchar strategy= (allany_subs->is_maxmin_applicable(join) ? + (SUBS_MAXMIN_INJECTED | SUBS_MAXMIN_ENGINE) : + SUBS_IN_TO_EXISTS); + allany_subs->add_strategy(strategy); + } /* Transform each subquery predicate according to its overloaded @@ -932,16 +933,12 @@ bool convert_join_subqueries_to_semijoins(JOIN *join) /* Revert to the IN->EXISTS strategy in the rare case when the subquery could not be flattened. - TODO: This is a limitation done for simplicity. Such subqueries could also - be executed via materialization. In order to determine this, we should - re-run the test for materialization that was done in - check_and_do_in_subquery_rewrites. */ - in_subq->in_strategy= SUBS_IN_TO_EXISTS; + in_subq->reset_strategy(SUBS_IN_TO_EXISTS); if (is_materialization_applicable(thd, in_subq, in_subq->unit->first_select())) { - in_subq->in_strategy|= SUBS_MATERIALIZATION; + in_subq->add_strategy(SUBS_MATERIALIZATION); } in_subq= li++; @@ -1260,7 +1257,7 @@ static bool convert_subq_to_sj(JOIN *parent_join, Item_in_subselect *subq_pred) /* 3. Remove the original subquery predicate from the WHERE/ON */ // The subqueries were replaced for Item_int(1) earlier - subq_pred->in_strategy= SUBS_SEMI_JOIN; // for subsequent executions + subq_pred->reset_strategy(SUBS_SEMI_JOIN); // for subsequent executions /*TODO: also reset the 'with_subselect' there. */ /* n. Adjust the parent_join->table_count counter */ @@ -1434,7 +1431,7 @@ static bool convert_subq_to_jtbm(JOIN *parent_join, double read_time; DBUG_ENTER("convert_subq_to_jtbm"); - subq_pred->in_strategy &= ~SUBS_IN_TO_EXISTS; + subq_pred->set_strategy(SUBS_MATERIALIZATION); if (subq_pred->optimize(&rows, &read_time)) DBUG_RETURN(TRUE); @@ -4514,8 +4511,8 @@ bool JOIN::choose_subquery_plan(table_map join_tables) } else return false; - - DBUG_ASSERT(in_subs->in_strategy); /* A strategy must be chosen earlier. */ + /* A strategy must be chosen earlier. */ + DBUG_ASSERT(in_subs->has_strategy()); DBUG_ASSERT(in_to_exists_where || in_to_exists_having); DBUG_ASSERT(!in_to_exists_where || in_to_exists_where->fixed); DBUG_ASSERT(!in_to_exists_having || in_to_exists_having->fixed); @@ -4525,8 +4522,8 @@ bool JOIN::choose_subquery_plan(table_map join_tables) strategies are possible and allowed by the user (checked during the prepare phase. */ - if (in_subs->in_strategy & SUBS_MATERIALIZATION && - in_subs->in_strategy & SUBS_IN_TO_EXISTS) + if (in_subs->test_strategy(SUBS_MATERIALIZATION) && + in_subs->test_strategy(SUBS_IN_TO_EXISTS)) { JOIN *outer_join; JOIN *inner_join= this; @@ -4630,9 +4627,9 @@ bool JOIN::choose_subquery_plan(table_map join_tables) /* C.3 Compare the costs and choose the cheaper strategy. */ if (materialize_strategy_cost >= in_exists_strategy_cost) - in_subs->in_strategy&= ~SUBS_MATERIALIZATION; + in_subs->set_strategy(SUBS_IN_TO_EXISTS); else - in_subs->in_strategy&= ~SUBS_IN_TO_EXISTS; + in_subs->set_strategy(SUBS_MATERIALIZATION); DBUG_PRINT("info", ("mat_strategy_cost: %.2f, mat_cost: %.2f, write_cost: %.2f, lookup_cost: %.2f", @@ -4653,7 +4650,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables) otherwise use materialization. */ - if (in_subs->in_strategy & SUBS_MATERIALIZATION && + if (in_subs->test_strategy(SUBS_MATERIALIZATION) && in_subs->setup_mat_engine()) { /* @@ -4661,11 +4658,10 @@ bool JOIN::choose_subquery_plan(table_map join_tables) but it is not possible to execute it due to limitations in the implementation, fall back to IN-TO-EXISTS. */ - in_subs->in_strategy&= ~SUBS_MATERIALIZATION; - in_subs->in_strategy|= SUBS_IN_TO_EXISTS; + in_subs->set_strategy(SUBS_IN_TO_EXISTS); } - if (in_subs->in_strategy & SUBS_MATERIALIZATION) + if (in_subs->test_strategy(SUBS_MATERIALIZATION)) { /* Restore the original query plan used for materialization. */ if (reopt_result == REOPT_NEW_PLAN) @@ -4690,7 +4686,7 @@ bool JOIN::choose_subquery_plan(table_map join_tables) */ select_limit= in_subs->unit->select_limit_cnt; } - else if (in_subs->in_strategy & SUBS_IN_TO_EXISTS) + else if (in_subs->test_strategy(SUBS_IN_TO_EXISTS)) { if (reopt_result == REOPT_NONE && in_to_exists_where && const_tables != table_count) @@ -4771,7 +4767,7 @@ bool JOIN::choose_tableless_subquery_plan() { Item_in_subselect *in_subs; in_subs= (Item_in_subselect*) subs_predicate; - in_subs->in_strategy= SUBS_IN_TO_EXISTS; + in_subs->set_strategy(SUBS_IN_TO_EXISTS); if (in_subs->create_in_to_exists_cond(this) || in_subs->inject_in_to_exists_cond(this)) return TRUE; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index cacec989518..3e27355cc26 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -6453,8 +6453,8 @@ find_field_in_tables(THD *thd, Item_ident *item, { Item *subs= sl->master_unit()->item; if (subs->type() == Item::SUBSELECT_ITEM && - ((Item_subselect*)subs)->substype() == Item_subselect::IN_SUBS && - ((Item_in_subselect*)subs)->in_strategy & SUBS_SEMI_JOIN) + ((Item_subselect*)subs)->substype() == Item_subselect::IN_SUBS && + ((Item_in_subselect*)subs)->test_strategy(SUBS_SEMI_JOIN)) { continue; } diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 4a4beb496aa..117a866555c 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -3676,8 +3676,8 @@ bool st_select_lex::is_merged_child_of(st_select_lex *ancestor) { Item *subs= sl->master_unit()->item; if (subs && subs->type() == Item::SUBSELECT_ITEM && - ((Item_subselect*)subs)->substype() == Item_subselect::IN_SUBS && - ((Item_in_subselect*)subs)->in_strategy & SUBS_SEMI_JOIN) + ((Item_subselect*)subs)->substype() == Item_subselect::IN_SUBS && + ((Item_in_subselect*)subs)->test_strategy(SUBS_SEMI_JOIN)) { continue; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index b9088a7ce60..8d83b4589d5 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -689,6 +689,10 @@ JOIN::prepare(Item ***rref_pointer_array, aggregate functions with implicit grouping (there is no GROUP BY). */ if (thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && !group_list && + !(select_lex->master_unit()->item && + select_lex->master_unit()->item->is_in_predicate() && + ((Item_in_subselect*)select_lex->master_unit()->item)-> + test_set_strategy(SUBS_MAXMIN_INJECTED)) && select_lex->full_group_by_flag == (NON_AGG_FIELD_USED | SUM_FUNC_USED)) { my_message(ER_MIX_OF_GROUP_FUNC_AND_FIELDS, @@ -813,7 +817,7 @@ inject_jtbm_conds(JOIN *join, List *join_list, Item **join_where) double rows; double read_time; - subq_pred->in_strategy &= ~SUBS_IN_TO_EXISTS; + DBUG_ASSERT(subq_pred->test_set_strategy(SUBS_MATERIALIZATION)); subq_pred->optimize(&rows, &read_time); subq_pred->jtbm_read_time= read_time; @@ -3181,7 +3185,7 @@ make_join_statistics(JOIN *join, List &tables_list, */ bool skip_unprefixed_keyparts= !(join->is_in_subquery() && - ((Item_in_subselect*)join->unit->item)->in_strategy & SUBS_IN_TO_EXISTS); + ((Item_in_subselect*)join->unit->item)->test_strategy(SUBS_IN_TO_EXISTS)); if (keyuse_array->elements && sort_and_filter_keyuse(join->thd, keyuse_array, -- cgit v1.2.1 From 28b2eaa81a2209d3c268e34db0046f4dc7c8d4fe Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Sat, 12 Nov 2011 02:20:44 -0800 Subject: Fixed LP bug #823301. A bug in the code of the function key_or could lead to a situation when performing of an OR operation for one index changes the result the operation for another index. This bug is fixed with this patch. Also corrected the specification and the code of the function or_sel_tree_with_checks. --- sql/opt_range.cc | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'sql') diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 3ed975a59bb..815a6f5f316 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -1136,10 +1136,10 @@ int SEL_IMERGE::and_sel_tree(RANGE_OPT_PARAM *param, SEL_TREE *tree, 2. In the second mode, when is_first_check_pass==FALSE : 2.1. For each rt_j in the imerge that can be ored (see the function - sel_trees_can_be_ored), but not must be ored, with rt the function - replaces rt_j for a range tree such that for each index for which - ranges are defined in both in rt_j and rt the tree contains the - result of oring of these ranges. + sel_trees_can_be_ored) with rt the function replaces rt_j for a + range tree such that for each index for which ranges are defined + in both in rt_j and rt the tree contains the result of oring of + these ranges. 2.2. In other cases the function does not produce any imerge. When is_first_check==TRUE the function returns FALSE in the parameter @@ -1163,7 +1163,7 @@ int SEL_IMERGE::or_sel_tree_with_checks(RANGE_OPT_PARAM *param, bool *is_last_check_pass) { bool was_ored= FALSE; - *is_last_check_pass= TRUE; + *is_last_check_pass= is_first_check_pass; SEL_TREE** or_tree = trees; for (uint i= 0; i < n_trees; i++, or_tree++) { @@ -1174,7 +1174,7 @@ int SEL_IMERGE::or_sel_tree_with_checks(RANGE_OPT_PARAM *param, { bool must_be_ored= sel_trees_must_be_ored(param, *or_tree, tree, ored_keys); - if (must_be_ored || !is_first_check_pass) + if (must_be_ored || !is_first_check_pass) { result_keys.clear_all(); result= *or_tree; @@ -1210,22 +1210,19 @@ int SEL_IMERGE::or_sel_tree_with_checks(RANGE_OPT_PARAM *param, { if (result_keys.is_clear_all()) result->type= SEL_TREE::ALWAYS; - *is_last_check_pass= TRUE; if ((result->type == SEL_TREE::MAYBE) || (result->type == SEL_TREE::ALWAYS)) return 1; /* SEL_TREE::IMPOSSIBLE is impossible here */ result->keys_map= result_keys; *or_tree= result; - if (is_first_check_pass) - return 0; was_ored= TRUE; } } if (was_ored) return 0; - if (!*is_last_check_pass && + if (is_first_check_pass && !*is_last_check_pass && !(tree= new SEL_TREE(tree, FALSE, param))) return (-1); return or_sel_tree(param, tree); @@ -8382,9 +8379,9 @@ tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) /* Build the imerge part of the tree for the formula (1) */ SEL_TREE *rt1= tree1; SEL_TREE *rt2= tree2; - if (!no_merges1) + if (no_merges1) rt1= new SEL_TREE(tree1, TRUE, param); - if (!no_merges2) + if (no_merges2) rt2= new SEL_TREE(tree2, TRUE, param); if (!rt1 || !rt2 || result->merges.push_back(imerge_from_ranges) || @@ -9079,6 +9076,13 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2) key2: [---] tmp: [---------] */ + if (key2->use_count) + { + SEL_ARG *key2_cpy= new SEL_ARG(*key2); + if (key2_cpy) + return 0; + key2= key2_cpy; + } key2->copy_max_to_min(tmp); continue; } -- cgit v1.2.1 From f76bfc40ea697473e7f1bea211a68a22210e7b53 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 13 Nov 2011 12:02:13 +0200 Subject: Fix for LP BUG#824425: Prohibiting subqueries in rows for left part of IN/ALL/ANY Fix for walk() method of subqueries: always call the method on the subquery. --- sql/item.h | 1 + sql/item_cmpfunc.cc | 9 +++++++++ sql/item_subselect.cc | 2 +- sql/item_subselect.h | 1 + 4 files changed, 12 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/item.h b/sql/item.h index fa6918d8484..b858d8ce587 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1023,6 +1023,7 @@ public: virtual bool view_used_tables_processor(uchar *arg) { return 0; } virtual bool eval_not_null_tables(uchar *opt_arg) { return 0; } virtual bool clear_sum_processor(uchar *opt_arg) { return 0; } + virtual bool is_subquery_processor (uchar *opt_arg) { return 0; } /* To call bool function for all arguments */ struct bool_func_call_args diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index a33dd090f14..5b2c862e9eb 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1436,6 +1436,7 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref) cache->setup(args[0]); if (cache->cols() == 1) { + DBUG_ASSERT(args[0]->type() != ROW_ITEM); if ((used_tables_cache= args[0]->used_tables())) cache->set_used_tables(OUTER_REF_TABLE_BIT); else @@ -1446,6 +1447,14 @@ bool Item_in_optimizer::fix_left(THD *thd, Item **ref) uint n= cache->cols(); for (uint i= 0; i < n; i++) { + /* Check that the expression (part of row) do not contain a subquery */ + if (args[0]->element_index(i)->walk(&Item::is_subquery_processor, + FALSE, NULL)) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), + "SUBQUERY in ROW in left expression of IN/ALL/ANY"); + return 1; + } if (args[0]->element_index(i)->used_tables()) ((Item_cache *)cache->element_index(i))->set_used_tables(OUTER_REF_TABLE_BIT); else diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 6134903fee6..71408528903 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -528,7 +528,7 @@ bool Item_subselect::walk(Item_processor processor, bool walk_subquery, invalidated by irreversible cleanups (those happen after an uncorrelated subquery has been executed). */ - return FALSE; + return (this->*processor)(argument); } if (walk_subquery) diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 0ec0969e0ae..2012306c0f7 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -226,6 +226,7 @@ public: const char *func_name() const { DBUG_ASSERT(0); return "subselect"; } virtual bool expr_cache_is_needed(THD *); virtual void get_cache_parameters(List ¶meters); + virtual bool is_subquery_processor (uchar *opt_arg) { return 1; } friend class select_result_interceptor; friend class Item_in_optimizer; -- cgit v1.2.1 From 147721bbb69fef8923655482fafcf35d7a82e805 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 14 Nov 2011 19:24:36 +0200 Subject: Fix bug lp:889744 MariaDB 5.5 merges changes from MySQL 5.5 where all constant expressions are wrapped into an Item_cache. As a result, constant single-row subqueries were also wrapped in an Item_cache. When analyzing the where clause for constant expressions that can be evaluated during optimization, subqueries wrapped into an Item_cache did not appear as expensive, and were therefore evaluated during optimization. Such evaluation is against the current architecture of MariaDB 5.3 where subquries are executed during the execute phase. The patch adds the is_expensive() predicate to Item_cache. This makes Item_cache consistent with other wrapping Item classes that need to look at the properties of the wrapped object. --- sql/item.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'sql') diff --git a/sql/item.h b/sql/item.h index b858d8ce587..e05fbf3b67d 100644 --- a/sql/item.h +++ b/sql/item.h @@ -3637,6 +3637,20 @@ public: virtual void store(Item *item); virtual bool cache_value()= 0; bool is_null() { return null_value; } + virtual bool is_expensive() + { + DBUG_ASSERT(example); + if (value_cached) + return false; + return example->is_expensive(); + } + bool is_expensive_processor(uchar *arg) + { + DBUG_ASSERT(example); + if (value_cached) + return false; + return example->is_expensive_processor(arg); + } }; -- cgit v1.2.1 From b4b7d941fea8b17bd0db2d30a74df6596101b4ed Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Tue, 15 Nov 2011 13:03:00 -0800 Subject: Fixed LP bug #889750. If the optimizer switch 'semijoin_with_cache' is set to 'off' then join cache cannot be used to join inner tables of a semijoin. Also fixed a bug in the function check_join_cache_usage() that led to wrong output of the EXPLAIN commands for some test cases. --- sql/sql_select.cc | 5 ++++- sql/sql_select.h | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 8d83b4589d5..c25d96ddef1 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -9025,7 +9025,7 @@ uint check_join_cache_usage(JOIN_TAB *tab, if (tab->inside_loosescan_range) goto no_join_cache; - if (tab->is_inner_table_of_semi_join_with_first_match() && + if (tab->is_inner_table_of_semijoin() && !join->allowed_semijoin_with_cache) goto no_join_cache; if (tab->is_inner_table_of_outer_join() && @@ -9172,7 +9172,10 @@ uint check_join_cache_usage(JOIN_TAB *tab, no_join_cache: if (tab->type != JT_ALL && tab->is_ref_for_hash_join()) + { tab->type= JT_ALL; + tab->ref.key_parts= 0; + } revise_cache_usage(tab); return 0; } diff --git a/sql/sql_select.h b/sql/sql_select.h index 5476ef9b46c..1d18b80d72e 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -391,6 +391,10 @@ typedef struct st_join_table { { return first_sj_inner_tab != NULL; } + bool is_inner_table_of_semijoin() + { + return emb_sj_nest != NULL; + } bool is_inner_table_of_outer_join() { return first_inner != NULL; -- cgit v1.2.1 From c05e5b9c65f76ba2d3a6844add88c03076b2cb5d Mon Sep 17 00:00:00 2001 From: Igor Babaev Date: Wed, 16 Nov 2011 06:11:25 -0800 Subject: Fixed LP bug #887479. The function setup_semijoin_dups_elimination erroneously assumed that if join_cache_level is set to 3 or 4 then the type of the access to a table cannot be JT_REF or JT_EQ_REF. This could lead to wrong query result sets. --- sql/opt_subselect.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sql') diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index 56815a624e2..a954811bdfa 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -3921,7 +3921,7 @@ int setup_semijoin_dups_elimination(JOIN *join, ulonglong options, if (j != join->const_tables && js_tab->use_quick != 2 && j <= no_jbuf_after && ((js_tab->type == JT_ALL && join_cache_level != 0) || - (join_cache_level > 4 && (tab->type == JT_REF || + (join_cache_level > 2 && (tab->type == JT_REF || tab->type == JT_EQ_REF)))) { /* Looks like we'll be using join buffer */ -- cgit v1.2.1 From 42221abaed700f6dc5d280b462755851780e8487 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 17 Nov 2011 01:25:10 +0200 Subject: Fix bug lp:869036 Apart from the fix, the patch also adds few more unrelated test cases for partial matching, and fixes few typos. Analysis: This bug uncovered that partial matching via rowid intersection didn't handle the case when: - the left IN argument has some NULLs, - there are no non-null value matches, and there is no non-null column, - the subquery columns that are not covered with the NULLs in the left IN argument contain at least one row, such that it has NULL values in all columns where the left IN operand has no NULLs. In this case there is a partial match. In addition the analysis of the related code uncovered incorrect handling of few other related cases. Solution: The solution for the bug is to check if there exists a row with NULLs in all columns other than the ones having NULL in the let IN operand. The check is implemented via checking whether the bitmaps that store NULL information in class Ordered_key have a non-empty intersection for the relevant columns. The intersection itself is implemented via the function bitmap_exists_intersection() in my_bitmap.c. --- sql/item_subselect.cc | 110 ++++++++++++++++++++++++++++++++++++++++++-------- sql/item_subselect.h | 13 ++++-- 2 files changed, 103 insertions(+), 20 deletions(-) (limited to 'sql') diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 71408528903..18374000dff 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -4849,14 +4849,14 @@ bool Ordered_key::init(MY_BITMAP *columns_to_index) Item_func_lt *fn_less_than; key_column_count= bitmap_bits_set(columns_to_index); - - // TIMOUR: check for mem allocation err, revert to scan - key_columns= (Item_field**) thd->alloc(key_column_count * sizeof(Item_field*)); compare_pred= (Item_func_lt**) thd->alloc(key_column_count * sizeof(Item_func_lt*)); + if (!key_columns || !compare_pred) + return TRUE; /* Revert to table scan partial match. */ + for (uint i= 0; i < columns_to_index->n_bits; i++) { if (!bitmap_is_set(columns_to_index, i)) @@ -5316,10 +5316,13 @@ subselect_rowid_merge_engine::init(MY_BITMAP *non_null_key_parts, merge_keys_count == 1 && non_null_key_parts)); /* Allocate buffers to hold the merged keys and the mapping between rowids and - row numbers. + row numbers. All small buffers are allocated in the runtime memroot. Big + buffers are allocated from the OS via malloc. */ if (!(merge_keys= (Ordered_key**) thd->alloc(merge_keys_count * sizeof(Ordered_key*))) || + !(null_bitmaps= (MY_BITMAP**) thd->alloc(merge_keys_count * + sizeof(MY_BITMAP*))) || !(row_num_to_rowid= (uchar*) my_malloc((size_t)(row_count * rowid_length), MYF(MY_WME)))) return TRUE; @@ -5537,6 +5540,56 @@ bool subselect_rowid_merge_engine::test_null_row(rownum_t row_num) } +/** + Test if a subset of NULL-able columns contains a row of NULLs. +*/ + +bool subselect_rowid_merge_engine:: +exists_complementing_null_row(MY_BITMAP *keys_to_complement) +{ + rownum_t highest_min_row= 0; + rownum_t lowest_max_row= UINT_MAX; + uint count_null_keys, i, j; + Ordered_key *cur_key; + + count_null_keys= keys_to_complement->n_bits - + bitmap_bits_set(keys_to_complement); + if (count_null_keys == 1) + { + /* + The caller guarantees that the complement to keys_to_complement + contains only columns with NULLs. Therefore if there is only one column, + it is guaranteed to contain NULLs. + */ + return TRUE; + } + + for (i= (non_null_key ? 1 : 0), j= 0; i < merge_keys_count; i++) + { + cur_key= merge_keys[i]; + if (bitmap_is_set(keys_to_complement, cur_key->get_keyid())) + continue; + DBUG_ASSERT(cur_key->get_null_count()); + if (cur_key->get_min_null_row() > highest_min_row) + highest_min_row= cur_key->get_min_null_row(); + if (cur_key->get_max_null_row() < lowest_max_row) + lowest_max_row= cur_key->get_max_null_row(); + null_bitmaps[j++]= cur_key->get_null_key(); + } + DBUG_ASSERT(count_null_keys == j); + + if (lowest_max_row < highest_min_row) + { + /* The intersection of NULL rows is empty. */ + return FALSE; + } + + return bitmap_exists_intersection((const MY_BITMAP**) null_bitmaps, + count_null_keys, + highest_min_row, lowest_max_row); +} + + /* @retval TRUE there is a partial match (UNKNOWN) @retval FALSE there is no match at all (FALSE) @@ -5549,7 +5602,7 @@ bool subselect_rowid_merge_engine::partial_match() Ordered_key *cur_key; rownum_t cur_row_num; uint count_nulls_in_search_key= 0; - uint max_covering_null_row_len= + uint max_null_in_any_row= ((select_materialize_with_stats *) result)->get_max_nulls_in_row(); bool res= FALSE; @@ -5602,29 +5655,52 @@ bool subselect_rowid_merge_engine::partial_match() /* If the outer reference consists of only NULLs, or if it has NULLs in all - nullable columns, the result is UNKNOWN. + nullable columns (above we guarantee there is a match for the non-null + coumns), the result is UNKNOWN. */ - if (count_nulls_in_search_key == - ((Item_in_subselect *) item)->left_expr->cols() - - (non_null_key ? non_null_key->get_column_count() : 0)) + if (count_nulls_in_search_key == merge_keys_count - test(non_null_key)) { res= TRUE; goto end; } + /* + If the outer row has NULLs in some columns, and + there is no match for any of the remaining columns, and + there is a subquery row with NULLs in all unmatched columns, + then there is a partial match, otherwise the result is FALSE. + */ + if (count_nulls_in_search_key && !pq.elements) + { + DBUG_ASSERT(!non_null_key); + /* + Check if the intersection of all NULL bitmaps of all keys that + are not in matching_outer_cols is non-empty. + */ + res= exists_complementing_null_row(&matching_outer_cols); + goto end; + } + /* If there is no NULL (sub)row that covers all NULL columns, and there is no - single match for any of the NULL columns, the result is FALSE. + match for any of the NULL columns, the result is FALSE. Notice that if there + is a non-null key, and there is only one matching key, the non-null key is + the matching key. This is so, because this method returns FALSE if the + non-null key doesn't have a match. */ - if ((pq.elements == 1 && non_null_key && - max_covering_null_row_len < merge_keys_count - 1) || - pq.elements == 0) + if (!count_nulls_in_search_key && + (!pq.elements || + (pq.elements == 1 && non_null_key && + max_null_in_any_row < merge_keys_count-1))) { - if (pq.elements == 0) + if (!pq.elements) { - DBUG_ASSERT(!non_null_key); /* Must follow from the logic of this method */ - /* This case must be handled by subselect_partial_match_engine::exec() */ - DBUG_ASSERT(max_covering_null_row_len != tmp_table->s->fields); + DBUG_ASSERT(!non_null_key); + /* + The case of a covering null row is handled by + subselect_partial_match_engine::exec() + */ + DBUG_ASSERT(max_null_in_any_row != tmp_table->s->fields); } res= FALSE; goto end; diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 2012306c0f7..28ce0061729 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -999,7 +999,7 @@ protected: /* - Distinguish the type od (0-based) row numbers from the type of the index into + Distinguish the type of (0-based) row numbers from the type of the index into an array of row numbers. */ typedef ha_rows rownum_t; @@ -1075,9 +1075,9 @@ protected: /* Count of NULLs per column. */ ha_rows null_count; /* The row number that contains the first NULL in a column. */ - ha_rows min_null_row; + rownum_t min_null_row; /* The row number that contains the last NULL in a column. */ - ha_rows max_null_row; + rownum_t max_null_row; protected: bool alloc_keys_buffers(); @@ -1110,6 +1110,10 @@ public: DBUG_ASSERT(i < key_column_count); return key_columns[i]->field->field_index; } + rownum_t get_min_null_row() { return min_null_row; } + rownum_t get_max_null_row() { return max_null_row; } + MY_BITMAP * get_null_key() { return &null_key; } + ha_rows get_null_count() { return null_count; } /* Get the search key element that corresponds to the i-th key part of this index. @@ -1280,6 +1284,8 @@ protected: Ordered_key **merge_keys; /* The number of elements in merge_keys. */ uint merge_keys_count; + /* The NULL bitmaps of merge keys.*/ + MY_BITMAP **null_bitmaps; /* An index on all non-NULL columns of 'tmp_table'. The index has the logical form: <[v_i1 | ... | v_ik], rownum>. It allows to find the row @@ -1305,6 +1311,7 @@ protected: static int cmp_keys_by_cur_rownum(void *arg, uchar *k1, uchar *k2); bool test_null_row(rownum_t row_num); + bool exists_complementing_null_row(MY_BITMAP *keys_to_complement); bool partial_match(); public: subselect_rowid_merge_engine(THD *thd_arg, -- cgit v1.2.1