Commit 7bd691f1 authored by bell@sanja.is.com.ua's avatar bell@sanja.is.com.ua

Recursion support made for SP (BUG#10100).

parent cae673d0
...@@ -3313,6 +3313,8 @@ static int handle_error(const char *query, struct st_query *q, ...@@ -3313,6 +3313,8 @@ static int handle_error(const char *query, struct st_query *q,
(q->expected_errno[i].code.errnum == err_errno)) || (q->expected_errno[i].code.errnum == err_errno)) ||
((q->expected_errno[i].type == ERR_SQLSTATE) && ((q->expected_errno[i].type == ERR_SQLSTATE) &&
(strcmp(q->expected_errno[i].code.sqlstate, err_sqlstate) == 0))) (strcmp(q->expected_errno[i].code.sqlstate, err_sqlstate) == 0)))
{
if (!disable_result_log)
{ {
if (q->expected_errors == 1) if (q->expected_errors == 1)
{ {
...@@ -3328,6 +3330,7 @@ static int handle_error(const char *query, struct st_query *q, ...@@ -3328,6 +3330,7 @@ static int handle_error(const char *query, struct st_query *q,
(q->expected_errno[0].type == ERR_ERRNO && (q->expected_errno[0].type == ERR_ERRNO &&
q->expected_errno[0].code.errnum != 0)) q->expected_errno[0].code.errnum != 0))
dynstr_append(ds,"Got one of the listed errors\n"); dynstr_append(ds,"Got one of the listed errors\n");
}
/* OK */ /* OK */
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -3335,11 +3338,14 @@ static int handle_error(const char *query, struct st_query *q, ...@@ -3335,11 +3338,14 @@ static int handle_error(const char *query, struct st_query *q,
DBUG_PRINT("info",("i: %d expected_errors: %d", i, q->expected_errors)); DBUG_PRINT("info",("i: %d expected_errors: %d", i, q->expected_errors));
if (!disable_result_log)
{
dynstr_append_mem(ds, "ERROR ",6); dynstr_append_mem(ds, "ERROR ",6);
replace_dynstr_append(ds, err_sqlstate); replace_dynstr_append(ds, err_sqlstate);
dynstr_append_mem(ds, ": ", 2); dynstr_append_mem(ds, ": ", 2);
replace_dynstr_append(ds, err_error); replace_dynstr_append(ds, err_error);
dynstr_append_mem(ds, "\n", 1); dynstr_append_mem(ds, "\n", 1);
}
if (i) if (i)
{ {
......
...@@ -33,6 +33,8 @@ begin ...@@ -33,6 +33,8 @@ begin
execute stmt; execute stmt;
end| end|
prepare stmt from "call p1()"| prepare stmt from "call p1()"|
set @SAVE_SP_RECURSION_LEVELS=@@max_sp_recursion_depth|
set @@max_sp_recursion_depth=100|
execute stmt| execute stmt|
ERROR HY000: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner ERROR HY000: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner
execute stmt| execute stmt|
...@@ -40,11 +42,18 @@ ERROR HY000: The prepared statement contains a stored routine call that refers t ...@@ -40,11 +42,18 @@ ERROR HY000: The prepared statement contains a stored routine call that refers t
execute stmt| execute stmt|
ERROR HY000: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner ERROR HY000: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner
call p1()| call p1()|
ERROR HY000: Recursive stored routines are not allowed. ERROR HY000: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner
call p1()|
ERROR HY000: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner
call p1()|
ERROR HY000: The prepared statement contains a stored routine call that refers to that same statement. It's not allowed to execute a prepared statement in such a recursive manner
set @@max_sp_recursion_depth=@SAVE_SP_RECURSION_LEVELS|
call p1()|
ERROR HY000: Recursive limit 0 (as set by the max_sp_recursion_depth variable) was exceeded for routine p1
call p1()| call p1()|
ERROR HY000: Recursive stored routines are not allowed. ERROR HY000: Recursive limit 0 (as set by the max_sp_recursion_depth variable) was exceeded for routine p1
call p1()| call p1()|
ERROR HY000: Recursive stored routines are not allowed. ERROR HY000: Recursive limit 0 (as set by the max_sp_recursion_depth variable) was exceeded for routine p1
drop procedure p1| drop procedure p1|
create procedure p1() create procedure p1()
begin begin
......
...@@ -708,7 +708,7 @@ return (i in (100, 200, bug11394(i-1), 400)); ...@@ -708,7 +708,7 @@ return (i in (100, 200, bug11394(i-1), 400));
end if; end if;
end| end|
select bug11394(2)| select bug11394(2)|
ERROR HY000: Recursive stored routines are not allowed. ERROR HY000: Recursive stored functions and triggers are not allowed.
drop function bug11394| drop function bug11394|
create function bug11394_1(i int) returns int create function bug11394_1(i int) returns int
begin begin
...@@ -719,7 +719,7 @@ return (select bug11394_1(i-1)); ...@@ -719,7 +719,7 @@ return (select bug11394_1(i-1));
end if; end if;
end| end|
select bug11394_1(2)| select bug11394_1(2)|
ERROR HY000: Recursive stored routines are not allowed. ERROR HY000: Recursive stored functions and triggers are not allowed.
drop function bug11394_1| drop function bug11394_1|
create function bug11394_2(i int) returns int return i| create function bug11394_2(i int) returns int return i|
select bug11394_2(bug11394_2(10))| select bug11394_2(bug11394_2(10))|
...@@ -733,7 +733,10 @@ call bug11394(i - 1,(select 1)); ...@@ -733,7 +733,10 @@ call bug11394(i - 1,(select 1));
end if; end if;
end| end|
call bug11394(2, 1)| call bug11394(2, 1)|
ERROR HY000: Recursive stored routines are not allowed. ERROR HY000: Recursive limit 0 (as set by the max_sp_recursion_depth variable) was exceeded for routine bug11394
set @@max_sp_recursion_depth=10|
call bug11394(2, 1)|
set @@max_sp_recursion_depth=default|
drop procedure bug11394| drop procedure bug11394|
CREATE PROCEDURE BUG_12490() HELP CONTENTS; CREATE PROCEDURE BUG_12490() HELP CONTENTS;
ERROR 0A000: HELP is not allowed in stored procedures ERROR 0A000: HELP is not allowed in stored procedures
......
...@@ -3617,4 +3617,189 @@ count(*) ...@@ -3617,4 +3617,189 @@ count(*)
drop table t3, t4| drop table t3, t4|
drop procedure bug14210| drop procedure bug14210|
set @@session.max_heap_table_size=default| set @@session.max_heap_table_size=default|
drop function if exists bug10100f|
drop procedure if exists bug10100p|
drop procedure if exists bug10100t|
drop procedure if exists bug10100pt|
drop procedure if exists bug10100pv|
drop procedure if exists bug10100pd|
drop procedure if exists bug10100pc|
create function bug10100f(prm int) returns int
begin
if prm > 1 then
return prm * bug10100f(prm - 1);
end if;
return 1;
end|
create procedure bug10100p(prm int, inout res int)
begin
set res = res * prm;
if prm > 1 then
call bug10100p(prm - 1, res);
end if;
end|
create procedure bug10100t(prm int)
begin
declare res int;
set res = 1;
call bug10100p(prm, res);
select res;
end|
create table t3 (a int)|
insert into t3 values (0)|
create view v1 as select a from t3;
create procedure bug10100pt(level int, lim int)
begin
if level < lim then
update t3 set a=level;
FLUSH TABLES;
call bug10100pt(level+1, lim);
else
select * from t3;
end if;
end|
create procedure bug10100pv(level int, lim int)
begin
if level < lim then
update v1 set a=level;
FLUSH TABLES;
call bug10100pv(level+1, lim);
else
select * from v1;
end if;
end|
prepare stmt2 from "select * from t3;";
create procedure bug10100pd(level int, lim int)
begin
if level < lim then
select level;
prepare stmt1 from "update t3 set a=a+2";
execute stmt1;
FLUSH TABLES;
execute stmt1;
FLUSH TABLES;
execute stmt1;
FLUSH TABLES;
deallocate prepare stmt1;
execute stmt2;
select * from t3;
call bug10100pd(level+1, lim);
else
execute stmt2;
end if;
end|
create procedure bug10100pc(level int, lim int)
begin
declare lv int;
declare c cursor for select a from t3;
open c;
if level < lim then
select level;
fetch c into lv;
select lv;
update t3 set a=level+lv;
FLUSH TABLES;
call bug10100pc(level+1, lim);
else
select * from t3;
end if;
close c;
end|
set @@max_sp_recursion_depth=4|
select @@max_sp_recursion_depth|
@@max_sp_recursion_depth
4
select bug10100f(3)|
ERROR HY000: Recursive stored functions and triggers are not allowed.
select bug10100f(6)|
ERROR HY000: Recursive stored functions and triggers are not allowed.
call bug10100t(5)|
res
120
call bug10100pt(1,5)|
a
4
call bug10100pv(1,5)|
a
4
update t3 set a=1|
call bug10100pd(1,5)|
level
1
a
7
a
7
level
2
a
13
a
13
level
3
a
19
a
19
level
4
a
25
a
25
a
25
select * from t3|
a
25
update t3 set a=1|
call bug10100pc(1,5)|
level
1
lv
1
level
2
lv
2
level
3
lv
4
level
4
lv
7
a
11
select * from t3|
a
11
set @@max_sp_recursion_depth=0|
select @@max_sp_recursion_depth|
@@max_sp_recursion_depth
0
select bug10100f(5)|
ERROR HY000: Recursive stored functions and triggers are not allowed.
call bug10100t(5)|
ERROR HY000: Recursive limit 0 (as set by the max_sp_recursion_depth variable) was exceeded for routine bug10100p
set @@max_sp_recursion_depth=255|
set @var=1|
call bug10100p(255, @var)|
call bug10100pt(1,255)|
call bug10100pv(1,255)|
call bug10100pd(1,255)|
call bug10100pc(1,255)|
set @@max_sp_recursion_depth=0|
deallocate prepare stmt2|
drop function bug10100f|
drop procedure bug10100p|
drop procedure bug10100t|
drop procedure bug10100pt|
drop procedure bug10100pv|
drop procedure bug10100pd|
drop procedure bug10100pc|
drop view v1|
drop table t3|
drop table t1,t2; drop table t1,t2;
...@@ -703,8 +703,11 @@ create trigger t1_ai after insert on t1 ...@@ -703,8 +703,11 @@ create trigger t1_ai after insert on t1
for each row insert into t2 values (new.f1+1); for each row insert into t2 values (new.f1+1);
create trigger t2_ai after insert on t2 create trigger t2_ai after insert on t2
for each row insert into t1 values (new.f2+1); for each row insert into t1 values (new.f2+1);
set @SAVE_SP_RECURSION_LEVELS=@@max_sp_recursion_depth;
set @@max_sp_recursion_depth=100;
insert into t1 values (1); insert into t1 values (1);
ERROR HY000: Can't update table 't1' in stored function/trigger because it is already used by statement which invoked this stored function/trigger. ERROR HY000: Can't update table 't1' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
set @@max_sp_recursion_depth=@SAVE_SP_RECURSION_LEVELS;
select * from t1; select * from t1;
f1 f1
1 1
......
...@@ -351,6 +351,14 @@ set global rpl_recovery_rank=100; ...@@ -351,6 +351,14 @@ set global rpl_recovery_rank=100;
set global server_id=100; set global server_id=100;
set global slow_launch_time=100; set global slow_launch_time=100;
set sort_buffer_size=100; set sort_buffer_size=100;
set @@max_sp_recursion_depth=10;
select @@max_sp_recursion_depth;
@@max_sp_recursion_depth
10
set @@max_sp_recursion_depth=0;
select @@max_sp_recursion_depth;
@@max_sp_recursion_depth
0
set sql_auto_is_null=1; set sql_auto_is_null=1;
select @@sql_auto_is_null; select @@sql_auto_is_null;
@@sql_auto_is_null @@sql_auto_is_null
......
...@@ -26,18 +26,29 @@ begin ...@@ -26,18 +26,29 @@ begin
execute stmt; execute stmt;
end| end|
prepare stmt from "call p1()"| prepare stmt from "call p1()"|
# Allow SP resursion to be show that it has not influence here
set @SAVE_SP_RECURSION_LEVELS=@@max_sp_recursion_depth|
set @@max_sp_recursion_depth=100|
--error ER_PS_NO_RECURSION --error ER_PS_NO_RECURSION
execute stmt| execute stmt|
--error ER_PS_NO_RECURSION --error ER_PS_NO_RECURSION
execute stmt| execute stmt|
--error ER_PS_NO_RECURSION --error ER_PS_NO_RECURSION
execute stmt| execute stmt|
--error ER_SP_NO_RECURSION --error ER_PS_NO_RECURSION
call p1()|
--error ER_PS_NO_RECURSION
call p1()| call p1()|
--error ER_SP_NO_RECURSION --error ER_PS_NO_RECURSION
call p1()| call p1()|
--error ER_SP_NO_RECURSION set @@max_sp_recursion_depth=@SAVE_SP_RECURSION_LEVELS|
--error ER_SP_RECURSION_LIMIT
call p1()| call p1()|
--error ER_SP_RECURSION_LIMIT
call p1()|
--error ER_SP_RECURSION_LIMIT
call p1()|
drop procedure p1| drop procedure p1|
# #
# C. Create/drop a stored procedure in Dynamic SQL. # C. Create/drop a stored procedure in Dynamic SQL.
......
...@@ -1044,10 +1044,11 @@ begin ...@@ -1044,10 +1044,11 @@ begin
call bug11394(i - 1,(select 1)); call bug11394(i - 1,(select 1));
end if; end if;
end| end|
# Again if we allow recursion for stored procedures (without --error ER_SP_RECURSION_LIMIT
# additional efforts) the following statement will crash the server. call bug11394(2, 1)|
--error 1424 set @@max_sp_recursion_depth=10|
call bug11394(2, 1)| call bug11394(2, 1)|
set @@max_sp_recursion_depth=default|
drop procedure bug11394| drop procedure bug11394|
delimiter ;| delimiter ;|
......
...@@ -4541,6 +4541,160 @@ drop table t3, t4| ...@@ -4541,6 +4541,160 @@ drop table t3, t4|
drop procedure bug14210| drop procedure bug14210|
set @@session.max_heap_table_size=default| set @@session.max_heap_table_size=default|
#
# BUG#10100: function (and stored procedure?) recursivity problem
#
--disable_warnings
drop function if exists bug10100f|
drop procedure if exists bug10100p|
drop procedure if exists bug10100t|
drop procedure if exists bug10100pt|
drop procedure if exists bug10100pv|
drop procedure if exists bug10100pd|
drop procedure if exists bug10100pc|
--enable_warnings
# routines with simple recursion
create function bug10100f(prm int) returns int
begin
if prm > 1 then
return prm * bug10100f(prm - 1);
end if;
return 1;
end|
create procedure bug10100p(prm int, inout res int)
begin
set res = res * prm;
if prm > 1 then
call bug10100p(prm - 1, res);
end if;
end|
create procedure bug10100t(prm int)
begin
declare res int;
set res = 1;
call bug10100p(prm, res);
select res;
end|
# a procedure which use tables and recursion
create table t3 (a int)|
insert into t3 values (0)|
create view v1 as select a from t3;
create procedure bug10100pt(level int, lim int)
begin
if level < lim then
update t3 set a=level;
FLUSH TABLES;
call bug10100pt(level+1, lim);
else
select * from t3;
end if;
end|
# view & recursion
create procedure bug10100pv(level int, lim int)
begin
if level < lim then
update v1 set a=level;
FLUSH TABLES;
call bug10100pv(level+1, lim);
else
select * from v1;
end if;
end|
# dynamic sql & recursion
prepare stmt2 from "select * from t3;";
create procedure bug10100pd(level int, lim int)
begin
if level < lim then
select level;
prepare stmt1 from "update t3 set a=a+2";
execute stmt1;
FLUSH TABLES;
execute stmt1;
FLUSH TABLES;
execute stmt1;
FLUSH TABLES;
deallocate prepare stmt1;
execute stmt2;
select * from t3;
call bug10100pd(level+1, lim);
else
execute stmt2;
end if;
end|
# cursor & recursion
create procedure bug10100pc(level int, lim int)
begin
declare lv int;
declare c cursor for select a from t3;
open c;
if level < lim then
select level;
fetch c into lv;
select lv;
update t3 set a=level+lv;
FLUSH TABLES;
call bug10100pc(level+1, lim);
else
select * from t3;
end if;
close c;
end|
set @@max_sp_recursion_depth=4|
select @@max_sp_recursion_depth|
-- error ER_SP_NO_RECURSION
select bug10100f(3)|
-- error ER_SP_NO_RECURSION
select bug10100f(6)|
call bug10100t(5)|
call bug10100pt(1,5)|
call bug10100pv(1,5)|
update t3 set a=1|
call bug10100pd(1,5)|
select * from t3|
update t3 set a=1|
call bug10100pc(1,5)|
select * from t3|
set @@max_sp_recursion_depth=0|
select @@max_sp_recursion_depth|
-- error ER_SP_NO_RECURSION
select bug10100f(5)|
-- error ER_SP_RECURSION_LIMIT
call bug10100t(5)|
#end of the stack checking
set @@max_sp_recursion_depth=255|
set @var=1|
#disable log because error about stack overrun contains numbers which
#depend on a system
-- disable_result_log
-- error ER_STACK_OVERRUN_NEED_MORE
call bug10100p(255, @var)|
-- error ER_STACK_OVERRUN_NEED_MORE
call bug10100pt(1,255)|
-- error ER_STACK_OVERRUN_NEED_MORE
call bug10100pv(1,255)|
-- error ER_STACK_OVERRUN_NEED_MORE
call bug10100pd(1,255)|
-- error ER_STACK_OVERRUN_NEED_MORE
call bug10100pc(1,255)|
-- enable_result_log
set @@max_sp_recursion_depth=0|
deallocate prepare stmt2|
drop function bug10100f|
drop procedure bug10100p|
drop procedure bug10100t|
drop procedure bug10100pt|
drop procedure bug10100pv|
drop procedure bug10100pd|
drop procedure bug10100pc|
drop view v1|
drop table t3|
# #
# BUG#NNNN: New bug synopsis # BUG#NNNN: New bug synopsis
# #
......
...@@ -743,8 +743,12 @@ create trigger t1_ai after insert on t1 ...@@ -743,8 +743,12 @@ create trigger t1_ai after insert on t1
for each row insert into t2 values (new.f1+1); for each row insert into t2 values (new.f1+1);
create trigger t2_ai after insert on t2 create trigger t2_ai after insert on t2
for each row insert into t1 values (new.f2+1); for each row insert into t1 values (new.f2+1);
# Allow SP resursion to be show that it has not influence here
set @SAVE_SP_RECURSION_LEVELS=@@max_sp_recursion_depth;
set @@max_sp_recursion_depth=100;
--error ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG --error ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG
insert into t1 values (1); insert into t1 values (1);
set @@max_sp_recursion_depth=@SAVE_SP_RECURSION_LEVELS;
select * from t1; select * from t1;
select * from t2; select * from t2;
drop trigger t1_ai; drop trigger t1_ai;
......
...@@ -237,6 +237,10 @@ set global rpl_recovery_rank=100; ...@@ -237,6 +237,10 @@ set global rpl_recovery_rank=100;
set global server_id=100; set global server_id=100;
set global slow_launch_time=100; set global slow_launch_time=100;
set sort_buffer_size=100; set sort_buffer_size=100;
set @@max_sp_recursion_depth=10;
select @@max_sp_recursion_depth;
set @@max_sp_recursion_depth=0;
select @@max_sp_recursion_depth;
set sql_auto_is_null=1; set sql_auto_is_null=1;
select @@sql_auto_is_null; select @@sql_auto_is_null;
set @@sql_auto_is_null=0; set @@sql_auto_is_null=0;
......
...@@ -4690,10 +4690,16 @@ Item_func_sp::sp_result_field(void) const ...@@ -4690,10 +4690,16 @@ Item_func_sp::sp_result_field(void) const
{ {
Field *field; Field *field;
DBUG_ENTER("Item_func_sp::sp_result_field"); DBUG_ENTER("Item_func_sp::sp_result_field");
DBUG_PRINT("info", ("sp: %s, flags: %x, level: %lu",
(m_sp ? "YES" : "NO"),
(m_sp ? m_sp->m_flags : (uint)0),
(m_sp ? m_sp->m_recursion_level : (ulong)0)));
if (!m_sp) if (!m_sp)
{ {
if (!(m_sp= sp_find_function(current_thd, m_name, TRUE))) THD *thd= current_thd;
if (!(m_sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, m_name,
&thd->sp_func_cache, TRUE)))
{ {
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str); my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str);
DBUG_RETURN(0); DBUG_RETURN(0);
...@@ -4925,7 +4931,8 @@ Item_func_sp::find_and_check_access(THD *thd, ulong want_access, ...@@ -4925,7 +4931,8 @@ Item_func_sp::find_and_check_access(THD *thd, ulong want_access,
bool res= TRUE; bool res= TRUE;
*save= 0; // Safety if error *save= 0; // Safety if error
if (! m_sp && ! (m_sp= sp_find_function(thd, m_name, TRUE))) if (! m_sp && ! (m_sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, m_name,
&thd->sp_func_cache, TRUE)))
{ {
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str); my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION", m_name->m_qname.str);
goto error; goto error;
......
...@@ -4543,6 +4543,7 @@ enum options_mysqld ...@@ -4543,6 +4543,7 @@ enum options_mysqld
OPT_OPTIMIZER_PRUNE_LEVEL, OPT_OPTIMIZER_PRUNE_LEVEL,
OPT_UPDATABLE_VIEWS_WITH_LIMIT, OPT_UPDATABLE_VIEWS_WITH_LIMIT,
OPT_SP_AUTOMATIC_PRIVILEGES, OPT_SP_AUTOMATIC_PRIVILEGES,
OPT_MAX_SP_RECURSION_DEPTH,
OPT_AUTO_INCREMENT, OPT_AUTO_INCREMENT_OFFSET, OPT_AUTO_INCREMENT, OPT_AUTO_INCREMENT_OFFSET,
OPT_ENABLE_LARGE_PAGES, OPT_ENABLE_LARGE_PAGES,
OPT_TIMED_MUTEXES, OPT_TIMED_MUTEXES,
...@@ -5745,6 +5746,11 @@ The minimum value for this variable is 4096.", ...@@ -5745,6 +5746,11 @@ The minimum value for this variable is 4096.",
(gptr*) &global_system_variables.read_buff_size, (gptr*) &global_system_variables.read_buff_size,
(gptr*) &max_system_variables.read_buff_size,0, GET_ULONG, REQUIRED_ARG, (gptr*) &max_system_variables.read_buff_size,0, GET_ULONG, REQUIRED_ARG,
128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE, 0}, 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE, 0},
{"max_sp_recursion_depth", OPT_MAX_SP_RECURSION_DEPTH,
"Maximum stored procedure recursion depth. (discussed with docs).",
(gptr*) &global_system_variables.max_sp_recursion_depth,
(gptr*) &max_system_variables.max_sp_recursion_depth, 0, GET_ULONG,
OPT_ARG, 0, 0, 255, 0, 1, 0 },
#ifdef HAVE_REPLICATION #ifdef HAVE_REPLICATION
{"relay_log_purge", OPT_RELAY_LOG_PURGE, {"relay_log_purge", OPT_RELAY_LOG_PURGE,
"0 = do not purge relay logs. 1 = purge them as soon as they are no more needed.", "0 = do not purge relay logs. 1 = purge them as soon as they are no more needed.",
......
...@@ -258,6 +258,8 @@ sys_var_long_ptr sys_max_relay_log_size("max_relay_log_size", ...@@ -258,6 +258,8 @@ sys_var_long_ptr sys_max_relay_log_size("max_relay_log_size",
fix_max_relay_log_size); fix_max_relay_log_size);
sys_var_thd_ulong sys_max_sort_length("max_sort_length", sys_var_thd_ulong sys_max_sort_length("max_sort_length",
&SV::max_sort_length); &SV::max_sort_length);
sys_var_thd_ulong sys_max_sp_recursion_depth("max_sp_recursion_depth",
&SV::max_sp_recursion_depth);
sys_var_max_user_conn sys_max_user_connections("max_user_connections"); sys_var_max_user_conn sys_max_user_connections("max_user_connections");
sys_var_thd_ulong sys_max_tmp_tables("max_tmp_tables", sys_var_thd_ulong sys_max_tmp_tables("max_tmp_tables",
&SV::max_tmp_tables); &SV::max_tmp_tables);
...@@ -628,6 +630,7 @@ sys_var *sys_variables[]= ...@@ -628,6 +630,7 @@ sys_var *sys_variables[]=
&sys_max_relay_log_size, &sys_max_relay_log_size,
&sys_max_seeks_for_key, &sys_max_seeks_for_key,
&sys_max_sort_length, &sys_max_sort_length,
&sys_max_sp_recursion_depth,
&sys_max_tmp_tables, &sys_max_tmp_tables,
&sys_max_user_connections, &sys_max_user_connections,
&sys_max_write_lock_count, &sys_max_write_lock_count,
...@@ -892,6 +895,8 @@ struct show_var_st init_vars[]= { ...@@ -892,6 +895,8 @@ struct show_var_st init_vars[]= {
{sys_max_relay_log_size.name, (char*) &sys_max_relay_log_size, SHOW_SYS}, {sys_max_relay_log_size.name, (char*) &sys_max_relay_log_size, SHOW_SYS},
{sys_max_seeks_for_key.name, (char*) &sys_max_seeks_for_key, SHOW_SYS}, {sys_max_seeks_for_key.name, (char*) &sys_max_seeks_for_key, SHOW_SYS},
{sys_max_sort_length.name, (char*) &sys_max_sort_length, SHOW_SYS}, {sys_max_sort_length.name, (char*) &sys_max_sort_length, SHOW_SYS},
{sys_max_sp_recursion_depth.name,
(char*) &sys_max_sp_recursion_depth, SHOW_SYS},
{sys_max_tmp_tables.name, (char*) &sys_max_tmp_tables, SHOW_SYS}, {sys_max_tmp_tables.name, (char*) &sys_max_tmp_tables, SHOW_SYS},
{sys_max_user_connections.name,(char*) &sys_max_user_connections, SHOW_SYS}, {sys_max_user_connections.name,(char*) &sys_max_user_connections, SHOW_SYS},
{sys_max_write_lock_count.name, (char*) &sys_max_write_lock_count,SHOW_SYS}, {sys_max_write_lock_count.name, (char*) &sys_max_write_lock_count,SHOW_SYS},
......
...@@ -5361,7 +5361,7 @@ ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG ...@@ -5361,7 +5361,7 @@ ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG
ER_NO_DEFAULT_FOR_VIEW_FIELD ER_NO_DEFAULT_FOR_VIEW_FIELD
eng "Field of view '%-.64s.%-.64s' underlying table doesn't have a default value" eng "Field of view '%-.64s.%-.64s' underlying table doesn't have a default value"
ER_SP_NO_RECURSION ER_SP_NO_RECURSION
eng "Recursive stored routines are not allowed." eng "Recursive stored functions and triggers are not allowed."
ER_TOO_BIG_SCALE 42000 S1009 ER_TOO_BIG_SCALE 42000 S1009
eng "Too big scale %d specified for column '%-.64s'. Maximum is %d." eng "Too big scale %d specified for column '%-.64s'. Maximum is %d."
ER_TOO_BIG_PRECISION 42000 S1009 ER_TOO_BIG_PRECISION 42000 S1009
...@@ -5421,3 +5421,5 @@ ER_NO_REFERENCED_ROW_2 23000 ...@@ -5421,3 +5421,5 @@ ER_NO_REFERENCED_ROW_2 23000
eng "Cannot add or update a child row: a foreign key constraint fails (%.192s)" eng "Cannot add or update a child row: a foreign key constraint fails (%.192s)"
ER_SP_BAD_VAR_SHADOW 42000 ER_SP_BAD_VAR_SHADOW 42000
eng "Variable '%-.64s' must be quoted with `...`, or renamed" eng "Variable '%-.64s' must be quoted with `...`, or renamed"
ER_SP_RECURSION_LIMIT
eng "Recursive limit %d (as set by the max_sp_recursion_depth variable) was exceeded for routine %.64s"
This diff is collapsed.
...@@ -36,7 +36,8 @@ int ...@@ -36,7 +36,8 @@ int
sp_drop_db_routines(THD *thd, char *db); sp_drop_db_routines(THD *thd, char *db);
sp_head * sp_head *
sp_find_procedure(THD *thd, sp_name *name, bool cache_only = 0); sp_find_routine(THD *thd, int type, sp_name *name,
sp_cache **cp, bool cache_only);
int int
sp_exists_routine(THD *thd, TABLE_LIST *procs, bool any, bool no_error); sp_exists_routine(THD *thd, TABLE_LIST *procs, bool any, bool no_error);
...@@ -57,9 +58,6 @@ sp_show_create_procedure(THD *thd, sp_name *name); ...@@ -57,9 +58,6 @@ sp_show_create_procedure(THD *thd, sp_name *name);
int int
sp_show_status_procedure(THD *thd, const char *wild); sp_show_status_procedure(THD *thd, const char *wild);
sp_head *
sp_find_function(THD *thd, sp_name *name, bool cache_only = 0);
int int
sp_create_function(THD *thd, sp_head *sp); sp_create_function(THD *thd, sp_head *sp);
......
...@@ -437,7 +437,8 @@ sp_head::operator delete(void *ptr, size_t size) ...@@ -437,7 +437,8 @@ sp_head::operator delete(void *ptr, size_t size)
sp_head::sp_head() sp_head::sp_head()
:Query_arena(&main_mem_root, INITIALIZED_FOR_SP), :Query_arena(&main_mem_root, INITIALIZED_FOR_SP),
m_flags(0), m_returns_cs(NULL) m_flags(0), m_returns_cs(NULL), m_recursion_level(0), m_next_cached_sp(0),
m_first_instance(this), m_first_free_instance(this), m_last_cached_sp(this)
{ {
extern byte * extern byte *
sp_table_key(const byte *ptr, uint *plen, my_bool first); sp_table_key(const byte *ptr, uint *plen, my_bool first);
...@@ -615,6 +616,7 @@ sp_head::create(THD *thd) ...@@ -615,6 +616,7 @@ sp_head::create(THD *thd)
sp_head::~sp_head() sp_head::~sp_head()
{ {
destroy(); destroy();
delete m_next_cached_sp;
if (m_thd) if (m_thd)
restore_thd_mem_root(m_thd); restore_thd_mem_root(m_thd);
} }
...@@ -840,6 +842,31 @@ static bool subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) ...@@ -840,6 +842,31 @@ static bool subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str)
} }
/*
Return appropriate error about recursion limit reaching
SYNOPSIS
sp_head::recursion_level_error()
NOTE
For functions and triggers we return error about prohibited recursion.
For stored procedures we return about reaching recursion limit.
*/
void sp_head::recursion_level_error()
{
if (m_type == TYPE_ENUM_PROCEDURE)
{
THD *thd= current_thd;
my_error(ER_SP_RECURSION_LIMIT, MYF(0),
thd->variables.max_sp_recursion_depth,
m_name);
}
else
my_error(ER_SP_NO_RECURSION, MYF(0));
}
/* /*
Execute the routine. The main instruction jump loop is there Execute the routine. The main instruction jump loop is there
Assume the parameters already set. Assume the parameters already set.
...@@ -869,37 +896,31 @@ int sp_head::execute(THD *thd) ...@@ -869,37 +896,31 @@ int sp_head::execute(THD *thd)
Item_change_list old_change_list; Item_change_list old_change_list;
String old_packet; String old_packet;
/* init per-instruction memroot */
init_alloc_root(&execute_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
/* Use some extra margin for possible SP recursion and functions */ /* Use some extra margin for possible SP recursion and functions */
if (check_stack_overrun(thd, 4*STACK_MIN_SIZE, olddb)) if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (char*)&old_packet))
{ {
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
if (m_flags & IS_INVOKED) /* init per-instruction memroot */
{ init_alloc_root(&execute_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
DBUG_ASSERT(!(m_flags & IS_INVOKED));
m_flags|= IS_INVOKED;
m_first_instance->m_first_free_instance= m_next_cached_sp;
DBUG_PRINT("info", ("first free for 0x%lx ++: 0x%lx->0x%lx, level: %lu, flags %x",
(ulong)m_first_instance, this, m_next_cached_sp,
m_next_cached_sp->m_recursion_level,
m_next_cached_sp->m_flags));
/* /*
We have to disable recursion for stored routines since in Check that if there are not any instances after this one then
many cases LEX structure and many Item's can't be used in pointer to the last instance points on this instance or if there are
reentrant way now. some instances after this one then recursion level of next instance
greater then recursion level of current instance on 1
TODO: We can circumvent this problem by using separate
sp_head instances for each recursive invocation.
NOTE: Theoretically arguments of procedure can be evaluated
before its invocation so there should be no problem with
recursion. But since we perform cleanup for CALL statement
as for any other statement only after its execution, its LEX
structure is not reusable for recursive calls. Thus we have
to prohibit recursion for stored procedures too.
*/ */
my_error(ER_SP_NO_RECURSION, MYF(0)); DBUG_ASSERT((m_next_cached_sp == 0 &&
DBUG_RETURN(-1); m_first_instance->m_last_cached_sp == this) ||
} (m_recursion_level + 1 == m_next_cached_sp->m_recursion_level));
m_flags|= IS_INVOKED;
dbchanged= FALSE; dbchanged= FALSE;
if (m_db.length && if (m_db.length &&
...@@ -1074,6 +1095,29 @@ int sp_head::execute(THD *thd) ...@@ -1074,6 +1095,29 @@ int sp_head::execute(THD *thd)
ret= mysql_change_db(thd, olddb, 1); ret= mysql_change_db(thd, olddb, 1);
} }
m_flags&= ~IS_INVOKED; m_flags&= ~IS_INVOKED;
DBUG_PRINT("info", ("first free for 0x%lx --: 0x%lx->0x%lx, level: %lu, flags %x",
(ulong)m_first_instance,
m_first_instance->m_first_free_instance, this,
m_recursion_level, m_flags));
/*
Check that we have one of following:
1) there are not free instances which means that this instance is last
in the list of instances (pointer to the last instance point on it and
ther are not other instances after this one in the list)
2) There are some free instances which mean that first free instance
should go just after this one and recursion level of that free instance
should be on 1 more then recursion leven of this instance.
*/
DBUG_ASSERT((m_first_instance->m_first_free_instance == 0 &&
this == m_first_instance->m_last_cached_sp &&
m_next_cached_sp == 0) ||
(m_first_instance->m_first_free_instance != 0 &&
m_first_instance->m_first_free_instance == m_next_cached_sp &&
m_first_instance->m_first_free_instance->m_recursion_level ==
m_recursion_level + 1));
m_first_instance->m_first_free_instance= this;
DBUG_RETURN(ret); DBUG_RETURN(ret);
} }
......
...@@ -140,6 +140,32 @@ public: ...@@ -140,6 +140,32 @@ public:
LEX_STRING m_definer_host; LEX_STRING m_definer_host;
longlong m_created; longlong m_created;
longlong m_modified; longlong m_modified;
/* Recursion level of the current SP instance. The levels are numbered from 0 */
ulong m_recursion_level;
/*
A list of diferent recursion level instances for the same procedure.
For every recursion level we have a sp_head instance. This instances
connected in the list. The list ordered by increasing recursion level
(m_recursion_level).
*/
sp_head *m_next_cached_sp;
/*
Pointer to the first element of the above list
*/
sp_head *m_first_instance;
/*
Pointer to the first free (non-INVOKED) routine in the list of
cached instances for this SP. This pointer is set only for the first
SP in the list of instences (see above m_first_cached_sp pointer).
The pointer equal to 0 if we have no free instances.
For non-first instance value of this pointer meanless (point to itself);
*/
sp_head *m_first_free_instance;
/*
Pointer to the last element in the list of instances of the SP.
For non-first instance value of this pointer meanless (point to itself);
*/
sp_head *m_last_cached_sp;
/* /*
Set containing names of stored routines used by this routine. Set containing names of stored routines used by this routine.
Note that unlike elements of similar set for statement elements of this Note that unlike elements of similar set for statement elements of this
...@@ -262,6 +288,8 @@ public: ...@@ -262,6 +288,8 @@ public:
void optimize(); void optimize();
void opt_mark(uint ip); void opt_mark(uint ip);
void recursion_level_error();
inline sp_instr * inline sp_instr *
get_instr(uint i) get_instr(uint i)
{ {
......
...@@ -1087,6 +1087,11 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, ...@@ -1087,6 +1087,11 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
/* find a unused table in the open table cache */ /* find a unused table in the open table cache */
if (refresh) if (refresh)
*refresh=0; *refresh=0;
/* an open table operation needs a lot of the stack space */
if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (char *)&alias))
return 0;
if (thd->killed) if (thd->killed)
DBUG_RETURN(0); DBUG_RETURN(0);
key_length= (uint) (strmov(strmov(key, table_list->db)+1, key_length= (uint) (strmov(strmov(key, table_list->db)+1,
......
...@@ -534,6 +534,7 @@ struct system_variables ...@@ -534,6 +534,7 @@ struct system_variables
ulong completion_type; ulong completion_type;
/* Determines which non-standard SQL behaviour should be enabled */ /* Determines which non-standard SQL behaviour should be enabled */
ulong sql_mode; ulong sql_mode;
ulong max_sp_recursion_depth;
/* check of key presence in updatable view */ /* check of key presence in updatable view */
ulong updatable_views_with_limit; ulong updatable_views_with_limit;
ulong default_week_format; ulong default_week_format;
......
...@@ -3694,7 +3694,8 @@ end_with_restore_list: ...@@ -3694,7 +3694,8 @@ end_with_restore_list:
if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0)) if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0))
break; break;
#ifdef HAVE_DLOPEN #ifdef HAVE_DLOPEN
if (sp_find_function(thd, lex->spname)) if (sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
&thd->sp_func_cache, FALSE))
{ {
my_error(ER_UDF_EXISTS, MYF(0), lex->spname->m_name.str); my_error(ER_UDF_EXISTS, MYF(0), lex->spname->m_name.str);
goto error; goto error;
...@@ -4228,7 +4229,8 @@ end_with_restore_list: ...@@ -4228,7 +4229,8 @@ end_with_restore_list:
By this moment all needed SPs should be in cache so no need to look By this moment all needed SPs should be in cache so no need to look
into DB. into DB.
*/ */
if (!(sp= sp_find_procedure(thd, lex->spname, TRUE))) if (!(sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
&thd->sp_proc_cache, TRUE)))
{ {
my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE", my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE",
lex->spname->m_qname.str); lex->spname->m_qname.str);
...@@ -4364,9 +4366,11 @@ end_with_restore_list: ...@@ -4364,9 +4366,11 @@ end_with_restore_list:
memcpy(&chistics, &lex->sp_chistics, sizeof(chistics)); memcpy(&chistics, &lex->sp_chistics, sizeof(chistics));
if (lex->sql_command == SQLCOM_ALTER_PROCEDURE) if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
sp= sp_find_procedure(thd, lex->spname); sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
&thd->sp_proc_cache, FALSE);
else else
sp= sp_find_function(thd, lex->spname); sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
&thd->sp_func_cache, FALSE);
mysql_reset_errors(thd, 0); mysql_reset_errors(thd, 0);
if (! sp) if (! sp)
{ {
...@@ -4435,9 +4439,11 @@ end_with_restore_list: ...@@ -4435,9 +4439,11 @@ end_with_restore_list:
char *db, *name; char *db, *name;
if (lex->sql_command == SQLCOM_DROP_PROCEDURE) if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
sp= sp_find_procedure(thd, lex->spname); sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname,
&thd->sp_proc_cache, FALSE);
else else
sp= sp_find_function(thd, lex->spname); sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, lex->spname,
&thd->sp_func_cache, FALSE);
mysql_reset_errors(thd, 0); mysql_reset_errors(thd, 0);
if (sp) if (sp)
{ {
......
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