]> granicus.if.org Git - postgresql/blobdiff - src/backend/tcop/pquery.c
Update copyright for the year 2010.
[postgresql] / src / backend / tcop / pquery.c
index 652b4ec464487fad746ec15266eddfa280db032f..05068fa406c9176a00a5bd20de5054b12082b3e1 100644 (file)
@@ -3,12 +3,12 @@
  * pquery.c
  *       POSTGRES process query command code
  *
- * Portions Copyright (c) 1996-2006, 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.105 2006/07/14 14:52:23 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.134 2010/01/02 16:57:52 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "access/xact.h"
 #include "commands/prepare.h"
 #include "commands/trigger.h"
+#include "executor/tstoreReceiver.h"
 #include "miscadmin.h"
+#include "pg_trace.h"
 #include "tcop/pquery.h"
 #include "tcop/tcopprot.h"
 #include "tcop/utility.h"
 #include "utils/memutils.h"
+#include "utils/snapmgr.h"
 
 
 /*
 Portal         ActivePortal = NULL;
 
 
-static void ProcessQuery(Query *parsetree,
-                        Plan *plan,
+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,
@@ -57,29 +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 = parsetree->commandType;         /* operation */
-       qd->parsetree = parsetree;      /* parse tree */
-       qd->plantree = plantree;        /* plan */
-       qd->snapshot = snapshot;        /* snapshot */
-       qd->crosscheck_snapshot = crosscheck_snapshot;          /* RI check snapshot */
+       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->doInstrument = doInstrument;        /* instrumentation wanted? */
+       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 = 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->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;
 }
@@ -92,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);
 }
@@ -99,10 +142,11 @@ FreeQueryDesc(QueryDesc *qdesc)
 
 /*
  * ProcessQuery
- *             Execute a single plannable query within a PORTAL_MULTI_QUERY portal
+ *             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
@@ -114,48 +158,27 @@ FreeQueryDesc(QueryDesc *qdesc)
  * error; otherwise the executor's memory usage will be leaked.
  */
 static void
-ProcessQuery(Query *parsetree,
-                        Plan *plan,
+ProcessQuery(PlannedStmt *plan,
+                        const char *sourceText,
                         ParamListInfo params,
                         DestReceiver *dest,
                         char *completionTag)
 {
-       int                     operation = parsetree->commandType;
        QueryDesc  *queryDesc;
 
-       ereport(DEBUG3,
-                       (errmsg_internal("ProcessQuery")));
-
-       /*
-        * Check for special-case destinations
-        */
-       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;
-               }
-       }
+       elog(DEBUG3, "ProcessQuery");
 
        /*
-        * Must always set snapshot for plannable queries.      Note we assume that
-        * caller will take care of restoring ActiveSnapshot on exit/error.
+        * Must always set a snapshot for plannable queries.
         */
-       ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
+       PushActiveSnapshot(GetTransactionSnapshot());
 
        /*
         * Create the QueryDesc object
         */
-       queryDesc = CreateQueryDesc(parsetree, plan,
-                                                               ActiveSnapshot, InvalidSnapshot,
-                                                               dest, params, false);
+       queryDesc = CreateQueryDesc(plan, sourceText,
+                                                               GetActiveSnapshot(), InvalidSnapshot,
+                                                               dest, params, 0);
 
        /*
         * Set up to collect AFTER triggers
@@ -179,7 +202,7 @@ ProcessQuery(Query *parsetree,
        {
                Oid                     lastOid;
 
-               switch (operation)
+               switch (queryDesc->operation)
                {
                        case CMD_SELECT:
                                strcpy(completionTag, "SELECT");
@@ -209,47 +232,123 @@ 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.
         */
        ExecutorEnd(queryDesc);
 
        FreeQueryDesc(queryDesc);
