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 1/4] 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 2/4] 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 3/4] 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 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 4/4] 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);