Skip to content

Commit 025ffe5

Browse files
committed
Allow PL/pgSQL FOR statement to return values to scalars as well as
records and row types. Pavel Stehule
1 parent 18cbc7a commit 025ffe5

File tree

4 files changed

+124
-23
lines changed

4 files changed

+124
-23
lines changed

doc/src/sgml/plpgsql.sgml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.84 2006/02/05 02:47:53 momjian Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.85 2006/02/12 06:03:38 momjian Exp $
33
-->
44

55
<chapter id="plpgsql">
@@ -2008,11 +2008,13 @@ END LOOP;
20082008
accordingly. The syntax is:
20092009
<synopsis>
20102010
<optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
2011-
FOR <replaceable>record_or_row</replaceable> IN <replaceable>query</replaceable> LOOP
2011+
FOR <replaceable>target</replaceable> IN <replaceable>query</replaceable> LOOP
20122012
<replaceable>statements</replaceable>
20132013
END LOOP <optional> <replaceable>label</replaceable> </optional>;
20142014
</synopsis>
2015-
The record or row variable is successively assigned each row
2015+
<replaceable>Target</replaceable> is a record variable, row variable,
2016+
or a comma-separated list of simple variables and record/row fields
2017+
which is successively assigned each row
20162018
resulting from the <replaceable>query</replaceable> (which must be a
20172019
<command>SELECT</command> command) and the loop body is executed for each
20182020
row. Here is an example:
@@ -2047,7 +2049,7 @@ $$ LANGUAGE plpgsql;
20472049
rows:
20482050
<synopsis>
20492051
<optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
2050-
FOR <replaceable>record_or_row</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> LOOP
2052+
FOR <replaceable>target</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> LOOP
20512053
<replaceable>statements</replaceable>
20522054
END LOOP <optional> <replaceable>label</replaceable> </optional>;
20532055
</synopsis>
@@ -2067,7 +2069,7 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>;
20672069
<literal>IN</> and <literal>LOOP</>. If <literal>..</> is not seen then
20682070
the loop is presumed to be a loop over rows. Mistyping the <literal>..</>
20692071
is thus likely to lead to a complaint along the lines of
2070-
<quote>loop variable of loop over rows must be a record or row variable</>,
2072+
<quote>loop variable of loop over rows must be a record or row or scalar variable</>,
20712073
rather than the simple syntax error one might expect to get.
20722074
</para>
20732075
</note>

src/pl/plpgsql/src/gram.y

Lines changed: 75 additions & 18 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.83 2006/02/12 04:59:32 tgl Exp $
7+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.84 2006/02/12 06:03:38 momjian Exp $
88
*
99
* This software is copyrighted by Jan Wieck - Hamburg.
1010
*
@@ -58,7 +58,9 @@ static void check_sql_expr(const char *stmt);
5858
static void plpgsql_sql_error_callback(void *arg);
5959
static void check_labels(const char *start_label,
6060
const char *end_label);
61-
61+
static PLpgSQL_row *make_scalar_list1(const char *name,
62+
PLpgSQL_datum *variable);
63+
6264
%}
6365

