summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/r/kill.result2
-rw-r--r--mysql-test/t/kill.test53
-rw-r--r--sql/sql_select.cc93
3 files changed, 108 insertions, 40 deletions
diff --git a/mysql-test/r/kill.result b/mysql-test/r/kill.result
index 4bc04c6de3c..a4966d9d5ec 100644
--- a/mysql-test/r/kill.result
+++ b/mysql-test/r/kill.result
@@ -123,3 +123,5 @@ release_lock("lock27563")
drop table t1, t2;
drop function bug27563;
drop procedure proc27563;
+PREPARE stmt FROM 'EXPLAIN SELECT * FROM t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20,t21,t22,t23,t24,t25,t26,t27,t28,t29,t30,t31,t32,t33,t34,t35,t36,t37,t38,t39,t40 WHERE a1=a2 AND a2=a3 AND a3=a4 AND a4=a5 AND a5=a6 AND a6=a7 AND a7=a8 AND a8=a9 AND a9=a10 AND a10=a11 AND a11=a12 AND a12=a13 AND a13=a14 AND a14=a15 AND a15=a16 AND a16=a17 AND a17=a18 AND a18=a19 AND a19=a20 AND a20=a21 AND a21=a22 AND a22=a23 AND a23=a24 AND a24=a25 AND a25=a26 AND a26=a27 AND a27=a28 AND a28=a29 AND a29=a30 AND a30=a31 AND a31=a32 AND a32=a33 AND a33=a34 AND a34=a35 AND a35=a36 AND a36=a37 AND a37=a38 AND a38=a39 AND a39=a40 ';
+EXECUTE stmt;
diff --git a/mysql-test/t/kill.test b/mysql-test/t/kill.test
index 83194e214fb..5f6bae00254 100644
--- a/mysql-test/t/kill.test
+++ b/mysql-test/t/kill.test
@@ -249,3 +249,56 @@ 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 $i=$tab_count;
+while ($i)
+{
+ let $a= a$i;
+ let $t= t$i;
+ dec $i;
+ if ($i)
+ {
+ let $comma=,;
+ let $from=$comma$t$from;
+ let $where=a$i=$a $and $where;
+ }
+ if (!$i)
+ {
+ let $from=FROM $t$from;
+ let $where=WHERE $where;
+ }
+ let $and=AND;
+}
+
+--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
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 57dff4b23a5..b240e88e983 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -49,15 +49,15 @@ static int sort_keyuse(KEYUSE *a,KEYUSE *b);
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 void* ptr1, const void* ptr2);
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_LIST *tables, COND *conds,
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_tables)
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_tables)
*/
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_map join_tables)
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 *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 *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,19 +4912,26 @@ best_extension_by_limited_search(JOIN *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 (thd->killed)
+ DBUG_RETURN(TRUE);
if (!rest_tables)
{
DBUG_PRINT("best",("read_time: %g record_count: %g",read_time,
@@ -4935,10 +4948,10 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
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 +4984,9 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
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 +4994,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
break; // Don't test all combinations
}
}
+ DBUG_RETURN(FALSE);
}