diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 1ecbc1b2e5f54..d02749db03020 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -2571,12 +2571,12 @@ bool sp_head::add_instr_jump(THD *thd, sp_pcontext *spcont, uint dest) bool sp_head::add_instr_jump_forward_with_backpatch(THD *thd, - sp_pcontext *spcont) + sp_pcontext *spcont, + sp_label *lab) { 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; } diff --git a/sql/sp_head.h b/sql/sp_head.h index b4c3cdaac6cf3..3d55d9e911dc2 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -344,7 +344,15 @@ class sp_head :private Query_arena, add_instr_jump(THD *thd, sp_pcontext *spcont, uint dest); bool - add_instr_jump_forward_with_backpatch(THD *thd, sp_pcontext *spcont); + add_instr_jump_forward_with_backpatch(THD *thd, sp_pcontext *spcont, + sp_label *lab); + bool + add_instr_jump_forward_with_backpatch(THD *thd, sp_pcontext *spcont) + { + return add_instr_jump_forward_with_backpatch(thd, spcont, + spcont->last_label()); + } + /** Returns true if any substatement in the routine directly diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index fec4e309d3f75..4b346836f04d3 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -5449,6 +5449,63 @@ LEX::sp_block_with_exceptions_finalize_exceptions(THD *thd, return sphead->add_instr_jump(thd, spcont, executable_section_ip); } + +bool LEX::sp_change_context(THD *thd, const sp_pcontext *ctx, bool exclusive) +{ + uint n; + uint ip= sphead->instructions(); + if ((n= spcont->diff_handlers(ctx, exclusive))) + { + sp_instr_hpop *hpop= new (thd->mem_root) sp_instr_hpop(ip++, spcont, n); + if (hpop == NULL || sphead->add_instr(hpop)) + return true; + } + if ((n= spcont->diff_cursors(ctx, exclusive))) + { + sp_instr_cpop *cpop= new (thd->mem_root) sp_instr_cpop(ip++, spcont, n); + if (cpop == NULL || sphead->add_instr(cpop)) + return true; + } + return false; +} + + +bool LEX::sp_leave_statement(THD *thd, const LEX_STRING label_name) +{ + sp_label *lab= spcont->find_label(label_name); + if (!lab) + { + my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", label_name.str); + return true; + } + + /* + When jumping to a BEGIN-END block end, the target jump + points to the block hpop/cpop cleanup instructions, + so we should exclude the block context here. + When jumping to something else (i.e., SP_LAB_ITER), + there are no hpop/cpop at the jump destination, + so we should include the block context here for cleanup. + */ + bool exclusive= (lab->type == sp_label::BEGIN); + return sp_change_context(thd, lab->ctx, exclusive) || + sphead->add_instr_jump_forward_with_backpatch(thd, spcont, lab); +} + + +bool LEX::sp_iterate_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), "ITERATE", label_name.str); + return true; + } + return sp_change_context(thd, lab->ctx, false) || + sphead->add_instr_jump(thd, spcont, lab->ip); /* Jump back */ +} + + #ifdef MYSQL_SERVER uint binlog_unsafe_map[256]; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index ac76dddffcb9c..c2d1ef83959e1 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3151,6 +3151,10 @@ 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_leave_statement(THD *thd, const LEX_STRING label_name); + bool sp_iterate_statement(THD *thd, const LEX_STRING label_name); + // 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.yy b/sql/sql_yacc.yy index 1d637276f3d20..53ab7eb14ac47 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -3600,90 +3600,15 @@ sp_proc_stmt_return: sp_proc_stmt_leave: LEAVE_SYM label_ident { - LEX *lex= Lex; - sp_head *sp = lex->sphead; - sp_pcontext *ctx= lex->spcont; - sp_label *lab= ctx->find_label($2); - - if (! lab) - my_yyabort_error((ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", $2.str)); - - sp_instr_jump *i; - uint ip= sp->instructions(); - uint n; - /* - When jumping to a BEGIN-END block end, the target jump - points to the block hpop/cpop cleanup instructions, - so we should exclude the block context here. - When jumping to something else (i.e., SP_LAB_ITER), - there are no hpop/cpop at the jump destination, - so we should include the block context here for cleanup. - */ - bool exclusive= (lab->type == sp_label::BEGIN); - - n= ctx->diff_handlers(lab->ctx, exclusive); - if (n) - { - sp_instr_hpop *hpop= new (thd->mem_root) - sp_instr_hpop(ip++, ctx, n); - if (hpop == NULL) - MYSQL_YYABORT; - sp->add_instr(hpop); - } - n= ctx->diff_cursors(lab->ctx, exclusive); - if (n) - { - sp_instr_cpop *cpop= new (thd->mem_root) - sp_instr_cpop(ip++, ctx, n); - if (cpop == NULL) - MYSQL_YYABORT; - sp->add_instr(cpop); - } - i= new (thd->mem_root) sp_instr_jump(ip, ctx); - if (i == NULL) + if (Lex->sp_leave_statement(thd, $2)) MYSQL_YYABORT; - sp->push_backpatch(thd, i, lab); /* Jumping forward */ - sp->add_instr(i); } ; sp_proc_stmt_iterate: ITERATE_SYM label_ident { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - sp_pcontext *ctx= lex->spcont; - sp_label *lab= ctx->find_label($2); - - if (! lab || lab->type != sp_label::ITERATION) - my_yyabort_error((ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", $2.str)); - - sp_instr_jump *i; - uint ip= sp->instructions(); - uint n; - - n= ctx->diff_handlers(lab->ctx, FALSE); /* Inclusive the dest. */ - if (n) - { - sp_instr_hpop *hpop= new (thd->mem_root) - sp_instr_hpop(ip++, ctx, n); - if (hpop == NULL || - sp->add_instr(hpop)) - MYSQL_YYABORT; - } - n= ctx->diff_cursors(lab->ctx, FALSE); /* Inclusive the dest. */ - if (n) - { - sp_instr_cpop *cpop= new (thd->mem_root) - sp_instr_cpop(ip++, ctx, n); - if (cpop == NULL || - sp->add_instr(cpop)) - MYSQL_YYABORT; - } - i= new (thd->mem_root) - sp_instr_jump(ip, ctx, lab->ip); /* Jump back */ - if (i == NULL || - sp->add_instr(i)) + if (Lex->sp_iterate_statement(thd, $2)) MYSQL_YYABORT; } ; diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index ada471b880d7b..d003680fcc52c 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -3007,90 +3007,15 @@ sp_proc_stmt_return: sp_proc_stmt_leave: LEAVE_SYM label_ident { - LEX *lex= Lex; - sp_head *sp = lex->sphead; - sp_pcontext *ctx= lex->spcont; - sp_label *lab= ctx->find_label($2); - - if (! lab) - my_yyabort_error((ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", $2.str)); - - sp_instr_jump *i; - uint ip= sp->instructions(); - uint n; - /* - When jumping to a BEGIN-END block end, the target jump - points to the block hpop/cpop cleanup instructions, - so we should exclude the block context here. - When jumping to something else (i.e., SP_LAB_ITER), - there are no hpop/cpop at the jump destination, - so we should include the block context here for cleanup. - */ - bool exclusive= (lab->type == sp_label::BEGIN); - - n= ctx->diff_handlers(lab->ctx, exclusive); - if (n) - { - sp_instr_hpop *hpop= new (thd->mem_root) - sp_instr_hpop(ip++, ctx, n); - if (hpop == NULL) - MYSQL_YYABORT; - sp->add_instr(hpop); - } - n= ctx->diff_cursors(lab->ctx, exclusive); - if (n) - { - sp_instr_cpop *cpop= new (thd->mem_root) - sp_instr_cpop(ip++, ctx, n); - if (cpop == NULL) - MYSQL_YYABORT; - sp->add_instr(cpop); - } - i= new (thd->mem_root) sp_instr_jump(ip, ctx); - if (i == NULL) + if (Lex->sp_leave_statement(thd, $2)) MYSQL_YYABORT; - sp->push_backpatch(thd, i, lab); /* Jumping forward */ - sp->add_instr(i); } ; sp_proc_stmt_iterate: ITERATE_SYM label_ident { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - sp_pcontext *ctx= lex->spcont; - sp_label *lab= ctx->find_label($2); - - if (! lab || lab->type != sp_label::ITERATION) - my_yyabort_error((ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", $2.str)); - - sp_instr_jump *i; - uint ip= sp->instructions(); - uint n; - - n= ctx->diff_handlers(lab->ctx, FALSE); /* Inclusive the dest. */ - if (n) - { - sp_instr_hpop *hpop= new (thd->mem_root) - sp_instr_hpop(ip++, ctx, n); - if (hpop == NULL || - sp->add_instr(hpop)) - MYSQL_YYABORT; - } - n= ctx->diff_cursors(lab->ctx, FALSE); /* Inclusive the dest. */ - if (n) - { - sp_instr_cpop *cpop= new (thd->mem_root) - sp_instr_cpop(ip++, ctx, n); - if (cpop == NULL || - sp->add_instr(cpop)) - MYSQL_YYABORT; - } - i= new (thd->mem_root) - sp_instr_jump(ip, ctx, lab->ip); /* Jump back */ - if (i == NULL || - sp->add_instr(i)) + if (Lex->sp_iterate_statement(thd, $2)) MYSQL_YYABORT; } ;