mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
Allow PL/pgSQL FOR statement to return values to scalars as well as
records and row types. Pavel Stehule
This commit is contained in:
@ -1,5 +1,5 @@
|
|||||||
<!--
|
<!--
|
||||||
$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;
|
|||||||
accordingly. The syntax is:
|
accordingly. The syntax is:
|
||||||
<synopsis>
|
<synopsis>
|
||||||
<optional> <<<replaceable>label</replaceable>>> </optional>
|
<optional> <<<replaceable>label</replaceable>>> </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;
|
|||||||
rows:
|
rows:
|
||||||
<synopsis>
|
<synopsis>
|
||||||
<optional> <<<replaceable>label</replaceable>>> </optional>
|
<optional> <<<replaceable>label</replaceable>>> </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>;
|
|||||||
<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 @@
|
|||||||
* 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,7 +58,9 @@ 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);
|
||||||
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
%union {
|
%union {
|
||||||
@ -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 :
|
|||||||
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 :
|
|||||||
|
|
||||||
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 :
|
|||||||
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;
|
||||||
@ -1027,14 +1049,31 @@ for_control :
|
|||||||
* until we know what's what.
|
* until we know what's what.
|
||||||
*/
|
*/
|
||||||
for_variable : T_SCALAR
|
for_variable : T_SCALAR
|
||||||
{
|
{
|
||||||
|
int tok;
|
||||||
char *name;
|
char *name;
|
||||||
|
|
||||||
|
name = pstrdup(yytext);
|
||||||
|
$$.scalar = yylval.scalar;
|
||||||
|
$$.lineno = plpgsql_scanner_lineno();
|
||||||
|
|
||||||
plpgsql_convert_ident(yytext, &name, 1);
|
if((tok = yylex()) == ',')
|
||||||
$$.name = name;
|
{
|
||||||
$$.lineno = plpgsql_scanner_lineno();
|
plpgsql_push_back_token(tok);
|
||||||
$$.rec = NULL;
|
$$.name = NULL;
|
||||||
$$.row = NULL;
|
$$.row = read_into_scalar_list(name, $$.scalar);
|
||||||
|
$$.rec = NULL;
|
||||||
|
$$.scalar = NULL;
|
||||||
|
|
||||||
|
pfree(name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
plpgsql_push_back_token(tok);
|
||||||
|
$$.name = name;
|
||||||
|
$$.row = NULL;
|
||||||
|
$$.rec = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
| T_WORD
|
| T_WORD
|
||||||
{
|
{
|
||||||
@ -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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
|||||||
$$ 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
|
|||||||
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;
|
||||||
|
Reference in New Issue
Block a user