mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
Merge sanja.is.com.ua:/home/bell/mysql/bk/work-bug7-5.0
into sanja.is.com.ua:/home/bell/mysql/bk/work-merge-5.0 mysql-test/r/sp-error.result: Auto merged mysql-test/r/trigger.result: Auto merged mysql-test/t/sp-error.test: Auto merged mysql-test/t/sp.test: Auto merged mysql-test/t/trigger.test: Auto merged sql/item_func.cc: Auto merged sql/mysqld.cc: Auto merged sql/set_var.cc: Auto merged sql/sp_head.cc: Auto merged sql/sp_head.h: Auto merged sql/sql_base.cc: Auto merged sql/sql_class.h: Auto merged sql/sql_parse.cc: Auto merged mysql-test/r/sp.result: merge sql/share/errmsg.txt: merge
This commit is contained in:
@ -3314,20 +3314,23 @@ static int handle_error(const char *query, struct st_query *q,
|
|||||||
((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 (q->expected_errors == 1)
|
if (!disable_result_log)
|
||||||
{
|
{
|
||||||
/* Only log error if there is one possible error */
|
if (q->expected_errors == 1)
|
||||||
dynstr_append_mem(ds, "ERROR ", 6);
|
{
|
||||||
replace_dynstr_append(ds, err_sqlstate);
|
/* Only log error if there is one possible error */
|
||||||
dynstr_append_mem(ds, ": ", 2);
|
dynstr_append_mem(ds, "ERROR ", 6);
|
||||||
replace_dynstr_append(ds, err_error);
|
replace_dynstr_append(ds, err_sqlstate);
|
||||||
dynstr_append_mem(ds,"\n",1);
|
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 */
|
/* OK */
|
||||||
DBUG_RETURN(0);
|
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));
|
DBUG_PRINT("info",("i: %d expected_errors: %d", i, q->expected_errors));
|
||||||
|
|
||||||
dynstr_append_mem(ds, "ERROR ",6);
|
if (!disable_result_log)
|
||||||
replace_dynstr_append(ds, err_sqlstate);
|
{
|
||||||
dynstr_append_mem(ds, ": ", 2);
|
dynstr_append_mem(ds, "ERROR ",6);
|
||||||
replace_dynstr_append(ds, err_error);
|
replace_dynstr_append(ds, err_sqlstate);
|
||||||
dynstr_append_mem(ds, "\n", 1);
|
dynstr_append_mem(ds, ": ", 2);
|
||||||
|
replace_dynstr_append(ds, err_error);
|
||||||
|
dynstr_append_mem(ds, "\n", 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (i)
|
if (i)
|
||||||
{
|
{
|
||||||
|
@ -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
|
|||||||
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()|
|
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()|
|
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
|
||||||
|
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 limit 0 (as set by the max_sp_recursion_depth variable) was exceeded for routine p1
|
||||||
|
call p1()|
|
||||||
|
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));
|
|||||||
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));
|
|||||||
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));
|
|||||||
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,6 +3617,191 @@ 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 function if exists bug14723|
|
drop function if exists bug14723|
|
||||||
drop procedure if exists bug14723|
|
drop procedure if exists bug14723|
|
||||||
/*!50003 create function bug14723()
|
/*!50003 create function bug14723()
|
||||||
|
@ -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;
|
|||||||
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
|
|||||||
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()|
|
call p1()|
|
||||||
--error ER_SP_NO_RECURSION
|
--error ER_PS_NO_RECURSION
|
||||||
call p1()|
|
call p1()|
|
||||||
--error ER_SP_NO_RECURSION
|
--error ER_PS_NO_RECURSION
|
||||||
call p1()|
|
call p1()|
|
||||||
|
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|
|
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
|
|||||||
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.
|
|
||||||
--error 1424
|
|
||||||
call bug11394(2, 1)|
|
call bug11394(2, 1)|
|
||||||
|
set @@max_sp_recursion_depth=10|
|
||||||
|
call bug11394(2, 1)|
|
||||||
|
set @@max_sp_recursion_depth=default|
|
||||||
drop procedure bug11394|
|
drop procedure bug11394|
|
||||||
delimiter ;|
|
delimiter ;|
|
||||||
|
|
||||||
|
@ -4622,6 +4622,160 @@ CALL bug13549_2()|
|
|||||||
drop procedure bug13549_2|
|
drop procedure bug13549_2|
|
||||||
drop procedure bug13549_1|
|
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
|
# BUG#NNNN: New bug synopsis
|
||||||
#
|
#
|
||||||
|
@ -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;
|
|||||||
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
|
|||||||
{
|
{
|
||||||
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);
|
||||||
@ -4919,7 +4925,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;
|
||||||
|
@ -4545,6 +4545,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,
|
||||||
@ -5760,6 +5761,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.",
|
||||||
|
@ -261,6 +261,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);
|
||||||
@ -631,6 +633,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,
|
||||||
@ -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_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
|
|||||||
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
|
||||||
@ -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."
|
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
|
ER_OLD_FILE_FORMAT
|
||||||
eng "'%-.64s' has an old format, you should re-create the '%s' object(s)"
|
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"
|
||||||
|
321
sql/sp.cc
321
sql/sp.cc
@ -29,6 +29,11 @@ create_string(THD *thd, String *buf,
|
|||||||
const char *returns, ulong returnslen,
|
const char *returns, ulong returnslen,
|
||||||
const char *body, ulong bodylen,
|
const char *body, ulong bodylen,
|
||||||
st_sp_chistics *chistics);
|
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,83 +382,10 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
|
|||||||
close_proc_table(thd, &open_tables_state_backup);
|
close_proc_table(thd, &open_tables_state_backup);
|
||||||
table= 0;
|
table= 0;
|
||||||
|
|
||||||
{
|
ret= db_load_routine(thd, type, name, sphp,
|
||||||
String defstr;
|
sql_mode, params, returns, body, chistics,
|
||||||
LEX *oldlex= thd->lex;
|
definer, created, modified);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
thd->spcont= 0;
|
|
||||||
if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL)
|
|
||||||
{
|
|
||||||
LEX *newlex= thd->lex;
|
|
||||||
sp_head *sp= newlex->sphead;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if (table)
|
if (table)
|
||||||
close_proc_table(thd, &open_tables_state_backup);
|
close_proc_table(thd, &open_tables_state_backup);
|
||||||
@ -461,6 +393,69 @@ db_done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
dbchanged= FALSE;
|
||||||
|
if ((ret= sp_use_new_db(thd, name->m_db.str, olddb, sizeof(olddb),
|
||||||
|
1, &dbchanged)))
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
lex_start(thd, (uchar*)defstr.c_ptr(), defstr.length());
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sp_returns_type(THD *thd, String &result, sp_head *sp)
|
sp_returns_type(THD *thd, String &result, sp_head *sp)
|
||||||
{
|
{
|
||||||
@ -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,
|
store(sp->m_chistics->comment.str, sp->m_chistics->comment.length,
|
||||||
system_charset_info);
|
system_charset_info);
|
||||||
|
|
||||||
if ((sp->m_type == TYPE_ENUM_FUNCTION) &&
|
if (!trust_routine_creators && mysql_bin_log.is_open())
|
||||||
!trust_function_creators && mysql_bin_log.is_open())
|
|
||||||
{
|
{
|
||||||
if (!sp->m_chistics->detistic)
|
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.
|
a non-deterministic read-only function in an update statement.
|
||||||
*/
|
*/
|
||||||
enum enum_sp_data_access access=
|
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.
|
stored procedures cache and looking into mysql.proc if needed.
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
sp_find_procedure()
|
sp_find_routine()
|
||||||
thd - thread context
|
thd - thread context
|
||||||
|
type - type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE)
|
||||||
name - name of procedure
|
name - name of procedure
|
||||||
|
cp - hash to look routine in
|
||||||
cache_only - if true perform cache-only lookup
|
cache_only - if true perform cache-only lookup
|
||||||
(Don't look in mysql.proc).
|
(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
|
RETURN VALUE
|
||||||
Non-0 pointer to sp_head object for the procedure, or
|
Non-0 pointer to sp_head object for the procedure, or
|
||||||
0 - in case of error.
|
0 - in case of error.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
sp_head *
|
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;
|
sp_head *sp;
|
||||||
DBUG_ENTER("sp_find_procedure");
|
ulong depth= (type == TYPE_ENUM_PROCEDURE ?
|
||||||
DBUG_PRINT("enter", ("name: %.*s.%.*s",
|
thd->variables.max_sp_recursion_depth :
|
||||||
name->m_db.length, name->m_db.str,
|
0);
|
||||||
name->m_name.length, name->m_name.str));
|
|
||||||
|
|
||||||
if (!(sp= sp_cache_lookup(&thd->sp_proc_cache, name)) && !cache_only)
|
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(cp, name)))
|
||||||
{
|
{
|
||||||
if (db_find_routine(thd, TYPE_ENUM_PROCEDURE, name, &sp) == SP_OK)
|
ulong level;
|
||||||
sp_cache_insert(&thd->sp_proc_cache, sp);
|
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);
|
DBUG_RETURN(sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
sp_exists_routine(THD *thd, TABLE_LIST *tables, bool any, bool no_error)
|
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);
|
lex_name.str= thd->strmake(table->table_name, lex_name.length);
|
||||||
name= new sp_name(lex_db, lex_name);
|
name= new sp_name(lex_db, lex_name);
|
||||||
name->init_qname(thd);
|
name->init_qname(thd);
|
||||||
if (sp_find_procedure(thd, name) != NULL ||
|
if (sp_find_routine(thd, TYPE_ENUM_PROCEDURE, name,
|
||||||
sp_find_function(thd, name) != NULL)
|
&thd->sp_proc_cache, FALSE) != NULL ||
|
||||||
|
sp_find_routine(thd, TYPE_ENUM_FUNCTION, name,
|
||||||
|
&thd->sp_func_cache, FALSE) != NULL)
|
||||||
{
|
{
|
||||||
if (any)
|
if (any)
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
@ -1028,7 +1085,8 @@ sp_show_create_procedure(THD *thd, sp_name *name)
|
|||||||
DBUG_ENTER("sp_show_create_procedure");
|
DBUG_ENTER("sp_show_create_procedure");
|
||||||
DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str));
|
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);
|
int ret= sp->show_create_procedure(thd);
|
||||||
|
|
||||||
@ -1054,42 +1112,6 @@ sp_show_status_procedure(THD *thd, const char *wild)
|
|||||||
FUNCTION
|
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
|
int
|
||||||
sp_create_function(THD *thd, sp_head *sp)
|
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_ENTER("sp_show_create_function");
|
||||||
DBUG_PRINT("enter", ("name: %.*s", name->m_name.length, name->m_name.str));
|
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);
|
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),
|
&thd->sp_func_cache : &thd->sp_proc_cache),
|
||||||
&name)))
|
&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_name.str= strchr(name.m_qname.str, '.');
|
||||||
name.m_db.length= name.m_name.str - 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,
|
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
|
else
|
||||||
sp_cache_insert(&thd->sp_proc_cache, sp);
|
sp_cache_insert(&thd->sp_proc_cache, sp);
|
||||||
}
|
}
|
||||||
delete newlex;
|
|
||||||
thd->lex= oldlex;
|
|
||||||
}
|
}
|
||||||
if (sp)
|
if (sp)
|
||||||
{
|
{
|
||||||
@ -1589,30 +1606,30 @@ create_string(THD *thd, String *buf,
|
|||||||
chistics->comment.length))
|
chistics->comment.length))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
buf->append(STRING_WITH_LEN("CREATE "));
|
buf->append("CREATE ", 7);
|
||||||
if (type == TYPE_ENUM_FUNCTION)
|
if (type == TYPE_ENUM_FUNCTION)
|
||||||
buf->append(STRING_WITH_LEN("FUNCTION "));
|
buf->append("FUNCTION ", 9);
|
||||||
else
|
else
|
||||||
buf->append(STRING_WITH_LEN("PROCEDURE "));
|
buf->append("PROCEDURE ", 10);
|
||||||
append_identifier(thd, buf, name->m_name.str, name->m_name.length);
|
append_identifier(thd, buf, name->m_name.str, name->m_name.length);
|
||||||
buf->append('(');
|
buf->append('(');
|
||||||
buf->append(params, paramslen);
|
buf->append(params, paramslen);
|
||||||
buf->append(')');
|
buf->append(')');
|
||||||
if (type == TYPE_ENUM_FUNCTION)
|
if (type == TYPE_ENUM_FUNCTION)
|
||||||
{
|
{
|
||||||
buf->append(STRING_WITH_LEN(" RETURNS "));
|
buf->append(" RETURNS ", 9);
|
||||||
buf->append(returns, returnslen);
|
buf->append(returns, returnslen);
|
||||||
}
|
}
|
||||||
buf->append('\n');
|
buf->append('\n');
|
||||||
switch (chistics->daccess) {
|
switch (chistics->daccess) {
|
||||||
case SP_NO_SQL:
|
case SP_NO_SQL:
|
||||||
buf->append(STRING_WITH_LEN(" NO SQL\n"));
|
buf->append(" NO SQL\n");
|
||||||
break;
|
break;
|
||||||
case SP_READS_SQL_DATA:
|
case SP_READS_SQL_DATA:
|
||||||
buf->append(STRING_WITH_LEN(" READS SQL DATA\n"));
|
buf->append(" READS SQL DATA\n");
|
||||||
break;
|
break;
|
||||||
case SP_MODIFIES_SQL_DATA:
|
case SP_MODIFIES_SQL_DATA:
|
||||||
buf->append(STRING_WITH_LEN(" MODIFIES SQL DATA\n"));
|
buf->append(" MODIFIES SQL DATA\n");
|
||||||
break;
|
break;
|
||||||
case SP_DEFAULT_ACCESS:
|
case SP_DEFAULT_ACCESS:
|
||||||
case SP_CONTAINS_SQL:
|
case SP_CONTAINS_SQL:
|
||||||
@ -1620,12 +1637,12 @@ create_string(THD *thd, String *buf,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (chistics->detistic)
|
if (chistics->detistic)
|
||||||
buf->append(STRING_WITH_LEN(" DETERMINISTIC\n"));
|
buf->append(" DETERMINISTIC\n", 18);
|
||||||
if (chistics->suid == SP_IS_NOT_SUID)
|
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)
|
if (chistics->comment.length)
|
||||||
{
|
{
|
||||||
buf->append(STRING_WITH_LEN(" COMMENT "));
|
buf->append(" COMMENT ");
|
||||||
append_unescaped(buf, chistics->comment.str, chistics->comment.length);
|
append_unescaped(buf, chistics->comment.str, chistics->comment.length);
|
||||||
buf->append('\n');
|
buf->append('\n');
|
||||||
}
|
}
|
||||||
|
6
sql/sp.h
6
sql/sp.h
@ -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);
|
|||||||
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);
|
||||||
|
|
||||||
|
100
sql/sp_head.cc
100
sql/sp_head.cc
@ -478,7 +478,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);
|
||||||
@ -659,6 +660,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);
|
||||||
}
|
}
|
||||||
@ -884,6 +886,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.
|
||||||
@ -913,37 +940,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;
|
||||||
|
|
||||||
|
/* Use some extra margin for possible SP recursion and functions */
|
||||||
|
if (check_stack_overrun(thd, 8 * STACK_MIN_SIZE, (char*)&old_packet))
|
||||||
|
{
|
||||||
|
DBUG_RETURN(-1);
|
||||||
|
}
|
||||||
|
|
||||||
/* init per-instruction memroot */
|
/* init per-instruction memroot */
|
||||||
init_alloc_root(&execute_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
|
init_alloc_root(&execute_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
|
||||||
|
|
||||||
|
DBUG_ASSERT(!(m_flags & IS_INVOKED));
|
||||||
/* Use some extra margin for possible SP recursion and functions */
|
|
||||||
if (check_stack_overrun(thd, 4*STACK_MIN_SIZE, olddb))
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
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;
|
dbchanged= FALSE;
|
||||||
if (m_db.length &&
|
if (m_db.length &&
|
||||||
@ -1118,6 +1139,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,6 +143,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
|
||||||
@ -266,6 +292,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)
|
||||||
{
|
{
|
||||||
|
@ -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 */
|
/* 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,
|
||||||
|
@ -530,6 +530,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;
|
||||||
|
@ -3682,7 +3682,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;
|
||||||
@ -4216,7 +4217,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);
|
||||||
@ -4340,9 +4342,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)
|
||||||
{
|
{
|
||||||
@ -4418,9 +4422,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)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user