Hello.

Here's a patch(WIP) that implements INSERT .. RETURNING inside a CTE. Should apply cleanly against CVS head.

The INSERT query isn't rewritten so rules and default values don't work. Recursive CTEs don't work either.

Regards,
Marko Tiikkaja
*** a/src/backend/commands/explain.c
--- b/src/backend/commands/explain.c
***************
*** 651,656 **** explain_outNode(StringInfo str,
--- 651,659 ----
  		case T_Hash:
  			pname = "Hash";
  			break;
+ 		case T_InsertReturning:
+ 			pname = "INSERT RETURNING";
+ 			break;
  		default:
  			pname = "???";
  			break;
*** a/src/backend/executor/Makefile
--- b/src/backend/executor/Makefile
***************
*** 15,21 **** include $(top_builddir)/src/Makefile.global
  OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \
         execProcnode.o execQual.o execScan.o execTuples.o \
         execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
!        nodeBitmapAnd.o nodeBitmapOr.o \
         nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \
         nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
         nodeNestloop.o nodeFunctionscan.o nodeRecursiveunion.o nodeResult.o \
--- 15,21 ----
  OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \
         execProcnode.o execQual.o execScan.o execTuples.o \
         execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
!        nodeBitmapAnd.o nodeBitmapOr.o nodeInsertReturning.o \
         nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \
         nodeHashjoin.o nodeIndexscan.o nodeMaterial.o nodeMergejoin.o \
         nodeNestloop.o nodeFunctionscan.o nodeRecursiveunion.o nodeResult.o \
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 86,94 **** static void ExecutePlan(EState *estate, PlanState *planstate,
  			DestReceiver *dest);
  static void ExecSelect(TupleTableSlot *slot,
  		   DestReceiver *dest, EState *estate);
! static void ExecInsert(TupleTableSlot *slot, ItemPointer tupleid,
  		   TupleTableSlot *planSlot,
! 		   DestReceiver *dest, EState *estate);
  static void ExecDelete(ItemPointer tupleid,
  		   TupleTableSlot *planSlot,
  		   DestReceiver *dest, EState *estate);
--- 86,94 ----
  			DestReceiver *dest);
  static void ExecSelect(TupleTableSlot *slot,
  		   DestReceiver *dest, EState *estate);
! void ExecInsert(TupleTableSlot *slot, ItemPointer tupleid,
  		   TupleTableSlot *planSlot,
! 		   DestReceiver *dest, EState *estate, ResultRelInfo* resultRelInfo, bool clearReturningTuple);
  static void ExecDelete(ItemPointer tupleid,
  		   TupleTableSlot *planSlot,
  		   DestReceiver *dest, EState *estate);
