From ca242117cedd1c10106b23385dca9b7781ef41f7 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Wed, 24 Aug 2016 15:23:04 +0400 Subject: [PATCH] MDEV-10411 Providing compatibility for basic PL/SQL constructs Part 19: CONTINUE statement --- .../suite/compat/oracle/r/sp-code.result | 30 +++++++ mysql-test/suite/compat/oracle/r/sp.result | 81 +++++++++++++++++ mysql-test/suite/compat/oracle/t/sp-code.test | 22 +++++ mysql-test/suite/compat/oracle/t/sp.test | 90 +++++++++++++++++++ sql/sql_lex.cc | 53 +++++++++++ sql/sql_lex.h | 6 ++ sql/sql_yacc_ora.yy | 16 ++++ 7 files changed, 298 insertions(+) diff --git a/mysql-test/suite/compat/oracle/r/sp-code.result b/mysql-test/suite/compat/oracle/r/sp-code.result index 6f8de418888..d96028d6e4d 100644 --- a/mysql-test/suite/compat/oracle/r/sp-code.result +++ b/mysql-test/suite/compat/oracle/r/sp-code.result @@ -797,3 +797,33 @@ SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL; f1(3) f1(4) f1(5) f1(6) 6006 8008 10008 12010 DROP FUNCTION f1; +# Testing CONTINUE statement +CREATE FUNCTION f1(a INT) RETURN INT +AS +total INT:= 0; +BEGIN +FOR i IN 1 .. a +LOOP +CONTINUE WHEN i=5; +total:= total + 1; +END LOOP; +RETURN total; +END; +/ +SHOW FUNCTION CODE f1; +Pos Instruction +0 set total@1 0 +1 set i@2 1 +2 set [upper_bound]@3 a@0 +3 jump_if_not 10(10) (i@2 <= [upper_bound]@3) +4 jump_if_not 7(0) (i@2 = 5) +5 set i@2 (i@2 + 1) +6 jump 3 +7 set total@1 (total@1 + 1) +8 set i@2 (i@2 + 1) +9 jump 3 +10 freturn 3 total@1 +SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL; +f1(3) f1(4) f1(5) f1(6) +3 4 4 5 +DROP FUNCTION f1; diff --git a/mysql-test/suite/compat/oracle/r/sp.result b/mysql-test/suite/compat/oracle/r/sp.result index 97b1e1d649a..c1f1b1c415d 100644 --- a/mysql-test/suite/compat/oracle/r/sp.result +++ b/mysql-test/suite/compat/oracle/r/sp.result @@ -946,3 +946,84 @@ SELECT f1(3,3,0), f1(3,3,1), f1(3,3,2), f1(3,3,3), f1(3,3,4) FROM DUAL; f1(3,3,0) f1(3,3,1) f1(3,3,2) f1(3,3,3) f1(3,3,4) 3000 3003 3006 3009 3009 DROP FUNCTION f1; +# Testing CONTINUE statement +CREATE FUNCTION f1(a INT) RETURN INT +AS +total INT:= 0; +BEGIN +FOR i IN 1 .. a +LOOP +IF i=5 THEN +CONTINUE; +END IF; +total:= total + 1; +END LOOP; +RETURN total; +END; +/ +SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL; +f1(3) f1(4) f1(5) f1(6) +3 4 4 5 +DROP FUNCTION f1; +CREATE FUNCTION f1(a INT) RETURN INT +AS +total INT:= 0; +BEGIN +<> +FOR j IN 1 .. 2 +LOOP +FOR i IN 1 .. a +LOOP +IF i=5 THEN +CONTINUE lj; +END IF; +total:= total + 1; +END LOOP; +END LOOP; +RETURN total; +END; +/ +SELECT f1(3), f1(4), f1(5) FROM DUAL; +f1(3) f1(4) f1(5) +6 8 8 +DROP FUNCTION f1; +CREATE FUNCTION f1(a INT) RETURN INT +AS +total INT:= 0; +BEGIN +<> +FOR j IN 1 .. 2 +LOOP +FOR i IN 1 .. a +LOOP +CONTINUE lj WHEN i=5; +total:= total + 1; +END LOOP; +END LOOP; +RETURN total; +END; +/ +SELECT f1(3), f1(4), f1(5) FROM DUAL; +f1(3) f1(4) f1(5) +6 8 8 +DROP FUNCTION f1; +CREATE FUNCTION f1(a INT) RETURN INT +AS +total INT:= 0; +i INT:= 1; +BEGIN +WHILE i <= a +LOOP +i:= i + 1; +IF i=6 THEN +CONTINUE; +END IF; +total:= total + 1; +END LOOP; +RETURN total; +END; +/ +SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL; +f1(3) f1(4) f1(5) f1(6) +3 4 4 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 73fb5fb91d6..5dacd9cb483 100644 --- a/mysql-test/suite/compat/oracle/t/sp-code.test +++ b/mysql-test/suite/compat/oracle/t/sp-code.test @@ -593,3 +593,25 @@ DELIMITER ;/ SHOW FUNCTION CODE f1; SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL; DROP FUNCTION f1; + + +--echo # Testing CONTINUE statement + +DELIMITER /; +CREATE FUNCTION f1(a INT) RETURN INT +AS + total INT:= 0; +BEGIN + FOR i IN 1 .. a + LOOP + CONTINUE WHEN i=5; + total:= total + 1; + END LOOP; + RETURN total; +END; +/ +DELIMITER ;/ +SHOW FUNCTION CODE f1; +SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL; +DROP FUNCTION f1; + diff --git a/mysql-test/suite/compat/oracle/t/sp.test b/mysql-test/suite/compat/oracle/t/sp.test index a45e4961184..8ae001bf7d1 100644 --- a/mysql-test/suite/compat/oracle/t/sp.test +++ b/mysql-test/suite/compat/oracle/t/sp.test @@ -996,3 +996,93 @@ DELIMITER ;/ SHOW FUNCTION CODE f1; SELECT f1(3,3,0), f1(3,3,1), f1(3,3,2), f1(3,3,3), f1(3,3,4) FROM DUAL; DROP FUNCTION f1; + + +--echo # Testing CONTINUE statement + +DELIMITER /; +CREATE FUNCTION f1(a INT) RETURN INT +AS + total INT:= 0; +BEGIN + FOR i IN 1 .. a + LOOP + IF i=5 THEN + CONTINUE; + END IF; + total:= total + 1; + END LOOP; + RETURN total; +END; +/ +DELIMITER ;/ +SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL; +DROP FUNCTION f1; + + +DELIMITER /; +CREATE FUNCTION f1(a INT) RETURN INT +AS + total INT:= 0; +BEGIN + <> + FOR j IN 1 .. 2 + LOOP + FOR i IN 1 .. a + LOOP + IF i=5 THEN + CONTINUE lj; + END IF; + total:= total + 1; + END LOOP; + END LOOP; + RETURN total; +END; +/ +DELIMITER ;/ +SELECT f1(3), f1(4), f1(5) FROM DUAL; +DROP FUNCTION f1; + + +DELIMITER /; +CREATE FUNCTION f1(a INT) RETURN INT +AS + total INT:= 0; +BEGIN + <> + FOR j IN 1 .. 2 + LOOP + FOR i IN 1 .. a + LOOP + CONTINUE lj WHEN i=5; + total:= total + 1; + END LOOP; + END LOOP; + RETURN total; +END; +/ +DELIMITER ;/ +SELECT f1(3), f1(4), f1(5) FROM DUAL; +DROP FUNCTION f1; + + +DELIMITER /; +CREATE FUNCTION f1(a INT) RETURN INT +AS + total INT:= 0; + i INT:= 1; +BEGIN + WHILE i <= a + LOOP + i:= i + 1; + IF i=6 THEN + CONTINUE; + END IF; + total:= total + 1; + END LOOP; + RETURN total; +END; +/ +DELIMITER ;/ +SELECT f1(3), f1(4), f1(5), f1(6) FROM DUAL; +DROP FUNCTION f1; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 22e6e7e5245..e67536a838d 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -5675,6 +5675,12 @@ bool LEX::sp_iterate_statement(THD *thd, const LEX_STRING label_name) my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", label_name.str); return true; } + return sp_continue_loop(thd, lab); +} + + +bool LEX::sp_continue_loop(THD *thd, sp_label *lab) +{ if (lab->ctx->for_loop().m_index) { // We're in a FOR loop, increment the index variable before backward jump @@ -5689,6 +5695,53 @@ bool LEX::sp_iterate_statement(THD *thd, const LEX_STRING label_name) } +bool LEX::sp_continue_loop(THD *thd, sp_label *lab, Item *when) +{ + if (!when) + return sp_continue_loop(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_continue_loop(thd, lab)) + return true; + i->backpatch(sphead->instructions(), spcont); + return false; +} + + +bool LEX::sp_continue_statement(THD *thd, Item *when) +{ + sp_label *lab= spcont->find_label_current_loop_start(); + if (!lab) + { + my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "CONTINUE", ""); + return true; + } + DBUG_ASSERT(lab->type == sp_label::ITERATION); + return sp_continue_loop(thd, lab, when); +} + + +bool LEX::sp_continue_statement(THD *thd, const LEX_STRING label_name, Item *when) +{ + sp_label *lab= spcont->find_label(label_name); + if (!lab || lab->type != sp_label::ITERATION) + { + my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "CONTINUE", label_name); + return true; + } + return sp_continue_loop(thd, lab, when); +} + + bool LEX::maybe_start_compound_statement(THD *thd) { if (!sphead) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 0d834c2e1d3..b9cb4e35687 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2634,6 +2634,9 @@ private: bool sp_exit_block(THD *thd, sp_label *lab); bool sp_exit_block(THD *thd, sp_label *lab, Item *when); + bool sp_continue_loop(THD *thd, sp_label *lab); + bool sp_continue_loop(THD *thd, sp_label *lab, Item *when); + bool sp_for_loop_condition(THD *thd, const Lex_for_loop_st &loop); bool sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop); @@ -3161,6 +3164,9 @@ public: 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_continue_statement(THD *thd, Item *when); + bool sp_continue_statement(THD *thd, const LEX_STRING label_name, Item *when); bool sp_iterate_statement(THD *thd, const LEX_STRING label_name); bool maybe_start_compound_statement(THD *thd); diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 8bdff7b4b51..46344a59b88 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -1264,6 +1264,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_continue %type sp_proc_stmt_exit %type sp_proc_stmt_leave %type sp_proc_stmt_iterate @@ -2866,6 +2867,7 @@ sp_proc_stmt_in_returns_clause: sp_proc_stmt: sp_proc_stmt_in_returns_clause | sp_proc_stmt_statement + | sp_proc_stmt_continue | sp_proc_stmt_exit | sp_proc_stmt_leave | sp_proc_stmt_iterate @@ -3004,6 +3006,20 @@ sp_proc_stmt_exit: } ; +sp_proc_stmt_continue: + CONTINUE_SYM opt_sp_proc_stmt_exit_when_clause + { + if (Lex->sp_continue_statement(thd, $2)) + MYSQL_YYABORT; + } + | CONTINUE_SYM label_ident opt_sp_proc_stmt_exit_when_clause + { + if (Lex->sp_continue_statement(thd, $2, $3)) + MYSQL_YYABORT; + } + ; + + sp_proc_stmt_leave: LEAVE_SYM label_ident {