diff -c -r pgsql.old/doc/src/sgml/plpgsql.sgml pgsql2/doc/src/sgml/plpgsql.sgml *** pgsql.old/doc/src/sgml/plpgsql.sgml 2005-12-08 19:02:04.000000000 +0100 --- pgsql2/doc/src/sgml/plpgsql.sgml 2005-12-19 20:49:08.000000000 +0100 *************** *** 1024,1037 **** Assignment ! An assignment of a value to a variable or row/record field is ! written as: ! identifier := expression; As explained above, the expression in such a statement is evaluated by means of an SQL SELECT command sent to the main ! database engine. The expression must yield a single value. --- 1024,1040 ---- Assignment ! An assignment of a value to a variable or row/record of field or list of ! variables is written as: ! target := expression; As explained above, the expression in such a statement is evaluated by means of an SQL SELECT command sent to the main ! database engine. The expression must yield a single scalar, row, or record ! value. Target is a ! record variable, row variable, or a comma-separated list of ! simple variables and record/row fields. *************** *** 1050,1055 **** --- 1053,1059 ---- user_id := 20; tax := subtotal * 0.06; + x, y, z := ROW(10,20,30); *************** *** 2008,2018 **** accordingly. The syntax is: <<label>> ! FOR record_or_row IN query LOOP statements END LOOP label ; ! The record or row variable 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: --- 2012,2024 ---- accordingly. The syntax is: <<label>> ! FOR target IN query LOOP statements END LOOP label ; ! 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,2053 **** rows: <<label>> ! FOR record_or_row IN EXECUTE text_expression LOOP statements END LOOP label ; --- 2053,2059 ---- rows: <<label>> ! FOR target IN EXECUTE text_expression LOOP statements END LOOP label ; *************** *** 2056,2062 **** expression, which is evaluated and replanned on each entry to the FOR loop. This allows the programmer to choose the speed of a preplanned query or the flexibility of a dynamic query, just ! as with a plain EXECUTE statement. --- 2062,2070 ---- expression, which is evaluated and replanned on each entry to the FOR loop. This allows the programmer to choose the speed of a preplanned query or the flexibility of a dynamic query, just ! as with a plain EXECUTE statement. Target is a ! record variable, row variable, or a comma-separated list of ! simple variables and record/row fields diff -c -r pgsql.old/src/pl/plpgsql/src/gram.y pgsql2/src/pl/plpgsql/src/gram.y *** pgsql.old/src/pl/plpgsql/src/gram.y 2005-10-13 17:34:19.000000000 +0200 --- pgsql2/src/pl/plpgsql/src/gram.y 2005-12-19 18:56:21.000000000 +0100 *************** *** 59,64 **** --- 59,67 ---- 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,81 **** --- 79,85 ---- int lineno; PLpgSQL_rec *rec; PLpgSQL_row *row; + PLpgSQL_datum *scalar; } forvariable; struct { *************** *** 709,716 **** assign_var : T_SCALAR { ! check_assignable(yylval.scalar); ! $$ = yylval.scalar->dno; } | T_ROW { --- 713,736 ---- assign_var : T_SCALAR { ! int tok; ! PLpgSQL_datum *var = yylval.scalar; ! char *name = pstrdup(yytext); ! ! if ((tok = yylex()) == ',') ! { ! PLpgSQL_row *row; ! ! plpgsql_push_back_token(tok); ! row = read_into_scalar_list(name, var); ! $$ = row->rowno; ! } ! else ! { ! plpgsql_push_back_token(tok); ! check_assignable(var); ! $$ = var->dno; ! } } | T_ROW { *************** *** 884,893 **** new->rec = $2.rec; else if ($2.row) new->row = $2.row; else { plpgsql_error_lineno = $1; ! yyerror("loop variable of loop over rows must be a record or row variable"); } new->query = expr; --- 904,915 ---- new->rec = $2.rec; else if ($2.row) new->row = $2.row; + else if ($2.scalar) + new->row = make_scalar_list1($2.name, $2.scalar); else { plpgsql_error_lineno = $1; ! yyerror("loop variable of loop over rows must be a record or row or scalar variable"); } new->query = expr; *************** *** 942,947 **** --- 964,978 ---- expr2 = plpgsql_read_expression(K_LOOP, "LOOP"); + /* T_WORD identifier is converted */ + if ($2.scalar) + { + char *name; + plpgsql_convert_ident($2.name, &name, 1); + pfree($2.name); + $2.name = name; + } + fvar = (PLpgSQL_var *) plpgsql_build_variable($2.name, $2.lineno, *************** *** 989,998 **** new->rec = $2.rec; else if ($2.row) new->row = $2.row; else { plpgsql_error_lineno = $1; ! yyerror("loop variable of loop over rows must be record or row variable"); } new->query = expr1; --- 1020,1031 ---- new->rec = $2.rec; else if ($2.row) new->row = $2.row; + else if ($2.scalar) + new->row = make_scalar_list1($2.name, $2.scalar); else { plpgsql_error_lineno = $1; ! yyerror("loop variable of loop over rows must be record or row or scalar variable"); } new->query = expr1; *************** *** 1004,1016 **** for_variable : T_SCALAR { ! char *name; ! ! plpgsql_convert_ident(yytext, &name, 1); ! $$.name = name; $$.lineno = plpgsql_scanner_lineno(); ! $$.rec = NULL; ! $$.row = NULL; } | T_WORD { --- 1037,1066 ---- for_variable : T_SCALAR { ! char * name; ! int tok; ! ! name = pstrdup(yytext); ! $$.scalar = yylval.scalar; $$.lineno = plpgsql_scanner_lineno(); ! ! 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 { *************** *** 1024,1046 **** } | T_RECORD { ! char *name; ! ! plpgsql_convert_ident(yytext, &name, 1); ! $$.name = name; $$.lineno = plpgsql_scanner_lineno(); $$.rec = yylval.rec; $$.row = NULL; } | T_ROW { ! char *name; ! ! plpgsql_convert_ident(yytext, &name, 1); ! $$.name = name; $$.lineno = plpgsql_scanner_lineno(); $$.row = yylval.row; $$.rec = NULL; } ; --- 1074,1092 ---- } | T_RECORD { ! $$.name = NULL; $$.lineno = plpgsql_scanner_lineno(); $$.rec = yylval.rec; $$.row = NULL; + $$.scalar = NULL; } | T_ROW { ! $$.name = NULL; $$.lineno = plpgsql_scanner_lineno(); $$.row = yylval.row; $$.rec = NULL; + $$.scalar = NULL; } ; *************** *** 2166,2171 **** --- 2212,2241 ---- return row; } + 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; + } + + /* * 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 diff -c -r pgsql.old/src/test/regress/sql/plpgsql.sql pgsql2/src/test/regress/sql/plpgsql.sql *** pgsql.old/src/test/regress/sql/plpgsql.sql 2005-09-14 20:35:38.000000000 +0200 --- pgsql2/src/test/regress/sql/plpgsql.sql 2005-12-19 19:55:38.000000000 +0100 *************** *** 2280,2282 **** --- 2280,2306 ---- end loop outer_label; end; $$ language plpgsql; + + 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; + for r in select generate_series+1, generate_series+2 from generate_series(1,4) loop + a, b := r; + raise notice '% %', a,b; + end loop; + end; + $$ language plpgsql;