From 061bf717e0a0b46b2b05567b569bcfe53bbfc12f Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Thu, 5 Feb 2009 13:30:39 +0400 Subject: Bug #42037: Queries containing a subquery with DISTINCT and ORDER BY could cause a server crash Dependent subqueries like SELECT COUNT(*) FROM t1, t2 WHERE t2.b IN (SELECT DISTINCT t2.b FROM t2 WHERE t2.b = t1.a) caused a memory leak proportional to the number of outer rows. The make_simple_join() function has been modified to JOIN class method to store join_tab_reexec and table_reexec values in the parent join only (make_simple_join of tmp_join may access these values via 'this' pointer of the parent JOIN). NOTE: this patch doesn't include standard test case (this is "out of memory" bug). See bug #42037 page for test cases. sql/sql_select.cc: Bug #42037: Queries containing a subquery with DISTINCT and ORDER BY could cause a server crash The make_simple_join() function has been modified to JOIN class method to store join_tab_reexec and table_reexec values in the parent join only. sql/sql_select.h: Bug #42037: Queries containing a subquery with DISTINCT and ORDER BY could cause a server crash 1. The make_simple_join() function has been modified to JOIN class method. 2. Type of JOIN::table_reexec field has been changed from TABLE** to TABLE *table_reexec[1]: this field always was NULL or a pointer to one-element array of pointers, so a pointer to a pointer has been replaced with one pointer and unnecessary memory allocation has been eliminated. --- sql/sql_select.cc | 73 +++++++++++++++++++++++++------------------------------ 1 file changed, 33 insertions(+), 40 deletions(-) (limited to 'sql/sql_select.cc') diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 74d1158d8b7..a341cf5e0e9 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -78,7 +78,6 @@ static store_key *get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables, KEY_PART_INFO *key_part, char *key_buff, uint maybe_null); -static bool make_simple_join(JOIN *join,TABLE *tmp_table); static void make_outerjoin_info(JOIN *join); static bool make_join_select(JOIN *join,SQL_SELECT *select,COND *item); static void make_join_readinfo(JOIN *join, ulonglong options); @@ -1809,7 +1808,7 @@ JOIN::exec() /* Free first data from old join */ curr_join->join_free(); - if (make_simple_join(curr_join, curr_tmp_table)) + if (curr_join->make_simple_join(this, curr_tmp_table)) DBUG_VOID_RETURN; calc_group_buffer(curr_join, group_list); count_field_types(select_lex, &curr_join->tmp_table_param, @@ -1929,7 +1928,7 @@ JOIN::exec() curr_join->select_distinct=0; } curr_tmp_table->reginfo.lock_type= TL_UNLOCK; - if (make_simple_join(curr_join, curr_tmp_table)) + if (curr_join->make_simple_join(this, curr_tmp_table)) DBUG_VOID_RETURN; calc_group_buffer(curr_join, curr_join->group_list); count_field_types(select_lex, &curr_join->tmp_table_param, @@ -5431,48 +5430,42 @@ store_val_in_field(Field *field, Item *item, enum_check_fields check_flag) } -static bool -make_simple_join(JOIN *join,TABLE *tmp_table) +/** + @details Initialize a JOIN as a query execution plan + that accesses a single table via a table scan. + + @param parent contains JOIN_TAB and TABLE object buffers for this join + @param tmp_table temporary table + + @retval FALSE success + @retval TRUE error occurred +*/ +bool +JOIN::make_simple_join(JOIN *parent, TABLE *tmp_table) { - TABLE **tableptr; - JOIN_TAB *join_tab; - DBUG_ENTER("make_simple_join"); + DBUG_ENTER("JOIN::make_simple_join"); /* Reuse TABLE * and JOIN_TAB if already allocated by a previous call to this function through JOIN::exec (may happen for sub-queries). */ - if (!join->table_reexec) - { - if (!(join->table_reexec= (TABLE**) join->thd->alloc(sizeof(TABLE*)))) - DBUG_RETURN(TRUE); /* purecov: inspected */ - if (join->tmp_join) - join->tmp_join->table_reexec= join->table_reexec; - } - if (!join->join_tab_reexec) - { - if (!(join->join_tab_reexec= - (JOIN_TAB*) join->thd->alloc(sizeof(JOIN_TAB)))) - DBUG_RETURN(TRUE); /* purecov: inspected */ - if (join->tmp_join) - join->tmp_join->join_tab_reexec= join->join_tab_reexec; - } - tableptr= join->table_reexec; - join_tab= join->join_tab_reexec; - - join->join_tab=join_tab; - join->table=tableptr; tableptr[0]=tmp_table; - join->tables=1; - join->const_tables=0; - join->const_table_map=0; - join->tmp_table_param.field_count= join->tmp_table_param.sum_func_count= - join->tmp_table_param.func_count=0; - join->tmp_table_param.copy_field=join->tmp_table_param.copy_field_end=0; - join->first_record=join->sort_and_group=0; - join->send_records=(ha_rows) 0; - join->group=0; - join->row_limit=join->unit->select_limit_cnt; - join->do_send_rows = (join->row_limit) ? 1 : 0; + if (!parent->join_tab_reexec && + !(parent->join_tab_reexec= (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB)))) + DBUG_RETURN(TRUE); /* purecov: inspected */ + + join_tab= parent->join_tab_reexec; + table= &parent->table_reexec[0]; parent->table_reexec[0]= tmp_table; + tables= 1; + const_tables= 0; + const_table_map= 0; + tmp_table_param.field_count= tmp_table_param.sum_func_count= + tmp_table_param.func_count= 0; + tmp_table_param.copy_field= tmp_table_param.copy_field_end=0; + first_record= sort_and_group=0; + send_records= (ha_rows) 0; + group= 0; + row_limit= unit->select_limit_cnt; + do_send_rows= row_limit ? 1 : 0; join_tab->cache.buff=0; /* No caching */ join_tab->table=tmp_table; @@ -5489,7 +5482,7 @@ make_simple_join(JOIN *join,TABLE *tmp_table) join_tab->ref.key = -1; join_tab->not_used_in_distinct=0; join_tab->read_first_record= join_init_read_record; - join_tab->join=join; + join_tab->join= this; join_tab->ref.key_parts= 0; bzero((char*) &join_tab->read_record,sizeof(join_tab->read_record)); tmp_table->status=0; -- cgit v1.2.1