Using results from INSERT ... RETURNING

Поиск
Список
Период
Сортировка
От Marko Tiikkaja
Тема Using results from INSERT ... RETURNING
Дата
Msg-id 4A53B0BA.6040104@cs.helsinki.fi
обсуждение исходный текст
Ответы Re: Using results from INSERT ... RETURNING  (Peter Eisentraut <peter_e@gmx.net>)
Re: Using results from INSERT ... RETURNING  (Jaime Casanova <jcasanov@systemguards.com.ec>)
Список pgsql-hackers
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? */

В списке pgsql-hackers по дате отправления:

Предыдущее
От: "David E. Wheeler"
Дата:
Сообщение: Re: Maintenance Policy?
Следующее
От: Sergey Burladyan
Дата:
Сообщение: Re: 8.4, One-Time Filter and subquery ( ... FROM function() union all ... )