Commit 025ffe58 authored by Bruce Momjian's avatar Bruce Momjian

Allow PL/pgSQL FOR statement to return values to scalars as well as

records and row types.

Pavel Stehule
parent 18cbc7ae
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.84 2006/02/05 02:47:53 momjian Exp $ $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.85 2006/02/12 06:03:38 momjian Exp $
--> -->
<chapter id="plpgsql"> <chapter id="plpgsql">
...@@ -2008,11 +2008,13 @@ END LOOP; ...@@ -2008,11 +2008,13 @@ END LOOP;
accordingly. The syntax is: accordingly. The syntax is:
<synopsis> <synopsis>
<optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional> <optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
FOR <replaceable>record_or_row</replaceable> IN <replaceable>query</replaceable> LOOP FOR <replaceable>target</replaceable> IN <replaceable>query</replaceable> LOOP
<replaceable>statements</replaceable> <replaceable>statements</replaceable>
END LOOP <optional> <replaceable>label</replaceable> </optional>; END LOOP <optional> <replaceable>label</replaceable> </optional>;
</synopsis> </synopsis>
The record or row variable is successively assigned each row <replaceable>Target</replaceable> is a record variable, row variable,
or a comma-separated list of simple variables and record/row fields
which is successively assigned each row
resulting from the <replaceable>query</replaceable> (which must be a resulting from the <replaceable>query</replaceable> (which must be a
<command>SELECT</command> command) and the loop body is executed for each <command>SELECT</command> command) and the loop body is executed for each
row. Here is an example: row. Here is an example:
...@@ -2047,7 +2049,7 @@ $$ LANGUAGE plpgsql; ...@@ -2047,7 +2049,7 @@ $$ LANGUAGE plpgsql;
rows: rows:
<synopsis> <synopsis>
<optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional> <optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
FOR <replaceable>record_or_row</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> LOOP FOR <replaceable>target</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> LOOP
<replaceable>statements</replaceable> <replaceable>statements</replaceable>
END LOOP <optional> <replaceable>label</replaceable> </optional>; END LOOP <optional> <replaceable>label</replaceable> </optional>;
</synopsis> </synopsis>
...@@ -2067,7 +2069,7 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>; ...@@ -2067,7 +2069,7 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>;
<literal>IN</> and <literal>LOOP</>. If <literal>..</> is not seen then <literal>IN</> and <literal>LOOP</>. If <literal>..</> is not seen then
the loop is presumed to be a loop over rows. Mistyping the <literal>..</> the loop is presumed to be a loop over rows. Mistyping the <literal>..</>
is thus likely to lead to a complaint along the lines of is thus likely to lead to a complaint along the lines of
<quote>loop variable of loop over rows must be a record or row variable</>, <quote>loop variable of loop over rows must be a record or row or scalar variable</>,
rather than the simple syntax error one might expect to get. rather than the simple syntax error one might expect to get.
</para> </para>
</note> </note>
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.83 2006/02/12 04:59:32 tgl Exp $ * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.84 2006/02/12 06:03:38 momjian Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -58,6 +58,8 @@ static void check_sql_expr(const char *stmt); ...@@ -58,6 +58,8 @@ static void check_sql_expr(const char *stmt);
static void plpgsql_sql_error_callback(void *arg); static void plpgsql_sql_error_callback(void *arg);
static void check_labels(const char *start_label, static void check_labels(const char *start_label,
const char *end_label); const char *end_label);
static PLpgSQL_row *make_scalar_list1(const char *name,
PLpgSQL_datum *variable);
%} %}
...@@ -76,6 +78,7 @@ static void check_labels(const char *start_label, ...@@ -76,6 +78,7 @@ static void check_labels(const char *start_label,
int lineno; int lineno;
PLpgSQL_rec *rec; PLpgSQL_rec *rec;
PLpgSQL_row *row; PLpgSQL_row *row;
PLpgSQL_datum *scalar;
} forvariable; } forvariable;
struct struct
{ {
...@@ -890,10 +893,15 @@ for_control : ...@@ -890,10 +893,15 @@ for_control :
new->row = $2.row; new->row = $2.row;
check_assignable((PLpgSQL_datum *) new->row); check_assignable((PLpgSQL_datum *) new->row);
} }
else if ($2.scalar)
{
new->row = make_scalar_list1($2.name, $2.scalar);
check_assignable((PLpgSQL_datum *) new->row);
}
else else
{ {
plpgsql_error_lineno = $1; plpgsql_error_lineno = $1;
yyerror("loop variable of loop over rows must be a record or row variable"); yyerror("loop variable of loop over rows must be a record, row, or scalar variable");
} }
new->query = expr; new->query = expr;
...@@ -948,6 +956,15 @@ for_control : ...@@ -948,6 +956,15 @@ for_control :
expr2 = plpgsql_read_expression(K_LOOP, "LOOP"); expr2 = plpgsql_read_expression(K_LOOP, "LOOP");
/* T_SCALAR identifier waits for converting */
if ($2.scalar)
{
char *name;
plpgsql_convert_ident($2.name, &name, 1);
pfree($2.name);
$2.name = name;
}
/* create loop's private variable */ /* create loop's private variable */
fvar = (PLpgSQL_var *) fvar = (PLpgSQL_var *)
plpgsql_build_variable($2.name, plpgsql_build_variable($2.name,
...@@ -1002,10 +1019,15 @@ for_control : ...@@ -1002,10 +1019,15 @@ for_control :
new->row = $2.row; new->row = $2.row;
check_assignable((PLpgSQL_datum *) new->row); check_assignable((PLpgSQL_datum *) new->row);
} }
else if ($2.scalar)
{
new->row = make_scalar_list1($2.name, $2.scalar);
check_assignable((PLpgSQL_datum *) new->row);
}
else else
{ {
plpgsql_error_lineno = $1; plpgsql_error_lineno = $1;
yyerror("loop variable of loop over rows must be record or row variable"); yyerror("loop variable of loop over rows must be record, row, or scalar variable");
} }
new->query = expr1; new->query = expr1;
...@@ -1028,13 +1050,30 @@ for_control : ...@@ -1028,13 +1050,30 @@ for_control :
*/ */
for_variable : T_SCALAR for_variable : T_SCALAR
{ {
int tok;
char *name; char *name;
plpgsql_convert_ident(yytext, &name, 1); name = pstrdup(yytext);
$$.name = name; $$.scalar = yylval.scalar;
$$.lineno = plpgsql_scanner_lineno(); $$.lineno = plpgsql_scanner_lineno();
if((tok = yylex()) == ',')
{
plpgsql_push_back_token(tok);
$$.name = NULL;
$$.row = read_into_scalar_list(name, $$.scalar);
$$.rec = NULL; $$.rec = NULL;
$$.scalar = NULL;
pfree(name);
}
else
{
plpgsql_push_back_token(tok);
$$.name = name;
$$.row = NULL; $$.row = NULL;
$$.rec = NULL;
}
} }
| T_WORD | T_WORD
{ {
...@@ -1048,20 +1087,14 @@ for_variable : T_SCALAR ...@@ -1048,20 +1087,14 @@ for_variable : T_SCALAR
} }
| T_RECORD | T_RECORD
{ {
char *name; $$.name = NULL;
plpgsql_convert_ident(yytext, &name, 1);
$$.name = name;
$$.lineno = plpgsql_scanner_lineno(); $$.lineno = plpgsql_scanner_lineno();
$$.rec = yylval.rec; $$.rec = yylval.rec;
$$.row = NULL; $$.row = NULL;
} }
| T_ROW | T_ROW
{ {
char *name; $$.name = NULL;
plpgsql_convert_ident(yytext, &name, 1);
$$.name = name;
$$.lineno = plpgsql_scanner_lineno(); $$.lineno = plpgsql_scanner_lineno();
$$.row = yylval.row; $$.row = yylval.row;
$$.rec = NULL; $$.rec = NULL;
...@@ -2088,6 +2121,30 @@ make_fetch_stmt(void) ...@@ -2088,6 +2121,30 @@ make_fetch_stmt(void)
} }
static PLpgSQL_row *
make_scalar_list1(const char *name,
PLpgSQL_datum *variable)
{
PLpgSQL_row *row;
check_assignable(variable);
row = palloc(sizeof(PLpgSQL_row));
row->dtype = PLPGSQL_DTYPE_ROW;
row->refname = pstrdup("*internal*");
row->lineno = plpgsql_scanner_lineno();
row->rowtupdesc = NULL;
row->nfields = 1;
row->fieldnames = palloc(sizeof(char *) * 1);
row->varnos = palloc(sizeof(int) * 1);
row->fieldnames[0] = pstrdup(name);
row->varnos[0] = variable->dno;
plpgsql_adddatum((PLpgSQL_datum *)row);
return row;
}
static void static void
check_assignable(PLpgSQL_datum *datum) check_assignable(PLpgSQL_datum *datum)
{ {
......
...@@ -2721,3 +2721,23 @@ end; ...@@ -2721,3 +2721,23 @@ end;
$$ language plpgsql; $$ language plpgsql;
ERROR: end label "outer_label" specified for unlabelled block ERROR: end label "outer_label" specified for unlabelled block
CONTEXT: compile of PL/pgSQL function "end_label4" near line 5 CONTEXT: compile of PL/pgSQL function "end_label4" near line 5
-- using list of scalars in fori and fore stmts
create function for_vect() returns void as $$
<<lbl>>declare a integer; b varchar; c varchar; r record;
begin
-- old fori
for i in 1 .. 10 loop
raise notice '%', i;
end loop;
for a in select 1 from generate_series(1,4) loop
raise notice '%', a;
end loop;
for a,b,c in select generate_series, 'BB','CC' from generate_series(1,4) loop
raise notice '% % %', a, b, c;
end loop;
-- using qualified names in fors, fore is enabled, disabled only for fori
for lbl.a, lbl.b, lbl.c in execute E'select generate_series, \'bb\',\'cc\' from generate_series(1,4)' loop
raise notice '% % %', a, b, c;
end loop;
end;
$$ language plpgsql;
...@@ -2280,3 +2280,25 @@ begin ...@@ -2280,3 +2280,25 @@ begin
end loop outer_label; end loop outer_label;
end; end;
$$ language plpgsql; $$ language plpgsql;
-- using list of scalars in fori and fore stmts
create function for_vect() returns void as $$
<<lbl>>declare a integer; b varchar; c varchar; r record;
begin
-- old fori
for i in 1 .. 10 loop
raise notice '%', i;
end loop;
for a in select 1 from generate_series(1,4) loop
raise notice '%', a;
end loop;
for a,b,c in select generate_series, 'BB','CC' from generate_series(1,4) loop
raise notice '% % %', a, b, c;
end loop;
-- using qualified names in fors, fore is enabled, disabled only for fori
for lbl.a, lbl.b, lbl.c in execute E'select generate_series, \'bb\',\'cc\' from generate_series(1,4)' loop
raise notice '% % %', a, b, c;
end loop;
end;
$$ language plpgsql;
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