Index: src/backend/access/common/printtup.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/access/common/printtup.c,v
retrieving revision 1.91
diff -C5 -r1.91 printtup.c
*** src/backend/access/common/printtup.c	22 Jun 2005 17:45:45 -0000	1.91
--- src/backend/access/common/printtup.c	5 Jul 2005 03:36:00 -0000
***************
*** 15,24 ****
--- 15,25 ----
   */
  #include "postgres.h"
  
  #include "access/heapam.h"
  #include "access/printtup.h"
+ #include "executor/executor.h"
  #include "libpq/libpq.h"
  #include "libpq/pqformat.h"
  #include "tcop/pquery.h"
  #include "utils/lsyscache.h"
  #include "utils/portal.h"
***************
*** 60,69 ****
--- 61,75 ----
  	Portal		portal;			/* the Portal we are printing from */
  	bool		sendDescrip;	/* send RowDescription at startup? */
  	TupleDesc	attrinfo;		/* The attr info we are set up for */
  	int			nattrs;
  	PrinttupAttrInfo *myinfo;	/* Cached info about each attr */
+ 	Datum	   *values;			/* preallocated space for deformtuple */
+ 	char	   *nulls;
+ 	char	   *ret;			/* map of tuples to return */
+ 	TupleDesc	retTupDesc;
+ 	bool		isret;
  } DR_printtup;
  
  /* ----------------
   *		Initialize: create a DestReceiver for printtup
   * ----------------
***************
*** 98,116 ****
--- 104,126 ----
  	self->sendDescrip = (dest == Remote);
  
  	self->attrinfo = NULL;
  	self->nattrs = 0;
  	self->myinfo = NULL;
+ 	self->values = NULL;
+ 	self->nulls = NULL;
+ 	self->ret = NULL;
  
  	return (DestReceiver *) self;
  }
  
  static void
  printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
  {
  	DR_printtup *myState = (DR_printtup *) self;
  	Portal		portal = myState->portal;
+ 	List		*ret = ((Query *) linitial(portal->parseTrees))->returning;
  
  	if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3)
  	{
  		/*
  		 * Send portal name to frontend (obsolete cruft, gone in proto
***************
*** 124,141 ****
  			portalName = "blank";
  
  		pq_puttextmessage('P', portalName);
  	}
  
  	/*
! 	 * If this is a retrieve, and we are supposed to emit row
! 	 * descriptions, then we send back the tuple descriptor of the tuples.
  	 */
! 	if (operation == CMD_SELECT && myState->sendDescrip)
! 		SendRowDescriptionMessage(typeinfo,
! 								  FetchPortalTargetList(portal),
! 								  portal->formats);
  
  	/* ----------------
  	 * We could set up the derived attr info at this time, but we postpone it
  	 * until the first call of printtup, for 2 reasons:
  	 * 1. We don't waste time (compared to the old way) if there are no
--- 134,193 ----
  			portalName = "blank";
  
  		pq_puttextmessage('P', portalName);
  	}
  
+ 	myState->isret = (ret != NIL);
+ 	myState->retTupDesc = NULL;
+ 	if(operation != CMD_SELECT)
+ 	{
+ 		if(myState->isret)
+ 		{
+ 			/* form tuple descriptor for return type */
+ 			/* XXX: we're saying no OIDs here, but do we mean it? */
+ 			myState->retTupDesc = ExecTypeFromTL(ret, false);
+ 			/* Only send the row back if a select would have */
+ 			myState->sendDescrip = (true && myState->sendDescrip);
+ 		}
+ 		else
+ 			myState->sendDescrip = false;
+ 	}
+ 
+ 	if (myState->isret)
+ 	{
+ 		ListCell    *r;
+ 		myState->ret = palloc0(typeinfo->natts * sizeof(char));
+ 
+ 		foreach(r, ret)
+ 		{
+ 			TargetEntry *te = lfirst(r);
+ 			myState->ret[te->resorigcol - 1] = 'y';
+ 		}
+ 	}
+ 
  	/*
! 	 * If this is a SELECT or an INSERT/DELETE/UPDATE which returns rows
! 	 * and we are supposed to emit row descriptions, then we send back the
! 	 * tuple descriptor of the tuples.
  	 */
