List:Commits« Previous MessageNext Message »
From:Gleb Shchepa Date:February 2 2009 4:50pm
Subject:bzr commit into mysql-5.0-bugteam branch (gshchepa:2728) Bug#42037
View as plain text  
#At file:///work/bzr/5.0-42037/ based on revid:azundris@stripped

 2728 Gleb Shchepa	2009-02-02
      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.
modified:
  sql/sql_select.cc
  sql/sql_select.h

per-file messages:
  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.
=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2008-12-24 15:24:11 +0000
+++ b/sql/sql_select.cc	2009-02-02 16:50:16 +0000
@@ -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);
@@ -1804,7 +1803,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,
@@ -1924,7 +1923,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, 
@@ -5414,48 +5413,42 @@ store_val_in_field(Field *field, Item *i
 }
 
 
-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;
@@ -5472,7 +5465,7 @@ make_simple_join(JOIN *join,TABLE *tmp_t
   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;

=== modified file 'sql/sql_select.h'
--- a/sql/sql_select.h	2008-12-24 15:24:11 +0000
+++ b/sql/sql_select.h	2009-02-02 16:50:16 +0000
@@ -352,9 +352,12 @@ public:
     cleared only at the end of the execution of the whole query and not caching
     allocations that occur in repetition at execution time will result in 
     excessive memory usage.
+    Note: make_simple_join always creates an execution plan that accesses
+    a single table, thus it is sufficient to have a one-element array for
+    table_reexec.
   */  
   SORT_FIELD *sortorder;                        // make_unireg_sortorder()
-  TABLE **table_reexec;                         // make_simple_join()
+  TABLE *table_reexec[1];                       // make_simple_join()
   JOIN_TAB *join_tab_reexec;                    // make_simple_join()
   /* end of allocation caching storage */
 
@@ -384,7 +387,7 @@ public:
     exec_tmp_table1= 0;
     exec_tmp_table2= 0;
     sortorder= 0;
-    table_reexec= 0;
+    table_reexec[0]= 0;
     join_tab_reexec= 0;
     thd= thd_arg;
     sum_funcs= sum_funcs2= 0;
@@ -476,6 +479,8 @@ public:
     return (unit == &thd->lex->unit && (unit->fake_select_lex == 0 ||
                                         select_lex == unit->fake_select_lex));
   }
+private:
+  bool make_simple_join(JOIN *join, TABLE *tmp_table);
 };
 
 

Thread
bzr commit into mysql-5.0-bugteam branch (gshchepa:2728) Bug#42037Gleb Shchepa2 Feb