- 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.
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.
+
+
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;
+
+
+
+
+
WHILE>
+
+ in PL/pgSQL
+
+
<<label>>
WHILE expression LOOP
* 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.
*
PLpgSQL_execstate estate;
ErrorContextCallback plerrcontext;
int i;
+ int rc;
/*
* Setup the execution state
*/
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")));
}
/*
PLpgSQL_execstate estate;
ErrorContextCallback plerrcontext;
int i;
+ int rc;
PLpgSQL_var *var;
PLpgSQL_rec *rec_new,
*rec_old;
*/
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)
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)
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);
}
{
PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
PLpgSQL_datum *var;
- bool isnull = false;
+ bool isnull;
if (diag_item->target <= 0)
continue;
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);
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;
exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
{
bool value;
- bool isnull = false;
+ bool isnull;
int rc;
for (;;)
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;
PLpgSQL_var *var;
Datum value;
Oid valtype;
- bool isnull = false;
+ bool isnull;
bool found = false;
int rc = PLPGSQL_RC_OK;
}
/*
- * 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
* 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)
* 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;
}
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)
/* ----------
- * 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;
}
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)
* 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;
}
(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;
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;