I wrote:
Attached is the latest version of this patch.

Here's that same patch in context diff format.  Sorry for the noise.


Regards,
Marko Tiikkaja
*** a/doc/src/sgml/queries.sgml
--- b/doc/src/sgml/queries.sgml
***************
*** 1499,1505 **** SELECT 3, 'three';
  <synopsis>
  SELECT <replaceable>select_list</replaceable> FROM 
<replaceable>table_expression</replaceable>
  </synopsis>
!    and can appear anywhere a <literal>SELECT</> can.  For example, you can
     use it as part of a <literal>UNION</>, or attach a
     <replaceable>sort_specification</replaceable> (<literal>ORDER BY</>,
     <literal>LIMIT</>, and/or <literal>OFFSET</>) to it.  <literal>VALUES</>
--- 1499,1505 ----
  <synopsis>
  SELECT <replaceable>select_list</replaceable> FROM 
<replaceable>table_expression</replaceable>
  </synopsis>
!    and can appear anywhere a <literal>SELECT</literal> can.  For example, you 
can
     use it as part of a <literal>UNION</>, or attach a
     <replaceable>sort_specification</replaceable> (<literal>ORDER BY</>,
     <literal>LIMIT</>, and/or <literal>OFFSET</>) to it.  <literal>VALUES</>
***************
*** 1529,1538 **** SELECT <replaceable>select_list</replaceable> FROM 
<replaceable>table_expression
    </indexterm>
  
    <para>
!    <literal>WITH</> provides a way to write subqueries for use in a larger
!    <literal>SELECT</> query.  The subqueries can be thought of as defining
!    temporary tables that exist just for this query.  One use of this feature
!    is to break down complicated queries into simpler parts.  An example is:
  
  <programlisting>
  WITH regional_sales AS (
--- 1529,1539 ----
    </indexterm>
  
    <para>
!    <literal>WITH</> provides a way to write subqueries for use in a
!    larger query.  The subqueries can be thought of as defining
!    temporary tables that exist just for this query.  One use of this
!    feature is to break down complicated queries into simpler parts.
!    An example is:
  
  <programlisting>
  WITH regional_sales AS (
***************
*** 1560,1565 **** GROUP BY region, product;
--- 1561,1590 ----
    </para>
  
    <para>
+   A <literal>WITH</literal> clause can also have an
+   <literal>INSERT</literal>, <literal>UPDATE</literal> or
+   <literal>DELETE</literal> (each optionally with a
+   <literal>RETURNING</literal> clause) statement in it.  The example below
+   moves rows from the main table, foo_log into a partition,
+   foo_log_200910.
+ 
+ <programlisting>
+ WITH rows AS (
+         DELETE FROM ONLY foo_log
+         WHERE
+            foo_date &gt;= '2009-10-01' AND
+            foo_date &lt;  '2009-11-01'
+            RETURNING *
+      ), t AS (
+            INSERT INTO foo_log_200910
+            SELECT * FROM rows
+      )
+ VALUES(true);
+ </programlisting>
+ 
+   </para>
+ 
+   <para>
     The optional <literal>RECURSIVE</> modifier changes <literal>WITH</>
     from a mere syntactic convenience into a feature that accomplishes
     things not otherwise possible in standard SQL.  Using
*** a/doc/src/sgml/ref/select.sgml
--- b/doc/src/sgml/ref/select.sgml
***************
*** 58,64 **** SELECT [ ALL | DISTINCT [ ON ( <replaceable 
class="parameter">expression</replac
  
  <phrase>and <replaceable class="parameter">with_query</replaceable> 
is:</phrase>
  
!     <replaceable class="parameter">with_query_name</replaceable> [ ( 
<replaceable class="parameter">column_name</replaceable> [, ...] ) ] AS ( 
<replaceable class="parameter">select</replaceable> )
  
  TABLE { [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * 
] | <replaceable class="parameter">with_query_name</replaceable> }
  </synopsis>
--- 58,64 ----
  
  <phrase>and <replaceable class="parameter">with_query</replaceable> 
is:</phrase>
  
!     <replaceable class="parameter">with_query_name</replaceable> [ ( 
<replaceable class="parameter">column_name</replaceable> [, ...] ) ] AS ( 
<replaceable class="parameter">select</replaceable> | (<replaceable 
class="parameter">insert</replaceable> | <replaceable 
class="parameter">update</replaceable> | <replaceable 
class="parameter">delete</replaceable> [ RETURNING...]))
  
  TABLE { [ ONLY ] <replaceable class="parameter">table_name</replaceable> [ * 
] | <replaceable class="parameter">with_query_name</replaceable> }
  </synopsis>
*** a/src/backend/commands/copy.c
--- b/src/backend/commands/copy.c
***************
*** 2160,2166 **** CopyFrom(CopyState cstate)
                        heap_insert(cstate->rel, tuple, mycid, hi_options, 
bistate);
  
                        if (resultRelInfo->ri_NumIndices > 0)
!                               recheckIndexes = ExecInsertIndexTuples(slot, 
&(tuple->t_self),
                                                                                
                           estate, false);
  
                        /* AFTER ROW INSERT Triggers */
--- 2160,2167 ----
                        heap_insert(cstate->rel, tuple, mycid, hi_options, 
bistate);
  
                        if (resultRelInfo->ri_NumIndices > 0)
!                               recheckIndexes = 
ExecInsertIndexTuples(resultRelInfo,
!                                                                               
                           slot, &(tuple->t_self),
                                                                                
                           estate, false);
  
                        /* AFTER ROW INSERT Triggers */
*** a/src/backend/commands/portalcmds.c
--- b/src/backend/commands/portalcmds.c
***************
*** 48,53 **** PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
--- 48,58 ----
        Portal          portal;
        MemoryContext oldContext;
  
+       if (stmt->hasWritableCtes)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("Non-SELECT cursors are not 
implemented")));
+ 
        if (cstmt == NULL || !IsA(cstmt, DeclareCursorStmt))
                elog(ERROR, "PerformCursorOpen called for non-cursor query");
  
*** a/src/backend/commands/vacuum.c
--- b/src/backend/commands/vacuum.c
***************
*** 3088,3094 **** move_chain_tuple(Relation rel,
        if (ec->resultRelInfo->ri_NumIndices > 0)
        {
                ExecStoreTuple(&newtup, ec->slot, InvalidBuffer, false);
!               ExecInsertIndexTuples(ec->slot, &(newtup.t_self), ec->estate, 
true);
                ResetPerTupleExprContext(ec->estate);
        }
  }
--- 3088,3094 ----
        if (ec->resultRelInfo->ri_NumIndices > 0)
        {
                ExecStoreTuple(&newtup, ec->slot, InvalidBuffer, false);
!               ExecInsertIndexTuples(ec->resultRelInfo, ec->slot, 
&(newtup.t_self), ec->estate, true);
                ResetPerTupleExprContext(ec->estate);
        }
  }
***************
*** 3214,3220 **** move_plain_tuple(Relation rel,
        if (ec->resultRelInfo->ri_NumIndices > 0)
        {
                ExecStoreTuple(&newtup, ec->slot, InvalidBuffer, false);
!               ExecInsertIndexTuples(ec->slot, &(newtup.t_self), ec->estate, 
true);
                ResetPerTupleExprContext(ec->estate);
        }
  }
--- 3214,3220 ----
        if (ec->resultRelInfo->ri_NumIndices > 0)
        {
                ExecStoreTuple(&newtup, ec->slot, InvalidBuffer, false);
!               ExecInsertIndexTuples(ec->resultRelInfo, ec->slot, 
&(newtup.t_self), ec->estate, true);
                ResetPerTupleExprContext(ec->estate);
        }
  }
*** a/src/backend/commands/view.c
--- b/src/backend/commands/view.c
***************
*** 394,399 **** DefineView(ViewStmt *stmt, const char *queryString)
--- 394,400 ----
        Query      *viewParse;
        Oid                     viewOid;
        RangeVar   *view;
+       ListCell   *lc;
  
        /*
         * Run parse analysis to convert the raw parse tree to a Query.  Note 
this
***************
*** 412,417 **** DefineView(ViewStmt *stmt, const char *queryString)
--- 413,430 ----
                viewParse->commandType != CMD_SELECT)
                elog(ERROR, "unexpected parse analysis result");
  
+       /* .. but it doesn't check for DML inside CTEs */
+       foreach(lc, viewParse->cteList)
+       {
+               CommonTableExpr         *cte;
+ 
+               cte = (CommonTableExpr *) lfirst(lc);
+               if (((Query *) cte->ctequery)->commandType != CMD_SELECT)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("INSERT/UPDATE/DELETE inside a 
CTE not allowed in a view definition")));
+       }
+ 
        /*
         * If a list of column names was given, run through and insert these 
into
         * the actual query tree. - thomas 2000-03-08
*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 924,939 **** InitResultRelInfo(ResultRelInfo *resultRelInfo,
        resultRelInfo->ri_ConstraintExprs = NULL;
        resultRelInfo->ri_junkFilter = NULL;
        resultRelInfo->ri_projectReturning = NULL;
- 
-       /*
-        * If there are indices on the result relation, open them and save
-        * descriptors in the result relation info, so that we can add new index
-        * entries for the tuples we add/update.  We need not do this for a
-        * DELETE, however, since deletion doesn't affect indexes.
-        */
-       if (resultRelationDesc->rd_rel->relhasindex &&
-               operation != CMD_DELETE)
-               ExecOpenIndices(resultRelInfo);
  }
  
  /*
--- 924,929 ----
***************
*** 1174,1179 **** ExecutePlan(EState *estate,
--- 1164,1251 ----
         */
        estate->es_direction = direction;
  
+       /* Process top-level CTEs in case they have writes inside */
+       if (estate->es_plannedstmt->hasWritableCtes)
+       {
+               ListCell *lc;
+ 
+               foreach(lc, estate->es_plannedstmt->planTree->initPlan)
+               {
+                       SubPlan *sp;
+                       int cte_param_id;
+                       ParamExecData* prmdata;
+                       CteScanState *leader;
+ 
+                       sp = (SubPlan *) lfirst(lc);
+                       if (sp->subLinkType != CTE_SUBLINK)
+                               continue;
+ 
+                       cte_param_id = linitial_int(sp->setParam);
+                       prmdata = &(estate->es_param_exec_vals[cte_param_id]);
+                       leader = (CteScanState *) 
DatumGetPointer(prmdata->value);
+ 
+ 
+                       /*
+                        * bump CID.
+                        *
+                        * We're currently relying on the fact that there can 
only be
+                        * a SELECT or VALUES as the top-level statement.
+                        *
+                        * XXX we should probably update the snapshot a bit 
differently
+                        */
+                       CommandCounterIncrement();
+                       estate->es_output_cid = GetCurrentCommandId(true);
+                       estate->es_snapshot->curcid = estate->es_output_cid;
+ 
+                       /*
+                        * If there's no leader, the CTE isn't referenced 
anywhere
+                        * so we can just go ahead and scan the plan
+                        */
+                       if (!leader)
+                       {
+                               TupleTableSlot *slot;
+                               PlanState *ps = (PlanState *) 
list_nth(estate->es_subplanstates,
+                                                                               
                           sp->plan_id - 1);
+ 
+                               Assert(IsA(ps, ModifyTableState));
+ 
+                               /*
+                                * We might have a RETURNING here, which means 
that
+                                * we have to loop until the plan returns NULL.
+                                */
+                               for (;;)
+                               {
+                                       slot = ExecProcNode(ps);
+                                       if (TupIsNull(slot))
+                                               break;
+                               }
+                       }
+                       else
+                       {
+                               TupleTableSlot* slot;
+                               PlanState *ps = (PlanState *) 
list_nth(estate->es_subplanstates,
+                                                                               
                           sp->plan_id - 1);
+ 
+                               /* Regular CTE, ignore */
+                               if (!IsA(ps, ModifyTableState))
+                                       continue;
+ 
+                               /*
+                                * Scan through the leader CTE so the RETURNING 
tuples are
+                                * stored into the tuple store.
+                                */
+                               for (;;)
+                               {
+                                       slot = ExecProcNode((PlanState *) 
leader);
+                                       if (TupIsNull(slot))
+                                               break;
+                               }
+ 
+                               ExecReScan((PlanState *) leader, NULL);
+                       }
+               }
+       }
+ 
        /*
         * Loop until we've processed the proper number of tuples from the plan.
         */
***************
*** 1943,1949 **** EvalPlanQualStart(EPQState *epqstate, EState *parentestate, 
Plan *planTree)
         * ExecInitSubPlan expects to be able to find these entries.
         * Some of the SubPlans might not be used in the part of the plan tree
         * we intend to run, but since it's not easy to tell which, we just
!        * initialize them all.
         */
        Assert(estate->es_subplanstates == NIL);
        foreach(l, parentestate->es_plannedstmt->subplans)
--- 2015,2022 ----
         * ExecInitSubPlan expects to be able to find these entries.
         * Some of the SubPlans might not be used in the part of the plan tree
         * we intend to run, but since it's not easy to tell which, we just
!        * initialize them all.  However, we will never run ModifyTable nodes in
!        * EvalPlanQual() so don't initialize them.
         */
        Assert(estate->es_subplanstates == NIL);
        foreach(l, parentestate->es_plannedstmt->subplans)
***************
*** 1951,1957 **** EvalPlanQualStart(EPQState *epqstate, EState *parentestate, 
Plan *planTree)
                Plan       *subplan = (Plan *) lfirst(l);
                PlanState  *subplanstate;
  
!               subplanstate = ExecInitNode(subplan, estate, 0);
  
                estate->es_subplanstates = lappend(estate->es_subplanstates,
                                                                                
   subplanstate);
--- 2024,2034 ----
                Plan       *subplan = (Plan *) lfirst(l);
                PlanState  *subplanstate;
  
!               /* Don't initialize ModifyTable subplans. */
!               if (IsA(subplan, ModifyTable))
!                       subplanstate = NULL;
!               else
!                       subplanstate = ExecInitNode(subplan, estate, 0);
  
                estate->es_subplanstates = lappend(estate->es_subplanstates,
                                                                                
   subplanstate);
*** a/src/backend/executor/execUtils.c
--- b/src/backend/executor/execUtils.c
***************
*** 968,980 **** ExecCloseIndices(ResultRelInfo *resultRelInfo)
   * ----------------------------------------------------------------
   */
  List *
! ExecInsertIndexTuples(TupleTableSlot *slot,
                                          ItemPointer tupleid,
                                          EState *estate,
                                          bool is_vacuum_full)
  {
        List       *result = NIL;
-       ResultRelInfo *resultRelInfo;
        int                     i;
        int                     numIndices;
        RelationPtr relationDescs;
--- 968,980 ----
   * ----------------------------------------------------------------
   */
  List *
! ExecInsertIndexTuples(ResultRelInfo* resultRelInfo,
!                                         TupleTableSlot *slot,
                                          ItemPointer tupleid,
                                          EState *estate,
                                          bool is_vacuum_full)
  {
        List       *result = NIL;
        int                     i;
        int                     numIndices;
        RelationPtr relationDescs;
***************
*** 987,993 **** ExecInsertIndexTuples(TupleTableSlot *slot,
        /*
         * Get information from the result relation info structure.
         */
-       resultRelInfo = estate->es_result_relation_info;
        numIndices = resultRelInfo->ri_NumIndices;
        relationDescs = resultRelInfo->ri_IndexRelationDescs;
        indexInfoArray = resultRelInfo->ri_IndexRelationInfo;
--- 987,992 ----
*** a/src/backend/executor/nodeModifyTable.c
--- b/src/backend/executor/nodeModifyTable.c
***************
*** 158,169 **** ExecProcessReturning(ProjectionInfo *projectReturning,
   * ----------------------------------------------------------------
   */
  static TupleTableSlot *
! ExecInsert(TupleTableSlot *slot,
                   TupleTableSlot *planSlot,
                   EState *estate)
  {
        HeapTuple       tuple;
-       ResultRelInfo *resultRelInfo;
        Relation        resultRelationDesc;
        Oid                     newId;
        List       *recheckIndexes = NIL;
--- 158,169 ----
   * ----------------------------------------------------------------
   */
  static TupleTableSlot *
! ExecInsert(ResultRelInfo *resultRelInfo,
!                  TupleTableSlot *slot,
                   TupleTableSlot *planSlot,
                   EState *estate)
  {
        HeapTuple       tuple;
        Relation        resultRelationDesc;
        Oid                     newId;
        List       *recheckIndexes = NIL;
***************
*** 177,183 **** ExecInsert(TupleTableSlot *slot,
        /*
         * get information on the (current) result relation
         */
-       resultRelInfo = estate->es_result_relation_info;
        resultRelationDesc = resultRelInfo->ri_RelationDesc;
  
        /*
--- 177,182 ----
***************
*** 247,253 **** ExecInsert(TupleTableSlot *slot,
         * insert index entries for tuple
         */
        if (resultRelInfo->ri_NumIndices > 0)
!               recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
                                                                                
           estate, false);
  
        /* AFTER ROW INSERT Triggers */
--- 246,253 ----
         * insert index entries for tuple
         */
        if (resultRelInfo->ri_NumIndices > 0)
!               recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
!                                                                               
           slot, &(tuple->t_self),
                                                                                
           estate, false);
  
        /* AFTER ROW INSERT Triggers */
***************
*** 271,282 **** ExecInsert(TupleTableSlot *slot,
   * ----------------------------------------------------------------
   */
  static TupleTableSlot *
! ExecDelete(ItemPointer tupleid,
                   TupleTableSlot *planSlot,
                   EPQState *epqstate,
                   EState *estate)
  {
-       ResultRelInfo *resultRelInfo;
        Relation        resultRelationDesc;
        HTSU_Result result;
        ItemPointerData update_ctid;
--- 271,282 ----
   * ----------------------------------------------------------------
   */
  static TupleTableSlot *
! ExecDelete(ResultRelInfo *resultRelInfo,
!                  ItemPointer tupleid,
                   TupleTableSlot *planSlot,
                   EPQState *epqstate,
                   EState *estate)
  {
        Relation        resultRelationDesc;
        HTSU_Result result;
        ItemPointerData update_ctid;
***************
*** 285,291 **** ExecDelete(ItemPointer tupleid,
        /*
         * get information on the (current) result relation
         */
-       resultRelInfo = estate->es_result_relation_info;
        resultRelationDesc = resultRelInfo->ri_RelationDesc;
  
        /* BEFORE ROW DELETE Triggers */
--- 285,290 ----
***************
*** 414,427 **** ldelete:;
   * ----------------------------------------------------------------
   */
  static TupleTableSlot *
! ExecUpdate(ItemPointer tupleid,
                   TupleTableSlot *slot,
                   TupleTableSlot *planSlot,
                   EPQState *epqstate,
                   EState *estate)
  {
        HeapTuple       tuple;
-       ResultRelInfo *resultRelInfo;
        Relation        resultRelationDesc;
        HTSU_Result result;
        ItemPointerData update_ctid;
--- 413,426 ----
   * ----------------------------------------------------------------
   */
  static TupleTableSlot *
! ExecUpdate(ResultRelInfo *resultRelInfo,
!                  ItemPointer tupleid,
                   TupleTableSlot *slot,
                   TupleTableSlot *planSlot,
                   EPQState *epqstate,
                   EState *estate)
  {
        HeapTuple       tuple;
        Relation        resultRelationDesc;
        HTSU_Result result;
        ItemPointerData update_ctid;
***************
*** 443,449 **** ExecUpdate(ItemPointer tupleid,
        /*
         * get information on the (current) result relation
         */
-       resultRelInfo = estate->es_result_relation_info;
        resultRelationDesc = resultRelInfo->ri_RelationDesc;
  
        /* BEFORE ROW UPDATE Triggers */
--- 442,447 ----
***************
*** 561,567 **** lreplace:;
         * If it's a HOT update, we mustn't insert new index entries.
         */
        if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
!               recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self),
                                                                                
           estate, false);
  
        /* AFTER ROW UPDATE Triggers */
--- 559,566 ----
         * If it's a HOT update, we mustn't insert new index entries.
         */
        if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple))
!               recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
!                                                                               
           slot, &(tuple->t_self),
                                                                                
           estate, false);
  
        /* AFTER ROW UPDATE Triggers */
***************
*** 587,601 **** fireBSTriggers(ModifyTableState *node)
        {
                case CMD_INSERT:
                        ExecBSInsertTriggers(node->ps.state,
!                                                                
node->ps.state->es_result_relations);
                        break;
                case CMD_UPDATE:
                        ExecBSUpdateTriggers(node->ps.state,
!                                                                
node->ps.state->es_result_relations);
                        break;
                case CMD_DELETE:
                        ExecBSDeleteTriggers(node->ps.state,
!                                                                
node->ps.state->es_result_relations);
                        break;
                default:
                        elog(ERROR, "unknown operation");
--- 586,600 ----
        {
                case CMD_INSERT:
                        ExecBSInsertTriggers(node->ps.state,
!                                                                
node->resultRelInfo);
                        break;
                case CMD_UPDATE:
                        ExecBSUpdateTriggers(node->ps.state,
!                                                                
node->resultRelInfo);
                        break;
                case CMD_DELETE:
                        ExecBSDeleteTriggers(node->ps.state,
!                                                                
node->resultRelInfo);
                        break;
                default:
                        elog(ERROR, "unknown operation");
***************
*** 613,627 **** fireASTriggers(ModifyTableState *node)
        {
                case CMD_INSERT:
                        ExecASInsertTriggers(node->ps.state,
!                                                                
node->ps.state->es_result_relations);
                        break;
                case CMD_UPDATE:
                        ExecASUpdateTriggers(node->ps.state,
!                                                                
node->ps.state->es_result_relations);
                        break;
                case CMD_DELETE:
                        ExecASDeleteTriggers(node->ps.state,
!                                                                
node->ps.state->es_result_relations);
                        break;
                default:
                        elog(ERROR, "unknown operation");
--- 612,626 ----
        {
                case CMD_INSERT:
                        ExecASInsertTriggers(node->ps.state,
!                                                                
node->resultRelInfo);
                        break;
                case CMD_UPDATE:
                        ExecASUpdateTriggers(node->ps.state,
!                                                                
node->resultRelInfo);
                        break;
                case CMD_DELETE:
                        ExecASDeleteTriggers(node->ps.state,
!                                                                
node->resultRelInfo);
                        break;
                default:
                        elog(ERROR, "unknown operation");
***************
*** 643,648 **** ExecModifyTable(ModifyTableState *node)
--- 642,648 ----
        EState *estate = node->ps.state;
        CmdType operation = node->operation;
        PlanState *subplanstate;
+       ResultRelInfo *resultRelInfo;
        JunkFilter *junkfilter;
        TupleTableSlot *slot;
        TupleTableSlot *planSlot;
***************
*** 658,674 **** ExecModifyTable(ModifyTableState *node)
                node->fireBSTriggers = false;
        }
  
-       /*
-        * es_result_relation_info must point to the currently active result
-        * relation.  (Note we assume that ModifyTable nodes can't be nested.)
-        * We want it to be NULL whenever we're not within ModifyTable, though.
-        */
-       estate->es_result_relation_info =
-               estate->es_result_relations + node->mt_whichplan;
- 
        /* Preload local variables */
        subplanstate = node->mt_plans[node->mt_whichplan];
!       junkfilter = estate->es_result_relation_info->ri_junkFilter;
  
        /*
         * Fetch rows from subplan(s), and execute the required table 
modification
--- 658,667 ----
                node->fireBSTriggers = false;
        }
  
        /* Preload local variables */
        subplanstate = node->mt_plans[node->mt_whichplan];
!       resultRelInfo = node->resultRelInfo + node->mt_whichplan;
!       junkfilter = resultRelInfo->ri_junkFilter;
  
        /*
         * Fetch rows from subplan(s), and execute the required table 
modification
***************
*** 684,692 **** ExecModifyTable(ModifyTableState *node)
                        node->mt_whichplan++;
                        if (node->mt_whichplan < node->mt_nplans)
                        {
-                               estate->es_result_relation_info++;
                                subplanstate = 
node->mt_plans[node->mt_whichplan];
!                               junkfilter = 
estate->es_result_relation_info->ri_junkFilter;
                                EvalPlanQualSetPlan(&node->mt_epqstate, 
subplanstate->plan);
                                continue;
                        }
--- 677,685 ----
                        node->mt_whichplan++;
                        if (node->mt_whichplan < node->mt_nplans)
                        {
                                subplanstate = 
node->mt_plans[node->mt_whichplan];
!                               resultRelInfo = node->resultRelInfo + 
node->mt_whichplan;
!                               junkfilter = resultRelInfo->ri_junkFilter;
                                EvalPlanQualSetPlan(&node->mt_epqstate, 
subplanstate->plan);
                                continue;
                        }
***************
*** 728,741 **** ExecModifyTable(ModifyTableState *node)
                switch (operation)
                {
                        case CMD_INSERT:
!                               slot = ExecInsert(slot, planSlot, estate);
                                break;
                        case CMD_UPDATE:
!                               slot = ExecUpdate(tupleid, slot, planSlot,
                                                                  
&node->mt_epqstate, estate);
                                break;
                        case CMD_DELETE:
!                               slot = ExecDelete(tupleid, planSlot,
                                                                  
&node->mt_epqstate, estate);
                                break;
                        default:
--- 721,737 ----
                switch (operation)
                {
                        case CMD_INSERT:
!                               slot = ExecInsert(resultRelInfo,
!                                                                 slot, 
planSlot, estate);
                                break;
                        case CMD_UPDATE:
!                               slot = ExecUpdate(resultRelInfo,
!                                                                 tupleid, 
slot, planSlot,
                                                                  
&node->mt_epqstate, estate);
                                break;
                        case CMD_DELETE:
!                               slot = ExecDelete(resultRelInfo,
!                                                                 tupleid, 
planSlot,
                                                                  
&node->mt_epqstate, estate);
                                break;
                        default:
***************
*** 748,762 **** ExecModifyTable(ModifyTableState *node)
                 * the work on next call.
                 */
                if (slot)
-               {
-                       estate->es_result_relation_info = NULL;
                        return slot;
-               }
        }
  
-       /* Reset es_result_relation_info before exiting */
-       estate->es_result_relation_info = NULL;
- 
        /*
         * We're done, but fire AFTER STATEMENT triggers before exiting.
         */
--- 744,752 ----
***************
*** 803,827 **** ExecInitModifyTable(ModifyTable *node, EState *estate, int 
eflags)
        mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * 
nplans);
        mtstate->mt_nplans = nplans;
        mtstate->operation = operation;
        /* set up epqstate with dummy subplan pointer for the moment */
        EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, node->epqParam);
        mtstate->fireBSTriggers = true;
  
-       /* For the moment, assume our targets are exactly the global result 
rels */
- 
        /*
         * call ExecInitNode on each of the plans to be executed and save the
         * results into the array "mt_plans".  Note we *must* set
         * estate->es_result_relation_info correctly while we initialize each
         * sub-plan; ExecContextForcesOids depends on that!
         */
-       estate->es_result_relation_info = estate->es_result_relations;
        i = 0;
        foreach(l, node->plans)
        {
                subplan = (Plan *) lfirst(l);
                mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags);
!               estate->es_result_relation_info++;
                i++;
        }
        estate->es_result_relation_info = NULL;
--- 793,831 ----
        mtstate->mt_plans = (PlanState **) palloc0(sizeof(PlanState *) * 
nplans);
        mtstate->mt_nplans = nplans;
        mtstate->operation = operation;
+       mtstate->resultRelIndex = node->resultRelIndex;
+       mtstate->resultRelInfo = estate->es_result_relations + 
node->resultRelIndex;
+ 
        /* set up epqstate with dummy subplan pointer for the moment */
        EvalPlanQualInit(&mtstate->mt_epqstate, estate, NULL, node->epqParam);
        mtstate->fireBSTriggers = true;
  
        /*
         * call ExecInitNode on each of the plans to be executed and save the
         * results into the array "mt_plans".  Note we *must* set
         * estate->es_result_relation_info correctly while we initialize each
         * sub-plan; ExecContextForcesOids depends on that!
         */
        i = 0;
+       resultRelInfo = mtstate->resultRelInfo;
        foreach(l, node->plans)
        {
                subplan = (Plan *) lfirst(l);
+ 
+               /*
+                * If there are indices on the result relation, open them and 
save
+                * descriptors in the result relation info, so that we can add 
new index
+                * entries for the tuples we add/update.  We need not do this 
for a
+                * DELETE, however, since deletion doesn't affect indexes.
+                */
+               if (resultRelInfo->ri_RelationDesc->rd_rel->relhasindex &&
+                       operation != CMD_DELETE)
+                       ExecOpenIndices(resultRelInfo);
+ 
+               estate->es_result_relation_info = resultRelInfo;
                mtstate->mt_plans[i] = ExecInitNode(subplan, estate, eflags);
! 
!               resultRelInfo++;
                i++;
        }
        estate->es_result_relation_info = NULL;
***************
*** 858,865 **** ExecInitModifyTable(ModifyTable *node, EState *estate, int 
eflags)
                /*
                 * Build a projection for each result rel.
                 */
!               Assert(list_length(node->returningLists) == 
estate->es_num_result_relations);
!               resultRelInfo = estate->es_result_relations;
                foreach(l, node->returningLists)
                {
                        List       *rlist = (List *) lfirst(l);
--- 862,868 ----
                /*
                 * Build a projection for each result rel.
                 */
!               resultRelInfo = mtstate->resultRelInfo;
                foreach(l, node->returningLists)
                {
                        List       *rlist = (List *) lfirst(l);
***************
*** 958,964 **** ExecInitModifyTable(ModifyTable *node, EState *estate, int 
eflags)
  
                if (junk_filter_needed)
                {
!                       resultRelInfo = estate->es_result_relations;
                        for (i = 0; i < nplans; i++)
                        {
                                JunkFilter *j;
--- 961,967 ----
  
                if (junk_filter_needed)
                {
!                       resultRelInfo = mtstate->resultRelInfo;
                        for (i = 0; i < nplans; i++)
                        {
                                JunkFilter *j;
***************
*** 987,993 **** ExecInitModifyTable(ModifyTable *node, EState *estate, int 
eflags)
                else
                {
                        if (operation == CMD_INSERT)
!                               
ExecCheckPlanOutput(estate->es_result_relations->ri_RelationDesc,
                                                                        
subplan->targetlist);
                }
        }
--- 990,996 ----
                else
                {
                        if (operation == CMD_INSERT)
!                               
ExecCheckPlanOutput(mtstate->resultRelInfo->ri_RelationDesc,
                                                                        
subplan->targetlist);
                }
        }
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 84,89 **** _copyPlannedStmt(PlannedStmt *from)
--- 84,90 ----
        COPY_NODE_FIELD(resultRelations);
        COPY_NODE_FIELD(utilityStmt);
        COPY_NODE_FIELD(intoClause);
+       COPY_SCALAR_FIELD(hasWritableCtes);
        COPY_NODE_FIELD(subplans);
        COPY_BITMAPSET_FIELD(rewindPlanIDs);
        COPY_NODE_FIELD(rowMarks);
***************
*** 172,177 **** _copyModifyTable(ModifyTable *from)
--- 173,179 ----
         */
        COPY_SCALAR_FIELD(operation);
        COPY_NODE_FIELD(resultRelations);
+       COPY_SCALAR_FIELD(resultRelIndex);
        COPY_NODE_FIELD(plans);
        COPY_NODE_FIELD(returningLists);
        COPY_NODE_FIELD(rowMarks);
*** a/src/backend/nodes/nodeFuncs.c
--- b/src/backend/nodes/nodeFuncs.c
***************
*** 2383,2388 **** bool
--- 2383,2432 ----
                                        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/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 327,332 **** _outModifyTable(StringInfo str, ModifyTable *node)
--- 327,333 ----
  
        WRITE_ENUM_FIELD(operation, CmdType);
        WRITE_NODE_FIELD(resultRelations);
+       WRITE_INT_FIELD(resultRelIndex);
        WRITE_NODE_FIELD(plans);
        WRITE_NODE_FIELD(returningLists);
        WRITE_NODE_FIELD(rowMarks);
***************
*** 1529,1534 **** _outPlannerGlobal(StringInfo str, PlannerGlobal *node)
--- 1530,1536 ----
        WRITE_NODE_FIELD(finalrowmarks);
        WRITE_NODE_FIELD(relationOids);
        WRITE_NODE_FIELD(invalItems);
+       WRITE_NODE_FIELD(resultRelations);
        WRITE_UINT_FIELD(lastPHId);
        WRITE_BOOL_FIELD(transientPlan);
  }
***************
*** 1543,1549 **** _outPlannerInfo(StringInfo str, PlannerInfo *node)
        WRITE_NODE_FIELD(glob);
        WRITE_UINT_FIELD(query_level);
        WRITE_NODE_FIELD(join_rel_list);
-       WRITE_NODE_FIELD(resultRelations);
        WRITE_NODE_FIELD(init_plans);
        WRITE_NODE_FIELD(cte_plan_ids);
        WRITE_NODE_FIELD(eq_classes);
--- 1545,1550 ----
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
***************
*** 3759,3768 **** make_modifytable(CmdType operation, List *resultRelations,
        double          total_size;
        ListCell   *subnode;
  
-       Assert(list_length(resultRelations) == list_length(subplans));
-       Assert(returningLists == NIL ||
-                  list_length(resultRelations) == list_length(returningLists));
- 
        /*
         * Compute cost as sum of subplan costs.
         */
--- 3759,3764 ----
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
***************
*** 160,165 **** standard_planner(Query *parse, int cursorOptions, 
ParamListInfo boundParams)
--- 160,167 ----
        glob->finalrowmarks = NIL;
        glob->relationOids = NIL;
        glob->invalItems = NIL;
+       glob->hasWritableCtes = false;
+       glob->resultRelations = NIL;
        glob->lastPHId = 0;
        glob->transientPlan = false;
  
***************
*** 237,245 **** standard_planner(Query *parse, int cursorOptions, 
ParamListInfo boundParams)
        result->transientPlan = glob->transientPlan;
        result->planTree = top_plan;
        result->rtable = glob->finalrtable;
!       result->resultRelations = root->resultRelations;
        result->utilityStmt = parse->utilityStmt;
        result->intoClause = parse->intoClause;
        result->subplans = glob->subplans;
        result->rewindPlanIDs = glob->rewindPlanIDs;
        result->rowMarks = glob->finalrowmarks;
--- 239,248 ----
        result->transientPlan = glob->transientPlan;
        result->planTree = top_plan;
        result->rtable = glob->finalrtable;
!       result->resultRelations = glob->resultRelations;
        result->utilityStmt = parse->utilityStmt;
        result->intoClause = parse->intoClause;
+       result->hasWritableCtes = glob->hasWritableCtes;
        result->subplans = glob->subplans;
        result->rewindPlanIDs = glob->rewindPlanIDs;
        result->rowMarks = glob->finalrowmarks;
***************
*** 541,547 **** subquery_planner(PlannerGlobal *glob, Query *parse,
                                rowMarks = root->rowMarks;
  
                        plan = (Plan *) make_modifytable(parse->commandType,
!                                                                               
         copyObject(root->resultRelations),
                                                                                
         list_make1(plan),
                                                                                
         returningLists,
                                                                                
         rowMarks,
--- 544,550 ----
                                rowMarks = root->rowMarks;
  
                        plan = (Plan *) make_modifytable(parse->commandType,
!                                                                               
         list_make1_int(parse->resultRelation),
                                                                                
         list_make1(plan),
                                                                                
         returningLists,
                                                                                
         rowMarks,
***************
*** 706,718 **** inheritance_planner(PlannerInfo *root)
        Query      *parse = root->parse;
        int                     parentRTindex = parse->resultRelation;
        List       *subplans = NIL;
-       List       *resultRelations = NIL;
        List       *returningLists = NIL;
        List       *rtable = NIL;
        List       *rowMarks;
        List       *tlist;
        PlannerInfo subroot;
        ListCell   *l;
  
        foreach(l, root->append_rel_list)
        {
--- 709,721 ----
        Query      *parse = root->parse;
        int                     parentRTindex = parse->resultRelation;
        List       *subplans = NIL;
        List       *returningLists = NIL;
        List       *rtable = NIL;
        List       *rowMarks;
        List       *tlist;
        PlannerInfo subroot;
        ListCell   *l;
+       List       *resultRelations = NIL;
  
        foreach(l, root->append_rel_list)
        {
***************
*** 772,779 **** inheritance_planner(PlannerInfo *root)
                }
        }
  
-       root->resultRelations = resultRelations;
- 
        /* Mark result as unordered (probably unnecessary) */
        root->query_pathkeys = NIL;
  
--- 775,780 ----
***************
*** 783,789 **** inheritance_planner(PlannerInfo *root)
         */
        if (subplans == NIL)
        {
-               root->resultRelations = list_make1_int(parentRTindex);
                /* although dummy, it must have a valid tlist for executor */
                tlist = preprocess_targetlist(root, parse->targetList);
                return (Plan *) make_result(root,
--- 784,789 ----
***************
*** 818,824 **** inheritance_planner(PlannerInfo *root)
  
        /* And last, tack on a ModifyTable node to do the UPDATE/DELETE work */
        return (Plan *) make_modifytable(parse->commandType,
!                                                                        
copyObject(root->resultRelations),
                                                                         
subplans, 
                                                                         
returningLists,
                                                                         
rowMarks,
--- 818,824 ----
  
        /* And last, tack on a ModifyTable node to do the UPDATE/DELETE work */
        return (Plan *) make_modifytable(parse->commandType,
!                                                                        
resultRelations,
                                                                         
subplans, 
                                                                         
returningLists,
                                                                         
rowMarks,
***************
*** 1667,1678 **** grouping_planner(PlannerInfo *root, double tuple_fraction)
                                                                                
  count_est);
        }
  
-       /* Compute result-relations list if needed */
-       if (parse->resultRelation)
-               root->resultRelations = list_make1_int(parse->resultRelation);
-       else
-               root->resultRelations = NIL;
- 
        /*
         * Return the actual output ordering in query_pathkeys for possible use 
by
         * an outer query level.
--- 1667,1672 ----
*** a/src/backend/optimizer/plan/setrefs.c
--- b/src/backend/optimizer/plan/setrefs.c
***************
*** 516,521 **** set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
--- 516,525 ----
                                                                                
          (Plan *) lfirst(l),
                                                                                
          rtoffset);
                                }
+ 
+                               splan->resultRelIndex = 
list_length(glob->resultRelations);
+                               glob->resultRelations = 
list_concat(glob->resultRelations,
+                                                                               
                        splan->resultRelations);
                        }
                        break;
                case T_Append:
*** a/src/backend/optimizer/plan/subselect.c
--- b/src/backend/optimizer/plan/subselect.c
***************
*** 873,888 **** SS_process_ctes(PlannerInfo *root)
                Bitmapset  *tmpset;
                int                     paramid;
                Param      *prm;
  
                /*
!                * Ignore CTEs that are not actually referenced anywhere.
                 */
!               if (cte->cterefcount == 0)
                {
                        /* Make a dummy entry in cte_plan_ids */
                        root->cte_plan_ids = lappend_int(root->cte_plan_ids, 
-1);
                        continue;
                }
  
                /*
                 * Copy the source Query node.  Probably not necessary, but 
let's keep
--- 873,905 ----
                Bitmapset  *tmpset;
                int                     paramid;
                Param      *prm;
+               CmdType         cmdType = ((Query *) 
cte->ctequery)->commandType;
  
                /*
!                * Ignore SELECT CTEs that are not actually referenced anywhere.
                 */
!               if (cte->cterefcount == 0 && cmdType == CMD_SELECT)
                {
                        /* Make a dummy entry in cte_plan_ids */
                        root->cte_plan_ids = lappend_int(root->cte_plan_ids, 
-1);
                        continue;
                }
+               else if (cmdType != CMD_SELECT)
+               {
+                       /* We don't know reference counts until here */
+                       if (cte->cterefcount > 0 &&
+                               ((Query *) cte->ctequery)->returningList == NIL)
+                       {
+                               ereport(ERROR,
+                                               
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                errmsg("INSERT/UPDATE/DELETE 
without RETURNING is only allowed inside a non-referenced CTE")));
+                       }
+ 
+                       if (root->query_level > 1)
+                               ereport(ERROR,
+                                               
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                errmsg("INSERT/UPDATE/DELETE 
inside a CTE is only allowed on the top level")));
+               }
  
                /*
                 * Copy the source Query node.  Probably not necessary, but 
let's keep
***************
*** 899,904 **** SS_process_ctes(PlannerInfo *root)
--- 916,924 ----
                                                                
cte->cterecursive, 0.0,
                                                                &subroot);
  
+               if (subroot->parse->commandType != CMD_SELECT)
+                       root->glob->hasWritableCtes = true;
+ 
                /*
                 * Make a SubPlan node for it.  This is just enough unlike
                 * build_subplan that we can't share code.
*** a/src/backend/parser/gram.y
--- b/src/backend/parser/gram.y
***************
*** 7374,7379 **** common_table_expr:  name opt_name_list AS select_with_parens
--- 7374,7406 ----
                                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"
  
  
***************
*** 225,246 **** 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, false);
        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),
--- 226,250 ----
  analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
  {
        Query      *query;
!       List       *cteList;
  
        query = parse_sub_analyze(cte->ctequery, pstate, cte, false);
        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.
!        * Note, however, that we can't yet decice whether to allow
!        * INSERT/UPDATE/DELETE without a RETURNING clause or not because we 
don't
!        * know the refcount.
         */
!       Assert(IsA(query, Query) && query->utilityStmt == NULL);
! 
        if (query->intoClause)
                ereport(ERROR,
                                (errcode(ERRCODE_SYNTAX_ERROR),
***************
*** 251,257 **** analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
        if (!cte->cterecursive)
        {
                /* Compute the output column names/types if not done yet */
!               analyzeCTETargetList(pstate, cte, query->targetList);
        }
        else
        {
--- 255,261 ----
        if (!cte->cterecursive)
        {
                /* Compute the output column names/types if not done yet */
!               analyzeCTETargetList(pstate, cte, cteList);
        }
        else
        {
***************
*** 269,275 **** 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;
--- 273,279 ----
                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
***************
*** 24,29 ****
--- 24,30 ----
  #include "funcapi.h"
  #include "nodes/makefuncs.h"
  #include "nodes/nodeFuncs.h"
+ #include "nodes/plannodes.h"
  #include "parser/parsetree.h"
  #include "parser/parse_relation.h"
  #include "parser/parse_type.h"
*** a/src/backend/parser/parse_target.c
--- b/src/backend/parser/parse_target.c
***************
*** 314,323 **** 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",
--- 314,333 ----
                        {
                                CommonTableExpr *cte = GetCTEForRTE(pstate, 
rte, netlevelsup);
                                TargetEntry *ste;
+                               List            *cteList;
+                               Query           *ctequery;
  
                                /* should be analyzed by now */
                                Assert(IsA(cte->ctequery, Query));
! 
!                               ctequery = (Query *) cte->ctequery;
! 
!                               if (ctequery->commandType == CMD_SELECT)
!                                       cteList = ctequery->targetList;
!                               else
!                                       cteList = ctequery->returningList;
! 
!                               ste = get_tle_by_resno(cteList,
                                                                           
attnum);
                                if (ste == NULL || ste->resjunk)
                                        elog(ERROR, "subquery %s does not have 
attribute %d",
***************
*** 1345,1355 **** 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);
--- 1355,1374 ----
                        {
                                CommonTableExpr *cte = GetCTEForRTE(pstate, 
rte, netlevelsup);
                                TargetEntry *ste;
+                               List            *cteList;
+                               Query           *ctequery;
  
                                /* should be analyzed by now */
                                Assert(IsA(cte->ctequery, Query));
! 
!                               ctequery = (Query *) cte->ctequery;
! 
!                               if (ctequery->commandType == CMD_SELECT)
!                                       cteList = ctequery->targetList;
!                               else
!                                       cteList = ctequery->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);
***************
*** 1372,1378 **** expandRecordVariable(ParseState *pstate, Var *var, int 
levelsup)
                                                 levelsup++)
                                                pstate = 
pstate->parentParseState;
                                        mypstate.parentParseState = pstate;
!                                       mypstate.p_rtable = ((Query *) 
cte->ctequery)->rtable;
                                        /* don't bother filling the rest of the 
fake pstate */
  
                                        return expandRecordVariable(&mypstate, 
(Var *) expr, 0);
--- 1391,1397 ----
                                                 levelsup++)
                                                pstate = 
pstate->parentParseState;
                                        mypstate.parentParseState = pstate;
!                                       mypstate.p_rtable = ctequery->rtable;
                                        /* don't bother filling the rest of the 
fake pstate */
  
                                        return expandRecordVariable(&mypstate, 
(Var *) expr, 0);
*** a/src/backend/rewrite/rewriteHandler.c
--- b/src/backend/rewrite/rewriteHandler.c
***************
*** 1632,1637 **** RewriteQuery(Query *parsetree, List *rewrite_events)
--- 1632,1641 ----
        bool            returning = false;
        Query      *qual_product = NULL;
        List       *rewritten = NIL;
+       ListCell        *lc;
+       CommonTableExpr *cte;
+       Query           *ctequery;
+       List            *newstuff;
  
        /*
         * If the statement is an update, insert or delete - fire rules on it.
***************
*** 1749,1755 **** RewriteQuery(Query *parsetree, List *rewrite_events)
                                foreach(n, product_queries)
                                {
                                        Query      *pt = (Query *) lfirst(n);
-                                       List       *newstuff;
  
                                        newstuff = RewriteQuery(pt, 
rewrite_events);
                                        rewritten = list_concat(rewritten, 
newstuff);
--- 1753,1758 ----
***************
*** 1804,1809 **** RewriteQuery(Query *parsetree, List *rewrite_events)
--- 1807,1861 ----
        }
  
        /*
+        * Rewrite DML statements inside CTEs.  If there are any
+        * DO ALSO rules, they are added to the top level (there
+        * won't be any non-top-level CTEs with DML).
+        */
+       foreach(lc, parsetree->cteList)
+       {
+               cte = lfirst(lc);
+ 
+               ctequery = (Query *) cte->ctequery;
+ 
+               if (ctequery->commandType == CMD_SELECT)
+                       continue;
+ 
+               newstuff = RewriteQuery(ctequery, NIL);
+ 
+               /*
+                * For UPDATE and DELETE, the actual query is
+                * added to the end of the list.
+                */
+               if (list_length(newstuff) > 1 &&
+                       ctequery->commandType != CMD_INSERT)
+               {
+                       ListCell        *lc;
+                       int n = 1;
+ 
+                       foreach(lc, newstuff)
+                       {
+                               /*
+                                * If this is the last one, don't add it to the 
results.
+                                * Instead, update the query inside the CTE.
+                                */
+                               if (n == list_length(newstuff))
+                                       cte->ctequery = (Node *) lfirst(lc);
+                               else
+                                       rewritten = lcons((void *) lfirst(lc), 
rewritten);
+                                       
+                               n++;
+                       }
+ 
+               }
+               else
+               {
+                       cte->ctequery = (Node *) linitial(newstuff);
+                       rewritten = list_concat(rewritten,
+                                                         
list_delete_first(newstuff));
+               }
+       }
+ 
+       /*
         * For INSERTs, the original query is done first; for UPDATE/DELETE, it 
is
         * done last.  This is needed because update and delete rule actions 
might
         * not do anything if they are invoked after the update or delete is
*** a/src/backend/tcop/pquery.c
--- b/src/backend/tcop/pquery.c
***************
*** 293,298 **** ChoosePortalStrategy(List *stmts)
--- 293,299 ----
                        if (pstmt->canSetTag)
                        {
                                if (pstmt->commandType == CMD_SELECT &&
+                                       pstmt->hasWritableCtes == false &&
                                        pstmt->utilityStmt == NULL &&
                                        pstmt->intoClause == NULL)
                                        return PORTAL_ONE_SELECT;
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 3858,3866 **** 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",
--- 3858,3873 ----
                                }
                                if (lc != NULL)
                                {
!                                       Query           *ctequery = (Query *) 
cte->ctequery;
!                                       List            *ctelist;
!                                       TargetEntry     *ste;
! 
!                                       if (ctequery->commandType != CMD_SELECT)
!                                               ctelist = 
ctequery->returningList;
!                                       else
!                                               ctelist = ctequery->targetList;
! 
!                                       ste = get_tle_by_resno(ctelist, attnum);
  
                                        if (ste == NULL || ste->resjunk)
                                                elog(ERROR, "subquery %s does 
not have attribute %d",
*** a/src/include/executor/executor.h
--- b/src/include/executor/executor.h
***************
*** 319,325 **** extern void ExecCloseScanRelation(Relation scanrel);
  
  extern void ExecOpenIndices(ResultRelInfo *resultRelInfo);
  extern void ExecCloseIndices(ResultRelInfo *resultRelInfo);
! extern List *ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid,
                                          EState *estate, bool is_vacuum_full);
  
  extern void RegisterExprContextCallback(ExprContext *econtext,
--- 319,326 ----
  
  extern void ExecOpenIndices(ResultRelInfo *resultRelInfo);
  extern void ExecCloseIndices(ResultRelInfo *resultRelInfo);
! extern List *ExecInsertIndexTuples(ResultRelInfo *resultRelInfo,
!                                         TupleTableSlot *slot, ItemPointer 
tupleid,
                                          EState *estate, bool is_vacuum_full);
  
  extern void RegisterExprContextCallback(ExprContext *econtext,
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 1021,1026 **** typedef struct ModifyTableState
--- 1021,1028 ----
        PlanState         **mt_plans;           /* subplans (one per target 
rel) */
        int                             mt_nplans;              /* number of 
plans in the array */
        int                             mt_whichplan;   /* which one is being 
executed (0..n-1) */
+       int                             resultRelIndex;
+       ResultRelInfo  *resultRelInfo;
        EPQState                mt_epqstate;    /* for evaluating EvalPlanQual 
rechecks */
        bool                    fireBSTriggers; /* do we need to fire stmt 
triggers? */
  } ModifyTableState;
*** a/src/include/nodes/plannodes.h
--- b/src/include/nodes/plannodes.h
***************
*** 55,60 **** typedef struct PlannedStmt
--- 55,62 ----
  
        IntoClause *intoClause;         /* target for SELECT INTO / CREATE 
TABLE AS */
  
+       bool            hasWritableCtes;
+ 
        List       *subplans;           /* Plan trees for SubPlan expressions */
  
        Bitmapset  *rewindPlanIDs;      /* indices of subplans that require 
REWIND */
***************
*** 165,170 **** typedef struct ModifyTable
--- 167,173 ----
        Plan            plan;
        CmdType         operation;                      /* INSERT, UPDATE, or 
DELETE */
        List       *resultRelations;    /* integer list of RT indexes */
+       int                     resultRelIndex;
        List       *plans;                              /* plan(s) producing 
source data */
        List       *returningLists;             /* per-target-table RETURNING 
tlists */
        List       *rowMarks;                   /* PlanRowMarks (non-locking 
only) */
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
***************
*** 80,85 **** typedef struct PlannerGlobal
--- 80,89 ----
  
        List       *invalItems;         /* other dependencies, as 
PlanInvalItems */
  
+       bool            hasWritableCtes;/* is there an (INSERT|UPDATE|DELETE) 
.. RETURNING inside a CTE? */
+ 
+       List       *resultRelations;/* list of result relations */
+ 
        Index           lastPHId;               /* highest PlaceHolderVar ID 
assigned */
  
        bool            transientPlan;  /* redo plan when TransactionXmin 
changes? */
***************
*** 142,149 **** typedef struct PlannerInfo
        List       *join_rel_list;      /* list of join-relation RelOptInfos */
        struct HTAB *join_rel_hash; /* optional hashtable for join relations */
  
-       List       *resultRelations;    /* integer list of RT indexes, or NIL */
- 
        List       *init_plans;         /* init SubPlans for query */
  
        List       *cte_plan_ids;       /* per-CTE-item list of subplan IDs */
--- 146,151 ----
*** a/src/test/regress/expected/with.out
--- b/src/test/regress/expected/with.out
***************
*** 1026,1028 **** SELECT * FROM t;
--- 1026,1110 ----
   10
  (55 rows)
  
+ --
+ -- Writeable CTEs with RETURNING
+ --
+ WITH t AS (
+     INSERT INTO y
+     VALUES
+         (11),
+         (12),
+         (13),
+         (14),
+         (15),
+         (16),
+         (17),
+         (18),
+         (19),
+         (20)
+     RETURNING *
+ )
+ SELECT * FROM t;
+  a  
+ ----
+  11
+  12
+  13
+  14
+  15
+  16
+  17
+  18
+  19
+  20
+ (10 rows)
+ 
+ WITH t AS (
+     UPDATE y
+     SET a=a+1
+     RETURNING *
+ )
+ SELECT * FROM t;
+  a  
+ ----
+   2
+   3
+   4
+   5
+   6
+   7
+   8
+   9
+  10
+  11
+  12
+  13
+  14
+  15
+  16
+  17
+  18
+  19
+  20
+  21
+ (20 rows)
+ 
+ WITH t AS (
+     DELETE FROM y
+     WHERE a <= 10
+     RETURNING *
+ )
+ SELECT * FROM t;
+  a  
+ ----
+   2
+   3
+   4
+   5
+   6
+   7
+   8
+   9
+  10
+ (9 rows)
+ 
*** a/src/test/regress/sql/with.sql
--- b/src/test/regress/sql/with.sql
***************
*** 500,502 **** WITH RECURSIVE t(j) AS (
--- 500,537 ----
      SELECT j+1 FROM t WHERE j < 10
  )
  SELECT * FROM t;
+ 
+ --
+ -- Writeable CTEs with RETURNING
+ --
+ 
+ WITH t AS (
+     INSERT INTO y
+     VALUES
+         (11),
+         (12),
+         (13),
+         (14),
+         (15),
+         (16),
+         (17),
+         (18),
+         (19),
+         (20)
+     RETURNING *
+ )
+ SELECT * FROM t;
+ 
+ WITH t AS (
+     UPDATE y
+     SET a=a+1
+     RETURNING *
+ )
+ SELECT * FROM t;
+ 
+ WITH t AS (
+     DELETE FROM y
+     WHERE a <= 10
+     RETURNING *
+ )
+ SELECT * FROM t;
-- 
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