diff options
-rw-r--r-- | client/mysqltest.c | 40 | ||||
-rw-r--r-- | mysql-test/r/sp-dynamic.result | 15 | ||||
-rw-r--r-- | mysql-test/r/sp-error.result | 9 | ||||
-rw-r--r-- | mysql-test/r/sp.result | 185 | ||||
-rw-r--r-- | mysql-test/r/trigger.result | 3 | ||||
-rw-r--r-- | mysql-test/r/variables.result | 8 | ||||
-rw-r--r-- | mysql-test/t/sp-dynamic.test | 17 | ||||
-rw-r--r-- | mysql-test/t/sp-error.test | 7 | ||||
-rw-r--r-- | mysql-test/t/sp.test | 154 | ||||
-rw-r--r-- | mysql-test/t/trigger.test | 4 | ||||
-rw-r--r-- | mysql-test/t/variables.test | 4 | ||||
-rw-r--r-- | sql/item_func.cc | 11 | ||||
-rw-r--r-- | sql/mysqld.cc | 6 | ||||
-rw-r--r-- | sql/set_var.cc | 5 | ||||
-rw-r--r-- | sql/share/errmsg.txt | 4 | ||||
-rw-r--r-- | sql/sp.cc | 319 | ||||
-rw-r--r-- | sql/sp.h | 6 | ||||
-rw-r--r-- | sql/sp_head.cc | 96 | ||||
-rw-r--r-- | sql/sp_head.h | 28 | ||||
-rw-r--r-- | sql/sql_base.cc | 5 | ||||
-rw-r--r-- | sql/sql_class.h | 1 | ||||
-rw-r--r-- | sql/sql_parse.cc | 18 |
22 files changed, 726 insertions, 219 deletions
diff --git a/client/mysqltest.c b/client/mysqltest.c index 58c0928c36d..f6d28101fa0 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -3314,20 +3314,23 @@ static int handle_error(const char *query, struct st_query *q, ((q->expected_errno[i].type == ERR_SQLSTATE) && (strcmp(q->expected_errno[i].code.sqlstate, err_sqlstate) == 0))) { - if (q->expected_errors == 1) + if (!disable_result_log) { - /* Only log error if there is one possible error */ - dynstr_append_mem(ds, "ERROR ", 6); - replace_dynstr_append(ds, err_sqlstate); - dynstr_append_mem(ds, ": ", 2); - replace_dynstr_append(ds, err_error); - dynstr_append_mem(ds,"\n",1); + if (q->expected_errors == 1) + { + /* Only log error if there is one possible error */ + dynstr_append_mem(ds, "ERROR ", 6); + replace_dynstr_append(ds, err_sqlstate); + dynstr_append_mem(ds, ": ", 2); + replace_dynstr_append(ds, err_error); + dynstr_append_mem(ds,"\n",1); + } + /* Don't log error if we may not get an error */ + else if (q->expected_errno[0].type == ERR_SQLSTATE || + (q->expected_errno[0].type == ERR_ERRNO && + q->expected_errno[0].code.errnum != 0)) + dynstr_append(ds,"Got one of the listed errors\n"); } - /* Don't log error if we may not get an error */ - else if (q->expected_errno[0].type == ERR_SQLSTATE || - (q->expected_errno[0].type == ERR_ERRNO && - q->expected_errno[0].code.errnum != 0)) - dynstr_append(ds,"Got one of the listed errors\n"); /* OK */ DBUG_RETURN(0); } @@ -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)); - dynstr_append_mem(ds, "ERROR ",6); - replace_dynstr_append(ds, err_sqlstate); - dynstr_append_mem(ds, ": ", 2); - replace_dynstr_append(ds, err_error); - dynstr_append_mem(ds, "\n", 1); + if (!disable_result_log) + { + dynstr_append_mem(ds, "ERROR ",6); + replace_dynstr_append(ds, err_sqlstate); + dynstr_append_mem(ds, ": ", 2); + replace_dynstr_append(ds, err_error); + dynstr_append_mem(ds, "\n", 1); + } if (i) { diff --git a/mysql-test/r/sp-dynamic.result b/mysql-test/r/sp-dynamic.result index 8fe469431cc..cf07f540608 100644 --- a/mysql-test/r/sp-dynamic.result +++ b/mysql-test/r/sp-dynamic.result @@ -33,6 +33,8 @@ begin execute stmt; end| prepare stmt from "call p1()"| +set @SAVE_SP_RECURSION_LEVELS=@@max_sp_recursion_depth| +set @@max_sp_recursion_depth=100| 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 execute stmt| @@ -40,11 +42,18 @@ ERROR HY000: The prepared statement contains a stored routine call that refers t 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 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()| -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()| -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| create procedure p1() begin diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 858f7d0bb16..963f14820be 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -708,7 +708,7 @@ return (i in (100, 200, bug11394(i-1), 400)); end if; end| select bug11394(2)| -ERROR HY000: Recursive stored routines are not allowed. +ERROR HY000: Recursive stored functions and triggers are not allowed. drop function bug11394| create function bug11394_1(i int) returns int begin @@ -719,7 +719,7 @@ return (select bug11394_1(i-1)); end if; end| 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| create function bug11394_2(i int) returns int return i| select bug11394_2(bug11394_2(10))| @@ -733,7 +733,10 @@ call bug11394(i - 1,(select 1)); end if; end| 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| CREATE PROCEDURE BUG_12490() HELP CONTENTS; ERROR 0A000: HELP is not allowed in stored procedures diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 29a4fbaa439..b1216f94245 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -3617,6 +3617,191 @@ count(*) drop table t3, t4| drop procedure bug14210| 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 function if exists bug14723| drop procedure if exists bug14723| /*!50003 create function bug14723() diff --git a/mysql-test/r/trigger.result b/mysql-test/r/trigger.result index 9fc62ed123f..ff92fc543d4 100644 --- a/mysql-test/r/trigger.result +++ b/mysql-test/r/trigger.result @@ -703,8 +703,11 @@ create trigger t1_ai after insert on t1 for each row insert into t2 values (new.f1+1); create trigger t2_ai after insert on t2 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); 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; f1 1 diff --git a/mysql-test/r/variables.result b/mysql-test/r/variables.result index 3ecc48620b1..df180218a09 100644 --- a/mysql-test/r/variables.result +++ b/mysql-test/r/variables.result @@ -351,6 +351,14 @@ set global rpl_recovery_rank=100; set global server_id=100; set global slow_launch_time=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; select @@sql_auto_is_null; @@sql_auto_is_null diff --git a/mysql-test/t/sp-dynamic.test b/mysql-test/t/sp-dynamic.test index e9816ee3ef0..5416f5931ff 100644 --- a/mysql-test/t/sp-dynamic.test +++ b/mysql-test/t/sp-dynamic.test @@ -26,18 +26,29 @@ begin execute stmt; end| 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 execute stmt| --error ER_PS_NO_RECURSION execute stmt| --error ER_PS_NO_RECURSION execute stmt| ---error ER_SP_NO_RECURSION +--error ER_PS_NO_RECURSION +call p1()| +--error ER_PS_NO_RECURSION call p1()| ---error ER_SP_NO_RECURSION +--error ER_PS_NO_RECURSION call p1()| ---error ER_SP_NO_RECURSION +set @@max_sp_recursion_depth=@SAVE_SP_RECURSION_LEVELS| +--error ER_SP_RECURSION_LIMIT call p1()| +--error ER_SP_RECURSION_LIMIT +call p1()| +--error ER_SP_RECURSION_LIMIT +call p1()| + drop procedure p1| # # C. Create/drop a stored procedure in Dynamic SQL. diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index 5057dd0d9f8..69e5f73817b 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -1044,10 +1044,11 @@ begin call bug11394(i - 1,(select 1)); end if; end| -# Again if we allow recursion for stored procedures (without -# additional efforts) the following statement will crash the server. ---error 1424 +--error ER_SP_RECURSION_LIMIT +call bug11394(2, 1)| +set @@max_sp_recursion_depth=10| call bug11394(2, 1)| +set @@max_sp_recursion_depth=default| drop procedure bug11394| delimiter ;| diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 022721c7794..def6afc000e 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -4623,6 +4623,160 @@ drop procedure bug13549_2| drop procedure bug13549_1| # +# 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 # #--disable_warnings diff --git a/mysql-test/t/trigger.test b/mysql-test/t/trigger.test index daa6ac2e6fa..d4fa5268762 100644 --- a/mysql-test/t/trigger.test +++ b/mysql-test/t/trigger.test @@ -743,8 +743,12 @@ create trigger t1_ai after insert on t1 for each row insert into t2 values (new.f1+1); create trigger t2_ai after insert on t2 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 insert into t1 values (1); +set @@max_sp_recursion_depth=@SAVE_SP_RECURSION_LEVELS; select * from t1; select * from t2; drop trigger t1_ai; diff --git a/mysql-test/t/variables.test b/mysql-test/t/variables.test index f23cc0152c1..2d0e2dbc9c9 100644 --- a/mysql-test/t/variables.test +++ b/mysql-test/t/variables.test @@ -237,6 +237,10 @@ set global rpl_recovery_rank=100; set global server_id=100; set global slow_launch_time=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; select @@sql_auto_is_null; set @@sql_auto_is_null=0; diff --git a/sql/item_func.cc b/sql/item_func.cc index c20a774e57e..ef3ebde74e5 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -4690,10 +4690,16 @@ Item_func_sp::sp_result_field(void) const { Field *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= 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); DBUG_RETURN(0); @@ -4919,7 +4925,8 @@ Item_func_sp::find_and_check_access(THD *thd, ulong want_access, bool res= TRUE; *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); goto error; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 35032d1ec41..b5b95e48889 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4545,6 +4545,7 @@ enum options_mysqld OPT_OPTIMIZER_PRUNE_LEVEL, OPT_UPDATABLE_VIEWS_WITH_LIMIT, OPT_SP_AUTOMATIC_PRIVILEGES, + OPT_MAX_SP_RECURSION_DEPTH, OPT_AUTO_INCREMENT, OPT_AUTO_INCREMENT_OFFSET, OPT_ENABLE_LARGE_PAGES, OPT_TIMED_MUTEXES, @@ -5760,6 +5761,11 @@ The minimum value for this variable is 4096.", (gptr*) &global_system_variables.read_buff_size, (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}, + {"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 {"relay_log_purge", OPT_RELAY_LOG_PURGE, "0 = do not purge relay logs. 1 = purge them as soon as they are no more needed.", diff --git a/sql/set_var.cc b/sql/set_var.cc index 89ad00d7839..632c37d2296 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -261,6 +261,8 @@ sys_var_long_ptr sys_max_relay_log_size("max_relay_log_size", fix_max_relay_log_size); sys_var_thd_ulong sys_max_sort_length("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_thd_ulong sys_max_tmp_tables("max_tmp_tables", &SV::max_tmp_tables); @@ -631,6 +633,7 @@ sys_var *sys_variables[]= &sys_max_relay_log_size, &sys_max_seeks_for_key, &sys_max_sort_length, + &sys_max_sp_recursion_depth, &sys_max_tmp_tables, &sys_max_user_connections, &sys_max_write_lock_count, @@ -896,6 +899,8 @@ struct show_var_st init_vars[]= { {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_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_user_connections.name,(char*) &sys_max_user_connections, SHOW_SYS}, {sys_max_write_lock_count.name, (char*) &sys_max_write_lock_count,SHOW_SYS}, diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index d2da1d0a7a8..d0773e8af3b 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5361,7 +5361,7 @@ ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG ER_NO_DEFAULT_FOR_VIEW_FIELD eng "Field of view '%-.64s.%-.64s' underlying table doesn't have a default value" 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 eng "Too big scale %d specified for column '%-.64s'. Maximum is %d." ER_TOO_BIG_PRECISION 42000 S1009 @@ -5425,3 +5425,5 @@ ER_TRG_NO_DEFINER eng "No definer attribute for trigger '%-.64s'.'%-.64s'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger." ER_OLD_FILE_FORMAT eng "'%-.64s' has an old format, you should re-create the '%s' object(s)" +ER_SP_RECURSION_LIMIT + eng "Recursive limit %d (as set by the max_sp_recursion_depth variable) was exceeded for routine %.64s" diff --git a/sql/sp.cc b/sql/sp.cc index 6939f87b31a..861a41d8501 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -29,6 +29,11 @@ create_string(THD *thd, String *buf, const char *returns, ulong returnslen, const char *body, ulong bodylen, st_sp_chistics *chistics); +static int +db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, + ulong sql_mode, const char *params, const char *returns, + const char *body, st_sp_chistics &chistics, + const char *definer, longlong created, longlong modified); /* * @@ -377,87 +382,77 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) close_proc_table(thd, &open_tables_state_backup); table= 0; - { - String defstr; - LEX *oldlex= thd->lex; - sp_rcontext *save_spcont= thd->spcont; - char olddb[128]; - bool dbchanged; - enum enum_sql_command oldcmd= thd->lex->sql_command; - ulong old_sql_mode= thd->variables.sql_mode; - ha_rows select_limit= thd->variables.select_limit; - - thd->variables.sql_mode= sql_mode; - thd->variables.select_limit= HA_POS_ERROR; - - defstr.set_charset(system_charset_info); - if (!create_string(thd, &defstr, - type, - name, - params, strlen(params), - returns, strlen(returns), - body, strlen(body), - &chistics)) - { - ret= SP_INTERNAL_ERROR; - goto done; - } + ret= db_load_routine(thd, type, name, sphp, + sql_mode, params, returns, body, chistics, + definer, created, modified); + + done: + if (table) + close_proc_table(thd, &open_tables_state_backup); + DBUG_RETURN(ret); +} - dbchanged= FALSE; - if ((ret= sp_use_new_db(thd, name->m_db.str, olddb, sizeof(olddb), - 1, &dbchanged))) - goto done; - { - /* This is something of a kludge. We need to initialize some fields - * in thd->lex (the unit and master stuff), and the easiest way to - * do it is, is to call mysql_init_query(), but this unfortunately - * resets teh value_list where we keep the CALL parameters. So we - * copy the list and then restore it. (... and found_semicolon too). - */ - List<Item> tmpvals= thd->lex->value_list; - char *tmpfsc= thd->lex->found_semicolon; - - lex_start(thd, (uchar*)defstr.c_ptr(), defstr.length()); - thd->lex->value_list= tmpvals; - thd->lex->found_semicolon= tmpfsc; - } +static int +db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, + ulong sql_mode, const char *params, const char *returns, + const char *body, st_sp_chistics &chistics, + const char *definer, longlong created, longlong modified) +{ + LEX *oldlex= thd->lex, newlex; + String defstr; + char olddb[128]; + bool dbchanged; + ulong old_sql_mode= thd->variables.sql_mode; + ha_rows select_limit= thd->variables.select_limit; + int ret= SP_INTERNAL_ERROR; + + thd->variables.sql_mode= sql_mode; + thd->variables.select_limit= HA_POS_ERROR; + + thd->lex= &newlex; + newlex.current_select= NULL; + + defstr.set_charset(system_charset_info); + if (!create_string(thd, &defstr, + type, + name, + params, strlen(params), + returns, strlen(returns), + body, strlen(body), + &chistics)) + goto end; - thd->spcont= 0; - if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL) - { - LEX *newlex= thd->lex; - sp_head *sp= newlex->sphead; + dbchanged= FALSE; + if ((ret= sp_use_new_db(thd, name->m_db.str, olddb, sizeof(olddb), + 1, &dbchanged))) + goto end; - if (dbchanged && (ret= mysql_change_db(thd, olddb, 1))) - goto done; - if (sp) - { - delete sp; - newlex->sphead= NULL; - } - ret= SP_PARSE_ERROR; - } - else - { - if (dbchanged && (ret= mysql_change_db(thd, olddb, 1))) - goto db_done; - *sphp= thd->lex->sphead; - (*sphp)->set_definer((char*) definer, (uint) strlen(definer)); - (*sphp)->set_info(created, modified, &chistics, sql_mode); - (*sphp)->optimize(); - } -db_done: - thd->spcont= save_spcont; - thd->lex->sql_command= oldcmd; - thd->variables.sql_mode= old_sql_mode; - thd->variables.select_limit= select_limit; - } + lex_start(thd, (uchar*)defstr.c_ptr(), defstr.length()); - done: - if (table) - close_proc_table(thd, &open_tables_state_backup); - DBUG_RETURN(ret); + if (yyparse(thd) || thd->is_fatal_error || newlex.sphead == NULL) + { + sp_head *sp= newlex.sphead; + + if (dbchanged && (ret= mysql_change_db(thd, olddb, 1))) + goto end; + delete sp; + ret= SP_PARSE_ERROR; + } + else + { + if (dbchanged && (ret= mysql_change_db(thd, olddb, 1))) + goto end; + *sphp= newlex.sphead; + (*sphp)->set_info((char *)definer, (uint)strlen(definer), + created, modified, &chistics, sql_mode); + (*sphp)->optimize(); + } + thd->variables.sql_mode= old_sql_mode; + thd->variables.select_limit= select_limit; +end: + thd->lex= oldlex; + return ret; } @@ -555,13 +550,12 @@ db_create_routine(THD *thd, int type, sp_head *sp) store(sp->m_chistics->comment.str, sp->m_chistics->comment.length, system_charset_info); - if ((sp->m_type == TYPE_ENUM_FUNCTION) && - !trust_function_creators && mysql_bin_log.is_open()) + if (!trust_routine_creators && mysql_bin_log.is_open()) { if (!sp->m_chistics->detistic) { /* - Note that this test is not perfect; one could use + Note that for a _function_ this test is not enough; one could use a non-deterministic read-only function in an update statement. */ enum enum_sp_data_access access= @@ -903,45 +897,106 @@ err: ******************************************************************************/ /* - Obtain object representing stored procedure by its name from + Obtain object representing stored procedure/function by its name from stored procedures cache and looking into mysql.proc if needed. SYNOPSIS - sp_find_procedure() + sp_find_routine() thd - thread context + type - type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE) name - name of procedure + cp - hash to look routine in cache_only - if true perform cache-only lookup (Don't look in mysql.proc). - TODO - We should consider merging of sp_find_procedure() and - sp_find_function() into one sp_find_routine() function - (the same applies to other similarly paired functions). - RETURN VALUE Non-0 pointer to sp_head object for the procedure, or 0 - in case of error. */ sp_head * -sp_find_procedure(THD *thd, sp_name *name, bool cache_only) +sp_find_routine(THD *thd, int type, sp_name *name, sp_cache **cp, + bool cache_only) { sp_head *sp; - DBUG_ENTER("sp_find_procedure"); - DBUG_PRINT("enter", ("name: %.*s.%.*s", - name->m_db.length, name->m_db.str, - name->m_name.length, name->m_name.str)); + ulong depth= (type == TYPE_ENUM_PROCEDURE ? + thd->variables.max_sp_recursion_depth : + 0); + + DBUG_ENTER("sp_find_routine"); + DBUG_PRINT("enter", ("name: %.*s.%.*s, type: %d, cache only %d", + name->m_db.length, name->m_db.str, + name->m_name.length, name->m_name.str, + type, cache_only)); - if (!(sp= sp_cache_lookup(&thd->sp_proc_cache, name)) && !cache_only) + if ((sp= sp_cache_lookup(cp, name))) { - if (db_find_routine(thd, TYPE_ENUM_PROCEDURE, name, &sp) == SP_OK) - sp_cache_insert(&thd->sp_proc_cache, sp); + ulong level; + DBUG_PRINT("info", ("found: 0x%lx", (ulong)sp)); + if (sp->m_first_free_instance) + { + DBUG_PRINT("info", ("first free: 0x%lx, level: %lu, flags %x", + (ulong)sp->m_first_free_instance, + sp->m_first_free_instance->m_recursion_level, + sp->m_first_free_instance->m_flags)); + DBUG_ASSERT(!(sp->m_first_free_instance->m_flags & sp_head::IS_INVOKED)); + if (sp->m_first_free_instance->m_recursion_level > depth) + { + sp->recursion_level_error(); + DBUG_RETURN(0); + } + DBUG_RETURN(sp->m_first_free_instance); + } + level= sp->m_last_cached_sp->m_recursion_level + 1; + if (level > depth) + { + sp->recursion_level_error(); + DBUG_RETURN(0); + } + { + sp_head *new_sp; + const char *returns= ""; + char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2]; + String retstr(64); + strxmov(definer, sp->m_definer_user.str, "@", + sp->m_definer_host.str, NullS); + if (type == TYPE_ENUM_FUNCTION) + { + sp_returns_type(thd, retstr, sp); + returns= retstr.ptr(); + } + if (db_load_routine(thd, type, name, &new_sp, + sp->m_sql_mode, sp->m_params.str, returns, + sp->m_body.str, *sp->m_chistics, definer, + sp->m_created, sp->m_modified) == SP_OK) + { + sp->m_last_cached_sp->m_next_cached_sp= new_sp; + new_sp->m_recursion_level= level; + new_sp->m_first_instance= sp; + sp->m_last_cached_sp= sp->m_first_free_instance= new_sp; + DBUG_PRINT("info", ("added level: 0x%lx, level: %lu, flags %x", + (ulong)new_sp, new_sp->m_recursion_level, + new_sp->m_flags)); + DBUG_RETURN(new_sp); + } + DBUG_RETURN(0); + } + } + if (!cache_only) + { + if (db_find_routine(thd, type, name, &sp) == SP_OK) + { + sp_cache_insert(cp, sp); + DBUG_PRINT("info", ("added new: 0x%lx, level: %lu, flags %x", + (ulong)sp, sp->m_recursion_level, + sp->m_flags)); + } } - DBUG_RETURN(sp); } + int sp_exists_routine(THD *thd, TABLE_LIST *tables, bool any, bool no_error) { @@ -959,8 +1014,10 @@ sp_exists_routine(THD *thd, TABLE_LIST *tables, bool any, bool no_error) lex_name.str= thd->strmake(table->table_name, lex_name.length); name= new sp_name(lex_db, lex_name); name->init_qname(thd); - if (sp_find_procedure(thd, name) != NULL || - sp_find_function(thd, name) != NULL) + if (sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name, + &thd->sp_proc_cache, FALSE) != NULL || + sp_find_routine(thd, TYPE_ENUM_FUNCTION, name, + &thd->sp_func_cache, FALSE) != NULL) { if (any) DBUG_RETURN(1); @@ -1028,7 +1085,8 @@ sp_show_create_procedure(THD *thd, sp_name *name) DBUG_ENTER("sp_show_create_procedure"); DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str)); - if ((sp= sp_find_procedure(thd, name))) + if ((sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name, + &thd->sp_proc_cache, FALSE))) { int ret= sp->show_create_procedure(thd); @@ -1054,42 +1112,6 @@ sp_show_status_procedure(THD *thd, const char *wild) FUNCTION ******************************************************************************/ -/* - Obtain object representing stored function by its name from - stored functions cache and looking into mysql.proc if needed. - - SYNOPSIS - sp_find_function() - thd - thread context - name - name of function - cache_only - if true perform cache-only lookup - (Don't look in mysql.proc). - - NOTE - See TODO section for sp_find_procedure(). - - RETURN VALUE - Non-0 pointer to sp_head object for the function, or - 0 - in case of error. -*/ - -sp_head * -sp_find_function(THD *thd, sp_name *name, bool cache_only) -{ - sp_head *sp; - DBUG_ENTER("sp_find_function"); - DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str)); - - if (!(sp= sp_cache_lookup(&thd->sp_func_cache, name)) && - !cache_only) - { - if (db_find_routine(thd, TYPE_ENUM_FUNCTION, name, &sp) == SP_OK) - sp_cache_insert(&thd->sp_func_cache, sp); - } - DBUG_RETURN(sp); -} - - int sp_create_function(THD *thd, sp_head *sp) { @@ -1137,7 +1159,8 @@ sp_show_create_function(THD *thd, sp_name *name) DBUG_ENTER("sp_show_create_function"); DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str)); - if ((sp= sp_find_function(thd, name))) + if ((sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, name, + &thd->sp_func_cache, FALSE))) { int ret= sp->show_create_function(thd); @@ -1447,10 +1470,6 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, &thd->sp_func_cache : &thd->sp_proc_cache), &name))) { - LEX *oldlex= thd->lex; - LEX *newlex= new st_lex; - thd->lex= newlex; - newlex->current_select= NULL; name.m_name.str= strchr(name.m_qname.str, '.'); name.m_db.length= name.m_name.str - name.m_qname.str; name.m_db.str= strmake_root(thd->mem_root, name.m_qname.str, @@ -1465,8 +1484,6 @@ sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex, else sp_cache_insert(&thd->sp_proc_cache, sp); } - delete newlex; - thd->lex= oldlex; } if (sp) { @@ -1589,30 +1606,30 @@ create_string(THD *thd, String *buf, chistics->comment.length)) return FALSE; - buf->append(STRING_WITH_LEN("CREATE ")); + buf->append("CREATE ", 7); if (type == TYPE_ENUM_FUNCTION) - buf->append(STRING_WITH_LEN("FUNCTION ")); + buf->append("FUNCTION ", 9); else - buf->append(STRING_WITH_LEN("PROCEDURE ")); + buf->append("PROCEDURE ", 10); append_identifier(thd, buf, name->m_name.str, name->m_name.length); buf->append('('); buf->append(params, paramslen); buf->append(')'); if (type == TYPE_ENUM_FUNCTION) { - buf->append(STRING_WITH_LEN(" RETURNS ")); + buf->append(" RETURNS ", 9); buf->append(returns, returnslen); } buf->append('\n'); switch (chistics->daccess) { case SP_NO_SQL: - buf->append(STRING_WITH_LEN(" NO SQL\n")); + buf->append(" NO SQL\n"); break; case SP_READS_SQL_DATA: - buf->append(STRING_WITH_LEN(" READS SQL DATA\n")); + buf->append(" READS SQL DATA\n"); break; case SP_MODIFIES_SQL_DATA: - buf->append(STRING_WITH_LEN(" MODIFIES SQL DATA\n")); + buf->append(" MODIFIES SQL DATA\n"); break; case SP_DEFAULT_ACCESS: case SP_CONTAINS_SQL: @@ -1620,12 +1637,12 @@ create_string(THD *thd, String *buf, break; } if (chistics->detistic) - buf->append(STRING_WITH_LEN(" DETERMINISTIC\n")); + buf->append(" DETERMINISTIC\n", 18); if (chistics->suid == SP_IS_NOT_SUID) - buf->append(STRING_WITH_LEN(" SQL SECURITY INVOKER\n")); + buf->append(" SQL SECURITY INVOKER\n", 25); if (chistics->comment.length) { - buf->append(STRING_WITH_LEN(" COMMENT ")); + buf->append(" COMMENT "); append_unescaped(buf, chistics->comment.str, chistics->comment.length); buf->append('\n'); } @@ -36,7 +36,8 @@ int sp_drop_db_routines(THD *thd, char *db); 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 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); int sp_show_status_procedure(THD *thd, const char *wild); -sp_head * -sp_find_function(THD *thd, sp_name *name, bool cache_only = 0); - int sp_create_function(THD *thd, sp_head *sp); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 601d41a0afe..a6e88c08789 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -478,7 +478,8 @@ sp_head::operator delete(void *ptr, size_t size) sp_head::sp_head() :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 * sp_table_key(const byte *ptr, uint *plen, my_bool first); @@ -659,6 +660,7 @@ sp_head::create(THD *thd) sp_head::~sp_head() { destroy(); + delete m_next_cached_sp; if (m_thd) restore_thd_mem_root(m_thd); } @@ -885,6 +887,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 Assume the parameters already set. @@ -913,37 +940,31 @@ int sp_head::execute(THD *thd) Item_change_list old_change_list; 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 */ - 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); } - if (m_flags & IS_INVOKED) - { - /* - We have to disable recursion for stored routines since in - many cases LEX structure and many Item's can't be used in - reentrant way now. - - 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_RETURN(-1); - } + /* 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)); + /* + Check that if there are not any instances after this one then + pointer to the last instance points on this instance or if there are + some instances after this one then recursion level of next instance + greater then recursion level of current instance on 1 + */ + DBUG_ASSERT((m_next_cached_sp == 0 && + m_first_instance->m_last_cached_sp == this) || + (m_recursion_level + 1 == m_next_cached_sp->m_recursion_level)); dbchanged= FALSE; if (m_db.length && @@ -1118,6 +1139,29 @@ int sp_head::execute(THD *thd) ret= mysql_change_db(thd, olddb, 1); } 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); } diff --git a/sql/sp_head.h b/sql/sp_head.h index 9addec706e7..6334bca0fc6 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -143,6 +143,32 @@ public: LEX_STRING m_definer_host; longlong m_created; 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. Note that unlike elements of similar set for statement elements of this @@ -266,6 +292,8 @@ public: void optimize(); void opt_mark(uint ip); + void recursion_level_error(); + inline sp_instr * get_instr(uint i) { diff --git a/sql/sql_base.cc b/sql/sql_base.cc index cfd06008690..b765f2f53e2 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1088,6 +1088,11 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, /* find a unused table in the open table cache */ if (refresh) *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) DBUG_RETURN(0); key_length= (uint) (strmov(strmov(key, table_list->db)+1, diff --git a/sql/sql_class.h b/sql/sql_class.h index e92bf3b2bec..ee24d9e904b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -530,6 +530,7 @@ struct system_variables ulong completion_type; /* Determines which non-standard SQL behaviour should be enabled */ ulong sql_mode; + ulong max_sp_recursion_depth; /* check of key presence in updatable view */ ulong updatable_views_with_limit; ulong default_week_format; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 462d1d0fbcd..437e910d592 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3682,7 +3682,8 @@ end_with_restore_list: if (check_access(thd,INSERT_ACL,"mysql",0,1,0,0)) break; #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); goto error; @@ -4216,7 +4217,8 @@ end_with_restore_list: By this moment all needed SPs should be in cache so no need to look 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", lex->spname->m_qname.str); @@ -4340,9 +4342,11 @@ end_with_restore_list: memcpy(&chistics, &lex->sp_chistics, sizeof(chistics)); 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 - 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); if (! sp) { @@ -4418,9 +4422,11 @@ end_with_restore_list: char *db, *name; 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 - 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); if (sp) { |