***************
*** 98,104 **** static void ExecUpdate(TupleTableSlot *slot, ItemPointer tupleid,
  static void ExecProcessReturning(ProjectionInfo *projectReturning,
  					 TupleTableSlot *tupleSlot,
  					 TupleTableSlot *planSlot,
! 					 DestReceiver *dest);
  static TupleTableSlot *EvalPlanQualNext(EState *estate);
  static void EndEvalPlanQual(EState *estate);
  static void ExecCheckRTPerms(List *rangeTable);
--- 98,105 ----
  static void ExecProcessReturning(ProjectionInfo *projectReturning,
  					 TupleTableSlot *tupleSlot,
  					 TupleTableSlot *planSlot,
! 					 DestReceiver *dest,
! 					 bool clearTuple);
  static TupleTableSlot *EvalPlanQualNext(EState *estate);
  static void EndEvalPlanQual(EState *estate);
  static void ExecCheckRTPerms(List *rangeTable);
***************
*** 190,196 **** standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
  		case CMD_SELECT:
  			/* SELECT INTO and SELECT FOR UPDATE/SHARE need to mark tuples */
  			if (queryDesc->plannedstmt->intoClause != NULL ||
! 				queryDesc->plannedstmt->rowMarks != NIL)
  				estate->es_output_cid = GetCurrentCommandId(true);
  			break;
  
--- 191,198 ----
  		case CMD_SELECT:
  			/* SELECT INTO and SELECT FOR UPDATE/SHARE need to mark tuples */
  			if (queryDesc->plannedstmt->intoClause != NULL ||
! 				queryDesc->plannedstmt->rowMarks != NIL ||
! 				queryDesc->plannedstmt->hasWritableCtes)
  				estate->es_output_cid = GetCurrentCommandId(true);
  			break;
  
***************
*** 1670,1676 **** lnext:	;
  				break;
  
  			case CMD_INSERT:
! 				ExecInsert(slot, tupleid, planSlot, dest, estate);
  				break;
  
  			case CMD_DELETE:
--- 1672,1678 ----
  				break;
  
  			case CMD_INSERT:
! 				ExecInsert(slot, tupleid, planSlot, dest, estate, estate->es_result_relation_info, true);
  				break;
  
  			case CMD_DELETE:
***************
*** 1742,1756 **** ExecSelect(TupleTableSlot *slot,
   *		index relations.
   * ----------------------------------------------------------------
   */
! static void
  ExecInsert(TupleTableSlot *slot,
  		   ItemPointer tupleid,
  		   TupleTableSlot *planSlot,
  		   DestReceiver *dest,
! 		   EState *estate)
  {
  	HeapTuple	tuple;
- 	ResultRelInfo *resultRelInfo;
  	Relation	resultRelationDesc;
  	Oid			newId;
  
--- 1744,1759 ----
   *		index relations.
   * ----------------------------------------------------------------
   */
! void
  ExecInsert(TupleTableSlot *slot,
  		   ItemPointer tupleid,
  		   TupleTableSlot *planSlot,
  		   DestReceiver *dest,
! 		   EState *estate,
! 		   ResultRelInfo* resultRelInfo,
! 		   bool clearReturningTuple)
  {
  	HeapTuple	tuple;
  	Relation	resultRelationDesc;
  	Oid			newId;
  
***************
*** 1763,1769 **** ExecInsert(TupleTableSlot *slot,
  	/*
  	 * get information on the (current) result relation
  	 */
- 	resultRelInfo = estate->es_result_relation_info;
  	resultRelationDesc = resultRelInfo->ri_RelationDesc;
  
  	/*
--- 1766,1771 ----
***************
*** 1842,1848 **** ExecInsert(TupleTableSlot *slot,
  	/* Process RETURNING if present */
  	if (resultRelInfo->ri_projectReturning)
  		ExecProcessReturning(resultRelInfo->ri_projectReturning,
! 							 slot, planSlot, dest);
  }
  
  /* ----------------------------------------------------------------
--- 1844,1850 ----
  	/* Process RETURNING if present */
  	if (resultRelInfo->ri_projectReturning)
  		ExecProcessReturning(resultRelInfo->ri_projectReturning,
! 							 slot, planSlot, dest, clearReturningTuple);
  }
  
  /* ----------------------------------------------------------------
***************
*** 1968,1974 **** ldelete:;
  		ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
  
  		ExecProcessReturning(resultRelInfo->ri_projectReturning,
! 							 slot, planSlot, dest);
  
  		ExecClearTuple(slot);
  		ReleaseBuffer(delbuffer);
--- 1970,1976 ----
  		ExecStoreTuple(&deltuple, slot, InvalidBuffer, false);
  
  		ExecProcessReturning(resultRelInfo->ri_projectReturning,
! 							 slot, planSlot, dest, true);
  
  		ExecClearTuple(slot);
  		ReleaseBuffer(delbuffer);
***************
*** 2140,2146 **** lreplace:;
  	/* Process RETURNING if present */
  	if (resultRelInfo->ri_projectReturning)
  		ExecProcessReturning(resultRelInfo->ri_projectReturning,
! 							 slot, planSlot, dest);
  }
  
  /*
--- 2142,2148 ----
  	/* Process RETURNING if present */
  	if (resultRelInfo->ri_projectReturning)
  		ExecProcessReturning(resultRelInfo->ri_projectReturning,
! 							 slot, planSlot, dest, true);
  }
  
  /*
***************
*** 2254,2260 **** static void
  ExecProcessReturning(ProjectionInfo *projectReturning,
  					 TupleTableSlot *tupleSlot,
  					 TupleTableSlot *planSlot,
! 					 DestReceiver *dest)
  {
  	ExprContext *econtext = projectReturning->pi_exprContext;
  	TupleTableSlot *retSlot;
--- 2256,2262 ----
  ExecProcessReturning(ProjectionInfo *projectReturning,
  					 TupleTableSlot *tupleSlot,
  					 TupleTableSlot *planSlot,
! 					 DestReceiver *dest, bool clearTuple)
  {
  	ExprContext *econtext = projectReturning->pi_exprContext;
  	TupleTableSlot *retSlot;
***************
*** 2275,2281 **** ExecProcessReturning(ProjectionInfo *projectReturning,
  	/* Send to dest */
  	(*dest->receiveSlot) (retSlot, dest);
  
! 	ExecClearTuple(retSlot);
  }
  
  /*
--- 2277,2284 ----
  	/* Send to dest */
  	(*dest->receiveSlot) (retSlot, dest);
  
! 	if (clearTuple)
! 		ExecClearTuple(retSlot);
  }
  
  /*
*** a/src/backend/executor/execProcnode.c
--- b/src/backend/executor/execProcnode.c
***************
*** 286,291 **** ExecInitNode(Plan *node, EState *estate, int eflags)
--- 286,296 ----
  												 estate, eflags);
  			break;
  
+ 		case T_InsertReturning:
+ 			result = (PlanState *) ExecInitInsertReturning((Limit *) node,
+ 												 estate, eflags);
+ 			break;
+ 
  		default:
  			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
  			result = NULL;		/* keep compiler quiet */
***************
*** 451,456 **** ExecProcNode(PlanState *node)
--- 456,465 ----
  			result = ExecLimit((LimitState *) node);
  			break;
  
+ 		case T_InsertReturningState:
+ 			result = ExecInsertReturning((InsertReturningState *) node);
+ 			break;
+ 
  		default:
  			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
  			result = NULL;
***************
*** 627,632 **** ExecCountSlotsNode(Plan *node)
--- 636,644 ----
  		case T_Limit:
  			return ExecCountSlotsLimit((Limit *) node);
  
+ 		case T_InsertReturning:
+ 			return ExecCountSlotsInsertReturning((InsertReturning *) node);
+ 
  		default:
  			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
  			break;
***************
*** 783,788 **** ExecEndNode(PlanState *node)
--- 795,804 ----
  			ExecEndLimit((LimitState *) node);
  			break;
  
+ 		case T_InsertReturningState:
+ 			ExecEndInsertReturning((InsertReturningState *) node);
+ 			break;
+ 
  		default:
  			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
  			break;