! 	if (myState->sendDescrip)
! 	{
! 		if (operation == CMD_SELECT)
! 		{
! 			SendRowDescriptionMessage(typeinfo,
! 						  FetchPortalTargetList(portal),
! 						  portal->formats);
! 		}
! 		else if (myState->isret)
! 		{
! 			/* XXX: must be a bit smarter about portal->formats :-( */
! 			SendRowDescriptionMessage(myState->retTupDesc,
! 						  ret,
! 						  portal->formats);
! 		}
! 	}
! 									
  
  	/* ----------------
  	 * We could set up the derived attr info at this time, but we postpone it
  	 * until the first call of printtup, for 2 reasons:
  	 * 1. We don't waste time (compared to the old way) if there are no
***************
*** 283,294 ****
--- 335,355 ----
  	TupleDesc typeinfo = slot->tts_tupleDescriptor;
  	DR_printtup *myState = (DR_printtup *) self;
  	StringInfoData buf;
  	int			natts = typeinfo->natts;
  	int			i;
+ 	char	   *ret = myState->ret;
+ 
+ 	/* Short circuit if we're not meant to be returning anything */
+ 
+ 	if(!myState->sendDescrip)
+ 		return;
  
  	/* Set or update my derived attribute info, if needed */
+ 
+ 	/* Handle returning case */
+ 
  	if (myState->attrinfo != typeinfo || myState->nattrs != natts)
  		printtup_prepare_info(myState, typeinfo, natts);
  
  	/* Make sure the tuple is fully deconstructed */
  	slot_getallattrs(slot);
***************
*** 296,316 ****
  	/*
  	 * Prepare a DataRow message
  	 */
  	pq_beginmessage(&buf, 'D');
  
