aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/pl/plpgsql/src/gram.y33
-rw-r--r--src/pl/plpgsql/src/pl_exec.c204
-rw-r--r--src/pl/plpgsql/src/pl_funcs.c5
-rw-r--r--src/pl/plpgsql/src/plpgsql.h11
-rw-r--r--src/pl/plpgsql/src/scan.l3
-rw-r--r--src/test/regress/expected/plpgsql.out137
-rw-r--r--src/test/regress/sql/plpgsql.sql94
7 files changed, 425 insertions, 62 deletions
diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index e8350686eee..f33d373883e 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 <declhdr> decl_sect
%type <varname> decl_varname
%type <str> decl_renname
-%type <ival> decl_const decl_notnull
+%type <boolean> decl_const decl_notnull exit_type
%type <expr> decl_defval decl_cursor_query
%type <dtype> decl_datatype
%type <row> 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 04403fa5640..d1ea2d843ee 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 23b2b3e71a1..03280c94b12 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 a724df67963..3615b3bf066 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 f9295196439..680a58fc018 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 21101958ab4..23ef2d3f2ed 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); <NULL>
(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---';
+ <<lbl>>
+ loop
+ _i := _i - 1;
+ loop
+ raise notice '%', _i;
+ continue lbl when _i > 0;
+ exit lbl;
+ end loop;
+ end loop;
+
+ raise notice '---3---';
+ <<the_loop>>
+ 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_block1>>
+ 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 375eef8959c..393fdc52b0c 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---';
+ <<lbl>>
+ loop
+ _i := _i - 1;
+ loop
+ raise notice '%', _i;
+ continue lbl when _i > 0;
+ exit lbl;
+ end loop;
+ end loop;
+
+ raise notice '---3---';
+ <<the_loop>>
+ 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_block1>>
+ 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;