Commit 81ba971d authored by Alexander Barkov's avatar Alexander Barkov

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)
parent 0040b0f3
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)) ...@@ -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; ...@@ -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;
-- 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)) ...@@ -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; ...@@ -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;
...@@ -2555,6 +2555,33 @@ int sp_head::add_instr(sp_instr *instr) ...@@ -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
......
...@@ -337,6 +337,15 @@ class sp_head :private Query_arena, ...@@ -337,6 +337,15 @@ class sp_head :private Query_arena,
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.
......
...@@ -5386,8 +5386,57 @@ sp_head *LEX::make_sp_head(THD *thd, sp_name *name, ...@@ -5386,8 +5386,57 @@ 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];
......
...@@ -3145,6 +3145,11 @@ struct LEX: public Query_tables_list ...@@ -3145,6 +3145,11 @@ struct LEX: public Query_tables_list
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)
{ {
......
...@@ -207,6 +207,7 @@ static bool push_sp_empty_label(THD *thd) ...@@ -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 ...@@ -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: ...@@ -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,21 +3456,42 @@ sp_unlabeled_block: ...@@ -3452,21 +3456,42 @@ 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
{
if (Lex->sp_block_with_exceptions_finalize_declarations(thd))
MYSQL_YYABORT;
}
BEGIN_SYM BEGIN_SYM
opt_exception_clause sp_block_statements_and_exceptions
{ {
$2.hndlrs+= $4; $2.hndlrs+= $5;
if (Lex->sp_block_finalize(thd, $2))
MYSQL_YYABORT;
} }
sp_proc_stmts
END END
;
sp_block_statements_and_exceptions:
sp_instr_addr
sp_proc_stmts
{ {
if (Lex->sp_block_finalize(thd, $2)) 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; MYSQL_YYABORT;
$$= $4;
} }
; ;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment