Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
P
Postgres FD Implementation
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Abuhujair Javed
Postgres FD Implementation
Commits
15c72174
Commit
15c72174
authored
Feb 12, 2006
by
Tom Lane
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Apply code-reviewed version of for-scalar-list patch: mostly, fixing
it to report reasonable errors in error cases.
parent
58634caa
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
157 additions
and
98 deletions
+157
-98
doc/src/sgml/plpgsql.sgml
doc/src/sgml/plpgsql.sgml
+11
-8
src/pl/plpgsql/src/gram.y
src/pl/plpgsql/src/gram.y
+93
-75
src/test/regress/expected/plpgsql.out
src/test/regress/expected/plpgsql.out
+38
-7
src/test/regress/sql/plpgsql.sql
src/test/regress/sql/plpgsql.sql
+15
-8
No files found.
doc/src/sgml/plpgsql.sgml
View file @
15c72174
<!--
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.8
5 2006/02/12 06:03:38 momjian
Exp $
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.8
6 2006/02/12 06:37:05 tgl
Exp $
-->
<chapter id="plpgsql">
...
...
@@ -1968,10 +1968,12 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>;
</synopsis>
<para>
This form of <literal>FOR</> creates a loop that iterates over a range
of integer
values. The variable
This form of <literal>FOR</> creates a loop that iterates over a range
of integer
values. The variable
<replaceable>name</replaceable> is automatically defined as type
<type>integer</> and exists only inside the loop. The two expressions giving
<type>integer</> and exists only inside the loop (any existing
definition of the variable name is ignored within the loop).
The two expressions giving
the lower and upper bound of the range are evaluated once when entering
the loop. The iteration step is normally 1, but is -1 when <literal>REVERSE</> is
specified.
...
...
@@ -2012,9 +2014,9 @@ FOR <replaceable>target</replaceable> IN <replaceable>query</replaceable> LOOP
<replaceable>statements</replaceable>
END LOOP <optional> <replaceable>label</replaceable> </optional>;
</synopsis>
<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
The <replaceable>target</replaceable> is a record variable, row variable,
or
comma-separated list of scalar variables.
The <replaceable>target</replaceable>
is successively assigned each row
resulting from the <replaceable>query</replaceable> (which must be a
<command>SELECT</command> command) and the loop body is executed for each
row. Here is an example:
...
...
@@ -2069,7 +2071,8 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>;
<literal>IN</> and <literal>LOOP</>. If <literal>..</> is not seen then
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
<quote>loop variable of loop over rows must be a record or row or scalar variable</>,
<quote>loop variable of loop over rows must be a record or row variable
or list of scalar variables</>,
rather than the simple syntax error one might expect to get.
</para>
</note>
...
...
src/pl/plpgsql/src/gram.y
View file @
15c72174
...
...
@@ -4,7 +4,7 @@
* procedural language
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.8
4 2006/02/12 06:03:38 momjian
Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.8
5 2006/02/12 06:37:05 tgl
Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
...
...
@@ -54,13 +54,14 @@ static PLpgSQL_stmt *make_fetch_stmt(void);
static void check_assignable(PLpgSQL_datum *datum);
static PLpgSQL_row *read_into_scalar_list(const char *initial_name,
PLpgSQL_datum *initial_datum);
static PLpgSQL_row *make_scalar_list1(const char *initial_name,
PLpgSQL_datum *initial_datum,
int lineno);
static void check_sql_expr(const char *stmt);
static void plpgsql_sql_error_callback(void *arg);
static void check_labels(const char *start_label,
const char *end_label);
static PLpgSQL_row *make_scalar_list1(const char *name,
PLpgSQL_datum *variable);
%}
%union {
...
...
@@ -76,9 +77,9 @@ static PLpgSQL_row *make_scalar_list1(const char *name,
{
char *name;
int lineno;
PLpgSQL_datum *scalar;
PLpgSQL_rec *rec;
PLpgSQL_row *row;
PLpgSQL_datum *scalar;
} forvariable;
struct
{
...
...
@@ -895,13 +896,14 @@ for_control :
}
else if ($2.scalar)
{
new->row = make_scalar_list1($2.name, $2.scalar);
check_assignable((PLpgSQL_datum *) new->row);
/* convert single scalar to list */
new->row = make_scalar_list1($2.name, $2.scalar, $2.lineno);
/* no need for check_assignable */
}
else
{
plpgsql_error_lineno = $
1
;
yyerror("loop variable of loop over rows must be a record
, row, or scalar variable
");
plpgsql_error_lineno = $
2.lineno
;
yyerror("loop variable of loop over rows must be a record
or row variable or list of scalar variables
");
}
new->query = expr;
...
...
@@ -950,24 +952,24 @@ for_control :
PLpgSQL_expr *expr2;
PLpgSQL_var *fvar;
PLpgSQL_stmt_fori *new;
char *varname;
/* First expression is well-formed */
check_sql_expr(expr1->query);
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;
}
/* should have had a single variable name */
plpgsql_error_lineno = $2.lineno;
if ($2.scalar && $2.row)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("integer FOR loop must have just one target variable")));
/* create loop's private variable */
plpgsql_convert_ident($2.name, &varname, 1);
fvar = (PLpgSQL_var *)
plpgsql_build_variable(
$2.
name,
plpgsql_build_variable(
var
name,
$2.lineno,
plpgsql_build_datatype(INT4OID,
-1),
...
...
@@ -1021,13 +1023,14 @@ for_control :
}
else if ($2.scalar)
{
new->row = make_scalar_list1($2.name, $2.scalar);
check_assignable((PLpgSQL_datum *) new->row);
/* convert single scalar to list */
new->row = make_scalar_list1($2.name, $2.scalar, $2.lineno);
/* no need for check_assignable */
}
else
{
plpgsql_error_lineno = $
1
;
yyerror("loop variable of loop over rows must be
record, row, or scalar variable
");
plpgsql_error_lineno = $
2.lineno
;
yyerror("loop variable of loop over rows must be
a record or row variable or list of scalar variables
");
}
new->query = expr1;
...
...
@@ -1047,55 +1050,63 @@ for_control :
* if any, because that's what we need for the loop-over-query case. Note
* that we must NOT apply check_assignable() or any other semantic check
* until we know what's what.
*
* However, if we see a comma-separated list of names, we know that it
* can't be an integer FOR loop and so it's OK to check the variables
* immediately. In particular, for T_WORD followed by comma, we should
* complain that the name is not known rather than say it's a syntax error.
* Note that the non-error result of this case sets *both* $$.scalar and
* $$.row; see the for_control production.
*/
for_variable : T_SCALAR
{
int tok;
char *name;
name = pstrdup(yytext);
$$.scalar = yylval.scalar;
$$.lineno = plpgsql_scanner_lineno();
{
int tok;
if((tok = yylex()) == ',')
{
plpgsql_push_back_token(tok);
$$.name = 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;
}
$$.name = pstrdup(yytext);
$$.lineno = plpgsql_scanner_lineno();
$$.scalar = yylval.scalar;
$$.rec = NULL;
$$.row = NULL;
/* check for comma-separated list */
tok = yylex();
plpgsql_push_back_token(tok);
if (tok == ',')
$$.row = read_into_scalar_list($$.name, $$.scalar);
}
| T_WORD
{
char *name
;
int tok
;
plpgsql_convert_ident(yytext, &name, 1);
$$.name = name;
$$.name = pstrdup(yytext);
$$.lineno = plpgsql_scanner_lineno();
$$.scalar = NULL;
$$.rec = NULL;
$$.row = NULL;
/* check for comma-separated list */
tok = yylex();
plpgsql_push_back_token(tok);
if (tok == ',')
{
plpgsql_error_lineno = $$.lineno;
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("\"%s\" is not a scalar variable",
$$.name)));
}
}
| T_RECORD
{
$$.name =
NULL
;
$$.name =
pstrdup(yytext)
;
$$.lineno = plpgsql_scanner_lineno();
$$.scalar = NULL;
$$.rec = yylval.rec;
$$.row = NULL;
}
| T_ROW
{
$$.name = NULL
;
$$.name = pstrdup(yytext)
;
$$.lineno = plpgsql_scanner_lineno();
$$.scalar = NULL;
$$.row = yylval.row;
$$.rec = NULL;
}
...
...
@@ -2121,30 +2132,6 @@ 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
check_assignable(PLpgSQL_datum *datum)
{
...
...
@@ -2256,6 +2243,37 @@ read_into_scalar_list(const char *initial_name,
return row;
}
/*
* Convert a single scalar into a "row" list. This is exactly
* like read_into_scalar_list except we never consume any input.
* In fact, since this can be invoked long after the source
* input was actually read, the lineno has to be passed in.
*/
static PLpgSQL_row *
make_scalar_list1(const char *initial_name,
PLpgSQL_datum *initial_datum,
int lineno)
{
PLpgSQL_row *row;
check_assignable(initial_datum);
row = palloc(sizeof(PLpgSQL_row));
row->dtype = PLPGSQL_DTYPE_ROW;
row->refname = pstrdup("*internal*");
row->lineno = lineno;
row->rowtupdesc = NULL;
row->nfields = 1;
row->fieldnames = palloc(sizeof(char *));
row->varnos = palloc(sizeof(int));
row->fieldnames[0] = pstrdup(initial_name);
row->varnos[0] = initial_datum->dno;
plpgsql_adddatum((PLpgSQL_datum *)row);
return row;
}
/*
* When the PL/PgSQL parser expects to see a SQL statement, it is very
* liberal in what it accepts; for example, we often assume an
...
...
src/test/regress/expected/plpgsql.out
View file @
15c72174
...
...
@@ -2722,22 +2722,53 @@ $$ language plpgsql;
ERROR: end label "outer_label" specified for unlabelled block
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 $$
create function for_vect() returns void as $
proc
$
<<lbl>>declare a integer; b varchar; c varchar; r record;
begin
--
old
fori
for i in 1 ..
10
loop
-- fori
for i in 1 ..
3
loop
raise notice '%', i;
end loop;
for a in select 1 from generate_series(1,4) loop
-- fore with record var
for r in select gs as aa, 'BB' as bb, 'CC' as cc from generate_series(1,4) gs loop
raise notice '% % %', r.aa, r.bb, r.cc;
end loop;
-- fore with single scalar
for a in select gs from generate_series(1,4) gs loop
raise notice '%', a;
end loop;
for a,b,c in select generate_series, 'BB','CC' from generate_series(1,4) loop
-- fore with multiple scalars
for a,b,c in select gs, 'BB','CC' from generate_series(1,4) gs 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
for lbl.a, lbl.b, lbl.c in execute
$$select gs, 'bb','cc' from generate_series(1,4) gs$$
loop
raise notice '% % %', a, b, c;
end loop;
end;
$$ language plpgsql;
$proc$ language plpgsql;
select for_vect();
NOTICE: 1
NOTICE: 2
NOTICE: 3
NOTICE: 1 BB CC
NOTICE: 2 BB CC
NOTICE: 3 BB CC
NOTICE: 4 BB CC
NOTICE: 1
NOTICE: 2
NOTICE: 3
NOTICE: 4
NOTICE: 1 BB CC
NOTICE: 2 BB CC
NOTICE: 3 BB CC
NOTICE: 4 BB CC
NOTICE: 1 bb cc
NOTICE: 2 bb cc
NOTICE: 3 bb cc
NOTICE: 4 bb cc
for_vect
----------
(1 row)
src/test/regress/sql/plpgsql.sql
View file @
15c72174
...
...
@@ -2281,24 +2281,31 @@ begin
end
;
$$
language
plpgsql
;
-- using list of scalars in fori and fore stmts
create
function
for_vect
()
returns
void
as
$$
create
function
for_vect
()
returns
void
as
$
proc
$
<<
lbl
>>
declare
a
integer
;
b
varchar
;
c
varchar
;
r
record
;
begin
--
old
fori
for
i
in
1
..
10
loop
-- fori
for
i
in
1
..
3
loop
raise
notice
'%'
,
i
;
end
loop
;
for
a
in
select
1
from
generate_series
(
1
,
4
)
loop
-- fore with record var
for
r
in
select
gs
as
aa
,
'BB'
as
bb
,
'CC'
as
cc
from
generate_series
(
1
,
4
)
gs
loop
raise
notice
'% % %'
,
r
.
aa
,
r
.
bb
,
r
.
cc
;
end
loop
;
-- fore with single scalar
for
a
in
select
gs
from
generate_series
(
1
,
4
)
gs
loop
raise
notice
'%'
,
a
;
end
loop
;
for
a
,
b
,
c
in
select
generate_series
,
'BB'
,
'CC'
from
generate_series
(
1
,
4
)
loop
-- fore with multiple scalars
for
a
,
b
,
c
in
select
gs
,
'BB'
,
'CC'
from
generate_series
(
1
,
4
)
gs
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
for
lbl
.
a
,
lbl
.
b
,
lbl
.
c
in
execute
$$
select
gs
,
'bb'
,
'cc'
from
generate_series
(
1
,
4
)
gs
$$
loop
raise
notice
'% % %'
,
a
,
b
,
c
;
end
loop
;
end
;
$$
language
plpgsql
;
$
proc
$
language
plpgsql
;
select
for_vect
();
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment