1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-07 00:04:31 +03: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:
Alexander Barkov
2016-08-15 16:25:27 +04:00
parent 0040b0f380
commit 81ba971d03
9 changed files with 493 additions and 11 deletions

View 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;

View File

@@ -430,12 +430,13 @@ CREATE PROCEDURE sp1 (p1 IN VARCHAR2(20), p2 OUT VARCHAR2(30))
IS IS
v1 INT; v1 INT;
BEGIN BEGIN
EXCEPTION WHEN NOT FOUND THEN SELECT c1 INTO v1 FROM t1;
p2 := p1;
EXCEPTION
WHEN NOT FOUND THEN
BEGIN BEGIN
p2 := 'def'; p2 := 'def';
END; END;
SELECT c1 INTO v1 FROM t1;
p2 := p1;
END; END;
/ /
CALL sp1('abc', @a); CALL sp1('abc', @a);
@@ -444,3 +445,23 @@ SELECT @a;
def def
DROP PROCEDURE sp1; DROP PROCEDURE sp1;
DROP TABLE t1; 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;

View 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;

View File

@@ -458,12 +458,13 @@ CREATE PROCEDURE sp1 (p1 IN VARCHAR2(20), p2 OUT VARCHAR2(30))
IS IS
v1 INT; v1 INT;
BEGIN BEGIN
EXCEPTION WHEN NOT FOUND THEN SELECT c1 INTO v1 FROM t1;
p2 := p1;
EXCEPTION
WHEN NOT FOUND THEN
BEGIN BEGIN
p2 := 'def'; p2 := 'def';
END; END;
SELECT c1 INTO v1 FROM t1;
p2 := p1;
END; END;
/ /
@@ -474,3 +475,25 @@ SELECT @a;
DROP PROCEDURE sp1; DROP PROCEDURE sp1;
DROP TABLE t1; 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;

View File

@@ -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: Do some minimal optimization of the code:
-# Mark used instructions -# Mark used instructions

View File

@@ -337,6 +337,15 @@ public:
int int
add_instr(sp_instr *instr); 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 Returns true if any substatement in the routine directly
(not through another routine) modifies data/changes table. (not through another routine) modifies data/changes table.

View File

@@ -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 #ifdef MYSQL_SERVER
uint binlog_unsafe_map[256]; uint binlog_unsafe_map[256];

View File

@@ -3145,6 +3145,11 @@ public:
res->join(b1, b2); res->join(b1, b2);
return false; 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 // Check if "KEY IF NOT EXISTS name" used outside of ALTER context
bool check_add_key(DDL_options_st ddl) bool check_add_key(DDL_options_st ddl)
{ {

View File

@@ -207,6 +207,7 @@ static bool push_sp_empty_label(THD *thd)
ulong ulong_num; ulong ulong_num;
ulonglong ulonglong_number; ulonglong ulonglong_number;
longlong longlong_number; longlong longlong_number;
uint sp_instr_addr;
/* structs */ /* structs */
LEX_STRING lex_str; LEX_STRING lex_str;
@@ -1310,7 +1311,9 @@ END_OF_INPUT
%type <num> sp_decl_idents sp_handler_type sp_hcond_list %type <num> sp_decl_idents sp_handler_type sp_hcond_list
%type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value %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 <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> opt_exception_clause exception_handlers
%type <num> sp_block_statements_and_exceptions
%type <lex> sp_cursor_stmt %type <lex> sp_cursor_stmt
%type <spname> sp_name %type <spname> sp_name
%type <spvar> sp_param_name sp_param_name_and_type %type <spvar> sp_param_name sp_param_name_and_type
@@ -2329,6 +2332,7 @@ sp_opt_inout:
| IN_SYM { $$= sp_variable::MODE_IN; } | IN_SYM { $$= sp_variable::MODE_IN; }
| OUT_SYM { $$= sp_variable::MODE_OUT; } | OUT_SYM { $$= sp_variable::MODE_OUT; }
| INOUT_SYM { $$= sp_variable::MODE_INOUT; } | INOUT_SYM { $$= sp_variable::MODE_INOUT; }
| IN_SYM OUT_SYM { $$= sp_variable::MODE_INOUT; }
; ;
sp_parenthesized_fdparam_list: sp_parenthesized_fdparam_list:
@@ -3452,22 +3456,43 @@ sp_unlabeled_block:
} }
; ;
sp_instr_addr:
{ $$= Lex->sphead->instructions(); }
;
sp_body: sp_body:
{ {
Lex->sp_block_init(thd); Lex->sp_block_init(thd);
} }
opt_sp_decl_body_list 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 BEGIN_SYM
END sp_block_statements_and_exceptions
{ {
$2.hndlrs+= $5;
if (Lex->sp_block_finalize(thd, $2)) if (Lex->sp_block_finalize(thd, $2))
MYSQL_YYABORT; 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: sp_unlabeled_block_not_atomic: