Header And Logo

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

Data Structures | Defines | Typedefs | Functions | Variables

portalmem.c File Reference

#include "postgres.h"
#include "access/xact.h"
#include "catalog/pg_type.h"
#include "commands/portalcmds.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
Include dependency graph for portalmem.c:

Go to the source code of this file.

Data Structures

struct  portalhashent

Defines

#define PORTALS_PER_USER   16
#define MAX_PORTALNAME_LEN   NAMEDATALEN
#define PortalHashTableLookup(NAME, PORTAL)
#define PortalHashTableInsert(PORTAL, NAME)
#define PortalHashTableDelete(PORTAL)

Typedefs

typedef struct portalhashent PortalHashEnt

Functions

void EnablePortalManager (void)
Portal GetPortalByName (const char *name)
NodePortalListGetPrimaryStmt (List *stmts)
Portal CreatePortal (const char *name, bool allowDup, bool dupSilent)
Portal CreateNewPortal (void)
void PortalDefineQuery (Portal portal, const char *prepStmtName, const char *sourceText, const char *commandTag, List *stmts, CachedPlan *cplan)
static void PortalReleaseCachedPlan (Portal portal)
void PortalCreateHoldStore (Portal portal)
void PinPortal (Portal portal)
void UnpinPortal (Portal portal)
void MarkPortalDone (Portal portal)
void MarkPortalFailed (Portal portal)
void PortalDrop (Portal portal, bool isTopCommit)
void PortalHashTableDeleteAll (void)
bool PreCommit_Portals (bool isPrepare)
void AtAbort_Portals (void)
void AtCleanup_Portals (void)
void AtSubCommit_Portals (SubTransactionId mySubid, SubTransactionId parentSubid, ResourceOwner parentXactOwner)
void AtSubAbort_Portals (SubTransactionId mySubid, SubTransactionId parentSubid, ResourceOwner parentXactOwner)
void AtSubCleanup_Portals (SubTransactionId mySubid)
Datum pg_cursor (PG_FUNCTION_ARGS)
bool ThereAreNoReadyPortals (void)

Variables

static HTABPortalHashTable = NULL
static MemoryContext PortalMemory = NULL

Define Documentation

#define MAX_PORTALNAME_LEN   NAMEDATALEN

Definition at line 44 of file portalmem.c.

#define PortalHashTableDelete (   PORTAL  ) 
Value:
do { \
    PortalHashEnt *hentry; \
    \
    hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
                                           PORTAL->name, HASH_REMOVE, NULL); \
    if (hentry == NULL) \
        elog(WARNING, "trying to delete portal name that does not exist"); \
} while(0)

Definition at line 79 of file portalmem.c.

Referenced by PortalDrop().

#define PortalHashTableInsert (   PORTAL,
  NAME 
)
Value:
do { \
    PortalHashEnt *hentry; bool found; \
    \
    hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
                                           (NAME), HASH_ENTER, &found); \
    if (found) \
        elog(ERROR, "duplicate portal name"); \
    hentry->portal = PORTAL; \
    /* To avoid duplicate storage, make PORTAL->name point to htab entry */ \
    PORTAL->name = hentry->portalname; \
} while(0)

Definition at line 66 of file portalmem.c.

Referenced by CreatePortal().

#define PortalHashTableLookup (   NAME,
  PORTAL 
)
Value:
do { \
    PortalHashEnt *hentry; \
    \
    hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
                                           (NAME), HASH_FIND, NULL); \
    if (hentry) \
        PORTAL = hentry->portal; \
    else \
        PORTAL = NULL; \
} while(0)

Definition at line 54 of file portalmem.c.

Referenced by GetPortalByName().

#define PORTALS_PER_USER   16

Definition at line 36 of file portalmem.c.

Referenced by EnablePortalManager().


Typedef Documentation

typedef struct portalhashent PortalHashEnt

Function Documentation

void AtAbort_Portals ( void   ) 

Definition at line 736 of file portalmem.c.

References PortalData::cleanup, PortalData::createSubid, hash_seq_init(), hash_seq_search(), InvalidSubTransactionId, MarkPortalFailed(), MemoryContextDeleteChildren(), NULL, PointerIsValid, portalhashent::portal, PORTAL_ACTIVE, PORTAL_READY, PortalGetHeapMemory, PortalReleaseCachedPlan(), PortalData::resowner, and PortalData::status.

