Commit ebedff77 authored by unknown's avatar unknown

Fixed bug #28598.

mysqld crashed when a long-running explain query was killed from
another connection.

When the current thread caught a kill signal executing the function
best_extension_by_limited_search it just silently returned to  
the calling function greedy_search without initializing elements of
the join->best_positions array.
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 caused a crash.


sql/sql_select.cc:
  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.
mysql-test/t/kill.test:
  Updated test case for bug #28598.
mysql-test/r/kill.result:
  Updated test case for bug #28598.
parent d7a90fa1
...@@ -123,3 +123,5 @@ release_lock("lock27563") ...@@ -123,3 +123,5 @@ release_lock("lock27563")
drop table t1, t2; drop table t1, t2;
drop function bug27563; drop function bug27563;
drop procedure proc27563; 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;
...@@ -249,3 +249,56 @@ select release_lock("lock27563"); ...@@ -249,3 +249,56 @@ select release_lock("lock27563");
drop table t1, t2; drop table t1, t2;
drop function bug27563; drop function bug27563;
drop procedure proc27563; 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
...@@ -49,15 +49,15 @@ static int sort_keyuse(KEYUSE *a,KEYUSE *b); ...@@ -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 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, static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse,
table_map used_tables); 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, static void best_access_path(JOIN *join, JOIN_TAB *s, THD *thd,
table_map remaining_tables, uint idx, table_map remaining_tables, uint idx,
double record_count, double read_time); double record_count, double read_time);
static void optimize_straight_join(JOIN *join, table_map join_tables); 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); 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, table_map remaining_tables,
uint idx, double record_count, uint idx, double record_count,
double read_time, uint depth, double read_time, uint depth,
...@@ -69,7 +69,7 @@ static int join_tab_cmp_straight(const void* ptr1, const void* ptr2); ...@@ -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 TODO: 'find_best' is here only temporarily until 'greedy_search' is
tested and approved. 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); double record_count,double read_time);
static uint cache_record_length(JOIN *join,uint index); static uint cache_record_length(JOIN *join,uint index);
static double prev_record_reads(JOIN *join,table_map found_ref); 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, ...@@ -2717,7 +2717,8 @@ make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
if (join->const_tables != join->tables) if (join->const_tables != join->tables)
{ {
optimize_keyuse(join, keyuse_array); 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 else
{ {
...@@ -4307,11 +4308,12 @@ best_access_path(JOIN *join, ...@@ -4307,11 +4308,12 @@ best_access_path(JOIN *join,
the array 'join->best_positions', and the cost of the plan in the array 'join->best_positions', and the cost of the plan in
'join->best_read'. 'join->best_read'.
RETURN RETURN VALUES
None FALSE ok
TRUE Fatal error
*/ */
static void static bool
choose_plan(JOIN *join, table_map join_tables) choose_plan(JOIN *join, table_map join_tables)
{ {
uint search_depth= join->thd->variables.optimizer_search_depth; uint search_depth= join->thd->variables.optimizer_search_depth;
...@@ -4344,14 +4346,16 @@ choose_plan(JOIN *join, table_map join_tables) ...@@ -4344,14 +4346,16 @@ choose_plan(JOIN *join, table_map join_tables)
the greedy version. Will be removed when greedy_search is approved. the greedy version. Will be removed when greedy_search is approved.
*/ */
join->best_read= DBL_MAX; 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 else
{ {
if (search_depth == 0) if (search_depth == 0)
/* Automatically determine a reasonable value for 'search_depth' */ /* Automatically determine a reasonable value for 'search_depth' */
search_depth= determine_search_depth(join); 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) ...@@ -4361,7 +4365,7 @@ choose_plan(JOIN *join, table_map join_tables)
*/ */
if (join->thd->lex->orig_sql_command != SQLCOM_SHOW_STATUS) if (join->thd->lex->orig_sql_command != SQLCOM_SHOW_STATUS)
join->thd->status_var.last_query_cost= join->best_read; 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) ...@@ -4589,11 +4593,12 @@ optimize_straight_join(JOIN *join, table_map join_tables)
In the future, 'greedy_search' might be extended to support other In the future, 'greedy_search' might be extended to support other
implementations of 'best_extension', e.g. some simpler quadratic procedure. implementations of 'best_extension', e.g. some simpler quadratic procedure.
RETURN RETURN VALUES
None FALSE ok
TRUE Fatal error
*/ */
static void static bool
greedy_search(JOIN *join, greedy_search(JOIN *join,
table_map remaining_tables, table_map remaining_tables,
uint search_depth, uint search_depth,
...@@ -4615,8 +4620,9 @@ greedy_search(JOIN *join, ...@@ -4615,8 +4620,9 @@ greedy_search(JOIN *join,
do { do {
/* Find the extension of the current QEP with the lowest cost */ /* Find the extension of the current QEP with the lowest cost */
join->best_read= DBL_MAX; join->best_read= DBL_MAX;
best_extension_by_limited_search(join, remaining_tables, idx, record_count, if (best_extension_by_limited_search(join, remaining_tables, idx, record_count,
read_time, search_depth, prune_level); read_time, search_depth, prune_level))
DBUG_RETURN(TRUE);
if (size_remain <= search_depth) if (size_remain <= search_depth)
{ {
...@@ -4627,7 +4633,7 @@ greedy_search(JOIN *join, ...@@ -4627,7 +4633,7 @@ greedy_search(JOIN *join,
DBUG_EXECUTE("opt", print_plan(join, join->tables, DBUG_EXECUTE("opt", print_plan(join, join->tables,
record_count, read_time, read_time, record_count, read_time, read_time,
"optimal");); "optimal"););
DBUG_VOID_RETURN; DBUG_RETURN(FALSE);
} }
/* select the first table in the optimal extension as most promising */ /* select the first table in the optimal extension as most promising */
...@@ -4772,11 +4778,12 @@ greedy_search(JOIN *join, ...@@ -4772,11 +4778,12 @@ greedy_search(JOIN *join,
The parameter 'search_depth' provides control over the recursion The parameter 'search_depth' provides control over the recursion
depth, and thus the size of the resulting optimal plan. depth, and thus the size of the resulting optimal plan.
RETURN RETURN VALUES
None FALSE ok
TRUE Fatal error
*/ */
static void static bool
best_extension_by_limited_search(JOIN *join, best_extension_by_limited_search(JOIN *join,
table_map remaining_tables, table_map remaining_tables,
uint idx, uint idx,
...@@ -4785,11 +4792,11 @@ best_extension_by_limited_search(JOIN *join, ...@@ -4785,11 +4792,11 @@ best_extension_by_limited_search(JOIN *join,
uint search_depth, uint search_depth,
uint prune_level) uint prune_level)
{ {
DBUG_ENTER("best_extension_by_limited_search");
THD *thd= join->thd; THD *thd= join->thd;
if (thd->killed) // Abort if (thd->killed) // Abort
return; DBUG_RETURN(TRUE);
DBUG_ENTER("best_extension_by_limited_search");
/* /*
'join' is a partial plan with lower cost than the best plan so far, '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, ...@@ -4869,15 +4876,14 @@ best_extension_by_limited_search(JOIN *join,
if ( (search_depth > 1) && (remaining_tables & ~real_table_bit) ) if ( (search_depth > 1) && (remaining_tables & ~real_table_bit) )
{ /* Recursively expand the current partial plan */ { /* Recursively expand the current partial plan */
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos); swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
best_extension_by_limited_search(join, if (best_extension_by_limited_search(join,
remaining_tables & ~real_table_bit, remaining_tables & ~real_table_bit,
idx + 1, idx + 1,
current_record_count, current_record_count,
current_read_time, current_read_time,
search_depth - 1, search_depth - 1,
prune_level); prune_level))
if (thd->killed) DBUG_RETURN(TRUE);
DBUG_VOID_RETURN;
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos); swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
} }
else else
...@@ -4906,19 +4912,26 @@ best_extension_by_limited_search(JOIN *join, ...@@ -4906,19 +4912,26 @@ best_extension_by_limited_search(JOIN *join,
restore_prev_nj_state(s); restore_prev_nj_state(s);
} }
} }
DBUG_VOID_RETURN; DBUG_RETURN(FALSE);
} }
/* /*
TODO: this function is here only temporarily until 'greedy_search' is TODO: this function is here only temporarily until 'greedy_search' is
tested and accepted. 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, find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
double read_time) double read_time)
{ {
DBUG_ENTER("find_best");
THD *thd= join->thd; THD *thd= join->thd;
if (thd->killed)
DBUG_RETURN(TRUE);
if (!rest_tables) if (!rest_tables)
{ {
DBUG_PRINT("best",("read_time: %g record_count: %g",read_time, 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, ...@@ -4935,10 +4948,10 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
sizeof(POSITION)*idx); sizeof(POSITION)*idx);
join->best_read= read_time - 0.001; join->best_read= read_time - 0.001;
} }
return; DBUG_RETURN(FALSE);
} }
if (read_time+record_count/(double) TIME_FOR_COMPARE >= join->best_read) 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; JOIN_TAB *s;
double best_record_count=DBL_MAX,best_read_time=DBL_MAX; 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, ...@@ -4971,10 +4984,9 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
best_read_time=current_read_time; best_read_time=current_read_time;
} }
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos); swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
find_best(join,rest_tables & ~real_table_bit,idx+1, if (find_best(join,rest_tables & ~real_table_bit,idx+1,
current_record_count,current_read_time); current_record_count,current_read_time))
if (thd->killed) DBUG_RETURN(TRUE);
return;
swap_variables(JOIN_TAB*, join->best_ref[idx], *pos); swap_variables(JOIN_TAB*, join->best_ref[idx], *pos);
} }
restore_prev_nj_state(s); restore_prev_nj_state(s);
...@@ -4982,6 +4994,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, ...@@ -4982,6 +4994,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
break; // Don't test all combinations break; // Don't test all combinations
} }
} }
DBUG_RETURN(FALSE);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment