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">
......@@ -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 &gt; 100;
CONTINUE WHEN count &lt; 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>&lt;&lt;<replaceable>label</replaceable>&gt;&gt;</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,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;
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,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;
......
This diff is collapsed.
......@@ -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;
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