mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 10:14:19 +01:00
Merge bk-internal.mysql.com:/home/bk/mysql-5.0
into mysql.com:/home/kostja/mysql/mysql-5.0-dsql3 sql/mysql_priv.h: Auto merged sql/sql_parse.cc: Auto merged sql/share/errmsg.txt: manual merge
This commit is contained in:
commit
17994730c1
19 changed files with 1679 additions and 700 deletions
|
@ -1,5 +1,5 @@
|
|||
prepare stmt1 from ' show full processlist ';
|
||||
execute stmt1;
|
||||
Id User Host db Command Time State Info
|
||||
number root localhost test Execute time NULL show full processlist
|
||||
number root localhost test Query time NULL show full processlist
|
||||
deallocate prepare stmt1;
|
||||
|
|
364
mysql-test/r/sp-dynamic.result
Normal file
364
mysql-test/r/sp-dynamic.result
Normal file
|
@ -0,0 +1,364 @@
|
|||
create procedure p1()
|
||||
begin
|
||||
prepare stmt from "select 1";
|
||||
execute stmt;
|
||||
execute stmt;
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
end|
|
||||
call p1()|
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
call p1()|
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
call p1()|
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
drop procedure p1|
|
||||
create procedure p1()
|
||||
begin
|
||||
execute stmt;
|
||||
end|
|
||||
prepare stmt from "call p1()"|
|
||||
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|
|
||||
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|
|
||||
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.
|
||||
call p1()|
|
||||
ERROR HY000: Recursive stored routines are not allowed.
|
||||
call p1()|
|
||||
ERROR HY000: Recursive stored routines are not allowed.
|
||||
drop procedure p1|
|
||||
create procedure p1()
|
||||
begin
|
||||
prepare stmt from "create procedure p2() begin select 1; end";
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
end|
|
||||
call p1()|
|
||||
ERROR HY000: This command is not supported in the prepared statement protocol yet
|
||||
call p1()|
|
||||
ERROR HY000: This command is not supported in the prepared statement protocol yet
|
||||
drop procedure p1|
|
||||
create procedure p1()
|
||||
begin
|
||||
prepare stmt from "drop procedure p2";
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
end|
|
||||
call p1()|
|
||||
ERROR HY000: This command is not supported in the prepared statement protocol yet
|
||||
call p1()|
|
||||
ERROR HY000: This command is not supported in the prepared statement protocol yet
|
||||
drop procedure p1|
|
||||
create procedure p1()
|
||||
begin
|
||||
prepare stmt_drop from "drop table if exists t1";
|
||||
execute stmt_drop;
|
||||
prepare stmt from "create table t1 (a int)";
|
||||
execute stmt;
|
||||
insert into t1 (a) values (1);
|
||||
select * from t1;
|
||||
deallocate prepare stmt;
|
||||
deallocate prepare stmt_drop;
|
||||
end|
|
||||
call p1()|
|
||||
a
|
||||
1
|
||||
Warnings:
|
||||
Note 1051 Unknown table 't1'
|
||||
call p1()|
|
||||
a
|
||||
1
|
||||
drop procedure p1|
|
||||
create procedure p1()
|
||||
begin
|
||||
set @tab_name=concat("tab_", replace(curdate(), '-', '_'));
|
||||
set @drop_sql=concat("drop table if exists ", @tab_name);
|
||||
set @create_sql=concat("create table ", @tab_name, " (a int)");
|
||||
set @insert_sql=concat("insert into ", @tab_name, " values (1), (2), (3)");
|
||||
set @select_sql=concat("select * from ", @tab_name);
|
||||
select @tab_name;
|
||||
select @drop_sql;
|
||||
select @create_sql;
|
||||
select @insert_sql;
|
||||
select @select_sql;
|
||||
prepare stmt_drop from @drop_sql;
|
||||
execute stmt_drop;
|
||||
prepare stmt from @create_sql;
|
||||
execute stmt;
|
||||
prepare stmt from @insert_sql;
|
||||
execute stmt;
|
||||
prepare stmt from @select_sql;
|
||||
execute stmt;
|
||||
execute stmt_drop;
|
||||
deallocate prepare stmt;
|
||||
deallocate prepare stmt_drop;
|
||||
end|
|
||||
call p1()|
|
||||
call p1()|
|
||||
drop procedure p1|
|
||||
create procedure p1()
|
||||
begin
|
||||
prepare stmt_drop from "drop table if exists t1";
|
||||
execute stmt_drop;
|
||||
prepare stmt from "create table t1 (a int)";
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
deallocate prepare stmt_drop;
|
||||
end|
|
||||
drop function if exists f1|
|
||||
create function f1(a int) returns int
|
||||
begin
|
||||
call p1();
|
||||
return 1;
|
||||
end|
|
||||
select f1(0)|
|
||||
ERROR 0A000: Dynamic SQL is not allowed in stored function or trigger
|
||||
select f1(f1(0))|
|
||||
ERROR 0A000: Dynamic SQL is not allowed in stored function or trigger
|
||||
select f1(f1(f1(0)))|
|
||||
ERROR 0A000: Dynamic SQL is not allowed in stored function or trigger
|
||||
drop function f1|
|
||||
drop procedure p1|
|
||||
create procedure p1()
|
||||
begin
|
||||
drop table if exists t1;
|
||||
create table t1 (id integer not null primary key,
|
||||
name varchar(20) not null);
|
||||
insert into t1 (id, name) values (1, 'aaa'), (2, 'bbb'), (3, 'ccc');
|
||||
prepare stmt from "select name from t1";
|
||||
execute stmt;
|
||||
select name from t1;
|
||||
execute stmt;
|
||||
prepare stmt from
|
||||
"select name from t1 where name=(select name from t1 where id=2)";
|
||||
execute stmt;
|
||||
select name from t1 where name=(select name from t1 where id=2);
|
||||
execute stmt;
|
||||
end|
|
||||
call p1()|
|
||||
name
|
||||
aaa
|
||||
bbb
|
||||
ccc
|
||||
name
|
||||
aaa
|
||||
bbb
|
||||
ccc
|
||||
name
|
||||
aaa
|
||||
bbb
|
||||
ccc
|
||||
name
|
||||
bbb
|
||||
name
|
||||
bbb
|
||||
name
|
||||
bbb
|
||||
call p1()|
|
||||
name
|
||||
aaa
|
||||
bbb
|
||||
ccc
|
||||
name
|
||||
aaa
|
||||
bbb
|
||||
ccc
|
||||
name
|
||||
aaa
|
||||
bbb
|
||||
ccc
|
||||
name
|
||||
bbb
|
||||
name
|
||||
bbb
|
||||
name
|
||||
bbb
|
||||
drop procedure p1|
|
||||
prepare stmt from "select * from t1"|
|
||||
create procedure p1()
|
||||
begin
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
end|
|
||||
call p1()|
|
||||
id name
|
||||
1 aaa
|
||||
2 bbb
|
||||
3 ccc
|
||||
call p1()|
|
||||
ERROR HY000: Unknown prepared statement handler (stmt) given to EXECUTE
|
||||
drop procedure p1|
|
||||
create procedure p1()
|
||||
begin
|
||||
declare a char(10);
|
||||
set a="sp-variable";
|
||||
set @a="mysql-variable";
|
||||
prepare stmt from "select 'dynamic sql:', @a, a";
|
||||
execute stmt;
|
||||
end|
|
||||
call p1()|
|
||||
ERROR 42S22: Unknown column 'a' in 'field list'
|
||||
call p1()|
|
||||
ERROR 42S22: Unknown column 'a' in 'field list'
|
||||
drop procedure p1|
|
||||
create procedure p1()
|
||||
begin
|
||||
prepare stmt from 'select ? as a';
|
||||
execute stmt using @a;
|
||||
end|
|
||||
set @a=1|
|
||||
call p1()|
|
||||
a
|
||||
1
|
||||
call p1()|
|
||||
a
|
||||
1
|
||||
drop procedure p1|
|
||||
drop table if exists t1|
|
||||
create table t1 (id integer primary key auto_increment,
|
||||
stmt_text char(35), status varchar(20))|
|
||||
insert into t1 (stmt_text) values
|
||||
("select 1"), ("flush tables"), ("handler t1 open as ha"),
|
||||
("analyze table t1"), ("check table t1"), ("checksum table t1"),
|
||||
("check table t1"), ("optimize table t1"), ("repair table t1"),
|
||||
("describe extended select * from t1"),
|
||||
("help help"), ("show databases"), ("show tables"),
|
||||
("show table status"), ("show open tables"), ("show storage engines"),
|
||||
("insert into t1 (id) values (1)"), ("update t1 set status=''"),
|
||||
("delete from t1"), ("truncate t1"), ("call p1()"), ("foo bar")|
|
||||
create procedure p1()
|
||||
begin
|
||||
declare v_stmt_text varchar(255);
|
||||
declare v_id integer;
|
||||
declare done int default 0;
|
||||
declare c cursor for select id, stmt_text from t1;
|
||||
declare continue handler for 1295 -- ER_UNSUPPORTED_PS
|
||||
set @status='not supported';
|
||||
declare continue handler for 1064 -- ER_SYNTAX_ERROR
|
||||
set @status='syntax error';
|
||||
declare continue handler for sqlstate '02000' set done = 1;
|
||||
prepare update_stmt from "update t1 set status=? where id=?";
|
||||
open c;
|
||||
repeat
|
||||
if not done then
|
||||
fetch c into v_id, v_stmt_text;
|
||||
set @id=v_id, @stmt_text=v_stmt_text;
|
||||
set @status="supported";
|
||||
prepare stmt from @stmt_text;
|
||||
execute update_stmt using @status, @id;
|
||||
end if;
|
||||
until done end repeat;
|
||||
deallocate prepare update_stmt;
|
||||
end|
|
||||
call p1()|
|
||||
select * from t1|
|
||||
id stmt_text status
|
||||
1 select 1 supported
|
||||
2 flush tables not supported
|
||||
3 handler t1 open as ha not supported
|
||||
4 analyze table t1 not supported
|
||||
5 check table t1 not supported
|
||||
6 checksum table t1 not supported
|
||||
7 check table t1 not supported
|
||||
8 optimize table t1 not supported
|
||||
9 repair table t1 not supported
|
||||
10 describe extended select * from t1 supported
|
||||
11 help help not supported
|
||||
12 show databases supported
|
||||
13 show tables supported
|
||||
14 show table status supported
|
||||
15 show open tables supported
|
||||
16 show storage engines supported
|
||||
17 insert into t1 (id) values (1) supported
|
||||
18 update t1 set status='' supported
|
||||
19 delete from t1 supported
|
||||
20 truncate t1 supported
|
||||
21 call p1() supported
|
||||
22 foo bar syntax error
|
||||
drop procedure p1|
|
||||
drop table t1|
|
||||
prepare stmt from 'select 1'|
|
||||
create procedure p1() execute stmt|
|
||||
call p1()|
|
||||
1
|
||||
1
|
||||
call p1()|
|
||||
1
|
||||
1
|
||||
drop procedure p1|
|
||||
create function f1() returns int
|
||||
begin
|
||||
deallocate prepare stmt;
|
||||
return 1;
|
||||
end|
|
||||
ERROR 0A000: Dynamic SQL is not allowed in stored function or trigger
|
||||
create procedure p1()
|
||||
begin
|
||||
prepare stmt from 'select 1 A';
|
||||
execute stmt;
|
||||
end|
|
||||
prepare stmt from 'call p1()'|
|
||||
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|
|
||||
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
|
||||
drop procedure p1|
|
||||
drop table if exists t1, t2|
|
||||
create procedure p1 (a int) language sql deterministic
|
||||
begin
|
||||
declare rsql varchar(100);
|
||||
drop table if exists t1, t2;
|
||||
set @rsql= "create table t1 (a int)";
|
||||
select @rsql;
|
||||
prepare pst from @rsql;
|
||||
execute pst;
|
||||
set @rsql= null;
|
||||
set @rsql= "create table t2 (a int)";
|
||||
select @rsql;
|
||||
prepare pst from @rsql;
|
||||
execute pst;
|
||||
drop table if exists t1, t2;
|
||||
end|
|
||||
set @a:=0|
|
||||
call p1(@a)|
|
||||
@rsql
|
||||
create table t1 (a int)
|
||||
@rsql
|
||||
create table t2 (a int)
|
||||
Warnings:
|
||||
Note 1051 Unknown table 't1'
|
||||
Note 1051 Unknown table 't2'
|
||||
select @a|
|
||||
@a
|
||||
0
|
||||
call p1(@a)|
|
||||
@rsql
|
||||
create table t1 (a int)
|
||||
@rsql
|
||||
create table t2 (a int)
|
||||
Warnings:
|
||||
Note 1051 Unknown table 't1'
|
||||
Note 1051 Unknown table 't2'
|
||||
select @a|
|
||||
@a
|
||||
0
|
||||
drop procedure if exists p1|
|
|
@ -618,7 +618,7 @@ select * from t1|
|
|||
call bug8408_p()|
|
||||
val x
|
||||
select bug8408_f()|
|
||||
ERROR 0A000: PROCEDURE test.bug8408_p can't return a result set in the given context
|
||||
ERROR 0A000: Not allowed to return a result set from a function
|
||||
drop procedure bug8408_p|
|
||||
drop function bug8408_f|
|
||||
create function bug8408() returns int
|
||||
|
@ -665,20 +665,6 @@ select default(t30.s1) from t30;
|
|||
end|
|
||||
drop procedure bug10969|
|
||||
drop table t1|
|
||||
prepare stmt from "select 1";
|
||||
create procedure p() deallocate prepare stmt;
|
||||
ERROR 0A000: DEALLOCATE is not allowed in stored procedures
|
||||
create function f() returns int begin deallocate prepare stmt;
|
||||
ERROR 0A000: DEALLOCATE is not allowed in stored procedures
|
||||
create procedure p() prepare stmt from "select 1";
|
||||
ERROR 0A000: PREPARE is not allowed in stored procedures
|
||||
create function f() returns int begin prepare stmt from "select 1";
|
||||
ERROR 0A000: PREPARE is not allowed in stored procedures
|
||||
create procedure p() execute stmt;
|
||||
ERROR 0A000: EXECUTE is not allowed in stored procedures
|
||||
create function f() returns int begin execute stmt;
|
||||
ERROR 0A000: EXECUTE is not allowed in stored procedures
|
||||
deallocate prepare stmt;
|
||||
create table t1(f1 int);
|
||||
create table t2(f1 int);
|
||||
CREATE PROCEDURE SP001()
|
||||
|
|
|
@ -689,7 +689,7 @@ call bug11587();
|
|||
set new.c2= '2004-04-02';
|
||||
end|
|
||||
insert into t1 (c1) values (4),(5),(6);
|
||||
ERROR 0A000: PROCEDURE test.bug11587 can't return a result set in the given context
|
||||
ERROR 0A000: Not allowed to return a result set from a trigger
|
||||
select * from t1;
|
||||
c1 c2
|
||||
1 NULL
|
||||
|
|
335
mysql-test/t/sp-dynamic.test
Normal file
335
mysql-test/t/sp-dynamic.test
Normal file
|
@ -0,0 +1,335 @@
|
|||
delimiter |;
|
||||
######################################################################
|
||||
# Test Dynamic SQL in stored procedures. #############################
|
||||
######################################################################
|
||||
#
|
||||
# A. Basics
|
||||
#
|
||||
create procedure p1()
|
||||
begin
|
||||
prepare stmt from "select 1";
|
||||
execute stmt;
|
||||
execute stmt;
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
end|
|
||||
call p1()|
|
||||
call p1()|
|
||||
call p1()|
|
||||
drop procedure p1|
|
||||
#
|
||||
# B. Recursion. Recusion is disabled in SP, and recursive use of PS is not
|
||||
# possible as well.
|
||||
#
|
||||
create procedure p1()
|
||||
begin
|
||||
execute stmt;
|
||||
end|
|
||||
prepare stmt from "call p1()"|
|
||||
--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
|
||||
call p1()|
|
||||
--error ER_SP_NO_RECURSION
|
||||
call p1()|
|
||||
--error ER_SP_NO_RECURSION
|
||||
call p1()|
|
||||
drop procedure p1|
|
||||
#
|
||||
# C. Create/drop a stored procedure in Dynamic SQL.
|
||||
# One cannot create stored procedure from a stored procedure because of
|
||||
# the way MySQL SP cache works: it's important that this limitation is not
|
||||
# possible to circumvent by means of Dynamic SQL.
|
||||
#
|
||||
create procedure p1()
|
||||
begin
|
||||
prepare stmt from "create procedure p2() begin select 1; end";
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
end|
|
||||
--error ER_UNSUPPORTED_PS
|
||||
call p1()|
|
||||
--error ER_UNSUPPORTED_PS
|
||||
call p1()|
|
||||
drop procedure p1|
|
||||
create procedure p1()
|
||||
begin
|
||||
prepare stmt from "drop procedure p2";
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
end|
|
||||
--error ER_UNSUPPORTED_PS
|
||||
call p1()|
|
||||
--error ER_UNSUPPORTED_PS
|
||||
call p1()|
|
||||
drop procedure p1|
|
||||
#
|
||||
# D. Create/Drop a table (a DDL that issues a commit) in Dynamic SQL.
|
||||
# (should work ok).
|
||||
#
|
||||
create procedure p1()
|
||||
begin
|
||||
prepare stmt_drop from "drop table if exists t1";
|
||||
execute stmt_drop;
|
||||
prepare stmt from "create table t1 (a int)";
|
||||
execute stmt;
|
||||
insert into t1 (a) values (1);
|
||||
select * from t1;
|
||||
deallocate prepare stmt;
|
||||
deallocate prepare stmt_drop;
|
||||
end|
|
||||
call p1()|
|
||||
call p1()|
|
||||
drop procedure p1|
|
||||
#
|
||||
# A more real example (a case similar to submitted by 24/7).
|
||||
#
|
||||
create procedure p1()
|
||||
begin
|
||||
set @tab_name=concat("tab_", replace(curdate(), '-', '_'));
|
||||
set @drop_sql=concat("drop table if exists ", @tab_name);
|
||||
set @create_sql=concat("create table ", @tab_name, " (a int)");
|
||||
set @insert_sql=concat("insert into ", @tab_name, " values (1), (2), (3)");
|
||||
set @select_sql=concat("select * from ", @tab_name);
|
||||
select @tab_name;
|
||||
select @drop_sql;
|
||||
select @create_sql;
|
||||
select @insert_sql;
|
||||
select @select_sql;
|
||||
prepare stmt_drop from @drop_sql;
|
||||
execute stmt_drop;
|
||||
prepare stmt from @create_sql;
|
||||
execute stmt;
|
||||
prepare stmt from @insert_sql;
|
||||
execute stmt;
|
||||
prepare stmt from @select_sql;
|
||||
execute stmt;
|
||||
execute stmt_drop;
|
||||
deallocate prepare stmt;
|
||||
deallocate prepare stmt_drop;
|
||||
end|
|
||||
--disable_result_log
|
||||
call p1()|
|
||||
call p1()|
|
||||
--enable_result_log
|
||||
drop procedure p1|
|
||||
#
|
||||
# E. Calling a stored procedure with Dynamic SQL
|
||||
# from a stored function (currently disabled).
|
||||
#
|
||||
create procedure p1()
|
||||
begin
|
||||
prepare stmt_drop from "drop table if exists t1";
|
||||
execute stmt_drop;
|
||||
prepare stmt from "create table t1 (a int)";
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
deallocate prepare stmt_drop;
|
||||
end|
|
||||
--disable_warnings
|
||||
drop function if exists f1|
|
||||
--enable_warnings
|
||||
create function f1(a int) returns int
|
||||
begin
|
||||
call p1();
|
||||
return 1;
|
||||
end|
|
||||
|
||||
# Every stored procedure that contains Dynamic SQL is marked as
|
||||
# such. Stored procedures that contain Dynamic SQL are not
|
||||
# allowed in a stored function or trigger, and here we get the
|
||||
# corresponding error message.
|
||||
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
select f1(0)|
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
select f1(f1(0))|
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
select f1(f1(f1(0)))|
|
||||
drop function f1|
|
||||
drop procedure p1|
|
||||
#
|
||||
# F. Rollback and cleanup lists management in Dynamic SQL.
|
||||
#
|
||||
create procedure p1()
|
||||
begin
|
||||
drop table if exists t1;
|
||||
create table t1 (id integer not null primary key,
|
||||
name varchar(20) not null);
|
||||
insert into t1 (id, name) values (1, 'aaa'), (2, 'bbb'), (3, 'ccc');
|
||||
prepare stmt from "select name from t1";
|
||||
execute stmt;
|
||||
select name from t1;
|
||||
execute stmt;
|
||||
prepare stmt from
|
||||
"select name from t1 where name=(select name from t1 where id=2)";
|
||||
execute stmt;
|
||||
select name from t1 where name=(select name from t1 where id=2);
|
||||
execute stmt;
|
||||
end|
|
||||
call p1()|
|
||||
call p1()|
|
||||
drop procedure p1|
|
||||
#
|
||||
# H. Executing a statement prepared externally in SP.
|
||||
#
|
||||
prepare stmt from "select * from t1"|
|
||||
create procedure p1()
|
||||
begin
|
||||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
end|
|
||||
call p1()|
|
||||
--error ER_UNKNOWN_STMT_HANDLER
|
||||
call p1()|
|
||||
drop procedure p1|
|
||||
#
|
||||
# I. Use of an SP variable in Dynamic SQL is not possible and
|
||||
# this limitation is necessary for correct binary logging: prepared
|
||||
# statements do not substitute SP variables with their values for binlog, so
|
||||
# SP variables must be not accessible in Dynamic SQL.
|
||||
#
|
||||
create procedure p1()
|
||||
begin
|
||||
declare a char(10);
|
||||
set a="sp-variable";
|
||||
set @a="mysql-variable";
|
||||
prepare stmt from "select 'dynamic sql:', @a, a";
|
||||
execute stmt;
|
||||
end|
|
||||
--error ER_BAD_FIELD_ERROR
|
||||
call p1()|
|
||||
--error ER_BAD_FIELD_ERROR
|
||||
call p1()|
|
||||
drop procedure p1|
|
||||
#
|
||||
# J. Use of placeholders in Dynamic SQL.
|
||||
#
|
||||
create procedure p1()
|
||||
begin
|
||||
prepare stmt from 'select ? as a';
|
||||
execute stmt using @a;
|
||||
end|
|
||||
set @a=1|
|
||||
call p1()|
|
||||
call p1()|
|
||||
drop procedure p1|
|
||||
#
|
||||
# K. Use of continue handlers with Dynamic SQL.
|
||||
#
|
||||
drop table if exists t1|
|
||||
create table t1 (id integer primary key auto_increment,
|
||||
stmt_text char(35), status varchar(20))|
|
||||
insert into t1 (stmt_text) values
|
||||
("select 1"), ("flush tables"), ("handler t1 open as ha"),
|
||||
("analyze table t1"), ("check table t1"), ("checksum table t1"),
|
||||
("check table t1"), ("optimize table t1"), ("repair table t1"),
|
||||
("describe extended select * from t1"),
|
||||
("help help"), ("show databases"), ("show tables"),
|
||||
("show table status"), ("show open tables"), ("show storage engines"),
|
||||
("insert into t1 (id) values (1)"), ("update t1 set status=''"),
|
||||
("delete from t1"), ("truncate t1"), ("call p1()"), ("foo bar")|
|
||||
create procedure p1()
|
||||
begin
|
||||
declare v_stmt_text varchar(255);
|
||||
declare v_id integer;
|
||||
declare done int default 0;
|
||||
declare c cursor for select id, stmt_text from t1;
|
||||
declare continue handler for 1295 -- ER_UNSUPPORTED_PS
|
||||
set @status='not supported';
|
||||
declare continue handler for 1064 -- ER_SYNTAX_ERROR
|
||||
set @status='syntax error';
|
||||
declare continue handler for sqlstate '02000' set done = 1;
|
||||
|
||||
prepare update_stmt from "update t1 set status=? where id=?";
|
||||
open c;
|
||||
repeat
|
||||
if not done then
|
||||
fetch c into v_id, v_stmt_text;
|
||||
set @id=v_id, @stmt_text=v_stmt_text;
|
||||
set @status="supported";
|
||||
prepare stmt from @stmt_text;
|
||||
execute update_stmt using @status, @id;
|
||||
end if;
|
||||
until done end repeat;
|
||||
deallocate prepare update_stmt;
|
||||
end|
|
||||
call p1()|
|
||||
select * from t1|
|
||||
drop procedure p1|
|
||||
drop table t1|
|
||||
#
|
||||
# Bug#7115 "Prepared Statements: packet error if execution within stored
|
||||
# procedure".
|
||||
#
|
||||
prepare stmt from 'select 1'|
|
||||
create procedure p1() execute stmt|
|
||||
call p1()|
|
||||
call p1()|
|
||||
drop procedure p1|
|
||||
#
|
||||
# Bug#10975 "Prepared statements: crash if function deallocates"
|
||||
# Check that a prepared statement that is currently in use
|
||||
# can't be deallocated.
|
||||
#
|
||||
# a) Prepared statements and stored procedure cache:
|
||||
#
|
||||
# TODO: add when the corresponding bug (Bug #12093 "SP not found on second
|
||||
# PS execution if another thread drops other SP in between") is fixed.
|
||||
#
|
||||
# b) attempt to deallocate a prepared statement that is being executed
|
||||
--error ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG
|
||||
create function f1() returns int
|
||||
begin
|
||||
deallocate prepare stmt;
|
||||
return 1;
|
||||
end|
|
||||
|
||||
# b)-2 a crash (#1) spotted by Sergey Petrunia during code review
|
||||
create procedure p1()
|
||||
begin
|
||||
prepare stmt from 'select 1 A';
|
||||
execute stmt;
|
||||
end|
|
||||
prepare stmt from 'call p1()'|
|
||||
--error ER_PS_NO_RECURSION
|
||||
execute stmt|
|
||||
--error ER_PS_NO_RECURSION
|
||||
execute stmt|
|
||||
drop procedure p1|
|
||||
|
||||
#
|
||||
# Bug#10605 "Stored procedure with multiple SQL prepared statements
|
||||
# disconnects client"
|
||||
#
|
||||
--disable_warnings
|
||||
drop table if exists t1, t2|
|
||||
--enable_warnings
|
||||
create procedure p1 (a int) language sql deterministic
|
||||
begin
|
||||
declare rsql varchar(100);
|
||||
drop table if exists t1, t2;
|
||||
set @rsql= "create table t1 (a int)";
|
||||
select @rsql;
|
||||
prepare pst from @rsql;
|
||||
execute pst;
|
||||
set @rsql= null;
|
||||
set @rsql= "create table t2 (a int)";
|
||||
select @rsql;
|
||||
prepare pst from @rsql;
|
||||
execute pst;
|
||||
drop table if exists t1, t2;
|
||||
end|
|
||||
set @a:=0|
|
||||
call p1(@a)|
|
||||
select @a|
|
||||
call p1(@a)|
|
||||
select @a|
|
||||
drop procedure if exists p1|
|
||||
|
||||
# End of the test
|
||||
delimiter ;|
|
|
@ -875,7 +875,7 @@ create procedure bug8408_p()
|
|||
select * from t1|
|
||||
|
||||
call bug8408_p()|
|
||||
--error ER_SP_BADSELECT
|
||||
--error ER_SP_NO_RETSET
|
||||
select bug8408_f()|
|
||||
|
||||
drop procedure bug8408_p|
|
||||
|
@ -956,39 +956,10 @@ end|
|
|||
drop procedure bug10969|
|
||||
|
||||
|
||||
#
|
||||
# BUG#NNNN: New bug synopsis
|
||||
#
|
||||
#--disable_warnings
|
||||
#drop procedure if exists bugNNNN|
|
||||
#--enable_warnings
|
||||
#create procedure bugNNNN...
|
||||
|
||||
|
||||
drop table t1|
|
||||
|
||||
delimiter ;|
|
||||
|
||||
#
|
||||
# Bug#10975, #10605, #7115: Dynamic SQL by means of
|
||||
# PREPARE/EXECUTE/DEALLOCATE is not supported yet.
|
||||
# Check that an error message is returned.
|
||||
#
|
||||
prepare stmt from "select 1";
|
||||
--error ER_SP_BADSTATEMENT
|
||||
create procedure p() deallocate prepare stmt;
|
||||
--error ER_SP_BADSTATEMENT
|
||||
create function f() returns int begin deallocate prepare stmt;
|
||||
--error ER_SP_BADSTATEMENT
|
||||
create procedure p() prepare stmt from "select 1";
|
||||
--error ER_SP_BADSTATEMENT
|
||||
create function f() returns int begin prepare stmt from "select 1";
|
||||
--error ER_SP_BADSTATEMENT
|
||||
create procedure p() execute stmt;
|
||||
--error ER_SP_BADSTATEMENT
|
||||
create function f() returns int begin execute stmt;
|
||||
deallocate prepare stmt;
|
||||
|
||||
# BUG#9814: Closing a cursor that is not open
|
||||
create table t1(f1 int);
|
||||
create table t2(f1 int);
|
||||
|
@ -1114,3 +1085,12 @@ drop function bug11834_1;
|
|||
execute stmt;
|
||||
deallocate prepare stmt;
|
||||
drop function bug11834_2;
|
||||
#
|
||||
# BUG#NNNN: New bug synopsis
|
||||
#
|
||||
#--disable_warnings
|
||||
#drop procedure if exists bugNNNN|
|
||||
#--enable_warnings
|
||||
#create procedure bugNNNN...
|
||||
|
||||
|
||||
|
|
|
@ -723,7 +723,7 @@ begin
|
|||
end|
|
||||
delimiter ;|
|
||||
|
||||
--error 1312
|
||||
--error ER_SP_NO_RETSET
|
||||
insert into t1 (c1) values (4),(5),(6);
|
||||
select * from t1;
|
||||
|
||||
|
|
|
@ -3834,21 +3834,21 @@ longlong Item_func_get_user_var::val_int()
|
|||
stores this variable and its value in thd->user_var_events, so that it can be
|
||||
written to the binlog (will be written just before the query is written, see
|
||||
log.cc).
|
||||
|
||||
|
||||
RETURN
|
||||
0 OK
|
||||
0 OK
|
||||
1 Failed to put appropriate record into binary log
|
||||
|
||||
|
||||
*/
|
||||
|
||||
int get_var_with_binlog(THD *thd, LEX_STRING &name,
|
||||
user_var_entry **out_entry)
|
||||
int get_var_with_binlog(THD *thd, enum_sql_command sql_command,
|
||||
LEX_STRING &name, user_var_entry **out_entry)
|
||||
{
|
||||
BINLOG_USER_VAR_EVENT *user_var_event;
|
||||
user_var_entry *var_entry;
|
||||
var_entry= get_variable(&thd->user_vars, name, 0);
|
||||
|
||||
if (!(opt_bin_log && is_update_query(thd->lex->sql_command)))
|
||||
|
||||
if (!(opt_bin_log && is_update_query(sql_command)))
|
||||
{
|
||||
*out_entry= var_entry;
|
||||
return 0;
|
||||
|
@ -3941,7 +3941,7 @@ void Item_func_get_user_var::fix_length_and_dec()
|
|||
decimals=NOT_FIXED_DEC;
|
||||
max_length=MAX_BLOB_WIDTH;
|
||||
|
||||
error= get_var_with_binlog(thd, name, &var_entry);
|
||||
error= get_var_with_binlog(thd, thd->lex->sql_command, name, &var_entry);
|
||||
|
||||
if (var_entry)
|
||||
{
|
||||
|
|
|
@ -1177,9 +1177,6 @@ class Xid_log_event: public Log_event
|
|||
Every time a query uses the value of a user variable, a User_var_log_event is
|
||||
written before the Query_log_event, to set the user variable.
|
||||
|
||||
Every time a query uses the value of a user variable, a User_var_log_event is
|
||||
written before the Query_log_event, to set the user variable.
|
||||
|
||||
****************************************************************************/
|
||||
|
||||
class User_var_log_event: public Log_event
|
||||
|
|
|
@ -589,7 +589,7 @@ bool mysql_change_db(THD *thd,const char *name,bool no_access_check);
|
|||
void mysql_parse(THD *thd,char *inBuf,uint length);
|
||||
bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length);
|
||||
bool is_update_query(enum enum_sql_command command);
|
||||
bool alloc_query(THD *thd, char *packet, ulong packet_length);
|
||||
bool alloc_query(THD *thd, const char *packet, uint packet_length);
|
||||
void mysql_init_select(LEX *lex);
|
||||
void mysql_reset_thd_for_next_command(THD *thd);
|
||||
void mysql_init_query(THD *thd, uchar *buf, uint length);
|
||||
|
@ -848,16 +848,17 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond);
|
|||
bool get_schema_tables_result(JOIN *join);
|
||||
|
||||
/* sql_prepare.cc */
|
||||
bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
|
||||
LEX_STRING *name);
|
||||
|
||||
void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length);
|
||||
void mysql_stmt_execute(THD *thd, char *packet, uint packet_length);
|
||||
void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name);
|
||||
void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length);
|
||||
void mysql_stmt_close(THD *thd, char *packet);
|
||||
void mysql_sql_stmt_prepare(THD *thd);
|
||||
void mysql_sql_stmt_execute(THD *thd);
|
||||
void mysql_sql_stmt_close(THD *thd);
|
||||
void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length);
|
||||
void mysql_stmt_reset(THD *thd, char *packet);
|
||||
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
|
||||
void reinit_stmt_before_use(THD *thd, LEX *lex);
|
||||
void init_stmt_after_parse(THD*, LEX*);
|
||||
|
||||
/* sql_handler.cc */
|
||||
bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen);
|
||||
|
@ -1364,8 +1365,8 @@ extern int sql_cache_hit(THD *thd, char *inBuf, uint length);
|
|||
/* item_func.cc */
|
||||
Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
|
||||
LEX_STRING component);
|
||||
int get_var_with_binlog(THD *thd, LEX_STRING &name,
|
||||
user_var_entry **out_entry);
|
||||
int get_var_with_binlog(THD *thd, enum_sql_command sql_command,
|
||||
LEX_STRING &name, user_var_entry **out_entry);
|
||||
/* log.cc */
|
||||
bool flush_error_log(void);
|
||||
|
||||
|
|
|
@ -5401,3 +5401,5 @@ ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG
|
|||
eng "Can't update table '%-.64s' in stored function/trigger because it is already used by statement which invoked this stored function/trigger."
|
||||
ER_VIEW_PREVENT_UPDATE
|
||||
eng "The definition of table '%-.64s' prevents operation %s on table '%-.64s'."
|
||||
ER_PS_NO_RECURSION
|
||||
eng "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"
|
||||
|
|
|
@ -47,15 +47,30 @@ sp_map_result_type(enum enum_field_types type)
|
|||
}
|
||||
|
||||
/*
|
||||
* Returns TRUE if the 'cmd' is a command that might result in
|
||||
* multiple result sets being sent back.
|
||||
* Note: This does not include SQLCOM_SELECT which is treated
|
||||
* separately in sql_yacc.yy.
|
||||
*/
|
||||
bool
|
||||
sp_multi_results_command(enum enum_sql_command cmd)
|
||||
SYNOPSIS
|
||||
sp_get_flags_for_command()
|
||||
|
||||
DESCRIPTION
|
||||
Returns a combination of:
|
||||
* sp_head::MULTI_RESULTS: added if the 'cmd' is a command that might
|
||||
result in multiple result sets being sent back.
|
||||
* sp_head::CONTAINS_DYNAMIC_SQL: added if 'cmd' is one of PREPARE,
|
||||
EXECUTE, DEALLOCATE.
|
||||
*/
|
||||
|
||||
uint
|
||||
sp_get_flags_for_command(LEX *lex)
|
||||
{
|
||||
switch (cmd) {
|
||||
uint flags;
|
||||
|
||||
switch (lex->sql_command) {
|
||||
case SQLCOM_SELECT:
|
||||
if (lex->result)
|
||||
{
|
||||
flags= 0; /* This is a SELECT with INTO clause */
|
||||
break;
|
||||
}
|
||||
/* fallthrough */
|
||||
case SQLCOM_ANALYZE:
|
||||
case SQLCOM_CHECKSUM:
|
||||
case SQLCOM_HA_READ:
|
||||
|
@ -90,10 +105,26 @@ sp_multi_results_command(enum enum_sql_command cmd)
|
|||
case SQLCOM_SHOW_TABLES:
|
||||
case SQLCOM_SHOW_VARIABLES:
|
||||
case SQLCOM_SHOW_WARNS:
|
||||
return TRUE;
|
||||
flags= sp_head::MULTI_RESULTS;
|
||||
break;
|
||||
/*
|
||||
EXECUTE statement may return a result set, but doesn't have to.
|
||||
We can't, however, know it in advance, and therefore must add
|
||||
this statement here. This is ok, as is equivalent to a result-set
|
||||
statement within an IF condition.
|
||||
*/
|
||||
case SQLCOM_EXECUTE:
|
||||
flags= sp_head::MULTI_RESULTS | sp_head::CONTAINS_DYNAMIC_SQL;
|
||||
break;
|
||||
case SQLCOM_PREPARE:
|
||||
case SQLCOM_DEALLOCATE_PREPARE:
|
||||
flags= sp_head::CONTAINS_DYNAMIC_SQL;
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
flags= 0;
|
||||
break;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
||||
|
@ -364,9 +395,7 @@ sp_head::operator delete(void *ptr, size_t size)
|
|||
|
||||
sp_head::sp_head()
|
||||
:Query_arena(&main_mem_root, INITIALIZED_FOR_SP),
|
||||
m_returns_cs(NULL), m_has_return(FALSE),
|
||||
m_simple_case(FALSE), m_multi_results(FALSE), m_in_handler(FALSE),
|
||||
m_is_invoked(FALSE)
|
||||
m_flags(0), m_returns_cs(NULL)
|
||||
{
|
||||
extern byte *
|
||||
sp_table_key(const byte *ptr, uint *plen, my_bool first);
|
||||
|
@ -782,7 +811,7 @@ int sp_head::execute(THD *thd)
|
|||
DBUG_RETURN(-1);
|
||||
}
|
||||
|
||||
if (m_is_invoked)
|
||||
if (m_flags & IS_INVOKED)
|
||||
{
|
||||
/*
|
||||
We have to disable recursion for stored routines since in
|
||||
|
@ -802,7 +831,7 @@ int sp_head::execute(THD *thd)
|
|||
my_error(ER_SP_NO_RECURSION, MYF(0));
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
m_is_invoked= TRUE;
|
||||
m_flags|= IS_INVOKED;
|
||||
|
||||
dbchanged= FALSE;
|
||||
if (m_db.length &&
|
||||
|
@ -889,6 +918,15 @@ int sp_head::execute(THD *thd)
|
|||
|
||||
/* we should cleanup free_list and memroot, used by instruction */
|
||||
thd->free_items();
|
||||
/*
|
||||
FIXME: we must free user var events only if the routine is executed
|
||||
in non-prelocked mode and statement-by-statement replication is used.
|
||||
But if we don't free them now, the server crashes because user var
|
||||
events are allocated in execute_mem_root. This is Bug#12637, and when
|
||||
it's fixed, please add if (thd->options & OPTION_BIN_LOG) here.
|
||||
*/
|
||||
if (opt_bin_log)
|
||||
reset_dynamic(&thd->user_var_events);
|
||||
free_root(&execute_mem_root, MYF(0));
|
||||
|
||||
/*
|
||||
|
@ -955,7 +993,7 @@ int sp_head::execute(THD *thd)
|
|||
if (! thd->killed)
|
||||
ret= mysql_change_db(thd, olddb, 0);
|
||||
}
|
||||
m_is_invoked= FALSE;
|
||||
m_flags&= ~IS_INVOKED;
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
|
@ -1172,7 +1210,7 @@ int sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||
thd->spcont= save_spcont;
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
|
||||
|
||||
if (csize > 0 || hmax > 0 || cmax > 0)
|
||||
{
|
||||
Item_null *nit= NULL; // Re-use this, and only create if needed
|
||||
|
@ -1349,7 +1387,7 @@ int sp_head::execute_procedure(THD *thd, List<Item> *args)
|
|||
nctx->pop_all_cursors(); // To avoid memory leaks after an error
|
||||
delete nctx; // Does nothing
|
||||
thd->spcont= save_spcont;
|
||||
|
||||
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
|
@ -1397,7 +1435,6 @@ sp_head::restore_lex(THD *thd)
|
|||
LEX *sublex= thd->lex;
|
||||
LEX *oldlex= (LEX *)m_lex.pop();
|
||||
|
||||
init_stmt_after_parse(thd, sublex);
|
||||
if (! oldlex)
|
||||
return; // Nothing to restore
|
||||
|
||||
|
|
|
@ -33,8 +33,8 @@
|
|||
Item_result
|
||||
sp_map_result_type(enum enum_field_types type);
|
||||
|
||||
bool
|
||||
sp_multi_results_command(enum enum_sql_command cmd);
|
||||
uint
|
||||
sp_get_flags_for_command(LEX *lex);
|
||||
|
||||
struct sp_label;
|
||||
class sp_instr;
|
||||
|
@ -107,18 +107,23 @@ class sp_head :private Query_arena
|
|||
|
||||
MEM_ROOT main_mem_root;
|
||||
public:
|
||||
/* Possible values of m_flags */
|
||||
const static int
|
||||
HAS_RETURN= 1, // For FUNCTIONs only: is set if has RETURN
|
||||
IN_SIMPLE_CASE= 2, // Is set if parsing a simple CASE
|
||||
IN_HANDLER= 4, // Is set if the parser is in a handler body
|
||||
MULTI_RESULTS= 8, // Is set if a procedure with SELECT(s)
|
||||
CONTAINS_DYNAMIC_SQL= 16, // Is set if a procedure with PREPARE/EXECUTE
|
||||
IS_INVOKED= 32; // Is set if this sp_head is being used.
|
||||
|
||||
int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE
|
||||
uint m_flags; // Boolean attributes of a stored routine
|
||||
enum enum_field_types m_returns; // For FUNCTIONs only
|
||||
Field::geometry_type m_geom_returns;
|
||||
CHARSET_INFO *m_returns_cs; // For FUNCTIONs only
|
||||
TYPELIB *m_returns_typelib; // For FUNCTIONs only
|
||||
uint m_returns_len; // For FUNCTIONs only
|
||||
uint m_returns_pack; // For FUNCTIONs only
|
||||
my_bool m_has_return; // For FUNCTIONs only
|
||||
my_bool m_simple_case; // TRUE if parsing simple case, FALSE otherwise
|
||||
my_bool m_multi_results; // TRUE if a procedure with SELECT(s)
|
||||
my_bool m_in_handler; // TRUE if parser in a handler body
|
||||
uchar *m_tmp_query; // Temporary pointer to sub query string
|
||||
uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value
|
||||
st_sp_chistics *m_chistics;
|
||||
|
@ -265,6 +270,19 @@ public:
|
|||
bool add_used_tables_to_table_list(THD *thd,
|
||||
TABLE_LIST ***query_tables_last_ptr);
|
||||
|
||||
/*
|
||||
Check if this stored routine contains statements disallowed
|
||||
in a stored function or trigger, and set an appropriate error message
|
||||
if this is the case.
|
||||
*/
|
||||
bool is_not_allowed_in_function(const char *where)
|
||||
{
|
||||
if (m_flags & CONTAINS_DYNAMIC_SQL)
|
||||
my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "Dynamic SQL");
|
||||
else if (m_flags & MULTI_RESULTS)
|
||||
my_error(ER_SP_NO_RETSET, MYF(0), where);
|
||||
return test(m_flags & (CONTAINS_DYNAMIC_SQL|MULTI_RESULTS));
|
||||
}
|
||||
private:
|
||||
|
||||
MEM_ROOT *m_thd_root; // Temp. store for thd's mem_root
|
||||
|
@ -290,9 +308,6 @@ private:
|
|||
*/
|
||||
HASH m_sptabs;
|
||||
|
||||
/* Used for tracking of routine invocations and preventing recursion. */
|
||||
bool m_is_invoked;
|
||||
|
||||
int
|
||||
execute(THD *thd);
|
||||
|
||||
|
|
|
@ -551,11 +551,6 @@ void THD::cleanup_after_query()
|
|||
}
|
||||
/* Free Items that were created during this execution */
|
||||
free_items();
|
||||
/*
|
||||
In the rest of code we assume that free_list never points to garbage:
|
||||
Keep this predicate true.
|
||||
*/
|
||||
free_list= 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1686,23 +1681,17 @@ Statement_map::Statement_map() :
|
|||
NULL,MYF(0));
|
||||
}
|
||||
|
||||
|
||||
int Statement_map::insert(Statement *statement)
|
||||
{
|
||||
int rc= my_hash_insert(&st_hash, (byte *) statement);
|
||||
if (rc == 0)
|
||||
last_found_statement= statement;
|
||||
if (statement->name.str)
|
||||
{
|
||||
/*
|
||||
If there is a statement with the same name, remove it. It is ok to
|
||||
remove old and fail to insert new one at the same time.
|
||||
*/
|
||||
Statement *old_stmt;
|
||||
if ((old_stmt= find_by_name(&statement->name)))
|
||||
erase(old_stmt);
|
||||
if ((rc= my_hash_insert(&names_hash, (byte*)statement)))
|
||||
hash_delete(&st_hash, (byte*)statement);
|
||||
}
|
||||
if (rc == 0)
|
||||
last_found_statement= statement;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -128,6 +128,7 @@ void lex_start(THD *thd, uchar *buf,uint length)
|
|||
lex->update_list.empty();
|
||||
lex->param_list.empty();
|
||||
lex->view_list.empty();
|
||||
lex->prepared_stmt_params.empty();
|
||||
lex->unit.next= lex->unit.master=
|
||||
lex->unit.link_next= lex->unit.return_to= 0;
|
||||
lex->unit.prev= lex->unit.link_prev= 0;
|
||||
|
@ -143,6 +144,7 @@ void lex_start(THD *thd, uchar *buf,uint length)
|
|||
lex->describe= 0;
|
||||
lex->subqueries= FALSE;
|
||||
lex->view_prepare_mode= FALSE;
|
||||
lex->stmt_prepare_mode= FALSE;
|
||||
lex->derived_tables= 0;
|
||||
lex->lock_option= TL_READ;
|
||||
lex->found_semicolon= 0;
|
||||
|
@ -568,8 +570,7 @@ int yylex(void *arg, void *yythd)
|
|||
its value in a query for the binlog, the query must stay
|
||||
grammatically correct.
|
||||
*/
|
||||
else if (c == '?' && ((THD*) yythd)->command == COM_STMT_PREPARE &&
|
||||
!ident_map[yyPeek()])
|
||||
else if (c == '?' && lex->stmt_prepare_mode && !ident_map[yyPeek()])
|
||||
return(PARAM_MARKER);
|
||||
return((int) c);
|
||||
|
||||
|
@ -981,7 +982,7 @@ int yylex(void *arg, void *yythd)
|
|||
{
|
||||
THD* thd= (THD*)yythd;
|
||||
if ((thd->client_capabilities & CLIENT_MULTI_STATEMENTS) &&
|
||||
(thd->command != COM_STMT_PREPARE))
|
||||
!lex->stmt_prepare_mode)
|
||||
{
|
||||
lex->safe_to_cache_query= 0;
|
||||
lex->found_semicolon=(char*) lex->ptr;
|
||||
|
|
|
@ -808,6 +808,11 @@ typedef struct st_lex
|
|||
to an .frm file. We need this definition to stay untouched.
|
||||
*/
|
||||
bool view_prepare_mode;
|
||||
/*
|
||||
TRUE if we're parsing a prepared statement: in this mode
|
||||
we should allow placeholders and disallow multistatements.
|
||||
*/
|
||||
bool stmt_prepare_mode;
|
||||
bool safe_to_cache_query;
|
||||
bool subqueries, ignore;
|
||||
bool variables_used;
|
||||
|
|
135
sql/sql_parse.cc
135
sql/sql_parse.cc
|
@ -1644,7 +1644,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
|||
}
|
||||
case COM_STMT_PREPARE:
|
||||
{
|
||||
mysql_stmt_prepare(thd, packet, packet_length, 0);
|
||||
mysql_stmt_prepare(thd, packet, packet_length);
|
||||
break;
|
||||
}
|
||||
case COM_STMT_CLOSE:
|
||||
|
@ -1664,6 +1664,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
|||
char *packet_end= thd->query + thd->query_length;
|
||||
mysql_log.write(thd,command,"%s",thd->query);
|
||||
DBUG_PRINT("query",("%-.4096s",thd->query));
|
||||
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
|
||||
|
||||
mysql_parse(thd,thd->query, thd->query_length);
|
||||
|
||||
while (!thd->killed && thd->lex->found_semicolon && !thd->net.report_error)
|
||||
|
@ -2220,7 +2224,7 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
|
|||
TRUE error; In this case thd->fatal_error is set
|
||||
*/
|
||||
|
||||
bool alloc_query(THD *thd, char *packet, ulong packet_length)
|
||||
bool alloc_query(THD *thd, const char *packet, uint packet_length)
|
||||
{
|
||||
packet_length--; // Remove end null
|
||||
/* Remove garbage at start and end of query */
|
||||
|
@ -2229,7 +2233,7 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length)
|
|||
packet++;
|
||||
packet_length--;
|
||||
}
|
||||
char *pos=packet+packet_length; // Point at end null
|
||||
const char *pos= packet + packet_length; // Point at end null
|
||||
while (packet_length > 0 &&
|
||||
(pos[-1] == ';' || my_isspace(thd->charset() ,pos[-1])))
|
||||
{
|
||||
|
@ -2250,8 +2254,6 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length)
|
|||
thd->packet.shrink(thd->variables.net_buffer_length);
|
||||
thd->convert_buffer.shrink(thd->variables.net_buffer_length);
|
||||
|
||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -2466,112 +2468,17 @@ mysql_execute_command(THD *thd)
|
|||
}
|
||||
case SQLCOM_PREPARE:
|
||||
{
|
||||
char *query_str;
|
||||
uint query_len;
|
||||
if (lex->prepared_stmt_code_is_varref)
|
||||
{
|
||||
/* This is PREPARE stmt FROM @var. */
|
||||
String str;
|
||||
CHARSET_INFO *to_cs= thd->variables.collation_connection;
|
||||
bool need_conversion;
|
||||
user_var_entry *entry;
|
||||
String *pstr= &str;
|
||||
uint32 unused;
|
||||
/*
|
||||
Convert @var contents to string in connection character set. Although
|
||||
it is known that int/real/NULL value cannot be a valid query we still
|
||||
convert it for error messages to uniform.
|
||||
*/
|
||||
if ((entry=
|
||||
(user_var_entry*)hash_search(&thd->user_vars,
|
||||
(byte*)lex->prepared_stmt_code.str,
|
||||
lex->prepared_stmt_code.length))
|
||||
&& entry->value)
|
||||
{
|
||||
my_bool is_var_null;
|
||||
pstr= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC);
|
||||
/*
|
||||
NULL value of variable checked early as entry->value so here
|
||||
we can't get NULL in normal conditions
|
||||
*/
|
||||
DBUG_ASSERT(!is_var_null);
|
||||
if (!pstr)
|
||||
goto error;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
variable absent or equal to NULL, so we need to set variable to
|
||||
something reasonable to get readable error message during parsing
|
||||
*/
|
||||
str.set("NULL", 4, &my_charset_latin1);
|
||||
}
|
||||
|
||||
need_conversion=
|
||||
String::needs_conversion(pstr->length(), pstr->charset(),
|
||||
to_cs, &unused);
|
||||
|
||||
query_len= need_conversion? (pstr->length() * to_cs->mbmaxlen) :
|
||||
pstr->length();
|
||||
if (!(query_str= alloc_root(thd->mem_root, query_len+1)))
|
||||
goto error;
|
||||
|
||||
if (need_conversion)
|
||||
{
|
||||
uint dummy_errors;
|
||||
query_len= copy_and_convert(query_str, query_len, to_cs,
|
||||
pstr->ptr(), pstr->length(),
|
||||
pstr->charset(), &dummy_errors);
|
||||
}
|
||||
else
|
||||
memcpy(query_str, pstr->ptr(), pstr->length());
|
||||
query_str[query_len]= 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
query_str= lex->prepared_stmt_code.str;
|
||||
query_len= lex->prepared_stmt_code.length;
|
||||
DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n",
|
||||
lex->prepared_stmt_name.length,
|
||||
lex->prepared_stmt_name.str,
|
||||
query_len, query_str));
|
||||
}
|
||||
thd->command= COM_STMT_PREPARE;
|
||||
if (!(res= mysql_stmt_prepare(thd, query_str, query_len + 1,
|
||||
&lex->prepared_stmt_name)))
|
||||
send_ok(thd, 0L, 0L, "Statement prepared");
|
||||
mysql_sql_stmt_prepare(thd);
|
||||
break;
|
||||
}
|
||||
case SQLCOM_EXECUTE:
|
||||
{
|
||||
DBUG_PRINT("info", ("EXECUTE: %.*s\n",
|
||||
lex->prepared_stmt_name.length,
|
||||
lex->prepared_stmt_name.str));
|
||||
mysql_sql_stmt_execute(thd, &lex->prepared_stmt_name);
|
||||
lex->prepared_stmt_params.empty();
|
||||
mysql_sql_stmt_execute(thd);
|
||||
break;
|
||||
}
|
||||
case SQLCOM_DEALLOCATE_PREPARE:
|
||||
{
|
||||
Statement* stmt;
|
||||
DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n",
|
||||
lex->prepared_stmt_name.length,
|
||||
lex->prepared_stmt_name.str));
|
||||
/* We account deallocate in the same manner as mysql_stmt_close */
|
||||
statistic_increment(thd->status_var.com_stmt_close, &LOCK_status);
|
||||
if ((stmt= thd->stmt_map.find_by_name(&lex->prepared_stmt_name)))
|
||||
{
|
||||
thd->stmt_map.erase(stmt);
|
||||
send_ok(thd);
|
||||
}
|
||||
else
|
||||
{
|
||||
my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0),
|
||||
lex->prepared_stmt_name.length,
|
||||
lex->prepared_stmt_name.str,
|
||||
"DEALLOCATE PREPARE");
|
||||
goto error;
|
||||
}
|
||||
mysql_sql_stmt_close(thd);
|
||||
break;
|
||||
}
|
||||
case SQLCOM_DO:
|
||||
|
@ -4128,7 +4035,7 @@ end_with_restore_list:
|
|||
}
|
||||
#endif
|
||||
if (lex->sphead->m_type == TYPE_ENUM_FUNCTION &&
|
||||
!lex->sphead->m_has_return)
|
||||
!(lex->sphead->m_flags & sp_head::HAS_RETURN))
|
||||
{
|
||||
my_error(ER_SP_NORETURN, MYF(0), name);
|
||||
delete lex->sphead;
|
||||
|
@ -4217,15 +4124,31 @@ end_with_restore_list:
|
|||
ha_rows select_limit;
|
||||
/* bits that should be cleared in thd->server_status */
|
||||
uint bits_to_be_cleared= 0;
|
||||
/*
|
||||
Check that the stored procedure doesn't contain Dynamic SQL
|
||||
and doesn't return result sets: such stored procedures can't
|
||||
be called from a function or trigger.
|
||||
*/
|
||||
if (thd->in_sub_stmt)
|
||||
{
|
||||
const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ?
|
||||
"trigger" : "function");
|
||||
if (sp->is_not_allowed_in_function(where))
|
||||
goto error;
|
||||
}
|
||||
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
my_bool nsok= thd->net.no_send_ok;
|
||||
thd->net.no_send_ok= TRUE;
|
||||
#endif
|
||||
if (sp->m_multi_results)
|
||||
if (sp->m_flags & sp_head::MULTI_RESULTS)
|
||||
{
|
||||
if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS))
|
||||
{
|
||||
/*
|
||||
The client does not support multiple result sets being sent
|
||||
back
|
||||
*/
|
||||
my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str);
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
thd->net.no_send_ok= nsok;
|
||||
|
@ -4269,7 +4192,7 @@ end_with_restore_list:
|
|||
thd->row_count_func= 0;
|
||||
|
||||
/*
|
||||
We never write CALL statements int binlog:
|
||||
We never write CALL statements into binlog:
|
||||
- If the mode is non-prelocked, each statement will be logged
|
||||
separately.
|
||||
- If the mode is prelocked, the invoking statement will care
|
||||
|
|
1253
sql/sql_prepare.cc
1253
sql/sql_prepare.cc
File diff suppressed because it is too large
Load diff
|
@ -921,16 +921,11 @@ deallocate:
|
|||
{
|
||||
THD *thd=YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
if (thd->command == COM_STMT_PREPARE)
|
||||
if (lex->stmt_prepare_mode)
|
||||
{
|
||||
yyerror(ER(ER_SYNTAX_ERROR));
|
||||
YYABORT;
|
||||
}
|
||||
if (lex->sphead)
|
||||
{
|
||||
my_error(ER_SP_BADSTATEMENT, MYF(0), "DEALLOCATE");
|
||||
YYABORT;
|
||||
}
|
||||
lex->sql_command= SQLCOM_DEALLOCATE_PREPARE;
|
||||
lex->prepared_stmt_name= $3;
|
||||
};
|
||||
|
@ -946,16 +941,11 @@ prepare:
|
|||
{
|
||||
THD *thd=YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
if (thd->command == COM_STMT_PREPARE)
|
||||
if (lex->stmt_prepare_mode)
|
||||
{
|
||||
yyerror(ER(ER_SYNTAX_ERROR));
|
||||
YYABORT;
|
||||
}
|
||||
if (lex->sphead)
|
||||
{
|
||||
my_error(ER_SP_BADSTATEMENT, MYF(0), "PREPARE");
|
||||
YYABORT;
|
||||
}
|
||||
lex->sql_command= SQLCOM_PREPARE;
|
||||
lex->prepared_stmt_name= $2;
|
||||
};
|
||||
|
@ -981,16 +971,11 @@ execute:
|
|||
{
|
||||
THD *thd=YYTHD;
|
||||
LEX *lex= thd->lex;
|
||||
if (thd->command == COM_STMT_PREPARE)
|
||||
if (lex->stmt_prepare_mode)
|
||||
{
|
||||
yyerror(ER(ER_SYNTAX_ERROR));
|
||||
YYABORT;
|
||||
}
|
||||
if (lex->sphead)
|
||||
{
|
||||
my_error(ER_SP_BADSTATEMENT, MYF(0), "EXECUTE");
|
||||
YYABORT;
|
||||
}
|
||||
lex->sql_command= SQLCOM_EXECUTE;
|
||||
lex->prepared_stmt_name= $2;
|
||||
}
|
||||
|
@ -1324,11 +1309,8 @@ create:
|
|||
YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
|
||||
sp->restore_thd_mem_root(YYTHD);
|
||||
|
||||
if (sp->m_multi_results)
|
||||
{
|
||||
my_error(ER_SP_NO_RETSET, MYF(0), "trigger");
|
||||
YYABORT;
|
||||
}
|
||||
if (sp->is_not_allowed_in_function("trigger"))
|
||||
YYABORT;
|
||||
|
||||
/*
|
||||
We have to do it after parsing trigger body, because some of
|
||||
|
@ -1481,11 +1463,9 @@ create_function_tail:
|
|||
LEX *lex= Lex;
|
||||
sp_head *sp= lex->sphead;
|
||||
|
||||
if (sp->m_multi_results)
|
||||
{
|
||||
my_error(ER_SP_NO_RETSET, MYF(0), "function");
|
||||
YYABORT;
|
||||
}
|
||||
if (sp->is_not_allowed_in_function("function"))
|
||||
YYABORT;
|
||||
|
||||
if (sp->check_backpatch(YYTHD))
|
||||
YYABORT;
|
||||
lex->sql_command= SQLCOM_CREATE_SPFUNCTION;
|
||||
|
@ -1735,7 +1715,7 @@ sp_decl:
|
|||
|
||||
sp->add_instr(i);
|
||||
sp->push_backpatch(i, ctx->push_label((char *)"", 0));
|
||||
sp->m_in_handler= TRUE;
|
||||
sp->m_flags|= sp_head::IN_HANDLER;
|
||||
}
|
||||
sp_hcond_list sp_proc_stmt
|
||||
{
|
||||
|
@ -1759,7 +1739,7 @@ sp_decl:
|
|||
sp->push_backpatch(i, lex->spcont->last_label()); /* Block end */
|
||||
}
|
||||
lex->sphead->backpatch(hlab);
|
||||
sp->m_in_handler= FALSE;
|
||||
sp->m_flags&= ~sp_head::IN_HANDLER;
|
||||
$$.vars= $$.conds= $$.curs= 0;
|
||||
$$.hndlrs= $6;
|
||||
ctx->add_handlers($6);
|
||||
|
@ -1971,12 +1951,7 @@ sp_proc_stmt:
|
|||
LEX *lex= Lex;
|
||||
sp_head *sp= lex->sphead;
|
||||
|
||||
if ((lex->sql_command == SQLCOM_SELECT && !lex->result) ||
|
||||
sp_multi_results_command(lex->sql_command))
|
||||
{
|
||||
/* We maybe have one or more SELECT without INTO */
|
||||
sp->m_multi_results= TRUE;
|
||||
}
|
||||
sp->m_flags|= sp_get_flags_for_command(lex);
|
||||
if (lex->sql_command == SQLCOM_CHANGE_DB)
|
||||
{ /* "USE db" doesn't work in a procedure */
|
||||
my_error(ER_SP_BADSTATEMENT, MYF(0), "USE");
|
||||
|
@ -2026,14 +2001,14 @@ sp_proc_stmt:
|
|||
i= new sp_instr_freturn(sp->instructions(), lex->spcont,
|
||||
$3, sp->m_returns, lex);
|
||||
sp->add_instr(i);
|
||||
sp->m_has_return= TRUE;
|
||||
sp->m_flags|= sp_head::HAS_RETURN;
|
||||
}
|
||||
sp->restore_lex(YYTHD);
|
||||
}
|
||||
| IF sp_if END IF {}
|
||||
| CASE_SYM WHEN_SYM
|
||||
{
|
||||
Lex->sphead->m_simple_case= FALSE;
|
||||
Lex->sphead->m_flags&= ~sp_head::IN_SIMPLE_CASE;
|
||||
}
|
||||
sp_case END CASE_SYM {}
|
||||
| CASE_SYM
|
||||
|
@ -2053,7 +2028,7 @@ sp_proc_stmt:
|
|||
|
||||
lex->spcont->push_pvar(&dummy, MYSQL_TYPE_STRING, sp_param_in);
|
||||
lex->sphead->add_instr(i);
|
||||
lex->sphead->m_simple_case= TRUE;
|
||||
lex->sphead->m_flags|= sp_head::IN_SIMPLE_CASE;
|
||||
lex->sphead->restore_lex(YYTHD);
|
||||
}
|
||||
sp_case END CASE_SYM
|
||||
|
@ -2367,7 +2342,7 @@ sp_case:
|
|||
uint ip= sp->instructions();
|
||||
sp_instr_jump_if_not *i;
|
||||
|
||||
if (! sp->m_simple_case)
|
||||
if (! (sp->m_flags & sp_head::IN_SIMPLE_CASE))
|
||||
i= new sp_instr_jump_if_not(ip, ctx, $2, lex);
|
||||
else
|
||||
{ /* Simple case: <caseval> = <whenval> */
|
||||
|
|
Loading…
Add table
Reference in a new issue