mirror of
https://github.com/MariaDB/server.git
synced 2025-01-18 13:02:28 +01:00
c910050a76
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). mysql-test/r/not_embedded_server.result: Since we don't want to log Dynamic SQL in stored procedures, now the general log gets only one log entry per SQL statement. mysql-test/r/sp-error.result: - remove obsolete tests - a better error message for the case when a stored procedure that returns a result set is called from a function mysql-test/r/trigger.result: - a better error message for the case when a stored procedure that returns a result set is called from a trigger mysql-test/t/sp-error.test: - a better error message for the case when a stored procedure that returns a result set is called from a function. - move the comment to its place (end of file). mysql-test/t/trigger.test: - a better error message for the case when a stored procedure that returns a result set is called from a trigger sql/item_func.cc: - we need to pass sql_command explicitly to get_var_with_binlog, because when creating a query for SQL prepared statement thd->lex->sql_command points at SQLCOM_EXECUTE, which is not listed in the list of update queries. sql/log_event.h: - remove an extra copy of the previous sentence sql/mysql_priv.h: - fix declarations of sql_prepare.cc API sql/share/errmsg.txt: - a new error message, when one attempts to execute a prepared statement which is currently being executed (this can happen only in Dynamic SQL at the moment). sql/sp_head.cc: - extend sp_multi_results_command to return different flags for a command (and rename it) - add support for SQLCOM_PREPARE,SQLCOM_EXECUTE, SQLCOM_DEALLOCATE to sp_get_flags_for_command - replace multiple boolean sp_head members with uint m_flags - a fix for a crash when user variables are used in a stored procedure and binlog is on. A temporary fix for Bug#12637 "SP crashes the server if it has update query with user var & binlog is enabled", which actually stands for stored functions: now instead of a crash we break replication if a user variable is used in a stored function which is executed in prelocked mode. sql/sp_head.h: - replace multiple boolean flags of sp_head with uint m_flags; - add flag CONTAINS_DYNAMIC_SQL - use this flag to error if a stored procedure with Dynamic SQL is called from a function or trigger. sql/sql_class.cc: - Statement_map::insert should not delete a statement if it exists, now it's done externally to be able to handle the case when the statement being deleted is in use. - remove extra code (free_list is already reset in free_items) sql/sql_lex.cc: - add lex->stmt_prepare_mode; we can't rely on thd->command any more, because we don't reset it any more (Dynamic SQL requirement is that PS are as little intrusive as possible). sql/sql_lex.h: - declare bool LEX::stmt_prepare_mode sql/sql_parse.cc: - move prepared statement code to sql_prepare.cc - change declarations (refactored code) - better error message when one attempts to use Dynamic SQL or a stored procedure that returns a result set in a function or trigger. sql/sql_prepare.cc: - major refactoring to ensure PREPARE/EXECUTE commands do not reset global THD state and allow their use in stored procedures. - add Prepared_statement::flags and use it to ensure no recursive execution of a prepared statement is possible - better comments sql/sql_yacc.yy: - enable PREPARE/EXECUTE/DEALLOCATE in stored procedures - produce an error message on attempt to use PREPARE/EXECUTE/DEALLOCATE in a stored function or trigger mysql-test/r/sp-dynamic.result: - sp-dynamic.test results mysql-test/t/sp-dynamic.test: - a new test for PREPARE/EXECUTE/DEALLOCATE in stored procedures.
364 lines
8.3 KiB
Text
364 lines
8.3 KiB
Text
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|
|