Skip to content

Commit ae05097

Browse files
committed
Fixed crashing bug in recursive SQL if write to tmp table would fail
This error was discovered while working on MDEV-30540 Wrong result with IN list length reaching IN_PREDICATE_CONVERSION_THRESHOLD If there is read error from handler::ha_rnd_next() during a recursive query, st_select_lex_unit::exec_recursive() will crash as it will try to get the error code from a structure that was deleted by the callee. The code was using the construct: sl->join->exec(); saved_error=sl->join->error; This does not work as sl->join was freed by the exec() and sl->join would be set to 0. Fixed by having JOIN::exec() return the error code. The included test case simulates the error in ha_rnd_next(), which causes a crash without the patch. scovered whle working on MDEV-30540 Wrong result with IN list length reaching IN_PREDICATE_CONVERSION_THRESHOLD If there is read error from handler::ha_rnd_next() during a recursive query, st_select_lex_unit::exec_recursive() will crash as it will try to get the error code from a structure that was deleted by the callee. The code was using the construct: sl->join->exec(); saved_error=sl->join->error; This does not work as sl->join was freed by the exec() and sl->join was set to 0. Fixed by having JOIN::exec() return the error code. The included test case simulates the error in ha_rnd_next(), which causes a crash without the patch.
1 parent bc3596f commit ae05097

File tree

10 files changed

+171
-42
lines changed

10 files changed

+171
-42
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
set @save_big_tables=@@big_tables;
2+
set big_tables=1;
3+
Warnings:
4+
Warning 1287 '@@big_tables' is deprecated and will be removed in a future release
5+
create table folks(id int, name char(32), dob date, father int, mother int);
6+
insert into folks values
7+
(100, 'Me', '2000-01-01', 20, 30),
8+
(20, 'Dad', '1970-02-02', 10, 9),
9+
(30, 'Mom', '1975-03-03', 8, 7),
10+
(10, 'Grandpa Bill', '1940-04-05', null, null),
11+
(9, 'Grandma Ann', '1941-10-15', null, null),
12+
(25, 'Uncle Jim', '1968-11-18', 8, 7),
13+
(98, 'Sister Amy', '2001-06-20', 20, 30),
14+
(7, 'Grandma Sally', '1943-08-23', null, 6),
15+
(8, 'Grandpa Ben', '1940-10-21', null, null),
16+
(6, 'Grandgrandma Martha', '1923-05-17', null, null),
17+
(67, 'Cousin Eddie', '1992-02-28', 25, 27),
18+
(27, 'Auntie Melinda', '1971-03-29', null, null);
19+
call mtr.add_suppression(".*marked as crashed.*");
20+
SET @saved_dbug= @@SESSION.debug_dbug;
21+
SET SESSION debug_dbug="+d,ha_rnd_next_error";
22+
SET @ha_rnd_next_error_counter=110;
23+
with recursive
24+
ancestor_couples(h_id, h_name, h_dob, h_father, h_mother,
25+
w_id, w_name, w_dob, w_father, w_mother)
26+
as
27+
(
28+
select h.*, w.*
29+
from folks h, folks w, coupled_ancestors a
30+
where a.father = h.id AND a.mother = w.id
31+
union
32+
select h.*, w.*
33+
from folks v, folks h, folks w
34+
where v.name = 'Me' and
35+
(v.father = h.id AND v.mother= w.id)
36+
),
37+
coupled_ancestors (id, name, dob, father, mother)
38+
as
39+
(
40+
select h_id, h_name, h_dob, h_father, h_mother
41+
from ancestor_couples
42+
union
43+
select w_id, w_name, w_dob, w_father, w_mother
44+
from ancestor_couples
45+
)
46+
select h_name, h_dob, w_name, w_dob
47+
from ancestor_couples;
48+
ERROR HY000: Table '(temporary)' is marked as crashed and should be repaired
49+
drop table folks;
50+
set big_tables=@save_big_tables;
51+
Warnings:
52+
Warning 1287 '@@big_tables' is deprecated and will be removed in a future release
53+
SET @@SESSION.debug_dbug=@saved_dbug;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#
2+
# This test simulates an error in an aria file discovered during a recursive SQL call.
3+
# The error handling causes used join structures to be deleted, which caused crashes in
4+
# upper levels when trying to access structures that does not exist anymore
5+
#
6+
7+
--source include/have_debug_sync.inc
8+
--source include/not_embedded.inc
9+
10+
set @save_big_tables=@@big_tables;
11+
set big_tables=1;
12+
13+
create table folks(id int, name char(32), dob date, father int, mother int);
14+
15+
insert into folks values
16+
(100, 'Me', '2000-01-01', 20, 30),
17+
(20, 'Dad', '1970-02-02', 10, 9),
18+
(30, 'Mom', '1975-03-03', 8, 7),
19+
(10, 'Grandpa Bill', '1940-04-05', null, null),
20+
(9, 'Grandma Ann', '1941-10-15', null, null),
21+
(25, 'Uncle Jim', '1968-11-18', 8, 7),
22+
(98, 'Sister Amy', '2001-06-20', 20, 30),
23+
(7, 'Grandma Sally', '1943-08-23', null, 6),
24+
(8, 'Grandpa Ben', '1940-10-21', null, null),
25+
(6, 'Grandgrandma Martha', '1923-05-17', null, null),
26+
(67, 'Cousin Eddie', '1992-02-28', 25, 27),
27+
(27, 'Auntie Melinda', '1971-03-29', null, null);
28+
29+
30+
call mtr.add_suppression(".*marked as crashed.*");
31+
SET @saved_dbug= @@SESSION.debug_dbug;
32+
SET SESSION debug_dbug="+d,ha_rnd_next_error";
33+
SET @ha_rnd_next_error_counter=110;
34+
35+
let q=
36+
with recursive
37+
ancestor_couples(h_id, h_name, h_dob, h_father, h_mother,
38+
w_id, w_name, w_dob, w_father, w_mother)
39+
as
40+
(
41+
select h.*, w.*
42+
from folks h, folks w, coupled_ancestors a
43+
where a.father = h.id AND a.mother = w.id
44+
union
45+
select h.*, w.*
46+
from folks v, folks h, folks w
47+
where v.name = 'Me' and
48+
(v.father = h.id AND v.mother= w.id)
49+
),
50+
coupled_ancestors (id, name, dob, father, mother)
51+
as
52+
(
53+
select h_id, h_name, h_dob, h_father, h_mother
54+
from ancestor_couples
55+
union
56+
select w_id, w_name, w_dob, w_father, w_mother
57+
from ancestor_couples
58+
)
59+
select h_name, h_dob, w_name, w_dob
60+
from ancestor_couples;
61+
62+
--error ER_CRASHED_ON_USAGE
63+
eval $q;
64+
drop table folks;
65+
66+
set big_tables=@save_big_tables;
67+
SET @@SESSION.debug_dbug=@saved_dbug;

sql/debug.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ static const LEX_CSTRING debug_crash_counter=
3535
static const LEX_CSTRING debug_error_counter=
3636
{ STRING_WITH_LEN("debug_error_counter") };
3737

