mirror of
https://github.com/postgres/postgres.git
synced 2025-06-03 00:02:26 -04:00
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
@ -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">
|
<chapter id="plpgsql">
|
||||||
@ -1779,10 +1779,10 @@ END IF;
|
|||||||
</indexterm>
|
</indexterm>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
With the <literal>LOOP</>, <literal>EXIT</>, <literal>WHILE</>,
|
With the <literal>LOOP</>, <literal>EXIT</>,
|
||||||
and <literal>FOR</> statements, you can arrange for your
|
<literal>CONTINUE</>, <literal>WHILE</>, and <literal>FOR</>
|
||||||
<application>PL/pgSQL</application> function to repeat a series
|
statements, you can arrange for your <application>PL/pgSQL</>
|
||||||
of commands.
|
function to repeat a series of commands.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<sect3>
|
<sect3>
|
||||||
@ -1807,30 +1807,36 @@ END LOOP;
|
|||||||
<sect3>
|
<sect3>
|
||||||
<title><literal>EXIT</></title>
|
<title><literal>EXIT</></title>
|
||||||
|
|
||||||
|
<indexterm>
|
||||||
|
<primary>EXIT</primary>
|
||||||
|
<secondary>in PL/pgSQL</secondary>
|
||||||
|
</indexterm>
|
||||||
|
|
||||||
<synopsis>
|
<synopsis>
|
||||||
EXIT <optional> <replaceable>label</replaceable> </optional> <optional> WHEN <replaceable>expression</replaceable> </optional>;
|
EXIT <optional> <replaceable>label</replaceable> </optional> <optional> WHEN <replaceable>expression</replaceable> </optional>;
|
||||||
</synopsis>
|
</synopsis>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
If no <replaceable>label</replaceable> is given,
|
If no <replaceable>label</replaceable> is given, the innermost
|
||||||
the innermost loop is terminated and the
|
loop is terminated and the statement following <literal>END
|
||||||
statement following <literal>END LOOP</> is executed next.
|
LOOP</> is executed next. If <replaceable>label</replaceable>
|
||||||
If <replaceable>label</replaceable> is given, it
|
is given, it must be the label of the current or some outer
|
||||||
must be the label of the current or some outer level of nested loop
|
level of nested loop or block. Then the named loop or block is
|
||||||
or block. Then the named loop or block is terminated and control
|
terminated and control continues with the statement after the
|
||||||
continues with the statement after the loop's/block's corresponding
|
loop's/block's corresponding <literal>END</>.
|
||||||
<literal>END</>.
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
If <literal>WHEN</> is present, loop exit occurs only if the specified
|
If <literal>WHEN</> is specified, the loop exit occurs only if
|
||||||
condition is true, otherwise control passes to the statement after
|
<replaceable>expression</> is true. Otherwise, control passes
|
||||||
<literal>EXIT</>.
|
to the statement after <literal>EXIT</>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
<literal>EXIT</> can be used to cause early exit from all types of
|
<literal>EXIT</> can be used with all types of loops; it is
|
||||||
loops; it is not limited to use with unconditional loops.
|
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>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -1858,9 +1864,61 @@ END;
|
|||||||
</para>
|
</para>
|
||||||
</sect3>
|
</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>
|
<sect3>
|
||||||
<title><literal>WHILE</></title>
|
<title><literal>WHILE</></title>
|
||||||
|
|
||||||
|
<indexterm>
|
||||||
|
<primary>WHILE</primary>
|
||||||
|
<secondary>in PL/pgSQL</secondary>
|
||||||
|
</indexterm>
|
||||||
|
|
||||||
<synopsis>
|
<synopsis>
|
||||||
<optional><<<replaceable>label</replaceable>>></optional>
|
<optional><<<replaceable>label</replaceable>>></optional>
|
||||||
WHILE <replaceable>expression</replaceable> LOOP
|
WHILE <replaceable>expression</replaceable> LOOP
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* procedural language
|
* procedural language
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@ -61,6 +61,7 @@ static void plpgsql_sql_error_callback(void *arg);
|
|||||||
|
|
||||||
%union {
|
%union {
|
||||||
int32 ival;
|
int32 ival;
|
||||||
|
bool boolean;
|
||||||
char *str;
|
char *str;
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
@ -100,7 +101,7 @@ static void plpgsql_sql_error_callback(void *arg);
|
|||||||
%type <declhdr> decl_sect
|
%type <declhdr> decl_sect
|
||||||
%type <varname> decl_varname
|
%type <varname> decl_varname
|
||||||
%type <str> decl_renname
|
%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 <expr> decl_defval decl_cursor_query
|
||||||
%type <dtype> decl_datatype
|
%type <dtype> decl_datatype
|
||||||
%type <row> decl_cursor_args
|
%type <row> decl_cursor_args
|
||||||
@ -153,6 +154,7 @@ static void plpgsql_sql_error_callback(void *arg);
|
|||||||
%token K_BEGIN
|
%token K_BEGIN
|
||||||
%token K_CLOSE
|
%token K_CLOSE
|
||||||
%token K_CONSTANT
|
%token K_CONSTANT
|
||||||
|
%token K_CONTINUE
|
||||||
%token K_CURSOR
|
%token K_CURSOR
|
||||||
%token K_DEBUG
|
%token K_DEBUG
|
||||||
%token K_DECLARE
|
%token K_DECLARE
|
||||||
@ -514,9 +516,9 @@ decl_renname : T_WORD
|
|||||||
;
|
;
|
||||||
|
|
||||||
decl_const :
|
decl_const :
|
||||||
{ $$ = 0; }
|
{ $$ = false; }
|
||||||
| K_CONSTANT
|
| K_CONSTANT
|
||||||
{ $$ = 1; }
|
{ $$ = true; }
|
||||||
;
|
;
|
||||||
|
|
||||||
decl_datatype :
|
decl_datatype :
|
||||||
@ -531,9 +533,9 @@ decl_datatype :
|
|||||||
;
|
;
|
||||||
|
|
||||||
decl_notnull :
|
decl_notnull :
|
||||||
{ $$ = 0; }
|
{ $$ = false; }
|
||||||
| K_NOT K_NULL
|
| K_NOT K_NULL
|
||||||
{ $$ = 1; }
|
{ $$ = true; }
|
||||||
;
|
;
|
||||||
|
|
||||||
decl_defval : ';'
|
decl_defval : ';'
|
||||||
@ -1035,12 +1037,13 @@ 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;
|
PLpgSQL_stmt_exit *new;
|
||||||
|
|
||||||
new = palloc0(sizeof(PLpgSQL_stmt_exit));
|
new = palloc0(sizeof(PLpgSQL_stmt_exit));
|
||||||
new->cmd_type = PLPGSQL_STMT_EXIT;
|
new->cmd_type = PLPGSQL_STMT_EXIT;
|
||||||
|
new->is_exit = $1;
|
||||||
new->lineno = $2;
|
new->lineno = $2;
|
||||||
new->label = $3;
|
new->label = $3;
|
||||||
new->cond = $4;
|
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
|
stmt_return : K_RETURN lno
|
||||||
{
|
{
|
||||||
PLpgSQL_stmt_return *new;
|
PLpgSQL_stmt_return *new;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* procedural language
|
* procedural language
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@ -194,6 +194,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
|
|||||||
PLpgSQL_execstate estate;
|
PLpgSQL_execstate estate;
|
||||||
ErrorContextCallback plerrcontext;
|
ErrorContextCallback plerrcontext;
|
||||||
int i;
|
int i;
|
||||||
|
int rc;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Setup the execution state
|
* Setup the execution state
|
||||||
@ -282,10 +283,21 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo)
|
|||||||
*/
|
*/
|
||||||
estate.err_text = NULL;
|
estate.err_text = NULL;
|
||||||
estate.err_stmt = (PLpgSQL_stmt *) (func->action);
|
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_stmt = NULL;
|
||||||
estate.err_text = 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_SYNTAX_ERROR),
|
||||||
|
errmsg("CONTINUE cannot be used outside a loop")));
|
||||||
|
else
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
|
(errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
|
||||||
errmsg("control reached end of function without RETURN")));
|
errmsg("control reached end of function without RETURN")));
|
||||||
@ -393,6 +405,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
|
|||||||
PLpgSQL_execstate estate;
|
PLpgSQL_execstate estate;
|
||||||
ErrorContextCallback plerrcontext;
|
ErrorContextCallback plerrcontext;
|
||||||
int i;
|
int i;
|
||||||
|
int rc;
|
||||||
PLpgSQL_var *var;
|
PLpgSQL_var *var;
|
||||||
PLpgSQL_rec *rec_new,
|
PLpgSQL_rec *rec_new,
|
||||||
*rec_old;
|
*rec_old;
|
||||||
@ -546,10 +559,21 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
|
|||||||
*/
|
*/
|
||||||
estate.err_text = NULL;
|
estate.err_text = NULL;
|
||||||
estate.err_stmt = (PLpgSQL_stmt *) (func->action);
|
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_stmt = NULL;
|
||||||
estate.err_text = 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_SYNTAX_ERROR),
|
||||||
|
errmsg("CONTINUE cannot be used outside a loop")));
|
||||||
|
else
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
|
(errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT),
|
||||||
errmsg("control reached end of trigger procedure without RETURN")));
|
errmsg("control reached end of trigger procedure without RETURN")));
|
||||||
@ -919,7 +943,9 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
|
|||||||
switch (rc)
|
switch (rc)
|
||||||
{
|
{
|
||||||
case PLPGSQL_RC_OK:
|
case PLPGSQL_RC_OK:
|
||||||
return PLPGSQL_RC_OK;
|
case PLPGSQL_RC_CONTINUE:
|
||||||
|
case PLPGSQL_RC_RETURN:
|
||||||
|
return rc;
|
||||||
|
|
||||||
case PLPGSQL_RC_EXIT:
|
case PLPGSQL_RC_EXIT:
|
||||||
if (estate->exitlabel == NULL)
|
if (estate->exitlabel == NULL)
|
||||||
@ -931,9 +957,6 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
|
|||||||
estate->exitlabel = NULL;
|
estate->exitlabel = NULL;
|
||||||
return PLPGSQL_RC_OK;
|
return PLPGSQL_RC_OK;
|
||||||
|
|
||||||
case PLPGSQL_RC_RETURN:
|
|
||||||
return PLPGSQL_RC_RETURN;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unrecognized rc: %d", rc);
|
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_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc);
|
||||||
PLpgSQL_datum *var;
|
PLpgSQL_datum *var;
|
||||||
bool isnull = false;
|
bool isnull;
|
||||||
|
|
||||||
if (diag_item->target <= 0)
|
if (diag_item->target <= 0)
|
||||||
continue;
|
continue;
|
||||||
@ -1165,7 +1188,7 @@ static int
|
|||||||
exec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt)
|
exec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt)
|
||||||
{
|
{
|
||||||
bool value;
|
bool value;
|
||||||
bool isnull = false;
|
bool isnull;
|
||||||
|
|
||||||
value = exec_eval_boolean(estate, stmt->cond, &isnull);
|
value = exec_eval_boolean(estate, stmt->cond, &isnull);
|
||||||
exec_eval_cleanup(estate);
|
exec_eval_cleanup(estate);
|
||||||
@ -1209,11 +1232,24 @@ exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt)
|
|||||||
return PLPGSQL_RC_OK;
|
return PLPGSQL_RC_OK;
|
||||||
if (stmt->label == NULL)
|
if (stmt->label == NULL)
|
||||||
return PLPGSQL_RC_EXIT;
|
return PLPGSQL_RC_EXIT;
|
||||||
if (strcmp(stmt->label, estate->exitlabel))
|
if (strcmp(stmt->label, estate->exitlabel) != 0)
|
||||||
return PLPGSQL_RC_EXIT;
|
return PLPGSQL_RC_EXIT;
|
||||||
estate->exitlabel = NULL;
|
estate->exitlabel = NULL;
|
||||||
return PLPGSQL_RC_OK;
|
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:
|
case PLPGSQL_RC_RETURN:
|
||||||
return PLPGSQL_RC_RETURN;
|
return PLPGSQL_RC_RETURN;
|
||||||
|
|
||||||
@ -1236,7 +1272,7 @@ static int
|
|||||||
exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
|
exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
|
||||||
{
|
{
|
||||||
bool value;
|
bool value;
|
||||||
bool isnull = false;
|
bool isnull;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
@ -1264,6 +1300,19 @@ exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
|
|||||||
estate->exitlabel = NULL;
|
estate->exitlabel = NULL;
|
||||||
return PLPGSQL_RC_OK;
|
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:
|
case PLPGSQL_RC_RETURN:
|
||||||
return PLPGSQL_RC_RETURN;
|
return PLPGSQL_RC_RETURN;
|
||||||
|
|
||||||
@ -1288,7 +1337,7 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
|
|||||||
PLpgSQL_var *var;
|
PLpgSQL_var *var;
|
||||||
Datum value;
|
Datum value;
|
||||||
Oid valtype;
|
Oid valtype;
|
||||||
bool isnull = false;
|
bool isnull;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
int rc = PLPGSQL_RC_OK;
|
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
|
* otherwise, this is a labelled exit that does not match
|
||||||
* the current statement's label, if any: return RC_EXIT so
|
* the current statement's label, if any: return RC_EXIT
|
||||||
* that the EXIT continues to recurse upward.
|
* so that the EXIT continues to propagate up the stack.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
break;
|
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
|
* Increase/decrease loop var
|
||||||
@ -1459,17 +1530,8 @@ exec_stmt_fors(PLpgSQL_execstate *estate, PLpgSQL_stmt_fors *stmt)
|
|||||||
* Execute the statements
|
* Execute the statements
|
||||||
*/
|
*/
|
||||||
rc = exec_stmts(estate, stmt->body);
|
rc = exec_stmts(estate, stmt->body);
|
||||||
|
|
||||||
if (rc != PLPGSQL_RC_OK)
|
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 (rc == PLPGSQL_RC_EXIT)
|
||||||
{
|
{
|
||||||
if (estate->exitlabel == NULL)
|
if (estate->exitlabel == NULL)
|
||||||
@ -1490,6 +1552,34 @@ exec_stmt_fors(PLpgSQL_execstate *estate, PLpgSQL_stmt_fors *stmt)
|
|||||||
* recurse upward.
|
* 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;
|
return rc;
|
||||||
}
|
}
|
||||||
@ -1563,7 +1653,7 @@ exec_stmt_select(PLpgSQL_execstate *estate, PLpgSQL_stmt_select *stmt)
|
|||||||
n = estate->eval_processed;
|
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.
|
* return.
|
||||||
*/
|
*/
|
||||||
if (n == 0)
|
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
|
static int
|
||||||
exec_stmt_exit(PLpgSQL_execstate *estate, PLpgSQL_stmt_exit *stmt)
|
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)
|
if (stmt->cond != NULL)
|
||||||
{
|
{
|
||||||
bool value;
|
bool value;
|
||||||
bool isnull = false;
|
bool isnull;
|
||||||
|
|
||||||
value = exec_eval_boolean(estate, stmt->cond, &isnull);
|
value = exec_eval_boolean(estate, stmt->cond, &isnull);
|
||||||
exec_eval_cleanup(estate);
|
exec_eval_cleanup(estate);
|
||||||
if (isnull || !value)
|
if (isnull || value == false)
|
||||||
return PLPGSQL_RC_OK;
|
return PLPGSQL_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
estate->exitlabel = stmt->label;
|
estate->exitlabel = stmt->label;
|
||||||
|
if (stmt->is_exit)
|
||||||
return PLPGSQL_RC_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)
|
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 (rc == PLPGSQL_RC_EXIT)
|
||||||
{
|
{
|
||||||
if (estate->exitlabel == NULL)
|
if (estate->exitlabel == NULL)
|
||||||
@ -2483,6 +2570,33 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt)
|
|||||||
* recurse upward.
|
* 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;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* procedural language
|
* procedural language
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@ -845,7 +845,8 @@ static void
|
|||||||
dump_exit(PLpgSQL_stmt_exit *stmt)
|
dump_exit(PLpgSQL_stmt_exit *stmt)
|
||||||
{
|
{
|
||||||
dump_ind();
|
dump_ind();
|
||||||
printf("EXIT lbl='%s'", stmt->label);
|
printf("%s label='%s'",
|
||||||
|
stmt->is_exit ? "EXIT" : "CONTINUE", stmt->label);
|
||||||
if (stmt->cond != NULL)
|
if (stmt->cond != NULL)
|
||||||
{
|
{
|
||||||
printf(" WHEN ");
|
printf(" WHEN ");
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* procedural language
|
* procedural language
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@ -125,7 +125,8 @@ enum
|
|||||||
{
|
{
|
||||||
PLPGSQL_RC_OK,
|
PLPGSQL_RC_OK,
|
||||||
PLPGSQL_RC_EXIT,
|
PLPGSQL_RC_EXIT,
|
||||||
PLPGSQL_RC_RETURN
|
PLPGSQL_RC_RETURN,
|
||||||
|
PLPGSQL_RC_CONTINUE
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
@ -485,9 +486,10 @@ typedef struct
|
|||||||
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{ /* EXIT statement */
|
{ /* EXIT or CONTINUE statement */
|
||||||
int cmd_type;
|
int cmd_type;
|
||||||
int lineno;
|
int lineno;
|
||||||
|
bool is_exit; /* Is this an exit or a continue? */
|
||||||
char *label;
|
char *label;
|
||||||
PLpgSQL_expr *cond;
|
PLpgSQL_expr *cond;
|
||||||
} PLpgSQL_stmt_exit;
|
} PLpgSQL_stmt_exit;
|
||||||
@ -610,7 +612,8 @@ typedef struct
|
|||||||
bool readonly_func;
|
bool readonly_func;
|
||||||
|
|
||||||
TupleDesc rettupdesc;
|
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 */
|
Tuplestorestate *tuple_store; /* SRFs accumulate results here */
|
||||||
MemoryContext tuple_store_cxt;
|
MemoryContext tuple_store_cxt;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* procedural language
|
* procedural language
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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.
|
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||||
*
|
*
|
||||||
@ -139,6 +139,7 @@ alias { return K_ALIAS; }
|
|||||||
begin { return K_BEGIN; }
|
begin { return K_BEGIN; }
|
||||||
close { return K_CLOSE; }
|
close { return K_CLOSE; }
|
||||||
constant { return K_CONSTANT; }
|
constant { return K_CONSTANT; }
|
||||||
|
continue { return K_CONTINUE; }
|
||||||
cursor { return K_CURSOR; }
|
cursor { return K_CURSOR; }
|
||||||
debug { return K_DEBUG; }
|
debug { return K_DEBUG; }
|
||||||
declare { return K_DECLARE; }
|
declare { return K_DECLARE; }
|
||||||
|
@ -2491,3 +2491,140 @@ NOTICE: {10,20,30}; 20; xyz; xyzabc; (10,aaa,,30); <NULL>
|
|||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
drop function raise_exprs();
|
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();
|
select raise_exprs();
|
||||||
drop function 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