/*-------------------------------------------------------------------------
*
- * portalmem.c--
- * backend portal memory context management stuff
+ * portalmem.c
+ * backend portal memory management
*
- * Copyright (c) 1994, Regents of the University of California
+ * Portals are objects representing the execution state of a query.
+ * This module provides memory management services for portals, but it
+ * doesn't actually run the executor for them.
*
*
+ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.12 1998/06/15 19:29:53 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.61 2003/08/04 02:40:08 momjian Exp $
*
*-------------------------------------------------------------------------
*/
-/*
- * NOTES
- * Do not confuse "Portal" with "PortalEntry" (or "PortalBuffer").
- * When a PQexec() routine is run, the resulting tuples
- * find their way into a "PortalEntry". The contents of the resulting
- * "PortalEntry" can then be inspected by other PQxxx functions.
- *
- * A "Portal" is a structure used to keep track of queries of the
- * form:
- * retrieve portal FOO ( blah... ) where blah...
- *
- * When the backend sees a "retrieve portal" query, it allocates
- * a "PortalD" structure, plans the query and then stores the query
- * in the portal without executing it. Later, when the backend
- * sees a
- * fetch 1 into FOO
- *
- * the system looks up the portal named "FOO" in the portal table,
- * gets the planned query and then calls the executor with a feature of
- * '(EXEC_FOR 1). The executor then runs the query and returns a single
- * tuple. The problem is that we have to hold onto the state of the
- * portal query until we see a "close p". This means we have to be
- * careful about memory management.
- *
- * Having said all that, here is what a PortalD currently looks like:
- *
- * struct PortalD {
- * char* name;
- * classObj(PortalVariableMemory) variable;
- * classObj(PortalHeapMemory) heap;
- * List queryDesc;
- * EState state;
- * void (*cleanup) ARGS((Portal portal));
- * };
- *
- * I hope this makes things clearer to whoever reads this -cim 2/22/91
- *
- * Here is an old comment taken from nodes/memnodes.h
- *
- * MemoryContext --
- * A logical context in which memory allocations occur.
- *
- * The types of memory contexts can be thought of as members of the
- * following inheritance hierarchy with properties summarized below.
- *
- * Node
- * |
- * MemoryContext___
- * / \
- * GlobalMemory PortalMemoryContext
- * / \
- * PortalVariableMemory PortalHeapMemory
- *
- * Flushed at Flushed at Checkpoints
- * Transaction Portal
- * Commit Close
- *
- * GlobalMemory n n n
- * PortalVariableMemory n y n
- * PortalHeapMemory y y y *
- *
- */
-#include <stdio.h> /* for sprintf() */
-#include <string.h> /* for strlen, strncpy */
-
#include "postgres.h"
-#include "lib/hasht.h"
-#include "utils/module.h"
-#include "utils/excid.h" /* for Unimplemented */
-#include "utils/mcxt.h"
+#include "miscadmin.h"
+#include "commands/portalcmds.h"
+#include "executor/executor.h"
#include "utils/hsearch.h"
-
-#include "nodes/memnodes.h"
-#include "nodes/nodes.h"
-#include "nodes/pg_list.h"
-#include "nodes/execnodes.h" /* for EState */
-
+#include "utils/memutils.h"
#include "utils/portal.h"
-static void CollectNamedPortals(Portal *portalP, int destroy);
-static Portal PortalHeapMemoryGetPortal(PortalHeapMemory context);
-static PortalVariableMemory PortalHeapMemoryGetVariableMemory(PortalHeapMemory context);
-static void PortalResetHeapMemory(Portal portal);
-static Portal PortalVariableMemoryGetPortal(PortalVariableMemory context);
-
-/* ----------------
- * ALLOCFREE_ERROR_ABORT
- * define this if you want a core dump when you try to
- * free memory already freed -cim 2/9/91
- * ----------------
+/*
+ * estimate of the maximum number of open portals a user would have,
+ * used in initially sizing the PortalHashTable in EnablePortalManager()
*/
-#undef ALLOCFREE_ERROR_ABORT
+#define PORTALS_PER_USER 64
+
/* ----------------
* Global state
* ----------------
*/
-static int PortalManagerEnableCount = 0;
-
-#define MAX_PORTALNAME_LEN 64 /* XXX LONGALIGNable value */
+#define MAX_PORTALNAME_LEN NAMEDATALEN
typedef struct portalhashent
{
Portal portal;
} PortalHashEnt;
-#define PortalManagerEnabled (PortalManagerEnableCount >= 1)
-
static HTAB *PortalHashTable = NULL;
#define PortalHashTableLookup(NAME, PORTAL) \
do { \
- PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \
+ PortalHashEnt *hentry; char key[MAX_PORTALNAME_LEN]; \
\
MemSet(key, 0, MAX_PORTALNAME_LEN); \
- sprintf(key, "%s", NAME); \
+ StrNCpy(key, NAME, MAX_PORTALNAME_LEN); \
hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
- key, HASH_FIND, &found); \
- if (hentry == NULL) \
- elog(FATAL, "error in PortalHashTable"); \
- if (found) \
+ key, HASH_FIND, NULL); \
+ if (hentry) \
PORTAL = hentry->portal; \
else \
PORTAL = NULL; \
} while(0)
-#define PortalHashTableInsert(PORTAL) \
+#define PortalHashTableInsert(PORTAL, NAME) \
do { \
PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \
\
MemSet(key, 0, MAX_PORTALNAME_LEN); \
- sprintf(key, "%s", PORTAL->name); \
+ StrNCpy(key, NAME, MAX_PORTALNAME_LEN); \
hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
key, HASH_ENTER, &found); \
if (hentry == NULL) \
- elog(FATAL, "error in PortalHashTable"); \
+ ereport(ERROR, \
+ (errcode(ERRCODE_OUT_OF_MEMORY), \
+ errmsg("out of memory"))); \
if (found) \
- elog(NOTICE, "trying to insert a portal name that exists."); \
+ 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)
#define PortalHashTableDelete(PORTAL) \
-{ \
- PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \
+do { \
+ PortalHashEnt *hentry; char key[MAX_PORTALNAME_LEN]; \
\
MemSet(key, 0, MAX_PORTALNAME_LEN); \
- sprintf(key, "%s", PORTAL->name); \
+ StrNCpy(key, PORTAL->name, MAX_PORTALNAME_LEN); \
hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
- key, HASH_REMOVE, &found); \
+ key, HASH_REMOVE, NULL); \
if (hentry == NULL) \
- elog(FATAL, "error in PortalHashTable"); \
- if (!found) \
- elog(NOTICE, "trying to delete portal name that does not exist."); \
+ elog(WARNING, "trying to delete portal name that does not exist"); \
} while(0)
-static GlobalMemory PortalMemory = NULL;
-static char PortalMemoryName[] = "Portal";
-
-static Portal BlankPortal = NULL;
-
-/* ----------------
- * Internal class definitions
- * ----------------
- */
-typedef struct HeapMemoryBlockData
-{
- AllocSetData setData;
- FixedItemData itemData;
-} HeapMemoryBlockData;
-
-typedef HeapMemoryBlockData *HeapMemoryBlock;
-
-#define HEAPMEMBLOCK(context) \
- ((HeapMemoryBlock)(context)->block)
-
-/* ----------------------------------------------------------------
- * Variable and heap memory methods
- * ----------------------------------------------------------------
- */
-/* ----------------
- * PortalVariableMemoryAlloc
- * ----------------
- */
-static Pointer
-PortalVariableMemoryAlloc(PortalVariableMemory this,
- Size size)
-{
- return (AllocSetAlloc(&this->setData, size));
-}
-
-/* ----------------
- * PortalVariableMemoryFree
- * ----------------
- */
-static void
-PortalVariableMemoryFree(PortalVariableMemory this,
- Pointer pointer)
-{
- AllocSetFree(&this->setData, pointer);
-}
-
-/* ----------------
- * PortalVariableMemoryRealloc
- * ----------------
- */
-static Pointer
-PortalVariableMemoryRealloc(PortalVariableMemory this,
- Pointer pointer,
- Size size)
-{
- return (AllocSetRealloc(&this->setData, pointer, size));
-}
-
-/* ----------------
- * PortalVariableMemoryGetName
- * ----------------
- */
-static char *
-PortalVariableMemoryGetName(PortalVariableMemory this)
-{
- return (form("%s-var", PortalVariableMemoryGetPortal(this)->name));
-}
-
-/* ----------------
- * PortalVariableMemoryDump
- * ----------------
- */
-static void
-PortalVariableMemoryDump(PortalVariableMemory this)
-{
- printf("--\n%s:\n", PortalVariableMemoryGetName(this));
-
- AllocSetDump(&this->setData); /* XXX is this the right interface */
-}
-
-/* ----------------
- * PortalHeapMemoryAlloc
- * ----------------
- */
-static Pointer
-PortalHeapMemoryAlloc(PortalHeapMemory this,
- Size size)
-{
- HeapMemoryBlock block = HEAPMEMBLOCK(this);
-
- AssertState(PointerIsValid(block));
-
- return (AllocSetAlloc(&block->setData, size));
-}
-
-/* ----------------
- * PortalHeapMemoryFree
- * ----------------
- */
-static void
-PortalHeapMemoryFree(PortalHeapMemory this,
- Pointer pointer)
-{
- HeapMemoryBlock block = HEAPMEMBLOCK(this);
-
- AssertState(PointerIsValid(block));
-
- if (AllocSetContains(&block->setData, pointer))
- AllocSetFree(&block->setData, pointer);
- else
- {
- elog(NOTICE,
- "PortalHeapMemoryFree: 0x%x not in alloc set!",
- pointer);
-#ifdef ALLOCFREE_ERROR_ABORT
- Assert(AllocSetContains(&block->setData, pointer));
-#endif /* ALLOCFREE_ERROR_ABORT */
- }
-}
-
-/* ----------------
- * PortalHeapMemoryRealloc
- * ----------------
- */
-static Pointer
-PortalHeapMemoryRealloc(PortalHeapMemory this,
- Pointer pointer,
- Size size)
-{
- HeapMemoryBlock block = HEAPMEMBLOCK(this);
-
- AssertState(PointerIsValid(block));
-
- return (AllocSetRealloc(&block->setData, pointer, size));
-}
-
-/* ----------------
- * PortalHeapMemoryGetName
- * ----------------
- */
-static char *
-PortalHeapMemoryGetName(PortalHeapMemory this)
-{
- return (form("%s-heap", PortalHeapMemoryGetPortal(this)->name));
-}
-
-/* ----------------
- * PortalHeapMemoryDump
- * ----------------
- */
-static void
-PortalHeapMemoryDump(PortalHeapMemory this)
-{
- HeapMemoryBlock block;
-
- printf("--\n%s:\n", PortalHeapMemoryGetName(this));
-
- /* XXX is this the right interface */
- if (PointerIsValid(this->block))
- AllocSetDump(&HEAPMEMBLOCK(this)->setData);
-
- /* dump the stack too */
- for (block = (HeapMemoryBlock) FixedStackGetTop(&this->stackData);
- PointerIsValid(block);
- block = (HeapMemoryBlock)
- FixedStackGetNext(&this->stackData, (Pointer) block))
- {
-
- printf("--\n");
- AllocSetDump(&block->setData);
- }
-}
-
-/* ----------------------------------------------------------------
- * variable / heap context method tables
- * ----------------------------------------------------------------
- */
-static struct MemoryContextMethodsData PortalVariableContextMethodsData = {
- PortalVariableMemoryAlloc, /* Pointer (*)(this, uint32) palloc */
- PortalVariableMemoryFree, /* void (*)(this, Pointer) pfree */
- PortalVariableMemoryRealloc,/* Pointer (*)(this, Pointer) repalloc */
- PortalVariableMemoryGetName,/* char* (*)(this) getName */
- PortalVariableMemoryDump /* void (*)(this) dump */
-};
-
-static struct MemoryContextMethodsData PortalHeapContextMethodsData = {
- PortalHeapMemoryAlloc, /* Pointer (*)(this, uint32) palloc */
- PortalHeapMemoryFree, /* void (*)(this, Pointer) pfree */
- PortalHeapMemoryRealloc, /* Pointer (*)(this, Pointer) repalloc */
- PortalHeapMemoryGetName, /* char* (*)(this) getName */
- PortalHeapMemoryDump /* void (*)(this) dump */
-};
-
-
-/* ----------------------------------------------------------------
- * private internal support routines
- * ----------------------------------------------------------------
- */
-/* ----------------
- * CreateNewBlankPortal
- * ----------------
- */
-static void
-CreateNewBlankPortal()
-{
- Portal portal;
-
- AssertState(!PortalIsValid(BlankPortal));
-
- /*
- * make new portal structure
- */
- portal = (Portal)
- MemoryContextAlloc((MemoryContext) PortalMemory, sizeof *portal);
-
- /*
- * initialize portal variable context
- */
- NodeSetTag((Node *) &portal->variable, T_PortalVariableMemory);
- AllocSetInit(&portal->variable.setData, DynamicAllocMode, (Size) 0);
- portal->variable.method = &PortalVariableContextMethodsData;
-
- /*
- * initialize portal heap context
- */
- NodeSetTag((Node *) &portal->heap, T_PortalHeapMemory);
- portal->heap.block = NULL;
- FixedStackInit(&portal->heap.stackData,
- offsetof(HeapMemoryBlockData, itemData));
- portal->heap.method = &PortalHeapContextMethodsData;
-
- /*
- * set bogus portal name
- */
- portal->name = "** Blank Portal **";
-
- /* initialize portal query */
- portal->queryDesc = NULL;
- portal->attinfo = NULL;
- portal->state = NULL;
- portal->cleanup = NULL;
-
- /*
- * install blank portal
- */
- BlankPortal = portal;
-}
-
-bool
-PortalNameIsSpecial(char *pname)
-{
- if (strcmp(pname, VACPNAME) == 0)
- return true;
- return false;
-}
-
-/*
- * This routine is used to collect all portals created in this xaction
- * and then destroy them. There is a little trickiness required as a
- * result of the dynamic hashing interface to getting every hash entry
- * sequentially. Its use of static variables requires that we get every
- * entry *before* we destroy anything (destroying updates the hashtable
- * and screws up the sequential walk of the table). -mer 17 Aug 1992
- */
-static void
-CollectNamedPortals(Portal *portalP, int destroy)
-{
- static Portal *portalList = (Portal *) NULL;
- static int listIndex = 0;
- static int maxIndex = 9;
-
- if (portalList == (Portal *) NULL)
- portalList = (Portal *) malloc(10 * sizeof(Portal));
-
- if (destroy != 0)
- {
- int i;
-
- for (i = 0; i < listIndex; i++)
- PortalDestroy(&portalList[i]);
- listIndex = 0;
- }
- else
- {
- Assert(portalP);
- Assert(*portalP);
-
- /*
- * Don't delete special portals, up to portal creator to do this
- */
- if (PortalNameIsSpecial((*portalP)->name))
- return;
-
- portalList[listIndex] = *portalP;
- listIndex++;
- if (listIndex == maxIndex)
- {
- portalList = (Portal *)
- realloc(portalList, (maxIndex + 11) * sizeof(Portal));
- maxIndex += 10;
- }
- }
- return;
-}
+static MemoryContext PortalMemory = NULL;
-void
-AtEOXact_portals()
-{
- HashTableWalk(PortalHashTable, CollectNamedPortals, 0);
- CollectNamedPortals(NULL, 1);
-}
-
-/* ----------------
- * PortalDump
- * ----------------
- */
-#ifdef NOT_USED
-static void
-PortalDump(Portal *thisP)
-{
- /* XXX state/argument checking here */
-
- PortalVariableMemoryDump(PortalGetVariableMemory(*thisP));
- PortalHeapMemoryDump(PortalGetHeapMemory(*thisP));
-}
-
-#endif
-
-/* ----------------
- * DumpPortals
- * ----------------
- */
-#ifdef NOT_USED
-static void
-DumpPortals()
-{
- /* XXX state checking here */
-
- HashTableWalk(PortalHashTable, PortalDump, 0);
-}
-
-#endif
/* ----------------------------------------------------------------
* public portal interface functions
* ----------------------------------------------------------------
*/
+
/*
- * EnablePortalManager --
- * Enables/disables the portal management module.
+ * EnablePortalManager
+ * Enables the portal management module at backend startup.
*/
void
-EnablePortalManager(bool on)
+EnablePortalManager(void)
{
- static bool processing = false;
HASHCTL ctl;
- AssertState(!processing);
- AssertArg(BoolIsValid(on));
+ Assert(PortalMemory == NULL);
- if (BypassEnable(&PortalManagerEnableCount, on))
- return;
+ PortalMemory = AllocSetContextCreate(TopMemoryContext,
+ "PortalMemory",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
- processing = true;
+ ctl.keysize = MAX_PORTALNAME_LEN;
+ ctl.entrysize = sizeof(PortalHashEnt);
- if (on)
- { /* initialize */
- EnableMemoryContext(true);
-
- PortalMemory = CreateGlobalMemory(PortalMemoryName);
-
- ctl.keysize = MAX_PORTALNAME_LEN;
- ctl.datasize = sizeof(Portal);
-
- /*
- * use PORTALS_PER_USER, defined in utils/portal.h as a guess of
- * how many hash table entries to create, initially
- */
- PortalHashTable = hash_create(PORTALS_PER_USER * 3, &ctl, HASH_ELEM);
-
- CreateNewBlankPortal();
-
- }
- else
- { /* cleanup */
- if (PortalIsValid(BlankPortal))
- {
- PortalDestroy(&BlankPortal);
- MemoryContextFree((MemoryContext) PortalMemory,
- (Pointer) BlankPortal);
- BlankPortal = NULL;
- }
-
- /*
- * Each portal must free its non-memory resources specially.
- */
- HashTableWalk(PortalHashTable, PortalDestroy, 0);
- hash_destroy(PortalHashTable);
- PortalHashTable = NULL;
-
- GlobalMemoryDestroy(PortalMemory);
- PortalMemory = NULL;
-
- EnableMemoryContext(true);
- }
-
- processing = false;
+ /*
+ * use PORTALS_PER_USER as a guess of how many hash table entries to
+ * create, initially
+ */
+ PortalHashTable = hash_create("Portal hash", PORTALS_PER_USER,
+ &ctl, HASH_ELEM);
}
/*
- * GetPortalByName --
- * Returns a portal given a portal name; returns blank portal given
- * NULL; returns invalid portal if portal not found.
- *
- * Exceptions:
- * BadState if called when disabled.
+ * GetPortalByName
+ * Returns a portal given a portal name, or NULL if name not found.
*/
Portal
-GetPortalByName(char *name)
+GetPortalByName(const char *name)
{
Portal portal;
- AssertState(PortalManagerEnabled);
-
if (PointerIsValid(name))
PortalHashTableLookup(name, portal);
else
- {
- if (!PortalIsValid(BlankPortal))
- CreateNewBlankPortal();
- portal = BlankPortal;
- }
+ portal = NULL;
- return (portal);
+ return portal;
}
/*
- * BlankPortalAssignName --
- * Returns former blank portal as portal with given name.
+ * CreatePortal
+ * Returns a new portal given a name.
*
- * Side effect:
- * All references to the former blank portal become incorrect.
+ * allowDup: if true, automatically drop any pre-existing portal of the
+ * same name (if false, an error is raised).
*
- * Exceptions:
- * BadState if called when disabled.
- * BadState if called without an intervening call to GetPortalByName(NULL).
- * BadArg if portal name is invalid.
- * "WARN" if portal name is in use.
+ * dupSilent: if true, don't even emit a WARNING.
*/
Portal
-BlankPortalAssignName(char *name) /* XXX PortalName */
+CreatePortal(const char *name, bool allowDup, bool dupSilent)
{
Portal portal;
- uint16 length;
- AssertState(PortalManagerEnabled);
- AssertState(PortalIsValid(BlankPortal));
- AssertArg(PointerIsValid(name)); /* XXX PortalName */
+ AssertArg(PointerIsValid(name));
portal = GetPortalByName(name);
if (PortalIsValid(portal))
{
- elog(NOTICE, "BlankPortalAssignName: portal %s already exists", name);
- return (portal);
+ if (!allowDup)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_CURSOR),
+ errmsg("portal \"%s\" already exists", name)));
+ if (!dupSilent)
+ ereport(WARNING,
+ (errcode(ERRCODE_DUPLICATE_CURSOR),
+ errmsg("closing pre-existing portal \"%s\"",
+ name)));
+ PortalDrop(portal, false);
}
- /*
- * remove blank portal
- */
- portal = BlankPortal;
- BlankPortal = NULL;
+ /* make new portal structure */
+ portal = (Portal) MemoryContextAllocZero(PortalMemory, sizeof *portal);
- /*
- * initialize portal name
- */
- length = 1 + strlen(name);
- portal->name = (char *)
- MemoryContextAlloc((MemoryContext) &portal->variable, length);
+ /* initialize portal heap context; typically it won't store much */
+ portal->heap = AllocSetContextCreate(PortalMemory,
+ "PortalHeapMemory",
+ ALLOCSET_SMALL_MINSIZE,
+ ALLOCSET_SMALL_INITSIZE,
+ ALLOCSET_SMALL_MAXSIZE);
- strncpy(portal->name, name, length);
+ /* initialize portal fields that don't start off zero */
+ portal->cleanup = PortalCleanup;
+ portal->createXact = GetCurrentTransactionId();
+ portal->strategy = PORTAL_MULTI_QUERY;
+ portal->cursorOptions = CURSOR_OPT_NO_SCROLL;
+ portal->atStart = true;
+ portal->atEnd = true; /* disallow fetches until query is set */
- /*
- * put portal in table
- */
- PortalHashTableInsert(portal);
+ /* put portal in table (sets portal->name) */
+ PortalHashTableInsert(portal, name);
- return (portal);
+ return portal;
}
/*
- * PortalSetQuery --
- * Attaches a "query" to portal.
- *
- * Exceptions:
- * BadState if called when disabled.
- * BadArg if portal is invalid.
- * BadArg if queryDesc is "invalid."
- * BadArg if state is "invalid."
+ * CreateNewPortal
+ * Create a new portal, assigning it a random nonconflicting name.
*/
-void
-PortalSetQuery(Portal portal,
- QueryDesc *queryDesc,
- TupleDesc attinfo,
- EState *state,
- void (*cleanup) (Portal portal))
+Portal
+CreateNewPortal(void)
{
- AssertState(PortalManagerEnabled);
- AssertArg(PortalIsValid(portal));
- AssertArg(IsA((Node *) state, EState));
+ static unsigned int unnamed_portal_count = 0;
- portal->queryDesc = queryDesc;
- portal->state = state;
- portal->attinfo = attinfo;
- portal->cleanup = cleanup;
-}
+ char portalname[MAX_PORTALNAME_LEN];
-/*
- * PortalGetQueryDesc --
- * Returns query attached to portal.
- *
- * Exceptions:
- * BadState if called when disabled.
- * BadArg if portal is invalid.
- */
-QueryDesc *
-PortalGetQueryDesc(Portal portal)
-{
- AssertState(PortalManagerEnabled);
- AssertArg(PortalIsValid(portal));
+ /* Select a nonconflicting name */
+ for (;;)
+ {
+ unnamed_portal_count++;
+ sprintf(portalname, "<unnamed portal %u>", unnamed_portal_count);
+ if (GetPortalByName(portalname) == NULL)
+ break;
+ }
- return (portal->queryDesc);
+ return CreatePortal(portalname, false, false);
}
/*
- * PortalGetState --
- * Returns state attached to portal.
+ * PortalDefineQuery
+ * A simple subroutine to establish a portal's query.
*
- * Exceptions:
- * BadState if called when disabled.
- * BadArg if portal is invalid.
+ * Notes: commandTag shall be NULL if and only if the original query string
+ * (before rewriting) was an empty string. Also, the passed commandTag must
+ * be a pointer to a constant string, since it is not copied. The caller is
+ * responsible for ensuring that the passed sourceText (if any), parse and
+ * plan trees have adequate lifetime. Also, queryContext must accurately
+ * describe the location of the parse and plan trees.
*/
-EState *
-PortalGetState(Portal portal)
+void
+PortalDefineQuery(Portal portal,
+ const char *sourceText,
+ const char *commandTag,
+ List *parseTrees,
+ List *planTrees,
+ MemoryContext queryContext)
{
- AssertState(PortalManagerEnabled);
AssertArg(PortalIsValid(portal));
+ AssertState(portal->queryContext == NULL); /* else defined already */
+
+ Assert(length(parseTrees) == length(planTrees));
+
+ Assert(commandTag != NULL || parseTrees == NIL);
- return (portal->state);
+ portal->sourceText = sourceText;
+ portal->commandTag = commandTag;
+ portal->parseTrees = parseTrees;
+ portal->planTrees = planTrees;
+ portal->queryContext = queryContext;
}
/*
- * CreatePortal --
- * Returns a new portal given a name.
- *
- * Note:
- * This is expected to be of very limited usability. See instead,
- * BlankPortalAssignName.
- *
- * Exceptions:
- * BadState if called when disabled.
- * BadArg if portal name is invalid.
- * "WARN" if portal name is in use.
+ * PortalCreateHoldStore
+ * Create the tuplestore for a portal.
*/
-Portal
-CreatePortal(char *name) /* XXX PortalName */
+void
+PortalCreateHoldStore(Portal portal)
{
- Portal portal;
- uint16 length;
+ MemoryContext oldcxt;
- AssertState(PortalManagerEnabled);
- AssertArg(PointerIsValid(name)); /* XXX PortalName */
+ Assert(portal->holdContext == NULL);
+ Assert(portal->holdStore == NULL);
- portal = GetPortalByName(name);
- if (PortalIsValid(portal))
- {
- elog(NOTICE, "CreatePortal: portal %s already exists", name);
- return (portal);
- }
+ /*
+ * 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,
+ "PortalHeapMemory",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
- /* make new portal structure */
- portal = (Portal)
- MemoryContextAlloc((MemoryContext) PortalMemory, sizeof *portal);
-
- /* initialize portal variable context */
- NodeSetTag((Node *) &portal->variable, T_PortalVariableMemory);
- AllocSetInit(&portal->variable.setData, DynamicAllocMode, (Size) 0);
- portal->variable.method = &PortalVariableContextMethodsData;
-
- /* initialize portal heap context */
- NodeSetTag((Node *) &portal->heap, T_PortalHeapMemory);
- portal->heap.block = NULL;
- FixedStackInit(&portal->heap.stackData,
- offsetof(HeapMemoryBlockData, itemData));
- portal->heap.method = &PortalHeapContextMethodsData;
-
- /* initialize portal name */
- length = 1 + strlen(name);
- portal->name = (char *)
- MemoryContextAlloc((MemoryContext) &portal->variable, length);
- strncpy(portal->name, name, length);
-
- /* initialize portal query */
- portal->queryDesc = NULL;
- portal->attinfo = NULL;
- portal->state = NULL;
- portal->cleanup = NULL;
-
- /* put portal in table */
- PortalHashTableInsert(portal);
-
- /* Trap(PointerIsValid(name), Unimplemented); */
- return (portal);
+ /* Create the tuple store, selecting cross-transaction temp files. */
+ oldcxt = MemoryContextSwitchTo(portal->holdContext);
+
+ /* XXX: Should SortMem be used for this? */
+ portal->holdStore = tuplestore_begin_heap(true, true, SortMem);
+
+ MemoryContextSwitchTo(oldcxt);
}
/*
- * PortalDestroy --
- * Destroys portal.
+ * PortalDrop
+ * Destroy the portal.
*
- * Exceptions:
- * BadState if called when disabled.
- * BadArg if portal is invalid.
+ * isError: if true, we are destroying portals at the end of a failed
+ * transaction. (This causes PortalCleanup to skip unneeded steps.)
*/
void
-PortalDestroy(Portal *portalP)
+PortalDrop(Portal portal, bool isError)
{
- Portal portal = *portalP;
-
- AssertState(PortalManagerEnabled);
AssertArg(PortalIsValid(portal));
- /* remove portal from table if not blank portal */
- if (portal != BlankPortal)
- PortalHashTableDelete(portal);
+ /* Not sure if this case can validly happen or not... */
+ if (portal->portalActive)
+ elog(ERROR, "cannot drop active portal");
+
+ /*
+ * Remove portal from hash table. Because we do this first, 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);
- /* reset portal */
+ /* let portalcmds.c clean up the state it knows about */
if (PointerIsValid(portal->cleanup))
- (*portal->cleanup) (portal);
+ (*portal->cleanup) (portal, isError);
+
+ /*
+ * 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;
+ }
- PortalResetHeapMemory(portal);
- MemoryContextFree((MemoryContext) &portal->variable,
- (Pointer) portal->name);
- AllocSetReset(&portal->variable.setData); /* XXX log */
+ /* delete tuplestore storage, if any */
+ if (portal->holdContext)
+ MemoryContextDelete(portal->holdContext);
- if (portal != BlankPortal)
- MemoryContextFree((MemoryContext) PortalMemory, (Pointer) portal);
+ /* release subsidiary storage */
+ MemoryContextDelete(PortalGetHeapMemory(portal));
+
+ /* release portal struct (it's in PortalMemory) */
+ pfree(portal);
}
-/* ----------------
- * PortalResetHeapMemory --
- * Resets portal's heap memory context.
- *
- * Someday, Reset, Start, and End can be optimized by keeping a global
- * portal module stack of free HeapMemoryBlock's. This will make Start
- * and End be fast.
+/*
+ * DropDependentPortals
+ * Drop any portals using the specified context as queryContext.
*
- * Exceptions:
- * BadState if called when disabled.
- * BadState if called when not in PortalHeapMemory context.
- * BadArg if mode is invalid.
- * ----------------
+ * This is normally used to make sure we can safely drop a prepared statement.
*/
-static void
-PortalResetHeapMemory(Portal portal)
+void
+DropDependentPortals(MemoryContext queryContext)
{
- PortalHeapMemory context;
- MemoryContext currentContext;
+ HASH_SEQ_STATUS status;
+ PortalHashEnt *hentry;
- context = PortalGetHeapMemory(portal);
+ hash_seq_init(&status, PortalHashTable);
- if (PointerIsValid(context->block))
+ while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
{
- /* save present context */
- currentContext = MemoryContextSwitchTo((MemoryContext) context);
-
- do
- {
- EndPortalAllocMode();
- } while (PointerIsValid(context->block));
+ Portal portal = hentry->portal;
- /* restore context */
- MemoryContextSwitchTo(currentContext);
+ if (portal->queryContext == queryContext)
+ PortalDrop(portal, false);
}
}
+
/*
- * StartPortalAllocMode --
- * Starts a new block of portal heap allocation using mode and limit;
- * the current block is disabled until EndPortalAllocMode is called.
+ * Pre-commit processing for portals.
*
- * Note:
- * Note blocks may be stacked and restored arbitarily.
- * The semantics of mode and limit are described in aset.h.
+ * Any holdable cursors created in this transaction need to be converted to
+ * materialized form, since we are going to close down the executor and
+ * release locks. Remove all other portals created in this transaction.
+ * Portals remaining from prior transactions should be left untouched.
*
- * Exceptions:
- * BadState if called when disabled.
- * BadState if called when not in PortalHeapMemory context.
- * BadArg if mode is invalid.
+ * XXX This assumes that portals can be deleted in a random order, ie,
+ * no portal has a reference to any other (at least not one that will be
+ * exercised during deletion). I think this is okay at the moment, but
+ * we've had bugs of that ilk in the past. Keep a close eye on cursor
+ * references...
*/
void
-StartPortalAllocMode(AllocMode mode, Size limit)
+AtCommit_Portals(void)
{
- PortalHeapMemory context;
-
- AssertState(PortalManagerEnabled);
- AssertState(IsA(CurrentMemoryContext, PortalHeapMemory));
- /* AssertArg(AllocModeIsValid); */
+ HASH_SEQ_STATUS status;
+ PortalHashEnt *hentry;
+ TransactionId xact = GetCurrentTransactionId();
- context = (PortalHeapMemory) CurrentMemoryContext;
+ hash_seq_init(&status, PortalHashTable);
- /* stack current mode */
- if (PointerIsValid(context->block))
- FixedStackPush(&context->stackData, context->block);
-
- /* allocate and initialize new block */
- context->block =
- MemoryContextAlloc(
- (MemoryContext) PortalHeapMemoryGetVariableMemory(context),
- sizeof(HeapMemoryBlockData));
+ while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
+ {
+ Portal portal = hentry->portal;
- /* XXX careful, context->block has never been stacked => bad state */
+ /*
+ * Do not touch active portals --- this can only happen in the
+ * case of a multi-transaction utility command, such as VACUUM.
+ */
+ if (portal->portalActive)
+ continue;
- AllocSetInit(&HEAPMEMBLOCK(context)->setData, mode, limit);
+ if (portal->cursorOptions & CURSOR_OPT_HOLD)
+ {
+ /*
+ * Do nothing to cursors held over from a previous
+ * transaction.
+ */
+ if (portal->createXact != xact)
+ continue;
+
+ /*
+ * We are exiting the transaction that created a holdable
+ * cursor. Instead of dropping the portal, prepare it for
+ * access by later transactions.
+ *
+ * Note that PersistHoldablePortal() must release all resources
+ * used by the portal that are local to the creating
+ * transaction.
+ */
+ PortalCreateHoldStore(portal);
+ PersistHoldablePortal(portal);
+ }
+ else
+ {
+ /* Zap all non-holdable portals */
+ PortalDrop(portal, false);
+ }
+ }
}
/*
- * EndPortalAllocMode --
- * Ends current block of portal heap allocation; previous block is
- * reenabled.
+ * Abort processing for portals.
*
- * Note:
- * Note blocks may be stacked and restored arbitarily.
+ * At this point we reset the "active" flags and run the cleanup hook if
+ * present, but we can't release memory until the cleanup call.
*
- * Exceptions:
- * BadState if called when disabled.
- * BadState if called when not in PortalHeapMemory context.
+ * The reason we need to reset active is so that we can replace the unnamed
+ * portal, else we'll fail to execute ROLLBACK when it arrives. Also, we
+ * want to run the cleanup hook now to be certain it knows that we had an
+ * error abort and not successful conclusion.
*/
void
-EndPortalAllocMode()
+AtAbort_Portals(void)
{
- PortalHeapMemory context;
+ HASH_SEQ_STATUS status;
+ PortalHashEnt *hentry;
+ TransactionId xact = GetCurrentTransactionId();
- AssertState(PortalManagerEnabled);
- AssertState(IsA(CurrentMemoryContext, PortalHeapMemory));
+ hash_seq_init(&status, PortalHashTable);
- context = (PortalHeapMemory) CurrentMemoryContext;
- AssertState(PointerIsValid(context->block)); /* XXX Trap(...) */
+ while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
+ {
+ Portal portal = hentry->portal;
- /* free current mode */
- AllocSetReset(&HEAPMEMBLOCK(context)->setData);
- MemoryContextFree((MemoryContext) PortalHeapMemoryGetVariableMemory(context),
- context->block);
+ portal->portalActive = false;
- /* restore previous mode */
- context->block = FixedStackPop(&context->stackData);
-}
+ /*
+ * Do nothing else to cursors held over from a previous
+ * transaction. (This test must include checking CURSOR_OPT_HOLD,
+ * else we will fail to clean up a VACUUM portal if it fails after
+ * its first sub-transaction.)
+ */
+ if (portal->createXact != xact &&
+ (portal->cursorOptions & CURSOR_OPT_HOLD))
+ continue;
-/*
- * PortalGetVariableMemory --
- * Returns variable memory context for a given portal.
- *
- * Exceptions:
- * BadState if called when disabled.
- * BadArg if portal is invalid.
- */
-PortalVariableMemory
-PortalGetVariableMemory(Portal portal)
-{
- return (&portal->variable);
+ /* let portalcmds.c clean up the state it knows about */
+ if (PointerIsValid(portal->cleanup))
+ {
+ (*portal->cleanup) (portal, true);
+ portal->cleanup = NULL;
+ }
+ }
}
/*
- * PortalGetHeapMemory --
- * Returns heap memory context for a given portal.
+ * Post-abort cleanup for portals.
*
- * Exceptions:
- * BadState if called when disabled.
- * BadArg if portal is invalid.
+ * Delete all portals not held over from prior transactions.
*/
-PortalHeapMemory
-PortalGetHeapMemory(Portal portal)
+void
+AtCleanup_Portals(void)
{
- return (&portal->heap);
-}
+ HASH_SEQ_STATUS status;
+ PortalHashEnt *hentry;
+ TransactionId xact = GetCurrentTransactionId();
-/*
- * PortalVariableMemoryGetPortal --
- * Returns portal containing given variable memory context.
- *
- * Exceptions:
- * BadState if called when disabled.
- * BadArg if context is invalid.
- */
-static Portal
-PortalVariableMemoryGetPortal(PortalVariableMemory context)
-{
- return ((Portal) ((char *) context - offsetof(PortalD, variable)));
-}
+ hash_seq_init(&status, PortalHashTable);
-/*
- * PortalHeapMemoryGetPortal --
- * Returns portal containing given heap memory context.
- *
- * Exceptions:
- * BadState if called when disabled.
- * BadArg if context is invalid.
- */
-static Portal
-PortalHeapMemoryGetPortal(PortalHeapMemory context)
-{
- return ((Portal) ((char *) context - offsetof(PortalD, heap)));
-}
+ while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
+ {
+ Portal portal = hentry->portal;
-/*
- * PortalVariableMemoryGetHeapMemory --
- * Returns heap memory context associated with given variable memory.
- *
- * Exceptions:
- * BadState if called when disabled.
- * BadArg if context is invalid.
- */
-#ifdef NOT_USED
-PortalHeapMemory
-PortalVariableMemoryGetHeapMemory(PortalVariableMemory context)
-{
- return ((PortalHeapMemory) ((char *) context
- - offsetof(PortalD, variable)
- +offsetof(PortalD, heap)));
-}
+ /*
+ * Let's just make sure no one's active...
+ */
+ portal->portalActive = false;
-#endif
+ /*
+ * Do nothing else to cursors held over from a previous
+ * transaction. (This test must include checking CURSOR_OPT_HOLD,
+ * else we will fail to clean up a VACUUM portal if it fails after
+ * its first sub-transaction.)
+ */
+ if (portal->createXact != xact &&
+ (portal->cursorOptions & CURSOR_OPT_HOLD))
+ continue;
-/*
- * PortalHeapMemoryGetVariableMemory --
- * Returns variable memory context associated with given heap memory.
- *
- * Exceptions:
- * BadState if called when disabled.
- * BadArg if context is invalid.
- */
-static PortalVariableMemory
-PortalHeapMemoryGetVariableMemory(PortalHeapMemory context)
-{
- return ((PortalVariableMemory) ((char *) context
- - offsetof(PortalD, heap)
- +offsetof(PortalD, variable)));
+ /* Else zap it with prejudice. */
+ PortalDrop(portal, true);
+ }
}