]> granicus.if.org Git - postgresql/blobdiff - src/backend/tcop/pquery.c
Update copyright for the year 2010.
[postgresql] / src / backend / tcop / pquery.c
index 49e2a4b008210b48d1faf26b68b56697279633b3..05068fa406c9176a00a5bd20de5054b12082b3e1 100644 (file)
@@ -3,41 +3,51 @@
  * pquery.c
  *       POSTGRES process query command code
  *
- * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.81 2004/07/17 03:29:00 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.134 2010/01/02 16:57:52 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
-#include "executor/executor.h"
+#include "access/xact.h"
+#include "commands/prepare.h"
+#include "commands/trigger.h"
+#include "executor/tstoreReceiver.h"
 #include "miscadmin.h"
-#include "tcop/tcopprot.h"
+#include "pg_trace.h"
 #include "tcop/pquery.h"
+#include "tcop/tcopprot.h"
 #include "tcop/utility.h"
-#include "utils/guc.h"
 #include "utils/memutils.h"
+#include "utils/snapmgr.h"
 
 
 /*
  * ActivePortal is the currently executing Portal (the most closely nested,
  * if there are several).
  */
-Portal ActivePortal = NULL;
+Portal         ActivePortal = NULL;
 
 
+static void ProcessQuery(PlannedStmt *plan,
+                        const char *sourceText,
+                        ParamListInfo params,
+                        DestReceiver *dest,
+                        char *completionTag);
+static void FillPortalStore(Portal portal, bool isTopLevel);
 static uint32 RunFromStore(Portal portal, ScanDirection direction, long count,
                         DestReceiver *dest);
 static long PortalRunSelect(Portal portal, bool forward, long count,
                                DestReceiver *dest);
-static void PortalRunUtility(Portal portal, Query *query,
+static void PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
                                 DestReceiver *dest, char *completionTag);
-static void PortalRunMulti(Portal portal,
+static void PortalRunMulti(Portal portal, bool isTopLevel,
                           DestReceiver *dest, DestReceiver *altdest,
                           char *completionTag);
 static long DoPortalRunFetch(Portal portal,
@@ -51,25 +61,63 @@ static void DoPortalRewind(Portal portal);
  * CreateQueryDesc
  */
 QueryDesc *
-CreateQueryDesc(Query *parsetree,
-                               Plan *plantree,
+CreateQueryDesc(PlannedStmt *plannedstmt,
+                               const char *sourceText,
+                               Snapshot snapshot,
+                               Snapshot crosscheck_snapshot,
                                DestReceiver *dest,
                                ParamListInfo params,
-                               bool doInstrument)
+                               int instrument_options)
+{
+       QueryDesc  *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
+
+       qd->operation = plannedstmt->commandType;       /* operation */
+       qd->plannedstmt = plannedstmt;          /* plan */
+       qd->utilitystmt = plannedstmt->utilityStmt; /* in case DECLARE CURSOR */
+       qd->sourceText = sourceText;    /* query text */
+       qd->snapshot = RegisterSnapshot(snapshot);      /* snapshot */
+       /* RI check snapshot */
+       qd->crosscheck_snapshot = RegisterSnapshot(crosscheck_snapshot);
+       qd->dest = dest;                        /* output dest */
+       qd->params = params;            /* parameter values passed into query */
+       qd->instrument_options = instrument_options;    /* instrumentation wanted? */
+
+       /* null these fields until set by ExecutorStart */
+       qd->tupDesc = NULL;
+       qd->estate = NULL;
+       qd->planstate = NULL;
+       qd->totaltime = NULL;
+
+       return qd;
+}
+
+/*
+ * CreateUtilityQueryDesc
+ */
+QueryDesc *
+CreateUtilityQueryDesc(Node *utilitystmt,
+                                          const char *sourceText,
+                                          Snapshot snapshot,
+                                          DestReceiver *dest,
+                                          ParamListInfo params)
 {
        QueryDesc  *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
 
-       qd->operation = parsetree->commandType;         /* operation */
-       qd->parsetree = parsetree;      /* parse tree */
-       qd->plantree = plantree;        /* plan */
+       qd->operation = CMD_UTILITY;    /* operation */
+       qd->plannedstmt = NULL;
+       qd->utilitystmt = utilitystmt;          /* utility command */
+       qd->sourceText = sourceText;    /* query text */
+       qd->snapshot = RegisterSnapshot(snapshot);      /* snapshot */
+       qd->crosscheck_snapshot = InvalidSnapshot;      /* RI check snapshot */
        qd->dest = dest;                        /* output dest */
        qd->params = params;            /* parameter values passed into query */
-       qd->doInstrument = doInstrument;        /* instrumentation wanted? */
+       qd->instrument_options = false; /* uninteresting for utilities */
 
        /* null these fields until set by ExecutorStart */
        qd->tupDesc = NULL;
        qd->estate = NULL;
        qd->planstate = NULL;
+       qd->totaltime = NULL;
 
        return qd;
 }
@@ -82,6 +130,11 @@ FreeQueryDesc(QueryDesc *qdesc)
 {
        /* Can't be a live query */
        Assert(qdesc->estate == NULL);
+
+       /* forget our snapshots */
+       UnregisterSnapshot(qdesc->snapshot);
+       UnregisterSnapshot(qdesc->crosscheck_snapshot);
+
        /* Only the QueryDesc itself need be freed */
        pfree(qdesc);
 }
@@ -89,10 +142,11 @@ FreeQueryDesc(QueryDesc *qdesc)
 
 /*
  * ProcessQuery
- *             Execute a single query
+ *             Execute a single plannable query within a PORTAL_MULTI_QUERY
+ *             or PORTAL_ONE_RETURNING portal
  *
- *     parsetree: the query tree
  *     plan: the plan tree for the query
+ *     sourceText: the source text of the query
  *     params: any parameters needed
  *     dest: where to send results
  *     completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
@@ -103,43 +157,38 @@ FreeQueryDesc(QueryDesc *qdesc)
  * Must be called in a memory context that will be reset or deleted on
  * error; otherwise the executor's memory usage will be leaked.
  */
-void
-ProcessQuery(Query *parsetree,
-                        Plan *plan,
+static void
+ProcessQuery(PlannedStmt *plan,
+                        const char *sourceText,
                         ParamListInfo params,
                         DestReceiver *dest,
                         char *completionTag)
 {
-       int                     operation = parsetree->commandType;
        QueryDesc  *queryDesc;
 
+       elog(DEBUG3, "ProcessQuery");
+
        /*
-        * Check for special-case destinations
+        * Must always set a snapshot for plannable queries.
         */
-       if (operation == CMD_SELECT)
-       {
-               if (parsetree->into != NULL)
-               {
-                       /*
-                        * SELECT INTO table (a/k/a CREATE AS ... SELECT).
-                        *
-                        * Override the normal communication destination; execMain.c
-                        * special-cases this case.  (Perhaps would be cleaner to have
-                        * an additional destination type?)
-                        */
-                       dest = None_Receiver;
-               }
-       }
+       PushActiveSnapshot(GetTransactionSnapshot());
 
        /*
         * Create the QueryDesc object
         */
-       queryDesc = CreateQueryDesc(parsetree, plan, dest, params, false);
+       queryDesc = CreateQueryDesc(plan, sourceText,
+                                                               GetActiveSnapshot(), InvalidSnapshot,
+                                                               dest, params, 0);
+
+       /*
+        * Set up to collect AFTER triggers
+        */
+       AfterTriggerBeginQuery();
 
        /*
-        * Call ExecStart to prepare the plan for execution
+        * Call ExecutorStart to prepare the plan for execution
         */
-       ExecutorStart(queryDesc, false, false);
+       ExecutorStart(queryDesc, 0);
 
        /*
         * Run the plan to completion.
@@ -153,7 +202,7 @@ ProcessQuery(Query *parsetree,
        {
                Oid                     lastOid;
 
-               switch (operation)
+               switch (queryDesc->operation)
                {
                        case CMD_SELECT:
                                strcpy(completionTag, "SELECT");
@@ -164,7 +213,7 @@ ProcessQuery(Query *parsetree,
                                else
                                        lastOid = InvalidOid;
                                snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
-                               "INSERT %u %u", lastOid, queryDesc->estate->es_processed);
+                                  "INSERT %u %u", lastOid, queryDesc->estate->es_processed);
                                break;
                        case CMD_UPDATE:
                                snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
@@ -180,6 +229,11 @@ ProcessQuery(Query *parsetree,
                }
        }
 
+       /* Now take care of any queued AFTER triggers */
+       AfterTriggerEndQuery(queryDesc->estate);
+
+       PopActiveSnapshot();
+
        /*
         * Now, we close down all the scans and free allocated resources.
         */
@@ -190,34 +244,200 @@ ProcessQuery(Query *parsetree,
 
 /*
  * ChoosePortalStrategy
- *             Select portal execution strategy given the intended query list.
+ *             Select portal execution strategy given the intended statement list.
+ *
+ * The list elements can be Querys, PlannedStmts, or utility statements.
+ * That's more general than portals need, but plancache.c uses this too.
  *
  * See the comments in portal.h.
  */
 PortalStrategy
-ChoosePortalStrategy(List *parseTrees)
+ChoosePortalStrategy(List *stmts)
 {
-       PortalStrategy strategy;
+       int                     nSetTag;
+       ListCell   *lc;
+
+       /*
+        * PORTAL_ONE_SELECT and PORTAL_UTIL_SELECT need only consider the
+        * single-statement case, since there are no rewrite rules that can add
+        * auxiliary queries to a SELECT or a utility command.
+        */
+       if (list_length(stmts) == 1)
+       {
+               Node       *stmt = (Node *) linitial(stmts);
+
+               if (IsA(stmt, Query))
+               {
+                       Query      *query = (Query *) stmt;
+
+                       if (query->canSetTag)
+                       {
+                               if (query->commandType == CMD_SELECT &&
+                                       query->utilityStmt == NULL &&
+                                       query->intoClause == NULL)
+                                       return PORTAL_ONE_SELECT;
+                               if (query->commandType == CMD_UTILITY &&
+                                       query->utilityStmt != NULL)
+                               {
+                                       if (UtilityReturnsTuples(query->utilityStmt))
+                                               return PORTAL_UTIL_SELECT;
+                                       /* it can't be ONE_RETURNING, so give up */
+                                       return PORTAL_MULTI_QUERY;
+                               }
+                       }
+               }
+               else if (IsA(stmt, PlannedStmt))
+               {
+                       PlannedStmt *pstmt = (PlannedStmt *) stmt;
 
-       strategy = PORTAL_MULTI_QUERY;          /* default assumption */
+                       if (pstmt->canSetTag)
+                       {
+                               if (pstmt->commandType == CMD_SELECT &&
+                                       pstmt->utilityStmt == NULL &&
+                                       pstmt->intoClause == NULL)
+                                       return PORTAL_ONE_SELECT;
+                       }
+               }
+               else
+               {
+                       /* must be a utility command; assume it's canSetTag */
+                       if (UtilityReturnsTuples(stmt))
+                               return PORTAL_UTIL_SELECT;
+                       /* it can't be ONE_RETURNING, so give up */
+                       return PORTAL_MULTI_QUERY;
+               }
+       }
 
-       if (list_length(parseTrees) == 1)
+       /*
+        * PORTAL_ONE_RETURNING has to allow auxiliary queries added by rewrite.
+        * Choose PORTAL_ONE_RETURNING if there is exactly one canSetTag query and
+        * it has a RETURNING list.
+        */
+       nSetTag = 0;
+       foreach(lc, stmts)
        {
-               Query      *query = (Query *) linitial(parseTrees);
-
-               if (query->commandType == CMD_SELECT &&
-                       query->canSetTag &&
-                       query->into == NULL)
-                       strategy = PORTAL_ONE_SELECT;
-               else if (query->commandType == CMD_UTILITY &&
-                                query->canSetTag &&
-                                query->utilityStmt != NULL)
+               Node       *stmt = (Node *) lfirst(lc);
+
+               if (IsA(stmt, Query))
+               {
+                       Query      *query = (Query *) stmt;
+
+                       if (query->canSetTag)
+                       {
+                               if (++nSetTag > 1)
+                                       return PORTAL_MULTI_QUERY;      /* no need to look further */
+                               if (query->returningList == NIL)
+                                       return PORTAL_MULTI_QUERY;      /* no need to look further */
+                       }
+               }
+               else if (IsA(stmt, PlannedStmt))
                {
-                       if (UtilityReturnsTuples(query->utilityStmt))
-                               strategy = PORTAL_UTIL_SELECT;
+                       PlannedStmt *pstmt = (PlannedStmt *) stmt;
+
+                       if (pstmt->canSetTag)
+                       {
+                               if (++nSetTag > 1)
+                                       return PORTAL_MULTI_QUERY;      /* no need to look further */
+                               if (!pstmt->hasReturning)
+                                       return PORTAL_MULTI_QUERY;      /* no need to look further */
+                       }
                }
+               /* otherwise, utility command, assumed not canSetTag */
        }
-       return strategy;
+       if (nSetTag == 1)
+               return PORTAL_ONE_RETURNING;
+
+       /* Else, it's the general case... */
+       return PORTAL_MULTI_QUERY;
+}
+
+/*
+ * FetchPortalTargetList
+ *             Given a portal that returns tuples, extract the query targetlist.
+ *             Returns NIL if the portal doesn't have a determinable targetlist.
+ *
+ * Note: do not modify the result.
+ */
+List *
+FetchPortalTargetList(Portal portal)
+{
+       /* no point in looking if we determined it doesn't return tuples */
+       if (portal->strategy == PORTAL_MULTI_QUERY)
+               return NIL;
+       /* get the primary statement and find out what it returns */
+       return FetchStatementTargetList(PortalGetPrimaryStmt(portal));
+}
+
+/*
+ * FetchStatementTargetList
+ *             Given a statement that returns tuples, extract the query targetlist.
+ *             Returns NIL if the statement doesn't have a determinable targetlist.
+ *
+ * This can be applied to a Query, a PlannedStmt, or a utility statement.
+ * That's more general than portals need, but plancache.c uses this too.
+ *
+ * Note: do not modify the result.
+ *
+ * XXX be careful to keep this in sync with UtilityReturnsTuples.
+ */
+List *
+FetchStatementTargetList(Node *stmt)
+{
+       if (stmt == NULL)
+               return NIL;
+       if (IsA(stmt, Query))
+       {
+               Query      *query = (Query *) stmt;
+
+               if (query->commandType == CMD_UTILITY &&
+                       query->utilityStmt != NULL)
+               {
+                       /* transfer attention to utility statement */
+                       stmt = query->utilityStmt;
+               }
+               else
+               {
+                       if (query->commandType == CMD_SELECT &&
+                               query->utilityStmt == NULL &&
+                               query->intoClause == NULL)
+                               return query->targetList;
+                       if (query->returningList)
+                               return query->returningList;
+                       return NIL;
+               }
+       }
+       if (IsA(stmt, PlannedStmt))
+       {
+               PlannedStmt *pstmt = (PlannedStmt *) stmt;
+
+               if (pstmt->commandType == CMD_SELECT &&
+                       pstmt->utilityStmt == NULL &&
+                       pstmt->intoClause == NULL)
+                       return pstmt->planTree->targetlist;
+               if (pstmt->hasReturning)
+                       return pstmt->planTree->targetlist;
+               return NIL;
+       }
+       if (IsA(stmt, FetchStmt))
+       {
+               FetchStmt  *fstmt = (FetchStmt *) stmt;
+               Portal          subportal;
+
+               Assert(!fstmt->ismove);
+               subportal = GetPortalByName(fstmt->portalname);
+               Assert(PortalIsValid(subportal));
+               return FetchPortalTargetList(subportal);
+       }
+       if (IsA(stmt, ExecuteStmt))
+       {
+               ExecuteStmt *estmt = (ExecuteStmt *) stmt;
+               PreparedStatement *entry;
+
+               Assert(!estmt->into);
+               entry = FetchPreparedStatement(estmt->name, true);
+               return FetchPreparedStatementTargetList(entry);
+       }
+       return NIL;
 }
 
 /*
@@ -229,111 +449,183 @@ ChoosePortalStrategy(List *parseTrees)
  * the query, they must be passed in here (caller is responsible for
  * giving them appropriate lifetime).
  *
+ * The caller can optionally pass a snapshot to be used; pass InvalidSnapshot
+ * for the normal behavior of setting a new snapshot.  This parameter is
+ * presently ignored for non-PORTAL_ONE_SELECT portals (it's only intended
+ * to be used for cursors).
+ *
  * On return, portal is ready to accept PortalRun() calls, and the result
  * tupdesc (if any) is known.
  */
 void
-PortalStart(Portal portal, ParamListInfo params)
+PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
 {
        Portal          saveActivePortal;
        ResourceOwner saveResourceOwner;
        MemoryContext savePortalContext;
        MemoryContext oldContext;
        QueryDesc  *queryDesc;
+       int                     eflags;
 
        AssertArg(PortalIsValid(portal));
-       AssertState(portal->queryContext != NULL);      /* query defined? */
-       AssertState(portal->status == PORTAL_NEW);      /* else extra PortalStart */
+       AssertState(portal->status == PORTAL_DEFINED);
 
        /*
-        * Set global portal context pointers.  (Should we set QueryContext?)
+        * Set up global portal context pointers.
         */
        saveActivePortal = ActivePortal;
-       ActivePortal = portal;
        saveResourceOwner = CurrentResourceOwner;
-       CurrentResourceOwner = portal->resowner;
        savePortalContext = PortalContext;
-       PortalContext = PortalGetHeapMemory(portal);
+       PG_TRY();
+       {
+               ActivePortal = portal;
+               CurrentResourceOwner = portal->resowner;
+               PortalContext = PortalGetHeapMemory(portal);
 
-       oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
+               oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
 
-       /* Must remember portal param list, if any */
-       portal->portalParams = params;
+               /* Must remember portal param list, if any */
+               portal->portalParams = params;
 
-       /*
-        * Determine the portal execution strategy
-        */
-       portal->strategy = ChoosePortalStrategy(portal->parseTrees);
+               /*
+                * Determine the portal execution strategy
+                */
+               portal->strategy = ChoosePortalStrategy(portal->stmts);
 
-       /*
-        * Fire her up according to the strategy
-        */
-       switch (portal->strategy)
-       {
-               case PORTAL_ONE_SELECT:
+               /*
+                * Fire her up according to the strategy
+                */
+               switch (portal->strategy)
+               {
+                       case PORTAL_ONE_SELECT:
 
-                       /*
-                        * Must set query snapshot before starting executor.
-                        */
-                       SetQuerySnapshot();
+                               /* Must set snapshot before starting executor. */
+                               if (snapshot)
+                                       PushActiveSnapshot(snapshot);
+                               else
+                                       PushActiveSnapshot(GetTransactionSnapshot());
 
-                       /*
-                        * Create QueryDesc in portal's context; for the moment, set
-                        * the destination to None.
-                        */
-                       queryDesc = CreateQueryDesc((Query *) linitial(portal->parseTrees),
-                                                                               (Plan *) linitial(portal->planTrees),
-                                                                               None_Receiver,
-                                                                               params,
-                                                                               false);
+                               /*
+                                * Create QueryDesc in portal's context; for the moment, set
+                                * the destination to DestNone.
+                                */
+                               queryDesc = CreateQueryDesc((PlannedStmt *) linitial(portal->stmts),
+                                                                                       portal->sourceText,
+                                                                                       GetActiveSnapshot(),
+                                                                                       InvalidSnapshot,
+                                                                                       None_Receiver,
+                                                                                       params,
+                                                                                       0);
 
-                       /*
-                        * Call ExecStart to prepare the plan for execution
-                        */
-                       ExecutorStart(queryDesc, false, false);
+                               /*
+                                * We do *not* call AfterTriggerBeginQuery() here.      We assume
+                                * that a SELECT cannot queue any triggers.  It would be messy
+                                * to support triggers since the execution of the portal may
+                                * be interleaved with other queries.
+                                */
 
-                       /*
-                        * This tells PortalCleanup to shut down the executor
-                        */
-                       portal->queryDesc = queryDesc;
+                               /*
+                                * If it's a scrollable cursor, executor needs to support
+                                * REWIND and backwards scan.
+                                */
+                               if (portal->cursorOptions & CURSOR_OPT_SCROLL)
+                                       eflags = EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD;
+                               else
+                                       eflags = 0; /* default run-to-completion flags */
 
-                       /*
-                        * Remember tuple descriptor (computed by ExecutorStart)
-                        */
-                       portal->tupDesc = queryDesc->tupDesc;
+                               /*
+                                * Call ExecutorStart to prepare the plan for execution
+                                */
+                               ExecutorStart(queryDesc, eflags);
 
-                       /*
-                        * Reset cursor position data to "start of query"
-                        */
-                       portal->atStart = true;
-                       portal->atEnd = false;          /* allow fetches */
-                       portal->portalPos = 0;
-                       portal->posOverflow = false;
-                       break;
+                               /*
+                                * This tells PortalCleanup to shut down the executor
+                                */
+                               portal->queryDesc = queryDesc;
 
-               case PORTAL_UTIL_SELECT:
+                               /*
+                                * Remember tuple descriptor (computed by ExecutorStart)
+                                */
+                               portal->tupDesc = queryDesc->tupDesc;
 
-                       /*
-                        * We don't set query snapshot here, because PortalRunUtility
-                        * will take care of it.
-                        */
-                       portal->tupDesc =
-                               UtilityTupleDescriptor(((Query *) linitial(portal->parseTrees))->utilityStmt);
+                               /*
+                                * Reset cursor position data to "start of query"
+                                */
+                               portal->atStart = true;
+                               portal->atEnd = false;  /* allow fetches */
+                               portal->portalPos = 0;
+                               portal->posOverflow = false;
 
-                       /*
-                        * Reset cursor position data to "start of query"
-                        */
-                       portal->atStart = true;
-                       portal->atEnd = false;          /* allow fetches */
-                       portal->portalPos = 0;
-                       portal->posOverflow = false;
-                       break;
+                               PopActiveSnapshot();
+                               break;
 
-               case PORTAL_MULTI_QUERY:
-                       /* Need do nothing now */
-                       portal->tupDesc = NULL;
-                       break;
+                       case PORTAL_ONE_RETURNING:
+
+                               /*
+                                * We don't start the executor until we are told to run the
+                                * portal.      We do need to set up the result tupdesc.
+                                */
+                               {
+                                       PlannedStmt *pstmt;
+
+                                       pstmt = (PlannedStmt *) PortalGetPrimaryStmt(portal);
+                                       Assert(IsA(pstmt, PlannedStmt));
+                                       Assert(pstmt->hasReturning);
+                                       portal->tupDesc =
+                                               ExecCleanTypeFromTL(pstmt->planTree->targetlist,
+                                                                                       false);
+                               }
+
+                               /*
+                                * Reset cursor position data to "start of query"
+                                */
+                               portal->atStart = true;
+                               portal->atEnd = false;  /* allow fetches */
+                               portal->portalPos = 0;
+                               portal->posOverflow = false;
+                               break;
+
+                       case PORTAL_UTIL_SELECT:
+
+                               /*
+                                * We don't set snapshot here, because PortalRunUtility will
+                                * take care of it if needed.
+                                */
+                               {
+                                       Node       *ustmt = PortalGetPrimaryStmt(portal);
+
+                                       Assert(!IsA(ustmt, PlannedStmt));
+                                       portal->tupDesc = UtilityTupleDescriptor(ustmt);
+                               }
+
+                               /*
+                                * Reset cursor position data to "start of query"
+                                */
+                               portal->atStart = true;
+                               portal->atEnd = false;  /* allow fetches */
+                               portal->portalPos = 0;
+                               portal->posOverflow = false;
+                               break;
+
+                       case PORTAL_MULTI_QUERY:
+                               /* Need do nothing now */
+                               portal->tupDesc = NULL;
+                               break;
+               }
+       }
+       PG_CATCH();
+       {
+               /* Uncaught error while executing portal: mark it dead */
+               portal->status = PORTAL_FAILED;
+
+               /* Restore global vars and propagate error */
+               ActivePortal = saveActivePortal;
+               CurrentResourceOwner = saveResourceOwner;
+               PortalContext = savePortalContext;
+
+               PG_RE_THROW();
        }
+       PG_END_TRY();
 
        MemoryContextSwitchTo(oldContext);
 
@@ -349,8 +641,8 @@ PortalStart(Portal portal, ParamListInfo params)
  *             Select the format codes for a portal's output.
  *
  * This must be run after PortalStart for a portal that will be read by
- * a Remote or RemoteExecute destination.  It is not presently needed for
- * other destination types.
+ * a DestRemote or DestRemoteExecute destination.  It is not presently needed
+ * for other destination types.
  *
  * formats[] is the client format request, as per Bind message conventions.
  */
@@ -402,6 +694,9 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
  * interpreted as "all rows".  Note that count is ignored in multi-query
  * situations, where we always run the portal to completion.
  *
+ * isTopLevel: true if query is being executed at backend "top level"
+ * (that is, directly from a client command message)
+ *
  * dest: where to send output of primary (canSetTag) query
  *
  * altdest: where to send output of non-primary queries
@@ -414,31 +709,33 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
  * suspended due to exhaustion of the count parameter.
  */
 bool
-PortalRun(Portal portal, long count,
+PortalRun(Portal portal, long count, bool isTopLevel,
                  DestReceiver *dest, DestReceiver *altdest,
                  char *completionTag)
 {
        bool            result;
+       ResourceOwner saveTopTransactionResourceOwner;
+       MemoryContext saveTopTransactionContext;
        Portal          saveActivePortal;
        ResourceOwner saveResourceOwner;
        MemoryContext savePortalContext;
-       MemoryContext saveQueryContext;
-       MemoryContext oldContext;
+       MemoryContext saveMemoryContext;
 
        AssertArg(PortalIsValid(portal));
 
+       TRACE_POSTGRESQL_QUERY_EXECUTE_START();
+
        /* Initialize completion tag to empty string */
        if (completionTag)
                completionTag[0] = '\0';
 
        if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
        {
-               ereport(DEBUG3,
-                       (errmsg_internal("PortalRun")));
+               elog(DEBUG3, "PortalRun");
                /* PORTAL_MULTI_QUERY logs its own stats per query */
                ResetUsage();
        }
-       
+
        /*
         * Check for improper portal use, and mark portal active.
         */
@@ -449,108 +746,142 @@ PortalRun(Portal portal, long count,
        portal->status = PORTAL_ACTIVE;
 
        /*
-        * Set global portal context pointers.
+        * Set up global portal context pointers.
+        *
+        * We have to play a special game here to support utility commands like
+        * VACUUM and CLUSTER, which internally start and commit transactions.
+        * When we are called to execute such a command, CurrentResourceOwner will
+        * be pointing to the TopTransactionResourceOwner --- which will be
+        * destroyed and replaced in the course of the internal commit and
+        * restart.  So we need to be prepared to restore it as pointing to the
+        * exit-time TopTransactionResourceOwner.  (Ain't that ugly?  This idea of
+        * internally starting whole new transactions is not good.)
+        * CurrentMemoryContext has a similar problem, but the other pointers we
+        * save here will be NULL or pointing to longer-lived objects.
         */
+       saveTopTransactionResourceOwner = TopTransactionResourceOwner;
+       saveTopTransactionContext = TopTransactionContext;
        saveActivePortal = ActivePortal;
-       ActivePortal = portal;
        saveResourceOwner = CurrentResourceOwner;
-       CurrentResourceOwner = portal->resowner;
        savePortalContext = PortalContext;
-       PortalContext = PortalGetHeapMemory(portal);
-       saveQueryContext = QueryContext;
-       QueryContext = portal->queryContext;
+       saveMemoryContext = CurrentMemoryContext;
+       PG_TRY();
+       {
+               ActivePortal = portal;
+               CurrentResourceOwner = portal->resowner;
+               PortalContext = PortalGetHeapMemory(portal);
 
-       oldContext = MemoryContextSwitchTo(PortalContext);
+               MemoryContextSwitchTo(PortalContext);
 
-       switch (portal->strategy)
-       {
-               case PORTAL_ONE_SELECT:
-                       (void) PortalRunSelect(portal, true, count, dest);
-                       /* we know the query is supposed to set the tag */
-                       if (completionTag && portal->commandTag)
-                               strcpy(completionTag, portal->commandTag);
+               switch (portal->strategy)
+               {
+                       case PORTAL_ONE_SELECT:
+                               (void) PortalRunSelect(portal, true, count, dest);
 
-                       /* Mark portal not active */
-                       portal->status = PORTAL_READY;
+                               /* we know the query is supposed to set the tag */
+                               if (completionTag && portal->commandTag)
+                                       strcpy(completionTag, portal->commandTag);
 
-                       /*
-                        * Since it's a forward fetch, say DONE iff atEnd is now true.
-                        */
-                       result = portal->atEnd;
-                       break;
+                               /* Mark portal not active */
+                               portal->status = PORTAL_READY;
 
-               case PORTAL_UTIL_SELECT:
+                               /*
+                                * Since it's a forward fetch, say DONE iff atEnd is now true.
+                                */
+                               result = portal->atEnd;
+                               break;
 
-                       /*
-                        * If we have not yet run the utility statement, do so,
-                        * storing its results in the portal's tuplestore.
-                        */
-                       if (!portal->portalUtilReady)
-                       {
-                               DestReceiver *treceiver;
-
-                               PortalCreateHoldStore(portal);
-                               treceiver = CreateDestReceiver(Tuplestore, portal);
-                               PortalRunUtility(portal, linitial(portal->parseTrees),
-                                                                treceiver, NULL);
-                               (*treceiver->rDestroy) (treceiver);
-                               portal->portalUtilReady = true;
-                       }
+                       case PORTAL_ONE_RETURNING:
+                       case PORTAL_UTIL_SELECT:
 
-                       /*
-                        * Now fetch desired portion of results.
-                        */
-                       (void) PortalRunSelect(portal, true, count, dest);
+                               /*
+                                * If we have not yet run the command, do so, storing its
+                                * results in the portal's tuplestore.
+                                */
+                               if (!portal->holdStore)
+                                       FillPortalStore(portal, isTopLevel);
 
-                       /*
-                        * We know the query is supposed to set the tag; we assume
-                        * only the default tag is needed.
-                        */
-                       if (completionTag && portal->commandTag)
-                               strcpy(completionTag, portal->commandTag);
+                               /*
+                                * Now fetch desired portion of results.
+                                */
+                               (void) PortalRunSelect(portal, true, count, dest);
 
-                       /* Mark portal not active */
-                       portal->status = PORTAL_READY;
+                               /* we know the query is supposed to set the tag */
+                               if (completionTag && portal->commandTag)
+                                       strcpy(completionTag, portal->commandTag);
 
-                       /*
-                        * Since it's a forward fetch, say DONE iff atEnd is now true.
-                        */
-                       result = portal->atEnd;
-                       break;
+                               /* Mark portal not active */
+                               portal->status = PORTAL_READY;
 
-               case PORTAL_MULTI_QUERY:
-                       PortalRunMulti(portal, dest, altdest, completionTag);
+                               /*
+                                * Since it's a forward fetch, say DONE iff atEnd is now true.
+                                */
+                               result = portal->atEnd;
+                               break;
 
-                       /* Prevent portal's commands from being re-executed */
-                       portal->status = PORTAL_DONE;
+                       case PORTAL_MULTI_QUERY:
+                               PortalRunMulti(portal, isTopLevel,
+                                                          dest, altdest, completionTag);
 
-                       /* Always complete at end of RunMulti */
-                       result = true;
-                       break;
+                               /* Prevent portal's commands from being re-executed */
+                               portal->status = PORTAL_DONE;
 
-               default:
-                       elog(ERROR, "unrecognized portal strategy: %d",
-                                (int) portal->strategy);
-                       result = false;         /* keep compiler quiet */
-                       break;
+                               /* Always complete at end of RunMulti */
+                               result = true;
+                               break;
+
+                       default:
+                               elog(ERROR, "unrecognized portal strategy: %d",
+                                        (int) portal->strategy);
+                               result = false; /* keep compiler quiet */
+                               break;
+               }
        }
+       PG_CATCH();
+       {
+               /* Uncaught error while executing portal: mark it dead */
+               portal->status = PORTAL_FAILED;
 
-       MemoryContextSwitchTo(oldContext);
+               /* Restore global vars and propagate error */
+               if (saveMemoryContext == saveTopTransactionContext)
+                       MemoryContextSwitchTo(TopTransactionContext);
+               else
+                       MemoryContextSwitchTo(saveMemoryContext);
+               ActivePortal = saveActivePortal;
+               if (saveResourceOwner == saveTopTransactionResourceOwner)
+                       CurrentResourceOwner = TopTransactionResourceOwner;
+               else
+                       CurrentResourceOwner = saveResourceOwner;
+               PortalContext = savePortalContext;
 
+               PG_RE_THROW();
+       }
+       PG_END_TRY();
+
+       if (saveMemoryContext == saveTopTransactionContext)
+               MemoryContextSwitchTo(TopTransactionContext);
+       else
+               MemoryContextSwitchTo(saveMemoryContext);
        ActivePortal = saveActivePortal;
-       CurrentResourceOwner = saveResourceOwner;
+       if (saveResourceOwner == saveTopTransactionResourceOwner)
+               CurrentResourceOwner = TopTransactionResourceOwner;
+       else
+               CurrentResourceOwner = saveResourceOwner;
        PortalContext = savePortalContext;
-       QueryContext = saveQueryContext;
 
        if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
                ShowUsage("EXECUTOR STATISTICS");
 
+       TRACE_POSTGRESQL_QUERY_EXECUTE_DONE();
+
        return result;
 }
 
 /*
  * PortalRunSelect
- *             Execute a portal's query in SELECT cases (also UTIL_SELECT).
+ *             Execute a portal's query in PORTAL_ONE_SELECT mode, and also
+ *             when fetching from a completed holdStore in PORTAL_ONE_RETURNING
+ *             and PORTAL_UTIL_SELECT cases.
  *
  * This handles simple N-rows-forward-or-backward cases.  For more complex
  * nonsequential access to a portal, see PortalRunFetch.
@@ -575,8 +906,8 @@ PortalRunSelect(Portal portal,
        uint32          nprocessed;
 
        /*
-        * NB: queryDesc will be NULL if we are fetching from a held cursor or
-        * completed utility query; can't use it in that path.
+        * NB: queryDesc will be NULL if we are fetching from a held cursor or a
+        * completed utility query; can't use it in that path.
         */
        queryDesc = PortalGetQueryDesc(portal);
 
@@ -585,7 +916,7 @@ PortalRunSelect(Portal portal,
 
        /*
         * Force the queryDesc destination to the right thing.  This supports
-        * MOVE, for example, which will pass in dest = None.  This is okay to
+        * MOVE, for example, which will pass in dest = DestNone.  This is okay to
         * change as long as we do it on every fetch.  (The Executor must not
         * assume that dest never changes.)
         */
@@ -593,15 +924,15 @@ PortalRunSelect(Portal portal,
                queryDesc->dest = dest;
 
        /*
-        * Determine which direction to go in, and check to see if we're
-        * already at the end of the available tuples in that direction.  If
-        * so, set the direction to NoMovement to avoid trying to fetch any
-        * tuples.      (This check exists because not all plan node types are
-        * robust about being called again if they've already returned NULL
-        * once.)  Then call the executor (we must not skip this, because the
-        * destination needs to see a setup and shutdown even if no tuples are
-        * available).  Finally, update the portal position state depending on
-        * the number of tuples that were retrieved.
+        * Determine which direction to go in, and check to see if we're already
+        * at the end of the available tuples in that direction.  If so, set the
+        * direction to NoMovement to avoid trying to fetch any tuples.  (This
+        * check exists because not all plan node types are robust about being
+        * called again if they've already returned NULL once.)  Then call the
+        * executor (we must not skip this, because the destination needs to see a
+        * setup and shutdown even if no tuples are available).  Finally, update
+        * the portal position state depending on the number of tuples that were
+        * retrieved.
         */
        if (forward)
        {
@@ -618,11 +949,13 @@ PortalRunSelect(Portal portal,
                        nprocessed = RunFromStore(portal, direction, count, dest);
                else
                {
+                       PushActiveSnapshot(queryDesc->snapshot);
                        ExecutorRun(queryDesc, direction, count);
                        nprocessed = queryDesc->estate->es_processed;
+                       PopActiveSnapshot();
                }
 
-               if (direction != NoMovementScanDirection)
+               if (!ScanDirectionIsNoMovement(direction))
                {
                        long            oldPos;
 
@@ -659,11 +992,13 @@ PortalRunSelect(Portal portal,
                        nprocessed = RunFromStore(portal, direction, count, dest);
                else
                {
+                       PushActiveSnapshot(queryDesc->snapshot);
                        ExecutorRun(queryDesc, direction, count);
                        nprocessed = queryDesc->estate->es_processed;
+                       PopActiveSnapshot();
                }
 
-               if (direction != NoMovementScanDirection)
+               if (!ScanDirectionIsNoMovement(direction))
                {
                        if (nprocessed > 0 && portal->atEnd)
                        {
@@ -693,6 +1028,58 @@ PortalRunSelect(Portal portal,
        return nprocessed;
 }
 
+/*
+ * FillPortalStore
+ *             Run the query and load result tuples into the portal's tuple store.
+ *
+ * This is used for PORTAL_ONE_RETURNING and PORTAL_UTIL_SELECT cases only.
+ */
+static void
+FillPortalStore(Portal portal, bool isTopLevel)
+{
+       DestReceiver *treceiver;
+       char            completionTag[COMPLETION_TAG_BUFSIZE];
+
+       PortalCreateHoldStore(portal);
+       treceiver = CreateDestReceiver(DestTuplestore);
+       SetTuplestoreDestReceiverParams(treceiver,
+                                                                       portal->holdStore,
+                                                                       portal->holdContext,
+                                                                       false);
+
+       completionTag[0] = '\0';
+
+       switch (portal->strategy)
+       {
+               case PORTAL_ONE_RETURNING:
+
+                       /*
+                        * Run the portal to completion just as for the default
+                        * MULTI_QUERY case, but send the primary query's output to the
+                        * tuplestore. Auxiliary query outputs are discarded.
+                        */
+                       PortalRunMulti(portal, isTopLevel,
+                                                  treceiver, None_Receiver, completionTag);
+                       break;
+
+               case PORTAL_UTIL_SELECT:
+                       PortalRunUtility(portal, (Node *) linitial(portal->stmts),
+                                                        isTopLevel, treceiver, completionTag);
+                       break;
+
+               default:
+                       elog(ERROR, "unsupported portal strategy: %d",
+                                (int) portal->strategy);
+                       break;
+       }
+
+       /* Override default completion tag with actual command result */
+       if (completionTag[0] != '\0')
+               portal->commandTag = pstrdup(completionTag);
+
+       (*treceiver->rDestroy) (treceiver);
+}
+
 /*
  * RunFromStore
  *             Fetch tuples from the portal's tuple store.
@@ -710,42 +1097,43 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
                         DestReceiver *dest)
 {
        long            current_tuple_count = 0;
+       TupleTableSlot *slot;
+
+       slot = MakeSingleTupleTableSlot(portal->tupDesc);
 
        (*dest->rStartup) (dest, CMD_SELECT, portal->tupDesc);
 
-       if (direction == NoMovementScanDirection)
+       if (ScanDirectionIsNoMovement(direction))
        {
                /* do nothing except start/stop the destination */
        }
        else
        {
-               bool            forward = (direction == ForwardScanDirection);
+               bool            forward = ScanDirectionIsForward(direction);
 
                for (;;)
                {
                        MemoryContext oldcontext;
-                       HeapTuple       tup;
-                       bool            should_free;
+                       bool            ok;
 
                        oldcontext = MemoryContextSwitchTo(portal->holdContext);
 
-                       tup = tuplestore_getheaptuple(portal->holdStore, forward,
-                                                                                 &should_free);
+                       ok = tuplestore_gettupleslot(portal->holdStore, forward, false,
+                                                                                slot);
 
                        MemoryContextSwitchTo(oldcontext);
 
-                       if (tup == NULL)
+                       if (!ok)
                                break;
 
-                       (*dest->receiveTuple) (tup, portal->tupDesc, dest);
+                       (*dest->receiveSlot) (slot, dest);
 
-                       if (should_free)
-                               pfree(tup);
+                       ExecClearTuple(slot);
 
                        /*
-                        * check our tuple count.. if we've processed the proper
-                        * number then quit, else loop again and process more tuples.
-                        * Zero count means no limit.
+                        * check our tuple count.. if we've processed the proper number
+                        * then quit, else loop again and process more tuples. Zero count
+                        * means no limit.
                         */
                        current_tuple_count++;
                        if (count && count == current_tuple_count)
@@ -755,6 +1143,8 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
 
        (*dest->rShutdown) (dest);
 
+       ExecDropSingleTupleTableSlot(slot);
+
        return (uint32) current_tuple_count;
 }
 
@@ -763,31 +1153,28 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
  *             Execute a utility statement inside a portal.
  */
 static void
-PortalRunUtility(Portal portal, Query *query,
+PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
                                 DestReceiver *dest, char *completionTag)
 {
-       Node       *utilityStmt = query->utilityStmt;
+       bool            active_snapshot_set;
 
-       ereport(DEBUG3,
-                       (errmsg_internal("ProcessUtility")));
+       elog(DEBUG3, "ProcessUtility");
 
        /*
-        * Set snapshot if utility stmt needs one.      Most reliable way to do
-        * this seems to be to enumerate those that do not need one; this is a
-        * short list.  Transaction control, LOCK, and SET must *not* set a
-        * snapshot since they need to be executable at the start of a
-        * serializable transaction without freezing a snapshot.  By extension
-        * we allow SHOW not to set a snapshot.  The other stmts listed are
-        * just efficiency hacks.  Beware of listing anything that can modify
-        * the database --- if, say, it has to update an index with
-        * expressions that invoke user-defined functions, then it had better
-        * have a snapshot.
+        * Set snapshot if utility stmt needs one.      Most reliable way to do this
+        * seems to be to enumerate those that do not need one; this is a short
+        * list.  Transaction control, LOCK, and SET must *not* set a snapshot
+        * since they need to be executable at the start of a serializable
+        * transaction without freezing a snapshot.  By extension we allow SHOW
+        * not to set a snapshot.  The other stmts listed are just efficiency
+        * hacks.  Beware of listing anything that can modify the database --- if,
+        * say, it has to update an index with expressions that invoke
+        * user-defined functions, then it had better have a snapshot.
         */
        if (!(IsA(utilityStmt, TransactionStmt) ||
                  IsA(utilityStmt, LockStmt) ||
                  IsA(utilityStmt, VariableSetStmt) ||
                  IsA(utilityStmt, VariableShowStmt) ||
-                 IsA(utilityStmt, VariableResetStmt) ||
                  IsA(utilityStmt, ConstraintsSetStmt) ||
        /* efficiency hacks from here down */
                  IsA(utilityStmt, FetchStmt) ||
@@ -795,119 +1182,128 @@ PortalRunUtility(Portal portal, Query *query,
                  IsA(utilityStmt, NotifyStmt) ||
                  IsA(utilityStmt, UnlistenStmt) ||
                  IsA(utilityStmt, CheckPointStmt)))
-               SetQuerySnapshot();
-
-       if (query->canSetTag)
        {
-               /* utility statement can override default tag string */
-               ProcessUtility(utilityStmt, dest, completionTag);
-               if (completionTag && completionTag[0] == '\0' && portal->commandTag)
-                       strcpy(completionTag, portal->commandTag);      /* use the default */
+               PushActiveSnapshot(GetTransactionSnapshot());
+               active_snapshot_set = true;
        }
        else
-       {
-               /* utility added by rewrite cannot set tag */
-               ProcessUtility(utilityStmt, dest, NULL);
-       }
+               active_snapshot_set = false;
+
+       ProcessUtility(utilityStmt,
+                                  portal->sourceText,
+                                  portal->portalParams,
+                                  isTopLevel,
+                                  dest,
+                                  completionTag);
 
        /* Some utility statements may change context on us */
        MemoryContextSwitchTo(PortalGetHeapMemory(portal));
+
+       /*
+        * Some utility commands may pop the ActiveSnapshot stack from under us,
+        * so we only pop the stack if we actually see a snapshot set.  Note that
+        * the set of utility commands that do this must be the same set
+        * disallowed to run inside a transaction; otherwise, we could be popping
+        * a snapshot that belongs to some other operation.
+        */
+       if (active_snapshot_set && ActiveSnapshotSet())
+               PopActiveSnapshot();
 }
 
 /*
  * PortalRunMulti
- *             Execute a portal's queries in the general case (multi queries).
+ *             Execute a portal's queries in the general case (multi queries
+ *             or non-SELECT-like queries)
  */
 static void
-PortalRunMulti(Portal portal,
+PortalRunMulti(Portal portal, bool isTopLevel,
                           DestReceiver *dest, DestReceiver *altdest,
                           char *completionTag)
 {
-       ListCell   *querylist_item;
-       ListCell   *planlist_item;
+       ListCell   *stmtlist_item;
 
        /*
-        * If the destination is RemoteExecute, change to None.  The reason is
-        * that the client won't be expecting any tuples, and indeed has no
-        * way to know what they are, since there is no provision for Describe
-        * to send a RowDescription message when this portal execution
-        * strategy is in effect.  This presently will only affect SELECT
-        * commands added to non-SELECT queries by rewrite rules: such
-        * commands will be executed, but the results will be discarded unless
-        * you use "simple Query" protocol.
+        * If the destination is DestRemoteExecute, change to DestNone.  The
+        * reason is that the client won't be expecting any tuples, and indeed has
+        * no way to know what they are, since there is no provision for Describe
+        * to send a RowDescription message when this portal execution strategy is
+        * in effect.  This presently will only affect SELECT commands added to
+        * non-SELECT queries by rewrite rules: such commands will be executed,
+        * but the results will be discarded unless you use "simple Query"
+        * protocol.
         */
-       if (dest->mydest == RemoteExecute)
+       if (dest->mydest == DestRemoteExecute)
                dest = None_Receiver;
-       if (altdest->mydest == RemoteExecute)
+       if (altdest->mydest == DestRemoteExecute)
                altdest = None_Receiver;
 
        /*
-        * Loop to handle the individual queries generated from a single
-        * parsetree by analysis and rewrite.
+        * Loop to handle the individual queries generated from a single parsetree
+        * by analysis and rewrite.
         */
-       forboth(querylist_item, portal->parseTrees,
-                       planlist_item, portal->planTrees)
+       foreach(stmtlist_item, portal->stmts)
        {
-               Query      *query = (Query *) lfirst(querylist_item);
-               Plan       *plan = (Plan *) lfirst(planlist_item);
+               Node       *stmt = (Node *) lfirst(stmtlist_item);
 
                /*
                 * If we got a cancel signal in prior command, quit
                 */
                CHECK_FOR_INTERRUPTS();
 
-               if (query->commandType == CMD_UTILITY)
-               {
-                       /*
-                        * process utility functions (create, destroy, etc..)
-                        */
-                       Assert(plan == NULL);
-
-                       PortalRunUtility(portal, query,
-                                                        query->canSetTag ? dest : altdest,
-                                                        completionTag);
-               }
-               else
+               if (IsA(stmt, PlannedStmt) &&
+                       ((PlannedStmt *) stmt)->utilityStmt == NULL)
                {
                        /*
                         * process a plannable query.
                         */
-                       ereport(DEBUG3,
-                                       (errmsg_internal("ProcessQuery")));
+                       PlannedStmt *pstmt = (PlannedStmt *) stmt;
 
-                       /* Must always set snapshot for plannable queries */
-                       SetQuerySnapshot();
+                       TRACE_POSTGRESQL_QUERY_EXECUTE_START();
 
-                       /*
-                        * execute the plan
-                        */
                        if (log_executor_stats)
                                ResetUsage();
 
-                       if (query->canSetTag)
+                       if (pstmt->canSetTag)
                        {
                                /* statement can set tag string */
-                               ProcessQuery(query, plan,
+                               ProcessQuery(pstmt,
+                                                        portal->sourceText,
                                                         portal->portalParams,
                                                         dest, completionTag);
                        }
                        else
                        {
                                /* stmt added by rewrite cannot set tag */
-                               ProcessQuery(query, plan,
+                               ProcessQuery(pstmt,
+                                                        portal->sourceText,
                                                         portal->portalParams,
                                                         altdest, NULL);
                        }
 
                        if (log_executor_stats)
                                ShowUsage("EXECUTOR STATISTICS");
+
+                       TRACE_POSTGRESQL_QUERY_EXECUTE_DONE();
+               }
+               else
+               {
+                       /*
+                        * process utility functions (create, destroy, etc..)
+                        *
+                        * These are assumed canSetTag if they're the only stmt in the
+                        * portal.
+                        */
+                       if (list_length(portal->stmts) == 1)
+                               PortalRunUtility(portal, stmt, isTopLevel, dest, completionTag);
+                       else
+                               PortalRunUtility(portal, stmt, isTopLevel, altdest, NULL);
                }
 
                /*
-                * Increment command counter between queries, but not after the
-                * last one.
+                * Increment command counter between queries, but not after the last
+                * one.
                 */
-               if (planlist_item != NULL)
+               if (lnext(stmtlist_item) != NULL)
                        CommandCounterIncrement();
 
                /*
@@ -919,12 +1315,12 @@ PortalRunMulti(Portal portal,
        }
 
        /*
-        * If a command completion tag was supplied, use it.  Otherwise use
-        * the portal's commandTag as the default completion tag.
+        * If a command completion tag was supplied, use it.  Otherwise use the
+        * portal's commandTag as the default completion tag.
         *
         * Exception: clients will expect INSERT/UPDATE/DELETE tags to have
-        * counts, so fake something up if necessary.  (This could happen if
-        * the original query was replaced by a DO INSTEAD rule.)
+        * counts, so fake something up if necessary.  (This could happen if the
+        * original query was replaced by a DO INSTEAD rule.)
         */
        if (completionTag && completionTag[0] == '\0')
        {
@@ -943,6 +1339,8 @@ PortalRunMulti(Portal portal,
  * PortalRunFetch
  *             Variant form of PortalRun that supports SQL FETCH directions.
  *
+ * Note: we presently assume that no callers of this want isTopLevel = true.
+ *
  * Returns number of rows processed (suitable for use in result tag)
  */
 long
@@ -955,7 +1353,6 @@ PortalRunFetch(Portal portal,
        Portal          saveActivePortal;
        ResourceOwner saveResourceOwner;
        MemoryContext savePortalContext;
-       MemoryContext saveQueryContext;
        MemoryContext oldContext;
 
        AssertArg(PortalIsValid(portal));
@@ -970,30 +1367,60 @@ PortalRunFetch(Portal portal,
        portal->status = PORTAL_ACTIVE;
 
        /*
-        * Set global portal context pointers.
+        * Set up global portal context pointers.
         */
        saveActivePortal = ActivePortal;
-       ActivePortal = portal;
        saveResourceOwner = CurrentResourceOwner;
-       CurrentResourceOwner = portal->resowner;
        savePortalContext = PortalContext;
-       PortalContext = PortalGetHeapMemory(portal);
-       saveQueryContext = QueryContext;
-       QueryContext = portal->queryContext;
+       PG_TRY();
+       {
+               ActivePortal = portal;
+               CurrentResourceOwner = portal->resowner;
+               PortalContext = PortalGetHeapMemory(portal);
 
-       oldContext = MemoryContextSwitchTo(PortalContext);
+               oldContext = MemoryContextSwitchTo(PortalContext);
 
-       switch (portal->strategy)
+               switch (portal->strategy)
+               {
+                       case PORTAL_ONE_SELECT:
+                               result = DoPortalRunFetch(portal, fdirection, count, dest);
+                               break;
+
+                       case PORTAL_ONE_RETURNING:
+                       case PORTAL_UTIL_SELECT:
+
+                               /*
+                                * If we have not yet run the command, do so, storing its
+                                * results in the portal's tuplestore.
+                                */
+                               if (!portal->holdStore)
+                                       FillPortalStore(portal, false /* isTopLevel */ );
+
+                               /*
+                                * Now fetch desired portion of results.
+                                */
+                               result = DoPortalRunFetch(portal, fdirection, count, dest);
+                               break;
+
+                       default:
+                               elog(ERROR, "unsupported portal strategy");
+                               result = 0;             /* keep compiler quiet */
+                               break;
+               }
+       }
+       PG_CATCH();
        {
-               case PORTAL_ONE_SELECT:
-                       result = DoPortalRunFetch(portal, fdirection, count, dest);
-                       break;
+               /* Uncaught error while executing portal: mark it dead */
+               portal->status = PORTAL_FAILED;
 
-               default:
-                       elog(ERROR, "unsupported portal strategy");
-                       result = 0;                     /* keep compiler quiet */
-                       break;
+               /* Restore global vars and propagate error */
+               ActivePortal = saveActivePortal;
+               CurrentResourceOwner = saveResourceOwner;
+               PortalContext = savePortalContext;
+
+               PG_RE_THROW();
        }
+       PG_END_TRY();
 
        MemoryContextSwitchTo(oldContext);
 
@@ -1003,7 +1430,6 @@ PortalRunFetch(Portal portal,
        ActivePortal = saveActivePortal;
        CurrentResourceOwner = saveResourceOwner;
        PortalContext = savePortalContext;
-       QueryContext = saveQueryContext;
 
        return result;
 }
@@ -1022,7 +1448,9 @@ DoPortalRunFetch(Portal portal,
 {
        bool            forward;
 
-       Assert(portal->strategy == PORTAL_ONE_SELECT);
+       Assert(portal->strategy == PORTAL_ONE_SELECT ||
+                  portal->strategy == PORTAL_ONE_RETURNING ||
+                  portal->strategy == PORTAL_UTIL_SELECT);
 
        switch (fdirection)
        {
@@ -1046,11 +1474,11 @@ DoPortalRunFetch(Portal portal,
                        if (count > 0)
                        {
                                /*
-                                * Definition: Rewind to start, advance count-1 rows,
-                                * return next row (if any).  In practice, if the goal is
-                                * less than halfway back to the start, it's better to
-                                * scan from where we are.      In any case, we arrange to
-                                * fetch the target row going forwards.
+                                * Definition: Rewind to start, advance count-1 rows, return
+                                * next row (if any).  In practice, if the goal is less than
+                                * halfway back to the start, it's better to scan from where
+                                * we are.      In any case, we arrange to fetch the target row
+                                * going forwards.
                                 */
                                if (portal->posOverflow || portal->portalPos == LONG_MAX ||
                                        count - 1 <= portal->portalPos / 2)
@@ -1079,11 +1507,10 @@ DoPortalRunFetch(Portal portal,
                        {
                                /*
                                 * Definition: Advance to end, back up abs(count)-1 rows,
-                                * return prior row (if any).  We could optimize this if
-                                * we knew in advance where the end was, but typically we
-                                * won't. (Is it worth considering case where count > half
-                                * of size of query?  We could rewind once we know the
-                                * size ...)
+                                * return prior row (if any).  We could optimize this if we
+                                * knew in advance where the end was, but typically we won't.
+                                * (Is it worth considering case where count > half of size of
+                                * query?  We could rewind once we know the size ...)
                                 */
                                PortalRunSelect(portal, true, FETCH_ALL, None_Receiver);
                                if (count < -1)
@@ -1091,8 +1518,8 @@ DoPortalRunFetch(Portal portal,
                                return PortalRunSelect(portal, false, 1L, dest);
                        }
                        else
-/* count == 0 */
                        {
+                               /* count == 0 */
                                /* Rewind to start, return zero rows */
                                DoPortalRewind(portal);
                                return PortalRunSelect(portal, true, 0L, dest);
@@ -1102,8 +1529,7 @@ DoPortalRunFetch(Portal portal,
                        if (count > 0)
                        {
                                /*
-                                * Definition: advance count-1 rows, return next row (if
-                                * any).
+                                * Definition: advance count-1 rows, return next row (if any).
                                 */
                                if (count > 1)
                                        PortalRunSelect(portal, true, count - 1, None_Receiver);
@@ -1112,16 +1538,16 @@ DoPortalRunFetch(Portal portal,
                        else if (count < 0)
                        {
                                /*
-                                * Definition: back up abs(count)-1 rows, return prior row
-                                * (if any).
+                                * Definition: back up abs(count)-1 rows, return prior row (if
+                                * any).
                                 */
                                if (count < -1)
                                        PortalRunSelect(portal, false, -count - 1, None_Receiver);
                                return PortalRunSelect(portal, false, 1L, dest);
                        }
                        else
-/* count == 0 */
                        {
+                               /* count == 0 */
                                /* Same as FETCH FORWARD 0, so fall out of switch */
                                fdirection = FETCH_FORWARD;
                        }
@@ -1132,8 +1558,8 @@ DoPortalRunFetch(Portal portal,
        }
 
        /*
-        * Get here with fdirection == FETCH_FORWARD or FETCH_BACKWARD, and
-        * count >= 0.
+        * Get here with fdirection == FETCH_FORWARD or FETCH_BACKWARD, and count
+        * >= 0.
         */
        forward = (fdirection == FETCH_FORWARD);
 
@@ -1147,7 +1573,7 @@ DoPortalRunFetch(Portal portal,
                /* Are we sitting on a row? */
                on_row = (!portal->atStart && !portal->atEnd);
 
-               if (dest->mydest == None)
+               if (dest->mydest == DestNone)
                {
                        /* MOVE 0 returns 0/1 based on if FETCH 0 would return a row */
                        return on_row ? 1L : 0L;
@@ -1155,11 +1581,11 @@ DoPortalRunFetch(Portal portal,
                else
                {
                        /*
-                        * If we are sitting on a row, back up one so we can re-fetch
-                        * it. If we are not sitting on a row, we still have to start
-                        * up and shut down the executor so that the destination is
-                        * initialized and shut down correctly; so keep going.  To
-                        * PortalRunSelect, count == 0 means we will retrieve no row.
+                        * If we are sitting on a row, back up one so we can re-fetch it.
+                        * If we are not sitting on a row, we still have to start up and
+                        * shut down the executor so that the destination is initialized
+                        * and shut down correctly; so keep going.      To PortalRunSelect,
+                        * count == 0 means we will retrieve no row.
                         */
                        if (on_row)
                        {
@@ -1174,7 +1600,7 @@ DoPortalRunFetch(Portal portal,
        /*
         * Optimize MOVE BACKWARD ALL into a Rewind.
         */
-       if (!forward && count == FETCH_ALL && dest->mydest == None)
+       if (!forward && count == FETCH_ALL && dest->mydest == DestNone)
        {
                long            result = portal->portalPos;