diff -cr pgsql/src/pl/plpgsql/src/gram.y pgsql-iudret/src/pl/plpgsql/src/gram.y
*** pgsql/src/pl/plpgsql/src/gram.y	2006-02-12 01:37:05.000000000 -0500
--- pgsql-iudret/src/pl/plpgsql/src/gram.y	2006-03-02 17:08:47.000000000 -0500
***************
*** 50,55 ****
--- 50,56 ----
  static	PLpgSQL_expr	*read_sql_stmt(const char *sqlstart);
  static	PLpgSQL_type	*read_datatype(int tok);
  static	PLpgSQL_stmt	*make_select_stmt(void);
+ static	PLpgSQL_stmt	*make_returning_stmt(char *command);
  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,
***************
*** 141,146 ****
--- 142,148 ----
  %type <stmt>	stmt_for stmt_select stmt_perform
  %type <stmt>	stmt_dynexecute stmt_getdiag
  %type <stmt>	stmt_open stmt_fetch stmt_close stmt_null
+ %type <stmt>	stmt_insert stmt_update stmt_delete
  
  %type <list>	proc_exceptions
  %type <exception_block> exception_sect
***************
*** 170,175 ****
--- 172,178 ----
  %token	K_DEBUG
  %token	K_DECLARE
  %token	K_DEFAULT
+ %token	K_DELETE
  %token	K_DIAGNOSTICS
  %token	K_DOTDOT
  %token	K_ELSE
***************
*** 185,190 ****
--- 188,194 ----
  %token	K_IF
  %token	K_IN
  %token	K_INFO
+ %token	K_INSERT
  %token	K_INTO
  %token	K_IS
  %token	K_LOG
***************
*** 201,212 ****
--- 205,218 ----
  %token	K_RENAME
  %token	K_RESULT_OID
  %token	K_RETURN
+ %token	K_RETURNING
  %token	K_RETURN_NEXT
  %token	K_REVERSE
  %token	K_SELECT
  %token	K_THEN
  %token	K_TO
  %token	K_TYPE
+ %token	K_UPDATE
  %token	K_WARNING
  %token	K_WHEN
  %token	K_WHILE
***************
*** 605,610 ****
--- 611,622 ----
  						{ $$ = $1; }
  				| stmt_select
  						{ $$ = $1; }
+ 				| stmt_insert
+ 						{ $$ = $1; }
+ 				| stmt_update
+ 						{ $$ = $1; }
+ 				| stmt_delete
+ 						{ $$ = $1; }
  				| stmt_exit
  						{ $$ = $1; }
  				| stmt_return
***************
*** 1119,1124 ****
--- 1131,1157 ----
  					}
  				;
  
+ stmt_insert		: K_INSERT lno
+ 					{
+ 						$$ = make_returning_stmt("INSERT");
+ 						$$->lineno = $2;
+ 					}
+ 				;
+ 
+ stmt_update		: K_UPDATE lno
+ 					{
+ 						$$ = make_returning_stmt("UPDATE");
+ 						$$->lineno = $2;
+ 					}
+ 				;
+ 
+ stmt_delete		: K_DELETE lno
+ 					{
+ 						$$ = make_returning_stmt("DELETE");
+ 						$$->lineno = $2;
+ 					}
+ 				;
+ 
  stmt_exit		: exit_type lno opt_label opt_exitcond
  					{
  						PLpgSQL_stmt_exit *new;
***************
*** 2077,2087 ****
--- 2110,2260 ----
  		execsql = palloc(sizeof(PLpgSQL_stmt_execsql));
  		execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
  		execsql->sqlstmt  = expr;
+ 		execsql->rec	  = rec;
+ 		execsql->row	  = row;
  
  		return (PLpgSQL_stmt *)execsql;
  	}
  }
  
+ static PLpgSQL_stmt *
+ make_returning_stmt(char *command)
+ {
+ 	PLpgSQL_dstring		ds;
+ 	int					nparams = 0;
+ 	int					params[1024];
+ 	char				buf[32];
+ 	PLpgSQL_expr		*expr;
+ 	PLpgSQL_row			*row = NULL;
+ 	PLpgSQL_rec			*rec = NULL;
+ 	int					tok;
+ 	bool				have_returning = false;
+ 	bool				have_into = false;
+ 	PLpgSQL_stmt_execsql *execsql;
+ 
+ 	plpgsql_dstring_init(&ds);
+ 	plpgsql_dstring_append(&ds, command);
+ 	plpgsql_dstring_append(&ds, " ");
+ 
+ 	while (1)
+ 	{
+ 		tok = yylex();
+ 
+ 		if (tok == ';')
+ 			break;
+ 		if (tok == 0)
+ 		{
+ 			plpgsql_error_lineno = plpgsql_scanner_lineno();
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_SYNTAX_ERROR),
+ 					 errmsg("unexpected end of function definition")));
+ 		}
+ 		if (tok == K_RETURNING)
+ 		{
+ 			if (have_returning)
+ 			{
+ 				plpgsql_error_lineno = plpgsql_scanner_lineno();
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("RETURNING specified more than once")));
+ 			}
+ 			have_returning = true;
+ 		}
+ 		if (tok == K_INTO && have_returning)
+ 		{
+ 			if (have_into)
+ 			{
+ 				plpgsql_error_lineno = plpgsql_scanner_lineno();
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("RETURNING INTO specified more than once")));
+ 			}
+ 			tok = yylex();
+ 			switch (tok)
+ 			{
+ 				case T_ROW:
+ 					row = yylval.row;
+ 					check_assignable((PLpgSQL_datum *) row);
+ 					have_into = true;
+ 					break;
+ 
+ 				case T_RECORD:
+ 					rec = yylval.rec;
+ 					check_assignable((PLpgSQL_datum *) rec);
+ 					have_into = true;
+ 					break;
+ 
+ 				case T_SCALAR:
+ 					row = read_into_scalar_list(yytext, yylval.scalar);
+ 					have_into = true;
+ 					break;
+ 
+ 				default:
+ 					/* Treat the INTO as non-special */
+ 					plpgsql_dstring_append(&ds, " INTO ");
+ 					plpgsql_push_back_token(tok);
+ 					break;
+ 			}
+ 			continue;
+ 		}
+ 
+ 		if (plpgsql_SpaceScanned)
+ 			plpgsql_dstring_append(&ds, " ");
+ 
+ 		/* Check for array overflow */
+ 		if (nparams >= 1024)
+ 		{
+ 			plpgsql_error_lineno = plpgsql_scanner_lineno();
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ 					 errmsg("too many parameters specified in SQL statement")));
+ 		}
+ 
+ 		switch (tok)
+ 		{
+ 			case T_SCALAR:
+ 				params[nparams] = yylval.scalar->dno;
+ 				snprintf(buf, sizeof(buf), " $%d ", ++nparams);
+ 				plpgsql_dstring_append(&ds, buf);
+ 				break;
+ 
+ 			case T_ROW:
+ 				params[nparams] = yylval.row->rowno;
+ 				snprintf(buf, sizeof(buf), " $%d ", ++nparams);
+ 				plpgsql_dstring_append(&ds, buf);
+ 				break;
+ 
+ 			case T_RECORD:
+ 				params[nparams] = yylval.rec->recno;
+ 				snprintf(buf, sizeof(buf), " $%d ", ++nparams);
+ 				plpgsql_dstring_append(&ds, buf);
+ 				break;
+ 
+ 			default:
+ 				plpgsql_dstring_append(&ds, yytext);
+ 				break;
+ 		}
+ 	}
+ 
+ 	expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
+ 	expr->dtype			= PLPGSQL_DTYPE_EXPR;
+ 	expr->query			= pstrdup(plpgsql_dstring_get(&ds));
+ 	expr->plan			= NULL;
+ 	expr->nparams		= nparams;
+ 	while(nparams-- > 0)
+ 		expr->params[nparams] = params[nparams];
+ 	plpgsql_dstring_free(&ds);
+ 
+ 	check_sql_expr(expr->query);
+ 
+ 	execsql = palloc(sizeof(PLpgSQL_stmt_execsql));
+ 	execsql->cmd_type = PLPGSQL_STMT_EXECSQL;
+ 	execsql->sqlstmt  = expr;
+ 	execsql->rec  	  = rec;
+ 	execsql->row  	  = row;
+ 
+ 	return (PLpgSQL_stmt *)execsql;
+ }
  
  static PLpgSQL_stmt *
  make_fetch_stmt(void)
diff -cr pgsql/src/pl/plpgsql/src/pl_exec.c pgsql-iudret/src/pl/plpgsql/src/pl_exec.c
*** pgsql/src/pl/plpgsql/src/pl_exec.c	2006-03-02 00:34:12.000000000 -0500
--- pgsql-iudret/src/pl/plpgsql/src/pl_exec.c	2006-03-02 16:36:36.000000000 -0500
***************
*** 2266,2273 ****
  
  
  /* ----------
!  * exec_stmt_execsql			Execute an SQL statement not
!  *					returning any data.
   * ----------
   */
  static int
--- 2266,2273 ----
  
  
  /* ----------
!  * exec_stmt_execsql			Execute an SQL statement which
!  *					may return data.
   * ----------
   */
  static int
***************
*** 2279,2284 ****
--- 2279,2286 ----
  	char	   *nulls;
  	int			rc;
  	PLpgSQL_expr *expr = stmt->sqlstmt;