-
-       FreeSnapshot(ActiveSnapshot);
-       ActiveSnapshot = NULL;
 }
 
 /*
  * 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))
                {
-                       if (UtilityReturnsTuples(query->utilityStmt))
-                               strategy = PORTAL_UTIL_SELECT;
+                       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))
+               {
+                       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;
 }
 
 /*
@@ -258,46 +357,85 @@ ChoosePortalStrategy(List *parseTrees)
  *             Returns NIL if the portal doesn't have a determinable targetlist.
  *
  * Note: do not modify the result.
- *
- * XXX be careful to keep this in sync with FetchPreparedStatementTargetList,
- * and with UtilityReturnsTuples.
  */
 List *
 FetchPortalTargetList(Portal portal)
 {
-       if (portal->strategy == PORTAL_ONE_SELECT)
-               return ((Query *) linitial(portal->parseTrees))->targetList;
-       if (portal->strategy == PORTAL_UTIL_SELECT)
+       /* 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))
        {
-               Node       *utilityStmt;
+               Query      *query = (Query *) stmt;
 
-               utilityStmt = ((Query *) linitial(portal->parseTrees))->utilityStmt;
-               switch (nodeTag(utilityStmt))
+               if (query->commandType == CMD_UTILITY &&
+                       query->utilityStmt != NULL)
                {
-                       case T_FetchStmt:
-                               {
-                                       FetchStmt  *substmt = (FetchStmt *) utilityStmt;
-                                       Portal          subportal;
-
-                                       Assert(!substmt->ismove);
-                                       subportal = GetPortalByName(substmt->portalname);
-                                       Assert(PortalIsValid(subportal));
-                                       return FetchPortalTargetList(subportal);
-                               }
-
-                       case T_ExecuteStmt:
-                               {
-                                       ExecuteStmt *substmt = (ExecuteStmt *) utilityStmt;
-                                       PreparedStatement *entry;
+                       /* 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(!substmt->into);
-                                       entry = FetchPreparedStatement(substmt->name, true);
-                                       return FetchPreparedStatementTargetList(entry);
-                               }
+               Assert(!fstmt->ismove);
+               subportal = GetPortalByName(fstmt->portalname);
+               Assert(PortalIsValid(subportal));
+               return FetchPortalTargetList(subportal);
+       }
+       if (IsA(stmt, ExecuteStmt))
+       {
+               ExecuteStmt *estmt = (ExecuteStmt *) stmt;
+               PreparedStatement *entry;
 
-                       default:
-                               break;
-               }
+               Assert(!estmt->into);
+               entry = FetchPreparedStatement(estmt->name, true);
+               return FetchPreparedStatementTargetList(entry);
        }
        return NIL;
 }
@@ -323,7 +461,6 @@ void
 PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
 {
        Portal          saveActivePortal;
-       Snapshot        saveActiveSnapshot;
        ResourceOwner saveResourceOwner;
        MemoryContext savePortalContext;
        MemoryContext oldContext;
@@ -331,20 +468,17 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
        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 up global portal context pointers.  (Should we set QueryContext?)
+        * Set up global portal context pointers.
         */
        saveActivePortal = ActivePortal;
-       saveActiveSnapshot = ActiveSnapshot;
        saveResourceOwner = CurrentResourceOwner;
        savePortalContext = PortalContext;
        PG_TRY();
        {
                ActivePortal = portal;
-               ActiveSnapshot = NULL;  /* will be set later */
                CurrentResourceOwner = portal->resowner;
                PortalContext = PortalGetHeapMemory(portal);
 
@@ -356,7 +490,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
                /*
                 * Determine the portal execution strategy
                 */
-               portal->strategy = ChoosePortalStrategy(portal->parseTrees);
+               portal->strategy = ChoosePortalStrategy(portal->stmts);
 
                /*
                 * Fire her up according to the strategy
@@ -365,26 +499,23 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
                {
                        case PORTAL_ONE_SELECT:
 
-                               /*
-                                * Must set snapshot before starting executor.  Be sure to
-                                * copy it into the portal's context.
-                                */
+                               /* Must set snapshot before starting executor. */
                                if (snapshot)
-                                       ActiveSnapshot = CopySnapshot(snapshot);
+                                       PushActiveSnapshot(snapshot);
                                else
-                                       ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
+                                       PushActiveSnapshot(GetTransactionSnapshot());
 
                                /*
                                 * Create QueryDesc in portal's context; for the moment, set
                                 * the destination to DestNone.
                                 */
-                               queryDesc = CreateQueryDesc((Query *) linitial(portal->parseTrees),
-                                                                               (Plan *) linitial(portal->planTrees),
-                                                                                       ActiveSnapshot,
+                               queryDesc = CreateQueryDesc((PlannedStmt *) linitial(portal->stmts),
+                                                                                       portal->sourceText,
+                                                                                       GetActiveSnapshot(),
                                                                                        InvalidSnapshot,
                                                                                        None_Receiver,
                                                                                        params,
-                                                                                       false);
+                                                                                       0);
 
                                /*
                                 * We do *not* call AfterTriggerBeginQuery() here.      We assume
@@ -400,7 +531,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
                                if (portal->cursorOptions & CURSOR_OPT_SCROLL)
                                        eflags = EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD;
                                else
-                                       eflags = 0;             /* default run-to-completion flags */
+                                       eflags = 0; /* default run-to-completion flags */
 
                                /*
                                 * Call ExecutorStart to prepare the plan for execution
@@ -417,6 +548,34 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
                                 */
                                portal->tupDesc = queryDesc->tupDesc;
 
+                               /*
+                                * Reset cursor position data to "start of query"
+                                */
+                               portal->atStart = true;
+                               portal->atEnd = false;  /* allow fetches */
+                               portal->portalPos = 0;
+                               portal->posOverflow = false;
+
+                               PopActiveSnapshot();
+                               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"
                                 */
@@ -432,8 +591,12 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
                                 * We don't set snapshot here, because PortalRunUtility will
                                 * take care of it if needed.
                                 */
-                               portal->tupDesc =
-                                       UtilityTupleDescriptor(((Query *) linitial(portal->parseTrees))->utilityStmt);
+                               {
+                                       Node       *ustmt = PortalGetPrimaryStmt(portal);
+
+                                       Assert(!IsA(ustmt, PlannedStmt));
+                                       portal->tupDesc = UtilityTupleDescriptor(ustmt);
+                               }
 
                                /*
                                 * Reset cursor position data to "start of query"
@@ -457,7 +620,6 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
 
                /* Restore global vars and propagate error */
                ActivePortal = saveActivePortal;
-               ActiveSnapshot = saveActiveSnapshot;
                CurrentResourceOwner = saveResourceOwner;
                PortalContext = savePortalContext;
 
@@ -468,7 +630,6 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
        MemoryContextSwitchTo(oldContext);
 
        ActivePortal = saveActivePortal;
-       ActiveSnapshot = saveActiveSnapshot;
        CurrentResourceOwner = saveResourceOwner;
        PortalContext = savePortalContext;
 
@@ -533,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
@@ -545,7 +709,7 @@ 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)
 {
@@ -553,22 +717,21 @@ PortalRun(Portal portal, long count,
        ResourceOwner saveTopTransactionResourceOwner;
        MemoryContext saveTopTransactionContext;
        Portal          saveActivePortal;
-       Snapshot        saveActiveSnapshot;
        ResourceOwner saveResourceOwner;
        MemoryContext savePortalContext;
-       MemoryContext saveQueryContext;
        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();
        }
@@ -599,18 +762,14 @@ PortalRun(Portal portal, long count,
        saveTopTransactionResourceOwner = TopTransactionResourceOwner;
        saveTopTransactionContext = TopTransactionContext;
        saveActivePortal = ActivePortal;
-       saveActiveSnapshot = ActiveSnapshot;
        saveResourceOwner = CurrentResourceOwner;
        savePortalContext = PortalContext;
-       saveQueryContext = QueryContext;
        saveMemoryContext = CurrentMemoryContext;
        PG_TRY();
        {
                ActivePortal = portal;
-               ActiveSnapshot = NULL;  /* will be set later */
                CurrentResourceOwner = portal->resowner;
                PortalContext = PortalGetHeapMemory(portal);
-               QueryContext = portal->queryContext;
 
                MemoryContextSwitchTo(PortalContext);
 
@@ -618,6 +777,7 @@ PortalRun(Portal portal, long count,
                {
                        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);
@@ -631,33 +791,22 @@ PortalRun(Portal portal, long count,
                                result = portal->atEnd;
                                break;
 
+                       case PORTAL_ONE_RETURNING:
                        case PORTAL_UTIL_SELECT:
 
                                /*
-                                * If we have not yet run the utility statement, do so,
-                                * storing its results in the portal's tuplestore.
+                                * If we have not yet run the command, do so, storing its
+                                * results in the portal's tuplestore.
                                 */
-                               if (!portal->portalUtilReady)
-                               {
-                                       DestReceiver *treceiver;
-
-                                       PortalCreateHoldStore(portal);
-                                       treceiver = CreateDestReceiver(DestTuplestore, portal);
-                                       PortalRunUtility(portal, linitial(portal->parseTrees),
-                                                                        treceiver, NULL);
-                                       (*treceiver->rDestroy) (treceiver);
-                                       portal->portalUtilReady = true;
-                               }
+                               if (!portal->holdStore)
+                                       FillPortalStore(portal, isTopLevel);
 
                                /*
                                 * Now fetch desired portion of results.
                                 */
                                (void) PortalRunSelect(portal, true, count, dest);
 
-                               /*
-                                * We know the query is supposed to set the tag; we assume
-                                * only the default tag is needed.
-                                */
+                               /* we know the query is supposed to set the tag */
                                if (completionTag && portal->commandTag)
                                        strcpy(completionTag, portal->commandTag);
 
@@ -671,7 +820,8 @@ PortalRun(Portal portal, long count,
                                break;
 
                        case PORTAL_MULTI_QUERY:
-                               PortalRunMulti(portal, dest, altdest, completionTag);
+                               PortalRunMulti(portal, isTopLevel,
+                                                          dest, altdest, completionTag);
 
                                /* Prevent portal's commands from being re-executed */
                                portal->status = PORTAL_DONE;
@@ -698,13 +848,11 @@ PortalRun(Portal portal, long count,
                else
                        MemoryContextSwitchTo(saveMemoryContext);
                ActivePortal = saveActivePortal;
-               ActiveSnapshot = saveActiveSnapshot;
                if (saveResourceOwner == saveTopTransactionResourceOwner)
                        CurrentResourceOwner = TopTransactionResourceOwner;
                else
                        CurrentResourceOwner = saveResourceOwner;
                PortalContext = savePortalContext;
-               QueryContext = saveQueryContext;
 
                PG_RE_THROW();
        }
@@ -715,23 +863,25 @@ PortalRun(Portal portal, long count,
        else
                MemoryContextSwitchTo(saveMemoryContext);
        ActivePortal = saveActivePortal;
-       ActiveSnapshot = saveActiveSnapshot;
        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.
@@ -799,9 +949,10 @@ PortalRunSelect(Portal portal,
                        nprocessed = RunFromStore(portal, direction, count, dest);
                else
                {
-                       ActiveSnapshot = queryDesc->snapshot;
+                       PushActiveSnapshot(queryDesc->snapshot);
                        ExecutorRun(queryDesc, direction, count);
                        nprocessed = queryDesc->estate->es_processed;
+                       PopActiveSnapshot();
                }
 
                if (!ScanDirectionIsNoMovement(direction))
@@ -841,9 +992,10 @@ PortalRunSelect(Portal portal,
                        nprocessed = RunFromStore(portal, direction, count, dest);
                else
                {
-                       ActiveSnapshot = queryDesc->snapshot;
+                       PushActiveSnapshot(queryDesc->snapshot);
                        ExecutorRun(queryDesc, direction, count);
                        nprocessed = queryDesc->estate->es_processed;
+                       PopActiveSnapshot();
                }
 
                if (!ScanDirectionIsNoMovement(direction))
@@ -876,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.
@@ -914,7 +1118,8 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
 
                        oldcontext = MemoryContextSwitchTo(portal->holdContext);
 
-                       ok = tuplestore_gettupleslot(portal->holdStore, forward, slot);
+                       ok = tuplestore_gettupleslot(portal->holdStore, forward, false,
+                                                                                slot);
 
                        MemoryContextSwitchTo(oldcontext);
 
@@ -948,13 +1153,12 @@ 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
@@ -966,15 +1170,11 @@ PortalRunUtility(Portal portal, Query *query,
         * 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.
-        *
-        * Note we assume that caller will take care of restoring ActiveSnapshot
-        * on exit/error.
         */
        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) ||
@@ -982,42 +1182,45 @@ PortalRunUtility(Portal portal, Query *query,
                  IsA(utilityStmt, NotifyStmt) ||
                  IsA(utilityStmt, UnlistenStmt) ||
                  IsA(utilityStmt, CheckPointStmt)))
-               ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
-       else
-               ActiveSnapshot = NULL;
-
-       if (query->canSetTag)
        {
-               /* utility statement can override default tag string */
-               ProcessUtility(utilityStmt, portal->portalParams, 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, portal->portalParams, 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));
 
-       if (ActiveSnapshot)
-               FreeSnapshot(ActiveSnapshot);
-       ActiveSnapshot = NULL;
+       /*
+        * 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 DestRemoteExecute, change to DestNone.  The
@@ -1038,60 +1241,69 @@ PortalRunMulti(Portal portal,
         * 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.
                         */
+                       PlannedStmt *pstmt = (PlannedStmt *) stmt;
+
+                       TRACE_POSTGRESQL_QUERY_EXECUTE_START();
+
                        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.
                 */
-               if (lnext(planlist_item) != NULL)
+               if (lnext(stmtlist_item) != NULL)
                        CommandCounterIncrement();
 
                /*
@@ -1127,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
@@ -1137,10 +1351,8 @@ PortalRunFetch(Portal portal,
 {
        long            result;
        Portal          saveActivePortal;
-       Snapshot        saveActiveSnapshot;
        ResourceOwner saveResourceOwner;
        MemoryContext savePortalContext;
-       MemoryContext saveQueryContext;
        MemoryContext oldContext;
 
        AssertArg(PortalIsValid(portal));
@@ -1158,17 +1370,13 @@ PortalRunFetch(Portal portal,
         * Set up global portal context pointers.
         */
        saveActivePortal = ActivePortal;
-       saveActiveSnapshot = ActiveSnapshot;
        saveResourceOwner = CurrentResourceOwner;
        savePortalContext = PortalContext;
-       saveQueryContext = QueryContext;
        PG_TRY();
        {
                ActivePortal = portal;
-               ActiveSnapshot = NULL;  /* will be set later */
                CurrentResourceOwner = portal->resowner;
                PortalContext = PortalGetHeapMemory(portal);
-               QueryContext = portal->queryContext;
 
                oldContext = MemoryContextSwitchTo(PortalContext);
 
@@ -1178,23 +1386,15 @@ PortalRunFetch(Portal portal,
                                result = DoPortalRunFetch(portal, fdirection, count, dest);
                                break;
 
+                       case PORTAL_ONE_RETURNING:
                        case PORTAL_UTIL_SELECT:
 
                                /*
-                                * If we have not yet run the utility statement, do so,
-                                * storing its results in the portal's tuplestore.
+                                * If we have not yet run the command, do so, storing its
+                                * results in the portal's tuplestore.
                                 */
-                               if (!portal->portalUtilReady)
-                               {
-                                       DestReceiver *treceiver;
-
-                                       PortalCreateHoldStore(portal);
-                                       treceiver = CreateDestReceiver(DestTuplestore, portal);
-                                       PortalRunUtility(portal, linitial(portal->parseTrees),
-                                                                        treceiver, NULL);
-                                       (*treceiver->rDestroy) (treceiver);
-                                       portal->portalUtilReady = true;
-                               }
+                               if (!portal->holdStore)
+                                       FillPortalStore(portal, false /* isTopLevel */ );
 
                                /*
                                 * Now fetch desired portion of results.
@@ -1215,10 +1415,8 @@ PortalRunFetch(Portal portal,
 
                /* Restore global vars and propagate error */
                ActivePortal = saveActivePortal;
-               ActiveSnapshot = saveActiveSnapshot;
                CurrentResourceOwner = saveResourceOwner;
                PortalContext = savePortalContext;
-               QueryContext = saveQueryContext;
 
                PG_RE_THROW();
        }
@@ -1230,10 +1428,8 @@ PortalRunFetch(Portal portal,
        portal->status = PORTAL_READY;
 
        ActivePortal = saveActivePortal;
-       ActiveSnapshot = saveActiveSnapshot;
        CurrentResourceOwner = saveResourceOwner;
        PortalContext = savePortalContext;
-       QueryContext = saveQueryContext;
 
        return result;
 }
@@ -1253,6 +1449,7 @@ DoPortalRunFetch(Portal portal,
        bool            forward;
 
        Assert(portal->strategy == PORTAL_ONE_SELECT ||
+                  portal->strategy == PORTAL_ONE_RETURNING ||
                   portal->strategy == PORTAL_UTIL_SELECT);
 
        switch (fdirection)