mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
First phase of memory management rewrite (see backend/utils/mmgr/README
for details). It doesn't really do that much yet, since there are no short-term memory contexts in the executor, but the infrastructure is in place and long-term contexts are handled reasonably. A few long- standing bugs have been fixed, such as 'VACUUM; anything' in a single query string crashing. Also, out-of-memory is now considered a recoverable ERROR, not FATAL. Eliminate a large amount of crufty, now-dead code in and around memory management. Fix problem with holding off SIGTRAP, SIGSEGV, etc in postmaster and backend startup.
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.34 2000/05/28 17:55:55 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.35 2000/06/28 03:31:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -87,8 +87,8 @@ init_execution_state(FunctionCachePtr fcache)
|
||||
nextes = newes;
|
||||
preves = (execution_state *) NULL;
|
||||
|
||||
queryTree_list = pg_parse_and_rewrite(fcache->src, fcache->argOidVect,
|
||||
nargs, FALSE);
|
||||
queryTree_list = pg_parse_and_rewrite(fcache->src,
|
||||
fcache->argOidVect, nargs);
|
||||
|
||||
foreach(qtl_item, queryTree_list)
|
||||
{
|
||||
|
@ -32,7 +32,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.67 2000/06/15 03:32:09 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.68 2000/06/28 03:31:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -265,7 +265,7 @@ advance_transition_functions(AggStatePerAgg peraggstate,
|
||||
else
|
||||
newVal = FunctionCallInvoke(&fcinfo);
|
||||
if (!peraggstate->transtype1ByVal && !peraggstate->value1IsNull)
|
||||
pfree(peraggstate->value1);
|
||||
pfree(DatumGetPointer(peraggstate->value1));
|
||||
peraggstate->value1 = newVal;
|
||||
peraggstate->value1IsNull = fcinfo.isnull;
|
||||
}
|
||||
@ -288,7 +288,7 @@ advance_transition_functions(AggStatePerAgg peraggstate,
|
||||
else
|
||||
newVal = FunctionCallInvoke(&fcinfo);
|
||||
if (!peraggstate->transtype2ByVal && !peraggstate->value2IsNull)
|
||||
pfree(peraggstate->value2);
|
||||
pfree(DatumGetPointer(peraggstate->value2));
|
||||
peraggstate->value2 = newVal;
|
||||
peraggstate->value2IsNull = fcinfo.isnull;
|
||||
}
|
||||
@ -424,12 +424,12 @@ finalize_aggregate(AggStatePerAgg peraggstate,
|
||||
if (OidIsValid(peraggstate->xfn1_oid) &&
|
||||
!peraggstate->value1IsNull &&
|
||||
!peraggstate->transtype1ByVal)
|
||||
pfree(peraggstate->value1);
|
||||
pfree(DatumGetPointer(peraggstate->value1));
|
||||
|
||||
if (OidIsValid(peraggstate->xfn2_oid) &&
|
||||
!peraggstate->value2IsNull &&
|
||||
!peraggstate->transtype2ByVal)
|
||||
pfree(peraggstate->value2);
|
||||
pfree(DatumGetPointer(peraggstate->value2));
|
||||
}
|
||||
|
||||
/* ---------------------------------------
|
||||
|
@ -7,14 +7,14 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* $Id: nodeHash.c,v 1.47 2000/06/15 04:09:52 momjian Exp $
|
||||
* $Id: nodeHash.c,v 1.48 2000/06/28 03:31:34 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
/*
|
||||
* INTERFACE ROUTINES
|
||||
* ExecHash - generate an in-memory hash table of the relation
|
||||
* ExecInitHash - initialize node and subnodes..
|
||||
* ExecInitHash - initialize node and subnodes
|
||||
* ExecEndHash - shutdown node and subnodes
|
||||
*
|
||||
*/
|
||||
@ -23,11 +23,12 @@
|
||||
#include <math.h>
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "executor/execdebug.h"
|
||||
#include "executor/nodeHash.h"
|
||||
#include "executor/nodeHashjoin.h"
|
||||
#include "miscadmin.h"
|
||||
#include "utils/portal.h"
|
||||
|
||||
|
||||
static int hashFunc(Datum key, int len, bool byVal);
|
||||
|
||||
@ -235,8 +236,6 @@ ExecHashTableCreate(Hash *node)
|
||||
int totalbuckets;
|
||||
int bucketsize;
|
||||
int i;
|
||||
Portal myPortal;
|
||||
char myPortalName[64];
|
||||
MemoryContext oldcxt;
|
||||
|
||||
/* ----------------
|
||||
@ -348,23 +347,21 @@ ExecHashTableCreate(Hash *node)
|
||||
hashtable->outerBatchSize = NULL;
|
||||
|
||||
/* ----------------
|
||||
* Create a named portal in which to keep the hashtable working storage.
|
||||
* Each hashjoin must have its own portal, so be wary of name conflicts.
|
||||
* Create temporary memory contexts in which to keep the hashtable
|
||||
* working storage. See notes in executor/hashjoin.h.
|
||||
* ----------------
|
||||
*/
|
||||
i = 0;
|
||||
do
|
||||
{
|
||||
i++;
|
||||
sprintf(myPortalName, "<hashtable %d>", i);
|
||||
myPortal = GetPortalByName(myPortalName);
|
||||
} while (PortalIsValid(myPortal));
|
||||
myPortal = CreatePortal(myPortalName);
|
||||
Assert(PortalIsValid(myPortal));
|
||||
hashtable->myPortal = (void *) myPortal; /* kluge for circular
|
||||
* includes */
|
||||
hashtable->hashCxt = (MemoryContext) PortalGetVariableMemory(myPortal);
|
||||
hashtable->batchCxt = (MemoryContext) PortalGetHeapMemory(myPortal);
|
||||
hashtable->hashCxt = AllocSetContextCreate(TransactionCommandContext,
|
||||
"HashTableContext",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
|
||||
hashtable->batchCxt = AllocSetContextCreate(hashtable->hashCxt,
|
||||
"HashBatchContext",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
|
||||
/* Allocate data that will live for the life of the hashjoin */
|
||||
|
||||
@ -395,11 +392,10 @@ ExecHashTableCreate(Hash *node)
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare portal for the first-scan space allocations; allocate the
|
||||
* Prepare context for the first-scan space allocations; allocate the
|
||||
* hashbucket array therein, and set each bucket "empty".
|
||||
*/
|
||||
MemoryContextSwitchTo(hashtable->batchCxt);
|
||||
StartPortalAllocMode(DefaultAllocMode, 0);
|
||||
|
||||
hashtable->buckets = (HashJoinTuple *)
|
||||
palloc(nbuckets * sizeof(HashJoinTuple));
|
||||
@ -435,9 +431,8 @@ ExecHashTableDestroy(HashJoinTable hashtable)
|
||||
BufFileClose(hashtable->outerBatchFile[i]);
|
||||
}
|
||||
|
||||
/* Destroy the portal to release all working memory */
|
||||
/* cast here is a kluge for circular includes... */
|
||||
PortalDrop((Portal *) &hashtable->myPortal);
|
||||
/* Release working memory (batchCxt is a child, so it goes away too) */
|
||||
MemoryContextDelete(hashtable->hashCxt);
|
||||
|
||||
/* And drop the control block */
|
||||
pfree(hashtable);
|
||||
@ -676,11 +671,10 @@ ExecHashTableReset(HashJoinTable hashtable, long ntuples)
|
||||
|
||||
/*
|
||||
* Release all the hash buckets and tuples acquired in the prior pass,
|
||||
* and reinitialize the portal for a new pass.
|
||||
* and reinitialize the context for a new pass.
|
||||
*/
|
||||
MemoryContextReset(hashtable->batchCxt);
|
||||
oldcxt = MemoryContextSwitchTo(hashtable->batchCxt);
|
||||
EndPortalAllocMode();
|
||||
StartPortalAllocMode(DefaultAllocMode, 0);
|
||||
|
||||
/*
|
||||
* We still use the same number of physical buckets as in the first
|
||||
|
@ -3,21 +3,20 @@
|
||||
* spi.c
|
||||
* Server Programming Interface
|
||||
*
|
||||
* $Id: spi.c,v 1.46 2000/05/30 04:24:45 tgl Exp $
|
||||
* $Id: spi.c,v 1.47 2000/06/28 03:31:34 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "executor/spi_priv.h"
|
||||
#include "access/printtup.h"
|
||||
|
||||
static Portal _SPI_portal = (Portal) NULL;
|
||||
static _SPI_connection *_SPI_stack = NULL;
|
||||
static _SPI_connection *_SPI_current = NULL;
|
||||
static int _SPI_connected = -1;
|
||||
static int _SPI_curid = -1;
|
||||
|
||||
DLLIMPORT uint32 SPI_processed = 0;
|
||||
DLLIMPORT SPITupleTable *SPI_tuptable;
|
||||
DLLIMPORT SPITupleTable *SPI_tuptable = NULL;
|
||||
DLLIMPORT int SPI_result;
|
||||
|
||||
static int _SPI_execute(char *src, int tcount, _SPI_plan *plan);
|
||||
@ -46,28 +45,6 @@ extern void ShowUsage(void);
|
||||
int
|
||||
SPI_connect()
|
||||
{
|
||||
char pname[64];
|
||||
PortalVariableMemory pvmem;
|
||||
|
||||
/*
|
||||
* It's possible on startup and after commit/abort. In future we'll
|
||||
* catch commit/abort in some way...
|
||||
*/
|
||||
strcpy(pname, "<SPI manager>");
|
||||
_SPI_portal = GetPortalByName(pname);
|
||||
if (!PortalIsValid(_SPI_portal))
|
||||
{
|
||||
if (_SPI_stack != NULL) /* there was abort */
|
||||
free(_SPI_stack);
|
||||
_SPI_current = _SPI_stack = NULL;
|
||||
_SPI_connected = _SPI_curid = -1;
|
||||
SPI_processed = 0;
|
||||
SPI_tuptable = NULL;
|
||||
_SPI_portal = CreatePortal(pname);
|
||||
if (!PortalIsValid(_SPI_portal))
|
||||
elog(FATAL, "SPI_connect: global initialization failed");
|
||||
}
|
||||
|
||||
/*
|
||||
* When procedure called by Executor _SPI_curid expected to be equal
|
||||
* to _SPI_connected
|
||||
@ -99,15 +76,19 @@ SPI_connect()
|
||||
_SPI_current->processed = 0;
|
||||
_SPI_current->tuptable = NULL;
|
||||
|
||||
/* Create Portal for this procedure ... */
|
||||
snprintf(pname, 64, "<SPI %d>", _SPI_connected);
|
||||
_SPI_current->portal = CreatePortal(pname);
|
||||
if (!PortalIsValid(_SPI_current->portal))
|
||||
elog(FATAL, "SPI_connect: initialization failed");
|
||||
|
||||
/* ... and switch to Portal' Variable memory - procedure' context */
|
||||
pvmem = PortalGetVariableMemory(_SPI_current->portal);
|
||||
_SPI_current->savedcxt = MemoryContextSwitchTo((MemoryContext) pvmem);
|
||||
/* Create memory contexts for this procedure */
|
||||
_SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext,
|
||||
"SPI Proc",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
_SPI_current->execCxt = AllocSetContextCreate(TopTransactionContext,
|
||||
"SPI Exec",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
/* ... and switch to procedure's context */
|
||||
_SPI_current->savedcxt = MemoryContextSwitchTo(_SPI_current->procCxt);
|
||||
|
||||
_SPI_current->savedId = GetScanCommandId();
|
||||
SetScanCommandId(GetCurrentCommandId());
|
||||
@ -127,7 +108,10 @@ SPI_finish()
|
||||
|
||||
/* Restore memory context as it was before procedure call */
|
||||
MemoryContextSwitchTo(_SPI_current->savedcxt);
|
||||
PortalDrop(&(_SPI_current->portal));
|
||||
|
||||
/* Release memory used in procedure call */
|
||||
MemoryContextDelete(_SPI_current->execCxt);
|
||||
MemoryContextDelete(_SPI_current->procCxt);
|
||||
|
||||
SetScanCommandId(_SPI_current->savedId);
|
||||
|
||||
@ -142,6 +126,7 @@ SPI_finish()
|
||||
{
|
||||
free(_SPI_stack);
|
||||
_SPI_stack = NULL;
|
||||
_SPI_current = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -154,6 +139,25 @@ SPI_finish()
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up SPI state at transaction commit or abort (we don't care which).
|
||||
*/
|
||||
void
|
||||
AtEOXact_SPI(void)
|
||||
{
|
||||
/*
|
||||
* Note that memory contexts belonging to SPI stack entries will be
|
||||
* freed automatically, so we can ignore them here. We just need to
|
||||
* restore our static variables to initial state.
|
||||
*/
|
||||
if (_SPI_stack != NULL) /* there was abort */
|
||||
free(_SPI_stack);
|
||||
_SPI_current = _SPI_stack = NULL;
|
||||
_SPI_connected = _SPI_curid = -1;
|
||||
SPI_processed = 0;
|
||||
SPI_tuptable = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
SPI_push(void)
|
||||
{
|
||||
@ -508,61 +512,22 @@ SPI_palloc(Size size)
|
||||
void *
|
||||
SPI_repalloc(void *pointer, Size size)
|
||||
{
|
||||
MemoryContext oldcxt = NULL;
|
||||
|
||||
if (_SPI_curid + 1 == _SPI_connected) /* connected */
|
||||
{
|
||||
if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
|
||||
elog(FATAL, "SPI: stack corrupted");
|
||||
oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
|
||||
}
|
||||
|
||||
pointer = repalloc(pointer, size);
|
||||
|
||||
if (oldcxt)
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
return pointer;
|
||||
/* No longer need to worry which context chunk was in... */
|
||||
return repalloc(pointer, size);
|
||||
}
|
||||
|
||||
void
|
||||
SPI_pfree(void *pointer)
|
||||
{
|
||||
MemoryContext oldcxt = NULL;
|
||||
|
||||
if (_SPI_curid + 1 == _SPI_connected) /* connected */
|
||||
{
|
||||
if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
|
||||
elog(FATAL, "SPI: stack corrupted");
|
||||
oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
|
||||
}
|
||||
|
||||
/* No longer need to worry which context chunk was in... */
|
||||
pfree(pointer);
|
||||
|
||||
if (oldcxt)
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
SPI_freetuple(HeapTuple tuple)
|
||||
{
|
||||
MemoryContext oldcxt = NULL;
|
||||
|
||||
if (_SPI_curid + 1 == _SPI_connected) /* connected */
|
||||
{
|
||||
if (_SPI_current != &(_SPI_stack[_SPI_curid + 1]))
|
||||
elog(FATAL, "SPI: stack corrupted");
|
||||
oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
|
||||
}
|
||||
|
||||
/* No longer need to worry which context tuple was in... */
|
||||
heap_freetuple(tuple);
|
||||
|
||||
if (oldcxt)
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* =================== private functions =================== */
|
||||
@ -647,7 +612,7 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan)
|
||||
argtypes = plan->argtypes;
|
||||
}
|
||||
|
||||
queryTree_list = pg_parse_and_rewrite(src, argtypes, nargs, FALSE);
|
||||
queryTree_list = pg_parse_and_rewrite(src, argtypes, nargs);
|
||||
|
||||
_SPI_current->qtlist = queryTree_list;
|
||||
|
||||
@ -790,7 +755,6 @@ static int
|
||||
_SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
|
||||
{
|
||||
Query *parseTree = queryDesc->parsetree;
|
||||
Plan *plan = queryDesc->plantree;
|
||||
int operation = queryDesc->operation;
|
||||
CommandDest dest = queryDesc->dest;
|
||||
TupleDesc tupdesc;
|
||||
@ -875,16 +839,13 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
|
||||
#endif
|
||||
tupdesc = ExecutorStart(queryDesc, state);
|
||||
|
||||
/* Don't work currently */
|
||||
/* Don't work currently --- need to rearrange callers so that
|
||||
* we prepare the portal before doing CreateExecutorState() etc.
|
||||
* See pquery.c for the correct order of operations.
|
||||
*/
|
||||
if (isRetrieveIntoPortal)
|
||||
{
|
||||
ProcessPortal(intoName,
|
||||
parseTree,
|
||||
plan,
|
||||
state,
|
||||
tupdesc,
|
||||
None);
|
||||
return SPI_OK_CURSOR;
|
||||
elog(FATAL, "SPI_select: retrieve into portal not implemented");
|
||||
}
|
||||
|
||||
ExecutorRun(queryDesc, state, EXEC_FOR, parseTree->limitOffset, count);
|
||||
@ -920,27 +881,13 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
|
||||
static MemoryContext
|
||||
_SPI_execmem()
|
||||
{
|
||||
MemoryContext oldcxt;
|
||||
PortalHeapMemory phmem;
|
||||
|
||||
phmem = PortalGetHeapMemory(_SPI_current->portal);
|
||||
oldcxt = MemoryContextSwitchTo((MemoryContext) phmem);
|
||||
|
||||
return oldcxt;
|
||||
|
||||
return MemoryContextSwitchTo(_SPI_current->execCxt);
|
||||
}
|
||||
|
||||
static MemoryContext
|
||||
_SPI_procmem()
|
||||
{
|
||||
MemoryContext oldcxt;
|
||||
PortalVariableMemory pvmem;
|
||||
|
||||
pvmem = PortalGetVariableMemory(_SPI_current->portal);
|
||||
oldcxt = MemoryContextSwitchTo((MemoryContext) pvmem);
|
||||
|
||||
return oldcxt;
|
||||
|
||||
return MemoryContextSwitchTo(_SPI_current->procCxt);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -959,7 +906,6 @@ _SPI_begin_call(bool execmem)
|
||||
if (execmem) /* switch to the Executor memory context */
|
||||
{
|
||||
_SPI_execmem();
|
||||
StartPortalAllocMode(DefaultAllocMode, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -977,9 +923,10 @@ _SPI_end_call(bool procmem)
|
||||
_SPI_current->qtlist = NULL;
|
||||
|
||||
if (procmem) /* switch to the procedure memory context */
|
||||
{ /* but free Executor memory before */
|
||||
EndPortalAllocMode();
|
||||
{
|
||||
_SPI_procmem();
|
||||
/* and free Executor memory */
|
||||
MemoryContextResetAndDeleteChildren(_SPI_current->execCxt);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -1016,8 +963,7 @@ _SPI_copy_plan(_SPI_plan *plan, int location)
|
||||
MemoryContext oldcxt = NULL;
|
||||
|
||||
if (location == _SPI_CPLAN_PROCXT)
|
||||
oldcxt = MemoryContextSwitchTo((MemoryContext)
|
||||
PortalGetVariableMemory(_SPI_current->portal));
|
||||
oldcxt = MemoryContextSwitchTo(_SPI_current->procCxt);
|
||||
else if (location == _SPI_CPLAN_TOPCXT)
|
||||
oldcxt = MemoryContextSwitchTo(TopMemoryContext);
|
||||
|
||||
@ -1033,7 +979,7 @@ _SPI_copy_plan(_SPI_plan *plan, int location)
|
||||
else
|
||||
newplan->argtypes = NULL;
|
||||
|
||||
if (location != _SPI_CPLAN_CURCXT)
|
||||
if (oldcxt != NULL)
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
return newplan;
|
||||
|
Reference in New Issue
Block a user