diff --git a/mysql-test/suite/compat/oracle/r/sp-code.result b/mysql-test/suite/compat/oracle/r/sp-code.result index b3605b19db4..9ac733274e1 100644 --- a/mysql-test/suite/compat/oracle/r/sp-code.result +++ b/mysql-test/suite/compat/oracle/r/sp-code.result @@ -428,6 +428,28 @@ IS i INT := 0; BEGIN LOOP +i:= i + 1; +EXIT WHEN i >=5; +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(0) (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 diff --git a/mysql-test/suite/compat/oracle/r/sp.result b/mysql-test/suite/compat/oracle/r/sp.result index 13c4e246c0b..0d3c21eee77 100644 --- a/mysql-test/suite/compat/oracle/r/sp.result +++ b/mysql-test/suite/compat/oracle/r/sp.result @@ -585,3 +585,60 @@ SELECT f1() FROM DUAL; f1() 5 DROP FUNCTION f1; +CREATE FUNCTION f1 RETURN INT +IS +i INT := 0; +BEGIN +LOOP +i:= i + 1; +EXIT WHEN i >=5; +END LOOP; +RETURN i; +END; +/ +SELECT f1() FROM DUAL; +f1() +5 +DROP FUNCTION f1; +CREATE FUNCTION f1 RETURN INT +IS +i INT := 0; +BEGIN +<> +LOOP +<> +LOOP +i:= i + 1; +EXIT label2 WHEN i >= 5; +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; +EXIT label1 WHEN i >= 5; +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 23a74c0cae8..09f3a8aa30c 100644 --- a/mysql-test/suite/compat/oracle/t/sp-code.test +++ b/mysql-test/suite/compat/oracle/t/sp-code.test @@ -343,6 +343,25 @@ SHOW FUNCTION CODE f1; SELECT f1() FROM DUAL; DROP FUNCTION f1; + +DELIMITER /; +CREATE FUNCTION f1 RETURN INT +IS + i INT := 0; +BEGIN + LOOP + i:= i + 1; + EXIT WHEN i >=5; + END LOOP; + RETURN i; +END; +/ +DELIMITER ;/ +SHOW FUNCTION CODE f1; +SELECT f1() FROM DUAL; +DROP FUNCTION f1; + + DELIMITER /; CREATE FUNCTION f1 RETURN INT IS diff --git a/mysql-test/suite/compat/oracle/t/sp.test b/mysql-test/suite/compat/oracle/t/sp.test index 8fa242a770e..4c4f2d4fe3b 100644 --- a/mysql-test/suite/compat/oracle/t/sp.test +++ b/mysql-test/suite/compat/oracle/t/sp.test @@ -632,3 +632,66 @@ END; DELIMITER ;/ SELECT f1() FROM DUAL; DROP FUNCTION f1; + + +DELIMITER /; +CREATE FUNCTION f1 RETURN INT +IS + i INT := 0; +BEGIN + LOOP + i:= i + 1; + EXIT WHEN i >=5; + 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; + EXIT label2 WHEN i >= 5; + 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; + EXIT label1 WHEN i >= 5; + END LOOP; + i:= i + 100; + EXIT; + END LOOP; + RETURN i; +END; +/ +DELIMITER ;/ +SELECT f1() FROM DUAL; +DROP FUNCTION f1; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 07f411e39a0..572db170b77 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -5477,7 +5477,7 @@ 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); + return sp_exit_block(thd, lab, NULL); } @@ -5497,7 +5497,29 @@ bool LEX::sp_exit_block(THD *thd, sp_label *lab) } -bool LEX::sp_exit_statement(THD *thd) +bool LEX::sp_exit_block(THD *thd, sp_label *lab, Item *when) +{ + if (!when) + return sp_exit_block(thd, lab); + + sphead->reset_lex(thd); // This changes thd->lex + DBUG_ASSERT(sphead == thd->lex->sphead); + DBUG_ASSERT(spcont == thd->lex->spcont); + sp_instr_jump_if_not *i= new (thd->mem_root) + sp_instr_jump_if_not(sphead->instructions(), + spcont, + when, thd->lex); + if (i == NULL || + sphead->add_instr(i) || + sphead->restore_lex(thd) || + sp_exit_block(thd, lab)) + return true; + i->backpatch(sphead->instructions(), spcont); + return false; +} + + +bool LEX::sp_exit_statement(THD *thd, Item *item) { sp_label *lab= spcont->find_label_current_loop_start(); if (!lab) @@ -5506,11 +5528,11 @@ bool LEX::sp_exit_statement(THD *thd) return true; } DBUG_ASSERT(lab->type == sp_label::ITERATION); - return sp_exit_block(thd, lab); + return sp_exit_block(thd, lab, item); } -bool LEX::sp_exit_statement(THD *thd, const LEX_STRING label_name) +bool LEX::sp_exit_statement(THD *thd, const LEX_STRING label_name, Item *item) { sp_label *lab= spcont->find_label(label_name); if (!lab || lab->type != sp_label::ITERATION) @@ -5518,7 +5540,7 @@ bool LEX::sp_exit_statement(THD *thd, const LEX_STRING label_name) my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "EXIT", label_name); return true; } - return sp_exit_block(thd, lab); + return sp_exit_block(thd, lab, item); } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 21c7e8797b6..dc624de8d80 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2630,6 +2630,9 @@ private: void parse_error(); bool sp_block_finalize(THD *thd, const Lex_spblock_st spblock, class sp_label **splabel); + bool sp_change_context(THD *thd, const sp_pcontext *ctx, bool exclusive); + bool sp_exit_block(THD *thd, sp_label *lab); + bool sp_exit_block(THD *thd, sp_label *lab, Item *when); public: inline bool is_arena_for_set_stmt() {return arena_for_set_stmt != 0;} bool set_arena_for_set_stmt(Query_arena *backup); @@ -3151,10 +3154,8 @@ public: bool sp_block_with_exceptions_finalize_exceptions(THD *thd, 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_exit_statement(THD *thd, Item *when); + bool sp_exit_statement(THD *thd, const LEX_STRING label_name, Item *item); 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 94cfaa7107c..a863db6ae38 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -1094,6 +1094,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); signal_allowed_expr simple_target_specification condition_number + opt_sp_proc_stmt_exit_when_clause %type param_marker @@ -2963,17 +2964,22 @@ sp_proc_stmt_return: } ; +opt_sp_proc_stmt_exit_when_clause: + /* Empty */ { $$= NULL; } + | WHEN_SYM expr { $$= $2; } + ; + sp_proc_stmt_exit: - EXIT_SYM - { - if (Lex->sp_exit_statement(thd)) - MYSQL_YYABORT; - } - | EXIT_SYM label_ident + EXIT_SYM opt_sp_proc_stmt_exit_when_clause { if (Lex->sp_exit_statement(thd, $2)) MYSQL_YYABORT; } + | EXIT_SYM label_ident opt_sp_proc_stmt_exit_when_clause + { + if (Lex->sp_exit_statement(thd, $2, $3)) + MYSQL_YYABORT; + } ; sp_proc_stmt_leave: