Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit15c7217

Browse files
committed
Apply code-reviewed version of for-scalar-list patch: mostly, fixing
it to report reasonable errors in error cases.
1 parent58634ca commit15c7217

File tree

4 files changed

+157
-98
lines changed

4 files changed

+157
-98
lines changed

‎doc/src/sgml/plpgsql.sgml

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

55
<chapter id="plpgsql">
@@ -1968,10 +1968,12 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>;
19681968
</synopsis>
19691969

19701970
<para>
1971-
This form of <literal>FOR</> creates a loop that iterates over a range of integer
1972-
values. The variable
1971+
This form of <literal>FOR</> creates a loop that iterates over a range
1972+
of integervalues. The variable
19731973
<replaceable>name</replaceable> is automatically defined as type
1974-
<type>integer</> and exists only inside the loop. The two expressions giving
1974+
<type>integer</> and exists only inside the loop (any existing
1975+
definition of the variable name is ignored within the loop).
1976+
The two expressions giving
19751977
the lower and upper bound of the range are evaluated once when entering
19761978
the loop. The iteration step is normally 1, but is -1 when <literal>REVERSE</> is
19771979
specified.
@@ -2012,9 +2014,9 @@ FOR <replaceable>target</replaceable> IN <replaceable>query</replaceable> LOOP
20122014
<replaceable>statements</replaceable>
20132015
END LOOP <optional> <replaceable>label</replaceable> </optional>;
20142016
</synopsis>
2015-
<replaceable>Target</replaceable> is a record variable, row variable,
2016-
oracomma-separated list of simplevariables and record/row fields
2017-
which is successively assigned each row
2017+
The<replaceable>target</replaceable> is a record variable, row variable,
2018+
or comma-separated list ofscalarvariables.
2019+
The <replaceable>target</replaceable> is successively assigned each row
20182020
resulting from the <replaceable>query</replaceable> (which must be a
20192021
<command>SELECT</command> command) and the loop body is executed for each
20202022
row. Here is an example:
@@ -2069,7 +2071,8 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>;
20692071
<literal>IN</> and <literal>LOOP</>. If <literal>..</> is not seen then
20702072
the loop is presumed to be a loop over rows. Mistyping the <literal>..</>
20712073
is thus likely to lead to a complaint along the lines of
2072-
<quote>loop variable of loop over rows must be a record or row or scalar variable</>,
2074+
<quote>loop variable of loop over rows must be a record or row variable
2075+
or list of scalar variables</>,
20732076
rather than the simple syntax error one might expect to get.
20742077
</para>
20752078
</note>

‎src/pl/plpgsql/src/gram.y

Lines changed: 93 additions & 75 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.84 2006/02/12 06:03:38 momjian Exp $
7+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.85 2006/02/12 06:37:05 tgl Exp $
88
*
99
* This software is copyrighted by Jan Wieck - Hamburg.
1010
*
@@ -54,13 +54,14 @@ staticPLpgSQL_stmt*make_fetch_stmt(void);
5454
staticvoidcheck_assignable(PLpgSQL_datum *datum);
5555
staticPLpgSQL_row*read_into_scalar_list(constchar *initial_name,
5656
PLpgSQL_datum *initial_datum);
57+
static PLpgSQL_row*make_scalar_list1(constchar *initial_name,
58+
PLpgSQL_datum *initial_datum,
59+
int lineno);
5760
staticvoidcheck_sql_expr(constchar *stmt);
5861
staticvoidplpgsql_sql_error_callback(void *arg);
5962
staticvoidcheck_labels(constchar *start_label,
6063
constchar *end_label);
61-
static PLpgSQL_row *make_scalar_list1(constchar *name,
62-
PLpgSQL_datum *variable);
63-
64+
6465
%}
6566

6667
%union {
@@ -76,9 +77,9 @@ static PLpgSQL_row *make_scalar_list1(const char *name,
7677
{
7778
char *name;
7879
int lineno;
80+
PLpgSQL_datum *scalar;
7981
PLpgSQL_rec *rec;
8082
PLpgSQL_row *row;
81-
PLpgSQL_datum *scalar;
8283
}forvariable;
8384
struct
8485
{
@@ -895,13 +896,14 @@ for_control:
895896
}
896897
elseif ($2.scalar)
897898
{
898-
new->row = make_scalar_list1($2.name,$2.scalar);
899-
check_assignable((PLpgSQL_datum *) new->row);
899+
/* convert single scalar to list*/
900+
new->row = make_scalar_list1($2.name,$2.scalar,$2.lineno);
901+
/* no need for check_assignable*/
900902
}
901903
else
902904
{
903-
plpgsql_error_lineno =$1;
904-
yyerror("loop variable of loop over rows must be a record,row,or scalarvariable");
905+
plpgsql_error_lineno =$2.lineno;
906+
yyerror("loop variable of loop over rows must be a record orrow variableorlist ofscalarvariables");
905907
}
906908
new->query = expr;
907909

@@ -950,24 +952,24 @@ for_control:
950952
PLpgSQL_expr*expr2;
951953
PLpgSQL_var*fvar;
952954
PLpgSQL_stmt_fori*new;
955+
char*varname;
953956

954957
/* First expression is well-formed*/
955958
check_sql_expr(expr1->query);
956959

957960
expr2 = plpgsql_read_expression(K_LOOP,"LOOP");
958961

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-
}
962+
/* should have had a single variable name*/
963+
plpgsql_error_lineno =$2.lineno;
964+
if ($2.scalar &&$2.row)
965+
ereport(ERROR,
966+
(errcode(ERRCODE_SYNTAX_ERROR),
967+
errmsg("integer FOR loop must have just one target variable")));
967968

968969
/* create loop's private variable*/
970+
plpgsql_convert_ident($2.name, &varname,1);
969971
fvar = (PLpgSQL_var *)
970-
plpgsql_build_variable($2.name,
972+
plpgsql_build_variable(varname,
971973
$2.lineno,
972974
plpgsql_build_datatype(INT4OID,
973975
-1),
@@ -1021,13 +1023,14 @@ for_control:
10211023
}
10221024
elseif ($2.scalar)
10231025
{
1024-
new->row = make_scalar_list1($2.name,$2.scalar);
1025-
check_assignable((PLpgSQL_datum *) new->row);
1026+
/* convert single scalar to list*/
1027+
new->row = make_scalar_list1($2.name,$2.scalar,$2.lineno);
1028+
/* no need for check_assignable*/
10261029
}
10271030
else
10281031
{
1029-
plpgsql_error_lineno =$1;
1030-
yyerror("loop variable of loop over rows must be record,row,or scalarvariable");
1032+
plpgsql_error_lineno =$2.lineno;
1033+
yyerror("loop variable of loop over rows must bearecord orrow variableorlist ofscalarvariables");
10311034
}
10321035

10331036
new->query = expr1;
@@ -1047,55 +1050,63 @@ for_control:
10471050
* if any, because that's what we need for the loop-over-query case. Note
10481051
* that we must NOT apply check_assignable() or any other semantic check
10491052
* until we know what's what.
1053+
*
1054+
* However, if we see a comma-separated list of names, we know that it
1055+
* can't be an integer FOR loop and so it's OK to check the variables
1056+
* immediately. In particular, for T_WORD followed by comma, we should
1057+
* complain that the name is not known rather than say it's a syntax error.
1058+
* Note that the non-error result of this case sets *both* $$.scalar and
1059+
* $$.row; see the for_control production.
10501060
*/
10511061
for_variable:T_SCALAR
1052-
{
1053-
int tok;
1054-
char*name;
1055-
1056-
name = pstrdup(yytext);
1057-
$$.scalar = yylval.scalar;
1058-
$$.lineno = plpgsql_scanner_lineno();
1062+
{
1063+
inttok;
10591064

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-
}
1065+
$$.name = pstrdup(yytext);
1066+
$$.lineno = plpgsql_scanner_lineno();
1067+
$$.scalar = yylval.scalar;
1068+
$$.rec =NULL;
1069+
$$.row =NULL;
1070+
/* check for comma-separated list*/
1071+
tok =yylex();
1072+
plpgsql_push_back_token(tok);
1073+
if (tok ==',')
1074+
$$.row = read_into_scalar_list($$.name,$$.scalar);
10771075
}
10781076
|T_WORD
10791077
{
1080-
char*name;
1078+
inttok;
10811079

1082-
plpgsql_convert_ident(yytext, &name,1);
1083-
$$.name = name;
1080+
$$.name = pstrdup(yytext);
10841081
$$.lineno = plpgsql_scanner_lineno();
1082+
$$.scalar =NULL;
10851083
$$.rec =NULL;
10861084
$$.row =NULL;
1085+
/* check for comma-separated list*/
1086+
tok =yylex();
1087+
plpgsql_push_back_token(tok);
1088+
if (tok ==',')
1089+
{
1090+
plpgsql_error_lineno =$$.lineno;
1091+
ereport(ERROR,
1092+
(errcode(ERRCODE_SYNTAX_ERROR),
1093+
errmsg("\"%s\" is not a scalar variable",
1094+
$$.name)));
1095+
}
10871096
}
10881097
|T_RECORD
10891098
{
1090-
$$.name =NULL;
1099+
$$.name =pstrdup(yytext);
10911100
$$.lineno = plpgsql_scanner_lineno();
1101+
$$.scalar =NULL;
10921102
$$.rec = yylval.rec;
10931103
$$.row =NULL;
10941104
}
10951105
|T_ROW
10961106
{
1097-
$$.name =NULL;
1107+
$$.name =pstrdup(yytext);
10981108
$$.lineno = plpgsql_scanner_lineno();
1109+
$$.scalar =NULL;
10991110
$$.row = yylval.row;
11001111
$$.rec =NULL;
11011112
}
@@ -2121,30 +2132,6 @@ make_fetch_stmt(void)
21212132
}
21222133

21232134

2124-
static PLpgSQL_row *
2125-
make_scalar_list1(constchar *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-
21482135
staticvoid
21492136
check_assignable(PLpgSQL_datum *datum)
21502137
{
@@ -2256,6 +2243,37 @@ read_into_scalar_list(const char *initial_name,
22562243
return row;
22572244
}
22582245

2246+
/*
2247+
* Convert a single scalar into a "row" list. This is exactly
2248+
* like read_into_scalar_list except we never consume any input.
2249+
* In fact, since this can be invoked long after the source
2250+
* input was actually read, the lineno has to be passed in.
2251+
*/
2252+
static PLpgSQL_row *
2253+
make_scalar_list1(constchar *initial_name,
2254+
PLpgSQL_datum *initial_datum,
2255+
int lineno)
2256+
{
2257+
PLpgSQL_row*row;
2258+
2259+
check_assignable(initial_datum);
2260+
2261+
row =palloc(sizeof(PLpgSQL_row));
2262+
row->dtype = PLPGSQL_DTYPE_ROW;
2263+
row->refname =pstrdup("*internal*");
2264+
row->lineno = lineno;
2265+
row->rowtupdesc =NULL;
2266+
row->nfields =1;
2267+
row->fieldnames =palloc(sizeof(char *));
2268+
row->varnos =palloc(sizeof(int));
2269+
row->fieldnames[0] =pstrdup(initial_name);
2270+
row->varnos[0] = initial_datum->dno;
2271+
2272+
plpgsql_adddatum((PLpgSQL_datum *)row);
2273+
2274+
return row;
2275+
}
2276+
22592277
/*
22602278
* When the PL/PgSQL parser expects to see a SQL statement, it is very
22612279
* liberal in what it accepts; for example, we often assume an

‎src/test/regress/expected/plpgsql.out

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2722,22 +2722,53 @@ $$ language plpgsql;
27222722
ERROR: end label "outer_label" specified for unlabelled block
27232723
CONTEXT: compile of PL/pgSQL function "end_label4" near line 5
27242724
-- using list of scalars in fori and fore stmts
2725-
create function for_vect() returns void as $$
2725+
create function for_vect() returns void as $proc$
27262726
<<lbl>>declare a integer; b varchar; c varchar; r record;
27272727
begin
2728-
--oldfori
2729-
for i in 1 ..10 loop
2728+
-- fori
2729+
for i in 1 ..3 loop
27302730
raise notice '%', i;
27312731
end loop;
2732-
for a in select 1 from generate_series(1,4) loop
2732+
-- fore with record var
2733+
for r in select gs as aa, 'BB' as bb, 'CC' as cc from generate_series(1,4) gs loop
2734+
raise notice '% % %', r.aa, r.bb, r.cc;
2735+
end loop;
2736+
-- fore with single scalar
2737+
for a in select gs from generate_series(1,4) gs loop
27332738
raise notice '%', a;
27342739
end loop;
2735-
for a,b,c in select generate_series, 'BB','CC' from generate_series(1,4) loop
2740+
-- fore with multiple scalars
2741+
for a,b,c in select gs, 'BB','CC' from generate_series(1,4) gs loop
27362742
raise notice '% % %', a, b, c;
27372743
end loop;
27382744
-- using qualified names in fors, fore is enabled, disabled only for fori
2739-
for lbl.a, lbl.b, lbl.c in executeE'selectgenerate_series, \'bb\',\'cc\' from generate_series(1,4)' loop
2745+
for lbl.a, lbl.b, lbl.c in execute$$selectgs,'bb','cc' from generate_series(1,4) gs$$ loop
27402746
raise notice '% % %', a, b, c;
27412747
end loop;
27422748
end;
2743-
$$ language plpgsql;
2749+
$proc$ language plpgsql;
2750+
select for_vect();
2751+
NOTICE: 1
2752+
NOTICE: 2
2753+
NOTICE: 3
2754+
NOTICE: 1 BB CC
2755+
NOTICE: 2 BB CC
2756+
NOTICE: 3 BB CC
2757+
NOTICE: 4 BB CC
2758+
NOTICE: 1
2759+
NOTICE: 2
2760+
NOTICE: 3
2761+
NOTICE: 4
2762+
NOTICE: 1 BB CC
2763+
NOTICE: 2 BB CC
2764+
NOTICE: 3 BB CC
2765+
NOTICE: 4 BB CC
2766+
NOTICE: 1 bb cc
2767+
NOTICE: 2 bb cc
2768+
NOTICE: 3 bb cc
2769+
NOTICE: 4 bb cc
2770+
for_vect
2771+
----------
2772+
2773+
(1 row)
2774+

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp