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.
This commit is contained in:
parent
7a28de2052
commit
ebcb4c931d
doc/src/sgml
src
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.73 2005/06/19 23:39:05 neilc Exp $
|
||||
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.74 2005/06/22 01:35:02 neilc Exp $
|
||||
-->
|
||||
|
||||
<chapter id="plpgsql">
|
||||
@ -1779,10 +1779,10 @@ END IF;
|
||||
</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.
|
||||
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,30 +1807,36 @@ END LOOP;
|
||||
<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</>.
|
||||
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</>.
|
||||
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 to cause early exit from all types of
|
||||
loops; it is not limited to use with unconditional loops.
|
||||
<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>
|
||||
@ -1858,9 +1864,61 @@ END;
|
||||
</para>
|
||||
</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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 ");
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user