*** /dev/null
--- b/src/backend/executor/nodeInsertReturning.c
***************
*** 0 ****
--- 1,120 ----
+ #include "postgres.h"
+ 
+ #include "executor/executor.h"
+ #include "executor/execdebug.h"
+ #include "executor/nodeInsertReturning.h"
+ #include "utils/memutils.h"
+ 
+ static struct DR_insertReturning
+ {
+ 	DestReceiver		dr;
+ 	TupleTableSlot	  **targetSlot;
+ } receiver;
+ 
+ static void
+ receive(TupleTableSlot *slot, DestReceiver *self)
+ {
+ 	struct DR_insertReturning *myState = (struct DR_insertReturning *) self;
+ 
+ 	*myState->targetSlot = slot;
+ }
+ 
+ TupleTableSlot *
+ ExecInsertReturning(InsertReturningState *node)
+ {
+ 	EState		*estate = node->ps.state;
+ 
+ 	TupleTableSlot *slot;
+ 
+ 	/* Get a tuple from the subplan */
+ 	slot = ExecProcNode(outerPlanState(node));
+ 
+ 	if (TupIsNull(slot))
+ 		return NULL;
+ 
+ 	ExecInsert(slot, NULL, slot, (DestReceiver *) &receiver, estate, node->resultRelInfo, false);
+ 
+ 	return node->ps.ps_ResultTupleSlot;
+ }
+ 
+ InsertReturningState *
+ ExecInitInsertReturning(InsertReturning *node, EState *estate, int eflags)
+ {
+ 	InsertReturningState *resstate;
+ 	ExprContext *exprContext;
+ 	ResultRelInfo *resultRelInfo;
+ 	Relation resultRelation;
+ 
+ 	/*
+ 	 * create state structure
+ 	 */
+ 	resstate = makeNode(InsertReturningState);
+ 	resstate->ps.plan = (Plan *) node;
+ 	resstate->ps.state = estate;
+ 	resstate->ps.targetlist = node->plan.targetlist;
+ 
+ 	outerPlanState(resstate) = ExecInitNode(outerPlan(node), estate, eflags);
+ 
+ 	/* 
+ 	 * Initialize result tuple slot and assign
+ 	 * type from the target list.
+ 	 */
+ 	ExecInitResultTupleSlot(estate, &resstate->ps);
+ 	ExecAssignResultTypeFromTL(&resstate->ps);
+ 
+ 	/*
+ 	 * Prepare the RETURNING expression tree for execution. This
+ 	 * has to be done after calling ExecAssignResultTypeFromTL().
+ 	 */
+ 	resstate->ps.targetlist = (List *)
+ 		ExecInitExpr((Expr *) node->plan.targetlist,
+ 				  	 (PlanState *) resstate);
+ 
+ 	/* Initialize result relation info */
+ 	resultRelInfo = (ResultRelInfo *) palloc0(sizeof(ResultRelInfo));
+ 	resultRelation = heap_open(node->resultRelationOid, RowExclusiveLock);
+ 	InitResultRelInfo(resultRelInfo, resultRelation, node->resultRelationIndex, CMD_INSERT, estate->es_instrument);
+ 
+ 	/* Initialize RETURNING projection */
+ 	exprContext = CreateExprContext(estate);
+ 	resultRelInfo->ri_projectReturning = ExecBuildProjectionInfo(resstate->ps.targetlist,
+ 								exprContext,
+ 								resstate->ps.ps_ResultTupleSlot,
+ 								NULL);
+ 
+ 	resstate->resultRelInfo = resultRelInfo;
+ 
+ 	/* Assign tuple receiver info */
+ 	receiver.dr.receiveSlot = receive;
+ 	receiver.targetSlot = &resstate->ps.ps_ResultTupleSlot;
+ 
+ 	return resstate;
+ }
+ 
+ int
+ ExecCountSlotsInsertReturning(InsertReturning *node)
+ {
+ 	return ExecCountSlotsNode(outerPlan(node)) + 2;
+ }
+ 
+ void
+ ExecEndInsertReturning(InsertReturningState *node)
+ {
+ 	heap_close(node->resultRelInfo->ri_RelationDesc, NoLock);
+ 	pfree(node->resultRelInfo);
+ 
+ 	/*
+ 	 * Free the exprcontext
+ 	 */
+ 	ExecFreeExprContext(&node->ps);
+ 
+ 	/*
+ 	 * clean out the tuple table
+ 	 */
+ 	ExecClearTuple(node->ps.ps_ResultTupleSlot);
+ 
+ 	/*
+ 	 * shut down subplans
+ 	 */
+ 	ExecEndNode(outerPlanState(node));
+ }
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 1391,1396 **** _copyXmlExpr(XmlExpr *from)
--- 1391,1407 ----
  
  	return newnode;
  }
