List:Commits« Previous MessageNext Message »
From:<gshchepa Date:May 30 2007 2:37pm
Subject:bk commit into 5.0 tree (gshchepa:1.2504) BUG#28598
View as plain text  
Below is the list of changes that have just been committed into a local
5.0 repository of uchum. When uchum does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet@stripped, 2007-05-30 19:37:37+05:00, gshchepa@stripped +3 -0
  Fixed bug #28598.
  mysqld crashed when a long-running explain query was killed from
  another connection.
  
  When current thread was marked as killed, the
  best_extension_by_limited_search() function was silently returning
  without initialization of join->best_positions[] array elements.
  However, the greedy_search() function ignored thd->killed status
  after a calls to the best_extension_by_limited_search() function, and
  after several calls the greedy_search() function used an uninitialized
  data from the join->best_positions[idx] to search position in the
  join->best_ref[] array. 
  That search failed, and greedy_search() tried to call swap_variables()
  function with NULL argument - that is a crash.

  mysql-test/r/kill.result@stripped, 2007-05-30 19:37:17+05:00, gshchepa@stripped +2 -0
    Updated test case for bug #28598.

  mysql-test/t/kill.test@stripped, 2007-05-30 19:37:15+05:00, gshchepa@stripped +56 -0
    Updated test case for bug #28598.

  sql/sql_select.cc@stripped, 2007-05-30 19:36:17+05:00, gshchepa@stripped +51 -40
    Fixed bug #28598.
    choose_plan(), greedy_search(), best_extension_by_limited_search()
    and find_best() functions have been changed to return TRUE in case
    of fatal error.

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	gshchepa
# Host:	gleb.loc
# Root:	/home/uchum/work/bk/mysql-5.0-opt-28598

--- 1.526/sql/sql_select.cc	2007-05-27 01:19:34 +05:00
+++ 1.527/sql/sql_select.cc	2007-05-30 19:36:17 +05:00
@@ -49,15 +49,15 @@ static int sort_keyuse(KEYUSE *a,KEYUSE 
 static void set_position(JOIN *join,uint index,JOIN_TAB *table,KEYUSE *key);
 static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
 			       table_map used_tables);
-static void choose_plan(JOIN *join,table_map join_tables);
+static bool choose_plan(JOIN *join,table_map join_tables);
 
 static void best_access_path(JOIN *join, JOIN_TAB *s, THD *thd,
                              table_map remaining_tables, uint idx,
                              double record_count, double read_time);
 static void optimize_straight_join(JOIN *join, table_map join_tables);
-static void greedy_search(JOIN *join, table_map remaining_tables,
+static bool greedy_search(JOIN *join, table_map remaining_tables,
                              uint depth, uint prune_level);
-static void best_extension_by_limited_search(JOIN *join,
+static bool best_extension_by_limited_search(JOIN *join,
                                              table_map remaining_tables,
                                              uint idx, double record_count,
                                              double read_time, uint depth,
@@ -69,7 +69,7 @@ static int join_tab_cmp_straight(const v
   TODO: 'find_best' is here only temporarily until 'greedy_search' is
   tested and approved.
 */
-static void find_best(JOIN *join,table_map rest_tables,uint index,
+static bool find_best(JOIN *join,table_map rest_tables,uint index,
 		      double record_count,double read_time);
 static uint cache_record_length(JOIN *join,uint index);
 static double prev_record_reads(JOIN *join,table_map found_ref);
@@ -2717,7 +2717,8 @@ make_join_statistics(JOIN *join, TABLE_L
   if (join->const_tables != join->tables)
   {
     optimize_keyuse(join, keyuse_array);
-    choose_plan(join, all_table_map & ~join->const_table_map);
+    if (choose_plan(join, all_table_map & ~join->const_table_map))
+      DBUG_RETURN(TRUE);
   }
   else
   {
@@ -4307,11 +4308,12 @@ best_access_path(JOIN      *join,
     the array 'join->best_positions', and the cost of the plan in
     'join->best_read'.
 
-  RETURN
-    None
+  RETURN VALUES
+    FALSE       ok
+    TRUE        Fatal error
 */
 
-static void
+static bool
 choose_plan(JOIN *join, table_map join_tables)
 {
   uint search_depth= join->thd->variables.optimizer_search_depth;
@@ -4344,14 +4346,16 @@ choose_plan(JOIN *join, table_map join_t
         the greedy version. Will be removed when greedy_search is approved.
       */
       join->best_read= DBL_MAX;
-      find_best(join, join_tables, join->const_tables, 1.0, 0.0);
+      if (find_best(join, join_tables, join->const_tables, 1.0, 0.0))
+        DBUG_RETURN(TRUE);
     } 
     else
     {
       if (search_depth == 0)
         /* Automatically determine a reasonable value for 'search_depth' */
         search_depth= determine_search_depth(join);
-      greedy_search(join, join_tables, search_depth, prune_level);
+      if (greedy_search(join, join_tables, search_depth, prune_level))
+        DBUG_RETURN(TRUE);
     }
   }
 
@@ -4361,7 +4365,7 @@ choose_plan(JOIN *join, table_map join_t
   */
   if (join->thd->lex->orig_sql_command != SQLCOM_SHOW_STATUS)
     join->thd->status_var.last_query_cost= join->best_read;
-  DBUG_VOID_RETURN;
+  DBUG_RETURN(FALSE);
 }
 
 
@@ -4589,11 +4593,12 @@ optimize_straight_join(JOIN *join, table
     In the future, 'greedy_search' might be extended to support other
     implementations of 'best_extension', e.g. some simpler quadratic procedure.
 
-  RETURN
-    None
+  RETURN VALUES
+    FALSE       ok
+    TRUE        Fatal error
 */
 
-static void
+static bool
 greedy_search(JOIN      *join,
               table_map remaining_tables,
               uint      search_depth,
@@ -4615,8 +4620,9 @@ greedy_search(JOIN      *join,
   do {
     /* Find the extension of the current QEP with the lowest cost */
     join->best_read= DBL_MAX;
-    best_extension_by_limited_search(join, remaining_tables, idx, record_count,
-                                     read_time, search_depth, prune_level);
+    if (best_extension_by_limited_search(join, remaining_tables, idx, record_count,
+                                         read_time, search_depth, prune_level))
+      DBUG_RETURN(TRUE);
 
     if (size_remain <= search_depth)
     {
@@ -4627,7 +4633,7 @@ greedy_search(JOIN      *join,
       DBUG_EXECUTE("opt", print_plan(join, join->tables,
                                      record_count, read_time, read_time,
                                      "optimal"););
-      DBUG_VOID_RETURN;
+      DBUG_RETURN(FALSE);
     }
 
     /* select the first table in the optimal extension as most promising */
@@ -4772,11 +4778,12 @@ greedy_search(JOIN      *join,
     The parameter 'search_depth' provides control over the recursion
     depth, and thus the size of the resulting optimal plan.
 
-  RETURN
-    None
+  RETURN VALUES
+    FALSE       ok
+    TRUE        Fatal error
 */
 
-static void
+static bool
 best_extension_by_limited_search(JOIN      *join,
                                  table_map remaining_tables,
                                  uint      idx,
@@ -4785,11 +4792,11 @@ best_extension_by_limited_search(JOIN   
                                  uint      search_depth,
                                  uint      prune_level)
 {
+  DBUG_ENTER("best_extension_by_limited_search");
+
   THD *thd= join->thd;
   if (thd->killed)  // Abort
-    return;
-
-  DBUG_ENTER("best_extension_by_limited_search");
+    DBUG_RETURN(TRUE);
 
   /* 
      'join' is a partial plan with lower cost than the best plan so far,
@@ -4869,15 +4876,14 @@ best_extension_by_limited_search(JOIN   
       if ( (search_depth > 1) && (remaining_tables & ~real_table_bit) )
       { /* Recursively expand the current partial plan */
         swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
-        best_extension_by_limited_search(join,
-                                         remaining_tables & ~real_table_bit,
-                                         idx + 1,
-                                         current_record_count,
-                                         current_read_time,
-                                         search_depth - 1,
-                                         prune_level);
-        if (thd->killed)
-          DBUG_VOID_RETURN;
+        if (best_extension_by_limited_search(join,
+                                             remaining_tables & ~real_table_bit,
+                                             idx + 1,
+                                             current_record_count,
+                                             current_read_time,
+                                             search_depth - 1,
+                                             prune_level))
+          DBUG_RETURN(TRUE);
         swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
       }
       else
@@ -4906,18 +4912,23 @@ best_extension_by_limited_search(JOIN   
       restore_prev_nj_state(s);
     }
   }
-  DBUG_VOID_RETURN;
+  DBUG_RETURN(FALSE);
 }
 
 
 /*
   TODO: this function is here only temporarily until 'greedy_search' is
   tested and accepted.
+
+  RETURN VALUES
+    FALSE       ok
+    TRUE        Fatal error
 */
-static void
+static bool
 find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
 	  double read_time)
 {
+  DBUG_ENTER("find_best");
   THD *thd= join->thd;
   if (!rest_tables)
   {
@@ -4935,10 +4946,10 @@ find_best(JOIN *join,table_map rest_tabl
 	     sizeof(POSITION)*idx);
       join->best_read= read_time - 0.001;
     }
-    return;
+    DBUG_RETURN(FALSE);
   }
   if (read_time+record_count/(double) TIME_FOR_COMPARE >= join->best_read)
-    return;					/* Found better before */
+    DBUG_RETURN(FALSE);					/* Found better before */
 
   JOIN_TAB *s;
   double best_record_count=DBL_MAX,best_read_time=DBL_MAX;
@@ -4971,10 +4982,9 @@ find_best(JOIN *join,table_map rest_tabl
 	  best_read_time=current_read_time;
 	}
 	swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
-	find_best(join,rest_tables & ~real_table_bit,idx+1,
-		  current_record_count,current_read_time);
-        if (thd->killed)
-          return;
+	if (find_best(join,rest_tables & ~real_table_bit,idx+1,
+                      current_record_count,current_read_time))
+          DBUG_RETURN(TRUE);
 	swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
       }
       restore_prev_nj_state(s);
@@ -4982,6 +4992,7 @@ find_best(JOIN *join,table_map rest_tabl
 	break;				// Don't test all combinations
     }
   }
+  DBUG_RETURN(FALSE);
 }
 
 

--- 1.17/mysql-test/r/kill.result	2007-05-24 00:23:33 +05:00
+++ 1.18/mysql-test/r/kill.result	2007-05-30 19:37:17 +05:00
@@ -123,3 +123,5 @@ release_lock("lock27563")
 drop table t1, t2;
 drop function bug27563;
 drop procedure proc27563;
+PREPARE stmt FROM 'EXPLAIN SELECT * FROM  t40, t39, t38, t37, t36, t35, t34, t33, t32, t31, t30, t29, t28, t27, t26, t25, t24, t23, t22, t21, t20, t19, t18, t17, t16, t15, t14, t13, t12, t11, t10, t9, t8, t7, t6, t5, t4, t3, t2, t1 WHERE  a39=a40 AND a38=a39 AND a37=a38 AND a36=a37 AND a35=a36 AND a34=a35 AND a33=a34 AND a32=a33 AND a31=a32 AND a30=a31 AND a29=a30 AND a28=a29 AND a27=a28 AND a26=a27 AND a25=a26 AND a24=a25 AND a23=a24 AND a22=a23 AND a21=a22 AND a20=a21 AND a19=a20 AND a18=a19 AND a17=a18 AND a16=a17 AND a15=a16 AND a14=a15 AND a13=a14 AND a12=a13 AND a11=a12 AND a10=a11 AND a9=a10 AND a8=a9 AND a7=a8 AND a6=a7 AND a5=a6 AND a4=a5 AND a3=a4 AND a2=a3 AND a1=a2';
+EXECUTE stmt;

--- 1.25/mysql-test/t/kill.test	2007-05-24 00:23:43 +05:00
+++ 1.26/mysql-test/t/kill.test	2007-05-30 19:37:15 +05:00
@@ -249,3 +249,59 @@ select release_lock("lock27563");
 drop table t1, t2;
 drop function bug27563;
 drop procedure proc27563;
+
+#
+# Bug#28598: mysqld crash when killing a long-running explain query.
+#
+--disable_query_log
+connection con1;
+let $ID= `select connection_id()`;
+let $tab_count= 40;
+
+let $i= $tab_count;
+while ($i)
+{
+  eval CREATE TABLE t$i (a$i int, KEY(a$i));
+  eval INSERT INTO t$i VALUES (1),(2),(3),(4),(5),(6),(7);
+  dec $i ;
+}
+set session optimizer_search_depth=0;
+
+let $from= FROM ;
+let $where= WHERE;
+let $i= $tab_count;
+while ($i)
+{
+  let $from=$from$comma t$i;
+  if (!$comma)
+  {
+    let $comma=,;
+  }
+  let $j= $i;
+  dec $j;
+  if ($j)
+  {
+    let $where=$where $and a$j=a$i;
+    if (!$and)
+    {
+      let $and= AND;
+    }
+  }
+  dec $i;
+}
+
+--enable_query_log
+eval PREPARE stmt FROM 'EXPLAIN SELECT * $from $where';
+send EXECUTE stmt;
+--disable_query_log
+
+connection con2;
+real_sleep 2;
+eval kill query $ID;
+let $i= $tab_count;
+while ($i)
+{
+  eval DROP TABLE t$i;
+  dec $i ;
+}
+--enable_query_log
Thread
bk commit into 5.0 tree (gshchepa:1.2504) BUG#28598gshchepa30 May