Index: doc/src/sgml/plpgsql.sgml
===================================================================
RCS file: /Users/neilc/local/cvs/pgsql/doc/src/sgml/plpgsql.sgml,v
retrieving revision 1.74
diff -c -r1.74 plpgsql.sgml
*** doc/src/sgml/plpgsql.sgml 22 Jun 2005 01:35:02 -0000 1.74
--- doc/src/sgml/plpgsql.sgml 1 Jul 2005 11:43:36 -0000
***************
*** 456,462 ****
declarations
BEGIN
statements
! END;
--- 456,462 ----
declarations
BEGIN
statements
! END label ;
***************
*** 1789,1806 ****
LOOP>
! <<label>>
LOOP
statements
! END LOOP;
! LOOP> defines an unconditional loop that is repeated indefinitely
! until terminated by an EXIT> or RETURN
! statement. The optional label can be used by EXIT> statements in
! nested loops to specify which level of nesting should be
! terminated.
--- 1789,1807 ----
LOOP>
! <<label>>
LOOP
statements
! END LOOP label ;
! LOOP> defines an unconditional loop that is repeated
! indefinitely until terminated by an EXIT> or
! RETURN statement. The optional
! label can be used by EXIT>
! and CONTINUE statements in nested loops to
! specify which loop the statement should be applied to.
***************
*** 1920,1929 ****
! <<label>>
WHILE expression LOOP
statements
! END LOOP;
--- 1921,1930 ----
! <<label>>
WHILE expression LOOP
statements
! END LOOP label ;
***************
*** 1951,1960 ****
FOR> (integer variant)
! <<label>>
FOR name IN REVERSE expression .. expression LOOP
statements
! END LOOP;
--- 1952,1961 ----
FOR> (integer variant)
! <<label>>
FOR name IN REVERSE expression .. expression LOOP
statements
! END LOOP labal ;
***************
*** 1997,2006 ****
the results of a query and manipulate that data
accordingly. The syntax is:
! <<label>>
FOR record_or_row IN query LOOP
statements
! END LOOP;
The record or row variable is successively assigned each row
resulting from the query (which must be a
--- 1998,2007 ----
the results of a query and manipulate that data
accordingly. The syntax is:
! <<label>>
FOR record_or_row IN query LOOP
statements
! END LOOP label ;
The record or row variable is successively assigned each row
resulting from the query (which must be a
***************
*** 2036,2045 ****
The FOR-IN-EXECUTE> statement is another way to iterate over
rows:
! <<label>>
FOR record_or_row IN EXECUTE text_expression LOOP
statements
! END LOOP;
This is like the previous form, except that the source
SELECT statement is specified as a string
--- 2037,2046 ----
The FOR-IN-EXECUTE> statement is another way to iterate over
rows:
! <<label>>
FOR record_or_row IN EXECUTE text_expression LOOP
statements
! END LOOP label ;
This is like the previous form, except that the source
SELECT statement is specified as a string
Index: src/pl/plpgsql/src/gram.y
===================================================================
RCS file: /Users/neilc/local/cvs/pgsql/src/pl/plpgsql/src/gram.y,v
retrieving revision 1.77
diff -c -r1.77 gram.y
*** src/pl/plpgsql/src/gram.y 22 Jun 2005 01:35:02 -0000 1.77
--- src/pl/plpgsql/src/gram.y 1 Jul 2005 12:08:45 -0000
***************
*** 56,61 ****
--- 56,63 ----
PLpgSQL_datum *initial_datum);
static void check_sql_expr(const char *stmt);
static void plpgsql_sql_error_callback(void *arg);
+ static void check_labels(const char *start_label,
+ const char *end_label);
%}
***************
*** 69,75 ****
int lineno;
} varname;
struct
! {
char *name;
int lineno;
PLpgSQL_rec *rec;
--- 71,77 ----
int lineno;
} varname;
struct
! {
char *name;
int lineno;
PLpgSQL_rec *rec;
***************
*** 81,86 ****
--- 83,93 ----
int n_initvars;
int *initvarnos;
} declhdr;
+ struct
+ {
+ char *end_label;
+ List *stmts;
+ } loop_body;
List *list;
PLpgSQL_type *dtype;
PLpgSQL_datum *scalar; /* a VAR, RECFIELD, or TRIGARG */
***************
*** 119,129 ****
%type for_variable
%type for_control
! %type opt_lblname opt_label
! %type opt_exitlabel
%type execsql_start
! %type proc_sect proc_stmts stmt_else loop_body
%type proc_stmt pl_block
%type stmt_assign stmt_if stmt_loop stmt_while stmt_exit
%type stmt_return stmt_return_next stmt_raise stmt_execsql
--- 126,136 ----
%type for_variable
%type for_control
! %type opt_lblname opt_block_label opt_label
%type execsql_start
! %type proc_sect proc_stmts stmt_else
! %type loop_body
%type proc_stmt pl_block
%type stmt_assign stmt_if stmt_loop stmt_while stmt_exit
%type stmt_return stmt_return_next stmt_raise stmt_execsql
***************
*** 248,257 ****
| ';'
;
! pl_block : decl_sect K_BEGIN lno proc_sect exception_sect K_END
{
PLpgSQL_stmt_block *new;
new = palloc0(sizeof(PLpgSQL_stmt_block));
new->cmd_type = PLPGSQL_STMT_BLOCK;
--- 255,266 ----
| ';'
;
! pl_block : decl_sect K_BEGIN lno proc_sect exception_sect K_END opt_label
{
PLpgSQL_stmt_block *new;
+ check_labels($1.label, $7);
+
new = palloc0(sizeof(PLpgSQL_stmt_block));
new->cmd_type = PLPGSQL_STMT_BLOCK;
***************
*** 269,275 ****
;
! decl_sect : opt_label
{
plpgsql_ns_setlocal(false);
$$.label = $1;
--- 278,284 ----
;
! decl_sect : opt_block_label
{
plpgsql_ns_setlocal(false);
$$.label = $1;
***************
*** 277,283 ****
$$.initvarnos = NULL;
plpgsql_add_initdatums(NULL);
}
! | opt_label decl_start
{
plpgsql_ns_setlocal(false);
$$.label = $1;
--- 286,292 ----
$$.initvarnos = NULL;
plpgsql_add_initdatums(NULL);
}
! | opt_block_label decl_start
{
plpgsql_ns_setlocal(false);
$$.label = $1;
***************
*** 285,291 ****
$$.initvarnos = NULL;
plpgsql_add_initdatums(NULL);
}
! | opt_label decl_start decl_stmts
{
plpgsql_ns_setlocal(false);
if ($3 != NULL)
--- 294,300 ----
$$.initvarnos = NULL;
plpgsql_add_initdatums(NULL);
}
! | opt_block_label decl_start decl_stmts
{
plpgsql_ns_setlocal(false);
if ($3 != NULL)
***************
*** 409,415 ****
plpgsql_ns_setlocal(false);
query = read_sql_stmt("");
plpgsql_ns_setlocal(true);
!
$$ = query;
}
;
--- 418,424 ----
plpgsql_ns_setlocal(false);
query = read_sql_stmt("");
plpgsql_ns_setlocal(true);
!
$$ = query;
}
;
***************
*** 757,763 ****
* ... ...
* ELSE ELSE
* ... ...
! * END IF END IF
* END IF
*/
PLpgSQL_stmt_if *new_if;
--- 766,772 ----
* ... ...
* ELSE ELSE
* ... ...
! * END IF END IF
* END IF
*/
PLpgSQL_stmt_if *new_if;
***************
*** 776,786 ****
| K_ELSE proc_sect
{
! $$ = $2;
}
;
! stmt_loop : opt_label K_LOOP lno loop_body
{
PLpgSQL_stmt_loop *new;
--- 785,795 ----
| K_ELSE proc_sect
{
! $$ = $2;
}
;
! stmt_loop : opt_block_label K_LOOP lno loop_body
{
PLpgSQL_stmt_loop *new;
***************
*** 788,802 ****
new->cmd_type = PLPGSQL_STMT_LOOP;
new->lineno = $3;
new->label = $1;
! new->body = $4;
plpgsql_ns_pop();
$$ = (PLpgSQL_stmt *)new;
}
;
! stmt_while : opt_label K_WHILE lno expr_until_loop loop_body
{
PLpgSQL_stmt_while *new;
--- 797,812 ----
new->cmd_type = PLPGSQL_STMT_LOOP;
new->lineno = $3;
new->label = $1;
! new->body = $4.stmts;
+ check_labels($1, $4.end_label);
plpgsql_ns_pop();
$$ = (PLpgSQL_stmt *)new;
}
;
! stmt_while : opt_block_label K_WHILE lno expr_until_loop loop_body
{
PLpgSQL_stmt_while *new;
***************
*** 805,819 ****
new->lineno = $3;
new->label = $1;
new->cond = $4;
! new->body = $5;
plpgsql_ns_pop();
$$ = (PLpgSQL_stmt *)new;
}
;
! stmt_for : opt_label K_FOR for_control loop_body
{
/* This runs after we've scanned the loop body */
if ($3->cmd_type == PLPGSQL_STMT_FORI)
--- 815,830 ----
new->lineno = $3;
new->label = $1;
new->cond = $4;
! new->body = $5.stmts;
+ check_labels($1, $5.end_label);
plpgsql_ns_pop();
$$ = (PLpgSQL_stmt *)new;
}
;
! stmt_for : opt_block_label K_FOR for_control loop_body
{
/* This runs after we've scanned the loop body */
if ($3->cmd_type == PLPGSQL_STMT_FORI)
***************
*** 822,828 ****
new = (PLpgSQL_stmt_fori *) $3;
new->label = $1;
! new->body = $4;
$$ = (PLpgSQL_stmt *) new;
}
else if ($3->cmd_type == PLPGSQL_STMT_FORS)
--- 833,839 ----
new = (PLpgSQL_stmt_fori *) $3;
new->label = $1;
! new->body = $4.stmts;
$$ = (PLpgSQL_stmt *) new;
}
else if ($3->cmd_type == PLPGSQL_STMT_FORS)
***************
*** 831,837 ****
new = (PLpgSQL_stmt_fors *) $3;
new->label = $1;
! new->body = $4;
$$ = (PLpgSQL_stmt *) new;
}
else
--- 842,848 ----
new = (PLpgSQL_stmt_fors *) $3;
new->label = $1;
! new->body = $4.stmts;
$$ = (PLpgSQL_stmt *) new;
}
else
***************
*** 841,850 ****
Assert($3->cmd_type == PLPGSQL_STMT_DYNFORS);
new = (PLpgSQL_stmt_dynfors *) $3;
new->label = $1;
! new->body = $4;
$$ = (PLpgSQL_stmt *) new;
}
/* close namespace started in opt_label */
plpgsql_ns_pop();
}
--- 852,862 ----
Assert($3->cmd_type == PLPGSQL_STMT_DYNFORS);
new = (PLpgSQL_stmt_dynfors *) $3;
new->label = $1;
! new->body = $4.stmts;
$$ = (PLpgSQL_stmt *) new;
}
+ check_labels($1, $4.end_label);
/* close namespace started in opt_label */
plpgsql_ns_pop();
}
***************
*** 1037,1043 ****
}
;
! stmt_exit : exit_type lno opt_exitlabel opt_exitcond
{
PLpgSQL_stmt_exit *new;
--- 1049,1055 ----
}
;
! stmt_exit : exit_type lno opt_label opt_exitcond
{
PLpgSQL_stmt_exit *new;
***************
*** 1245,1252 ****
}
;
! loop_body : proc_sect K_END K_LOOP ';'
! { $$ = $1; }
;
stmt_execsql : execsql_start lno
--- 1257,1267 ----
}
;
! loop_body : proc_sect K_END K_LOOP opt_label ';'
! {
! $$.stmts = $1;
! $$.end_label = $4;
! }
;
stmt_execsql : execsql_start lno
***************
*** 1262,1268 ****
}
;
! stmt_dynexecute : K_EXECUTE lno
{
PLpgSQL_stmt_dynexecute *new;
PLpgSQL_expr *expr;
--- 1277,1283 ----
}
;
! stmt_dynexecute : K_EXECUTE lno
{
PLpgSQL_stmt_dynexecute *new;
PLpgSQL_expr *expr;
***************
*** 1418,1424 ****
errmsg("cursor \"%s\" has no arguments",
$3->refname)));
}
!
if (tok != ';')
{
plpgsql_error_lineno = plpgsql_scanner_lineno();
--- 1433,1439 ----
errmsg("cursor \"%s\" has no arguments",
$3->refname)));
}
!
if (tok != ';')
{
plpgsql_error_lineno = plpgsql_scanner_lineno();
***************
*** 1596,1602 ****
{ $$ = plpgsql_read_expression(K_LOOP, "LOOP"); }
;
! opt_label :
{
plpgsql_ns_push(NULL);
$$ = NULL;
--- 1611,1617 ----
{ $$ = plpgsql_read_expression(K_LOOP, "LOOP"); }
;
! opt_block_label :
{
plpgsql_ns_push(NULL);
$$ = NULL;
***************
*** 1608,1621 ****
}
;
! opt_exitlabel :
! { $$ = NULL; }
| T_LABEL
{
! char *name;
!
! plpgsql_convert_ident(yytext, &name, 1);
! $$ = name;
}
| T_WORD
{
--- 1623,1637 ----
}
;
! opt_label :
! {
! $$ = NULL;
! }
| T_LABEL
{
! char *label_name;
! plpgsql_convert_ident(yytext, &label_name, 1);
! $$ = label_name;
}
| T_WORD
{
***************
*** 2210,2213 ****
--- 2226,2254 ----
errposition(0);
}
+ static void
+ check_labels(const char *start_label, const char *end_label)
+ {
+ if (end_label)
+ {
+ if (!start_label)
+ {
+ plpgsql_error_lineno = plpgsql_scanner_lineno();
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("end label \"%s\" specified for unlabelled block",
+ end_label)));
+ }
+
+ if (strcmp(start_label, end_label) != 0)
+ {
+ plpgsql_error_lineno = plpgsql_scanner_lineno();
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("end label \"%s\" differs from block's label \"%s\"",
+ end_label, start_label)));
+ }
+ }
+ }
+
#include "pl_scan.c"
Index: src/test/regress/expected/plpgsql.out
===================================================================
RCS file: /Users/neilc/local/cvs/pgsql/src/test/regress/expected/plpgsql.out,v
retrieving revision 1.36
diff -c -r1.36 plpgsql.out
*** src/test/regress/expected/plpgsql.out 22 Jun 2005 07:28:47 -0000 1.36
--- src/test/regress/expected/plpgsql.out 1 Jul 2005 11:53:55 -0000
***************
*** 2491,2497 ****
(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);
--- 2491,2497 ----
(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);
***************
*** 2532,2538 ****
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;
--- 2532,2538 ----
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;
***************
*** 2666,2668 ****
--- 2666,2723 ----
drop function continue_test2();
drop function continue_test3();
drop table conttesttbl;
+ -- verbose end block and end loop
+ create function end_label1() returns void as $$
+ <>
+ begin
+ <>
+ for _i in 1 .. 10 loop
+ exit flbl1;
+ end loop flbl1;
+ <>
+ for _i in 1 .. 10 loop
+ exit flbl2;
+ end loop;
+ end blbl;
+ $$ language plpgsql;
+ select end_label1();
+ end_label1
+ ------------
+
+ (1 row)
+
+ drop function end_label1();
+ -- should fail: undefined end label
+ create function end_label2() returns void as $$
+ begin
+ for _i in 1 .. 10 loop
+ exit;
+ end loop flbl1;
+ end;
+ $$ language plpgsql;
+ ERROR: no such label at or near "flbl1" at character 101
+ LINE 5: end loop flbl1;
+ ^
+ -- should fail: end label does not match start label
+ create function end_label3() returns void as $$
+ <>
+ begin
+ <>
+ for _i in 1 .. 10 loop
+ exit;
+ end loop outer_label;
+ end;
+ $$ language plpgsql;
+ ERROR: end label "outer_label" differs from block's label "inner_label"
+ CONTEXT: compile of PL/pgSQL function "end_label3" near line 6
+ -- should fail: end label on a block without a start label
+ create function end_label4() returns void as $$
+ <>
+ begin
+ for _i in 1 .. 10 loop
+ exit;
+ end loop outer_label;
+ end;
+ $$ language plpgsql;
+ ERROR: end label "outer_label" specified for unlabelled block
+ CONTEXT: compile of PL/pgSQL function "end_label4" near line 5
Index: src/test/regress/sql/plpgsql.sql
===================================================================
RCS file: /Users/neilc/local/cvs/pgsql/src/test/regress/sql/plpgsql.sql,v
retrieving revision 1.31
diff -c -r1.31 plpgsql.sql
*** src/test/regress/sql/plpgsql.sql 22 Jun 2005 07:28:47 -0000 1.31
--- src/test/regress/sql/plpgsql.sql 1 Jul 2005 11:43:36 -0000
***************
*** 2113,2119 ****
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);
--- 2113,2119 ----
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);
***************
*** 2154,2160 ****
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;
--- 2154,2160 ----
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;
***************
*** 2232,2234 ****
--- 2232,2282 ----
drop function continue_test2();
drop function continue_test3();
drop table conttesttbl;
+
+ -- verbose end block and end loop
+ create function end_label1() returns void as $$
+ <>
+ begin
+ <>
+ for _i in 1 .. 10 loop
+ exit flbl1;
+ end loop flbl1;
+ <>
+ for _i in 1 .. 10 loop
+ exit flbl2;
+ end loop;
+ end blbl;
+ $$ language plpgsql;
+
+ select end_label1();
+ drop function end_label1();
+
+ -- should fail: undefined end label
+ create function end_label2() returns void as $$
+ begin
+ for _i in 1 .. 10 loop
+ exit;
+ end loop flbl1;
+ end;
+ $$ language plpgsql;
+
+ -- should fail: end label does not match start label
+ create function end_label3() returns void as $$
+ <>
+ begin
+ <>
+ for _i in 1 .. 10 loop
+ exit;
+ end loop outer_label;
+ end;
+ $$ language plpgsql;
+
+ -- should fail: end label on a block without a start label
+ create function end_label4() returns void as $$
+ <>
+ begin
+ for _i in 1 .. 10 loop
+ exit;
+ end loop outer_label;
+ end;
+ $$ language plpgsql;