! 	pq_sendint(&buf, natts, 2);
  
  	/*
  	 * send the attributes of this tuple
  	 */
  	for (i = 0; i < natts; ++i)
  	{
  		PrinttupAttrInfo *thisState = myState->myinfo + i;
  		Datum		origattr = slot->tts_values[i],
  					attr;
  
  		if (slot->tts_isnull[i])
  		{
  			pq_sendint(&buf, -1, 4);
  			continue;
  		}
--- 357,383 ----
  	/*
  	 * Prepare a DataRow message
  	 */
  	pq_beginmessage(&buf, 'D');
  
! 	if(myState->isret)
! 		pq_sendint(&buf, myState->retTupDesc->natts, 2);
! 	else
! 		pq_sendint(&buf, natts, 2);
  
  	/*
  	 * send the attributes of this tuple
  	 */
  	for (i = 0; i < natts; ++i)
  	{
  		PrinttupAttrInfo *thisState = myState->myinfo + i;
  		Datum		origattr = slot->tts_values[i],
  					attr;
  
+ 		if(myState->isret && ret[i] != 'y')
+ 			continue;
+ 
  		if (slot->tts_isnull[i])
  		{
  			pq_sendint(&buf, -1, 4);
  			continue;
  		}
***************
*** 450,459 ****
--- 517,534 ----
  	DR_printtup *myState = (DR_printtup *) self;
  
  	if (myState->myinfo)
  		pfree(myState->myinfo);
  	myState->myinfo = NULL;
+ 	if (myState->values)
+ 		pfree(myState->values);
+ 	myState->values = NULL;
+ 	if (myState->nulls)
+ 		pfree(myState->nulls);
+ 	myState->nulls = NULL;
+ 	if (myState->ret)
+ 		pfree(myState->ret);
  
  	myState->attrinfo = NULL;
  }
  
  /* ----------------
Index: src/backend/executor/execMain.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/executor/execMain.c,v
retrieving revision 1.251
diff -C5 -r1.251 execMain.c
*** src/backend/executor/execMain.c	28 Jun 2005 05:08:55 -0000	1.251
--- src/backend/executor/execMain.c	5 Jul 2005 03:36:00 -0000
***************
*** 81,92 ****
  			ScanDirection direction,
  			DestReceiver *dest);
  static void ExecSelect(TupleTableSlot *slot,
  		   DestReceiver *dest,
  		   EState *estate);
! static void ExecInsert(TupleTableSlot *slot, ItemPointer tupleid,
! 		   EState *estate);
  static void ExecDelete(TupleTableSlot *slot, ItemPointer tupleid,
  		   EState *estate);
  static void ExecUpdate(TupleTableSlot *slot, ItemPointer tupleid,
  		   EState *estate);
  static TupleTableSlot *EvalPlanQualNext(EState *estate);
--- 81,92 ----
  			ScanDirection direction,
  			DestReceiver *dest);
  static void ExecSelect(TupleTableSlot *slot,
  		   DestReceiver *dest,
  		   EState *estate);
! static void ExecInsert(TupleTableSlot *slot, DestReceiver *dest, 
!            ItemPointer tupleid, EState *estate);
  static void ExecDelete(TupleTableSlot *slot, ItemPointer tupleid,
  		   EState *estate);
  static void ExecUpdate(TupleTableSlot *slot, ItemPointer tupleid,
  		   EState *estate);
  static TupleTableSlot *EvalPlanQualNext(EState *estate);
***************
*** 803,812 ****
--- 803,813 ----
  	}
  
  	estate->es_into_relation_descriptor = intoRelationDesc;
  
  	queryDesc->tupDesc = tupType;
+ 	
  	queryDesc->planstate = planstate;
  }
  
  /*
   * Initialize ResultRelInfo data for one result relation
***************
*** 1253,1263 ****
  						   estate);
  				result = slot;
  				break;
  
  			case CMD_INSERT:
! 				ExecInsert(slot, tupleid, estate);
  				result = NULL;
  				break;
  
  			case CMD_DELETE:
  				ExecDelete(slot, tupleid, estate);
--- 1254,1264 ----
  						   estate);
  				result = slot;
  				break;
  
  			case CMD_INSERT:
! 				ExecInsert(slot, dest, tupleid, estate);
  				result = NULL;
  				break;
  
  			case CMD_DELETE:
  				ExecDelete(slot, tupleid, estate);
***************
*** 1362,1371 ****
--- 1363,1373 ----
   *		index relations.
   * ----------------------------------------------------------------
   */
  static void
  ExecInsert(TupleTableSlot *slot,
+ 		   DestReceiver *dest,
  		   ItemPointer tupleid,
  		   EState *estate)
  {
  	HeapTuple	tuple;
  	ResultRelInfo *resultRelInfo;
***************
*** 1420,1429 ****
--- 1422,1436 ----
  	 */
  	newId = heap_insert(resultRelationDesc, tuple,
  						estate->es_snapshot->curcid,
  						true, true);
  
+ 	/*
+ 	 * send the tuple to the destination
+ 	 */
+ 	(*dest->receiveSlot) (slot, dest);
+ 
  	IncrAppended();
  	(estate->es_processed)++;
  	estate->es_lastoid = newId;
  	setLastTid(&(tuple->t_self));
  
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.311
diff -C5 -r1.311 copyfuncs.c
*** src/backend/nodes/copyfuncs.c	2 Jul 2005 23:00:39 -0000	1.311
--- src/backend/nodes/copyfuncs.c	5 Jul 2005 03:36:00 -0000
***************
*** 1606,1615 ****
--- 1606,1616 ----
  	COPY_NODE_FIELD(rtable);
  	COPY_NODE_FIELD(jointree);
  	COPY_NODE_FIELD(rowMarks);
  	COPY_SCALAR_FIELD(forUpdate);
  	COPY_NODE_FIELD(targetList);
+ 	COPY_NODE_FIELD(returning);
  	COPY_NODE_FIELD(groupClause);
  	COPY_NODE_FIELD(havingQual);
  	COPY_NODE_FIELD(distinctClause);
  	COPY_NODE_FIELD(sortClause);
  	COPY_NODE_FIELD(limitOffset);
***************
*** 1627,1636 ****
--- 1628,1638 ----
  
  	COPY_NODE_FIELD(relation);
  	COPY_NODE_FIELD(cols);
  	COPY_NODE_FIELD(targetList);
  	COPY_NODE_FIELD(selectStmt);
+ 	COPY_NODE_FIELD(returning);
  
  	return newnode;
  }
  
  static DeleteStmt *
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.248
diff -C5 -r1.248 equalfuncs.c
*** src/backend/nodes/equalfuncs.c	2 Jul 2005 23:00:39 -0000	1.248
--- src/backend/nodes/equalfuncs.c	5 Jul 2005 03:36:00 -0000
***************
*** 643,652 ****
--- 643,653 ----
  	COMPARE_NODE_FIELD(rtable);
  	COMPARE_NODE_FIELD(jointree);
  	COMPARE_NODE_FIELD(rowMarks);
  	COMPARE_SCALAR_FIELD(forUpdate);
  	COMPARE_NODE_FIELD(targetList);
+ 	COMPARE_NODE_FIELD(returning);
  	COMPARE_NODE_FIELD(groupClause);
  	COMPARE_NODE_FIELD(havingQual);
  	COMPARE_NODE_FIELD(distinctClause);
  	COMPARE_NODE_FIELD(sortClause);
  	COMPARE_NODE_FIELD(limitOffset);
***************
*** 662,672 ****
  {
  	COMPARE_NODE_FIELD(relation);
  	COMPARE_NODE_FIELD(cols);
  	COMPARE_NODE_FIELD(targetList);
  	COMPARE_NODE_FIELD(selectStmt);
! 
  	return true;
  }
  
  static bool
  _equalDeleteStmt(DeleteStmt *a, DeleteStmt *b)
--- 663,673 ----
  {
  	COMPARE_NODE_FIELD(relation);
  	COMPARE_NODE_FIELD(cols);
  	COMPARE_NODE_FIELD(targetList);
  	COMPARE_NODE_FIELD(selectStmt);
! 	COMPARE_NODE_FIELD(returning);
  	return true;
  }
  
  static bool
  _equalDeleteStmt(DeleteStmt *a, DeleteStmt *b)
Index: src/backend/parser/analyze.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/parser/analyze.c,v
retrieving revision 1.322
diff -C5 -r1.322 analyze.c
*** src/backend/parser/analyze.c	5 Jun 2005 00:38:09 -0000	1.322
--- src/backend/parser/analyze.c	5 Jul 2005 03:36:00 -0000
***************
*** 101,110 ****
--- 101,111 ----
  static Query *transformViewStmt(ParseState *pstate, ViewStmt *stmt,
  				  List **extras_before, List **extras_after);
  static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
  static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt,
  					List **extras_before, List **extras_after);
+ static List * transformReturningList(ParseState *pstate, List *targetlist);
  static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt);
  static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt,
  				  List **extras_before, List **extras_after);
  static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
  static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt);
***************
*** 661,670 ****
--- 662,677 ----
  		/*
  		 * For INSERT ... VALUES, transform the given list of values to
  		 * form a targetlist for the INSERT.
  		 */
  		qry->targetList = transformTargetList(pstate, stmt->targetList);
+ 
+ 		/*
+ 		 * If we are returning any columns, we better transform them
+ 		 */
+ 
+ 		qry->returning = transformReturningList(pstate, stmt->returning);
  	}
  
  	/*
  	 * Now we are done with SELECT-like processing, and can get on with
  	 * transforming the target list to match the INSERT target columns.
***************
*** 724,733 ****
--- 731,806 ----
  
  	return qry;
  }
  
  /*
+  * transformReturningList()
+  * Turns a list of Strings into a list of TargetEntry's.
+  * XXX: Naturally, we aren't really talking about TargetEntry's here but
+  * the code fulfils our purpose.
+  *
+  */
+ static List *
+ transformReturningList(ParseState *pstate, List *returning)
+ {
+ 	List	   *p_target = NIL;
+ 	ListCell   *ret;
+ 	int	    i = 1;
+ 
+ 	foreach(ret, returning)
+ 	{
+ 		ResTarget		*res = lfirst(ret);
+ 		TargetEntry		*te;
+ 		Expr			*expr;
+ 		bool			 found = false;
+ 		int			 j;
+ 		Relation		 rel = pstate->p_target_relation;
+ 		Oid			 type_id = -1;
+ 		int32			 type_mod = -1;
+ 		int2			 attnum = -1;
+ 
+ 		for(j = 0; j < rel->rd_rel->relnatts; j++)
+ 		{
+ 			Form_pg_attribute att = rel->rd_att->attrs[j];
+ 
+ 			if(namestrcmp(&(att->attname), res->name) == 0 &&
+ 				!att->attisdropped)
+ 			{
+ 				found = true;
+ 				type_id = att->atttypid;
+ 				type_mod = att->atttypmod;
+ 				attnum = att->attnum;
+ 				break;
+ 			}
+ 		}
+ 
+ 		if(!found)
+ 			ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_COLUMN),
+ 				 errmsg("column \"%s\" of relation \"%s\" does not exist",
+ 					res->name, RelationGetRelationName(rel))));
+ 
+ 		expr = (Expr *) makeVar((AttrNumber) i,
+ 					0,
+ 					type_id,
+ 					type_mod,
+ 					0);
+ 
+ 		
+ 		te = makeTargetEntry(expr,
+ 				     (AttrNumber) i,
+ 				     res->name,
+ 				     false);
+ 		te->resorigcol = attnum;
+ 
+ 		p_target = lappend(p_target, te);
+ 		i++;
+ 	}
+ 	return p_target;
+ }
+ 
+ /*
   * transformCreateStmt -
   *	  transforms the "create table" statement
   *	  SQL92 allows constraints to be scattered all over, so thumb through
   *	   the columns and collect all constraints into one place.
   *	  If there are any implied indices (e.g. UNIQUE or PRIMARY KEY)
Index: src/backend/parser/gram.y
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.501
diff -C5 -r2.501 gram.y
*** src/backend/parser/gram.y	29 Jun 2005 20:34:13 -0000	2.501
--- src/backend/parser/gram.y	5 Jul 2005 03:36:00 -0000
***************
*** 240,249 ****
--- 240,250 ----
  
  %type <list>	for_locking_clause opt_for_locking_clause
  				update_list
  %type <boolean>	opt_all
  
+ %type <list>	opt_returning_list
  %type <node>	join_outer join_qual
  %type <jtype>	join_type
  
  %type <list>	extract_list overlay_list position_list
  %type <list>	substr_list trim_list
***************
*** 387,397 ****
  	PRIOR PRIVILEGES PROCEDURAL PROCEDURE
  
  	QUOTE
  
  	READ REAL RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME
! 	REPEATABLE REPLACE RESET RESTART RESTRICT RETURNS REVOKE RIGHT
  	ROLE ROLLBACK ROW ROWS RULE
  
  	SAVEPOINT SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE
  	SERIALIZABLE SESSION SESSION_USER SET SETOF SHARE
  	SHOW SIMILAR SIMPLE SMALLINT SOME STABLE START STATEMENT
--- 388,398 ----
  	PRIOR PRIVILEGES PROCEDURAL PROCEDURE
  
  	QUOTE
  
  	READ REAL RECHECK REFERENCES REINDEX RELATIVE_P RELEASE RENAME
! 	REPEATABLE REPLACE RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT
  	ROLE ROLLBACK ROW ROWS RULE
  
  	SAVEPOINT SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE
  	SERIALIZABLE SESSION SESSION_USER SET SETOF SHARE
  	SHOW SIMILAR SIMPLE SMALLINT SOME STABLE START STATEMENT
***************
*** 4870,4913 ****
  					$$ = (Node *) $4;
  				}
  		;
  
  insert_rest:
! 			VALUES '(' insert_target_list ')'
  				{
  					$$ = makeNode(InsertStmt);
  					$$->cols = NIL;
  					$$->targetList = $3;
  					$$->selectStmt = NULL;
  				}
! 			| DEFAULT VALUES
  				{
  					$$ = makeNode(InsertStmt);
  					$$->cols = NIL;
  					$$->targetList = NIL;
  					$$->selectStmt = NULL;
  				}
  			| SelectStmt
  				{
  					$$ = makeNode(InsertStmt);
  					$$->cols = NIL;
  					$$->targetList = NIL;
  					$$->selectStmt = $1;
  				}
! 			| '(' insert_column_list ')' VALUES '(' insert_target_list ')'
  				{
  					$$ = makeNode(InsertStmt);
  					$$->cols = $2;
  					$$->targetList = $6;
  					$$->selectStmt = NULL;
  				}
  			| '(' insert_column_list ')' SelectStmt
  				{
  					$$ = makeNode(InsertStmt);
  					$$->cols = $2;
  					$$->targetList = NIL;
  					$$->selectStmt = $4;
  				}
  		;
  
  insert_column_list:
  			insert_column_item
--- 4871,4919 ----
  					$$ = (Node *) $4;
  				}
  		;
  
  insert_rest:
! 			VALUES '(' insert_target_list ')' opt_returning_list
  				{
  					$$ = makeNode(InsertStmt);
  					$$->cols = NIL;
  					$$->targetList = $3;
  					$$->selectStmt = NULL;
+ 					$$->returning = $5;
  				}
! 			| DEFAULT VALUES opt_returning_list
  				{
  					$$ = makeNode(InsertStmt);
  					$$->cols = NIL;
  					$$->targetList = NIL;
  					$$->selectStmt = NULL;
+ 					$$->returning = $3;
  				}
  			| SelectStmt
  				{
  					$$ = makeNode(InsertStmt);
  					$$->cols = NIL;
  					$$->targetList = NIL;
  					$$->selectStmt = $1;
+ 					$$->returning = NIL;
  				}
! 			| '(' insert_column_list ')' VALUES '(' insert_target_list ')' opt_returning_list
  				{
  					$$ = makeNode(InsertStmt);
  					$$->cols = $2;
  					$$->targetList = $6;
  					$$->selectStmt = NULL;
+ 					$$->returning = $8;
  				}
  			| '(' insert_column_list ')' SelectStmt
  				{
  					$$ = makeNode(InsertStmt);
  					$$->cols = $2;
  					$$->targetList = NIL;
  					$$->selectStmt = $4;
+ 					$$->returning = NIL;
  				}
  		;
  
  insert_column_list:
  			insert_column_item
***************
*** 4924,4933 ****
--- 4930,4941 ----
  					$$->indirection = $2;
  					$$->val = NULL;
  				}
  		;
  
+ opt_returning_list:	RETURNING insert_column_list	{ $$ = $2; }
+ 				| /*EMPTY*/	{ $$ = NIL; }
  
  /*****************************************************************************
   *
   *		QUERY:
   *				DELETE STATEMENTS
***************
*** 8051,8060 ****
--- 8059,8069 ----
  			| REPEATABLE
  			| REPLACE
  			| RESET
  			| RESTART
  			| RESTRICT
+ 			| RETURNING
  			| RETURNS
  			| REVOKE
  			| ROLE
  			| ROLLBACK
  			| ROWS
Index: src/backend/parser/keywords.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/parser/keywords.c,v
retrieving revision 1.162
diff -C5 -r1.162 keywords.c
*** src/backend/parser/keywords.c	29 Jun 2005 20:34:14 -0000	1.162
--- src/backend/parser/keywords.c	5 Jul 2005 03:36:00 -0000
***************
*** 272,281 ****
--- 272,282 ----
  	{"repeatable", REPEATABLE},
  	{"replace", REPLACE},
  	{"reset", RESET},
  	{"restart", RESTART},
  	{"restrict", RESTRICT},
+ 	{"returning", RETURNING},
  	{"returns", RETURNS},
  	{"revoke", REVOKE},
  	{"right", RIGHT},
  	{"role", ROLE},
  	{"rollback", ROLLBACK},
Index: src/backend/parser/parse_relation.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/parser/parse_relation.c,v
retrieving revision 1.112
diff -C5 -r1.112 parse_relation.c
*** src/backend/parser/parse_relation.c	28 Jun 2005 05:08:58 -0000	1.112
--- src/backend/parser/parse_relation.c	5 Jul 2005 03:36:00 -0000
***************
*** 283,292 ****
--- 283,294 ----
   * NOTE: if the RTE is for a join, marking it as requiring read access does
   * nothing.  It might seem that we need to propagate the mark to all the
   * contained RTEs, but that is not necessary.  This is so because a join
   * expression can only appear in a FROM clause, and any table named in
   * FROM will be marked as requiring read access from the beginning.
+  *
+  * XXX: Use for RETURNING but be careful with SELECT_ACL check
   */
  Node *
  scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname)
  {
  	Node	   *result = NULL;
Index: src/include/executor/execdesc.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/executor/execdesc.h,v
retrieving revision 1.30
diff -C5 -r1.30 execdesc.h
*** src/include/executor/execdesc.h	31 Dec 2004 22:03:29 -0000	1.30
--- src/include/executor/execdesc.h	5 Jul 2005 03:36:00 -0000
***************
*** 41,50 ****
--- 41,51 ----
  
  	/* These fields are set by ExecutorStart */
  	TupleDesc	tupDesc;		/* descriptor for result tuples */
  	EState	   *estate;			/* executor's query-wide state */
  	PlanState  *planstate;		/* tree of per-plan-node state */
+ 	TupleDesc	retTupDesc;		/* descriptor of RETURNING clause */
  } QueryDesc;
  
  /* in pquery.c */
  extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree,
  				Snapshot snapshot,
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.285
diff -C5 -r1.285 parsenodes.h
*** src/include/nodes/parsenodes.h	28 Jun 2005 19:51:24 -0000	1.285
--- src/include/nodes/parsenodes.h	5 Jul 2005 03:36:00 -0000
***************
*** 96,105 ****
--- 96,107 ----
  	bool		forUpdate;		/* true if rowMarks are FOR UPDATE,
  								 * false if they are FOR SHARE */
  
  	List	   *targetList;		/* target list (of TargetEntry) */
  
+ 	List	   *returning;		/* list of columns to return */
+ 
  	List	   *groupClause;	/* a list of GroupClause's */
  
  	Node	   *havingQual;		/* qualifications applied to groups */
  
  	List	   *distinctClause; /* a list of SortClause's */
***************
*** 598,607 ****
--- 600,610 ----
  	 * VALUES, a targetList is supplied (empty for DEFAULT VALUES). If
  	 * SELECT, a complete SelectStmt (or set-operation tree) is supplied.
  	 */
  	List	   *targetList;		/* the target list (of ResTarget) */
  	Node	   *selectStmt;		/* the source SELECT */
+ 	List	   *returning;		/* List of columns to return */
  } InsertStmt;
  
  /* ----------------------
   *		Delete Statement
   * ----------------------
