LCOV - code coverage report
Current view: top level - src/backend/commands - portalcmds.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 108 125 86.4 %
Date: 2017-09-29 15:12:54 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * portalcmds.c
       4             :  *    Utility commands affecting portals (that is, SQL cursor commands)
       5             :  *
       6             :  * Note: see also tcop/pquery.c, which implements portal operations for
       7             :  * the FE/BE protocol.  This module uses pquery.c for some operations.
       8             :  * And both modules depend on utils/mmgr/portalmem.c, which controls
       9             :  * storage management for portals (but doesn't run any queries in them).
      10             :  *
      11             :  *
      12             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
      13             :  * Portions Copyright (c) 1994, Regents of the University of California
      14             :  *
      15             :  *
      16             :  * IDENTIFICATION
      17             :  *    src/backend/commands/portalcmds.c
      18             :  *
      19             :  *-------------------------------------------------------------------------
      20             :  */
      21             : 
      22             : #include "postgres.h"
      23             : 
      24             : #include <limits.h>
      25             : 
      26             : #include "access/xact.h"
      27             : #include "commands/portalcmds.h"
      28             : #include "executor/executor.h"
      29             : #include "executor/tstoreReceiver.h"
      30             : #include "rewrite/rewriteHandler.h"
      31             : #include "tcop/pquery.h"
      32             : #include "tcop/tcopprot.h"
      33             : #include "utils/memutils.h"
      34             : #include "utils/snapmgr.h"
      35             : 
      36             : 
      37             : /*
      38             :  * PerformCursorOpen
      39             :  *      Execute SQL DECLARE CURSOR command.
      40             :  */
      41             : void
      42          95 : PerformCursorOpen(DeclareCursorStmt *cstmt, ParamListInfo params,
      43             :                   const char *queryString, bool isTopLevel)
      44             : {
      45          95 :     Query      *query = castNode(Query, cstmt->query);
      46             :     List       *rewritten;
      47             :     PlannedStmt *plan;
      48             :     Portal      portal;
      49             :     MemoryContext oldContext;
      50             : 
      51             :     /*
      52             :      * Disallow empty-string cursor name (conflicts with protocol-level
      53             :      * unnamed portal).
      54             :      */
      55          95 :     if (!cstmt->portalname || cstmt->portalname[0] == '\0')
      56           0 :         ereport(ERROR,
      57             :                 (errcode(ERRCODE_INVALID_CURSOR_NAME),
      58             :                  errmsg("invalid cursor name: must not be empty")));
      59             : 
      60             :     /*
      61             :      * If this is a non-holdable cursor, we require that this statement has
      62             :      * been executed inside a transaction block (or else, it would have no
      63             :      * user-visible effect).
      64             :      */
      65          95 :     if (!(cstmt->options & CURSOR_OPT_HOLD))
      66          88 :         RequireTransactionChain(isTopLevel, "DECLARE CURSOR");
      67             : 
      68             :     /*
      69             :      * Parse analysis was done already, but we still have to run the rule
      70             :      * rewriter.  We do not do AcquireRewriteLocks: we assume the query either
      71             :      * came straight from the parser, or suitable locks were acquired by
      72             :      * plancache.c.
      73             :      *
      74             :      * Because the rewriter and planner tend to scribble on the input, we make
      75             :      * a preliminary copy of the source querytree.  This prevents problems in
      76             :      * the case that the DECLARE CURSOR is in a portal or plpgsql function and
      77             :      * is executed repeatedly.  (See also the same hack in EXPLAIN and
      78             :      * PREPARE.)  XXX FIXME someday.
      79             :      */
      80          95 :     rewritten = QueryRewrite((Query *) copyObject(query));
      81             : 
      82             :     /* SELECT should never rewrite to more or less than one query */
      83          95 :     if (list_length(rewritten) != 1)
      84           0 :         elog(ERROR, "non-SELECT statement in DECLARE CURSOR");
      85             : 
      86          95 :     query = linitial_node(Query, rewritten);
      87             : 
      88          95 :     if (query->commandType != CMD_SELECT)
      89           0 :         elog(ERROR, "non-SELECT statement in DECLARE CURSOR");
      90             : 
      91             :     /* Plan the query, applying the specified options */
      92          95 :     plan = pg_plan_query(query, cstmt->options, params);
      93             : 
      94             :     /*
      95             :      * Create a portal and copy the plan and queryString into its memory.
      96             :      */
      97          95 :     portal = CreatePortal(cstmt->portalname, false, false);
      98             : 
      99          95 :     oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
     100             : 
     101          95 :     plan = copyObject(plan);
     102             : 
     103          95 :     queryString = pstrdup(queryString);
     104             : 
     105          95 :     PortalDefineQuery(portal,
     106             :                       NULL,
     107             :                       queryString,
     108             :                       "SELECT", /* cursor's query is always a SELECT */
     109             :                       list_make1(plan),
     110             :                       NULL);
     111             : 
     112             :     /*----------
     113             :      * Also copy the outer portal's parameter list into the inner portal's
     114             :      * memory context.  We want to pass down the parameter values in case we
     115             :      * had a command like
     116             :      *      DECLARE c CURSOR FOR SELECT ... WHERE foo = $1
     117             :      * This will have been parsed using the outer parameter set and the
     118             :      * parameter value needs to be preserved for use when the cursor is
     119             :      * executed.
     120             :      *----------
     121             :      */
     122          95 :     params = copyParamList(params);
     123             : 
     124          95 :     MemoryContextSwitchTo(oldContext);
     125             : 
     126             :     /*
     127             :      * Set up options for portal.
     128             :      *
     129             :      * If the user didn't specify a SCROLL type, allow or disallow scrolling
     130             :      * based on whether it would require any additional runtime overhead to do
     131             :      * so.  Also, we disallow scrolling for FOR UPDATE cursors.
     132             :      */
     133          95 :     portal->cursorOptions = cstmt->options;
     134          95 :     if (!(portal->cursorOptions & (CURSOR_OPT_SCROLL | CURSOR_OPT_NO_SCROLL)))
     135             :     {
     136         102 :         if (plan->rowMarks == NIL &&
     137          48 :             ExecSupportsBackwardScan(plan->planTree))
     138          38 :             portal->cursorOptions |= CURSOR_OPT_SCROLL;
     139             :         else
     140          16 :             portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
     141             :     }
     142             : 
     143             :     /*
     144             :      * Start execution, inserting parameters if any.
     145             :      */
     146          95 :     PortalStart(portal, params, 0, GetActiveSnapshot());
     147             : 
     148          95 :     Assert(portal->strategy == PORTAL_ONE_SELECT);
     149             : 
     150             :     /*
     151             :      * We're done; the query won't actually be run until PerformPortalFetch is
     152             :      * called.
     153             :      */
     154          95 : }
     155             : 
     156             : /*
     157             :  * PerformPortalFetch
     158             :  *      Execute SQL FETCH or MOVE command.
     159             :  *
     160             :  *  stmt: parsetree node for command
     161             :  *  dest: where to send results
     162             :  *  completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
     163             :  *      in which to store a command completion status string.
     164             :  *
     165             :  * completionTag may be NULL if caller doesn't want a status string.
     166             :  */
     167             : void
     168         203 : PerformPortalFetch(FetchStmt *stmt,
     169             :                    DestReceiver *dest,
     170             :                    char *completionTag)
     171             : {
     172             :     Portal      portal;
     173             :     uint64      nprocessed;
     174             : 
     175             :     /*
     176             :      * Disallow empty-string cursor name (conflicts with protocol-level
     177             :      * unnamed portal).
     178             :      */
     179         203 :     if (!stmt->portalname || stmt->portalname[0] == '\0')
     180           0 :         ereport(ERROR,
     181             :                 (errcode(ERRCODE_INVALID_CURSOR_NAME),
     182             :                  errmsg("invalid cursor name: must not be empty")));
     183             : 
     184             :     /* get the portal from the portal name */
     185         203 :     portal = GetPortalByName(stmt->portalname);
     186         203 :     if (!PortalIsValid(portal))
     187             :     {
     188           4 :         ereport(ERROR,
     189             :                 (errcode(ERRCODE_UNDEFINED_CURSOR),
     190             :                  errmsg("cursor \"%s\" does not exist", stmt->portalname)));
     191         192 :         return;                 /* keep compiler happy */
     192             :     }
     193             : 
     194             :     /* Adjust dest if needed.  MOVE wants destination DestNone */
     195         199 :     if (stmt->ismove)
     196           6 :         dest = None_Receiver;
     197             : 
     198             :     /* Do it */
     199         199 :     nprocessed = PortalRunFetch(portal,
     200             :                                 stmt->direction,
     201             :                                 stmt->howMany,
     202             :                                 dest);
     203             : 
     204             :     /* Return command status if wanted */
     205         192 :     if (completionTag)
     206         192 :         snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s " UINT64_FORMAT,
     207         192 :                  stmt->ismove ? "MOVE" : "FETCH",
     208             :                  nprocessed);
     209             : }
     210             : 
     211             : /*
     212             :  * PerformPortalClose
     213             :  *      Close a cursor.
     214             :  */
     215             : void
     216          42 : PerformPortalClose(const char *name)
     217             : {
     218             :     Portal      portal;
     219             : 
     220             :     /* NULL means CLOSE ALL */
     221          42 :     if (name == NULL)
     222             :     {
     223           2 :         PortalHashTableDeleteAll();
     224           2 :         return;
     225             :     }
     226             : 
     227             :     /*
     228             :      * Disallow empty-string cursor name (conflicts with protocol-level
     229             :      * unnamed portal).
     230             :      */
     231          40 :     if (name[0] == '\0')
     232           0 :         ereport(ERROR,
     233             :                 (errcode(ERRCODE_INVALID_CURSOR_NAME),
     234             :                  errmsg("invalid cursor name: must not be empty")));
     235             : 
     236             :     /*
     237             :      * get the portal from the portal name
     238             :      */
     239          40 :     portal = GetPortalByName(name);
     240          40 :     if (!PortalIsValid(portal))
     241             :     {
     242           0 :         ereport(ERROR,
     243             :                 (errcode(ERRCODE_UNDEFINED_CURSOR),
     244             :                  errmsg("cursor \"%s\" does not exist", name)));
     245             :         return;                 /* keep compiler happy */
     246             :     }
     247             : 
     248             :     /*
     249             :      * Note: PortalCleanup is called as a side-effect, if not already done.
     250             :      */
     251          40 :     PortalDrop(portal, false);
     252             : }
     253             : 
     254             : /*
     255             :  * PortalCleanup
     256             :  *
     257             :  * Clean up a portal when it's dropped.  This is the standard cleanup hook
     258             :  * for portals.
     259             :  *
     260             :  * Note: if portal->status is PORTAL_FAILED, we are probably being called
     261             :  * during error abort, and must be careful to avoid doing anything that
     262             :  * is likely to fail again.
     263             :  */
     264             : void
     265       27376 : PortalCleanup(Portal portal)
     266             : {
     267             :     QueryDesc  *queryDesc;
     268             : 
     269             :     /*
     270             :      * sanity checks
     271             :      */
     272       27376 :     AssertArg(PortalIsValid(portal));
     273       27376 :     AssertArg(portal->cleanup == PortalCleanup);
     274             : 
     275             :     /*
     276             :      * Shut down executor, if still running.  We skip this during error abort,
     277             :      * since other mechanisms will take care of releasing executor resources,
     278             :      * and we can't be sure that ExecutorEnd itself wouldn't fail.
     279             :      */
     280       27376 :     queryDesc = PortalGetQueryDesc(portal);
     281       27376 :     if (queryDesc)
     282             :     {
     283             :         /*
     284             :          * Reset the queryDesc before anything else.  This prevents us from
     285             :          * trying to shut down the executor twice, in case of an error below.
     286             :          * The transaction abort mechanisms will take care of resource cleanup
     287             :          * in such a case.
     288             :          */
     289       10939 :         portal->queryDesc = NULL;
     290             : 
     291       10939 :         if (portal->status != PORTAL_FAILED)
     292             :         {
     293             :             ResourceOwner saveResourceOwner;
     294             : 
     295             :             /* We must make the portal's resource owner current */
     296       10510 :             saveResourceOwner = CurrentResourceOwner;
     297       10510 :             PG_TRY();
     298             :             {
     299       10510 :                 if (portal->resowner)
     300       10510 :                     CurrentResourceOwner = portal->resowner;
     301       10510 :                 ExecutorFinish(queryDesc);
     302       10510 :                 ExecutorEnd(queryDesc);
     303       10510 :                 FreeQueryDesc(queryDesc);
     304             :             }
     305           0 :             PG_CATCH();
     306             :             {
     307             :                 /* Ensure CurrentResourceOwner is restored on error */
     308           0 :                 CurrentResourceOwner = saveResourceOwner;
     309           0 :                 PG_RE_THROW();
     310             :             }
     311       10510 :             PG_END_TRY();
     312       10510 :             CurrentResourceOwner = saveResourceOwner;
     313             :         }
     314             :     }
     315       27376 : }
     316             : 
     317             : /*
     318             :  * PersistHoldablePortal
     319             :  *
     320             :  * Prepare the specified Portal for access outside of the current
     321             :  * transaction. When this function returns, all future accesses to the
     322             :  * portal must be done via the Tuplestore (not by invoking the
     323             :  * executor).
     324             :  */
     325             : void
     326           5 : PersistHoldablePortal(Portal portal)
     327             : {
     328           5 :     QueryDesc  *queryDesc = PortalGetQueryDesc(portal);
     329             :     Portal      saveActivePortal;
     330             :     ResourceOwner saveResourceOwner;
     331             :     MemoryContext savePortalContext;
     332             :     MemoryContext oldcxt;
     333             : 
     334             :     /*
     335             :      * If we're preserving a holdable portal, we had better be inside the
     336             :      * transaction that originally created it.
     337             :      */
     338           5 :     Assert(portal->createSubid != InvalidSubTransactionId);
     339           5 :     Assert(queryDesc != NULL);
     340             : 
     341             :     /*
     342             :      * Caller must have created the tuplestore already ... but not a snapshot.
     343             :      */
     344           5 :     Assert(portal->holdContext != NULL);
     345           5 :     Assert(portal->holdStore != NULL);
     346           5 :     Assert(portal->holdSnapshot == NULL);
     347             : 
     348             :     /*
     349             :      * Before closing down the executor, we must copy the tupdesc into
     350             :      * long-term memory, since it was created in executor memory.
     351             :      */
     352           5 :     oldcxt = MemoryContextSwitchTo(portal->holdContext);
     353             : 
     354           5 :     portal->tupDesc = CreateTupleDescCopy(portal->tupDesc);
     355             : 
     356           5 :     MemoryContextSwitchTo(oldcxt);
     357             : 
     358             :     /*
     359             :      * Check for improper portal use, and mark portal active.
     360             :      */
     361           5 :     MarkPortalActive(portal);
     362             : 
     363             :     /*
     364             :      * Set up global portal context pointers.
     365             :      */
     366           5 :     saveActivePortal = ActivePortal;
     367           5 :     saveResourceOwner = CurrentResourceOwner;
     368           5 :     savePortalContext = PortalContext;
     369           5 :     PG_TRY();
     370             :     {
     371           5 :         ActivePortal = portal;
     372           5 :         if (portal->resowner)
     373           5 :             CurrentResourceOwner = portal->resowner;
     374           5 :         PortalContext = PortalGetHeapMemory(portal);
     375             : 
     376           5 :         MemoryContextSwitchTo(PortalContext);
     377             : 
     378           5 :         PushActiveSnapshot(queryDesc->snapshot);
     379             : 
     380             :         /*
     381             :          * Rewind the executor: we need to store the entire result set in the
     382             :          * tuplestore, so that subsequent backward FETCHs can be processed.
     383             :          */
     384           5 :         ExecutorRewind(queryDesc);
     385             : 
     386             :         /*
     387             :          * Change the destination to output to the tuplestore.  Note we tell
     388             :          * the tuplestore receiver to detoast all data passed through it; this
     389             :          * makes it safe to not keep a snapshot associated with the data.
     390             :          */
     391           5 :         queryDesc->dest = CreateDestReceiver(DestTuplestore);
     392           5 :         SetTuplestoreDestReceiverParams(queryDesc->dest,
     393             :                                         portal->holdStore,
     394             :                                         portal->holdContext,
     395             :                                         true);
     396             : 
     397             :         /* Fetch the result set into the tuplestore */
     398           5 :         ExecutorRun(queryDesc, ForwardScanDirection, 0L, false);
     399             : 
     400           5 :         (*queryDesc->dest->rDestroy) (queryDesc->dest);
     401           5 :         queryDesc->dest = NULL;
     402             : 
     403             :         /*
     404             :          * Now shut down the inner executor.
     405             :          */
     406           5 :         portal->queryDesc = NULL;    /* prevent double shutdown */
     407           5 :         ExecutorFinish(queryDesc);
     408           5 :         ExecutorEnd(queryDesc);
     409           5 :         FreeQueryDesc(queryDesc);
     410             : 
     411             :         /*
     412             :          * Set the position in the result set.
     413             :          */
     414           5 :         MemoryContextSwitchTo(portal->holdContext);
     415             : 
     416           5 :         if (portal->atEnd)
     417             :         {
     418             :             /*
     419             :              * Just force the tuplestore forward to its end.  The size of the
     420             :              * skip request here is arbitrary.
     421             :              */
     422           0 :             while (tuplestore_skiptuples(portal->holdStore, 1000000, true))
     423             :                  /* continue */ ;
     424             :         }
     425             :         else
     426             :         {
     427           5 :             tuplestore_rescan(portal->holdStore);
     428             : 
     429           5 :             if (!tuplestore_skiptuples(portal->holdStore,
     430           5 :                                        portal->portalPos,
     431             :                                        true))
     432           0 :                 elog(ERROR, "unexpected end of tuple stream");
     433             :         }
     434             :     }
     435           0 :     PG_CATCH();
     436             :     {
     437             :         /* Uncaught error while executing portal: mark it dead */
     438           0 :         MarkPortalFailed(portal);
     439             : 
     440             :         /* Restore global vars and propagate error */
     441           0 :         ActivePortal = saveActivePortal;
     442           0 :         CurrentResourceOwner = saveResourceOwner;
     443           0 :         PortalContext = savePortalContext;
     444             : 
     445           0 :         PG_RE_THROW();
     446             :     }
     447           5 :     PG_END_TRY();
     448             : 
     449           5 :     MemoryContextSwitchTo(oldcxt);
     450             : 
     451             :     /* Mark portal not active */
     452           5 :     portal->status = PORTAL_READY;
     453             : 
     454           5 :     ActivePortal = saveActivePortal;
     455           5 :     CurrentResourceOwner = saveResourceOwner;
     456           5 :     PortalContext = savePortalContext;
     457             : 
     458           5 :     PopActiveSnapshot();
     459             : 
     460             :     /*
     461             :      * We can now release any subsidiary memory of the portal's heap context;
     462             :      * we'll never use it again.  The executor already dropped its context,
     463             :      * but this will clean up anything that glommed onto the portal's heap via
     464             :      * PortalContext.
     465             :      */
     466           5 :     MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
     467           5 : }

Generated by: LCOV version 1.11