Header And Logo

PostgreSQL
| The world's most advanced open source database.

portalcmds.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * portalcmds.c
00004  *    Utility commands affecting portals (that is, SQL cursor commands)
00005  *
00006  * Note: see also tcop/pquery.c, which implements portal operations for
00007  * the FE/BE protocol.  This module uses pquery.c for some operations.
00008  * And both modules depend on utils/mmgr/portalmem.c, which controls
00009  * storage management for portals (but doesn't run any queries in them).
00010  *
00011  *
00012  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00013  * Portions Copyright (c) 1994, Regents of the University of California
00014  *
00015  *
00016  * IDENTIFICATION
00017  *    src/backend/commands/portalcmds.c
00018  *
00019  *-------------------------------------------------------------------------
00020  */
00021 
00022 #include "postgres.h"
00023 
00024 #include <limits.h>
00025 
00026 #include "access/xact.h"
00027 #include "commands/portalcmds.h"
00028 #include "executor/executor.h"
00029 #include "executor/tstoreReceiver.h"
00030 #include "tcop/pquery.h"
00031 #include "utils/memutils.h"
00032 #include "utils/snapmgr.h"
00033 
00034 
00035 /*
00036  * PerformCursorOpen
00037  *      Execute SQL DECLARE CURSOR command.
00038  *
00039  * The query has already been through parse analysis, rewriting, and planning.
00040  * When it gets here, it looks like a SELECT PlannedStmt, except that the
00041  * utilityStmt field is set.
00042  */
00043 void
00044 PerformCursorOpen(PlannedStmt *stmt, ParamListInfo params,
00045                   const char *queryString, bool isTopLevel)
00046 {
00047     DeclareCursorStmt *cstmt = (DeclareCursorStmt *) stmt->utilityStmt;
00048     Portal      portal;
00049     MemoryContext oldContext;
00050 
00051     if (cstmt == NULL || !IsA(cstmt, DeclareCursorStmt))
00052         elog(ERROR, "PerformCursorOpen called for non-cursor query");
00053 
00054     /*
00055      * Disallow empty-string cursor name (conflicts with protocol-level
00056      * unnamed portal).
00057      */
00058     if (!cstmt->portalname || cstmt->portalname[0] == '\0')
00059         ereport(ERROR,
00060                 (errcode(ERRCODE_INVALID_CURSOR_NAME),
00061                  errmsg("invalid cursor name: must not be empty")));
00062 
00063     /*
00064      * If this is a non-holdable cursor, we require that this statement has
00065      * been executed inside a transaction block (or else, it would have no
00066      * user-visible effect).
00067      */
00068     if (!(cstmt->options & CURSOR_OPT_HOLD))
00069         RequireTransactionChain(isTopLevel, "DECLARE CURSOR");
00070 
00071     /*
00072      * Create a portal and copy the plan and queryString into its memory.
00073      */
00074     portal = CreatePortal(cstmt->portalname, false, false);
00075 
00076     oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
00077 
00078     stmt = copyObject(stmt);
00079     stmt->utilityStmt = NULL;   /* make it look like plain SELECT */
00080 
00081     queryString = pstrdup(queryString);
00082 
00083     PortalDefineQuery(portal,
00084                       NULL,
00085                       queryString,
00086                       "SELECT", /* cursor's query is always a SELECT */
00087                       list_make1(stmt),
00088                       NULL);
00089 
00090     /*----------
00091      * Also copy the outer portal's parameter list into the inner portal's
00092      * memory context.  We want to pass down the parameter values in case we
00093      * had a command like
00094      *      DECLARE c CURSOR FOR SELECT ... WHERE foo = $1
00095      * This will have been parsed using the outer parameter set and the
00096      * parameter value needs to be preserved for use when the cursor is
00097      * executed.
00098      *----------
00099      */
00100     params = copyParamList(params);
00101 
00102     MemoryContextSwitchTo(oldContext);
00103 
00104     /*
00105      * Set up options for portal.
00106      *
00107      * If the user didn't specify a SCROLL type, allow or disallow scrolling
00108      * based on whether it would require any additional runtime overhead to do
00109      * so.  Also, we disallow scrolling for FOR UPDATE cursors.
00110      */
00111     portal->cursorOptions = cstmt->options;
00112     if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
00113     {
00114         if (stmt->rowMarks == NIL &&
00115             ExecSupportsBackwardScan(stmt->planTree))
00116             portal->cursorOptions |= CURSOR_OPT_SCROLL;
00117         else
00118             portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
00119     }
00120 
00121     /*
00122      * Start execution, inserting parameters if any.
00123      */
00124     PortalStart(portal, params, 0, GetActiveSnapshot());
00125 
00126     Assert(portal->strategy == PORTAL_ONE_SELECT);
00127 
00128     /*
00129      * We're done; the query won't actually be run until PerformPortalFetch is
00130      * called.
00131      */
00132 }
00133 
00134 /*
00135  * PerformPortalFetch
00136  *      Execute SQL FETCH or MOVE command.
00137  *
00138  *  stmt: parsetree node for command
00139  *  dest: where to send results
00140  *  completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
00141  *      in which to store a command completion status string.
00142  *
00143  * completionTag may be NULL if caller doesn't want a status string.
00144  */
00145 void
00146 PerformPortalFetch(FetchStmt *stmt,
00147                    DestReceiver *dest,
00148                    char *completionTag)
00149 {
00150     Portal      portal;
00151     long        nprocessed;
00152 
00153     /*
00154      * Disallow empty-string cursor name (conflicts with protocol-level
00155      * unnamed portal).
00156      */
00157     if (!stmt->portalname || stmt->portalname[0] == '\0')
00158         ereport(ERROR,
00159                 (errcode(ERRCODE_INVALID_CURSOR_NAME),
00160                  errmsg("invalid cursor name: must not be empty")));
00161 
00162     /* get the portal from the portal name */
00163     portal = GetPortalByName(stmt->portalname);
00164     if (!PortalIsValid(portal))
00165     {
00166         ereport(ERROR,
00167                 (errcode(ERRCODE_UNDEFINED_CURSOR),
00168                  errmsg("cursor \"%s\" does not exist", stmt->portalname)));
00169         return;                 /* keep compiler happy */
00170     }
00171 
00172     /* Adjust dest if needed.  MOVE wants destination DestNone */
00173     if (stmt->ismove)
00174         dest = None_Receiver;
00175 
00176     /* Do it */
00177     nprocessed = PortalRunFetch(portal,
00178                                 stmt->direction,
00179                                 stmt->howMany,
00180                                 dest);
00181 
00182     /* Return command status if wanted */
00183     if (completionTag)
00184         snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
00185                  stmt->ismove ? "MOVE" : "FETCH",
00186                  nprocessed);
00187 }
00188 
00189 /*
00190  * PerformPortalClose
00191  *      Close a cursor.
00192  */
00193 void
00194 PerformPortalClose(const char *name)
00195 {
00196     Portal      portal;
00197 
00198     /* NULL means CLOSE ALL */
00199     if (name == NULL)
00200     {
00201         PortalHashTableDeleteAll();
00202         return;
00203     }
00204 
00205     /*
00206      * Disallow empty-string cursor name (conflicts with protocol-level
00207      * unnamed portal).
00208      */
00209     if (name[0] == '\0')
00210         ereport(ERROR,
00211                 (errcode(ERRCODE_INVALID_CURSOR_NAME),
00212                  errmsg("invalid cursor name: must not be empty")));
00213 
00214     /*
00215      * get the portal from the portal name
00216      */
00217     portal = GetPortalByName(name);
00218     if (!PortalIsValid(portal))
00219     {
00220         ereport(ERROR,
00221                 (errcode(ERRCODE_UNDEFINED_CURSOR),
00222                  errmsg("cursor \"%s\" does not exist", name)));
00223         return;                 /* keep compiler happy */
00224     }
00225 
00226     /*
00227      * Note: PortalCleanup is called as a side-effect, if not already done.
00228      */
00229     PortalDrop(portal, false);
00230 }
00231 
00232 /*
00233  * PortalCleanup
00234  *
00235  * Clean up a portal when it's dropped.  This is the standard cleanup hook
00236  * for portals.
00237  *
00238  * Note: if portal->status is PORTAL_FAILED, we are probably being called
00239  * during error abort, and must be careful to avoid doing anything that
00240  * is likely to fail again.
00241  */
00242 void
00243 PortalCleanup(Portal portal)
00244 {
00245     QueryDesc  *queryDesc;
00246 
00247     /*
00248      * sanity checks
00249      */
00250     AssertArg(PortalIsValid(portal));
00251     AssertArg(portal->cleanup == PortalCleanup);
00252 
00253     /*
00254      * Shut down executor, if still running.  We skip this during error abort,
00255      * since other mechanisms will take care of releasing executor resources,
00256      * and we can't be sure that ExecutorEnd itself wouldn't fail.
00257      */
00258     queryDesc = PortalGetQueryDesc(portal);
00259     if (queryDesc)
00260     {
00261         /*
00262          * Reset the queryDesc before anything else.  This prevents us from
00263          * trying to shut down the executor twice, in case of an error below.
00264          * The transaction abort mechanisms will take care of resource cleanup
00265          * in such a case.
00266          */
00267         portal->queryDesc = NULL;
00268 
00269         if (portal->status != PORTAL_FAILED)
00270         {
00271             ResourceOwner saveResourceOwner;
00272 
00273             /* We must make the portal's resource owner current */
00274             saveResourceOwner = CurrentResourceOwner;
00275             PG_TRY();
00276             {
00277                 CurrentResourceOwner = portal->resowner;
00278                 ExecutorFinish(queryDesc);
00279                 ExecutorEnd(queryDesc);
00280                 FreeQueryDesc(queryDesc);
00281             }
00282             PG_CATCH();
00283             {
00284                 /* Ensure CurrentResourceOwner is restored on error */
00285                 CurrentResourceOwner = saveResourceOwner;
00286                 PG_RE_THROW();
00287             }
00288             PG_END_TRY();
00289             CurrentResourceOwner = saveResourceOwner;
00290         }
00291     }
00292 }
00293 
00294 /*
00295  * PersistHoldablePortal
00296  *
00297  * Prepare the specified Portal for access outside of the current
00298  * transaction. When this function returns, all future accesses to the
00299  * portal must be done via the Tuplestore (not by invoking the
00300  * executor).
00301  */
00302 void
00303 PersistHoldablePortal(Portal portal)
00304 {
00305     QueryDesc  *queryDesc = PortalGetQueryDesc(portal);
00306     Portal      saveActivePortal;
00307     ResourceOwner saveResourceOwner;
00308     MemoryContext savePortalContext;
00309     MemoryContext oldcxt;
00310 
00311     /*
00312      * If we're preserving a holdable portal, we had better be inside the
00313      * transaction that originally created it.
00314      */
00315     Assert(portal->createSubid != InvalidSubTransactionId);
00316     Assert(queryDesc != NULL);
00317 
00318     /*
00319      * Caller must have created the tuplestore already.
00320      */
00321     Assert(portal->holdContext != NULL);
00322     Assert(portal->holdStore != NULL);
00323 
00324     /*
00325      * Before closing down the executor, we must copy the tupdesc into
00326      * long-term memory, since it was created in executor memory.
00327      */
00328     oldcxt = MemoryContextSwitchTo(portal->holdContext);
00329 
00330     portal->tupDesc = CreateTupleDescCopy(portal->tupDesc);
00331 
00332     MemoryContextSwitchTo(oldcxt);
00333 
00334     /*
00335      * Check for improper portal use, and mark portal active.
00336      */
00337     if (portal->status != PORTAL_READY)
00338         ereport(ERROR,
00339                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
00340                  errmsg("portal \"%s\" cannot be run", portal->name)));
00341     portal->status = PORTAL_ACTIVE;
00342 
00343     /*
00344      * Set up global portal context pointers.
00345      */
00346     saveActivePortal = ActivePortal;
00347     saveResourceOwner = CurrentResourceOwner;
00348     savePortalContext = PortalContext;
00349     PG_TRY();
00350     {
00351         ActivePortal = portal;
00352         CurrentResourceOwner = portal->resowner;
00353         PortalContext = PortalGetHeapMemory(portal);
00354 
00355         MemoryContextSwitchTo(PortalContext);
00356 
00357         PushActiveSnapshot(queryDesc->snapshot);
00358 
00359         /*
00360          * Rewind the executor: we need to store the entire result set in the
00361          * tuplestore, so that subsequent backward FETCHs can be processed.
00362          */
00363         ExecutorRewind(queryDesc);
00364 
00365         /*
00366          * Change the destination to output to the tuplestore.  Note we tell
00367          * the tuplestore receiver to detoast all data passed through it.
00368          */
00369         queryDesc->dest = CreateDestReceiver(DestTuplestore);
00370         SetTuplestoreDestReceiverParams(queryDesc->dest,
00371                                         portal->holdStore,
00372                                         portal->holdContext,
00373                                         true);
00374 
00375         /* Fetch the result set into the tuplestore */
00376         ExecutorRun(queryDesc, ForwardScanDirection, 0L);
00377 
00378         (*queryDesc->dest->rDestroy) (queryDesc->dest);
00379         queryDesc->dest = NULL;
00380 
00381         /*
00382          * Now shut down the inner executor.
00383          */
00384         portal->queryDesc = NULL;       /* prevent double shutdown */
00385         ExecutorFinish(queryDesc);
00386         ExecutorEnd(queryDesc);
00387         FreeQueryDesc(queryDesc);
00388 
00389         /*
00390          * Set the position in the result set: ideally, this could be
00391          * implemented by just skipping straight to the tuple # that we need
00392          * to be at, but the tuplestore API doesn't support that. So we start
00393          * at the beginning of the tuplestore and iterate through it until we
00394          * reach where we need to be.  FIXME someday?  (Fortunately, the
00395          * typical case is that we're supposed to be at or near the start of
00396          * the result set, so this isn't as bad as it sounds.)
00397          */
00398         MemoryContextSwitchTo(portal->holdContext);
00399 
00400         if (portal->atEnd)
00401         {
00402             /* we can handle this case even if posOverflow */
00403             while (tuplestore_advance(portal->holdStore, true))
00404                  /* continue */ ;
00405         }
00406         else
00407         {
00408             long        store_pos;
00409 
00410             if (portal->posOverflow)    /* oops, cannot trust portalPos */
00411                 ereport(ERROR,
00412                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
00413                          errmsg("could not reposition held cursor")));
00414 
00415             tuplestore_rescan(portal->holdStore);
00416 
00417             for (store_pos = 0; store_pos < portal->portalPos; store_pos++)
00418             {
00419                 if (!tuplestore_advance(portal->holdStore, true))
00420                     elog(ERROR, "unexpected end of tuple stream");
00421             }
00422         }
00423     }
00424     PG_CATCH();
00425     {
00426         /* Uncaught error while executing portal: mark it dead */
00427         MarkPortalFailed(portal);
00428 
00429         /* Restore global vars and propagate error */
00430         ActivePortal = saveActivePortal;
00431         CurrentResourceOwner = saveResourceOwner;
00432         PortalContext = savePortalContext;
00433 
00434         PG_RE_THROW();
00435     }
00436     PG_END_TRY();
00437 
00438     MemoryContextSwitchTo(oldcxt);
00439 
00440     /* Mark portal not active */
00441     portal->status = PORTAL_READY;
00442 
00443     ActivePortal = saveActivePortal;
00444     CurrentResourceOwner = saveResourceOwner;
00445     PortalContext = savePortalContext;
00446 
00447     PopActiveSnapshot();
00448 
00449     /*
00450      * We can now release any subsidiary memory of the portal's heap context;
00451      * we'll never use it again.  The executor already dropped its context,
00452      * but this will clean up anything that glommed onto the portal's heap via
00453      * PortalContext.
00454      */
00455     MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
00456 }