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))
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;
-- 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 @@ class sp_head :private Query_arena,
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,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
uint binlog_unsafe_map[256];
......
......@@ -3145,6 +3145,11 @@ struct LEX: public Query_tables_list
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,21 +3456,42 @@ sp_unlabeled_block:
}
;
sp_instr_addr:
{ $$= Lex->sphead->instructions(); }
;
sp_body:
{
Lex->sp_block_init(thd);
}
opt_sp_decl_body_list
{
if (Lex->sp_block_with_exceptions_finalize_declarations(thd))
MYSQL_YYABORT;
}
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
;
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;
$$= $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