Referenced by AbortTransaction().

{
    HASH_SEQ_STATUS status;
    PortalHashEnt *hentry;

    hash_seq_init(&status, PortalHashTable);

    while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
    {
        Portal      portal = hentry->portal;

        /* Any portal that was actually running has to be considered broken */
        if (portal->status == PORTAL_ACTIVE)
            MarkPortalFailed(portal);

        /*
         * Do nothing else to cursors held over from a previous transaction.
         */
        if (portal->createSubid == InvalidSubTransactionId)
            continue;

        /*
         * If it was created in the current transaction, we can't do normal
         * shutdown on a READY portal either; it might refer to objects
         * created in the failed transaction.  See comments in
         * AtSubAbort_Portals.
         */
        if (portal->status == PORTAL_READY)
            MarkPortalFailed(portal);

        /*
         * Allow portalcmds.c to clean up the state it knows about, if we
         * haven't already.
         */
        if (PointerIsValid(portal->cleanup))
        {
            (*portal->cleanup) (portal);
            portal->cleanup = NULL;
        }

        /* drop cached plan reference, if any */
        PortalReleaseCachedPlan(portal);

        /*
         * Any resources belonging to the portal will be released in the
         * upcoming transaction-wide cleanup; they will be gone before we run
         * PortalDrop.
         */
        portal->resowner = NULL;

        /*
         * Although we can't delete the portal data structure proper, we can
         * release any memory in subsidiary contexts, such as executor state.
         * The cleanup hook was the last thing that might have needed data
         * there.
         */
        MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
    }
}

void AtCleanup_Portals ( void   ) 

Definition at line 801 of file portalmem.c.

References Assert, PortalData::cleanup, PortalData::createSubid, hash_seq_init(), hash_seq_search(), InvalidSubTransactionId, NULL, portalhashent::portal, PORTAL_ACTIVE, PortalDrop(), PortalData::portalPinned, PortalData::resowner, and PortalData::status.

Referenced by CleanupTransaction().

{
    HASH_SEQ_STATUS status;
    PortalHashEnt *hentry;

    hash_seq_init(&status, PortalHashTable);

    while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
    {
        Portal      portal = hentry->portal;

        /* Do nothing to cursors held over from a previous transaction */
        if (portal->createSubid == InvalidSubTransactionId)
        {
            Assert(portal->status != PORTAL_ACTIVE);
            Assert(portal->resowner == NULL);
            continue;
        }

        /*
         * If a portal is still pinned, forcibly unpin it. PortalDrop will not
         * let us drop the portal otherwise. Whoever pinned the portal was
         * interrupted by the abort too and won't try to use it anymore.
         */
        if (portal->portalPinned)
            portal->portalPinned = false;

        /* We had better not be calling any user-defined code here */
        Assert(portal->cleanup == NULL);

        /* Zap it. */
        PortalDrop(portal, false);
    }
}

void AtSubAbort_Portals ( SubTransactionId  mySubid,
SubTransactionId  parentSubid,
ResourceOwner  parentXactOwner 
)

Definition at line 875 of file portalmem.c.

References PortalData::cleanup, PortalData::createSubid, hash_seq_init(), hash_seq_search(), MarkPortalFailed(), MemoryContextDeleteChildren(), NULL, PointerIsValid, portalhashent::portal, PORTAL_ACTIVE, PORTAL_READY, PortalGetHeapMemory, PortalReleaseCachedPlan(), PortalData::resowner, and PortalData::status.

Referenced by AbortSubTransaction().

{
    HASH_SEQ_STATUS status;
    PortalHashEnt *hentry;

    hash_seq_init(&status, PortalHashTable);

    while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
    {
        Portal      portal = hentry->portal;

        if (portal->createSubid != mySubid)
            continue;

        /*
         * Force any live portals of my own subtransaction into FAILED state.
         * We have to do this because they might refer to objects created or
         * changed in the failed subtransaction, leading to crashes if
         * execution is resumed, or even if we just try to run ExecutorEnd.
         * (Note we do NOT do this to upper-level portals, since they cannot
         * have such references and hence may be able to continue.)
         */
        if (portal->status == PORTAL_READY ||
            portal->status == PORTAL_ACTIVE)
            MarkPortalFailed(portal);

        /*
         * Allow portalcmds.c to clean up the state it knows about, if we
         * haven't already.
         */
        if (PointerIsValid(portal->cleanup))
        {
            (*portal->cleanup) (portal);
            portal->cleanup = NULL;
        }

        /* drop cached plan reference, if any */
        PortalReleaseCachedPlan(portal);

        /*
         * Any resources belonging to the portal will be released in the
         * upcoming transaction-wide cleanup; they will be gone before we run
         * PortalDrop.
         */
        portal->resowner = NULL;

        /*
         * Although we can't delete the portal data structure proper, we can
         * release any memory in subsidiary contexts, such as executor state.
         * The cleanup hook was the last thing that might have needed data
         * there.
         */
        MemoryContextDeleteChildren(PortalGetHeapMemory(portal));
    }
}

void AtSubCleanup_Portals ( SubTransactionId  mySubid  ) 

Definition at line 940 of file portalmem.c.

References Assert, PortalData::cleanup, PortalData::createSubid, hash_seq_init(), hash_seq_search(), NULL, portalhashent::portal, PortalDrop(), and PortalData::portalPinned.

Referenced by CleanupSubTransaction().

{
    HASH_SEQ_STATUS status;
    PortalHashEnt *hentry;

    hash_seq_init(&status, PortalHashTable);

    while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
    {
        Portal      portal = hentry->portal;

        if (portal->createSubid != mySubid)
            continue;

        /*
         * If a portal is still pinned, forcibly unpin it. PortalDrop will not
         * let us drop the portal otherwise. Whoever pinned the portal was
         * interrupted by the abort too and won't try to use it anymore.
         */
        if (portal->portalPinned)
            portal->portalPinned = false;

        /* We had better not be calling any user-defined code here */
        Assert(portal->cleanup == NULL);

        /* Zap it. */
        PortalDrop(portal, false);
    }
}

void AtSubCommit_Portals ( SubTransactionId  mySubid,
SubTransactionId  parentSubid,
ResourceOwner  parentXactOwner 
)

Definition at line 843 of file portalmem.c.

References PortalData::createSubid, hash_seq_init(), hash_seq_search(), NULL, portalhashent::portal, ResourceOwnerNewParent(), and PortalData::resowner.

Referenced by CommitSubTransaction().

{
    HASH_SEQ_STATUS status;
    PortalHashEnt *hentry;

    hash_seq_init(&status, PortalHashTable);

    while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
    {
        Portal      portal = hentry->portal;

        if (portal->createSubid == mySubid)
        {
            portal->createSubid = parentSubid;
            if (portal->resowner)
                ResourceOwnerNewParent(portal->resowner, parentXactOwner);
        }
    }
}

Portal CreateNewPortal ( void   ) 

Definition at line 253 of file portalmem.c.

References CreatePortal(), GetPortalByName(), and NULL.

Referenced by ExecuteQuery(), and SPI_cursor_open_internal().

{
    static unsigned int unnamed_portal_count = 0;

    char        portalname[MAX_PORTALNAME_LEN];

    /* Select a nonconflicting name */
    for (;;)
    {
        unnamed_portal_count++;
        sprintf(portalname, "<unnamed portal %u>", unnamed_portal_count);
        if (GetPortalByName(portalname) == NULL)
            break;
    }

    return CreatePortal(portalname, false, false);
}

Portal CreatePortal ( const char *  name,
bool  allowDup,
bool  dupSilent 
)

Definition at line 196 of file portalmem.c.

References ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE, ALLOCSET_SMALL_MINSIZE, AllocSetContextCreate(), AssertArg, PortalData::atEnd, PortalData::atStart, PortalData::cleanup, PortalData::createSubid, PortalData::creation_time, PortalData::cursorOptions, CurTransactionResourceOwner, ereport, errcode(), errmsg(), ERROR, GetCurrentStatementStartTimestamp(), GetCurrentSubTransactionId(), GetPortalByName(), PortalData::heap, MemoryContextAllocZero(), PointerIsValid, PortalDrop(), PortalHashTableInsert, PortalIsValid, ResourceOwnerCreate(), PortalData::resowner, PortalData::status, PortalData::strategy, PortalData::visible, and WARNING.

Referenced by CreateNewPortal(), exec_bind_message(), exec_simple_query(), PerformCursorOpen(), and SPI_cursor_open_internal().

