From ebcb4c931dc0ea5bc5e2199f39996f99fcab842b Mon Sep 17 00:00:00 2001 From: Neil Conway Date: Wed, 22 Jun 2005 01:35:03 +0000 Subject: [PATCH] Add a CONTINUE statement to PL/PgSQL, which can be used to begin the next iteration of a loop. Update documentation and add regression tests. Patch from Pavel Stehule, reviewed by Neil Conway. --- doc/src/sgml/plpgsql.sgml | 94 +++++++++--- src/pl/plpgsql/src/gram.y | 33 +++-- src/pl/plpgsql/src/pl_exec.c | 204 ++++++++++++++++++++------ src/pl/plpgsql/src/pl_funcs.c | 5 +- src/pl/plpgsql/src/plpgsql.h | 11 +- src/pl/plpgsql/src/scan.l | 3 +- src/test/regress/expected/plpgsql.out | 137 +++++++++++++++++ src/test/regress/sql/plpgsql.sql | 94 ++++++++++++ 8 files changed, 501 insertions(+), 80 deletions(-) diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index b202bba7b4..e8d687928f 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -1,5 +1,5 @@ @@ -1779,10 +1779,10 @@ END IF; - With the LOOP, EXIT, WHILE, - and FOR statements, you can arrange for your - PL/pgSQL function to repeat a series - of commands. + With the LOOP, EXIT, + CONTINUE, WHILE, and FOR + statements, you can arrange for your PL/pgSQL + function to repeat a series of commands. @@ -1807,30 +1807,36 @@ END LOOP; <literal>EXIT</> + + EXIT + in PL/pgSQL + + EXIT label WHEN expression ; - If no label is given, - the innermost loop is terminated and the - statement following END LOOP is executed next. - If label is given, it - must be the label of the current or some outer level of nested loop - or block. Then the named loop or block is terminated and control - continues with the statement after the loop's/block's corresponding - END. + If no label is given, the innermost + loop is terminated and the statement following END + LOOP is executed next. If label + is given, it must be the label of the current or some outer + level of nested loop or block. Then the named loop or block is + terminated and control continues with the statement after the + loop's/block's corresponding END. - If WHEN is present, loop exit occurs only if the specified - condition is true, otherwise control passes to the statement after - EXIT. + If WHEN is specified, the loop exit occurs only if + expression is true. Otherwise, control passes + to the statement after EXIT. - EXIT can be used to cause early exit from all types of - loops; it is not limited to use with unconditional loops. + EXIT can be used with all types of loops; it is + not limited to use with unconditional loops. When used with a + BEGIN block, EXIT passes + control to the next statement after the end of the block. @@ -1858,9 +1864,61 @@ END; + + <literal>CONTINUE</> + + + CONTINUE + in PL/pgSQL + + + +CONTINUE label WHEN expression ; + + + + If no label is given, the next iteration of + the innermost loop is begun. That is, control is passed back + to the loop control expression (if any), and the body of the + loop is re-evaluated. If label is present, it + specifies the label of the loop whose execution will be + continued. + + + + If WHEN is specified, the next iteration of the + loop is begun only if expression is + true. Otherwise, control passes to the statement after + CONTINUE. + + + + CONTINUE can be used with all types of loops; it + is not limited to use with unconditional loops. + + + + Examples: + +LOOP + -- some computations + EXIT WHEN count > 100; + CONTINUE WHEN count < 50; + -- some computations for count IN [50 .. 100] +END LOOP; + + + + + <literal>WHILE</> + + WHILE + in PL/pgSQL + + <<label>> WHILE expression LOOP diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index e8350686ee..f33d373883 100644 --- a/src/pl/plpgsql/src/gram.y +++ b/src/pl/plpgsql/src/gram.y @@ -4,7 +4,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.76 2005/06/14 06:43:14 neilc Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.77 2005/06/22 01:35:02 neilc Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -61,6 +61,7 @@ static void plpgsql_sql_error_callback(void *arg); %union { int32 ival; + bool boolean; char *str; struct { @@ -100,7 +101,7 @@ static void plpgsql_sql_error_callback(void *arg); %type decl_sect %type decl_varname %type decl_renname -%type decl_const decl_notnull +%type decl_const decl_notnull exit_type %type decl_defval decl_cursor_query %type decl_datatype %type decl_cursor_args @@ -153,6 +154,7 @@ static void plpgsql_sql_error_callback(void *arg); %token K_BEGIN %token K_CLOSE %token K_CONSTANT +%token K_CONTINUE %token K_CURSOR %token K_DEBUG %token K_DECLARE @@ -514,9 +516,9 @@ decl_renname : T_WORD ; decl_const : - { $$ = 0; } + { $$ = false; } | K_CONSTANT - { $$ = 1; } + { $$ = true; } ; decl_datatype : @@ -531,9 +533,9 @@ decl_datatype : ; decl_notnull : - { $$ = 0; } + { $$ = false; } | K_NOT K_NULL - { $$ = 1; } + { $$ = true; } ; decl_defval : ';' @@ -1035,13 +1037,14 @@ stmt_select : K_SELECT lno } ; -stmt_exit : K_EXIT lno opt_exitlabel opt_exitcond +stmt_exit : exit_type lno opt_exitlabel opt_exitcond { PLpgSQL_stmt_exit *new; new = palloc0(sizeof(PLpgSQL_stmt_exit)); new->cmd_type = PLPGSQL_STMT_EXIT; - new->lineno = $2; + new->is_exit = $1; + new->lineno = $2; new->label = $3; new->cond = $4; @@ -1049,6 +1052,16 @@ stmt_exit : K_EXIT lno opt_exitlabel opt_exitcond } ; +exit_type : K_EXIT + { + $$ = true; + } + | K_CONTINUE + { + $$ = false; + } + ; + stmt_return : K_RETURN lno { PLpgSQL_stmt_return *new; @@ -1056,8 +1069,8 @@ stmt_return : K_RETURN lno new = palloc0(sizeof(PLpgSQL_stmt_return)); new->cmd_type = PLPGSQL_STMT_RETURN; new->lineno = $2; - new->expr = NULL; - new->retvarno = -1; + new->expr = NULL; + new->retvarno = -1; if (plpgsql_curr_compile->fn_retset) { diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 04403fa564..d1ea2d843e 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.146 2005/06/20 22:51:29 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.147 2005/06/22 01:35:02 neilc Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -194,6 +194,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo) PLpgSQL_execstate estate; ErrorContextCallback plerrcontext; int i; + int rc; /* * Setup the execution state @@ -282,13 +283,24 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo) */ estate.err_text = NULL; estate.err_stmt = (PLpgSQL_stmt *) (func->action); - if (exec_stmt_block(&estate, func->action) != PLPGSQL_RC_RETURN) + rc = exec_stmt_block(&estate, func->action); + if (rc != PLPGSQL_RC_RETURN) { estate.err_stmt = NULL; estate.err_text = NULL; - ereport(ERROR, - (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), - errmsg("control reached end of function without RETURN"))); + + /* + * Provide a more helpful message if a CONTINUE has been used + * outside a loop. + */ + if (rc == PLPGSQL_RC_CONTINUE) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("CONTINUE cannot be used outside a loop"))); + else + ereport(ERROR, + (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), + errmsg("control reached end of function without RETURN"))); } /* @@ -393,6 +405,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func, PLpgSQL_execstate estate; ErrorContextCallback plerrcontext; int i; + int rc; PLpgSQL_var *var; PLpgSQL_rec *rec_new, *rec_old; @@ -546,13 +559,24 @@ plpgsql_exec_trigger(PLpgSQL_function *func, */ estate.err_text = NULL; estate.err_stmt = (PLpgSQL_stmt *) (func->action); - if (exec_stmt_block(&estate, func->action) != PLPGSQL_RC_RETURN) + rc = exec_stmt_block(&estate, func->action); + if (rc != PLPGSQL_RC_RETURN) { estate.err_stmt = NULL; estate.err_text = NULL; - ereport(ERROR, - (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), - errmsg("control reached end of trigger procedure without RETURN"))); + + /* + * Provide a more helpful message if a CONTINUE has been used + * outside a loop. + */ + if (rc == PLPGSQL_RC_CONTINUE) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("CONTINUE cannot be used outside a loop"))); + else + ereport(ERROR, + (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), + errmsg("control reached end of trigger procedure without RETURN"))); } if (estate.retisset) @@ -919,7 +943,9 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) switch (rc) { case PLPGSQL_RC_OK: - return PLPGSQL_RC_OK; + case PLPGSQL_RC_CONTINUE: + case PLPGSQL_RC_RETURN: + return rc; case PLPGSQL_RC_EXIT: if (estate->exitlabel == NULL) @@ -930,10 +956,7 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) return PLPGSQL_RC_EXIT; estate->exitlabel = NULL; return PLPGSQL_RC_OK; - - case PLPGSQL_RC_RETURN: - return PLPGSQL_RC_RETURN; - + default: elog(ERROR, "unrecognized rc: %d", rc); } @@ -1120,7 +1143,7 @@ exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt) { PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc); PLpgSQL_datum *var; - bool isnull = false; + bool isnull; if (diag_item->target <= 0) continue; @@ -1165,7 +1188,7 @@ static int exec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt) { bool value; - bool isnull = false; + bool isnull; value = exec_eval_boolean(estate, stmt->cond, &isnull); exec_eval_cleanup(estate); @@ -1209,10 +1232,23 @@ exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt) return PLPGSQL_RC_OK; if (stmt->label == NULL) return PLPGSQL_RC_EXIT; - if (strcmp(stmt->label, estate->exitlabel)) + if (strcmp(stmt->label, estate->exitlabel) != 0) return PLPGSQL_RC_EXIT; estate->exitlabel = NULL; return PLPGSQL_RC_OK; + + case PLPGSQL_RC_CONTINUE: + if (estate->exitlabel == NULL) + /* anonymous continue, so re-run the loop */ + break; + else if (stmt->label != NULL && + strcmp(stmt->label, estate->exitlabel) == 0) + /* label matches named continue, so re-run loop */ + estate->exitlabel = NULL; + else + /* label doesn't match named continue, so propagate upward */ + return PLPGSQL_RC_CONTINUE; + break; case PLPGSQL_RC_RETURN: return PLPGSQL_RC_RETURN; @@ -1236,7 +1272,7 @@ static int exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt) { bool value; - bool isnull = false; + bool isnull; int rc; for (;;) @@ -1264,6 +1300,19 @@ exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt) estate->exitlabel = NULL; return PLPGSQL_RC_OK; + case PLPGSQL_RC_CONTINUE: + if (estate->exitlabel == NULL) + /* anonymous continue, so re-run loop */ + break; + else if (stmt->label != NULL && + strcmp(stmt->label, estate->exitlabel) == 0) + /* label matches named continue, so re-run loop */ + estate->exitlabel = NULL; + else + /* label doesn't match named continue, propagate upward */ + return PLPGSQL_RC_CONTINUE; + break; + case PLPGSQL_RC_RETURN: return PLPGSQL_RC_RETURN; @@ -1288,7 +1337,7 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt) PLpgSQL_var *var; Datum value; Oid valtype; - bool isnull = false; + bool isnull; bool found = false; int rc = PLPGSQL_RC_OK; @@ -1366,13 +1415,35 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt) } /* - * otherwise, we processed a labelled exit that does not match - * the current statement's label, if any: return RC_EXIT so - * that the EXIT continues to recurse upward. + * otherwise, this is a labelled exit that does not match + * the current statement's label, if any: return RC_EXIT + * so that the EXIT continues to propagate up the stack. */ break; } + else if (rc == PLPGSQL_RC_CONTINUE) + { + if (estate->exitlabel == NULL) + /* anonymous continue, so continue the current loop */ + ; + else if (stmt->label != NULL && + strcmp(stmt->label, estate->exitlabel) == 0) + { + /* labelled continue, matches the current stmt's label */ + estate->exitlabel = NULL; + } + else + { + /* + * otherwise, this is a labelled continue that does + * not match the current statement's label, if any: + * return RC_CONTINUE so that the CONTINUE will + * propagate up the stack. + */ + break; + } + } /* * Increase/decrease loop var @@ -1459,17 +1530,8 @@ exec_stmt_fors(PLpgSQL_execstate *estate, PLpgSQL_stmt_fors *stmt) * Execute the statements */ rc = exec_stmts(estate, stmt->body); - if (rc != PLPGSQL_RC_OK) { - /* - * We're aborting the loop, so cleanup and set FOUND. - * (This code should match the code after the loop.) - */ - SPI_freetuptable(tuptab); - SPI_cursor_close(portal); - exec_set_found(estate, found); - if (rc == PLPGSQL_RC_EXIT) { if (estate->exitlabel == NULL) @@ -1490,6 +1552,34 @@ exec_stmt_fors(PLpgSQL_execstate *estate, PLpgSQL_stmt_fors *stmt) * recurse upward. */ } + else if (rc == PLPGSQL_RC_CONTINUE) + { + if (estate->exitlabel == NULL) + /* unlabelled continue, continue the current loop */ + continue; + else if (stmt->label != NULL && + strcmp(stmt->label, estate->exitlabel) == 0) + { + /* labelled continue, matches the current stmt's label */ + estate->exitlabel = NULL; + continue; + } + + /* + * otherwise, we processed a labelled continue + * that does not match the current statement's + * label, if any: return RC_CONTINUE so that the + * CONTINUE will propagate up the stack. + */ + } + + /* + * We're aborting the loop, so cleanup and set FOUND. + * (This code should match the code after the loop.) + */ + SPI_freetuptable(tuptab); + SPI_cursor_close(portal); + exec_set_found(estate, found); return rc; } @@ -1563,7 +1653,7 @@ exec_stmt_select(PLpgSQL_execstate *estate, PLpgSQL_stmt_select *stmt) n = estate->eval_processed; /* - * If the query didn't return any row, set the target to NULL and + * If the query didn't return any rows, set the target to NULL and * return. */ if (n == 0) @@ -1586,28 +1676,33 @@ exec_stmt_select(PLpgSQL_execstate *estate, PLpgSQL_stmt_select *stmt) /* ---------- - * exec_stmt_exit Start exiting loop(s) or blocks + * exec_stmt_exit Implements EXIT and CONTINUE + * + * This begins the process of exiting / restarting a loop. * ---------- */ static int exec_stmt_exit(PLpgSQL_execstate *estate, PLpgSQL_stmt_exit *stmt) { /* - * If the exit has a condition, check that it's true + * If the exit / continue has a condition, evaluate it */ if (stmt->cond != NULL) { bool value; - bool isnull = false; + bool isnull; value = exec_eval_boolean(estate, stmt->cond, &isnull); exec_eval_cleanup(estate); - if (isnull || !value) + if (isnull || value == false) return PLPGSQL_RC_OK; } estate->exitlabel = stmt->label; - return PLPGSQL_RC_EXIT; + if (stmt->is_exit) + return PLPGSQL_RC_EXIT; + else + return PLPGSQL_RC_CONTINUE; } @@ -2455,14 +2550,6 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt) if (rc != PLPGSQL_RC_OK) { - /* - * We're aborting the loop, so cleanup and set FOUND. - * (This code should match the code after the loop.) - */ - SPI_freetuptable(tuptab); - SPI_cursor_close(portal); - exec_set_found(estate, found); - if (rc == PLPGSQL_RC_EXIT) { if (estate->exitlabel == NULL) @@ -2483,6 +2570,33 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt) * recurse upward. */ } + else if (rc == PLPGSQL_RC_CONTINUE) + { + if (estate->exitlabel == NULL) + /* unlabelled continue, continue the current loop */ + continue; + else if (stmt->label != NULL && + strcmp(stmt->label, estate->exitlabel) == 0) + { + /* labelled continue, matches the current stmt's label */ + estate->exitlabel = NULL; + continue; + } + + /* + * otherwise, we process a labelled continue that + * does not match the current statement's label, + * so propagate RC_CONTINUE upward in the stack. + */ + } + + /* + * We're aborting the loop, so cleanup and set FOUND. + * (This code should match the code after the loop.) + */ + SPI_freetuptable(tuptab); + SPI_cursor_close(portal); + exec_set_found(estate, found); return rc; } diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c index 23b2b3e71a..03280c94b1 100644 --- a/src/pl/plpgsql/src/pl_funcs.c +++ b/src/pl/plpgsql/src/pl_funcs.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.44 2005/06/19 01:06:12 momjian Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.45 2005/06/22 01:35:02 neilc Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -845,7 +845,8 @@ static void dump_exit(PLpgSQL_stmt_exit *stmt) { dump_ind(); - printf("EXIT lbl='%s'", stmt->label); + printf("%s label='%s'", + stmt->is_exit ? "EXIT" : "CONTINUE", stmt->label); if (stmt->cond != NULL) { printf(" WHEN "); diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index a724df6796..3615b3bf06 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.63 2005/06/14 06:43:14 neilc Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.64 2005/06/22 01:35:02 neilc Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -125,7 +125,8 @@ enum { PLPGSQL_RC_OK, PLPGSQL_RC_EXIT, - PLPGSQL_RC_RETURN + PLPGSQL_RC_RETURN, + PLPGSQL_RC_CONTINUE }; /* ---------- @@ -485,9 +486,10 @@ typedef struct typedef struct -{ /* EXIT statement */ +{ /* EXIT or CONTINUE statement */ int cmd_type; int lineno; + bool is_exit; /* Is this an exit or a continue? */ char *label; PLpgSQL_expr *cond; } PLpgSQL_stmt_exit; @@ -610,7 +612,8 @@ typedef struct bool readonly_func; TupleDesc rettupdesc; - char *exitlabel; + char *exitlabel; /* the "target" label of the current + * EXIT or CONTINUE stmt, if any */ Tuplestorestate *tuple_store; /* SRFs accumulate results here */ MemoryContext tuple_store_cxt; diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l index f929519643..680a58fc01 100644 --- a/src/pl/plpgsql/src/scan.l +++ b/src/pl/plpgsql/src/scan.l @@ -4,7 +4,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.40 2005/03/11 19:13:43 momjian Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.41 2005/06/22 01:35:02 neilc Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -139,6 +139,7 @@ alias { return K_ALIAS; } begin { return K_BEGIN; } close { return K_CLOSE; } constant { return K_CONSTANT; } +continue { return K_CONTINUE; } cursor { return K_CURSOR; } debug { return K_DEBUG; } declare { return K_DECLARE; } diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index 21101958ab..23ef2d3f2e 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -2491,3 +2491,140 @@ NOTICE: {10,20,30}; 20; xyz; xyzabc; (10,aaa,,30); (1 row) drop function raise_exprs(); +-- continue statement +create table conttesttbl(idx serial, v integer); +NOTICE: CREATE TABLE will create implicit sequence "conttesttbl_idx_seq" for serial column "conttesttbl.idx" +insert into conttesttbl(v) values(10); +insert into conttesttbl(v) values(20); +insert into conttesttbl(v) values(30); +insert into conttesttbl(v) values(40); +create function continue_test1() returns void as $$ +declare _i integer = 0; _r record; +begin + raise notice '---1---'; + loop + _i := _i + 1; + raise notice '%', _i; + continue when _i < 10; + exit; + end loop; + + raise notice '---2---'; + <> + loop + _i := _i - 1; + loop + raise notice '%', _i; + continue lbl when _i > 0; + exit lbl; + end loop; + end loop; + + raise notice '---3---'; + <> + while _i < 10 loop + _i := _i + 1; + continue the_loop when _i % 2 = 0; + raise notice '%', _i; + end loop; + + raise notice '---4---'; + for _i in 1..10 loop + begin + -- applies to outer loop, not the nested begin block + continue when _i < 5; + raise notice '%', _i; + end; + end loop; + + raise notice '---5---'; + for _r in select * from conttesttbl loop + continue when _r.v <= 20; + raise notice '%', _r.v; + end loop; + + raise notice '---6---'; + for _r in execute 'select * from conttesttbl' loop + continue when _r.v <= 20; + raise notice '%', _r.v; + end loop; +end; $$ language plpgsql; +select continue_test1(); +NOTICE: ---1--- +NOTICE: 1 +NOTICE: 2 +NOTICE: 3 +NOTICE: 4 +NOTICE: 5 +NOTICE: 6 +NOTICE: 7 +NOTICE: 8 +NOTICE: 9 +NOTICE: 10 +NOTICE: ---2--- +NOTICE: 9 +NOTICE: 8 +NOTICE: 7 +NOTICE: 6 +NOTICE: 5 +NOTICE: 4 +NOTICE: 3 +NOTICE: 2 +NOTICE: 1 +NOTICE: 0 +NOTICE: ---3--- +NOTICE: 1 +NOTICE: 3 +NOTICE: 5 +NOTICE: 7 +NOTICE: 9 +NOTICE: ---4--- +NOTICE: 5 +NOTICE: 6 +NOTICE: 7 +NOTICE: 8 +NOTICE: 9 +NOTICE: 10 +NOTICE: ---5--- +NOTICE: 30 +NOTICE: 40 +NOTICE: ---6--- +NOTICE: 30 +NOTICE: 40 + continue_test1 +---------------- + +(1 row) + +-- CONTINUE is only legal inside a loop +create function continue_test2() returns void as $$ +begin + begin + continue; + end; + return; +end; +$$ language plpgsql; +-- should fail +select continue_test2(); +ERROR: CONTINUE cannot be used outside a loop +CONTEXT: PL/pgSQL function "continue_test2" +-- CONTINUE can't reference the label of a named block +create function continue_test3() returns void as $$ +begin + <> + begin + loop + continue begin_block1; + end loop; + end; +end; +$$ language plpgsql; +-- should fail +select continue_test3(); +ERROR: CONTINUE cannot be used outside a loop +CONTEXT: PL/pgSQL function "continue_test3" +drop function continue_test1(); +drop function continue_test2(); +drop function continue_test3(); +drop table conttesttbl; diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index 375eef8959..393fdc52b0 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -2112,3 +2112,97 @@ end;$$ language plpgsql; select raise_exprs(); drop function raise_exprs(); + +-- continue statement +create table conttesttbl(idx serial, v integer); +insert into conttesttbl(v) values(10); +insert into conttesttbl(v) values(20); +insert into conttesttbl(v) values(30); +insert into conttesttbl(v) values(40); + +create function continue_test1() returns void as $$ +declare _i integer = 0; _r record; +begin + raise notice '---1---'; + loop + _i := _i + 1; + raise notice '%', _i; + continue when _i < 10; + exit; + end loop; + + raise notice '---2---'; + <> + loop + _i := _i - 1; + loop + raise notice '%', _i; + continue lbl when _i > 0; + exit lbl; + end loop; + end loop; + + raise notice '---3---'; + <> + while _i < 10 loop + _i := _i + 1; + continue the_loop when _i % 2 = 0; + raise notice '%', _i; + end loop; + + raise notice '---4---'; + for _i in 1..10 loop + begin + -- applies to outer loop, not the nested begin block + continue when _i < 5; + raise notice '%', _i; + end; + end loop; + + raise notice '---5---'; + for _r in select * from conttesttbl loop + continue when _r.v <= 20; + raise notice '%', _r.v; + end loop; + + raise notice '---6---'; + for _r in execute 'select * from conttesttbl' loop + continue when _r.v <= 20; + raise notice '%', _r.v; + end loop; +end; $$ language plpgsql; + +select continue_test1(); + +-- CONTINUE is only legal inside a loop +create function continue_test2() returns void as $$ +begin + begin + continue; + end; + return; +end; +$$ language plpgsql; + +-- should fail +select continue_test2(); + +-- CONTINUE can't reference the label of a named block +create function continue_test3() returns void as $$ +begin + <> + begin + loop + continue begin_block1; + end loop; + end; +end; +$$ language plpgsql; + +-- should fail +select continue_test3(); + +drop function continue_test1(); +drop function continue_test2(); +drop function continue_test3(); +drop table conttesttbl; -- 2.40.0