diff options
author | Alexander Barkov <bar@mariadb.org> | 2016-08-15 16:25:27 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.org> | 2017-04-05 15:02:42 +0400 |
commit | 81ba971d0334dee5dce880ea848300cc5ed45ccb (patch) | |
tree | 6b14af9f914e55609960479b7501d41bb2220770 | |
parent | 0040b0f38060724e95137aa5564feca3da11bc02 (diff) | |
download | mariadb-git-81ba971d0334dee5dce880ea848300cc5ed45ccb.tar.gz |
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)
-rw-r--r-- | mysql-test/suite/compat/oracle/r/sp-code.result | 178 | ||||
-rw-r--r-- | mysql-test/suite/compat/oracle/r/sp.result | 27 | ||||
-rw-r--r-- | mysql-test/suite/compat/oracle/t/sp-code.test | 145 | ||||
-rw-r--r-- | mysql-test/suite/compat/oracle/t/sp.test | 29 | ||||
-rw-r--r-- | sql/sp_head.cc | 27 | ||||
-rw-r--r-- | sql/sp_head.h | 9 | ||||
-rw-r--r-- | sql/sql_lex.cc | 49 | ||||
-rw-r--r-- | sql/sql_lex.h | 5 | ||||
-rw-r--r-- | sql/sql_yacc_ora.yy | 33 |
9 files changed, 492 insertions, 10 deletions
diff --git a/mysql-test/suite/compat/oracle/r/sp-code.result b/mysql-test/suite/compat/oracle/r/sp-code.result new file mode 100644 index 00000000000..56339e636c3 --- /dev/null +++ b/mysql-test/suite/compat/oracle/r/sp-code.result @@ -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; diff --git a/mysql-test/suite/compat/oracle/r/sp.result b/mysql-test/suite/compat/oracle/r/sp.result index 94e08d2fcd7..42416b6ca33 100644 --- a/mysql-test/suite/compat/oracle/r/sp.result +++ b/mysql-test/suite/compat/oracle/r/sp.result @@ -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; diff --git a/mysql-test/suite/compat/oracle/t/sp-code.test b/mysql-test/suite/compat/oracle/t/sp-code.test new file mode 100644 index 00000000000..229fc053c7e --- /dev/null +++ b/mysql-test/suite/compat/oracle/t/sp-code.test @@ -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; diff --git a/mysql-test/suite/compat/oracle/t/sp.test b/mysql-test/suite/compat/oracle/t/sp.test index dc06f7c359b..a838a74d2da 100644 --- a/mysql-test/suite/compat/oracle/t/sp.test +++ b/mysql-test/suite/compat/oracle/t/sp.test @@ -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; diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 3126b3cc46d..7951ef538bc 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -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 diff --git a/sql/sp_head.h b/sql/sp_head.h index e5bc3ca9263..eb2f47ae2d8 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -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. diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 076f389f3a3..9c66aea04d9 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -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]; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 5d0b6a8b9e3..538cec38d43 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -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) { diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 58efd058d33..99e7ee41827 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -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; } ; |