{
    Portal      portal;

    AssertArg(PointerIsValid(name));

    portal = GetPortalByName(name);
    if (PortalIsValid(portal))
    {
        if (!allowDup)
            ereport(ERROR,
                    (errcode(ERRCODE_DUPLICATE_CURSOR),
                     errmsg("cursor \"%s\" already exists", name)));
        if (!dupSilent)
            ereport(WARNING,
                    (errcode(ERRCODE_DUPLICATE_CURSOR),
                     errmsg("closing existing cursor \"%s\"",
                            name)));
        PortalDrop(portal, false);
    }

    /* make new portal structure */
    portal = (Portal) MemoryContextAllocZero(PortalMemory, sizeof *portal);

    /* initialize portal heap context; typically it won't store much */
    portal->heap = AllocSetContextCreate(PortalMemory,
                                         "PortalHeapMemory",
                                         ALLOCSET_SMALL_MINSIZE,
                                         ALLOCSET_SMALL_INITSIZE,
                                         ALLOCSET_SMALL_MAXSIZE);

    /* create a resource owner for the portal */
    portal->resowner = ResourceOwnerCreate(CurTransactionResourceOwner,
                                           "Portal");

    /* initialize portal fields that don't start off zero */
    portal->status = PORTAL_NEW;
    portal->cleanup = PortalCleanup;
    portal->createSubid = GetCurrentSubTransactionId();
    portal->strategy = PORTAL_MULTI_QUERY;
    portal->cursorOptions = CURSOR_OPT_NO_SCROLL;
    portal->atStart = true;
    portal->atEnd = true;       /* disallow fetches until query is set */
    portal->visible = true;
    portal->creation_time = GetCurrentStatementStartTimestamp();

    /* put portal in table (sets portal->name) */
    PortalHashTableInsert(portal, name);

    return portal;
}

void EnablePortalManager ( void   ) 
Portal GetPortalByName ( const char *  name  ) 
void MarkPortalDone ( Portal  portal  ) 

Definition at line 412 of file portalmem.c.

References Assert, PortalData::cleanup, PointerIsValid, PORTAL_ACTIVE, and PortalData::status.

Referenced by PortalRun().

{
    /* Perform the state transition */
    Assert(portal->status == PORTAL_ACTIVE);
    portal->status = PORTAL_DONE;

    /*
     * Allow portalcmds.c to clean up the state it knows about.  We might as
     * well do that now, since the portal can't be executed any more.
     *
     * In some cases involving execution of a ROLLBACK command in an already
     * aborted transaction, this prevents an assertion failure caused by
     * reaching AtCleanup_Portals with the cleanup hook still unexecuted.
     */
    if (PointerIsValid(portal->cleanup))
    {
        (*portal->cleanup) (portal);
        portal->cleanup = NULL;
    }
}

void MarkPortalFailed ( Portal  portal  ) 

Definition at line 440 of file portalmem.c.

References Assert, PortalData::cleanup, PointerIsValid, PORTAL_DONE, and PortalData::status.

Referenced by AtAbort_Portals(), AtSubAbort_Portals(), PersistHoldablePortal(), PortalRun(), PortalRunFetch(), and PortalStart().

{
    /* Perform the state transition */
    Assert(portal->status != PORTAL_DONE);
    portal->status = PORTAL_FAILED;

    /*
     * Allow portalcmds.c to clean up the state it knows about.  We might as
     * well do that now, since the portal can't be executed any more.
     *
     * In some cases involving cleanup of an already aborted transaction, this
     * prevents an assertion failure caused by reaching AtCleanup_Portals with
     * the cleanup hook still unexecuted.
     */
    if (PointerIsValid(portal->cleanup))
    {
        (*portal->cleanup) (portal);
        portal->cleanup = NULL;
    }
}

Datum pg_cursor ( PG_FUNCTION_ARGS   ) 

Definition at line 972 of file portalmem.c.

References ReturnSetInfo::allowedModes, BoolGetDatum, BOOLOID, CreateTemplateTupleDesc(), PortalData::creation_time, CStringGetTextDatum, CURSOR_OPT_BINARY, CURSOR_OPT_HOLD, CURSOR_OPT_SCROLL, PortalData::cursorOptions, ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, ereport, errcode(), errmsg(), ERROR, hash_seq_init(), hash_seq_search(), IsA, MemoryContextSwitchTo(), MemSet, PortalData::name, NULL, ReturnSetInfo::returnMode, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, SFRM_Materialize_Random, PortalData::sourceText, TEXTOID, TimestampTzGetDatum, TIMESTAMPTZOID, TupleDescInitEntry(), tuplestore_begin_heap(), tuplestore_donestoring, tuplestore_putvalues(), values, PortalData::visible, and work_mem.

{
    ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
    TupleDesc   tupdesc;
    Tuplestorestate *tupstore;
    MemoryContext per_query_ctx;
    MemoryContext oldcontext;
    HASH_SEQ_STATUS hash_seq;
    PortalHashEnt *hentry;

    /* check to see if caller supports us returning a tuplestore */
    if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("set-valued function called in context that cannot accept a set")));
    if (!(rsinfo->allowedModes & SFRM_Materialize))
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("materialize mode required, but it is not " \
                        "allowed in this context")));

    /* need to build tuplestore in query context */
    per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
    oldcontext = MemoryContextSwitchTo(per_query_ctx);

    /*
     * build tupdesc for result tuples. This must match the definition of the
     * pg_cursors view in system_views.sql
     */
    tupdesc = CreateTemplateTupleDesc(6, false);
    TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
                       TEXTOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
                       TEXTOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber) 3, "is_holdable",
                       BOOLOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber) 4, "is_binary",
                       BOOLOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber) 5, "is_scrollable",
                       BOOLOID, -1, 0);
    TupleDescInitEntry(tupdesc, (AttrNumber) 6, "creation_time",
                       TIMESTAMPTZOID, -1, 0);

    /*
     * We put all the tuples into a tuplestore in one scan of the hashtable.
     * This avoids any issue of the hashtable possibly changing between calls.
     */
    tupstore =
        tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
                              false, work_mem);

    /* generate junk in short-term context */
    MemoryContextSwitchTo(oldcontext);

    hash_seq_init(&hash_seq, PortalHashTable);
    while ((hentry = hash_seq_search(&hash_seq)) != NULL)
    {
        Portal      portal = hentry->portal;
        Datum       values[6];
        bool        nulls[6];

        /* report only "visible" entries */
        if (!portal->visible)
            continue;

        MemSet(nulls, 0, sizeof(nulls));

        values[0] = CStringGetTextDatum(portal->name);
        values[1] = CStringGetTextDatum(portal->sourceText);
        values[2] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_HOLD);
        values[3] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_BINARY);
        values[4] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_SCROLL);
        values[5] = TimestampTzGetDatum(portal->creation_time);

        tuplestore_putvalues(tupstore, tupdesc, values, nulls);
    }

    /* clean up and return the tuplestore */
    tuplestore_donestoring(tupstore);

    rsinfo->returnMode = SFRM_Materialize;
    rsinfo->setResult = tupstore;
    rsinfo->setDesc = tupdesc;

    return (Datum) 0;
}

void PinPortal ( Portal  portal  ) 

Definition at line 388 of file portalmem.c.

References elog, ERROR, and PortalData::portalPinned.

Referenced by exec_for_query().

{
    if (portal->portalPinned)
        elog(ERROR, "portal already pinned");

    portal->portalPinned = true;
}

void PortalCreateHoldStore ( Portal  portal  ) 

Definition at line 347 of file portalmem.c.

References ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), Assert, CURSOR_OPT_SCROLL, PortalData::cursorOptions, PortalData::holdContext, PortalData::holdStore, MemoryContextSwitchTo(), NULL, tuplestore_begin_heap(), and work_mem.

Referenced by FillPortalStore(), and PreCommit_Portals().

{
    MemoryContext oldcxt;

    Assert(portal->holdContext == NULL);
    Assert(portal->holdStore == NULL);

    /*
     * Create the memory context that is used for storage of the tuple set.
     * Note this is NOT a child of the portal's heap memory.
     */
    portal->holdContext =
        AllocSetContextCreate(PortalMemory,
                              "PortalHoldContext",
                              ALLOCSET_DEFAULT_MINSIZE,
                              ALLOCSET_DEFAULT_INITSIZE,
                              ALLOCSET_DEFAULT_MAXSIZE);

    /*
     * Create the tuple store, selecting cross-transaction temp files, and
     * enabling random access only if cursor requires scrolling.
     *
     * XXX: Should maintenance_work_mem be used for the portal size?
     */
    oldcxt = MemoryContextSwitchTo(portal->holdContext);

    portal->holdStore =
        tuplestore_begin_heap(portal->cursorOptions & CURSOR_OPT_SCROLL,
                              true, work_mem);

    MemoryContextSwitchTo(oldcxt);
}

