From f88aec9e7afa07838e938e771530799b8b031c35 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@macbook.gmz" <> Date: Fri, 23 Feb 2007 18:49:41 +0200 Subject: [PATCH 01/13] Bug #26186: When handling DELETE ... FROM if there is no condition it is internally transformed to TRUNCATE for more efficient execution by the storage handler. The check for validity of the optional ORDER BY clause is done after the check for the above optimization and will not be performed if the optimization can be applied. Moved the validity check for ORDER BY before the optimization so it performed regardless of the optimization. --- mysql-test/r/delete.result | 9 +++++++++ mysql-test/t/delete.test | 18 ++++++++++++++++++ sql/sql_delete.cc | 37 +++++++++++++++++++++---------------- 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/mysql-test/r/delete.result b/mysql-test/r/delete.result index ba4e9386312..4bdf1c770d3 100644 --- a/mysql-test/r/delete.result +++ b/mysql-test/r/delete.result @@ -214,3 +214,12 @@ select count(*) from t1; count(*) 0 drop table t1; +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1); +DELETE FROM t1 ORDER BY x; +ERROR 42S22: Unknown column 'x' in 'order clause' +DELETE FROM t1 ORDER BY t2.x; +ERROR 42S22: Unknown column 't2.x' in 'order clause' +DELETE FROM t1 ORDER BY (SELECT x); +ERROR 42S22: Unknown column 'x' in 'field list' +DROP TABLE t1; diff --git a/mysql-test/t/delete.test b/mysql-test/t/delete.test index 306447dbd5a..36d627209db 100644 --- a/mysql-test/t/delete.test +++ b/mysql-test/t/delete.test @@ -203,3 +203,21 @@ select * from t1 where a is null; delete from t1 where a is null; select count(*) from t1; drop table t1; + +# +# Bug #26186: delete order by, sometimes accept unknown column +# +CREATE TABLE t1 (a INT); INSERT INTO t1 VALUES (1); + +--error ER_BAD_FIELD_ERROR +DELETE FROM t1 ORDER BY x; + +# even columns from a table not used in query (and not even existing) +--error ER_BAD_FIELD_ERROR +DELETE FROM t1 ORDER BY t2.x; + +# subquery (as long as the subquery from is valid or DUAL) +--error ER_BAD_FIELD_ERROR +DELETE FROM t1 ORDER BY (SELECT x); + +DROP TABLE t1; diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 749ee04493b..e4b013ef088 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -60,6 +60,27 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (mysql_prepare_delete(thd, table_list, &conds)) DBUG_RETURN(TRUE); + /* check ORDER BY even if it can be ignored */ + if (order && order->elements) + { + TABLE_LIST tables; + List fields; + List all_fields; + + bzero((char*) &tables,sizeof(tables)); + tables.table = table; + tables.alias = table_list->alias; + + if (select_lex->setup_ref_array(thd, order->elements) || + setup_order(thd, select_lex->ref_pointer_array, &tables, + fields, all_fields, (ORDER*) order->first)) + { + delete select; + free_underlaid_joins(thd, &thd->lex->select_lex); + DBUG_RETURN(TRUE); + } + } + const_cond= (!conds || conds->const_item()); safe_update=test(thd->options & OPTION_SAFE_UPDATES); if (safe_update && const_cond) @@ -148,23 +169,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, { uint length= 0; SORT_FIELD *sortorder; - TABLE_LIST tables; - List fields; - List all_fields; ha_rows examined_rows; - - bzero((char*) &tables,sizeof(tables)); - tables.table = table; - tables.alias = table_list->alias; - - if (select_lex->setup_ref_array(thd, order->elements) || - setup_order(thd, select_lex->ref_pointer_array, &tables, - fields, all_fields, (ORDER*) order->first)) - { - delete select; - free_underlaid_joins(thd, &thd->lex->select_lex); - DBUG_RETURN(TRUE); - } if ((!select || table->quick_keys.is_clear_all()) && limit != HA_POS_ERROR) usable_index= get_index_for_order(table, (ORDER*)(order->first), limit); From 8f9178e857195e9d7858e5f74938dd94925cd636 Mon Sep 17 00:00:00 2001 From: "igor@olga.mysql.com" <> Date: Thu, 22 Mar 2007 14:48:03 -0700 Subject: [PATCH 02/13] Fixed bug #27229: crash when a set function aggregated in outer context was used as an argument of GROUP_CONCAT. Ensured correct setting of the depended_from field in references generated for set functions aggregated in outer selects. A wrong value of this field resulted in wrong maps returned by used_tables() for these references. Made sure that a temporary table field is added for any set function aggregated in outer context when creation of a temporary table is needed to execute the inner subquery. --- mysql-test/r/subselect.result | 19 +++++++++++++++++++ mysql-test/t/subselect.test | 19 +++++++++++++++++++ sql/item.cc | 9 ++++++--- sql/item_sum.cc | 33 ++++++++++++++++++--------------- sql/item_sum.h | 4 +++- sql/sql_base.cc | 2 -- sql/sql_class.h | 3 --- sql/sql_insert.cc | 6 ++---- sql/sql_select.cc | 11 +++++++++-- 9 files changed, 76 insertions(+), 30 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 72bde001e87..81f087fe8fa 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -3905,3 +3905,22 @@ COUNT(*) a 2 2 3 3 DROP TABLE t1,t2; +CREATE TABLE t1 (a int, b int); +CREATE TABLE t2 (m int, n int); +INSERT INTO t1 VALUES (2,2), (2,2), (3,3), (3,3), (3,3), (4,4); +INSERT INTO t2 VALUES (1,11), (2,22), (3,32), (4,44), (4,44); +SELECT COUNT(*) c, a, +(SELECT GROUP_CONCAT(COUNT(a)) FROM t2 WHERE m = a) +FROM t1 GROUP BY a; +c a (SELECT GROUP_CONCAT(COUNT(a)) FROM t2 WHERE m = a) +2 2 2 +3 3 3 +1 4 1,1 +SELECT COUNT(*) c, a, +(SELECT GROUP_CONCAT(COUNT(a)+1) FROM t2 WHERE m = a) +FROM t1 GROUP BY a; +c a (SELECT GROUP_CONCAT(COUNT(a)+1) FROM t2 WHERE m = a) +2 2 3 +3 3 4 +1 4 2,2 +DROP table t1,t2; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index a238c8f070b..efd54ca95d1 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -2763,3 +2763,22 @@ SELECT COUNT(*), a HAVING (SELECT MIN(m) FROM t2 WHERE m = count(*)) > 1; DROP TABLE t1,t2; + +# +# Bug #27229: GROUP_CONCAT in subselect with COUNT() as an argument +# + +CREATE TABLE t1 (a int, b int); +CREATE TABLE t2 (m int, n int); +INSERT INTO t1 VALUES (2,2), (2,2), (3,3), (3,3), (3,3), (4,4); +INSERT INTO t2 VALUES (1,11), (2,22), (3,32), (4,44), (4,44); + +SELECT COUNT(*) c, a, + (SELECT GROUP_CONCAT(COUNT(a)) FROM t2 WHERE m = a) + FROM t1 GROUP BY a; + +SELECT COUNT(*) c, a, + (SELECT GROUP_CONCAT(COUNT(a)+1) FROM t2 WHERE m = a) + FROM t1 GROUP BY a; + +DROP table t1,t2; diff --git a/sql/item.cc b/sql/item.cc index 11a5039ca19..255a756b6d1 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1261,15 +1261,18 @@ void Item::split_sum_func2(THD *thd, Item **ref_pointer_array, Exception is Item_direct_view_ref which we need to convert to Item_ref to allow fields from view being stored in tmp table. */ + Item_aggregate_ref *item_ref; uint el= fields.elements; - Item *new_item, *real_itm= real_item(); + Item *real_itm= real_item(); ref_pointer_array[el]= real_itm; - if (!(new_item= new Item_aggregate_ref(&thd->lex->current_select->context, + if (!(item_ref= new Item_aggregate_ref(&thd->lex->current_select->context, ref_pointer_array + el, 0, name))) return; // fatal_error is set + if (type() == SUM_FUNC_ITEM) + item_ref->depended_from= ((Item_sum *) this)->depended_from(); fields.push_front(real_itm); - thd->change_item_tree(ref, new_item); + thd->change_item_tree(ref, item_ref); } } diff --git a/sql/item_sum.cc b/sql/item_sum.cc index fcebc89c6e9..03a1c32456a 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -61,9 +61,9 @@ bool Item_sum::init_sum_func_check(THD *thd) /* Save a pointer to object to be used in items for nested set functions */ thd->lex->in_sum_func= this; nest_level= thd->lex->current_select->nest_level; - nest_level_tables_count= thd->lex->current_select->join->tables; ref_by= 0; aggr_level= -1; + aggr_sel= NULL; max_arg_level= -1; max_sum_func_level= -1; return FALSE; @@ -151,7 +151,10 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref) invalid= aggr_level < 0 && !(allow_sum_func & (1 << nest_level)); } if (!invalid && aggr_level < 0) + { aggr_level= nest_level; + aggr_sel= thd->lex->current_select; + } /* By this moment we either found a subquery where the set function is to be aggregated and assigned a value that is >= 0 to aggr_level, @@ -212,7 +215,6 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref) bool Item_sum::register_sum_func(THD *thd, Item **ref) { SELECT_LEX *sl; - SELECT_LEX *aggr_sl= NULL; nesting_map allow_sum_func= thd->lex->allow_sum_func; for (sl= thd->lex->current_select->master_unit()->outer_select() ; sl && sl->nest_level > max_arg_level; @@ -222,7 +224,7 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref) { /* Found the most nested subquery where the function can be aggregated */ aggr_level= sl->nest_level; - aggr_sl= sl; + aggr_sel= sl; } } if (sl && (allow_sum_func & (1 << sl->nest_level))) @@ -233,21 +235,22 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref) The set function will be aggregated in this subquery. */ aggr_level= sl->nest_level; - aggr_sl= sl; + aggr_sel= sl; + } if (aggr_level >= 0) { ref_by= ref; - /* Add the object to the list of registered objects assigned to aggr_sl */ - if (!aggr_sl->inner_sum_func_list) + /* Add the object to the list of registered objects assigned to aggr_sel */ + if (!aggr_sel->inner_sum_func_list) next= this; else { - next= aggr_sl->inner_sum_func_list->next; - aggr_sl->inner_sum_func_list->next= this; + next= aggr_sel->inner_sum_func_list->next; + aggr_sel->inner_sum_func_list->next= this; } - aggr_sl->inner_sum_func_list= this; - aggr_sl->with_sum_func= 1; + aggr_sel->inner_sum_func_list= this; + aggr_sel->with_sum_func= 1; /* Mark Item_subselect(s) as containing aggregate function all the way up @@ -265,11 +268,11 @@ bool Item_sum::register_sum_func(THD *thd, Item **ref) has aggregate functions directly referenced (i.e. not through a sub-select). */ for (sl= thd->lex->current_select; - sl && sl != aggr_sl && sl->master_unit()->item; + sl && sl != aggr_sel && sl->master_unit()->item; sl= sl->master_unit()->outer_select() ) sl->master_unit()->item->with_sum_func= 1; } - thd->lex->current_select->mark_as_dependent(aggr_sl); + thd->lex->current_select->mark_as_dependent(aggr_sel); return FALSE; } @@ -299,10 +302,10 @@ Item_sum::Item_sum(List &list) :arg_count(list.elements), Item_sum::Item_sum(THD *thd, Item_sum *item): Item_result_field(thd, item), arg_count(item->arg_count), + aggr_sel(item->aggr_sel), nest_level(item->nest_level), aggr_level(item->aggr_level), quick_group(item->quick_group), used_tables_cache(item->used_tables_cache), - forced_const(item->forced_const), - nest_level_tables_count(item->nest_level_tables_count) + forced_const(item->forced_const) { if (arg_count <= 2) args=tmp_args; @@ -447,7 +450,7 @@ void Item_sum::update_used_tables () /* the aggregate function is aggregated into its local context */ if (aggr_level == nest_level) - used_tables_cache |= (1 << nest_level_tables_count) - 1; + used_tables_cache |= (1 << aggr_sel->join->tables) - 1; } } diff --git a/sql/item_sum.h b/sql/item_sum.h index 9ddda94a8ee..66c73e1d416 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -233,6 +233,7 @@ public: Item_sum *next; /* next in the circular chain of registered objects */ uint arg_count; Item_sum *in_sum_func; /* embedding set function if any */ + st_select_lex * aggr_sel; /* select where the function is aggregated */ int8 nest_level; /* number of the nesting level of the set function */ int8 aggr_level; /* nesting level of the aggregating subquery */ int8 max_arg_level; /* max level of unbound column references */ @@ -242,7 +243,6 @@ public: protected: table_map used_tables_cache; bool forced_const; - byte nest_level_tables_count; public: @@ -365,6 +365,8 @@ public: bool init_sum_func_check(THD *thd); bool check_sum_func(THD *thd, Item **ref); bool register_sum_func(THD *thd, Item **ref); + st_select_lex *depended_from() + { return (nest_level == aggr_level ? 0 : aggr_sel); } }; diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 13279abb1c8..77bb1d9642b 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -4864,7 +4864,6 @@ bool setup_tables_and_check_access(THD *thd, TABLE_LIST *leaves_tmp = NULL; bool first_table= true; - thd->leaf_count= 0; if (setup_tables (thd, context, from_clause, tables, conds, &leaves_tmp, select_insert)) return TRUE; @@ -4882,7 +4881,6 @@ bool setup_tables_and_check_access(THD *thd, return TRUE; } first_table= false; - thd->leaf_count++; } return FALSE; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 048da718dfd..99803802001 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1492,9 +1492,6 @@ public: query_id_t first_query_id; } binlog_evt_union; - /* pass up the count of "leaf" tables in a JOIN out of setup_tables() */ - byte leaf_count; - THD(); ~THD(); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 3c8f9fd5fc2..d9d32d14321 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2349,14 +2349,12 @@ bool mysql_insert_select_prepare(THD *thd) DBUG_ASSERT(select_lex->leaf_tables != 0); lex->leaf_tables_insert= select_lex->leaf_tables; /* skip all leaf tables belonged to view where we are insert */ - for (first_select_leaf_table= select_lex->leaf_tables->next_leaf, - thd->leaf_count --; + for (first_select_leaf_table= select_lex->leaf_tables->next_leaf; first_select_leaf_table && first_select_leaf_table->belong_to_view && first_select_leaf_table->belong_to_view == lex->leaf_tables_insert->belong_to_view; - first_select_leaf_table= first_select_leaf_table->next_leaf, - thd->leaf_count --) + first_select_leaf_table= first_select_leaf_table->next_leaf) {} select_lex->leaf_tables= first_select_leaf_table; DBUG_RETURN(FALSE); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index f46a212e7fb..c96ae153cf2 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -412,7 +412,12 @@ JOIN::prepare(Item ***rref_pointer_array, &select_lex->leaf_tables, FALSE, SELECT_ACL, SELECT_ACL)) DBUG_RETURN(-1); - tables= thd->leaf_count; + + TABLE_LIST *table_ptr; + for (table_ptr= select_lex->leaf_tables; + table_ptr; + table_ptr= table_ptr->next_leaf) + tables++; if (setup_wild(thd, tables_list, fields_list, &all_fields, wild_num) || select_lex->setup_ref_array(thd, og_num) || @@ -9186,7 +9191,9 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, Item::Type type=item->type(); if (not_all_columns) { - if (item->with_sum_func && type != Item::SUM_FUNC_ITEM) + if (item->with_sum_func && type != Item::SUM_FUNC_ITEM && + (type == Item::SUBSELECT_ITEM || + (item->used_tables() & ~PSEUDO_TABLE_BITS))) { /* Mark that the we have ignored an item that refers to a summary From 18ea806864365f5956210c1a781a19a90cb1a007 Mon Sep 17 00:00:00 2001 From: "igor@olga.mysql.com" <> Date: Sun, 25 Mar 2007 23:44:06 -0700 Subject: [PATCH 03/13] This is a fix for the memory corruption occurred in one of test cases from func_group.test after the patch for bug #27229 had been applied. The memory corruption happened because in some rare cases the function count_field_types underestimated the number of elements in in the array param->items_to_copy. --- sql/item_sum.cc | 3 +-- sql/sql_select.cc | 26 ++++++++++++++++---------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 422e9b4c6ea..368dc9a7d38 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -449,8 +449,7 @@ void Item_sum::update_used_tables () used_tables_cache&= PSEUDO_TABLE_BITS; /* the aggregate function is aggregated into its local context */ - if (aggr_level == nest_level) - used_tables_cache |= (1 << aggr_sel->join->tables) - 1; + used_tables_cache |= (1 << aggr_sel->join->tables) - 1; } } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 4fbc5dddb07..bb57764700d 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -9196,17 +9196,21 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, Item::Type type=item->type(); if (not_all_columns) { - if (item->with_sum_func && type != Item::SUM_FUNC_ITEM && - (type == Item::SUBSELECT_ITEM || - (item->used_tables() & ~PSEUDO_TABLE_BITS))) + if (item->with_sum_func && type != Item::SUM_FUNC_ITEM) { - /* - Mark that the we have ignored an item that refers to a summary - function. We need to know this if someone is going to use - DISTINCT on the result. - */ - param->using_indirect_summary_function=1; - continue; + if (item->used_tables() & OUTER_REF_TABLE_BIT) + item->update_used_tables(); + if (type == Item::SUBSELECT_ITEM || + (item->used_tables() & ~OUTER_REF_TABLE_BIT)) + { + /* + Mark that the we have ignored an item that refers to a summary + function. We need to know this if someone is going to use + DISTINCT on the result. + */ + param->using_indirect_summary_function=1; + continue; + } } if (item->const_item() && (int) hidden_field_count <= 0) continue; // We don't have to store this @@ -9391,6 +9395,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List &fields, table->s->default_values= table->record[1]+alloc_length; } copy_func[0]=0; // End marker + param->func_count= copy_func - param->items_to_copy; recinfo=param->start_recinfo; null_flags=(uchar*) table->record[0]; @@ -13571,6 +13576,7 @@ count_field_types(TMP_TABLE_PARAM *param, List &fields, if (!sum_item->quick_group) param->quick_group=0; // UDF SUM function param->sum_func_count++; + param->func_count++; for (uint i=0 ; i < sum_item->arg_count ; i++) { From 934884134658c718ca21365131264443d712fb3d Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@magare.gmz[kgeorge]" <> Date: Mon, 26 Mar 2007 12:32:51 +0300 Subject: [PATCH 04/13] Bug #26303: Reserve is not called before qs_append(). This may lead to buffer overflow. The String::qs_append() function will append a string without checking if there's enough space. So qs_append() must be called beforehand to ensure there's enough space in the buffer for the subsequent qs_append() calls. Fixed Item_case_expr::print() to make sure there's enough space before appending data by adding a call to String::reserve() to make sure qs_append() will have enough space. --- mysql-test/r/sp-code.result | 17 +++++++++++++++++ mysql-test/t/sp-code.test | 18 ++++++++++++++++++ sql/item.cc | 4 +++- sql/item.h | 4 ++-- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/sp-code.result b/mysql-test/r/sp-code.result index 67b030f87a4..588f4329368 100644 --- a/mysql-test/r/sp-code.result +++ b/mysql-test/r/sp-code.result @@ -621,3 +621,20 @@ Pos Instruction 0 stmt 2 "CREATE INDEX idx ON t1 (c1)" DROP PROCEDURE p1; End of 5.0 tests. +CREATE PROCEDURE p1() +BEGIN +DECLARE dummy int default 0; +CASE 12 +WHEN 12 +THEN SET dummy = 0; +END CASE; +END// +SHOW PROCEDURE CODE p1; +Pos Instruction +0 set dummy@0 0 +1 set_case_expr (6) 0 12 +2 jump_if_not 5(6) (case_expr@0 = 12) +3 set dummy@0 0 +4 jump 6 +5 error 1339 +DROP PROCEDURE p1; diff --git a/mysql-test/t/sp-code.test b/mysql-test/t/sp-code.test index 97bc29fcad2..1b33680cfaf 100644 --- a/mysql-test/t/sp-code.test +++ b/mysql-test/t/sp-code.test @@ -447,3 +447,21 @@ DROP PROCEDURE p1; --echo End of 5.0 tests. + +# +# Bug #26303: reserve() not called before qs_append() may lead to buffer +# overflow +# +DELIMITER //; +CREATE PROCEDURE p1() +BEGIN + DECLARE dummy int default 0; + + CASE 12 + WHEN 12 + THEN SET dummy = 0; + END CASE; +END// +DELIMITER ;// +SHOW PROCEDURE CODE p1; +DROP PROCEDURE p1; diff --git a/sql/item.cc b/sql/item.cc index 37ebd0b6e20..d675e03d2d7 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1088,7 +1088,7 @@ bool Item_splocal::set_value(THD *thd, sp_rcontext *ctx, Item **it) Item_case_expr methods *****************************************************************************/ -Item_case_expr::Item_case_expr(int case_expr_id) +Item_case_expr::Item_case_expr(uint case_expr_id) :Item_sp_variable( C_STRING_WITH_LEN("case_expr")), m_case_expr_id(case_expr_id) { @@ -1125,6 +1125,8 @@ Item_case_expr::this_item_addr(THD *thd, Item **) void Item_case_expr::print(String *str) { + if (str->reserve(MAX_INT_WIDTH + sizeof("case_expr@"))) + return; /* purecov: inspected */ VOID(str->append(STRING_WITH_LEN("case_expr@"))); str->qs_append(m_case_expr_id); } diff --git a/sql/item.h b/sql/item.h index 955f9c8489a..2cffe985480 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1116,7 +1116,7 @@ inline Item_result Item_splocal::result_type() const class Item_case_expr :public Item_sp_variable { public: - Item_case_expr(int case_expr_id); + Item_case_expr(uint case_expr_id); public: Item *this_item(); @@ -1135,7 +1135,7 @@ public: void print(String *str); private: - int m_case_expr_id; + uint m_case_expr_id; }; /***************************************************************************** From e6d81ad3383fa5f6dc282ff7d85d3e2cdbec5a9c Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@magare.gmz[kgeorge]" <> Date: Mon, 26 Mar 2007 13:17:40 +0300 Subject: [PATCH 05/13] Bug #27164: not reseting the data pointer to 0 causes wrong (large) length to be read from the row in _mi_calc_blob_length() when storing NULL values in (e.g) POINT columns. This large length is then used to allocate a block of memory that (on some OSes) causes trouble. Fixed by calling the base class's Field_blob::reset() from Field_geom::reset() that is called when storing a NULL value into the column. --- mysql-test/r/gis.result | 6 ++++++ mysql-test/t/gis.test | 8 ++++++++ sql/field.h | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/gis.result b/mysql-test/r/gis.result index de7e44ed7b2..643a3d6b434 100644 --- a/mysql-test/r/gis.result +++ b/mysql-test/r/gis.result @@ -718,4 +718,10 @@ point(b, b) IS NULL linestring(b) IS NULL polygon(b) IS NULL multipoint(b) IS NU 1 1 1 1 1 1 1 0 1 1 1 1 1 1 drop table t1; +CREATE TABLE t1(a POINT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (NULL); +SELECT * FROM t1; +a +NULL +DROP TABLE t1; End of 4.1 tests diff --git a/mysql-test/t/gis.test b/mysql-test/t/gis.test index 6bd0db92152..7182e040d46 100644 --- a/mysql-test/t/gis.test +++ b/mysql-test/t/gis.test @@ -411,4 +411,12 @@ from t1; drop table t1; +# +# Bug #27164: Crash when mixing InnoDB and MyISAM Geospatial tables +# +CREATE TABLE t1(a POINT) ENGINE=MyISAM; +INSERT INTO t1 VALUES (NULL); +SELECT * FROM t1; +DROP TABLE t1; + --echo End of 4.1 tests diff --git a/sql/field.h b/sql/field.h index d3e38db83d1..58177747120 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1115,7 +1115,7 @@ public: int store(const char *to, uint length, CHARSET_INFO *charset); int store(double nr) { return 1; } int store(longlong nr) { return 1; } - int reset(void) { return !maybe_null(); } + int reset(void) { return !maybe_null() || Field_blob::reset(); } void get_key_image(char *buff,uint length, CHARSET_INFO *cs,imagetype type); void set_key_image(char *buff,uint length, CHARSET_INFO *cs); From ce9cc47a731325f8c1e0fac4bfe955bb3dc00d68 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@magare.gmz" <> Date: Mon, 26 Mar 2007 16:52:52 +0300 Subject: [PATCH 06/13] WL3527: 5.0 part: enabled the optional FOR JOIN to all the three clauses : USE, FORCE and IGNORE --- mysql-test/r/select.result | 6 ++++++ mysql-test/t/select.test | 2 ++ sql/sql_yacc.yy | 12 ++++-------- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/select.result b/mysql-test/r/select.result index 31d15981d93..b501d547e0a 100644 --- a/mysql-test/r/select.result +++ b/mysql-test/r/select.result @@ -3636,6 +3636,12 @@ id select_type table type possible_keys key key_len ref rows Extra EXPLAIN SELECT 1 FROM t1 IGNORE INDEX FOR JOIN (a) WHERE a = 1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ALL NULL NULL NULL NULL 2 Using where +EXPLAIN SELECT 1 FROM t1 USE INDEX FOR JOIN (a) WHERE a = 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref a a 5 const 1 Using where; Using index +EXPLAIN SELECT 1 FROM t1 FORCE INDEX FOR JOIN (a) WHERE a = 1; +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ref a a 5 const 1 Using where; Using index DROP TABLE t1; CREATE TABLE t1 ( f1 int primary key, f2 int, f3 int, f4 int, f5 int, f6 int, checked_out int); CREATE TABLE t2 ( f11 int PRIMARY KEY ); diff --git a/mysql-test/t/select.test b/mysql-test/t/select.test index 883ea7bf0b0..c5c7d07ee25 100644 --- a/mysql-test/t/select.test +++ b/mysql-test/t/select.test @@ -3111,6 +3111,8 @@ DROP TABLE t1,t2,t3; CREATE TABLE t1 (a INT, b INT, KEY (a)); INSERT INTO t1 VALUES (1,1),(2,2); EXPLAIN SELECT 1 FROM t1 WHERE a = 1; EXPLAIN SELECT 1 FROM t1 IGNORE INDEX FOR JOIN (a) WHERE a = 1; +EXPLAIN SELECT 1 FROM t1 USE INDEX FOR JOIN (a) WHERE a = 1; +EXPLAIN SELECT 1 FROM t1 FORCE INDEX FOR JOIN (a) WHERE a = 1; DROP TABLE t1; # diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index cc8d1ae9e6d..bbb0d11b942 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1092,7 +1092,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); key_alg opt_btree_or_rtree %type - key_usage_list key_usage_list_inner using_list + key_usage_list using_list %type key_part @@ -5907,19 +5907,15 @@ opt_key_definition: sel->use_index_ptr= &sel->use_index; sel->table_join_options|= TL_OPTION_FORCE_INDEX; } - | IGNORE_SYM key_or_index opt_for_join key_usage_list_inner + | IGNORE_SYM key_usage_list { SELECT_LEX *sel= Select; - sel->ignore_index= *$4; + sel->ignore_index= *$2; sel->ignore_index_ptr= &sel->ignore_index; }; key_usage_list: - key_or_index key_usage_list_inner - { $$= $2; } - ; - -key_usage_list_inner: + key_or_index opt_for_join { Select->interval_list.empty(); } '(' key_list_or_empty ')' { $$= &Select->interval_list; } From 101857299a01f4bf854ff3c6a0241390dba7d250 Mon Sep 17 00:00:00 2001 From: "gkodinov/kgeorge@magare.gmz" <> Date: Mon, 26 Mar 2007 19:15:30 +0300 Subject: [PATCH 07/13] WL3527: 5.1 renamed "--old-mode" to "--old" to prevent ambiguity. "old" now appears in SHOW VARIABLES as a read-only option. --- mysql-test/r/group_by.result | 5 +++++ mysql-test/t/group_by.test | 7 ++++++- sql/mysqld.cc | 2 +- sql/set_var.cc | 3 +++ sql/set_var.h | 10 ++++++++++ sql/sql_base.cc | 2 -- 6 files changed, 25 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result index 5cff5fec7ed..c7464bb21d2 100644 --- a/mysql-test/r/group_by.result +++ b/mysql-test/r/group_by.result @@ -1141,4 +1141,9 @@ EXPLAIN SELECT 1 FROM t2 WHERE a IN id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t2 index NULL a 5 NULL 4 Using where; Using index 2 DEPENDENT SUBQUERY t1 ALL NULL NULL NULL NULL 256 Using where +SHOW VARIABLES LIKE 'old'; +Variable_name Value +old OFF +SET @@old = off; +ERROR HY000: Variable 'old' is a read only variable DROP TABLE t1, t2; diff --git a/mysql-test/t/group_by.test b/mysql-test/t/group_by.test index a3318745764..fb9c09d4763 100644 --- a/mysql-test/t/group_by.test +++ b/mysql-test/t/group_by.test @@ -755,7 +755,8 @@ SET SQL_MODE = ''; # # Bug #21174: Index degrades sort performance and -# optimizer does not honor IGNORE INDEX +# optimizer does not honor IGNORE INDEX. +# a.k.a WL3527. # CREATE TABLE t1 (a INT, b INT, PRIMARY KEY (a), @@ -819,4 +820,8 @@ EXPLAIN SELECT a, SUM(b) FROM t2 IGNORE INDEX (a) GROUP BY a LIMIT 2; EXPLAIN SELECT 1 FROM t2 WHERE a IN (SELECT a FROM t1 USE INDEX (i2) IGNORE INDEX (i2)); +SHOW VARIABLES LIKE 'old'; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +SET @@old = off; + DROP TABLE t1, t2; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a0687929de5..887d14e7f38 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -6268,7 +6268,7 @@ The minimum value for this variable is 4096.", (gptr*) &global_system_variables.net_write_timeout, (gptr*) &max_system_variables.net_write_timeout, 0, GET_ULONG, REQUIRED_ARG, NET_WRITE_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0}, - { "old_mode", OPT_OLD_MODE, "Use compatible behaviour.", + { "old", OPT_OLD_MODE, "Use compatible behavior.", (gptr*) &global_system_variables.old_mode, (gptr*) &max_system_variables.old_mode, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, diff --git a/sql/set_var.cc b/sql/set_var.cc index fef6fccfd06..7f55556c134 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -356,6 +356,8 @@ sys_var_thd_ulong sys_net_retry_count("net_retry_count", &SV::net_retry_count, 0, fix_net_retry_count); sys_var_thd_bool sys_new_mode("new", &SV::new_mode); +sys_var_bool_ptr_readonly sys_old_mode("old", + &global_system_variables.old_mode); sys_var_thd_bool sys_old_alter_table("old_alter_table", &SV::old_alter_table); sys_var_thd_bool sys_old_passwords("old_passwords", &SV::old_passwords); @@ -942,6 +944,7 @@ SHOW_VAR init_vars[]= { {sys_net_retry_count.name, (char*) &sys_net_retry_count, SHOW_SYS}, {sys_net_write_timeout.name,(char*) &sys_net_write_timeout, SHOW_SYS}, {sys_new_mode.name, (char*) &sys_new_mode, SHOW_SYS}, + {sys_old_mode.name, (char*) &sys_old_mode, SHOW_SYS}, {sys_old_alter_table.name, (char*) &sys_old_alter_table, SHOW_SYS}, {sys_old_passwords.name, (char*) &sys_old_passwords, SHOW_SYS}, {"open_files_limit", (char*) &open_files_limit, SHOW_LONG}, diff --git a/sql/set_var.h b/sql/set_var.h index 8887d91ec69..eac03fce610 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -169,6 +169,16 @@ public: }; +class sys_var_bool_ptr_readonly :public sys_var_bool_ptr +{ +public: + sys_var_bool_ptr_readonly(const char *name_arg, my_bool *value_arg) + :sys_var_bool_ptr(name_arg, value_arg) + {} + bool is_readonly() const { return 1; } +}; + + class sys_var_str :public sys_var { public: diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 4d7c2c485ab..995ed220d10 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5772,7 +5772,6 @@ bool setup_tables_and_check_access(THD *thd, TABLE_LIST *leaves_tmp= NULL; bool first_table= true; - thd->leaf_count= 0; if (setup_tables(thd, context, from_clause, tables, &leaves_tmp, select_insert)) return TRUE; @@ -5790,7 +5789,6 @@ bool setup_tables_and_check_access(THD *thd, return TRUE; } first_table= 0; - thd->leaf_count++; } return FALSE; } From adc07255ee9f88cc7521afa26b31545dd1d66d9c Mon Sep 17 00:00:00 2001 From: "igor@olga.mysql.com" <> Date: Tue, 27 Mar 2007 09:48:10 -0700 Subject: [PATCH 08/13] Fixed bug #27348. If a set function with a outer reference s(outer_ref) cannot be aggregated the outer query against which the reference has been resolved then MySQL interpretes s(outer_ref) in the same way as it would interpret s(const). Hovever the standard requires throwing an error in this situation. Added some code to support this requirement in ansi mode. Corrected another minor bug in Item_sum::check_sum_func. --- mysql-test/r/subselect.result | 23 +++++++++++++++++++++++ mysql-test/t/subselect.test | 27 +++++++++++++++++++++++++++ sql/item_sum.cc | 7 +++++-- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 81f087fe8fa..d5a1c0b2451 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -3924,3 +3924,26 @@ c a (SELECT GROUP_CONCAT(COUNT(a)+1) FROM t2 WHERE m = a) 3 3 4 1 4 2,2 DROP table t1,t2; +CREATE TABLE t1 (a int, b int); +INSERT INTO t1 VALUES (2,22),(1,11),(2,22); +SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 0 GROUP BY a; +a +1 +2 +SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 1 GROUP BY a; +a +SELECT a FROM t1 t0 +WHERE (SELECT COUNT(t0.b) FROM t1 t WHERE t.b>20) GROUP BY a; +a +1 +2 +SET @@sql_mode='ansi'; +SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 0 GROUP BY a; +ERROR HY000: Invalid use of group function +SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 1 GROUP BY a; +ERROR HY000: Invalid use of group function +SELECT a FROM t1 t0 +WHERE (SELECT COUNT(t0.b) FROM t1 t WHERE t.b>20) GROUP BY a; +ERROR HY000: Invalid use of group function +SET @@sql_mode=default; +DROP TABLE t1; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index efd54ca95d1..182b9b27ef7 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -2782,3 +2782,30 @@ SELECT COUNT(*) c, a, FROM t1 GROUP BY a; DROP table t1,t2; + +# +# Bug #27348: SET FUNCTION used in a subquery from WHERE condition +# + +CREATE TABLE t1 (a int, b int); +INSERT INTO t1 VALUES (2,22),(1,11),(2,22); + +SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 0 GROUP BY a; +SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 1 GROUP BY a; + +SELECT a FROM t1 t0 + WHERE (SELECT COUNT(t0.b) FROM t1 t WHERE t.b>20) GROUP BY a; + +SET @@sql_mode='ansi'; +--error 1111 +SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 0 GROUP BY a; +--error 1111 +SELECT a FROM t1 WHERE (SELECT COUNT(b) FROM DUAL) > 1 GROUP BY a; + +--error 1111 +SELECT a FROM t1 t0 + WHERE (SELECT COUNT(t0.b) FROM t1 t WHERE t.b>20) GROUP BY a; + +SET @@sql_mode=default; + +DROP TABLE t1; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 368dc9a7d38..e10357dc0e0 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -149,6 +149,8 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref) if (register_sum_func(thd, ref)) return TRUE; invalid= aggr_level < 0 && !(allow_sum_func & (1 << nest_level)); + if (!invalid && thd->variables.sql_mode & MODE_ANSI) + invalid= aggr_level < 0 && max_arg_level < nest_level; } if (!invalid && aggr_level < 0) { @@ -164,8 +166,9 @@ bool Item_sum::check_sum_func(THD *thd, Item **ref) Additionally we have to check whether possible nested set functions are acceptable here: they are not, if the level of aggregation of some of them is less than aggr_level. - */ - invalid= aggr_level <= max_sum_func_level; + */ + if (!invalid) + invalid= aggr_level <= max_sum_func_level; if (invalid) { my_message(ER_INVALID_GROUP_FUNC_USE, ER(ER_INVALID_GROUP_FUNC_USE), From ce3f182604e9949fdb03347ca4879b3b254816f7 Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Wed, 28 Mar 2007 18:38:42 +0400 Subject: [PATCH 09/13] BUG#26625: crash in range optimizer (out of mem) - Define Sql_alloc::operator new() as thow() so that C++ compiler handles NULL return values (there is no testcase as there is no portable way to set limit on the amount of memory that a process can allocate) --- sql/sql_list.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/sql_list.h b/sql/sql_list.h index 47379ab1ddf..2075361a398 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -30,7 +30,7 @@ class Sql_alloc { public: - static void *operator new(size_t size) + static void *operator new(size_t size) throw () { return (void*) sql_alloc((uint) size); } @@ -38,7 +38,7 @@ public: { return (void*) sql_alloc((uint) size); } - static void *operator new(size_t size, MEM_ROOT *mem_root) + static void *operator new(size_t size, MEM_ROOT *mem_root) throw () { return (void*) alloc_root(mem_root, (uint) size); } static void operator delete(void *ptr, size_t size) { TRASH(ptr, size); } static void operator delete(void *ptr, MEM_ROOT *mem_root) From ce28ca336b2ef04826ba76a7b0f625f131f81aa4 Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Wed, 28 Mar 2007 19:05:30 +0400 Subject: [PATCH 10/13] Delete: sql/mysqld.cc.rej --- sql/mysqld.cc.rej | 161 ---------------------------------------------- 1 file changed, 161 deletions(-) delete mode 100644 sql/mysqld.cc.rej diff --git a/sql/mysqld.cc.rej b/sql/mysqld.cc.rej deleted file mode 100644 index bd7338143ae..00000000000 --- a/sql/mysqld.cc.rej +++ /dev/null @@ -1,161 +0,0 @@ -*************** -*** 177,188 **** - } /* cplusplus */ - - -- #if defined(HAVE_LINUXTHREADS) -- #define THR_KILL_SIGNAL SIGINT -- #else -- #define THR_KILL_SIGNAL SIGUSR2 // Can't use this with LinuxThreads -- #endif -- - #ifdef HAVE_GLIBC2_STYLE_GETHOSTBYNAME_R - #include - #else ---- 177,182 ---- - } /* cplusplus */ - - - #ifdef HAVE_GLIBC2_STYLE_GETHOSTBYNAME_R - #include - #else -*************** -*** 505,510 **** - static void clean_up_mutexes(void); - static int test_if_case_insensitive(const char *dir_name); - static void create_pid_file(); - - /**************************************************************************** - ** Code to end mysqld ---- 499,505 ---- - static void clean_up_mutexes(void); - static int test_if_case_insensitive(const char *dir_name); - static void create_pid_file(); -+ static uint get_thread_lib(void); - - /**************************************************************************** - ** Code to end mysqld -*************** -*** 544,550 **** - DBUG_PRINT("info",("Waiting for select_thread")); - - #ifndef DONT_USE_THR_ALARM -! if (pthread_kill(select_thread,THR_CLIENT_ALARM)) - break; // allready dead - #endif - set_timespec(abstime, 2); ---- 539,546 ---- - DBUG_PRINT("info",("Waiting for select_thread")); - - #ifndef DONT_USE_THR_ALARM -! if (pthread_kill(select_thread, -! thd_lib_detected == THD_LIB_LT ? SIGALRM : SIGUSR1)) - break; // allready dead - #endif - set_timespec(abstime, 2); -*************** -*** 844,850 **** - sig,my_thread_id()); - } - #ifdef DONT_REMEMBER_SIGNAL -! sigset(sig,print_signal_warning); /* int. thread system calls */ - #endif - #if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) - if (sig == SIGALRM) ---- 840,846 ---- - sig,my_thread_id()); - } - #ifdef DONT_REMEMBER_SIGNAL -! my_sigset(sig, print_signal_warning); /* int. thread system calls */ - #endif - #if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) - if (sig == SIGALRM) -*************** -*** 1841,1848 **** - DBUG_ENTER("init_signals"); - - if (test_flags & TEST_SIGINT) -! sigset(THR_KILL_SIGNAL,end_thread_signal); -! sigset(THR_SERVER_ALARM,print_signal_warning); // Should never be called! - - if (!(test_flags & TEST_NO_STACKTRACE) || (test_flags & TEST_CORE_ON_SIGNAL)) - { ---- 1837,1847 ---- - DBUG_ENTER("init_signals"); - - if (test_flags & TEST_SIGINT) -! { -! my_sigset(thd_lib_detected == THD_LIB_LT ? SIGINT : SIGUSR2, -! end_thread_signal); -! } -! my_sigset(THR_SERVER_ALARM, print_signal_warning); // Should never be called! - - if (!(test_flags & TEST_NO_STACKTRACE) || (test_flags & TEST_CORE_ON_SIGNAL)) - { -*************** -*** 1877,1883 **** - #endif - (void) sigemptyset(&set); - #ifdef THREAD_SPECIFIC_SIGPIPE -! sigset(SIGPIPE,abort_thread); - sigaddset(&set,SIGPIPE); - #else - (void) signal(SIGPIPE,SIG_IGN); // Can't know which thread ---- 1876,1882 ---- - #endif - (void) sigemptyset(&set); - #ifdef THREAD_SPECIFIC_SIGPIPE -! my_sigset(SIGPIPE, abort_thread); - sigaddset(&set,SIGPIPE); - #else - (void) signal(SIGPIPE,SIG_IGN); // Can't know which thread -*************** -*** 2237,2244 **** - MY_INIT(argv[0]); // init my_sys library & pthreads - tzset(); // Set tzname - - start_time=time((time_t*) 0); -- - #ifdef OS2 - { - // fix timezone for daylight saving ---- 2236,2243 ---- - MY_INIT(argv[0]); // init my_sys library & pthreads - tzset(); // Set tzname - -+ thd_lib_detected= get_thread_lib(); - start_time=time((time_t*) 0); - #ifdef OS2 - { - // fix timezone for daylight saving -*************** -*** 5547,5552 **** - (void) my_write(file, (byte*) buff, (uint) (end-buff),MYF(MY_WME)); - (void) my_close(file, MYF(0)); - } - } - - ---- 5546,5567 ---- - (void) my_write(file, (byte*) buff, (uint) (end-buff),MYF(MY_WME)); - (void) my_close(file, MYF(0)); - } -+ } -+ -+ -+ static uint get_thread_lib(void) -+ { -+ char buff[64]; -+ -+ #ifdef _CS_GNU_LIBPTHREAD_VERSION -+ confstr(_CS_GNU_LIBPTHREAD_VERSION, buff, sizeof(buff)); -+ -+ if (!strncasecmp(buff, "NPTL", 4)) -+ return THD_LIB_NPTL; -+ else if (!strncasecmp(buff, "linuxthreads", 12)) -+ return THD_LIB_LT; -+ #endif -+ return THD_LIB_OTHER; - } - - From a8d439728fb3d4c02cbecd989b4df41dae01134b Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Wed, 28 Mar 2007 20:16:01 +0400 Subject: [PATCH 11/13] BUG#26624: high mem usage (crash) in range optimizer - Added PARAM::alloced_sel_args where we count the # of SEL_ARGs created by SEL_ARG tree cloning operations. - Made the range analyzer to shortcut and not do any more cloning if we've already created MAX_SEL_ARGS SEL_ARG objects in cloning. - Added comments about space complexity of SEL_ARG-graph representation. --- mysql-test/r/range.result | 28 +++++++ mysql-test/t/range.test | 32 ++++++++ sql/opt_range.cc | 162 ++++++++++++++++++++++++++++++-------- 3 files changed, 191 insertions(+), 31 deletions(-) diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index 4d6cafb61d1..15d5f1a0183 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -701,4 +701,32 @@ d8c4177d225791924.30714720 d8c4177d2380fc201.39666693 d8c4177d24ccef970.14957924 DROP TABLE t1; +create table t1 ( +c1 char(10), c2 char(10), c3 char(10), c4 char(10), +c5 char(10), c6 char(10), c7 char(10), c8 char(10), +c9 char(10), c10 char(10), c11 char(10), c12 char(10), +c13 char(10), c14 char(10), c15 char(10), c16 char(10), +index(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12,c13,c14,c15,c16) +); +insert into t1 (c1) values ('1'),('1'),('1'),('1'); +select * from t1 where +c1 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") +and c2 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") +and c3 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") +and c4 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") +and c5 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") +and c6 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") +and c7 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") +and c8 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") +and c9 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") +and c10 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") +and c11 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") +and c12 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") +and c13 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") +and c14 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") +and c15 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") +and c16 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") +; +c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12 c13 c14 c15 c16 +drop table t1; End of 4.1 tests diff --git a/mysql-test/t/range.test b/mysql-test/t/range.test index 68ba43a8ba9..1e4cb91c03f 100644 --- a/mysql-test/t/range.test +++ b/mysql-test/t/range.test @@ -563,4 +563,36 @@ SELECT s.oxid FROM t1 v, t1 s DROP TABLE t1; +# BUG#26624 high mem usage (crash) in range optimizer (depends on order of fields in where) +create table t1 ( + c1 char(10), c2 char(10), c3 char(10), c4 char(10), + c5 char(10), c6 char(10), c7 char(10), c8 char(10), + c9 char(10), c10 char(10), c11 char(10), c12 char(10), + c13 char(10), c14 char(10), c15 char(10), c16 char(10), + index(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12,c13,c14,c15,c16) +); + +insert into t1 (c1) values ('1'),('1'),('1'),('1'); + +# This must run without crash and fast: +select * from t1 where + c1 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") + and c2 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") + and c3 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") + and c4 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") + and c5 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") + and c6 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") + and c7 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") + and c8 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") + and c9 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") + and c10 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") + and c11 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") + and c12 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") + and c13 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") + and c14 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") + and c15 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") + and c16 in ("abcdefgh", "123456789", "qwertyuio", "asddfgh") +; +drop table t1; + --echo End of 4.1 tests diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 06e42ff363f..11ba6c0e465 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -128,6 +128,89 @@ static char is_null_string[2]= {1,0}; - get_quick_select() - Walk the SEL_ARG, materialize the key intervals, and create QUICK_RANGE_SELECT object that will read records within these intervals. + + 4. SPACE COMPLEXITY NOTES + + SEL_ARG graph is a representation of an ordered disjoint sequence of + intervals over the ordered set of index tuple values. + + For multi-part keys, one can construct a WHERE expression such that its + list of intervals will be of combinatorial size. Here is an example: + + (keypart1 IN (1,2, ..., n1)) AND + (keypart2 IN (1,2, ..., n2)) AND + (keypart3 IN (1,2, ..., n3)) + + For this WHERE clause the list of intervals will have n1*n2*n3 intervals + of form + + (keypart1, keypart2, keypart3) = (k1, k2, k3), where 1 <= k{i} <= n{i} + + SEL_ARG graph structure aims to reduce the amount of required space by + "sharing" the elementary intervals when possible (the pic at the + beginning of this comment has examples of such sharing). The sharing may + prevent combinatorial blowup: + + There are WHERE clauses that have combinatorial-size interval lists but + will be represented by a compact SEL_ARG graph. + Example: + (keypartN IN (1,2, ..., n1)) AND + ... + (keypart2 IN (1,2, ..., n2)) AND + (keypart1 IN (1,2, ..., n3)) + + but not in all cases: + + - There are WHERE clauses that do have a compact SEL_ARG-graph + representation but get_mm_tree() and its callees will construct a + graph of combinatorial size. + Example: + (keypart1 IN (1,2, ..., n1)) AND + (keypart2 IN (1,2, ..., n2)) AND + ... + (keypartN IN (1,2, ..., n3)) + + - There are WHERE clauses for which the minimal possible SEL_ARG graph + representation will have combinatorial size. + Example: + By induction: Let's take any interval on some keypart in the middle: + + kp15=1 + + Then let's AND it with this interval 'structure' from preceding and + following keyparts: + + (kp14=c1 AND kp16=c3) OR keypart14=c2) (*) + + We will obtain this SEL_ARG graph: + + kp14 $ kp15 $ kp16 + $ $ + +---------+ $ +--------+ $ +---------+ + | kp14=c1 |--$-->| kp15=1 |--$-->| kp16=c3 | + +---------+ $ +--------+ $ +---------+ + | $ $ + +---------+ $ +--------+ $ + | kp14=c2 |--$-->| kp15=1 | $ + +---------+ $ +--------+ $ + $ $ + + Note that we had to duplicate "kp15=1" and there was no way to avoid + that. + The induction step: AND the obtained expression with another "wrapping" + expression like (*). + When the process ends because of the limit on max. number of keyparts + we'll have: + + WHERE clause length is O(3*#max_keyparts) + SEL_ARG graph size is O(2^(#max_keyparts/2)) + + (it is also possible to construct a case where instead of 2 in 2^n we + have a bigger constant, e.g. 4, and get a graph with 4^(31/2)= 2^31 + nodes) + + We avoid consuming too much memory by setting a limit on the number of + SEL_ARG object we can construct during one range analysis invocation. */ class SEL_ARG :public Sql_alloc @@ -158,6 +241,8 @@ public: enum leaf_color { BLACK,RED } color; enum Type { IMPOSSIBLE, MAYBE, MAYBE_KEY, KEY_RANGE } type; + enum { MAX_SEL_ARGS = 64000 }; + SEL_ARG() {} SEL_ARG(SEL_ARG &); SEL_ARG(Field *,const char *,const char *); @@ -227,7 +312,8 @@ public: return new SEL_ARG(field, part, min_value, arg->max_value, min_flag, arg->max_flag, maybe_flag | arg->maybe_flag); } - SEL_ARG *clone(SEL_ARG *new_parent,SEL_ARG **next); + SEL_ARG *clone(struct st_qsel_param *param, SEL_ARG *new_parent, + SEL_ARG **next); bool copy_min(SEL_ARG* arg) { // Get overlapping range @@ -365,7 +451,7 @@ public: { return parent->left == this ? &parent->left : &parent->right; } - SEL_ARG *clone_tree(); + SEL_ARG *clone_tree(struct st_qsel_param *param); }; @@ -391,6 +477,8 @@ typedef struct st_qsel_param { max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; bool quick; // Don't calulate possible keys COND *cond; + /* Numbr of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */ + uint alloced_sel_args; } PARAM; static SEL_TREE * get_mm_parts(PARAM *param,COND *cond_func,Field *field, @@ -413,8 +501,8 @@ static void print_quick(QUICK_SELECT *quick,const key_map* needed_reg); static SEL_TREE *tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2); static SEL_TREE *tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2); static SEL_ARG *sel_add(SEL_ARG *key1,SEL_ARG *key2); -static SEL_ARG *key_or(SEL_ARG *key1,SEL_ARG *key2); -static SEL_ARG *key_and(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag); +static SEL_ARG *key_or(PARAM *param, SEL_ARG *key1,SEL_ARG *key2); +static SEL_ARG *key_and(PARAM *param, SEL_ARG *key1,SEL_ARG *key2,uint clone_flag); static bool get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1); static bool get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key, SEL_ARG *key_tree,char *min_key,uint min_key_flag, @@ -424,6 +512,7 @@ static bool eq_tree(SEL_ARG* a,SEL_ARG *b); static SEL_ARG null_element(SEL_ARG::IMPOSSIBLE); static bool null_part_in_key(KEY_PART *key_part, const char *key, uint length); + /*************************************************************************** ** Basic functions for SQL_SELECT and QUICK_SELECT ***************************************************************************/ @@ -568,12 +657,17 @@ SEL_ARG::SEL_ARG(Field *field_,uint8 part_,char *min_value_,char *max_value_, left=right= &null_element; } -SEL_ARG *SEL_ARG::clone(SEL_ARG *new_parent,SEL_ARG **next_arg) +SEL_ARG *SEL_ARG::clone(PARAM *param, SEL_ARG *new_parent, SEL_ARG **next_arg) { SEL_ARG *tmp; + + /* Bail out if we have already generated too many SEL_ARGs */ + if (++param->alloced_sel_args > MAX_SEL_ARGS) + return 0; + if (type != KEY_RANGE) { - if (!(tmp= new SEL_ARG(type))) + if (!(tmp= new (param->mem_root) SEL_ARG(type))) return 0; // out of memory tmp->prev= *next_arg; // Link into next/prev chain (*next_arg)->next=tmp; @@ -581,20 +675,20 @@ SEL_ARG *SEL_ARG::clone(SEL_ARG *new_parent,SEL_ARG **next_arg) } else { - if (!(tmp= new SEL_ARG(field,part, min_value,max_value, - min_flag, max_flag, maybe_flag))) + if (!(tmp= new (param->mem_root) SEL_ARG(field,part, min_value,max_value, + min_flag, max_flag, maybe_flag))) return 0; // OOM tmp->parent=new_parent; tmp->next_key_part=next_key_part; if (left != &null_element) - tmp->left=left->clone(tmp,next_arg); + tmp->left=left->clone(param, tmp, next_arg); tmp->prev= *next_arg; // Link into next/prev chain (*next_arg)->next=tmp; (*next_arg)= tmp; if (right != &null_element) - if (!(tmp->right= right->clone(tmp,next_arg))) + if (!(tmp->right= right->clone(param, tmp, next_arg))) return 0; // OOM } increment_use_count(1); @@ -672,11 +766,12 @@ static int sel_cmp(Field *field, char *a,char *b,uint8 a_flag,uint8 b_flag) } -SEL_ARG *SEL_ARG::clone_tree() +SEL_ARG *SEL_ARG::clone_tree(PARAM *param) { SEL_ARG tmp_link,*next_arg,*root; next_arg= &tmp_link; - root= clone((SEL_ARG *) 0, &next_arg); + if (!(root= clone(param, (SEL_ARG *) 0, &next_arg))) + return 0; next_arg->next=0; // Fix last link tmp_link.next->prev=0; // Fix first link if (root) // If not OOM @@ -890,6 +985,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, param.real_keynr[param.keys++]=idx; } param.key_parts_end=key_parts; + param.alloced_sel_args= 0; if ((tree=get_mm_tree(¶m,cond))) { @@ -991,7 +1087,8 @@ static SEL_TREE *get_mm_tree(PARAM *param,COND *cond) while ((item=li++)) { SEL_TREE *new_tree=get_mm_tree(param,item); - if (param->thd->is_fatal_error) + if (param->thd->is_fatal_error || + param->alloced_sel_args > SEL_ARG::MAX_SEL_ARGS) DBUG_RETURN(0); // out of memory tree=tree_and(param,tree,new_tree); if (tree && tree->type == SEL_TREE::IMPOSSIBLE) @@ -1524,7 +1621,7 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) tree1->type=SEL_TREE::KEY_SMALLER; DBUG_RETURN(tree1); } - + /* Join the trees key per key */ SEL_ARG **key1,**key2,**end; for (key1= tree1->keys,key2= tree2->keys,end=key1+param->keys ; @@ -1537,7 +1634,7 @@ tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) flag|=CLONE_KEY1_MAYBE; if (*key2 && !(*key2)->simple_key()) flag|=CLONE_KEY2_MAYBE; - *key1=key_and(*key1,*key2,flag); + *key1=key_and(param, *key1, *key2, flag); if (*key1 && (*key1)->type == SEL_ARG::IMPOSSIBLE) { tree1->type= SEL_TREE::IMPOSSIBLE; @@ -1574,7 +1671,7 @@ tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) for (key1= tree1->keys,key2= tree2->keys,end=key1+param->keys ; key1 != end ; key1++,key2++) { - *key1=key_or(*key1,*key2); + *key1= key_or(param, *key1, *key2); if (*key1) { result=tree1; // Added to tree1 @@ -1590,14 +1687,14 @@ tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) /* And key trees where key1->part < key2 -> part */ static SEL_ARG * -and_all_keys(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag) +and_all_keys(PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag) { SEL_ARG *next; ulong use_count=key1->use_count; if (key1->elements != 1) { - key2->use_count+=key1->elements-1; + key2->use_count+=key1->elements-1; //psergey: why we don't count that key1 has n-k-p? key2->increment_use_count((int) key1->elements-1); } if (key1->type == SEL_ARG::MAYBE_KEY) @@ -1609,7 +1706,7 @@ and_all_keys(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag) { if (next->next_key_part) { - SEL_ARG *tmp=key_and(next->next_key_part,key2,clone_flag); + SEL_ARG *tmp= key_and(param, next->next_key_part, key2, clone_flag); if (tmp && tmp->type == SEL_ARG::IMPOSSIBLE) { key1=key1->tree_delete(next); @@ -1618,6 +1715,8 @@ and_all_keys(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag) next->next_key_part=tmp; if (use_count) next->increment_use_count(use_count); + if (param->alloced_sel_args > SEL_ARG::MAX_SEL_ARGS) + break; } else next->next_key_part=key2; @@ -1644,7 +1743,7 @@ and_all_keys(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag) */ static SEL_ARG * -key_and(SEL_ARG *key1, SEL_ARG *key2, uint clone_flag) +key_and(PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag) { if (!key1) return key2; @@ -1660,9 +1759,9 @@ key_and(SEL_ARG *key1, SEL_ARG *key2, uint clone_flag) // key1->part < key2->part key1->use_count--; if (key1->use_count > 0) - if (!(key1= key1->clone_tree())) + if (!(key1= key1->clone_tree(param))) return 0; // OOM - return and_all_keys(key1,key2,clone_flag); + return and_all_keys(param, key1, key2, clone_flag); } if (((clone_flag & CLONE_KEY2_MAYBE) && @@ -1680,14 +1779,14 @@ key_and(SEL_ARG *key1, SEL_ARG *key2, uint clone_flag) if (key1->use_count > 1) { key1->use_count--; - if (!(key1=key1->clone_tree())) + if (!(key1=key1->clone_tree(param))) return 0; // OOM key1->use_count++; } if (key1->type == SEL_ARG::MAYBE_KEY) { // Both are maybe key - key1->next_key_part=key_and(key1->next_key_part,key2->next_key_part, - clone_flag); + key1->next_key_part=key_and(param, key1->next_key_part, + key2->next_key_part, clone_flag); if (key1->next_key_part && key1->next_key_part->type == SEL_ARG::IMPOSSIBLE) return key1; @@ -1698,7 +1797,7 @@ key_and(SEL_ARG *key1, SEL_ARG *key2, uint clone_flag) if (key2->next_key_part) { key1->use_count--; // Incremented in and_all_keys - return and_all_keys(key1,key2,clone_flag); + return and_all_keys(param, key1, key2, clone_flag); } key2->use_count--; // Key2 doesn't have a tree } @@ -1727,7 +1826,8 @@ key_and(SEL_ARG *key1, SEL_ARG *key2, uint clone_flag) } else if (get_range(&e2,&e1,key2)) continue; - SEL_ARG *next=key_and(e1->next_key_part,e2->next_key_part,clone_flag); + SEL_ARG *next=key_and(param, e1->next_key_part, e2->next_key_part, + clone_flag); e1->increment_use_count(1); e2->increment_use_count(1); if (!next || next->type != SEL_ARG::IMPOSSIBLE) @@ -1775,7 +1875,7 @@ get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1) static SEL_ARG * -key_or(SEL_ARG *key1,SEL_ARG *key2) +key_or(PARAM *param, SEL_ARG *key1,SEL_ARG *key2) { if (!key1) { @@ -1823,7 +1923,7 @@ key_or(SEL_ARG *key1,SEL_ARG *key2) { swap_variables(SEL_ARG *,key1,key2); } - if (key1->use_count > 0 || !(key1=key1->clone_tree())) + if (key1->use_count > 0 || !(key1=key1->clone_tree(param))) return 0; // OOM } @@ -1967,7 +2067,7 @@ key_or(SEL_ARG *key1,SEL_ARG *key2) { // tmp.min. <= x <= tmp.max tmp->maybe_flag|= key.maybe_flag; key.increment_use_count(key1->use_count+1); - tmp->next_key_part=key_or(tmp->next_key_part,key.next_key_part); + tmp->next_key_part= key_or(param, tmp->next_key_part, key.next_key_part); if (!cmp) // Key2 is ready break; key.copy_max_to_min(tmp); @@ -1998,7 +2098,7 @@ key_or(SEL_ARG *key1,SEL_ARG *key2) tmp->increment_use_count(key1->use_count+1); /* Increment key count as it may be used for next loop */ key.increment_use_count(1); - new_arg->next_key_part=key_or(tmp->next_key_part,key.next_key_part); + new_arg->next_key_part= key_or(param, tmp->next_key_part, key.next_key_part); key1=key1->insert(new_arg); break; } From 9939b3b75ecae753a8b3ce1463d981602bb56c18 Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Thu, 29 Mar 2007 10:27:58 +0400 Subject: [PATCH 12/13] BUG#26624: high mem usage (crash) in range optimizer - Post-review fixes --- sql/opt_range.cc | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 11ba6c0e465..ca8f31f5775 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -175,7 +175,7 @@ static char is_null_string[2]= {1,0}; Example: By induction: Let's take any interval on some keypart in the middle: - kp15=1 + kp15=c0 Then let's AND it with this interval 'structure' from preceding and following keyparts: @@ -184,18 +184,18 @@ static char is_null_string[2]= {1,0}; We will obtain this SEL_ARG graph: - kp14 $ kp15 $ kp16 - $ $ - +---------+ $ +--------+ $ +---------+ - | kp14=c1 |--$-->| kp15=1 |--$-->| kp16=c3 | - +---------+ $ +--------+ $ +---------+ - | $ $ - +---------+ $ +--------+ $ - | kp14=c2 |--$-->| kp15=1 | $ - +---------+ $ +--------+ $ - $ $ + kp14 $ kp15 $ kp16 + $ $ + +---------+ $ +---------+ $ +---------+ + | kp14=c1 |--$-->| kp15=c0 |--$-->| kp16=c3 | + +---------+ $ +---------+ $ +---------+ + | $ $ + +---------+ $ +---------+ $ + | kp14=c2 |--$-->| kp15=c0 | $ + +---------+ $ +---------+ $ + $ $ - Note that we had to duplicate "kp15=1" and there was no way to avoid + Note that we had to duplicate "kp15=c0" and there was no way to avoid that. The induction step: AND the obtained expression with another "wrapping" expression like (*). @@ -477,7 +477,7 @@ typedef struct st_qsel_param { max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; bool quick; // Don't calulate possible keys COND *cond; - /* Numbr of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */ + /* Number of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */ uint alloced_sel_args; } PARAM; @@ -681,7 +681,8 @@ SEL_ARG *SEL_ARG::clone(PARAM *param, SEL_ARG *new_parent, SEL_ARG **next_arg) tmp->parent=new_parent; tmp->next_key_part=next_key_part; if (left != &null_element) - tmp->left=left->clone(param, tmp, next_arg); + if (!(tmp->left=left->clone(param, tmp, next_arg))) + return 0; // OOM tmp->prev= *next_arg; // Link into next/prev chain (*next_arg)->next=tmp; From 064157b6e81a8ffdcf1e8f03857b34eaf270f8c0 Mon Sep 17 00:00:00 2001 From: "sergefp@mysql.com" <> Date: Thu, 29 Mar 2007 14:54:08 +0400 Subject: [PATCH 13/13] BUG#26624, merge to 5.1: make partition pruning module account for the changes made by the bug fix. --- mysql-test/r/subselect.result | 1 + sql/opt_range.cc | 35 ++++++++++++++++++++--------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 4b2ca585d61..089fb10aaae 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -3946,6 +3946,7 @@ SELECT a FROM t1 t0 WHERE (SELECT COUNT(t0.b) FROM t1 t WHERE t.b>20) GROUP BY a; ERROR HY000: Invalid use of group function SET @@sql_mode=default; +DROP TABLE t1; CREATE TABLE t1 (s1 char(1)); INSERT INTO t1 VALUES ('a'); SELECT * FROM t1 WHERE _utf8'a' = ANY (SELECT s1 FROM t1); diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 0c94a58c429..88e0eb27d80 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -84,7 +84,7 @@ static int sel_cmp(Field *f,char *a,char *b,uint8 a_flag,uint8 b_flag); static char is_null_string[2]= {1,0}; - +class RANGE_OPT_PARAM; /* A construction block of the SEL_ARG-graph. @@ -356,8 +356,7 @@ public: return new SEL_ARG(field, part, min_value, arg->max_value, min_flag, arg->max_flag, maybe_flag | arg->maybe_flag); } - SEL_ARG *clone(struct st_qsel_param *param, SEL_ARG *new_parent, - SEL_ARG **next); + SEL_ARG *clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent, SEL_ARG **next); bool copy_min(SEL_ARG* arg) { // Get overlapping range @@ -551,7 +550,7 @@ public: } return !field->key_cmp(min_val, max_val); } - SEL_ARG *clone_tree(struct st_qsel_param *param); + SEL_ARG *clone_tree(RANGE_OPT_PARAM *param); }; class SEL_IMERGE; @@ -631,6 +630,8 @@ public: using_real_indexes==TRUE */ uint real_keynr[MAX_KEY]; + /* Number of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */ + uint alloced_sel_args; }; class PARAM : public RANGE_OPT_PARAM @@ -659,8 +660,6 @@ public: /* Number of ranges in the last checked tree->key */ uint n_ranges; uint8 first_null_comp; /* first null component if any, 0 - otherwise */ - /* Number of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */ - uint alloced_sel_args; }; class TABLE_READ_PLAN; @@ -722,8 +721,9 @@ static void print_quick(QUICK_SELECT_I *quick, const key_map *needed_reg); static SEL_TREE *tree_and(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2); static SEL_TREE *tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2); static SEL_ARG *sel_add(SEL_ARG *key1,SEL_ARG *key2); -static SEL_ARG *key_or(PARAM *param, SEL_ARG *key1,SEL_ARG *key2); -static SEL_ARG *key_and(PARAM *param, SEL_ARG *key1,SEL_ARG *key2,uint clone_flag); +static SEL_ARG *key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2); +static SEL_ARG *key_and(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2, + uint clone_flag); static bool get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1); bool get_quick_keys(PARAM *param,QUICK_RANGE_SELECT *quick,KEY_PART *key, SEL_ARG *key_tree,char *min_key,uint min_key_flag, @@ -1599,7 +1599,8 @@ SEL_ARG::SEL_ARG(Field *field_,uint8 part_,char *min_value_,char *max_value_, left=right= &null_element; } -SEL_ARG *SEL_ARG::clone(PARAM *param, SEL_ARG *new_parent, SEL_ARG **next_arg) +SEL_ARG *SEL_ARG::clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent, + SEL_ARG **next_arg) { SEL_ARG *tmp; @@ -1709,7 +1710,7 @@ static int sel_cmp(Field *field, char *a,char *b,uint8 a_flag,uint8 b_flag) } -SEL_ARG *SEL_ARG::clone_tree(PARAM *param) +SEL_ARG *SEL_ARG::clone_tree(RANGE_OPT_PARAM *param) { SEL_ARG tmp_link,*next_arg,*root; next_arg= &tmp_link; @@ -2609,6 +2610,7 @@ bool prune_partitions(THD *thd, TABLE *table, Item *pprune_cond) range_par->using_real_indexes= FALSE; range_par->remove_jump_scans= FALSE; range_par->real_keynr[0]= 0; + range_par->alloced_sel_args= 0; thd->no_errors=1; // Don't warn about NULL thd->mem_root=&alloc; @@ -6156,7 +6158,8 @@ tree_or(RANGE_OPT_PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2) /* And key trees where key1->part < key2 -> part */ static SEL_ARG * -and_all_keys(PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag) +and_all_keys(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2, + uint clone_flag) { SEL_ARG *next; ulong use_count=key1->use_count; @@ -6202,8 +6205,10 @@ and_all_keys(PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag) SYNOPSIS key_and() - key1 First argument, root of its RB-tree - key2 Second argument, root of its RB-tree + param Range analysis context (needed to track if we have allocated + too many SEL_ARGs) + key1 First argument, root of its RB-tree + key2 Second argument, root of its RB-tree RETURN RB-tree root of the resulting SEL_ARG graph. @@ -6211,7 +6216,7 @@ and_all_keys(PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag) */ static SEL_ARG * -key_and(PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag) +key_and(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag) { if (!key1) return key2; @@ -6350,7 +6355,7 @@ get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1) static SEL_ARG * -key_or(PARAM *param, SEL_ARG *key1,SEL_ARG *key2) +key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2) { if (!key1) {