diff --git a/mysql-test/suite/compat/oracle/r/sp-code.result b/mysql-test/suite/compat/oracle/r/sp-code.result index b3605b19db49e..9ac733274e1c4 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 13c4e246c0b36..0d3c21eee77c9 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 23a74c0cae8be..09f3a8aa30c9d 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 8fa242a770e09..4c4f2d4fe3bec 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 07f411e39a0f4..572db170b77bc 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 21c7e8797b651..dc624de8d8045 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2630,6 +2630,9 @@ struct LEX: public Query_tables_list 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 @@ struct LEX: public Query_tables_list 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 94cfaa7107c1a..a863db6ae383d 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,15 +2964,20 @@ sp_proc_stmt_return: } ; +opt_sp_proc_stmt_exit_when_clause: + /* Empty */ { $$= NULL; } + | WHEN_SYM expr { $$= $2; } + ; + sp_proc_stmt_exit: - EXIT_SYM + EXIT_SYM opt_sp_proc_stmt_exit_when_clause { - if (Lex->sp_exit_statement(thd)) + if (Lex->sp_exit_statement(thd, $2)) MYSQL_YYABORT; } - | EXIT_SYM label_ident + | EXIT_SYM label_ident opt_sp_proc_stmt_exit_when_clause { - if (Lex->sp_exit_statement(thd, $2)) + if (Lex->sp_exit_statement(thd, $2, $3)) MYSQL_YYABORT; } ;