Pavel Stehule wrote:
> Attached patch provide continue stmt in plpgsql language. Continue stmt
> start new step in any loop stmt. If stmt continue is in begin end block,
> then find first outer loop (per recent discussion).
Attached is a revised patch. I didn't see anything major that needed to
be corrected, but there were some minor cleanups to be made. I also
clarified the error message when a CONTINUE is specified outside a loop
(although I wasn't quite sure what error message to use: SYNTAX_ERROR
seems a bit weird, as the problem isn't detected until the end of the
function's execution. I'm using PLPGSQL_ERROR at the moment, but
suggestions for a better choice are welcome).
Barring any objections, I'll apply this tomorrow.
-Neil
Index: doc/src/sgml/plpgsql.sgml
===================================================================
RCS file: /var/lib/cvs/pgsql/doc/src/sgml/plpgsql.sgml,v
retrieving revision 1.73
diff -c -r1.73 plpgsql.sgml
*** doc/src/sgml/plpgsql.sgml 19 Jun 2005 23:39:05 -0000 1.73
--- doc/src/sgml/plpgsql.sgml 21 Jun 2005 08:46:27 -0000
***************
*** 1779,1788 ****
</indexterm>
<para>
! With the <literal>LOOP</>, <literal>EXIT</>, <literal>WHILE</>,
! and <literal>FOR</> statements, you can arrange for your
! <application>PL/pgSQL</application> function to repeat a series
! of commands.
</para>
<sect3>
--- 1779,1788 ----
</indexterm>
<para>
! With the <literal>LOOP</>, <literal>EXIT</>,
! <literal>CONTINUE</>, <literal>WHILE</>, and <literal>FOR</>
! statements, you can arrange for your <application>PL/pgSQL</>
! function to repeat a series of commands.
</para>
<sect3>
***************
*** 1807,1836 ****
<sect3>
<title><literal>EXIT</></title>
<synopsis>
EXIT <optional> <replaceable>label</replaceable> </optional> <optional> WHEN <replaceable>expression</replaceable>
</optional>;
</synopsis>
<para>
! If no <replaceable>label</replaceable> is given,
! the innermost loop is terminated and the
! statement following <literal>END LOOP</> is executed next.
! If <replaceable>label</replaceable> 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
! <literal>END</>.
</para>
<para>
! If <literal>WHEN</> is present, loop exit occurs only if the specified
! condition is true, otherwise control passes to the statement after
! <literal>EXIT</>.
</para>
<para>
! <literal>EXIT</> can be used to cause early exit from all types of
! loops; it is not limited to use with unconditional loops.
</para>
<para>
--- 1807,1842 ----
<sect3>
<title><literal>EXIT</></title>
+ <indexterm>
+ <primary>EXIT</primary>
+ <secondary>in PL/pgSQL</secondary>
+ </indexterm>
+
<synopsis>
EXIT <optional> <replaceable>label</replaceable> </optional> <optional> WHEN <replaceable>expression</replaceable>
</optional>;
</synopsis>
<para>
! If no <replaceable>label</replaceable> is given, the innermost
! loop is terminated and the statement following <literal>END
! LOOP</> is executed next. If <replaceable>label</replaceable>
! 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 <literal>END</>.
</para>
<para>
! If <literal>WHEN</> is specified, the loop exit occurs only if
! <replaceable>expression</> is true. Otherwise, control passes
! to the statement after <literal>EXIT</>.
</para>
<para>
! <literal>EXIT</> can be used with all types of loops; it is
! not limited to use with unconditional loops. When used with a
! <literal>BEGIN</literal> block, <literal>EXIT</literal> passes
! control to the next statement after the end of the block.
</para>
<para>
***************
*** 1859,1866 ****
--- 1865,1924 ----
</sect3>
<sect3>
+ <title><literal>CONTINUE</></title>
+
+ <indexterm>
+ <primary>CONTINUE</primary>
+ <secondary>in PL/pgSQL</secondary>
+ </indexterm>
+
+ <synopsis>
+ CONTINUE <optional> <replaceable>label</replaceable> </optional> <optional> WHEN
<replaceable>expression</replaceable></optional>;
+ </synopsis>
+
+ <para>
+ If no <replaceable>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 <replaceable>label</> is present, it
+ specifies the label of the loop whose execution will be
+ continued.
+ </para>
+
+ <para>
+ If <literal>WHEN</> is specified, the next iteration of the
+ loop is begun only if <replaceable>expression</> is
+ true. Otherwise, control passes to the statement after
+ <literal>CONTINUE</>.
+ </para>
+
+ <para>
+ <literal>CONTINUE</> can be used with all types of loops; it
+ is not limited to use with unconditional loops.
+ </para>
+
+ <para>
+ Examples:
+ <programlisting>
+ LOOP
+ -- some computations
+ EXIT WHEN count > 100;
+ CONTINUE WHEN count < 50;
+ -- some computations for count IN [50 .. 100]
+ END LOOP;
+ </programlisting>
+ </para>
+ </sect3>
+
+
+ <sect3>
<title><literal>WHILE</></title>
+ <indexterm>
+ <primary>WHILE</primary>
+ <secondary>in PL/pgSQL</secondary>
+ </indexterm>
+
<synopsis>
<optional><<<replaceable>label</replaceable>>></optional>
WHILE <replaceable>expression</replaceable> LOOP
Index: src/pl/plpgsql/src/gram.y
===================================================================
RCS file: /var/lib/cvs/pgsql/src/pl/plpgsql/src/gram.y,v
retrieving revision 1.76
diff -c -r1.76 gram.y
*** src/pl/plpgsql/src/gram.y 14 Jun 2005 06:43:14 -0000 1.76
--- src/pl/plpgsql/src/gram.y 21 Jun 2005 07:19:38 -0000
***************
*** 61,66 ****
--- 61,67 ----
%union {
int32 ival;
+ bool boolean;
char *str;
struct
{
***************
*** 100,106 ****
%type <declhdr> decl_sect
%type <varname> decl_varname
%type <str> decl_renname
! %type <ival> decl_const decl_notnull
%type <expr> decl_defval decl_cursor_query
%type <dtype> decl_datatype
%type <row> decl_cursor_args
--- 101,107 ----
%type <declhdr> decl_sect
%type <varname> decl_varname
%type <str> decl_renname
! %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,158 ****
--- 154,160 ----
%token K_BEGIN
%token K_CLOSE
%token K_CONSTANT
+ %token K_CONTINUE
%token K_CURSOR
%token K_DEBUG
%token K_DECLARE
***************
*** 514,522 ****
;
decl_const :
! { $$ = 0; }
| K_CONSTANT
! { $$ = 1; }
;
decl_datatype :
--- 516,524 ----
;
decl_const :
! { $$ = false; }
| K_CONSTANT
! { $$ = true; }
;
decl_datatype :
***************
*** 531,539 ****
;
decl_notnull :
! { $$ = 0; }
| K_NOT K_NULL
! { $$ = 1; }
;
decl_defval : ';'
--- 533,541 ----
;
decl_notnull :
! { $$ = false; }
| K_NOT K_NULL
! { $$ = true; }
;
decl_defval : ';'
***************
*** 1035,1047 ****
}
;
! stmt_exit : K_EXIT 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->label = $3;
new->cond = $4;
--- 1037,1050 ----
}
;
! 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->is_exit = $1;
! new->lineno = $2;
new->label = $3;
new->cond = $4;
***************
*** 1049,1054 ****
--- 1052,1067 ----
}
;
+ exit_type : K_EXIT
+ {
+ $$ = true;
+ }
+ | K_CONTINUE
+ {
+ $$ = false;
+ }
+ ;
+
stmt_return : K_RETURN lno
{
PLpgSQL_stmt_return *new;
***************
*** 1056,1063 ****
new = palloc0(sizeof(PLpgSQL_stmt_return));
new->cmd_type = PLPGSQL_STMT_RETURN;
new->lineno = $2;
! new->expr = NULL;
! new->retvarno = -1;
if (plpgsql_curr_compile->fn_retset)
{
--- 1069,1076 ----
new = palloc0(sizeof(PLpgSQL_stmt_return));
new->cmd_type = PLPGSQL_STMT_RETURN;
new->lineno = $2;
! new->expr = NULL;
! new->retvarno = -1;
if (plpgsql_curr_compile->fn_retset)
{
Index: src/pl/plpgsql/src/pl_exec.c
===================================================================
RCS file: /var/lib/cvs/pgsql/src/pl/plpgsql/src/pl_exec.c,v
retrieving revision 1.146
diff -c -r1.146 pl_exec.c
*** src/pl/plpgsql/src/pl_exec.c 20 Jun 2005 22:51:29 -0000 1.146
--- src/pl/plpgsql/src/pl_exec.c 21 Jun 2005 08:01:39 -0000
***************
*** 194,199 ****
--- 194,200 ----
PLpgSQL_execstate estate;
ErrorContextCallback plerrcontext;
int i;
+ int rc;
/*
* Setup the execution state
***************
*** 282,294 ****
*/
estate.err_text = NULL;
estate.err_stmt = (PLpgSQL_stmt *) (func->action);
! if (exec_stmt_block(&estate, func->action) != 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")));
}
/*
--- 283,306 ----
*/
estate.err_text = NULL;
estate.err_stmt = (PLpgSQL_stmt *) (func->action);
! rc = exec_stmt_block(&estate, func->action);
! if (rc != PLPGSQL_RC_RETURN)
{
estate.err_stmt = NULL;
estate.err_text = NULL;
!
! /*
! * Provide a more helpful message if a CONTINUE has been used
! * outside a loop.
! */
! if (rc == PLPGSQL_RC_CONTINUE)
! ereport(ERROR,
! (errcode(ERRCODE_PLPGSQL_ERROR), /* XXX: sqlstate? */
! 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,398 ****
--- 405,411 ----
PLpgSQL_execstate estate;
ErrorContextCallback plerrcontext;
int i;
+ int rc;
PLpgSQL_var *var;
PLpgSQL_rec *rec_new,
*rec_old;
***************
*** 546,558 ****
*/
estate.err_text = NULL;
estate.err_stmt = (PLpgSQL_stmt *) (func->action);
! if (exec_stmt_block(&estate, func->action) != 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")));
}
if (estate.retisset)
--- 559,582 ----
*/
estate.err_text = NULL;
estate.err_stmt = (PLpgSQL_stmt *) (func->action);
! rc = exec_stmt_block(&estate, func->action);
! if (rc != PLPGSQL_RC_RETURN)
{
estate.err_stmt = NULL;
estate.err_text = NULL;
!
! /*
! * Provide a more helpful message if a CONTINUE has been used
! * outside a loop.
! */
! if (rc == PLPGSQL_RC_CONTINUE)
! ereport(ERROR,
! (errcode(ERRCODE_PLPGSQL_ERROR), /* XXX: sqlstate? */
! 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,925 ****
switch (rc)
{
case PLPGSQL_RC_OK:
! return PLPGSQL_RC_OK;
case PLPGSQL_RC_EXIT:
if (estate->exitlabel == NULL)
--- 943,951 ----
switch (rc)
{
case PLPGSQL_RC_OK:
! case PLPGSQL_RC_CONTINUE:
! case PLPGSQL_RC_RETURN:
! return rc;
case PLPGSQL_RC_EXIT:
if (estate->exitlabel == NULL)
***************
*** 930,939 ****
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);
}
--- 956,962 ----
return PLPGSQL_RC_EXIT;
estate->exitlabel = NULL;
return PLPGSQL_RC_OK;
!
default:
elog(ERROR, "unrecognized rc: %d", rc);
}
***************
*** 1120,1126 ****
{
PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
PLpgSQL_datum *var;
! bool isnull = false;
if (diag_item->target <= 0)
continue;
--- 1143,1149 ----
{
PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
PLpgSQL_datum *var;
! bool isnull;
if (diag_item->target <= 0)
continue;
***************
*** 1165,1171 ****
exec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt)
{
bool value;
! bool isnull = false;
value = exec_eval_boolean(estate, stmt->cond, &isnull);
exec_eval_cleanup(estate);
--- 1188,1194 ----
exec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt)
{
bool value;
! bool isnull;
value = exec_eval_boolean(estate, stmt->cond, &isnull);
exec_eval_cleanup(estate);
***************
*** 1209,1218 ****
return PLPGSQL_RC_OK;
if (stmt->label == NULL)
return PLPGSQL_RC_EXIT;
! if (strcmp(stmt->label, estate->exitlabel))
return PLPGSQL_RC_EXIT;
estate->exitlabel = NULL;
return PLPGSQL_RC_OK;
case PLPGSQL_RC_RETURN:
return PLPGSQL_RC_RETURN;
--- 1232,1254 ----
return PLPGSQL_RC_OK;
if (stmt->label == NULL)
return PLPGSQL_RC_EXIT;
! 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,1242 ****
exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
{
bool value;
! bool isnull = false;
int rc;
for (;;)
--- 1272,1278 ----
exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
{
bool value;
! bool isnull;
int rc;
for (;;)
***************
*** 1264,1269 ****
--- 1300,1318 ----
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,1294 ****
PLpgSQL_var *var;
Datum value;
Oid valtype;
! bool isnull = false;
bool found = false;
int rc = PLPGSQL_RC_OK;
--- 1337,1343 ----
PLpgSQL_var *var;
Datum value;
Oid valtype;
! bool isnull;
bool found = false;
int rc = PLPGSQL_RC_OK;
***************
*** 1366,1378 ****
}
/*
! * 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.
*/
break;
}
/*
* Increase/decrease loop var
--- 1415,1449 ----
}
/*
! * 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
***************
*** 1460,1475 ****
*/
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)
--- 1531,1539 ----
*/
rc = exec_stmts(estate, stmt->body);
+
if (rc != PLPGSQL_RC_OK)
{
if (rc == PLPGSQL_RC_EXIT)
{
if (estate->exitlabel == NULL)
***************
*** 1490,1495 ****
--- 1554,1587 ----
* 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,1569 ****
n = estate->eval_processed;
/*
! * If the query didn't return any row, set the target to NULL and
* return.
*/
if (n == 0)
--- 1655,1661 ----
n = estate->eval_processed;
/*
! * If the query didn't return any rows, set the target to NULL and
* return.
*/
if (n == 0)
***************
*** 1586,1613 ****
/* ----------
! * exec_stmt_exit Start exiting loop(s) or blocks
* ----------
*/
static int
exec_stmt_exit(PLpgSQL_execstate *estate, PLpgSQL_stmt_exit *stmt)
{
/*
! * If the exit has a condition, check that it's true
*/
if (stmt->cond != NULL)
{
bool value;
! bool isnull = false;
value = exec_eval_boolean(estate, stmt->cond, &isnull);
exec_eval_cleanup(estate);
! if (isnull || !value)
return PLPGSQL_RC_OK;
}
estate->exitlabel = stmt->label;
! return PLPGSQL_RC_EXIT;
}
--- 1678,1710 ----
/* ----------
! * 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 / continue has a condition, evaluate it
*/
if (stmt->cond != NULL)
{
bool value;
! bool isnull;
value = exec_eval_boolean(estate, stmt->cond, &isnull);
exec_eval_cleanup(estate);
! if (isnull || value == false)
return PLPGSQL_RC_OK;
}
estate->exitlabel = stmt->label;
! if (stmt->is_exit)
! return PLPGSQL_RC_EXIT;
! else
! return PLPGSQL_RC_CONTINUE;
}
***************
*** 2455,2468 ****
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)
--- 2552,2557 ----
***************
*** 2483,2488 ****
--- 2572,2604 ----
* 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;
}
Index: src/pl/plpgsql/src/pl_funcs.c
===================================================================
RCS file: /var/lib/cvs/pgsql/src/pl/plpgsql/src/pl_funcs.c,v
retrieving revision 1.44
diff -c -r1.44 pl_funcs.c
*** src/pl/plpgsql/src/pl_funcs.c 19 Jun 2005 01:06:12 -0000 1.44
--- src/pl/plpgsql/src/pl_funcs.c 21 Jun 2005 07:19:38 -0000
***************
*** 845,851 ****
dump_exit(PLpgSQL_stmt_exit *stmt)
{
dump_ind();
! printf("EXIT lbl='%s'", stmt->label);
if (stmt->cond != NULL)
{
printf(" WHEN ");
--- 845,852 ----
dump_exit(PLpgSQL_stmt_exit *stmt)
{
dump_ind();
! printf("%s label='%s'",
! stmt->is_exit ? "EXIT" : "CONTINUE", stmt->label);
if (stmt->cond != NULL)
{
printf(" WHEN ");
Index: src/pl/plpgsql/src/plpgsql.h
===================================================================
RCS file: /var/lib/cvs/pgsql/src/pl/plpgsql/src/plpgsql.h,v
retrieving revision 1.63
diff -c -r1.63 plpgsql.h
*** src/pl/plpgsql/src/plpgsql.h 14 Jun 2005 06:43:14 -0000 1.63
--- src/pl/plpgsql/src/plpgsql.h 21 Jun 2005 07:19:38 -0000
***************
*** 125,131 ****
{
PLPGSQL_RC_OK,
PLPGSQL_RC_EXIT,
! PLPGSQL_RC_RETURN
};
/* ----------
--- 125,132 ----
{
PLPGSQL_RC_OK,
PLPGSQL_RC_EXIT,
! PLPGSQL_RC_RETURN,
! PLPGSQL_RC_CONTINUE
};
/* ----------
***************
*** 485,493 ****
typedef struct
! { /* EXIT statement */
int cmd_type;
int lineno;
char *label;
PLpgSQL_expr *cond;
} PLpgSQL_stmt_exit;
--- 486,495 ----
typedef struct
! { /* 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,616 ****
bool readonly_func;
TupleDesc rettupdesc;
! char *exitlabel;
Tuplestorestate *tuple_store; /* SRFs accumulate results here */
MemoryContext tuple_store_cxt;
--- 612,619 ----
bool readonly_func;
TupleDesc rettupdesc;
! 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;
Index: src/pl/plpgsql/src/scan.l
===================================================================
RCS file: /var/lib/cvs/pgsql/src/pl/plpgsql/src/scan.l,v
retrieving revision 1.40
diff -c -r1.40 scan.l
*** src/pl/plpgsql/src/scan.l 11 Mar 2005 19:13:43 -0000 1.40
--- src/pl/plpgsql/src/scan.l 21 Jun 2005 07:19:38 -0000
***************
*** 139,144 ****
--- 139,145 ----
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; }
Index: src/test/regress/expected/plpgsql.out
===================================================================
RCS file: /var/lib/cvs/pgsql/src/test/regress/expected/plpgsql.out,v
retrieving revision 1.34
diff -c -r1.34 plpgsql.out
*** src/test/regress/expected/plpgsql.out 14 Jun 2005 06:43:14 -0000 1.34
--- src/test/regress/expected/plpgsql.out 21 Jun 2005 08:16:07 -0000
***************
*** 2491,2493 ****
--- 2491,2608 ----
(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---';
+ while _i < 10 loop
+ _i := _i + 1;
+ continue when _i % 2 = 0;
+ raise notice '%', _i;
+ end loop;
+ raise notice '---4---';
+ for _i in 1..10 loop
+ begin
+ -- finfing outer loop
+ 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"
+ drop function continue_test1();
+ drop function continue_test2();
+ drop table conttesttbl;
Index: src/test/regress/sql/plpgsql.sql
===================================================================
RCS file: /var/lib/cvs/pgsql/src/test/regress/sql/plpgsql.sql,v
retrieving revision 1.29
diff -c -r1.29 plpgsql.sql
*** src/test/regress/sql/plpgsql.sql 14 Jun 2005 06:43:15 -0000 1.29
--- src/test/regress/sql/plpgsql.sql 21 Jun 2005 07:54:17 -0000
***************
*** 2112,2114 ****
--- 2112,2186 ----
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---';
+ while _i < 10 loop
+ _i := _i + 1;
+ continue when _i % 2 = 0;
+ raise notice '%', _i;
+ end loop;
+ raise notice '---4---';
+ for _i in 1..10 loop
+ begin
+ -- finfing outer loop
+ 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();
+
+ drop function continue_test1();
+ drop function continue_test2();
+ drop table conttesttbl;