6466
%union {
@@ -76,6 +78,7 @@ static void check_labels(const char *start_label,
7678
int lineno;
7779
PLpgSQL_rec *rec;
7880
PLpgSQL_row *row;
81+
PLpgSQL_datum *scalar;
7982
} forvariable;
8083
struct
8184
{
@@ -890,10 +893,15 @@ for_control :
890893
new->row = $2.row;
891894
check_assignable((PLpgSQL_datum *) new->row);
892895
}
896+
else if ($2.scalar)
897+
{
898+
new->row = make_scalar_list1($2.name, $2.scalar);
899+
check_assignable((PLpgSQL_datum *) new->row);
900+
}
893901
else
894902
{
895903
plpgsql_error_lineno = $1;
896-
yyerror("loop variable of loop over rows must be a record or row variable");
904+
yyerror("loop variable of loop over rows must be a record, row, or scalar variable");
897905
}
898906
new->query = expr;
899907

@@ -948,6 +956,15 @@ for_control :
948956

949957
expr2 = plpgsql_read_expression(K_LOOP, "LOOP");
950958

959+
/* T_SCALAR identifier waits for converting */
960+
if ($2.scalar)
961+
{
962+
char *name;
963+
plpgsql_convert_ident($2.name, &name, 1);
964+
pfree($2.name);
965+
$2.name = name;
966+
}
967+
951968
/* create loop's private variable */
952969
fvar = (PLpgSQL_var *)
953970
plpgsql_build_variable($2.name,
@@ -1002,10 +1019,15 @@ for_control :
10021019
new->row = $2.row;
10031020
check_assignable((PLpgSQL_datum *) new->row);
10041021
}
1022+
else if ($2.scalar)
1023+
{
1024+
new->row = make_scalar_list1($2.name, $2.scalar);
1025+
check_assignable((PLpgSQL_datum *) new->row);
1026+
}
10051027
else
10061028
{
10071029
plpgsql_error_lineno = $1;
1008-
yyerror("loop variable of loop over rows must be record or row variable");
1030+
yyerror("loop variable of loop over rows must be record, row, or scalar variable");
10091031
}
10101032

10111033
new->query = expr1;
@@ -1027,14 +1049,31 @@ for_control :
10271049
* until we know what's what.
10281050
*/
10291051
for_variable : T_SCALAR
1030-
{
1052+
{
1053+
int tok;
10311054
char *name;
1055+
1056+
name = pstrdup(yytext);
1057+
$$.scalar = yylval.scalar;
1058+
$$.lineno = plpgsql_scanner_lineno();
10321059

1033-
plpgsql_convert_ident(yytext, &name, 1);
1034-
$$.name = name;
1035-
$$.lineno = plpgsql_scanner_lineno();
1036-
$$.rec = NULL;
1037-
$$.row = NULL;
1060+
if((tok = yylex()) == ',')
1061+
{
1062+
plpgsql_push_back_token(tok);
1063+
$$.name = NULL;
1064+
$$.row = read_into_scalar_list(name, $$.scalar);
1065+
$$.rec = NULL;
1066+
$$.scalar = NULL;
1067+
1068+
pfree(name);
1069+
}
1070+
else
1071+
{
1072+
plpgsql_push_back_token(tok);
1073+
$$.name = name;
1074+
$$.row = NULL;
1075+
$$.rec = NULL;
1076+
}
10381077
}
10391078
| T_WORD
10401079
{
@@ -1048,20 +1087,14 @@ for_variable : T_SCALAR
10481087
}
10491088
| T_RECORD
10501089
{
1051-
char *name;
1052-
1053-
plpgsql_convert_ident(yytext, &name, 1);
1054-
$$.name = name;
1090+
$$.name = NULL;
10551091
$$.lineno = plpgsql_scanner_lineno();
10561092
$$.rec = yylval.rec;
10571093
$$.row = NULL;
10581094
}
10591095
| T_ROW
10601096
{
1061-
char *name;
1062-
1063-
plpgsql_convert_ident(yytext, &name, 1);
1064-
$$.name = name;
1097+
$$.name = NULL;
10651098
$$.lineno = plpgsql_scanner_lineno();
10661099
$$.row = yylval.row;
10671100
$$.rec = NULL;
@@ -2088,6 +2121,30 @@ make_fetch_stmt(void)
20882121
}
20892122

20902123

2124+
static PLpgSQL_row *
2125+
make_scalar_list1(const char *name,
2126+
PLpgSQL_datum *variable)
2127+
{
2128+
PLpgSQL_row *row;
2129+
check_assignable(variable);
2130+
2131+
row = palloc(sizeof(PLpgSQL_row));
2132+
row->dtype = PLPGSQL_DTYPE_ROW;
2133+
row->refname = pstrdup("*internal*");
2134+
row->lineno = plpgsql_scanner_lineno();
2135+
row->rowtupdesc = NULL;
2136+
row->nfields = 1;
2137+
row->fieldnames = palloc(sizeof(char *) * 1);
2138+
row->varnos = palloc(sizeof(int) * 1);
2139+
row->fieldnames[0] = pstrdup(name);
2140+
row->varnos[0] = variable->dno;
2141+
2142+
plpgsql_adddatum((PLpgSQL_datum *)row);
2143+
2144+
return row;
2145+
}
2146+
2147+
20912148
static void
20922149
check_assignable(PLpgSQL_datum *datum)
20932150
{

src/test/regress/expected/plpgsql.out

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2721,3 +2721,23 @@ end;
27212721
$$ language plpgsql;
27222722
ERROR: end label "outer_label" specified for unlabelled block
27232723
CONTEXT: compile of PL/pgSQL function "end_label4" near line 5
2724+
-- using list of scalars in fori and fore stmts
2725+
create function for_vect() returns void as $$
2726+
<<lbl>>declare a integer; b varchar; c varchar; r record;
2727+
begin
2728+
-- old fori
2729+
for i in 1 .. 10 loop
2730+
raise notice '%', i;
2731+
end loop;
2732+
for a in select 1 from generate_series(1,4) loop
2733+
raise notice '%', a;
2734+
end loop;
2735+
for a,b,c in select generate_series, 'BB','CC' from generate_series(1,4) loop
2736+
raise notice '% % %', a, b, c;
2737+
end loop;
2738+
-- using qualified names in fors, fore is enabled, disabled only for fori
2739+
for lbl.a, lbl.b, lbl.c in execute E'select generate_series, \'bb\',\'cc\' from generate_series(1,4)' loop
2740+
raise notice '% % %', a, b, c;
2741+
end loop;
2742+
end;
2743+
$$ language plpgsql;

src/test/regress/sql/plpgsql.sql

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2280,3 +2280,25 @@ begin
22802280
end loop outer_label;
22812281
end;
22822282
$$ language plpgsql;
2283+
2284+
2285+
-- using list of scalars in fori and fore stmts
2286+
create function for_vect() returns void as $$
2287+
<<lbl>>declare a integer; b varchar; c varchar; r record;
2288+
begin
2289+
-- old fori
2290+
for i in 1 .. 10 loop
2291+
raise notice '%', i;
2292+
end loop;
2293+
for a in select 1 from generate_series(1,4) loop
2294+
raise notice '%', a;
2295+
end loop;
2296+
for a,b,c in select generate_series, 'BB','CC' from generate_series(1,4) loop
2297+
raise notice '% % %', a, b, c;
2298+
end loop;
2299+
-- using qualified names in fors, fore is enabled, disabled only for fori
2300+
for lbl.a, lbl.b, lbl.c in execute E'select generate_series, \'bb\',\'cc\' from generate_series(1,4)' loop
2301+
raise notice '% % %', a, b, c;
2302+
end loop;
2303+
end;
2304+
$$ language plpgsql;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy