From 8feb984211f559ad8bf2f231544cf8852b23632d Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Wed, 17 Aug 2016 10:33:31 +0400 Subject: [PATCH] MDEV-10411 Providing compatibility for basic PL/SQL constructs Part 5: EXIT statement Adding unconditional EXIT statement: EXIT [ label ] Conditional EXIT statements with WHERE clause will be added in a separate patch. --- .../suite/compat/oracle/r/sp-code.result | 110 ++++++++++++++++++ mysql-test/suite/compat/oracle/r/sp.result | 92 +++++++++++++++ mysql-test/suite/compat/oracle/t/sp-code.test | 78 +++++++++++++ mysql-test/suite/compat/oracle/t/sp.test | 107 +++++++++++++++++ sql/sp_pcontext.cc | 22 +++- sql/sp_pcontext.h | 10 +- sql/sql_lex.cc | 40 ++++++- sql/sql_lex.h | 3 + sql/sql_yacc_ora.yy | 15 +++ 9 files changed, 469 insertions(+), 8 deletions(-) diff --git a/mysql-test/suite/compat/oracle/r/sp-code.result b/mysql-test/suite/compat/oracle/r/sp-code.result index 8e6d6d940e4..b3605b19db4 100644 --- a/mysql-test/suite/compat/oracle/r/sp-code.result +++ b/mysql-test/suite/compat/oracle/r/sp-code.result @@ -396,3 +396,113 @@ SELECT @v; @v 324 DROP PROCEDURE p1; +# +# Testing EXIT statement +# +CREATE FUNCTION f1 RETURN INT +IS +i INT := 0; +BEGIN +LOOP +i:= i + 1; +IF i >= 5 THEN +EXIT; +END IF; +END LOOP; +RETURN i; +END; +/ +SHOW FUNCTION CODE f1; +Pos Instruction +0 set i@0 0 +1 set i@0 (i@0 + 1) +2 jump_if_not 1(1) (i@0 >= 5) +3 jump 4 +4 freturn 3 i@0 +SELECT f1() FROM DUAL; +f1() +5 +DROP FUNCTION f1; +CREATE FUNCTION f1 RETURN INT +IS +i INT := 0; +BEGIN +LOOP +BEGIN +i:= i + 1; +IF i >= 5 THEN +EXIT; +END IF; +EXCEPTION +WHEN OTHERS THEN i:= 1000; +END; +END LOOP; +RETURN i; +END; +/ +SHOW FUNCTION CODE f1; +Pos Instruction +0 set i@0 0 +1 jump 5 +2 set i@0 (i@0 + 1) +3 jump_if_not 8(8) (i@0 >= 5) +4 jump 10 +5 hpush_jump 2 1 EXIT +6 set i@0 1000 +7 hreturn 0 8 +8 hpop 1 +9 jump 5 +10 freturn 3 i@0 +SELECT f1() FROM DUAL; +f1() +5 +DROP FUNCTION f1; +CREATE PROCEDURE p1(a IN OUT INT) +IS +i INT := 0; +BEGIN +LOOP +LOOP +BEGIN +i:= i + 1; +IF i >=5 THEN +EXIT; +END IF; +EXCEPTION +WHEN OTHERS THEN a:=1000; +END; +END LOOP; +i:= i + 100; +EXIT; +END LOOP; +a:= i; +EXCEPTION +WHEN OTHERS THEN a:=11; +END; +/ +SHOW PROCEDURE CODE p1; +Pos Instruction +0 set i@1 0 +1 jump 14 +2 set i@1 (i@1 + 1) +3 jump_if_not 8(8) (i@1 >= 5) +4 jump 10 +5 hpush_jump 2 2 EXIT +6 set a@0 1000 +7 hreturn 0 8 +8 hpop 1 +9 jump 5 +10 set i@1 (i@1 + 100) +11 jump 12 +12 set a@0 i@1 +13 jump 17 +14 hpush_jump 5 2 EXIT +15 set a@0 11 +16 hreturn 0 17 +17 hpop 1 +set @v= 10; +CALL p1(@v); +SELECT @v; +@v +105 +DROP PROCEDURE p1; diff --git a/mysql-test/suite/compat/oracle/r/sp.result b/mysql-test/suite/compat/oracle/r/sp.result index d0fb26b04ae..13c4e246c0b 100644 --- a/mysql-test/suite/compat/oracle/r/sp.result +++ b/mysql-test/suite/compat/oracle/r/sp.result @@ -493,3 +493,95 @@ SELECT @v; @v 10 DROP PROCEDURE sp1; +# +# Testing EXIT statement +# +CREATE FUNCTION f1 RETURN INT +IS +i INT := 0; +BEGIN +EXIT; +END; +/ +ERROR 42000: EXIT with no matching label: +CREATE FUNCTION f1 RETURN INT +IS +i INT := 0; +BEGIN +<> +BEGIN +<> +LOOP +EXIT label1; +END LOOP; +END; +END; +/ +ERROR 42000: EXIT with no matching label: label1 +CREATE FUNCTION f1 RETURN INT +IS +i INT := 0; +BEGIN +LOOP +LOOP +i:= i + 1; +IF i >= 5 THEN +EXIT; +END IF; +END LOOP; +i:= i + 100; +EXIT; +END LOOP; +RETURN i; +END; +/ +SELECT f1() FROM DUAL; +f1() +105 +DROP FUNCTION f1; +CREATE FUNCTION f1 RETURN INT +IS +i INT := 0; +BEGIN +<> +LOOP +<> +LOOP +i:= i + 1; +IF i >= 5 THEN +EXIT label2; +END IF; +END LOOP; +i:= i + 100; +EXIT; +END LOOP; +RETURN i; +END; +/ +SELECT f1() FROM DUAL; +f1() +105 +DROP FUNCTION f1; +CREATE FUNCTION f1 RETURN INT +IS +i INT := 0; +BEGIN +<> +LOOP +<> +LOOP +i:= i + 1; +IF i >= 5 THEN +EXIT label1; +END IF; +END LOOP; +i:= i + 100; +EXIT; +END LOOP; +RETURN i; +END; +/ +SELECT f1() FROM DUAL; +f1() +5 +DROP FUNCTION f1; diff --git a/mysql-test/suite/compat/oracle/t/sp-code.test b/mysql-test/suite/compat/oracle/t/sp-code.test index dffd1a19fd2..23a74c0cae8 100644 --- a/mysql-test/suite/compat/oracle/t/sp-code.test +++ b/mysql-test/suite/compat/oracle/t/sp-code.test @@ -319,3 +319,81 @@ SET @v=10; CALL p1(@v); SELECT @v; DROP PROCEDURE p1; + +--echo # +--echo # Testing EXIT statement +--echo # + +DELIMITER /; +CREATE FUNCTION f1 RETURN INT +IS + i INT := 0; +BEGIN + LOOP + i:= i + 1; + IF i >= 5 THEN + EXIT; + END IF; + END LOOP; + RETURN i; +END; +/ +DELIMITER ;/ +SHOW FUNCTION CODE f1; +SELECT f1() FROM DUAL; +DROP FUNCTION f1; + +DELIMITER /; +CREATE FUNCTION f1 RETURN INT +IS + i INT := 0; +BEGIN + LOOP + BEGIN + i:= i + 1; + IF i >= 5 THEN + EXIT; + END IF; + EXCEPTION + WHEN OTHERS THEN i:= 1000; + END; + END LOOP; + RETURN i; +END; +/ +DELIMITER ;/ +SHOW FUNCTION CODE f1; +SELECT f1() FROM DUAL; +DROP FUNCTION f1; + + +DELIMITER /; +CREATE PROCEDURE p1(a IN OUT INT) +IS + i INT := 0; +BEGIN + LOOP + LOOP + BEGIN + i:= i + 1; + IF i >=5 THEN + EXIT; + END IF; + EXCEPTION + WHEN OTHERS THEN a:=1000; + END; + END LOOP; + i:= i + 100; + EXIT; + END LOOP; + a:= i; +EXCEPTION + WHEN OTHERS THEN a:=11; +END; +/ +DELIMITER ;/ +SHOW PROCEDURE CODE p1; +set @v= 10; +CALL p1(@v); +SELECT @v; +DROP PROCEDURE p1; diff --git a/mysql-test/suite/compat/oracle/t/sp.test b/mysql-test/suite/compat/oracle/t/sp.test index 6b47ab4f7d6..8fa242a770e 100644 --- a/mysql-test/suite/compat/oracle/t/sp.test +++ b/mysql-test/suite/compat/oracle/t/sp.test @@ -525,3 +525,110 @@ SET @v=10; CALL sp1(@v, 30002); SELECT @v; DROP PROCEDURE sp1; + + +--echo # +--echo # Testing EXIT statement +--echo # + +DELIMITER /; +--error ER_SP_LILABEL_MISMATCH +CREATE FUNCTION f1 RETURN INT +IS + i INT := 0; +BEGIN + EXIT; +END; +/ +DELIMITER ;/ + + +DELIMITER /; +--error ER_SP_LILABEL_MISMATCH +CREATE FUNCTION f1 RETURN INT +IS + i INT := 0; +BEGIN + <> + BEGIN + <> + LOOP + EXIT label1; + END LOOP; + END; +END; +/ +DELIMITER ;/ + + +DELIMITER /; +CREATE FUNCTION f1 RETURN INT +IS + i INT := 0; +BEGIN + LOOP + LOOP + i:= i + 1; + IF i >= 5 THEN + EXIT; + END IF; + END LOOP; + i:= i + 100; + EXIT; + END LOOP; + RETURN i; +END; +/ +DELIMITER ;/ +SELECT f1() FROM DUAL; +DROP FUNCTION f1; + + +DELIMITER /; +CREATE FUNCTION f1 RETURN INT +IS + i INT := 0; +BEGIN + <> + LOOP + <> + LOOP + i:= i + 1; + IF i >= 5 THEN + EXIT label2; + END IF; + END LOOP; + i:= i + 100; + EXIT; + END LOOP; + RETURN i; +END; +/ +DELIMITER ;/ +SELECT f1() FROM DUAL; +DROP FUNCTION f1; + + +DELIMITER /; +CREATE FUNCTION f1 RETURN INT +IS + i INT := 0; +BEGIN + <> + LOOP + <> + LOOP + i:= i + 1; + IF i >= 5 THEN + EXIT label1; + END IF; + END LOOP; + i:= i + 100; + EXIT; + END LOOP; + RETURN i; +END; +/ +DELIMITER ;/ +SELECT f1() FROM DUAL; +DROP FUNCTION f1; diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index e8748ffb852..db2d36978d4 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -197,10 +197,11 @@ sp_variable *sp_pcontext::add_variable(THD *thd, LEX_STRING name) } -sp_label *sp_pcontext::push_label(THD *thd, LEX_STRING name, uint ip) +sp_label *sp_pcontext::push_label(THD *thd, LEX_STRING name, uint ip, + sp_label::enum_type type) { sp_label *label= - new (thd->mem_root) sp_label(name, ip, sp_label::IMPLICIT, this); + new (thd->mem_root) sp_label(name, ip, type, this); if (!label) return NULL; @@ -236,6 +237,23 @@ sp_label *sp_pcontext::find_label(const LEX_STRING name) } +sp_label *sp_pcontext::find_label_current_loop_start() +{ + List_iterator_fast li(m_labels); + sp_label *lab; + + while ((lab= li++)) + { + if (lab->type == sp_label::ITERATION) + return lab; + } + // See a comment in sp_pcontext::find_label() + return (m_parent && (m_scope == REGULAR_SCOPE)) ? + m_parent->find_label_current_loop_start() : + NULL; +} + + bool sp_pcontext::add_condition(THD *thd, LEX_STRING name, sp_condition_value *value) diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index cbebc157dbb..6cbd98e89a8 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -402,10 +402,18 @@ public: // Labels. ///////////////////////////////////////////////////////////////////////// - sp_label *push_label(THD *thd, const LEX_STRING name, uint ip); + sp_label *push_label(THD *thd, const LEX_STRING name, uint ip, + sp_label::enum_type type); + + sp_label *push_label(THD *thd, const LEX_STRING name, uint ip) + { + return push_label(thd, name, ip, sp_label::IMPLICIT); + } sp_label *find_label(const LEX_STRING name); + sp_label *find_label_current_loop_start(); + sp_label *last_label() { sp_label *label= m_labels.head(); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 65e89ec7c58..07f411e39a0 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -5312,8 +5312,7 @@ bool LEX::sp_handler_declaration_finalize(THD *thd, int type) void LEX::sp_block_init(THD *thd, const LEX_STRING label) { - sp_label *lab= spcont->push_label(thd, label, sphead->instructions()); - lab->type= sp_label::BEGIN; + spcont->push_label(thd, label, sphead->instructions(), sp_label::BEGIN); spcont= spcont->push_context(thd, sp_pcontext::REGULAR_SCOPE); } @@ -5478,7 +5477,12 @@ bool LEX::sp_leave_statement(THD *thd, const LEX_STRING label_name) my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", label_name.str); return true; } + return sp_exit_block(thd, lab); +} + +bool LEX::sp_exit_block(THD *thd, sp_label *lab) +{ /* When jumping to a BEGIN-END block end, the target jump points to the block hpop/cpop cleanup instructions, @@ -5493,6 +5497,31 @@ bool LEX::sp_leave_statement(THD *thd, const LEX_STRING label_name) } +bool LEX::sp_exit_statement(THD *thd) +{ + sp_label *lab= spcont->find_label_current_loop_start(); + if (!lab) + { + my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "EXIT", ""); + return true; + } + DBUG_ASSERT(lab->type == sp_label::ITERATION); + return sp_exit_block(thd, lab); +} + + +bool LEX::sp_exit_statement(THD *thd, const LEX_STRING label_name) +{ + sp_label *lab= spcont->find_label(label_name); + if (!lab || lab->type != sp_label::ITERATION) + { + my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "EXIT", label_name); + return true; + } + return sp_exit_block(thd, lab); +} + + bool LEX::sp_iterate_statement(THD *thd, const LEX_STRING label_name) { sp_label *lab= spcont->find_label(label_name); @@ -5527,8 +5556,8 @@ bool LEX::sp_push_loop_label(THD *thd, const LEX_STRING label_name) my_error(ER_SP_LABEL_REDEFINE, MYF(0), label_name.str); return true; } - lab= spcont->push_label(thd, label_name, sphead->instructions()); - lab->type= sp_label::ITERATION; + spcont->push_label(thd, label_name, sphead->instructions(), + sp_label::ITERATION); return false; } @@ -5538,7 +5567,8 @@ bool LEX::sp_push_loop_empty_label(THD *thd) if (maybe_start_compound_statement(thd)) return true; /* Unlabeled controls get an empty label. */ - spcont->push_label(thd, empty_lex_str, sphead->instructions()); + spcont->push_label(thd, empty_lex_str, sphead->instructions(), + sp_label::ITERATION); return false; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 14094da8cbf..21c7e8797b6 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3152,6 +3152,9 @@ public: uint executable_section_ip, uint exception_count); bool sp_change_context(THD *thd, const sp_pcontext *ctx, bool exclusive); + bool sp_exit_block(THD *thd, sp_label *lab); + bool sp_exit_statement(THD *thd); + bool sp_exit_statement(THD *thd, const LEX_STRING label_name); bool sp_leave_statement(THD *thd, const LEX_STRING label_name); bool sp_iterate_statement(THD *thd, const LEX_STRING label_name); diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 028513c4fe6..94cfaa7107c 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -1260,6 +1260,7 @@ END_OF_INPUT %type sp_proc_stmt_if %type sp_labeled_control sp_unlabeled_control %type sp_labeled_block sp_unlabeled_block sp_unlabeled_block_not_atomic +%type sp_proc_stmt_exit %type sp_proc_stmt_leave %type sp_proc_stmt_iterate %type sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close @@ -2858,6 +2859,7 @@ sp_proc_stmt_in_returns_clause: sp_proc_stmt: sp_proc_stmt_in_returns_clause | sp_proc_stmt_statement + | sp_proc_stmt_exit | sp_proc_stmt_leave | sp_proc_stmt_iterate | sp_proc_stmt_open @@ -2961,6 +2963,19 @@ sp_proc_stmt_return: } ; +sp_proc_stmt_exit: + EXIT_SYM + { + if (Lex->sp_exit_statement(thd)) + MYSQL_YYABORT; + } + | EXIT_SYM label_ident + { + if (Lex->sp_exit_statement(thd, $2)) + MYSQL_YYABORT; + } + ; + sp_proc_stmt_leave: LEAVE_SYM label_ident {