diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index 517adc9d51..0c3839cd4d 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -1,5 +1,5 @@ @@ -2008,11 +2008,13 @@ END LOOP; accordingly. The syntax is: <<label>> -FOR record_or_row IN query LOOP +FOR target IN query LOOP statements END LOOP label ; - The record or row variable is successively assigned each row + Target 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 query (which must be a SELECT command) and the loop body is executed for each row. Here is an example: @@ -2047,7 +2049,7 @@ $$ LANGUAGE plpgsql; rows: <<label>> -FOR record_or_row IN EXECUTE text_expression LOOP +FOR target IN EXECUTE text_expression LOOP statements END LOOP label ; @@ -2067,7 +2069,7 @@ END LOOP label ; IN and LOOP. If .. is not seen then the loop is presumed to be a loop over rows. Mistyping the .. is thus likely to lead to a complaint along the lines of - loop variable of loop over rows must be a record or row variable, + 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. diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index 72735873c4..a23e57a158 100644 --- a/src/pl/plpgsql/src/gram.y +++ b/src/pl/plpgsql/src/gram.y @@ -4,7 +4,7 @@ * procedural language * * 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. * @@ -58,7 +58,9 @@ 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,6 +78,7 @@ static void check_labels(const char *start_label, int lineno; PLpgSQL_rec *rec; PLpgSQL_row *row; + PLpgSQL_datum *scalar; } forvariable; struct { @@ -890,10 +893,15 @@ for_control : new->row = $2.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 { 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; @@ -948,6 +956,15 @@ for_control : 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 */ fvar = (PLpgSQL_var *) plpgsql_build_variable($2.name, @@ -1002,10 +1019,15 @@ for_control : new->row = $2.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 { 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; @@ -1027,14 +1049,31 @@ for_control : * until we know what's what. */ for_variable : T_SCALAR - { + { + int tok; char *name; + + name = pstrdup(yytext); + $$.scalar = yylval.scalar; + $$.lineno = plpgsql_scanner_lineno(); - plpgsql_convert_ident(yytext, &name, 1); - $$.name = name; - $$.lineno = plpgsql_scanner_lineno(); - $$.rec = NULL; - $$.row = NULL; + 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; + } } | T_WORD { @@ -1048,20 +1087,14 @@ for_variable : T_SCALAR } | T_RECORD { - char *name; - - plpgsql_convert_ident(yytext, &name, 1); - $$.name = name; + $$.name = NULL; $$.lineno = plpgsql_scanner_lineno(); $$.rec = yylval.rec; $$.row = NULL; } | T_ROW { - char *name; - - plpgsql_convert_ident(yytext, &name, 1); - $$.name = name; + $$.name = NULL; $$.lineno = plpgsql_scanner_lineno(); $$.row = yylval.row; $$.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 check_assignable(PLpgSQL_datum *datum) { diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index aef73e1eee..3bcafcfb4d 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -2721,3 +2721,23 @@ end; $$ 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 $$ +<>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; diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index fdb2f46ff8..63b8bef14e 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -2280,3 +2280,25 @@ begin end loop outer_label; end; $$ language plpgsql; + + +-- using list of scalars in fori and fore stmts +create function for_vect() returns void as $$ +<>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;