+ 	
+ static InsertReturning *
+ _copyInsertReturning(InsertReturning *from)
+ {
+ 	InsertReturning    *newnode = makeNode(InsertReturning);
+ 
+ 	CopyPlanFields((Plan *) from, (Plan *) newnode);
+ 
+ 	return newnode;
+ }
+ 
  
  /*
   * _copyNullIfExpr (same as OpExpr)
***************
*** 4093,4098 **** copyObject(void *from)
--- 4104,4112 ----
  		case T_XmlSerialize:
  			retval = _copyXmlSerialize(from);
  			break;
+ 		case T_InsertReturning:
+ 			retval = _copyInsertReturning(from);
+ 			break;
  
  		default:
  			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from));
*** a/src/backend/nodes/nodeFuncs.c
--- b/src/backend/nodes/nodeFuncs.c
***************
*** 2354,2359 **** bool
--- 2354,2403 ----
  					return true;
  			}
  			break;
+ 		case T_InsertStmt:
+ 			{
+ 				InsertStmt *stmt = (InsertStmt *) node;
+ 
+ 				if (walker(stmt->relation, context))
+ 					return true;
+ 				if (walker(stmt->cols, context))
+ 					return true;
+ 				if (walker(stmt->selectStmt, context))
+ 					return true;
+ 				if (walker(stmt->returningList, context))
+ 					return true;
+ 			}
+ 			break;
+ 		case T_UpdateStmt:
+ 			{
+ 				UpdateStmt *stmt = (UpdateStmt *) node;
+ 
+ 				if (walker(stmt->relation, context))
+ 					return true;
+ 				if (walker(stmt->targetList, context))
+ 					return true;
+ 				if (walker(stmt->whereClause, context))
+ 					return true;
+ 				if (walker(stmt->fromClause, context))
+ 					return true;
+ 				if (walker(stmt->returningList, context))
+ 					return true;
+ 			}
+ 			break;
+ 		case T_DeleteStmt:
+ 			{
+ 				DeleteStmt *stmt = (DeleteStmt *) node;
+ 
+ 				if (walker(stmt->relation, context))
+ 					return true;
+ 				if (walker(stmt->usingClause, context))
+ 					return true;
+ 				if (walker(stmt->whereClause, context))
+ 					return true;
+ 				if (walker(stmt->returningList, context))
+ 					return true;
+ 			}
+ 			break;
  		case T_A_Expr:
  			{
  				A_Expr	   *expr = (A_Expr *) node;
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
***************
*** 155,160 **** standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
--- 155,161 ----
  	glob->finalrtable = NIL;
  	glob->relationOids = NIL;
  	glob->invalItems = NIL;
+ 	glob->hasWritableCtes = false;
  	glob->lastPHId = 0;
  	glob->transientPlan = false;
  
***************
*** 224,229 **** standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
--- 225,231 ----
  	result->resultRelations = root->resultRelations;
  	result->utilityStmt = parse->utilityStmt;
  	result->intoClause = parse->intoClause;
+ 	result->hasWritableCtes = glob->hasWritableCtes;
  	result->subplans = glob->subplans;
  	result->rewindPlanIDs = glob->rewindPlanIDs;
  	result->returningLists = root->returningLists;
*** a/src/backend/optimizer/plan/setrefs.c
--- b/src/backend/optimizer/plan/setrefs.c
***************
*** 375,380 **** set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
--- 375,393 ----
  			set_join_references(glob, (Join *) plan, rtoffset);
  			break;
  
+ 		case T_InsertReturning:
+ 			{
+ 				/*
+ 				 * grouping_planner() already called
+ 				 * set_returning_clause_references so the targetList's
+ 				 * references are already set.
+ 				 */
+ 				InsertReturning *splan = (InsertReturning *) plan;
+ 
+ 				splan->resultRelationIndex += rtoffset;
+ 			}
+ 			break;
+ 
  		case T_Hash:
  		case T_Material:
  		case T_Sort:
*** a/src/backend/optimizer/plan/subselect.c
--- b/src/backend/optimizer/plan/subselect.c
***************
*** 880,885 **** SS_process_ctes(PlannerInfo *root)
--- 880,886 ----
  		Bitmapset  *tmpset;
  		int			paramid;
  		Param	   *prm;
+ 		InsertReturning *returningNode;
  
  		/*
  		 * Ignore CTEs that are not actually referenced anywhere.
***************
*** 897,902 **** SS_process_ctes(PlannerInfo *root)
--- 898,904 ----
  		 */
  		subquery = (Query *) copyObject(cte->ctequery);
  
+ 
  		/*
  		 * Generate the plan for the CTE query.  Always plan for full
  		 * retrieval --- we don't have enough info to predict otherwise.
***************
*** 954,959 **** SS_process_ctes(PlannerInfo *root)
--- 956,985 ----
  		prm = generate_new_param(root, INTERNALOID, -1);
  		splan->setParam = list_make1_int(prm->paramid);
  
+ 		/* Handle INSERT .. RETURNING inside CTE */
+ 		if (subquery->commandType != CMD_SELECT)
+ 		{
+ 			Oid resultRelationOid;
+ 			Index resultRelationIndex;
+ 
+ 			Assert(subquery->commandType == CMD_INSERT);
+ 
+ 			Assert(subquery->resultRelation > 0);
+ 			Assert(list_length(subroot->returningLists) == 1);	
+ 			
+ 			returningNode = makeNode(InsertReturning);
+ 			returningNode->plan.lefttree = plan;
+ 
+ 			resultRelationOid = getrelid(subquery->resultRelation, subquery->rtable);
+ 			returningNode->resultRelationOid = resultRelationOid;
+ 			
+ 			returningNode->plan.targetlist = linitial(subroot->returningLists);
+ 
+ 			root->glob->hasWritableCtes = true;
+ 
+ 			plan = returningNode;
+ 		}
+ 
  		/*
  		 * Add the subplan and its rtable to the global lists.
  		 */
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 7026,7031 **** common_table_expr:  name opt_name_list AS select_with_parens
--- 7026,7058 ----
  				n->location = @1;
  				$$ = (Node *) n;
  			}
+         | name opt_name_list AS '(' InsertStmt ')'
+ 			{
+ 				CommonTableExpr *n = makeNode(CommonTableExpr);
+ 				n->ctename = $1;
+ 				n->aliascolnames = $2;
+ 				n->ctequery = $5;
+ 				n->location = @1;
+ 				$$ = (Node *) n;
+ 			}
+         | name opt_name_list AS '(' UpdateStmt ')'
+ 			{
+ 				CommonTableExpr *n = makeNode(CommonTableExpr);
+ 				n->ctename = $1;
+ 				n->aliascolnames = $2;
+ 				n->ctequery = $5;
+ 				n->location = @1;
+ 				$$ = (Node *) n;
+ 			}
+         | name opt_name_list AS '(' DeleteStmt ')'
+ 			{
+ 				CommonTableExpr *n = makeNode(CommonTableExpr);
+ 				n->ctename = $1;
+ 				n->aliascolnames = $2;
+ 				n->ctequery = $5;
+ 				n->location = @1;
+ 				$$ = (Node *) n;
+ 			}
  		;
  
  into_clause:
*** a/src/backend/parser/parse_cte.c
--- b/src/backend/parser/parse_cte.c
***************
*** 18,23 ****
--- 18,24 ----
  #include "nodes/nodeFuncs.h"
  #include "parser/analyze.h"
  #include "parser/parse_cte.h"
+ #include "nodes/plannodes.h"
  #include "utils/builtins.h"
  
  
***************
*** 246,268 **** transformWithClause(ParseState *pstate, WithClause *withClause)
  static void
  analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
  {
! 	Query	   *query;
  
  	/* Analysis not done already */
! 	Assert(IsA(cte->ctequery, SelectStmt));
  
  	query = parse_sub_analyze(cte->ctequery, pstate);
  	cte->ctequery = (Node *) query;
  
  	/*
  	 * Check that we got something reasonable.	Many of these conditions are
  	 * impossible given restrictions of the grammar, but check 'em anyway.
! 	 * (These are the same checks as in transformRangeSubselect.)
  	 */
  	if (!IsA(query, Query) ||
! 		query->commandType != CMD_SELECT ||
! 		query->utilityStmt != NULL)
! 		elog(ERROR, "unexpected non-SELECT command in subquery in WITH");
  	if (query->intoClause)
  		ereport(ERROR,
  				(errcode(ERRCODE_SYNTAX_ERROR),
--- 247,284 ----
  static void
  analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
  {
! 	Query		*query;
! 	List		*ctelist;
  
  	/* Analysis not done already */
! 	/* This needs to be one of SelectStmt, InsertStmt, UpdateStmt, DeleteStmt instead of:
! 	 * Assert(IsA(cte->ctequery, SelectStmt)); */
  
  	query = parse_sub_analyze(cte->ctequery, pstate);
  	cte->ctequery = (Node *) query;
  
+ 	if (query->commandType == CMD_SELECT)
+ 		ctelist = query->targetList;
+ 	else
+ 		ctelist = query->returningList;
+ 
  	/*
  	 * Check that we got something reasonable.	Many of these conditions are
  	 * impossible given restrictions of the grammar, but check 'em anyway.
! 	 * (In addition to the same checks as in transformRangeSubselect,
! 	 * this adds checks for (INSERT|UPDATE|DELETE)...RETURNING.)
  	 */
  	if (!IsA(query, Query) ||
! 		query->utilityStmt != NULL ||
! 		(query->commandType != CMD_SELECT &&
! 		((query->commandType == CMD_INSERT ||
! 		  query->commandType == CMD_UPDATE ||
! 		  query->commandType == CMD_DELETE) &&
! 		 query->returningList == NULL)))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_SYNTAX_ERROR),
! 				 errmsg("unexpected non-row-returning command in subquery in WITH"),
! 				 parser_errposition(pstate, 0)));
  	if (query->intoClause)
  		ereport(ERROR,
  				(errcode(ERRCODE_SYNTAX_ERROR),
***************
*** 273,279 **** analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
  	if (!cte->cterecursive)
  	{
  		/* Compute the output column names/types if not done yet */
! 		analyzeCTETargetList(pstate, cte, query->targetList);
  	}
  	else
  	{
--- 289,295 ----
  	if (!cte->cterecursive)
  	{
  		/* Compute the output column names/types if not done yet */
! 		analyzeCTETargetList(pstate, cte, ctelist);
  	}
  	else
  	{
***************
*** 291,297 **** analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
  		lctyp = list_head(cte->ctecoltypes);
  		lctypmod = list_head(cte->ctecoltypmods);
  		varattno = 0;
! 		foreach(lctlist, query->targetList)
  		{
  			TargetEntry *te = (TargetEntry *) lfirst(lctlist);
  			Node	   *texpr;
--- 307,313 ----
  		lctyp = list_head(cte->ctecoltypes);
  		lctypmod = list_head(cte->ctecoltypmods);
  		varattno = 0;
! 		foreach(lctlist, ctelist)
  		{
  			TargetEntry *te = (TargetEntry *) lfirst(lctlist);
  			Node	   *texpr;
*** a/src/backend/parser/parse_relation.c
--- b/src/backend/parser/parse_relation.c
***************
*** 1402,1409 **** addRangeTableEntryForCTE(ParseState *pstate,
  	rte->ctelevelsup = levelsup;
  
  	/* Self-reference if and only if CTE's parse analysis isn't completed */
! 	rte->self_reference = !IsA(cte->ctequery, Query);
! 	Assert(cte->cterecursive || !rte->self_reference);
  	/* Bump the CTE's refcount if this isn't a self-reference */
  	if (!rte->self_reference)
  		cte->cterefcount++;
--- 1402,1409 ----
  	rte->ctelevelsup = levelsup;
  
  	/* Self-reference if and only if CTE's parse analysis isn't completed */
! 	rte->self_reference = !IsA(cte->ctequery, Query) && !IsA(cte->ctequery, InsertReturning);
! 	Assert(cte->cterecursive || !rte->self_reference || IsA(cte->ctequery, InsertReturning));
  	/* Bump the CTE's refcount if this isn't a self-reference */
  	if (!rte->self_reference)
  		cte->cterefcount++;
*** a/src/backend/parser/parse_target.c
--- b/src/backend/parser/parse_target.c
***************
*** 310,319 **** markTargetListOrigin(ParseState *pstate, TargetEntry *tle,
  			{
  				CommonTableExpr *cte = GetCTEForRTE(pstate, rte, netlevelsup);
  				TargetEntry *ste;
  
  				/* should be analyzed by now */
  				Assert(IsA(cte->ctequery, Query));
! 				ste = get_tle_by_resno(((Query *) cte->ctequery)->targetList,
  									   attnum);
  				if (ste == NULL || ste->resjunk)
  					elog(ERROR, "subquery %s does not have attribute %d",
--- 310,321 ----
  			{
  				CommonTableExpr *cte = GetCTEForRTE(pstate, rte, netlevelsup);
  				TargetEntry *ste;
+ 				Query		*query;
  
  				/* should be analyzed by now */
  				Assert(IsA(cte->ctequery, Query));
! 				query = (Query *) cte->ctequery;
! 				ste = get_tle_by_resno((query->commandType == CMD_SELECT) ? query->targetList : query->returningList,
  									   attnum);
  				if (ste == NULL || ste->resjunk)
  					elog(ERROR, "subquery %s does not have attribute %d",
***************
*** 1233,1243 **** expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
  			{
  				CommonTableExpr *cte = GetCTEForRTE(pstate, rte, netlevelsup);
  				TargetEntry *ste;
  
  				/* should be analyzed by now */
  				Assert(IsA(cte->ctequery, Query));
! 				ste = get_tle_by_resno(((Query *) cte->ctequery)->targetList,
! 									   attnum);
  				if (ste == NULL || ste->resjunk)
  					elog(ERROR, "subquery %s does not have attribute %d",
  						 rte->eref->aliasname, attnum);
--- 1235,1252 ----
  			{
  				CommonTableExpr *cte = GetCTEForRTE(pstate, rte, netlevelsup);
  				TargetEntry *ste;
+ 				Query		*query;
+ 				List		*ctelist;
  
  				/* should be analyzed by now */
  				Assert(IsA(cte->ctequery, Query));
! 				query = (Query *) cte->ctequery;
! 				if (query->commandType == CMD_SELECT)
! 					ctelist = query->targetList;
! 				else
! 					ctelist = query->returningList;
! 
! 				ste = get_tle_by_resno(ctelist, attnum);
  				if (ste == NULL || ste->resjunk)
  					elog(ERROR, "subquery %s does not have attribute %d",
  						 rte->eref->aliasname, attnum);
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 3800,3808 **** get_name_for_var_field(Var *var, int fieldno,
  				}
  				if (lc != NULL)
  				{
! 					Query	   *ctequery = (Query *) cte->ctequery;
! 					TargetEntry *ste = get_tle_by_resno(ctequery->targetList,
! 														attnum);
  
  					if (ste == NULL || ste->resjunk)
  						elog(ERROR, "subquery %s does not have attribute %d",
--- 3800,3814 ----
  				}
  				if (lc != NULL)
  				{
! 					Query		*ctequery = (Query *) cte->ctequery;
! 					List		*ctelist;
! 
! 					if (ctequery->commandType == CMD_SELECT) 
! 						ctelist = ctequery->targetList;
! 					else
! 						ctelist = ctequery->returningList;
! 
! 					TargetEntry *ste = get_tle_by_resno(ctelist, attnum);
  
  					if (ste == NULL || ste->resjunk)
  						elog(ERROR, "subquery %s does not have attribute %d",
*** /dev/null
--- b/src/include/executor/nodeInsertReturning.h
***************
*** 0 ****
--- 1,11 ----
+ #ifndef NODEINSERTRETURNING_H
+ #define NODEINSERTRETURNING_H
+ 
+ #include "nodes/execnodes.h"
+ 
+ extern int	ExecCountSlotsInsertReturning(InsertReturning *node);
+ extern InsertReturningState *ExecInitInsertReturning(InsertReturning *node, EState *estate, int eflags);
+ extern TupleTableSlot *ExecInsertReturning(InsertReturningState *node);
+ extern void ExecEndInsertReturning(InsertReturningState *node);
+ 
+ #endif
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 978,983 **** typedef struct ResultState
--- 978,994 ----
  } ResultState;
  
  /* ----------------
+  *	 InsertReturningState information
+  * ----------------
+  */
+ typedef struct InsertReturningState
+ {
+ 	PlanState		ps;				/* its first field is NodeTag */
+ 	ResultRelInfo  *resultRelInfo;
+ } InsertReturningState;
+ 
+ 
+ /* ----------------
   *	 AppendState information
   *
   *		nplans			how many plans are in the list
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 71,76 **** typedef enum NodeTag
--- 71,77 ----
  	T_Hash,
  	T_SetOp,
  	T_Limit,
+ 	T_InsertReturning,
  	/* this one isn't a subclass of Plan: */
  	T_PlanInvalItem,
  
***************
*** 190,195 **** typedef enum NodeTag
--- 191,197 ----
  	T_NullTestState,
  	T_CoerceToDomainState,
  	T_DomainConstraintState,
+ 	T_InsertReturningState,
  
  	/*
  	 * TAGS FOR PLANNER NODES (relation.h)
*** a/src/include/nodes/plannodes.h
--- b/src/include/nodes/plannodes.h
***************
*** 53,58 **** typedef struct PlannedStmt
--- 53,60 ----
  
  	IntoClause *intoClause;		/* target for SELECT INTO / CREATE TABLE AS */
  
+ 	bool		hasWritableCtes; /* true if there's an (INSERT|UPDATE|DELETE) .. RETURNING inside a CTE */
+ 
  	List	   *subplans;		/* Plan trees for SubPlan expressions */
  
  	Bitmapset  *rewindPlanIDs;	/* indices of subplans that require REWIND */
***************
*** 164,169 **** typedef struct Result
--- 166,179 ----
  	Node	   *resconstantqual;
  } Result;
  
+ typedef struct InsertReturning
+ {
+ 	Plan	   plan;
+ 
+ 	Oid			resultRelationOid;
+ 	int			resultRelationIndex; /* rtable index of the result relation*/
+ } InsertReturning;
+ 
  /* ----------------
   *	 Append node -
   *		Generate the concatenation of the results of sub-plans.
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
***************
*** 76,81 **** typedef struct PlannerGlobal
--- 76,83 ----
  
  	List	   *invalItems;		/* other dependencies, as PlanInvalItems */
  
+ 	bool		hasWritableCtes; /* is there an (INSERT|UPDATE|DELETE) .. RETURNING inside a CTE? */
+ 
  	Index		lastPHId;		/* highest PlaceHolderVar ID assigned */
  
  	bool		transientPlan;	/* redo plan when TransactionXmin changes? */
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to