Skip to content

Commit 21868be

Browse files
committed
MDEV-26115 Crash when calling stored function in FOR loop argument
The 11.8 version with support for SPs returning ROWs.
1 parent a667c0c commit 21868be

File tree

6 files changed

+197
-6
lines changed

6 files changed

+197
-6
lines changed

mysql-test/main/sp-bugs2.result

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,75 @@ call p2('2');
5959
drop table t1;
6060
drop procedure p1;
6161
drop procedure p2;
62+
#
63+
# MDEV-26115: Crash when calling stored function in FOR loop argument
64+
#
65+
CREATE OR REPLACE FUNCTION cnt()
66+
RETURNS INTEGER NO SQL
67+
BEGIN
68+
RETURN 3;
69+
END;
70+
$
71+
CREATE OR REPLACE PROCEDURE p1()
72+
NO SQL
73+
BEGIN
74+
DECLARE i INTEGER;
75+
FOR i IN 1..cnt() DO
76+
SELECT 1;
77+
END FOR;
78+
END;
79+
$
80+
CALL p1();
81+
1
82+
1
83+
1
84+
1
85+
1
86+
1
87+
CALL p1();
88+
1
89+
1
90+
1
91+
1
92+
1
93+
1
94+
# Clean up
95+
DROP FUNCTION cnt;
96+
DROP PROCEDURE p1;
6297
# End of 10.11 tests
98+
#
99+
# MDEV-26115: Crash when calling stored function in FOR loop argument
100+
#
101+
CREATE FUNCTION sp_returns_row() RETURNS ROW(a INT, b VARCHAR(32))
102+
NO SQL
103+
BEGIN
104+
RETURN ROW(1,'b1');
105+
END;
106+
$$
107+
CREATE FUNCTION sp_returns_int(r ROW(a INT, b VARCHAR(32))) RETURNS INT
108+
NO SQL
109+
RETURN r.a
110+
$$
111+
CREATE PROCEDURE p1()
112+
NO SQL
113+
BEGIN
114+
DECLARE i INTEGER;
115+
FOR i IN 0..sp_returns_int(sp_returns_row()) DO
116+
SELECT i;
117+
END FOR;
118+
END;
119+
$$
120+
CALL p1;
121+
i
122+
0
123+
i
124+
1
125+
CALL p1;
126+
i
127+
0
128+
i
129+
1
130+
DROP PROCEDURE p1;
131+
DROP FUNCTION sp_returns_int;
132+
DROP FUNCTION sp_returns_row;
133+
# End of 11.8 tests

mysql-test/main/sp-bugs2.test

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,74 @@ drop table t1;
6767
drop procedure p1;
6868
drop procedure p2;
6969

70+
--echo #
71+
--echo # MDEV-26115: Crash when calling stored function in FOR loop argument
72+
--echo #
73+
--delimiter $
74+
CREATE OR REPLACE FUNCTION cnt()
75+
RETURNS INTEGER NO SQL
76+
BEGIN
77+
RETURN 3;
78+
END;
79+
$
80+
81+
CREATE OR REPLACE PROCEDURE p1()
82+
NO SQL
83+
BEGIN
84+
DECLARE i INTEGER;
85+
FOR i IN 1..cnt() DO
86+
SELECT 1;
87+
END FOR;
88+
END;
89+
$
90+
91+
--delimiter ;
92+
93+
CALL p1();
94+
CALL p1();
95+
96+
--echo # Clean up
97+
DROP FUNCTION cnt;
98+
DROP PROCEDURE p1;
99+
100+
70101
--echo # End of 10.11 tests
102+
103+
--echo #
104+
--echo # MDEV-26115: Crash when calling stored function in FOR loop argument
105+
--echo #
106+
107+
DELIMITER $$;
108+
109+
CREATE FUNCTION sp_returns_row() RETURNS ROW(a INT, b VARCHAR(32))
110+
NO SQL
111+
BEGIN
112+
RETURN ROW(1,'b1');
113+
END;
114+
$$
115+
116+
CREATE FUNCTION sp_returns_int(r ROW(a INT, b VARCHAR(32))) RETURNS INT
117+
NO SQL
118+
RETURN r.a
119+
$$
120+
121+
CREATE PROCEDURE p1()
122+
NO SQL
123+
BEGIN
124+
DECLARE i INTEGER;
125+
FOR i IN 0..sp_returns_int(sp_returns_row()) DO
126+
SELECT i;
127+
END FOR;
128+
END;
129+
$$
130+
131+
DELIMITER ;$$
132+
133+
CALL p1;
134+
CALL p1;
135+
136+
DROP PROCEDURE p1;
137+
DROP FUNCTION sp_returns_int;
138+
DROP FUNCTION sp_returns_row;
139+
140+
--echo # End of 11.8 tests

sql/item.cc

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2932,9 +2932,6 @@ Item_sp::func_name_cstring(THD *thd, bool is_package_function) const
29322932
void
29332933
Item_sp::cleanup()
29342934
{
2935-
delete sp_result_field;
2936-
sp_result_field= NULL;
2937-
sp_result_field_items= Item_args();
29382935
m_sp= NULL;
29392936
delete func_ctx;
29402937
func_ctx= NULL;
@@ -3106,7 +3103,6 @@ Item_sp::init_result_field(THD *thd, uint max_length, uint maybe_null,
31063103
DBUG_ENTER("Item_sp::init_result_field");
31073104

31083105
DBUG_ASSERT(m_sp != NULL);
3109-
DBUG_ASSERT(sp_result_field == NULL);
31103106

31113107
/*
31123108
A Field needs to be attached to a Table.
@@ -3120,6 +3116,30 @@ Item_sp::init_result_field(THD *thd, uint max_length, uint maybe_null,
31203116
dummy_table->s->table_name= Lex_ident_table(empty_clex_str);
31213117
dummy_table->maybe_null= maybe_null;
31223118

3119+
if (sp_result_field) // Non-first execution
3120+
{
3121+
if (Field_row *field_row= dynamic_cast<Field_row*>(sp_result_field))
3122+
{
3123+
/*
3124+
At the end of the previous execution cleanup() was called for
3125+
all Item_field instances in sp_result_field_items, which set their
3126+
Item_field::field to nullptr. We need to restore them to pointers
3127+
to the Fields in the virtual table.
3128+
*/
3129+
Virtual_tmp_table *tbl= field_row->virtual_tmp_table();
3130+
DBUG_ASSERT(tbl->s->fields == sp_result_field_items.argument_count());
3131+
for (uint i= 0; i < tbl->s->fields; i++)
3132+
{
3133+
Item *item= sp_result_field_items.arguments()[i];
3134+
Item_field *item_field= dynamic_cast<Item_field*>(item);
3135+
DBUG_ASSERT(item_field);
3136+
item_field->reset_field(tbl->field[i]);
3137+
}
3138+
}
3139+
DBUG_RETURN(FALSE);
3140+
}
3141+
3142+
31233143
if (m_sp->m_return_field_def.is_column_type_ref())
31243144
{
31253145
// RETURNS TYPE OF t1.col1

sql/item.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5891,6 +5891,11 @@ class Item_sp
58915891

58925892
Item_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name_arg);
58935893
Item_sp(THD *thd, Item_sp *item);
5894+
virtual ~Item_sp()
5895+
{
5896+
delete sp_result_field;
5897+
sp_result_field= NULL;
5898+
}
58945899
LEX_CSTRING func_name_cstring(THD *thd, bool is_package_function) const;
58955900
void cleanup();
58965901
bool sp_check_access(THD *thd);

sql/item_func.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6839,12 +6839,29 @@ Item_func_sp::fix_fields(THD *thd, Item **ref)
68396839
DBUG_RETURN(TRUE);
68406840
}
68416841

6842+
Query_arena *arena, backup;
6843+
/*
6844+
Allocation an instance of Item_func_sp used for initialization of
6845+
sp_result_field taken place inside the method init_result_field() is done
6846+
on sp_head's mem_root since Item_sp also allocated on this memory root.
6847+
6848+
Switching to SP/PS memory root is done explicitly before calling the method
6849+
init_result_field() instead doing that inside init_result_field()
6850+
since for the case when rollup aggregate function is handled
6851+
(@see Item_sum_sp::copy_or_same, @see JOIN::rollup_make_fields)
6852+
the runtime arena used for operations, so switching to SP/PS arena for this
6853+
case would result in assertion failure on second execution of the same
6854+
prepared statement because the memory root be already marked as read only.
6855+
*/
6856+
arena= thd->activate_stmt_arena_if_needed(&backup);
68426857
/*
68436858
We must call init_result_field before Item_func::fix_fields()
68446859
to make m_sp and result_field members available to fix_length_and_dec(),
68456860
which is called from Item_func::fix_fields().
68466861
*/
68476862
res= init_result_field(thd, max_length, maybe_null(), &null_value, &name);
6863+
if (arena)
6864+
thd->restore_active_arena(arena, &backup);
68486865

68496866
if (res)
68506867
DBUG_RETURN(TRUE);

sql/item_sum.cc

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,8 +1362,16 @@ Item_sum_sp::fix_fields(THD *thd, Item **ref)
13621362
return TRUE;
13631363
}
13641364

1365-
if (init_result_field(thd, max_length, maybe_null(), &null_value, &name))
1366-
return TRUE;
1365+
Query_arena *arena, backup;
1366+
arena= thd->activate_stmt_arena_if_needed(&backup);
1367+
1368+
bool ret= init_result_field(thd, max_length, maybe_null(),
1369+
&null_value, &name);
1370+
if (arena)
1371+
thd->restore_active_arena(arena, &backup);
1372+
1373+
if(ret)
1374+
return true;
13671375

13681376
for (uint i= 0 ; i < arg_count ; i++)
13691377
{

0 commit comments

Comments
 (0)