Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit c425dcb

Browse files
author
Neil Conway
committed
In PL/PgSQL, allow a block's label to be optionally specified at the
end of the block: <<label>> begin ... end label; Similarly for loops. This is per PL/SQL. Update the documentation and add regression tests. Patch from Pavel Stehule, code review by Neil Conway.
1 parent 784b948 commit c425dcb

File tree

4 files changed

+197
-53
lines changed

4 files changed

+197
-53
lines changed

doc/src/sgml/plpgsql.sgml

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.74 2005/06/22 01:35:02 neilc Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.75 2005/07/02 08:59:47 neilc Exp $
33
-->
44

55
<chapter id="plpgsql">
@@ -456,7 +456,7 @@ a_output := a_output || $$ if v_$$ || referrer_keys.kind || $$ like '$$
456456
<replaceable>declarations</replaceable> </optional>
457457
BEGIN
458458
<replaceable>statements</replaceable>
459-
END;
459+
END <optional> <replaceable>label</replaceable> </optional>;
460460
</synopsis>
461461
</para>
462462

@@ -1789,18 +1789,19 @@ END IF;
17891789
<title><literal>LOOP</></title>
17901790

17911791
<synopsis>
1792-
<optional>&lt;&lt;<replaceable>label</replaceable>&gt;&gt;</optional>
1792+
<optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
17931793
LOOP
17941794
<replaceable>statements</replaceable>
1795-
END LOOP;
1795+
END LOOP <optional> <replaceable>label</replaceable> </optional>;
17961796
</synopsis>
17971797

17981798
<para>
1799-
<literal>LOOP</> defines an unconditional loop that is repeated indefinitely
1800-
until terminated by an <literal>EXIT</> or <command>RETURN</command>
1801-
statement. The optional label can be used by <literal>EXIT</> statements in
1802-
nested loops to specify which level of nesting should be
1803-
terminated.
1799+
<literal>LOOP</> defines an unconditional loop that is repeated
1800+
indefinitely until terminated by an <literal>EXIT</> or
1801+
<command>RETURN</command> statement. The optional
1802+
<replaceable>label</replaceable> can be used by <literal>EXIT</>
1803+
and <literal>CONTINUE</literal> statements in nested loops to
1804+
specify which loop the statement should be applied to.
18041805
</para>
18051806
</sect3>
18061807

@@ -1920,10 +1921,10 @@ END LOOP;
19201921
</indexterm>
19211922

19221923
<synopsis>
1923-
<optional>&lt;&lt;<replaceable>label</replaceable>&gt;&gt;</optional>
1924+
<optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
19241925
WHILE <replaceable>expression</replaceable> LOOP
19251926
<replaceable>statements</replaceable>
1926-
END LOOP;
1927+
END LOOP <optional> <replaceable>label</replaceable> </optional>;
19271928
</synopsis>
19281929

19291930
<para>
@@ -1951,10 +1952,10 @@ END LOOP;
19511952
<title><literal>FOR</> (integer variant)</title>
19521953

19531954
<synopsis>
1954-
<optional>&lt;&lt;<replaceable>label</replaceable>&gt;&gt;</optional>
1955+
<optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
19551956
FOR <replaceable>name</replaceable> IN <optional> REVERSE </optional> <replaceable>expression</replaceable> .. <replaceable>expression</replaceable> LOOP
19561957
<replaceable>statements</replaceable>
1957-
END LOOP;
1958+
END LOOP <optional> <replaceable>labal</replaceable> </optional>;
19581959
</synopsis>
19591960

