372 lines
9.0 KiB
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;
|
|
}
|