38-
static bool debug_decrement_counter(const LEX_CSTRING *name)
38+
bool debug_decrement_counter(const LEX_CSTRING *name)
3939
{
4040
THD *thd= current_thd;
4141
user_var_entry *entry= (user_var_entry*)

sql/debug.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#ifndef DBUG_OFF
3232
void debug_crash_here(const char *keyword);
3333
bool debug_simulate_error(const char *keyword, uint error);
34+
bool debug_decrement_counter(const LEX_CSTRING *name);
3435
#else
3536
#define debug_crash_here(A) do { } while(0)
3637
#define debug_simulate_error(A, B) 0

sql/handler.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include <pfs_transaction_provider.h>
4343
#include <mysql/psi/mysql_transaction.h>
4444
#include "debug_sync.h" // DEBUG_SYNC
45+
#include "debug.h" // debug_decrement_counter
4546
#include "sql_audit.h"
4647
#include "ha_sequence.h"
4748
#include "rowid_filter.h"
@@ -3573,6 +3574,15 @@ int handler::ha_rnd_next(uchar *buf)
35733574
m_lock_type != F_UNLCK);
35743575
DBUG_ASSERT(inited == RND);
35753576

3577+
DBUG_EXECUTE_IF("ha_rnd_next_error",
3578+
{
3579+
LEX_CSTRING user_var= { STRING_WITH_LEN("ha_rnd_next_error_counter") };
3580+
if (debug_decrement_counter(&user_var))
3581+
{
3582+
print_error(HA_ERR_WRONG_IN_RECORD,MYF(0));
3583+
DBUG_RETURN(HA_ERR_WRONG_IN_RECORD);
3584+
}
3585+
});
35763586
do
35773587
{
35783588
TABLE_IO_WAIT(tracker, PSI_TABLE_FETCH_ROW, MAX_KEY, result,

sql/item_subselect.cc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4062,6 +4062,7 @@ int subselect_single_select_engine::exec()
40624062
char const *save_where= thd->where;
40634063
SELECT_LEX *save_select= thd->lex->current_select;
40644064
thd->lex->current_select= select_lex;
4065+
bool exec_error= 0;
40654066
DBUG_ENTER("subselect_single_select_engine::exec");
40664067

40674068
if (join->optimization_state == JOIN::NOT_OPTIMIZED)
@@ -4153,7 +4154,7 @@ int subselect_single_select_engine::exec()
41534154
}
41544155
}
41554156

4156-
join->exec();
4157+
exec_error= join->exec();
41574158

41584159
/* Enable the optimizations back */
41594160
for (JOIN_TAB **ptab= changed_tabs; ptab != last_changed_tab; ptab++)
@@ -4171,7 +4172,7 @@ int subselect_single_select_engine::exec()
41714172
item->make_const();
41724173
thd->where= save_where;
41734174
thd->lex->current_select= save_select;
4174-
DBUG_RETURN(join->error || thd->is_fatal_error || thd->is_error());
4175+
DBUG_RETURN(exec_error || thd->is_error());
41754176
}
41764177
thd->where= save_where;
41774178
thd->lex->current_select= save_select;
@@ -5718,9 +5719,8 @@ int subselect_hash_sj_engine::exec()
57185719
/* The subquery should be optimized, and materialized only once. */
57195720
DBUG_ASSERT(materialize_join->optimization_state == JOIN::OPTIMIZATION_DONE &&
57205721
!is_materialized);
5721-
materialize_join->exec();
5722-
if (unlikely((res= MY_TEST(materialize_join->error || thd->is_fatal_error ||
5723-
thd->is_error()))))
5722+
res= materialize_join->exec();
5723+
if (unlikely((res= (res || thd->is_error()))))
57245724
goto err;
57255725

