mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
Implement WL#2661 "Prepared Statements: Dynamic SQL in Stored Procedures".
The idea of the patch is to separate statement processing logic, such as parsing, validation of the parsed tree, execution and cleanup, from global query processing logic, such as logging, resetting priorities of a thread, resetting stored procedure cache, resetting thread count of errors and warnings. This makes PREPARE and EXECUTE behave similarly to the rest of SQL statements and allows their use in stored procedures. This patch contains a change in behaviour: until recently for each SQL prepared statement command, 2 queries were written to the general log, e.g. [Query] prepare stmt from @stmt_text; [Prepare] select * from t1 <-- contents of @stmt_text The chagne was necessary to prevent [Prepare] commands from being written to the general log when executing a stored procedure with Dynamic SQL. We should consider whether the old behavior is preferrable and probably restore it. This patch refixes Bug#7115, Bug#10975 (partially), Bug#10605 (various bugs in Dynamic SQL reported before it was disabled).
This commit is contained in:
@ -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;
|
||||
|
||||
|
@ -3841,14 +3841,14 @@ longlong Item_func_get_user_var::val_int()
|
||||
|
||||
*/
|
||||
|
||||
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);
|
||||
@ -1361,8 +1362,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);
|
||||
|
||||
|
@ -5399,3 +5399,5 @@ ER_DATETIME_FUNCTION_OVERFLOW 22008
|
||||
eng "Datetime function: %-.32s field overflow"
|
||||
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_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.
|
||||
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.
|
||||
*/
|
||||
bool
|
||||
sp_multi_results_command(enum enum_sql_command cmd)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -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:
|
||||
@ -4124,7 +4031,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;
|
||||
@ -4213,15 +4120,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;
|
||||
@ -4265,7 +4188,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
|
||||
|
1151
sql/sql_prepare.cc
1151
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");
|
||||
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");
|
||||
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> */
|
||||
|
Reference in New Issue
Block a user