+ 	PLpgSQL_rec *rec = NULL;
+ 	PLpgSQL_row *row = NULL;
  
  	/*
  	 * On the first call for this expression generate the plan
***************
*** 2307,2322 ****
  	}
  
  	/*
  	 * Execute the plan
  	 */
  	rc = SPI_execute_plan(expr->plan, values, nulls,
  						  estate->readonly_func, 0);
  	switch (rc)
  	{
  		case SPI_OK_UTILITY:
  		case SPI_OK_SELINTO:
  			break;
- 
  		case SPI_OK_INSERT:
  		case SPI_OK_DELETE:
  		case SPI_OK_UPDATE:
--- 2309,2343 ----
  	}
  
  	/*
+ 	 * If the user has selected the RETURNING option, we're going to
+ 	 * determine how to return it.
+ 	 */
+ 	if (stmt->rec != NULL)
+ 		rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);
+ 	else if (stmt->row != NULL)
+ 		row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]);
+ 
+ 	/*
  	 * Execute the plan
  	 */
  	rc = SPI_execute_plan(expr->plan, values, nulls,
  						  estate->readonly_func, 0);
+ 
+ 	/* Assign to INTO variable */
+ 	if (rec || row)
+ 	{
+ 		if (SPI_processed == 0)
+ 			exec_move_row(estate, rec, row, NULL, SPI_tuptable->tupdesc);
+ 		else
+ 			exec_move_row(estate, rec, row,
+ 				SPI_tuptable->vals[0], SPI_tuptable->tupdesc);
+ 	}
+ 
  	switch (rc)
  	{
  		case SPI_OK_UTILITY:
  		case SPI_OK_SELINTO:
  			break;
  		case SPI_OK_INSERT:
  		case SPI_OK_DELETE:
  		case SPI_OK_UPDATE:
***************
*** 2340,2357 ****
  				 expr->query, SPI_result_code_string(rc));
  	}
  
! 	/*
! 	 * Release any result tuples from SPI_execute_plan (probably shouldn't be
! 	 * any)
! 	 */
  	SPI_freetuptable(SPI_tuptable);
  
  	/* Save result info for GET DIAGNOSTICS */
  	estate->eval_processed = SPI_processed;
  	estate->eval_lastoid = SPI_lastoid;
  
! 	pfree(values);
! 	pfree(nulls);
  
  	return PLPGSQL_RC_OK;
  }
--- 2361,2375 ----
  				 expr->query, SPI_result_code_string(rc));
  	}
  
! 	/* Release any result tuples from SPI_execute_plan */
  	SPI_freetuptable(SPI_tuptable);
  
  	/* Save result info for GET DIAGNOSTICS */
  	estate->eval_processed = SPI_processed;
  	estate->eval_lastoid = SPI_lastoid;
  
!     pfree(values);
!     pfree(nulls);
  
  	return PLPGSQL_RC_OK;
  }
diff -cr pgsql/src/pl/plpgsql/src/plpgsql.h pgsql-iudret/src/pl/plpgsql/src/plpgsql.h
*** pgsql/src/pl/plpgsql/src/plpgsql.h	2006-03-02 00:34:12.000000000 -0500
--- pgsql-iudret/src/pl/plpgsql/src/plpgsql.h	2006-03-02 15:11:56.000000000 -0500
***************
*** 528,533 ****
--- 528,535 ----
  {								/* Generic SQL statement to execute */
  	int			cmd_type;
  	int			lineno;
+ 	PLpgSQL_rec *rec;			/* INTO record or row variable */
+ 	PLpgSQL_row *row;
  	PLpgSQL_expr *sqlstmt;
  } PLpgSQL_stmt_execsql;
  
diff -cr pgsql/src/pl/plpgsql/src/scan.l pgsql-iudret/src/pl/plpgsql/src/scan.l
*** pgsql/src/pl/plpgsql/src/scan.l	2006-02-27 11:09:50.000000000 -0500
--- pgsql-iudret/src/pl/plpgsql/src/scan.l	2006-03-02 16:24:38.000000000 -0500
***************
*** 144,149 ****
--- 144,150 ----
  debug			{ return K_DEBUG;			}
  declare			{ return K_DECLARE;			}
  default			{ return K_DEFAULT;			}
+ delete			{ return K_DELETE;			}
  diagnostics		{ return K_DIAGNOSTICS;		}
  else			{ return K_ELSE;			}
  elseif          { return K_ELSIF;           }
***************
*** 159,164 ****
--- 160,166 ----
  if				{ return K_IF;				}
  in				{ return K_IN;				}
  info			{ return K_INFO;			}
+ insert			{ return K_INSERT;			}
  into			{ return K_INTO;			}
  is				{ return K_IS;				}
  log				{ return K_LOG;				}
***************
*** 174,185 ****
--- 176,189 ----
  rename			{ return K_RENAME;			}
  result_oid		{ return K_RESULT_OID;		}
  return			{ return K_RETURN;			}
+ returning		{ return K_RETURNING;		}
  reverse			{ return K_REVERSE;			}
  row_count		{ return K_ROW_COUNT;		}
  select			{ return K_SELECT;			}
  then			{ return K_THEN;			}
  to				{ return K_TO;				}
  type			{ return K_TYPE;			}
+ update			{ return K_UPDATE;			}
  warning			{ return K_WARNING;			}
  when			{ return K_WHEN;			}
  while			{ return K_WHILE;			}

