Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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)