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 ****
      <title>Assignment</title>
  
      <para>
!      An assignment of a value to a variable or row/record field is
!      written as:
  <synopsis>
! <replaceable>identifier</replaceable> := <replaceable>expression</replaceable>;
  </synopsis>
       As explained above, the expression in such a statement is evaluated
       by means of an SQL <command>SELECT</> command sent to the main
!      database engine.  The expression must yield a single value.
      </para>
  
      <para>
--- 1024,1040 ----
      <title>Assignment</title>
  
      <para>
!      An assignment of a value to a variable or row/record of field or list of 
!      variables is written as:
  <synopsis>
! <replaceable>target</replaceable> := <replaceable>expression</replaceable>;
  </synopsis>
       As explained above, the expression in such a statement is evaluated
       by means of an SQL <command>SELECT</> command sent to the main
!      database engine.  The expression must yield a single scalar, row, or record 
!      value. <replaceable>Target</replaceable> is a
!      record variable, row variable, or a comma-separated list of
!      simple variables and record/row fields.
      </para>
  
      <para>
***************
*** 1050,1055 ****
--- 1053,1059 ----
  <programlisting>
  user_id := 20;
  tax := subtotal * 0.06;
+ x, y, z := ROW(10,20,30);
  </programlisting>
      </para>
     </sect2>
***************
*** 2008,2018 ****
       accordingly. The syntax is:
  <synopsis>
  <optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
! FOR <replaceable>record_or_row</replaceable> IN <replaceable>query</replaceable> LOOP
      <replaceable>statements</replaceable>
  END LOOP <optional> <replaceable>label</replaceable> </optional>;
  </synopsis>
!      The record or row variable 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:
--- 2012,2024 ----
       accordingly. The syntax is:
  <synopsis>
  <optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
! 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
       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:
***************
*** 2047,2053 ****
       rows:
  <synopsis>
  <optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
! FOR <replaceable>record_or_row</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> LOOP 
      <replaceable>statements</replaceable>
  END LOOP <optional> <replaceable>label</replaceable> </optional>;
  </synopsis>
--- 2053,2059 ----
       rows:
  <synopsis>
  <optional> &lt;&lt;<replaceable>label</replaceable>&gt;&gt; </optional>
! FOR <replaceable>target</replaceable> IN EXECUTE <replaceable>text_expression</replaceable> LOOP 
      <replaceable>statements</replaceable>
  END LOOP <optional> <replaceable>label</replaceable> </optional>;
  </synopsis>
***************
*** 2056,2062 ****
       expression, which is evaluated and replanned on each entry to
       the <literal>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 <command>EXECUTE</command> statement.
      </para>
  
      <note>
--- 2062,2070 ----
       expression, which is evaluated and replanned on each entry to
       the <literal>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 <command>EXECUTE</command> statement. <replaceable>Target</replaceable> is a
!      record variable, row variable, or a comma-separated list of
!      simple variables and record/row fields
      </para>
  
      <note>
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 $$
+ <<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;
+   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;