57265726
/*

sql/opt_subselect.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5869,10 +5869,10 @@ enum_nested_loop_state join_tab_execution_startup(JOIN_TAB *tab)
58695869
((subselect_hash_sj_engine*)in_subs->engine);
58705870
if (!hash_sj_engine->is_materialized)
58715871
{
5872-
hash_sj_engine->materialize_join->exec();
5872+
int error= hash_sj_engine->materialize_join->exec();
58735873
hash_sj_engine->is_materialized= TRUE;
58745874

5875-
if (unlikely(hash_sj_engine->materialize_join->error) ||
5875+
if (unlikely(error) ||
58765876
unlikely(tab->join->thd->is_fatal_error))
58775877
DBUG_RETURN(NESTED_LOOP_ERROR);
58785878
}

sql/sql_select.cc

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,8 @@ void dbug_serve_apcs(THD *thd, int n_calls)
382382
Intended usage:
383383

384384
DBUG_EXECUTE_IF("show_explain_probe_2",
385-
if (dbug_user_var_equals_int(thd, "select_id", select_id))
386-
dbug_serve_apcs(thd, 1);
385+
if (dbug_user_var_equals_int(thd, "select_id", select_id))
386+
dbug_serve_apcs(thd, 1);
387387
);
388388

389389
*/
@@ -751,7 +751,7 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select,
751751
else
752752
{
753753
for (sum_func= ref->in_sum_func; sum_func &&
754-
sum_func->aggr_level >= select->nest_level;
754+
sum_func->aggr_level >= select->nest_level;
755755
sum_func= sum_func->in_sum_func)
756756
{
757757
if (sum_func->aggr_level == select->nest_level)
@@ -4657,16 +4657,17 @@ bool JOIN::save_explain_data(Explain_query *output, bool can_overwrite,
46574657
}
46584658

46594659

4660-
void JOIN::exec()
4660+
int JOIN::exec()
46614661
{
4662+
int res;
46624663
DBUG_EXECUTE_IF("show_explain_probe_join_exec_start",
46634664
if (dbug_user_var_equals_int(thd,
46644665
"show_explain_probe_select_id",
46654666
select_lex->select_number))
46664667
dbug_serve_apcs(thd, 1);
46674668
);
46684669
ANALYZE_START_TRACKING(thd, &explain->time_tracker);
4669-
exec_inner();
4670+
res= exec_inner();
46704671
ANALYZE_STOP_TRACKING(thd, &explain->time_tracker);
46714672

46724673
DBUG_EXECUTE_IF("show_explain_probe_join_exec_end",
@@ -4675,10 +4676,11 @@ void JOIN::exec()
46754676
select_lex->select_number))
46764677
dbug_serve_apcs(thd, 1);
46774678
);
4679+
return res;
46784680
}
46794681

46804682

4681-
void JOIN::exec_inner()
4683+
int JOIN::exec_inner()
46824684
{
46834685
List<Item> *columns_list= &fields_list;
46844686
DBUG_ENTER("JOIN::exec_inner");
@@ -4714,12 +4716,12 @@ void JOIN::exec_inner()
47144716
{
47154717
thd->set_examined_row_count(0);
47164718
thd->limit_found_rows= 0;
4717-
DBUG_VOID_RETURN;
4719+
DBUG_RETURN(0);
47184720
}
47194721
columns_list= &procedure_fields_list;
47204722
}
47214723
if (result->prepare2(this))
4722-
DBUG_VOID_RETURN;
4724+
DBUG_RETURN(error);
47234725

47244726
if (!tables_list && (table_count || !select_lex->with_sum_func) &&
47254727
!select_lex->have_window_funcs())
@@ -4733,7 +4735,7 @@ void JOIN::exec_inner()
47334735
Protocol::SEND_NUM_ROWS |
47344736
Protocol::SEND_EOF))
47354737
{
4736-
DBUG_VOID_RETURN;
4738+
DBUG_RETURN(error);
47374739
}
47384740

47394741
/*
@@ -4771,7 +4773,7 @@ void JOIN::exec_inner()
47714773
/* Single select (without union) always returns 0 or 1 row */
47724774
thd->limit_found_rows= send_records;
47734775
thd->set_examined_row_count(0);
4774-
DBUG_VOID_RETURN;
4776+
DBUG_RETURN(error);
47754777
}
47764778

47774779
/*
@@ -4791,7 +4793,7 @@ void JOIN::exec_inner()
47914793
if (unlikely(thd->is_error()))
47924794
{
47934795
error= thd->is_error();
4794-
DBUG_VOID_RETURN;
4796+
DBUG_RETURN(error);
47954797
}
47964798

47974799
if (zero_result_cause)
@@ -4815,7 +4817,7 @@ void JOIN::exec_inner()
48154817
select_options,
48164818
zero_result_cause,
48174819
having ? having : tmp_having, all_fields);
4818-
DBUG_VOID_RETURN;
4820+
DBUG_RETURN(0);
48194821
}
48204822
}
48214823

@@ -4839,28 +4841,28 @@ void JOIN::exec_inner()
48394841
if (unlikely(thd->is_error()))
48404842
{
48414843
error= thd->is_error();
4842-
DBUG_VOID_RETURN;
4844+
DBUG_RETURN(error);
48434845
}
48444846
}
48454847
}
48464848

48474849
if ((this->select_lex->options & OPTION_SCHEMA_TABLE) &&
48484850
get_schema_tables_result(this, PROCESSED_BY_JOIN_EXEC))
4849-
DBUG_VOID_RETURN;
4851+
DBUG_RETURN(0);
48504852

48514853
if (select_options & SELECT_DESCRIBE)
48524854
{
48534855
select_describe(this, need_tmp,
48544856
order != 0 && !skip_sort_order,
48554857
select_distinct,
48564858
!table_count ? "No tables used" : NullS);
4857-
DBUG_VOID_RETURN;
4859+
DBUG_RETURN(0);
48584860
}
48594861
else if (select_lex->pushdown_select)
48604862
{
48614863
/* Execute the query pushed into a foreign engine */
48624864
error= select_lex->pushdown_select->execute();
4863-
DBUG_VOID_RETURN;
4865+
DBUG_RETURN(error);
48644866
}
48654867
else
48664868
{
@@ -4879,7 +4881,7 @@ void JOIN::exec_inner()
48794881
if (unlikely(thd->is_error()))
48804882
{
48814883
error= thd->is_error();
4882-
DBUG_VOID_RETURN;
4884+
DBUG_RETURN(error);
48834885
}
48844886

48854887
THD_STAGE_INFO(thd, stage_sending_data);
@@ -4894,7 +4896,7 @@ void JOIN::exec_inner()
48944896
DBUG_PRINT("counts", ("thd->examined_row_count: %lu",
48954897
(ulong) thd->get_examined_row_count()));
48964898

4897-
DBUG_VOID_RETURN;
4899+
DBUG_RETURN(error);
48984900
}
48994901

49004902

@@ -5067,7 +5069,7 @@ mysql_select(THD *thd, TABLE_LIST *tables, List<Item> &fields, COND *conds,
50675069
SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex)
50685070
{
50695071
int err= 0;
5070-
bool free_join= 1;
5072+
bool free_join= 1, exec_error= 0;
50715073
DBUG_ENTER("mysql_select");
50725074

50735075
if (!fields.is_empty())
@@ -5146,7 +5148,7 @@ mysql_select(THD *thd, TABLE_LIST *tables, List<Item> &fields, COND *conds,
51465148
if (unlikely(thd->is_error()))
51475149
goto err;
51485150

5149-
join->exec();
5151+
exec_error= join->exec();
51505152

51515153
if (thd->lex->describe & DESCRIBE_EXTENDED)
51525154
{
@@ -5166,9 +5168,9 @@ mysql_select(THD *thd, TABLE_LIST *tables, List<Item> &fields, COND *conds,
51665168
{
51675169
THD_STAGE_INFO(thd, stage_end);
51685170
err|= (int)(select_lex->cleanup());
5169-
DBUG_RETURN(err || thd->is_error());
5171+
DBUG_RETURN(exec_error || err || thd->is_error());
51705172
}
5171-
DBUG_RETURN(join->error ? join->error: err);
5173+
DBUG_RETURN(exec_error || err);
51725174
}
51735175

51745176

0 commit comments

Comments
 (0)