summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.org>2016-08-15 16:25:27 +0400
committerAlexander Barkov <bar@mariadb.org>2017-04-05 15:02:42 +0400
commit81ba971d0334dee5dce880ea848300cc5ed45ccb (patch)
tree6b14af9f914e55609960479b7501d41bb2220770
parent0040b0f38060724e95137aa5564feca3da11bc02 (diff)
downloadmariadb-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.result178
-rw-r--r--mysql-test/suite/compat/oracle/r/sp.result27
-rw-r--r--mysql-test/suite/compat/oracle/t/sp-code.test145
-rw-r--r--mysql-test/suite/compat/oracle/t/sp.test29
-rw-r--r--sql/sp_head.cc27
-rw-r--r--sql/sp_head.h9
-rw-r--r--sql/sql_lex.cc49
-rw-r--r--sql/sql_lex.h5
-rw-r--r--sql/sql_yacc_ora.yy33
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;
}
;