postgresql/src/backend/utils/mmgr/portalmem.c

372 lines
9.0 KiB
C

/*-------------------------------------------------------------------------
*
* portalmem.c
* backend portal memory context management stuff
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.37 2000/06/28 03:32:50 tgl 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(MemoryContext) heap;
* List queryDesc;
* EState state;
* void (*cleanup) ARGS((Portal portal));
* };
*
* I hope this makes things clearer to whoever reads this -cim 2/22/91
*/
#include "postgres.h"
#include "lib/hasht.h"
#include "utils/memutils.h"
#include "utils/portal.h"
static void CollectNamedPortals(Portal *portalP, int destroy);
/* ----------------
* Global state
* ----------------
*/
#define MAX_PORTALNAME_LEN 64 /* XXX LONGALIGNable value */
typedef struct portalhashent
{
char portalname[MAX_PORTALNAME_LEN];
Portal portal;
} PortalHashEnt;
static HTAB *PortalHashTable = NULL;
#define PortalHashTableLookup(NAME, PORTAL) \
do { \
PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \
\
MemSet(key, 0, MAX_PORTALNAME_LEN); \
snprintf(key, MAX_PORTALNAME_LEN - 1, "%s", NAME); \
hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
key, HASH_FIND, &found); \
if (hentry == NULL) \
elog(FATAL, "error in PortalHashTable"); \
if (found) \
PORTAL = hentry->portal; \
else \
PORTAL = NULL; \
} while(0)
#define PortalHashTableInsert(PORTAL) \
do { \
PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \
\
MemSet(key, 0, MAX_PORTALNAME_LEN); \
snprintf(key, MAX_PORTALNAME_LEN - 1, "%s", PORTAL->name); \
hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
key, HASH_ENTER, &found); \
if (hentry == NULL) \
elog(FATAL, "error in PortalHashTable"); \
if (found) \
elog(NOTICE, "trying to insert a portal name that exists."); \
hentry->portal = PORTAL; \
} while(0)
#define PortalHashTableDelete(PORTAL) \
do { \
PortalHashEnt *hentry; bool found; char key[MAX_PORTALNAME_LEN]; \
\
MemSet(key, 0, MAX_PORTALNAME_LEN); \
snprintf(key, MAX_PORTALNAME_LEN - 1, "%s", PORTAL->name); \
hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
key, HASH_REMOVE, &found); \
if (hentry == NULL) \
elog(FATAL, "error in PortalHashTable"); \
if (!found) \
elog(NOTICE, "trying to delete portal name that does not exist."); \
} while(0)
static MemoryContext PortalMemory = NULL;
/* ----------------------------------------------------------------
* private internal support routines
* ----------------------------------------------------------------
*/
/*
* 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++)
PortalDrop(&portalList[i]);
listIndex = 0;
}
else
{
Assert(portalP);
Assert(*portalP);
portalList[listIndex] = *portalP;
listIndex++;
if (listIndex == maxIndex)
{
portalList = (Portal *)
realloc(portalList, (maxIndex + 11) * sizeof(Portal));
maxIndex += 10;
}
}
return;
}
void
AtEOXact_portals()
{
HashTableWalk(PortalHashTable, (HashtFunc) CollectNamedPortals, 0);
CollectNamedPortals(NULL, 1);
}
/* ----------------------------------------------------------------
* public portal interface functions
* ----------------------------------------------------------------
*/
/*
* EnablePortalManager
* Enables the portal management module at backend startup.
*/
void
EnablePortalManager(void)
{
HASHCTL ctl;
Assert(PortalMemory == NULL);
PortalMemory = AllocSetContextCreate(TopMemoryContext,
"PortalMemory",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
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);
}
/*
* GetPortalByName
* Returns a portal given a portal name, or NULL if name not found.
*/
Portal
GetPortalByName(char *name)
{
Portal portal;
if (PointerIsValid(name))
PortalHashTableLookup(name, portal);
else
portal = NULL;
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."
*/
void
PortalSetQuery(Portal portal,
QueryDesc *queryDesc,
TupleDesc attinfo,
EState *state,
void (*cleanup) (Portal portal))
{
AssertArg(PortalIsValid(portal));
AssertArg(IsA((Node *) state, EState));
portal->queryDesc = queryDesc;
portal->state = state;
portal->attinfo = attinfo;
portal->cleanup = cleanup;
}
/*
* PortalGetQueryDesc
* Returns query attached to portal.
*
* Exceptions:
* BadState if called when disabled.
* BadArg if portal is invalid.
*/
QueryDesc *
PortalGetQueryDesc(Portal portal)
{
AssertArg(PortalIsValid(portal));
return portal->queryDesc;
}
/*
* PortalGetState
* Returns state attached to portal.
*
* Exceptions:
* BadState if called when disabled.
* BadArg if portal is invalid.
*/
EState *
PortalGetState(Portal portal)
{
AssertArg(PortalIsValid(portal));
return portal->state;
}
/*
* CreatePortal
* Returns a new portal given a name.
*
* Exceptions:
* BadState if called when disabled.
* BadArg if portal name is invalid.
* "NOTICE" if portal name is in use (existing portal is returned!)
*/
Portal
CreatePortal(char *name)
{
Portal portal;
AssertArg(PointerIsValid(name));
portal = GetPortalByName(name);
if (PortalIsValid(portal))
{
elog(NOTICE, "CreatePortal: portal \"%s\" already exists", name);
return portal;
}
/* make new portal structure */
portal = (Portal) MemoryContextAlloc(PortalMemory, sizeof *portal);
/* initialize portal name */
portal->name = MemoryContextStrdup(PortalMemory, name);
/* initialize portal heap context */
portal->heap = AllocSetContextCreate(PortalMemory,
"PortalHeapMemory",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
/* initialize portal query */
portal->queryDesc = NULL;
portal->attinfo = NULL;
portal->state = NULL;
portal->cleanup = NULL;
/* put portal in table */
PortalHashTableInsert(portal);
return portal;
}
/*
* PortalDrop
* Destroys portal.
*
* Exceptions:
* BadState if called when disabled.
* BadArg if portal is invalid.
*/
void
PortalDrop(Portal *portalP)
{
Portal portal = *portalP;
AssertArg(PortalIsValid(portal));
/* remove portal from hash table */
PortalHashTableDelete(portal);
/* reset portal */
if (PointerIsValid(portal->cleanup))
(*portal->cleanup) (portal);
/* release subsidiary storage */
MemoryContextDelete(PortalGetHeapMemory(portal));
/* release name and portal data (both are in PortalMemory) */
pfree(portal->name);
pfree(portal);
}
/*
* PortalGetHeapMemory
* Returns heap memory context for a given portal.
*/
MemoryContext
PortalGetHeapMemory(Portal portal)
{
return portal->heap;
}