Commit ebcb4c93 authored by Neil Conway's avatar Neil Conway

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.
parent 7a28de20
<!-- <!--
$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; ...@@ -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; ...@@ -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; ...@@ -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 &gt; 100;
CONTINUE WHEN count &lt; 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>&lt;&lt;<replaceable>label</replaceable>&gt;&gt;</optional> <optional>&lt;&lt;<replaceable>label</replaceable>&gt;&gt;</optional>
WHILE <replaceable>expression</replaceable> LOOP WHILE <replaceable>expression</replaceable> LOOP
......
...@@ -4,7 +4,7 @@ ...@@ -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); ...@@ -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); ...@@ -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); ...@@ -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 ...@@ -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 : ...@@ -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,13 +1037,14 @@ stmt_select : K_SELECT lno ...@@ -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; 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->lineno = $2; new->is_exit = $1;
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 ...@@ -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;
...@@ -1056,8 +1069,8 @@ stmt_return : K_RETURN lno ...@@ -1056,8 +1069,8 @@ stmt_return : K_RETURN lno
new = palloc0(sizeof(PLpgSQL_stmt_return)); new = palloc0(sizeof(PLpgSQL_stmt_return));
new->cmd_type = PLPGSQL_STMT_RETURN; new->cmd_type = PLPGSQL_STMT_RETURN;
new->lineno = $2; new->lineno = $2;
new->expr = NULL; new->expr = NULL;
new->retvarno = -1; new->retvarno = -1;
if (plpgsql_curr_compile->fn_retset) if (plpgsql_curr_compile->fn_retset)
{ {
......
This diff is collapsed.
...@@ -3,7 +3,7 @@ ...@@ -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 ...@@ -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 @@ ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 @@ ...@@ -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; } ...@@ -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> ...@@ -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; ...@@ -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;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment