List:Commits« Previous MessageNext Message »
From:Gleb Shchepa Date:January 30 2009 3:27pm
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-01-30
      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)
      
      hogged memory or even caused server crash on tables with
      significant number of rows (for example, this query failed
      on 1000 of records in every table and 50M of free
      virtual memory).
      
      Subselect engine resets JOIN structure of dependent inner
      query on every row of outer table. In this case restore_tmp()
      reset tmp_join->join_tab_reexec and tmp_join->table_reexec
      methods to NULL pointers and the next call to make_simple_join()
      reallocated them again and again.
      
      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-01-30 15:27:17 +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     'this' or parent JOIN object referencing 'this' as tmp_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-01-30 15:27:17 +0000
@@ -354,7 +354,7 @@ public:
     excessive memory usage.
   */  
   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 +384,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 +476,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 Shchepa30 Jan