mirror of
https://github.com/MariaDB/server.git
synced 2025-03-29 18:35:35 +01:00
MDEV-10411 Providing compatibility for basic PL/SQL constructs
- Part 9: EXCEPTION handlers The top-most stored routine blocks now support EXCEPTION clause in its correct place: AS [ declarations ] BEGIN statements [ EXCEPTION exceptions ] END Inner block will be done in a separate commit. - Part 14: IN OUT instead of INOUT (in SP parameter declarations)
This commit is contained in:
parent
0040b0f380
commit
81ba971d03
9 changed files with 493 additions and 11 deletions
mysql-test/suite/compat/oracle
sql
178
mysql-test/suite/compat/oracle/r/sp-code.result
Normal file
178
mysql-test/suite/compat/oracle/r/sp-code.result
Normal file
|
@ -0,0 +1,178 @@
|
|||
SET sql_mode=ORACLE;
|
||||
# No HANDLER declarations, no exceptions
|
||||
CREATE FUNCTION f1 RETURN INT
|
||||
AS
|
||||
BEGIN
|
||||
RETURN 10;
|
||||
END;
|
||||
/
|
||||
SHOW FUNCTION CODE f1;
|
||||
Pos Instruction
|
||||
0 freturn 3 10
|
||||
SELECT f1();
|
||||
f1()
|
||||
10
|
||||
DROP FUNCTION f1;
|
||||
# No HANDLER declarations, no code, no exceptions
|
||||
CREATE PROCEDURE p1 ()
|
||||
IS
|
||||
BEGIN
|
||||
END;
|
||||
/
|
||||
SHOW PROCEDURE CODE p1;
|
||||
Pos Instruction
|
||||
0 jump 3
|
||||
CALL p1;
|
||||
DROP PROCEDURE p1;
|
||||
# No HANDLER declarations, no code, some exceptions
|
||||
CREATE PROCEDURE p1 (v IN OUT INT)
|
||||
IS
|
||||
BEGIN
|
||||
EXCEPTION
|
||||
WHEN 1002 THEN v:=225;
|
||||
END;
|
||||
/
|
||||
SHOW PROCEDURE CODE p1;
|
||||
Pos Instruction
|
||||
0 jump 1
|
||||
1 hpush_jump 4 1 EXIT
|
||||
2 set v@0 225
|
||||
3 hreturn 0 4
|
||||
4 hpop 1
|
||||
set @v= 10;
|
||||
CALL p1(@v);
|
||||
SELECT @v;
|
||||
@v
|
||||
10
|
||||
DROP PROCEDURE p1;
|
||||
# No HANDLER declarations, some code, some exceptions
|
||||
CREATE PROCEDURE p1 (v IN OUT INT)
|
||||
IS
|
||||
BEGIN
|
||||
v:=224;
|
||||
EXCEPTION
|
||||
WHEN 1002 THEN v:=225;
|
||||
END;
|
||||
/
|
||||
SHOW PROCEDURE CODE p1;
|
||||
Pos Instruction
|
||||
0 jump 3
|
||||
1 set v@0 224
|
||||
2 jump 6
|
||||
3 hpush_jump 1 1 EXIT
|
||||
4 set v@0 225
|
||||
5 hreturn 0 6
|
||||
6 hpop 1
|
||||
set @v= 10;
|
||||
CALL p1(@v);
|
||||
SELECT @v;
|
||||
@v
|
||||
224
|
||||
DROP PROCEDURE p1;
|
||||
# Some HANDLER declarations, no code, no exceptions
|
||||
CREATE PROCEDURE p1 (v IN OUT INT)
|
||||
IS
|
||||
EXIT HANDLER FOR 1000
|
||||
BEGIN
|
||||
v:=123;
|
||||
END;
|
||||
BEGIN
|
||||
EXCEPTION
|
||||
WHEN 1002 THEN v:=225;
|
||||
END;
|
||||
/
|
||||
SHOW PROCEDURE CODE p1;
|
||||
Pos Instruction
|
||||
0 hpush_jump 3 1 EXIT
|
||||
1 set v@0 123
|
||||
2 hreturn 0 6
|
||||
3 hpush_jump 6 1 EXIT
|
||||
4 set v@0 225
|
||||
5 hreturn 0 6
|
||||
6 hpop 2
|
||||
set @v= 10;
|
||||
CALL p1(@v);
|
||||
SELECT @v;
|
||||
@v
|
||||
10
|
||||
DROP PROCEDURE p1;
|
||||
# Some HANDLER declarations, no code, some exceptions
|
||||
CREATE PROCEDURE p1 (v IN OUT INT)
|
||||
IS
|
||||
EXIT HANDLER FOR 1000
|
||||
BEGIN
|
||||
v:=123;
|
||||
END;
|
||||
BEGIN
|
||||
END;
|
||||
/
|
||||
SHOW PROCEDURE CODE p1;
|
||||
Pos Instruction
|
||||
0 hpush_jump 3 1 EXIT
|
||||
1 set v@0 123
|
||||
2 hreturn 0 3
|
||||
3 hpop 1
|
||||
set @v= 10;
|
||||
CALL p1(@v);
|
||||
SELECT @v;
|
||||
@v
|
||||
10
|
||||
DROP PROCEDURE p1;
|
||||
# Some HANDLER declarations, some code, no exceptions
|
||||
CREATE PROCEDURE p1 (v IN OUT INT)
|
||||
IS
|
||||
EXIT HANDLER FOR 1000
|
||||
BEGIN
|
||||
v:=123;
|
||||
END;
|
||||
BEGIN
|
||||
v:=223;
|
||||
END;
|
||||
/
|
||||
SHOW PROCEDURE CODE p1;
|
||||
Pos Instruction
|
||||
0 hpush_jump 3 1 EXIT
|
||||
1 set v@0 123
|
||||
2 hreturn 0 5
|
||||
3 set v@0 223
|
||||
4 jump 5
|
||||
5 hpop 1
|
||||
set @v= 10;
|
||||
CALL p1(@v);
|
||||
SELECT @v;
|
||||
@v
|
||||
223
|
||||
DROP PROCEDURE p1;
|
||||
# Some HANDLER declarations, some code, some exceptions
|
||||
CREATE PROCEDURE p1 (v IN OUT VARCHAR2(20))
|
||||
IS
|
||||
EXIT HANDLER FOR 1000
|
||||
BEGIN
|
||||
v:=123;
|
||||
END;
|
||||
CONTINUE HANDLER FOR 1001
|
||||
BEGIN
|
||||
SET v=223;
|
||||
END;
|
||||
BEGIN
|
||||
v:= 1;
|
||||
EXCEPTION
|
||||
WHEN 1002 THEN SET v=225;
|
||||
END;
|
||||
/
|
||||
SHOW PROCEDURE CODE p1;
|
||||
Pos Instruction
|
||||
0 hpush_jump 3 1 EXIT
|
||||
1 set v@0 123
|
||||
2 hreturn 0 12
|
||||
3 hpush_jump 8 1 CONTINUE
|
||||
4 set v@0 223
|
||||
5 hreturn 1
|
||||
6 set v@0 1
|
||||
7 jump 12
|
||||
8 hpush_jump 6 1 EXIT
|
||||
9 set v@0 225
|
||||
10 hreturn 0 12
|
||||
11 jump 6
|
||||
12 hpop 3
|
||||
DROP PROCEDURE p1;
|
|
@ -430,12 +430,13 @@ CREATE PROCEDURE sp1 (p1 IN VARCHAR2(20), p2 OUT VARCHAR2(30))
|
|||
IS
|
||||
v1 INT;
|
||||
BEGIN
|
||||
EXCEPTION WHEN NOT FOUND THEN
|
||||
SELECT c1 INTO v1 FROM t1;
|
||||
p2 := p1;
|
||||
EXCEPTION
|
||||
WHEN NOT FOUND THEN
|
||||
BEGIN
|
||||
p2 := 'def';
|
||||
END;
|
||||
SELECT c1 INTO v1 FROM t1;
|
||||
p2 := p1;
|
||||
END;
|
||||
/
|
||||
CALL sp1('abc', @a);
|
||||
|
@ -444,3 +445,23 @@ SELECT @a;
|
|||
def
|
||||
DROP PROCEDURE sp1;
|
||||
DROP TABLE t1;
|
||||
CREATE PROCEDURE sp1 (v IN OUT INT, error IN INT)
|
||||
IS
|
||||
BEGIN
|
||||
SIGNAL SQLSTATE '45000' SET MYSQL_ERRNO=error, MESSAGE_TEXT='User defined error!';
|
||||
v:= 223;
|
||||
EXCEPTION
|
||||
WHEN 30001 THEN
|
||||
BEGIN
|
||||
v:= 113;
|
||||
END;
|
||||
END;
|
||||
/
|
||||
SET @v=10;
|
||||
CALL sp1(@v, 30001);
|
||||
CALL sp1(@v, 30002);
|
||||
ERROR 45000: User defined error!
|
||||
SELECT @v;
|
||||
@v
|
||||
113
|
||||
DROP PROCEDURE sp1;
|
||||
|
|
145
mysql-test/suite/compat/oracle/t/sp-code.test
Normal file
145
mysql-test/suite/compat/oracle/t/sp-code.test
Normal file
|
@ -0,0 +1,145 @@
|
|||
-- source include/have_debug.inc
|
||||
|
||||
SET sql_mode=ORACLE;
|
||||
|
||||
--echo # No HANDLER declarations, no exceptions
|
||||
DELIMITER /;
|
||||
CREATE FUNCTION f1 RETURN INT
|
||||
AS
|
||||
BEGIN
|
||||
RETURN 10;
|
||||
END;
|
||||
/
|
||||
DELIMITER ;/
|
||||
SHOW FUNCTION CODE f1;
|
||||
SELECT f1();
|
||||
DROP FUNCTION f1;
|
||||
|
||||
--echo # No HANDLER declarations, no code, no exceptions
|
||||
DELIMITER /;
|
||||
CREATE PROCEDURE p1 ()
|
||||
IS
|
||||
BEGIN
|
||||
END;
|
||||
/
|
||||
DELIMITER ;/
|
||||
SHOW PROCEDURE CODE p1;
|
||||
CALL p1;
|
||||
DROP PROCEDURE p1;
|
||||
|
||||
|
||||
--echo # No HANDLER declarations, no code, some exceptions
|
||||
DELIMITER /;
|
||||
CREATE PROCEDURE p1 (v IN OUT INT)
|
||||
IS
|
||||
BEGIN
|
||||
EXCEPTION
|
||||
WHEN 1002 THEN v:=225;
|
||||
END;
|
||||
/
|
||||
DELIMITER ;/
|
||||
SHOW PROCEDURE CODE p1;
|
||||
set @v= 10;
|
||||
CALL p1(@v);
|
||||
SELECT @v;
|
||||
DROP PROCEDURE p1;
|
||||
|
||||
|
||||
--echo # No HANDLER declarations, some code, some exceptions
|
||||
DELIMITER /;
|
||||
CREATE PROCEDURE p1 (v IN OUT INT)
|
||||
IS
|
||||
BEGIN
|
||||
v:=224;
|
||||
EXCEPTION
|
||||
WHEN 1002 THEN v:=225;
|
||||
END;
|
||||
/
|
||||
DELIMITER ;/
|
||||
SHOW PROCEDURE CODE p1;
|
||||
set @v= 10;
|
||||
CALL p1(@v);
|
||||
SELECT @v;
|
||||
DROP PROCEDURE p1;
|
||||
|
||||
|
||||
--echo # Some HANDLER declarations, no code, no exceptions
|
||||
DELIMITER /;
|
||||
CREATE PROCEDURE p1 (v IN OUT INT)
|
||||
IS
|
||||
EXIT HANDLER FOR 1000
|
||||
BEGIN
|
||||
v:=123;
|
||||
END;
|
||||
BEGIN
|
||||
EXCEPTION
|
||||
WHEN 1002 THEN v:=225;
|
||||
END;
|
||||
/
|
||||
DELIMITER ;/
|
||||
SHOW PROCEDURE CODE p1;
|
||||
set @v= 10;
|
||||
CALL p1(@v);
|
||||
SELECT @v;
|
||||
DROP PROCEDURE p1;
|
||||
|
||||
|
||||
--echo # Some HANDLER declarations, no code, some exceptions
|
||||
DELIMITER /;
|
||||
CREATE PROCEDURE p1 (v IN OUT INT)
|
||||
IS
|
||||
EXIT HANDLER FOR 1000
|
||||
BEGIN
|
||||
v:=123;
|
||||
END;
|
||||
BEGIN
|
||||
END;
|
||||
/
|
||||
DELIMITER ;/
|
||||
SHOW PROCEDURE CODE p1;
|
||||
set @v= 10;
|
||||
CALL p1(@v);
|
||||
SELECT @v;
|
||||
DROP PROCEDURE p1;
|
||||
|
||||
|
||||
--echo # Some HANDLER declarations, some code, no exceptions
|
||||
DELIMITER /;
|
||||
CREATE PROCEDURE p1 (v IN OUT INT)
|
||||
IS
|
||||
EXIT HANDLER FOR 1000
|
||||
BEGIN
|
||||
v:=123;
|
||||
END;
|
||||
BEGIN
|
||||
v:=223;
|
||||
END;
|
||||
/
|
||||
DELIMITER ;/
|
||||
SHOW PROCEDURE CODE p1;
|
||||
set @v= 10;
|
||||
CALL p1(@v);
|
||||
SELECT @v;
|
||||
DROP PROCEDURE p1;
|
||||
|
||||
--echo # Some HANDLER declarations, some code, some exceptions
|
||||
DELIMITER /;
|
||||
CREATE PROCEDURE p1 (v IN OUT VARCHAR2(20))
|
||||
IS
|
||||
EXIT HANDLER FOR 1000
|
||||
BEGIN
|
||||
v:=123;
|
||||
END;
|
||||
CONTINUE HANDLER FOR 1001
|
||||
BEGIN
|
||||
SET v=223;
|
||||
END;
|
||||
BEGIN
|
||||
v:= 1;
|
||||
EXCEPTION
|
||||
WHEN 1002 THEN SET v=225;
|
||||
END;
|
||||
/
|
||||
DELIMITER ;/
|
||||
SHOW PROCEDURE CODE p1;
|
||||
DROP PROCEDURE p1;
|
|
@ -458,12 +458,13 @@ CREATE PROCEDURE sp1 (p1 IN VARCHAR2(20), p2 OUT VARCHAR2(30))
|
|||
IS
|
||||
v1 INT;
|
||||
BEGIN
|
||||
EXCEPTION WHEN NOT FOUND THEN
|
||||
SELECT c1 INTO v1 FROM t1;
|
||||
p2 := p1;
|
||||
EXCEPTION
|
||||
WHEN NOT FOUND THEN
|
||||
BEGIN
|
||||
p2 := 'def';
|
||||
END;
|
||||
SELECT c1 INTO v1 FROM t1;
|
||||
p2 := p1;
|
||||
END;
|
||||
/
|
||||
|
||||
|
@ -474,3 +475,25 @@ SELECT @a;
|
|||
|
||||
DROP PROCEDURE sp1;
|
||||
DROP TABLE t1;
|
||||
|
||||
|
||||
DELIMITER /;
|
||||
CREATE PROCEDURE sp1 (v IN OUT INT, error IN INT)
|
||||
IS
|
||||
BEGIN
|
||||
SIGNAL SQLSTATE '45000' SET MYSQL_ERRNO=error, MESSAGE_TEXT='User defined error!';
|
||||
v:= 223;
|
||||
EXCEPTION
|
||||
WHEN 30001 THEN
|
||||
BEGIN
|
||||
v:= 113;
|
||||
END;
|
||||
END;
|
||||
/
|
||||
DELIMITER ;/
|
||||
SET @v=10;
|
||||
CALL sp1(@v, 30001);
|
||||
--error 30002
|
||||
CALL sp1(@v, 30002);
|
||||
SELECT @v;
|
||||
DROP PROCEDURE sp1;
|
||||
|
|
|
@ -2555,6 +2555,33 @@ int sp_head::add_instr(sp_instr *instr)
|
|||
}
|
||||
|
||||
|
||||
bool sp_head::add_instr_jump(THD *thd, sp_pcontext *spcont)
|
||||
{
|
||||
sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(instructions(), spcont);
|
||||
return i == NULL || add_instr(i);
|
||||
}
|
||||
|
||||
|
||||
bool sp_head::add_instr_jump(THD *thd, sp_pcontext *spcont, uint dest)
|
||||
{
|
||||
sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(instructions(),
|
||||
spcont, dest);
|
||||
return i == NULL || add_instr(i);
|
||||
}
|
||||
|
||||
|
||||
bool sp_head::add_instr_jump_forward_with_backpatch(THD *thd,
|
||||
sp_pcontext *spcont)
|
||||
{
|
||||
sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(instructions(), spcont);
|
||||
if (i == NULL || add_instr(i))
|
||||
return true;
|
||||
sp_label *lab= spcont->last_label();
|
||||
push_backpatch(thd, i, lab);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
Do some minimal optimization of the code:
|
||||
-# Mark used instructions
|
||||
|
|
|
@ -337,6 +337,15 @@ public:
|
|||
int
|
||||
add_instr(sp_instr *instr);
|
||||
|
||||
bool
|
||||
add_instr_jump(THD *thd, sp_pcontext *spcont);
|
||||
|
||||
bool
|
||||
add_instr_jump(THD *thd, sp_pcontext *spcont, uint dest);
|
||||
|
||||
bool
|
||||
add_instr_jump_forward_with_backpatch(THD *thd, sp_pcontext *spcont);
|
||||
|
||||
/**
|
||||
Returns true if any substatement in the routine directly
|
||||
(not through another routine) modifies data/changes table.
|
||||
|
|
|
@ -5386,7 +5386,56 @@ sp_head *LEX::make_sp_head(THD *thd, sp_name *name,
|
|||
}
|
||||
|
||||
|
||||
bool LEX::sp_block_with_exceptions_finalize_declarations(THD *thd)
|
||||
{
|
||||
/*
|
||||
[ DECLARE declarations ]
|
||||
BEGIN executable_section
|
||||
[ EXCEPTION exceptions ]
|
||||
END
|
||||
|
||||
We are now at the "BEGIN" keyword.
|
||||
We have collected all declarations, including DECLARE HANDLER directives.
|
||||
But there will be possibly more handlers in the EXCEPTION section.
|
||||
|
||||
Generate a forward jump from the end of the DECLARE section to the
|
||||
beginning of the EXCEPTION section, over the executable section.
|
||||
*/
|
||||
return sphead->add_instr_jump(thd, spcont);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LEX::sp_block_with_exceptions_finalize_executable_section(THD *thd,
|
||||
uint executable_section_ip)
|
||||
{
|
||||
/*
|
||||
We're now at the end of "executable_section" of the block,
|
||||
near the "EXCEPTION" or the "END" keyword.
|
||||
Generate a jump to the END of the block over the EXCEPTION section.
|
||||
*/
|
||||
if (sphead->add_instr_jump_forward_with_backpatch(thd, spcont))
|
||||
return true;
|
||||
/*
|
||||
Set the destination for the jump that we added in
|
||||
sp_block_with_exceptions_finalize_declarations().
|
||||
*/
|
||||
sp_instr *instr= sphead->get_instr(executable_section_ip - 1);
|
||||
instr->backpatch(sphead->instructions(), spcont);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LEX::sp_block_with_exceptions_finalize_exceptions(THD *thd,
|
||||
uint executable_section_ip)
|
||||
{
|
||||
/*
|
||||
Generate a jump from the end of the EXCEPTION code
|
||||
to the executable section.
|
||||
*/
|
||||
return sphead->add_instr_jump(thd, spcont, executable_section_ip);
|
||||
}
|
||||
|
||||
#ifdef MYSQL_SERVER
|
||||
uint binlog_unsafe_map[256];
|
||||
|
|
|
@ -3145,6 +3145,11 @@ public:
|
|||
res->join(b1, b2);
|
||||
return false;
|
||||
}
|
||||
bool sp_block_with_exceptions_finalize_declarations(THD *thd);
|
||||
bool sp_block_with_exceptions_finalize_executable_section(THD *thd,
|
||||
uint executable_section_ip);
|
||||
bool sp_block_with_exceptions_finalize_exceptions(THD *thd,
|
||||
uint executable_section_ip);
|
||||
// Check if "KEY IF NOT EXISTS name" used outside of ALTER context
|
||||
bool check_add_key(DDL_options_st ddl)
|
||||
{
|
||||
|
|
|
@ -207,6 +207,7 @@ static bool push_sp_empty_label(THD *thd)
|
|||
ulong ulong_num;
|
||||
ulonglong ulonglong_number;
|
||||
longlong longlong_number;
|
||||
uint sp_instr_addr;
|
||||
|
||||
/* structs */
|
||||
LEX_STRING lex_str;
|
||||
|
@ -1310,7 +1311,9 @@ END_OF_INPUT
|
|||
%type <num> sp_decl_idents sp_handler_type sp_hcond_list
|
||||
%type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value
|
||||
%type <spblock> sp_decl_body sp_decl_body_list opt_sp_decl_body_list
|
||||
%type <sp_instr_addr> sp_instr_addr
|
||||
%type <num> opt_exception_clause exception_handlers
|
||||
%type <num> sp_block_statements_and_exceptions
|
||||
%type <lex> sp_cursor_stmt
|
||||
%type <spname> sp_name
|
||||
%type <spvar> sp_param_name sp_param_name_and_type
|
||||
|
@ -2329,6 +2332,7 @@ sp_opt_inout:
|
|||
| IN_SYM { $$= sp_variable::MODE_IN; }
|
||||
| OUT_SYM { $$= sp_variable::MODE_OUT; }
|
||||
| INOUT_SYM { $$= sp_variable::MODE_INOUT; }
|
||||
| IN_SYM OUT_SYM { $$= sp_variable::MODE_INOUT; }
|
||||
;
|
||||
|
||||
sp_parenthesized_fdparam_list:
|
||||
|
@ -3452,22 +3456,43 @@ sp_unlabeled_block:
|
|||
}
|
||||
;
|
||||
|
||||
sp_instr_addr:
|
||||
{ $$= Lex->sphead->instructions(); }
|
||||
;
|
||||
|
||||
sp_body:
|
||||
{
|
||||
Lex->sp_block_init(thd);
|
||||
}
|
||||
opt_sp_decl_body_list
|
||||
BEGIN_SYM
|
||||
opt_exception_clause
|
||||
{
|
||||
$2.hndlrs+= $4;
|
||||
if (Lex->sp_block_with_exceptions_finalize_declarations(thd))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
sp_proc_stmts
|
||||
END
|
||||
BEGIN_SYM
|
||||
sp_block_statements_and_exceptions
|
||||
{
|
||||
$2.hndlrs+= $5;
|
||||
if (Lex->sp_block_finalize(thd, $2))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
END
|
||||
;
|
||||
|
||||
sp_block_statements_and_exceptions:
|
||||
sp_instr_addr
|
||||
sp_proc_stmts
|
||||
{
|
||||
if (Lex->sp_block_with_exceptions_finalize_executable_section(thd,
|
||||
$1))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
opt_exception_clause
|
||||
{
|
||||
if (Lex->sp_block_with_exceptions_finalize_exceptions(thd, $1))
|
||||
MYSQL_YYABORT;
|
||||
$$= $4;
|
||||
}
|
||||
;
|
||||
|
||||
sp_unlabeled_block_not_atomic:
|
||||
|
|
Loading…
Add table
Reference in a new issue