19601961
<para>
@@ -1997,10 +1998,10 @@ END LOOP;
19971998
the results of a query and manipulate that data
19981999
accordingly. The syntax is:
19992000
<synopsis>
2000-
<optional>&lt;&lt;<replaceable>label</replaceable>&gt;&gt;</optional>
2001+
<optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
20012002
FOR <replaceable>record_or_row</replaceable> IN <replaceable>query</replaceable> LOOP
20022003
<replaceable>statements</replaceable>
2003-
END LOOP;
2004+
END LOOP <optional> <replaceable>label</replaceable> </optional>;
20042005
</synopsis>
20052006
The record or row variable is successively assigned each row
20062007
resulting from the <replaceable>query</replaceable> (which must be a
@@ -2036,10 +2037,10 @@ $$ LANGUAGE plpgsql;
20362037
The <literal>FOR-IN-EXECUTE</> statement is another way to iterate over
20372038
rows:
20382039
<synopsis>
2039-
<optional>&lt;&lt;<replaceable>label</replaceable>&gt;&gt;</optional>
2040+
<optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
20402041
FOR <replaceable>record_or_row</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> LOOP
20412042
<replaceable>statements</replaceable>
2042-
END LOOP;
2043+
END LOOP <optional> <replaceable>label</replaceable> </optional>;
20432044
</synopsis>
20442045
This is like the previous form, except that the source
20452046
<command>SELECT</command> statement is specified as a string

src/pl/plpgsql/src/gram.y

Lines changed: 72 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* procedural language
55
*
66
* IDENTIFICATION
7-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.78 2005/07/01 17:40:29 momjian Exp $
7+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.79 2005/07/02 08:59:47 neilc Exp $
88
*
99
* This software is copyrighted by Jan Wieck - Hamburg.
1010
*
@@ -56,6 +56,8 @@ static PLpgSQL_row *read_into_scalar_list(const char *initial_name,
5656
PLpgSQL_datum *initial_datum);
5757
static void check_sql_expr(const char *stmt);
5858
static void plpgsql_sql_error_callback(void *arg);
59+
static void check_labels(const char *start_label,
60+
const char *end_label);
5961

6062
%}
6163

@@ -69,7 +71,7 @@ static void plpgsql_sql_error_callback(void *arg);
6971
int lineno;
7072
} varname;
7173
struct
72-
{
74+
{
7375
char *name;
7476
int lineno;
7577
PLpgSQL_rec *rec;
@@ -81,6 +83,11 @@ static void plpgsql_sql_error_callback(void *arg);
8183
int n_initvars;
8284
int *initvarnos;
8385
} declhdr;
86+
struct
87+
{
88+
char *end_label;
89+
List *stmts;
90+
} loop_body;
8491
List *list;
8592
PLpgSQL_type *dtype;
8693
PLpgSQL_datum *scalar; /* a VAR, RECFIELD, or TRIGARG */
@@ -119,11 +126,11 @@ static void plpgsql_sql_error_callback(void *arg);
119126
%type <forvariable> for_variable
120127
%type <stmt> for_control
121128

122-
%type <str> opt_lblname opt_label
123-
%type <str> opt_exitlabel
129+
%type <str> opt_lblname opt_block_label opt_label
124130
%type <str> execsql_start
125131

126-
%type <list> proc_sect proc_stmts stmt_else loop_body
132+
%type <list> proc_sect proc_stmts stmt_else
133+
%type <loop_body> loop_body
127134
%type <stmt> proc_stmt pl_block
128135
%type <stmt> stmt_assign stmt_if stmt_loop stmt_while stmt_exit
129136
%type <stmt> stmt_return stmt_return_next stmt_raise stmt_execsql
@@ -248,7 +255,7 @@ opt_semi :
248255
| ';'
249256
;
250257

251-
pl_block : decl_sect K_BEGIN lno proc_sect exception_sect K_END
258+
pl_block : decl_sect K_BEGIN lno proc_sect exception_sect K_END opt_label
252259
{
253260
PLpgSQL_stmt_block *new;
254261

@@ -262,30 +269,31 @@ pl_block : decl_sect K_BEGIN lno proc_sect exception_sect K_END
262269
new->body = $4;
263270
new->exceptions = $5;
264271

272+
check_labels($1.label, $7);
265273
plpgsql_ns_pop();
266274

267275
$$ = (PLpgSQL_stmt *)new;
268276
}
269277
;
270278

271279

272-
decl_sect : opt_label
280+
decl_sect : opt_block_label
273281
{
274282
plpgsql_ns_setlocal(false);
275283
$$.label = $1;
276284
$$.n_initvars = 0;
277285
$$.initvarnos = NULL;
278286
plpgsql_add_initdatums(NULL);
279287
}
280-
| opt_label decl_start
288+
| opt_block_label decl_start
281289
{
282290
plpgsql_ns_setlocal(false);
283291
$$.label = $1;
284292
$$.n_initvars = 0;
285293
$$.initvarnos = NULL;
286294
plpgsql_add_initdatums(NULL);
287295
}
288-
| opt_label decl_start decl_stmts
296+
| opt_block_label decl_start decl_stmts
289297
{
290298
plpgsql_ns_setlocal(false);
291299
if ($3 != NULL)
@@ -409,7 +417,7 @@ decl_cursor_query :
409417
plpgsql_ns_setlocal(false);
410418
query = read_sql_stmt("");
411419
plpgsql_ns_setlocal(true);
412-
420+
413421
$$ = query;
414422
}
415423
;
@@ -757,7 +765,7 @@ stmt_else :
757765
* ... ...
758766
* ELSE ELSE
759767
* ... ...
760-
* END IF END IF
768+
* END IF END IF
761769
* END IF
762770
*/
763771
PLpgSQL_stmt_if *new_if;
@@ -776,27 +784,28 @@ stmt_else :
776784

777785
| K_ELSE proc_sect
778786
{
779-
$$ = $2;
787+
$$ = $2;
780788
}
781789
;
782790

783-
stmt_loop : opt_label K_LOOP lno loop_body
791+
stmt_loop : opt_block_label K_LOOP lno loop_body
784792
{
785793
PLpgSQL_stmt_loop *new;
786794

787795
new = palloc0(sizeof(PLpgSQL_stmt_loop));
788796
new->cmd_type = PLPGSQL_STMT_LOOP;
789797
new->lineno = $3;
790798
new->label = $1;
791-
new->body = $4;
799+
new->body = $4.stmts;
792800

801+
check_labels($1, $4.end_label);
793802
plpgsql_ns_pop();
794803

795804
$$ = (PLpgSQL_stmt *)new;
796805
}
797806
;
798807

799-
stmt_while : opt_label K_WHILE lno expr_until_loop loop_body
808+
stmt_while : opt_block_label K_WHILE lno expr_until_loop loop_body
800809
{
801810
PLpgSQL_stmt_while *new;
802811

@@ -805,15 +814,16 @@ stmt_while : opt_label K_WHILE lno expr_until_loop loop_body
805814
new->lineno = $3;
806815
new->label = $1;
807816
new->cond = $4;
808-
new->body = $5;
817+
new->body = $5.stmts;
809818

819+
check_labels($1, $5.end_label);
810820
plpgsql_ns_pop();
811821

812822
$$ = (PLpgSQL_stmt *)new;
813823
}
814824
;
815825

816-
stmt_for : opt_label K_FOR for_control loop_body
826+
stmt_for : opt_block_label K_FOR for_control loop_body
817827
{
818828
/* This runs after we've scanned the loop body */
819829
if ($3->cmd_type == PLPGSQL_STMT_FORI)
@@ -822,7 +832,7 @@ stmt_for : opt_label K_FOR for_control loop_body
822832

823833
new = (PLpgSQL_stmt_fori *) $3;
824834
new->label = $1;
825-
new->body = $4;
835+
new->body = $4.stmts;
826836
$$ = (PLpgSQL_stmt *) new;
827837
}
828838
else if ($3->cmd_type == PLPGSQL_STMT_FORS)
@@ -831,7 +841,7 @@ stmt_for : opt_label K_FOR for_control loop_body
831841

832842
new = (PLpgSQL_stmt_fors *) $3;
833843
new->label = $1;
834-
new->body = $4;
844+
new->body = $4.stmts;
835845
$$ = (PLpgSQL_stmt *) new;
836846
}
837847
else
@@ -841,10 +851,11 @@ stmt_for : opt_label K_FOR for_control loop_body
841851
Assert($3->cmd_type == PLPGSQL_STMT_DYNFORS);
842852
new = (PLpgSQL_stmt_dynfors *) $3;
843853
new->label = $1;
844-
new->body = $4;
854+
new->body = $4.stmts;
845855
$$ = (PLpgSQL_stmt *) new;
846856
}
847857

858+
check_labels($1, $4.end_label);
848859
/* close namespace started in opt_label */
849860
plpgsql_ns_pop();
850861
}
@@ -1037,7 +1048,7 @@ stmt_select : K_SELECT lno
10371048
}
10381049
;
10391050

1040-
stmt_exit : exit_type lno opt_exitlabel opt_exitcond
1051+
stmt_exit : exit_type lno opt_label opt_exitcond
10411052
{
10421053
PLpgSQL_stmt_exit *new;
10431054

@@ -1245,8 +1256,11 @@ raise_level : K_EXCEPTION
12451256
}
12461257
;
12471258

1248-
loop_body : proc_sect K_END K_LOOP ';'
1249-
{ $$ = $1; }
1259+
loop_body : proc_sect K_END K_LOOP opt_label ';'
1260+
{
1261+
$$.stmts = $1;
1262+
$$.end_label = $4;
1263+
}
12501264
;
12511265

12521266
stmt_execsql : execsql_start lno
@@ -1262,7 +1276,7 @@ stmt_execsql : execsql_start lno
12621276
}
12631277
;
12641278

1265-
stmt_dynexecute : K_EXECUTE lno
1279+
stmt_dynexecute : K_EXECUTE lno
12661280
{
12671281
PLpgSQL_stmt_dynexecute *new;
12681282
PLpgSQL_expr *expr;
@@ -1418,7 +1432,7 @@ stmt_open : K_OPEN lno cursor_varptr
14181432
errmsg("cursor \"%s\" has no arguments",
14191433
$3->refname)));
14201434
}
1421-
1435+
14221436
if (tok != ';')
14231437
{
14241438
plpgsql_error_lineno = plpgsql_scanner_lineno();
@@ -1596,7 +1610,7 @@ expr_until_loop :
15961610
{ $$ = plpgsql_read_expression(K_LOOP, "LOOP"); }
15971611
;
15981612

1599-
opt_label :
1613+
opt_block_label :
16001614
{
16011615
plpgsql_ns_push(NULL);
16021616
$$ = NULL;
@@ -1608,14 +1622,15 @@ opt_label :
16081622
}
16091623
;
16101624

1611-
opt_exitlabel :
1612-
{ $$ = NULL; }
1625+
opt_label :
1626+
{
1627+
$$ = NULL;
1628+
}
16131629
| T_LABEL
16141630
{
1615-
char *name;
1616-
1617-
plpgsql_convert_ident(yytext, &name, 1);
1618-
$$ = name;
1631+
char *label_name;
1632+
plpgsql_convert_ident(yytext, &label_name, 1);
1633+
$$ = label_name;
16191634
}
16201635
| T_WORD
16211636
{
@@ -2210,4 +2225,29 @@ plpgsql_sql_error_callback(void *arg)
22102225
errposition(0);
22112226
}
22122227

2228+
static void
2229+
check_labels(const char *start_label, const char *end_label)
2230+
{
2231+
if (end_label)
2232+
{
2233+
if (!start_label)
2234+
{
2235+
plpgsql_error_lineno = plpgsql_scanner_lineno();
2236+
ereport(ERROR,
2237+
(errcode(ERRCODE_SYNTAX_ERROR),
2238+
errmsg("end label \"%s\" specified for unlabelled block",
2239+
end_label)));
2240+
}
2241+
2242+
if (strcmp(start_label, end_label) != 0)
2243+
{
2244+
plpgsql_error_lineno = plpgsql_scanner_lineno();
2245+
ereport(ERROR,
2246+
(errcode(ERRCODE_SYNTAX_ERROR),
2247+
errmsg("end label \"%s\" differs from block's label \"%s\"",
2248+
end_label, start_label)));
2249+
}
2250+
}
2251+
}
2252+
22132253
#include "pl_scan.c"

0 commit comments

Comments
 (0)