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

Generated by: LCOV version 1.13