void PortalDefineQuery ( Portal  portal,
const char *  prepStmtName,
const char *  sourceText,
const char *  commandTag,
List stmts,
CachedPlan cplan 
)

Definition at line 300 of file portalmem.c.

References AssertArg, AssertState, PortalData::commandTag, PortalData::cplan, NIL, NULL, PORTAL_NEW, PortalIsValid, PortalData::prepStmtName, PortalData::sourceText, PortalData::status, and PortalData::stmts.

Referenced by exec_bind_message(), exec_simple_query(), ExecuteQuery(), PerformCursorOpen(), and SPI_cursor_open_internal().

{
    AssertArg(PortalIsValid(portal));
    AssertState(portal->status == PORTAL_NEW);

    AssertArg(sourceText != NULL);
    AssertArg(commandTag != NULL || stmts == NIL);

    portal->prepStmtName = prepStmtName;
    portal->sourceText = sourceText;
    portal->commandTag = commandTag;
    portal->stmts = stmts;
    portal->cplan = cplan;
    portal->status = PORTAL_DEFINED;
}

void PortalDrop ( Portal  portal,
bool  isTopCommit 
)

Definition at line 466 of file portalmem.c.

References AssertArg, PortalData::cleanup, ereport, errcode(), errmsg(), ERROR, PortalData::holdContext, PortalData::holdStore, MemoryContextDelete(), MemoryContextSwitchTo(), PortalData::name, pfree(), PointerIsValid, PORTAL_ACTIVE, PORTAL_FAILED, PortalGetHeapMemory, PortalHashTableDelete, PortalIsValid, PortalData::portalPinned, PortalReleaseCachedPlan(), RESOURCE_RELEASE_AFTER_LOCKS, RESOURCE_RELEASE_BEFORE_LOCKS, RESOURCE_RELEASE_LOCKS, ResourceOwnerDelete(), ResourceOwnerRelease(), PortalData::resowner, PortalData::status, and tuplestore_end().

Referenced by AtCleanup_Portals(), AtSubCleanup_Portals(), CreatePortal(), exec_simple_query(), ExecuteQuery(), PerformPortalClose(), PortalHashTableDeleteAll(), PostgresMain(), PreCommit_Portals(), and SPI_cursor_close().

{
    AssertArg(PortalIsValid(portal));

    /*
     * Don't allow dropping a pinned portal, it's still needed by whoever
     * pinned it. Not sure if the PORTAL_ACTIVE case can validly happen or
     * not...
     */
    if (portal->portalPinned ||
        portal->status == PORTAL_ACTIVE)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_CURSOR_STATE),
                 errmsg("cannot drop active portal \"%s\"", portal->name)));

    /*
     * Allow portalcmds.c to clean up the state it knows about, in particular
     * shutting down the executor if still active.  This step potentially runs
     * user-defined code so failure has to be expected.  It's the cleanup
     * hook's responsibility to not try to do that more than once, in the case
     * that failure occurs and then we come back to drop the portal again
     * during transaction abort.
     *
     * Note: in most paths of control, this will have been done already in
     * MarkPortalDone or MarkPortalFailed.  We're just making sure.
     */
    if (PointerIsValid(portal->cleanup))
    {
        (*portal->cleanup) (portal);
        portal->cleanup = NULL;
    }

    /*
     * Remove portal from hash table.  Because we do this here, we will not
     * come back to try to remove the portal again if there's any error in the
     * subsequent steps.  Better to leak a little memory than to get into an
     * infinite error-recovery loop.
     */
    PortalHashTableDelete(portal);

    /* drop cached plan reference, if any */
    PortalReleaseCachedPlan(portal);

    /*
     * Release any resources still attached to the portal.  There are several
     * cases being covered here:
     *
     * Top transaction commit (indicated by isTopCommit): normally we should
     * do nothing here and let the regular end-of-transaction resource
     * releasing mechanism handle these resources too.  However, if we have a
     * FAILED portal (eg, a cursor that got an error), we'd better clean up
     * its resources to avoid resource-leakage warning messages.
     *
     * Sub transaction commit: never comes here at all, since we don't kill
     * any portals in AtSubCommit_Portals().
     *
     * Main or sub transaction abort: we will do nothing here because
     * portal->resowner was already set NULL; the resources were already
     * cleaned up in transaction abort.
     *
     * Ordinary portal drop: must release resources.  However, if the portal
     * is not FAILED then we do not release its locks.  The locks become the
     * responsibility of the transaction's ResourceOwner (since it is the
     * parent of the portal's owner) and will be released when the transaction
     * eventually ends.
     */
    if (portal->resowner &&
        (!isTopCommit || portal->status == PORTAL_FAILED))
    {
        bool        isCommit = (portal->status != PORTAL_FAILED);

        ResourceOwnerRelease(portal->resowner,
                             RESOURCE_RELEASE_BEFORE_LOCKS,
                             isCommit, false);
        ResourceOwnerRelease(portal->resowner,
                             RESOURCE_RELEASE_LOCKS,
                             isCommit, false);
        ResourceOwnerRelease(portal->resowner,
                             RESOURCE_RELEASE_AFTER_LOCKS,
                             isCommit, false);
        ResourceOwnerDelete(portal->resowner);
    }
    portal->resowner = NULL;

    /*
     * Delete tuplestore if present.  We should do this even under error
     * conditions; since the tuplestore would have been using cross-
     * transaction storage, its temp files need to be explicitly deleted.
     */
    if (portal->holdStore)
    {
        MemoryContext oldcontext;

        oldcontext = MemoryContextSwitchTo(portal->holdContext);
        tuplestore_end(portal->holdStore);
        MemoryContextSwitchTo(oldcontext);
        portal->holdStore = NULL;
    }

    /* delete tuplestore storage, if any */
    if (portal->holdContext)
        MemoryContextDelete(portal->holdContext);

    /* release subsidiary storage */
    MemoryContextDelete(PortalGetHeapMemory(portal));

    /* release portal struct (it's in PortalMemory) */
    pfree(portal);
}

void PortalHashTableDeleteAll ( void   ) 

Definition at line 582 of file portalmem.c.

References hash_seq_init(), hash_seq_search(), hash_seq_term(), NULL, portalhashent::portal, PORTAL_ACTIVE, PortalDrop(), and PortalData::status.

Referenced by DiscardAll(), and PerformPortalClose().

{
    HASH_SEQ_STATUS status;
    PortalHashEnt *hentry;

    if (PortalHashTable == NULL)
        return;

    hash_seq_init(&status, PortalHashTable);
    while ((hentry = hash_seq_search(&status)) != NULL)
    {
        Portal      portal = hentry->portal;

        /* Can't close the active portal (the one running the command) */
        if (portal->status == PORTAL_ACTIVE)
            continue;

        PortalDrop(portal, false);

        /* Restart the iteration in case that led to other drops */
        hash_seq_term(&status);
        hash_seq_init(&status, PortalHashTable);
    }
}

Node* PortalListGetPrimaryStmt ( List stmts  ) 

Definition at line 158 of file portalmem.c.

References IsA, lfirst, and list_length().

Referenced by CachedPlanGetTargetList(), and PlanCacheComputeResultDesc().

{
    ListCell   *lc;

    foreach(lc, stmts)
    {
        Node       *stmt = (Node *) lfirst(lc);

        if (IsA(stmt, PlannedStmt))
        {
            if (((PlannedStmt *) stmt)->canSetTag)
                return stmt;
        }
        else if (IsA(stmt, Query))
        {
            if (((Query *) stmt)->canSetTag)
                return stmt;
        }
        else
        {
            /* Utility stmts are assumed canSetTag if they're the only stmt */
            if (list_length(stmts) == 1)
                return stmt;
        }
    }
    return NULL;
}

static void PortalReleaseCachedPlan ( Portal  portal  )  [static]

Definition at line 326 of file portalmem.c.

References PortalData::cplan, ReleaseCachedPlan(), and PortalData::stmts.

Referenced by AtAbort_Portals(), AtSubAbort_Portals(), PortalDrop(), and PreCommit_Portals().

{
    if (portal->cplan)
    {
        ReleaseCachedPlan(portal->cplan, false);
        portal->cplan = NULL;

        /*
         * We must also clear portal->stmts which is now a dangling reference
         * to the cached plan's plan list.  This protects any code that might
         * try to examine the Portal later.
         */
        portal->stmts = NIL;
    }
}

bool PreCommit_Portals ( bool  isPrepare  ) 

Definition at line 621 of file portalmem.c.

References PortalData::createSubid, CURSOR_OPT_HOLD, PortalData::cursorOptions, elog, ereport, errcode(), errmsg(), ERROR, hash_seq_init(), hash_seq_search(), hash_seq_term(), InvalidSubTransactionId, NULL, PersistHoldablePortal(), portalhashent::portal, PORTAL_ACTIVE, PORTAL_READY, PortalCreateHoldStore(), PortalDrop(), PortalData::portalPinned, PortalReleaseCachedPlan(), PortalData::resowner, and PortalData::status.

Referenced by CommitTransaction(), and PrepareTransaction().

{
    bool        result = false;
    HASH_SEQ_STATUS status;
    PortalHashEnt *hentry;

    hash_seq_init(&status, PortalHashTable);

    while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
    {
        Portal      portal = hentry->portal;

        /*
         * There should be no pinned portals anymore. Complain if someone
         * leaked one.
         */
        if (portal->portalPinned)
            elog(ERROR, "cannot commit while a portal is pinned");

        /*
         * Do not touch active portals --- this can only happen in the case of
         * a multi-transaction utility command, such as VACUUM.
         *
         * Note however that any resource owner attached to such a portal is
         * still going to go away, so don't leave a dangling pointer.
         */
        if (portal->status == PORTAL_ACTIVE)
        {
            portal->resowner = NULL;
            continue;
        }

        /* Is it a holdable portal created in the current xact? */
        if ((portal->cursorOptions & CURSOR_OPT_HOLD) &&
            portal->createSubid != InvalidSubTransactionId &&
            portal->status == PORTAL_READY)
        {
            /*
             * We are exiting the transaction that created a holdable cursor.
             * Instead of dropping the portal, prepare it for access by later
             * transactions.
             *
             * However, if this is PREPARE TRANSACTION rather than COMMIT,
             * refuse PREPARE, because the semantics seem pretty unclear.
             */
            if (isPrepare)
                ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                         errmsg("cannot PREPARE a transaction that has created a cursor WITH HOLD")));

            /*
             * Note that PersistHoldablePortal() must release all resources
             * used by the portal that are local to the creating transaction.
             */
            PortalCreateHoldStore(portal);
            PersistHoldablePortal(portal);

            /* drop cached plan reference, if any */
            PortalReleaseCachedPlan(portal);

            /*
             * Any resources belonging to the portal will be released in the
             * upcoming transaction-wide cleanup; the portal will no longer
             * have its own resources.
             */
            portal->resowner = NULL;

            /*
             * Having successfully exported the holdable cursor, mark it as
             * not belonging to this transaction.
             */
            portal->createSubid = InvalidSubTransactionId;

            /* Report we changed state */
            result = true;
        }
        else if (portal->createSubid == InvalidSubTransactionId)
        {
            /*
             * Do nothing to cursors held over from a previous transaction
             * (including ones we just froze in a previous cycle of this loop)
             */
            continue;
        }
        else
        {
            /* Zap all non-holdable portals */
            PortalDrop(portal, true);

            /* Report we changed state */
            result = true;
        }

        /*
         * After either freezing or dropping a portal, we have to restart the
         * iteration, because we could have invoked user-defined code that
         * caused a drop of the next portal in the hash chain.
         */
        hash_seq_term(&status);
        hash_seq_init(&status, PortalHashTable);
    }

    return result;
}

bool ThereAreNoReadyPortals ( void   ) 

Definition at line 1060 of file portalmem.c.

References hash_seq_init(), hash_seq_search(), NULL, portalhashent::portal, PORTAL_READY, and PortalData::status.

Referenced by CopyFrom().

{
    HASH_SEQ_STATUS status;
    PortalHashEnt *hentry;

    hash_seq_init(&status, PortalHashTable);

    while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
    {
        Portal      portal = hentry->portal;

        if (portal->status == PORTAL_READY)
            return false;
    }

    return true;
}

void UnpinPortal ( Portal  portal  ) 

Definition at line 397 of file portalmem.c.

References elog, ERROR, and PortalData::portalPinned.

Referenced by exec_for_query().

{
    if (!portal->portalPinned)
        elog(ERROR, "portal not pinned");

    portal->portalPinned = false;
}


Variable Documentation

HTAB* PortalHashTable = NULL [static]

Definition at line 52 of file portalmem.c.

MemoryContext PortalMemory = NULL [static]

Definition at line 89 of file portalmem.c.