Update EvalPlanQual() to work with new executor memory management method.

It doesn't leak memory anymore ...
This commit is contained in:
Tom Lane 2002-12-18 00:14:47 +00:00
parent 689659764f
commit 350260795a
3 changed files with 186 additions and 139 deletions

View File

@ -26,7 +26,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.194 2002/12/15 21:01:34 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.195 2002/12/18 00:14:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -46,6 +46,22 @@
#include "utils/lsyscache.h"
typedef struct execRowMark
{
Relation relation;
Index rti;
char resname[32];
} execRowMark;
typedef struct evalPlanQual
{
Index rti;
EState *estate;
PlanState *planstate;
struct evalPlanQual *next; /* stack of active PlanQual plans */
struct evalPlanQual *free; /* list of free PlanQual plans */
} evalPlanQual;
/* decls for local routines only used within this module */
static void InitPlan(QueryDesc *queryDesc);
static void initResultRelInfo(ResultRelInfo *resultRelInfo,
@ -69,6 +85,9 @@ static void ExecUpdate(TupleTableSlot *slot, ItemPointer tupleid,
static TupleTableSlot *EvalPlanQualNext(EState *estate);
static void EndEvalPlanQual(EState *estate);
static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation);
static void EvalPlanQualStart(evalPlanQual *epq, EState *estate,
evalPlanQual *priorepq);
static void EvalPlanQualStop(evalPlanQual *epq);
/* end of local decls */
@ -365,21 +384,6 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
* ===============================================================
*/
typedef struct execRowMark
{
Relation relation;
Index rti;
char resname[32];
} execRowMark;
typedef struct evalPlanQual
{
Plan *plan; /* XXX temporary */
PlanState *planstate;
Index rti;
EState estate;
struct evalPlanQual *free;
} evalPlanQual;
/* ----------------------------------------------------------------
* InitPlan
@ -518,10 +522,10 @@ InitPlan(QueryDesc *queryDesc)
}
/* mark EvalPlanQual not active */
estate->es_origPlan = plan;
estate->es_topPlan = plan;
estate->es_evalPlanQual = NULL;
estate->es_evTuple = NULL;
estate->es_evTupleNull = NULL;
estate->es_evTuple = NULL;
estate->es_useEvalPlan = false;
/*
@ -1594,7 +1598,6 @@ EvalPlanQual(EState *estate, Index rti, ItemPointer tid)
Relation relation;
HeapTupleData tuple;
HeapTuple copyTuple = NULL;
int rtsize;
bool endNode;
Assert(rti != 0);
@ -1686,15 +1689,13 @@ EvalPlanQual(EState *estate, Index rti, ItemPointer tid)
/*
* Need to run a recheck subquery. Find or create a PQ stack entry.
*/
epq = (evalPlanQual *) estate->es_evalPlanQual;
rtsize = length(estate->es_range_table);
epq = estate->es_evalPlanQual;
endNode = true;
if (epq != NULL && epq->rti == 0)
{
/* Top PQ stack entry is idle, so re-use it */
Assert(!(estate->es_useEvalPlan) &&
epq->estate.es_evalPlanQual == NULL);
Assert(!(estate->es_useEvalPlan) && epq->next == NULL);
epq->rti = rti;
endNode = false;
}
@ -1706,26 +1707,21 @@ EvalPlanQual(EState *estate, Index rti, ItemPointer tid)
* forget all what we done after Ra was suspended. Cool? -:))
*/
if (epq != NULL && epq->rti != rti &&
epq->estate.es_evTuple[rti - 1] != NULL)
epq->estate->es_evTuple[rti - 1] != NULL)
{
do
{
evalPlanQual *oldepq;
/* pop previous PlanQual from the stack */
epqstate = &(epq->estate);
oldepq = (evalPlanQual *) epqstate->es_evalPlanQual;
Assert(oldepq->rti != 0);
/* stop execution */
ExecEndNode(epq->planstate);
ExecDropTupleTable(epqstate->es_tupleTable, true);
epqstate->es_tupleTable = NULL;
heap_freetuple(epqstate->es_evTuple[epq->rti - 1]);
epqstate->es_evTuple[epq->rti - 1] = NULL;
EvalPlanQualStop(epq);
/* pop previous PlanQual from the stack */
oldepq = epq->next;
Assert(oldepq && oldepq->rti != 0);
/* push current PQ to freePQ stack */
oldepq->free = epq;
epq = oldepq;
estate->es_evalPlanQual = (Pointer) epq;
estate->es_evalPlanQual = epq;
} while (epq->rti != rti);
}
@ -1740,62 +1736,26 @@ EvalPlanQual(EState *estate, Index rti, ItemPointer tid)
if (newepq == NULL) /* first call or freePQ stack is empty */
{
newepq = (evalPlanQual *) palloc(sizeof(evalPlanQual));
newepq = (evalPlanQual *) palloc0(sizeof(evalPlanQual));
newepq->free = NULL;
/*
* Each stack level has its own copy of the plan tree. This
* is wasteful, but necessary until plan trees are fully
* read-only.
*/
newepq->plan = copyObject(estate->es_origPlan);
/*
* Init stack level's EState. We share top level's copy of
* es_result_relations array and other non-changing status. We
* need our own tupletable, es_param_exec_vals, and other
* changeable state.
*/
epqstate = &(newepq->estate);
memcpy(epqstate, estate, sizeof(EState));
epqstate->es_direction = ForwardScanDirection;
if (estate->es_origPlan->nParamExec > 0)
epqstate->es_param_exec_vals = (ParamExecData *)
palloc(estate->es_origPlan->nParamExec *
sizeof(ParamExecData));
epqstate->es_tupleTable = NULL;
epqstate->es_per_tuple_exprcontext = NULL;
/*
* Each epqstate must have its own es_evTupleNull state, but
* all the stack entries share es_evTuple state. This allows
* sub-rechecks to inherit the value being examined by an
* outer recheck.
*/
epqstate->es_evTupleNull = (bool *) palloc(rtsize * sizeof(bool));
if (epq == NULL)
/* first PQ stack entry */
epqstate->es_evTuple = (HeapTuple *)
palloc0(rtsize * sizeof(HeapTuple));
else
/* later stack entries share the same storage */
epqstate->es_evTuple = epq->estate.es_evTuple;
newepq->estate = NULL;
newepq->planstate = NULL;
}
else
{
/* recycle previously used EState */
epqstate = &(newepq->estate);
/* recycle previously used PlanQual */
Assert(newepq->estate == NULL);
epq->free = NULL;
}
/* push current PQ to the stack */
epqstate->es_evalPlanQual = (Pointer) epq;
newepq->next = epq;
epq = newepq;
estate->es_evalPlanQual = (Pointer) epq;
estate->es_evalPlanQual = epq;
epq->rti = rti;
endNode = false;
}
Assert(epq->rti == rti);
epqstate = &(epq->estate);
/*
* Ok - we're requested for the same RTE. Unfortunately we still have
@ -1804,81 +1764,78 @@ EvalPlanQual(EState *estate, Index rti, ItemPointer tid)
* could make that work if insertion of the target tuple were
* integrated with the Param mechanism somehow, so that the upper plan
* nodes know that their children's outputs have changed.
*
* Note that the stack of free evalPlanQual nodes is quite useless at
* the moment, since it only saves us from pallocing/releasing the
* evalPlanQual nodes themselves. But it will be useful once we
* implement ReScan instead of end/restart for re-using PlanQual nodes.
*/
if (endNode)
{
/* stop execution */
ExecEndNode(epq->planstate);
ExecDropTupleTable(epqstate->es_tupleTable, true);
epqstate->es_tupleTable = NULL;
EvalPlanQualStop(epq);
}
/*
* Initialize new recheck query.
*
* Note: if we were re-using PlanQual plans via ExecReScan, we'd need
* to instead copy down changeable state from the top plan (including
* es_result_relation_info, es_junkFilter) and reset locally changeable
* state in the epq (including es_param_exec_vals, es_evTupleNull).
*/
EvalPlanQualStart(epq, estate, epq->next);
/*
* free old RTE' tuple, if any, and store target tuple where
* relation's scan node will see it
*/
epqstate = epq->estate;
if (epqstate->es_evTuple[rti - 1] != NULL)
heap_freetuple(epqstate->es_evTuple[rti - 1]);
epqstate->es_evTuple[rti - 1] = copyTuple;
/*
* Initialize for new recheck query; be careful to copy down state
* that might have changed in top EState.
*/
epqstate->es_result_relation_info = estate->es_result_relation_info;
epqstate->es_junkFilter = estate->es_junkFilter;
if (estate->es_origPlan->nParamExec > 0)
memset(epqstate->es_param_exec_vals, 0,
estate->es_origPlan->nParamExec * sizeof(ParamExecData));
memset(epqstate->es_evTupleNull, false, rtsize * sizeof(bool));
epqstate->es_useEvalPlan = false;
Assert(epqstate->es_tupleTable == NULL);
epqstate->es_tupleTable =
ExecCreateTupleTable(estate->es_tupleTable->size);
epq->planstate = ExecInitNode(epq->plan, epqstate);
return EvalPlanQualNext(estate);
}
static TupleTableSlot *
EvalPlanQualNext(EState *estate)
{
evalPlanQual *epq = (evalPlanQual *) estate->es_evalPlanQual;
EState *epqstate = &(epq->estate);
evalPlanQual *oldepq;
evalPlanQual *epq = estate->es_evalPlanQual;
MemoryContext oldcontext;
TupleTableSlot *slot;
Assert(epq->rti != 0);
lpqnext:;
oldcontext = MemoryContextSwitchTo(epq->estate->es_query_cxt);
slot = ExecProcNode(epq->planstate);
MemoryContextSwitchTo(oldcontext);
/*
* No more tuples for this PQ. Continue previous one.
*/
if (TupIsNull(slot))
{
evalPlanQual *oldepq;
/* stop execution */
ExecEndNode(epq->planstate);
ExecDropTupleTable(epqstate->es_tupleTable, true);
epqstate->es_tupleTable = NULL;
heap_freetuple(epqstate->es_evTuple[epq->rti - 1]);
epqstate->es_evTuple[epq->rti - 1] = NULL;
EvalPlanQualStop(epq);
/* pop old PQ from the stack */
oldepq = (evalPlanQual *) epqstate->es_evalPlanQual;
if (oldepq == (evalPlanQual *) NULL)
oldepq = epq->next;
if (oldepq == NULL)
{
epq->rti = 0; /* this is the first (oldest) */
estate->es_useEvalPlan = false; /* PQ - mark as free and */
return (NULL); /* continue Query execution */
/* this is the first (oldest) PQ - mark as free */
epq->rti = 0;
estate->es_useEvalPlan = false;
/* and continue Query execution */
return (NULL);
}
Assert(oldepq->rti != 0);
/* push current PQ to freePQ stack */
oldepq->free = epq;
epq = oldepq;
epqstate = &(epq->estate);
estate->es_evalPlanQual = (Pointer) epq;
estate->es_evalPlanQual = epq;
goto lpqnext;
}
@ -1888,40 +1845,130 @@ lpqnext:;
static void
EndEvalPlanQual(EState *estate)
{
evalPlanQual *epq = (evalPlanQual *) estate->es_evalPlanQual;
EState *epqstate = &(epq->estate);
evalPlanQual *oldepq;
evalPlanQual *epq = estate->es_evalPlanQual;
if (epq->rti == 0) /* plans already shutdowned */
{
Assert(epq->estate.es_evalPlanQual == NULL);
Assert(epq->next == NULL);
return;
}
for (;;)
{
evalPlanQual *oldepq;
/* stop execution */
ExecEndNode(epq->planstate);
ExecDropTupleTable(epqstate->es_tupleTable, true);
epqstate->es_tupleTable = NULL;
if (epqstate->es_evTuple[epq->rti - 1] != NULL)
{
heap_freetuple(epqstate->es_evTuple[epq->rti - 1]);
epqstate->es_evTuple[epq->rti - 1] = NULL;
}
EvalPlanQualStop(epq);
/* pop old PQ from the stack */
oldepq = (evalPlanQual *) epqstate->es_evalPlanQual;
if (oldepq == (evalPlanQual *) NULL)
oldepq = epq->next;
if (oldepq == NULL)
{
epq->rti = 0; /* this is the first (oldest) */
estate->es_useEvalPlan = false; /* PQ - mark as free */
/* this is the first (oldest) PQ - mark as free */
epq->rti = 0;
estate->es_useEvalPlan = false;
break;
}
Assert(oldepq->rti != 0);
/* push current PQ to freePQ stack */
oldepq->free = epq;
epq = oldepq;
epqstate = &(epq->estate);
estate->es_evalPlanQual = (Pointer) epq;
estate->es_evalPlanQual = epq;
}
}
/*
* Start execution of one level of PlanQual.
*
* This is a cut-down version of ExecutorStart(): we copy some state from
* the top-level estate rather than initializing it fresh.
*/
static void
EvalPlanQualStart(evalPlanQual *epq, EState *estate, evalPlanQual *priorepq)
{
EState *epqstate;
int rtsize;
MemoryContext oldcontext;
rtsize = length(estate->es_range_table);
epq->estate = epqstate = CreateExecutorState();
oldcontext = MemoryContextSwitchTo(epqstate->es_query_cxt);
/*
* The epqstates share the top query's copy of unchanging state such
* as the snapshot, rangetable, result-rel info, and external Param info.
* They need their own copies of local state, including a tuple table,
* es_param_exec_vals, etc.
*/
epqstate->es_direction = ForwardScanDirection;
epqstate->es_snapshot = estate->es_snapshot;
epqstate->es_range_table = estate->es_range_table;
epqstate->es_result_relations = estate->es_result_relations;
epqstate->es_num_result_relations = estate->es_num_result_relations;
epqstate->es_result_relation_info = estate->es_result_relation_info;
epqstate->es_junkFilter = estate->es_junkFilter;
epqstate->es_into_relation_descriptor = estate->es_into_relation_descriptor;
epqstate->es_param_list_info = estate->es_param_list_info;
if (estate->es_topPlan->nParamExec > 0)
epqstate->es_param_exec_vals = (ParamExecData *)
palloc0(estate->es_topPlan->nParamExec * sizeof(ParamExecData));
epqstate->es_rowMark = estate->es_rowMark;
epqstate->es_instrument = estate->es_instrument;
epqstate->es_topPlan = estate->es_topPlan;
/*
* Each epqstate must have its own es_evTupleNull state, but
* all the stack entries share es_evTuple state. This allows
* sub-rechecks to inherit the value being examined by an
* outer recheck.
*/
epqstate->es_evTupleNull = (bool *) palloc0(rtsize * sizeof(bool));
if (priorepq == NULL)
/* first PQ stack entry */
epqstate->es_evTuple = (HeapTuple *)
palloc0(rtsize * sizeof(HeapTuple));
else
/* later stack entries share the same storage */
epqstate->es_evTuple = priorepq->estate->es_evTuple;
epqstate->es_tupleTable =
ExecCreateTupleTable(estate->es_tupleTable->size);
epq->planstate = ExecInitNode(estate->es_topPlan, epqstate);
MemoryContextSwitchTo(oldcontext);
}
/*
* End execution of one level of PlanQual.
*
* This is a cut-down version of ExecutorEnd(); basically we want to do most
* of the normal cleanup, but *not* close result relations (which we are
* just sharing from the outer query).
*/
static void
EvalPlanQualStop(evalPlanQual *epq)
{
EState *epqstate = epq->estate;
MemoryContext oldcontext;
oldcontext = MemoryContextSwitchTo(epqstate->es_query_cxt);
ExecEndNode(epq->planstate);
ExecDropTupleTable(epqstate->es_tupleTable, true);
epqstate->es_tupleTable = NULL;
if (epqstate->es_evTuple[epq->rti - 1] != NULL)
{
heap_freetuple(epqstate->es_evTuple[epq->rti - 1]);
epqstate->es_evTuple[epq->rti - 1] = NULL;
}
MemoryContextSwitchTo(oldcontext);
FreeExecutorState(epqstate);
epq->estate = NULL;
epq->planstate = NULL;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.93 2002/12/15 16:17:46 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.94 2002/12/18 00:14:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -204,7 +204,7 @@ CreateExecutorState(void)
estate->es_per_tuple_exprcontext = NULL;
estate->es_origPlan = NULL;
estate->es_topPlan = NULL;
estate->es_evalPlanQual = NULL;
estate->es_evTupleNull = NULL;
estate->es_evTuple = NULL;

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: execnodes.h,v 1.87 2002/12/15 21:01:34 tgl Exp $
* $Id: execnodes.h,v 1.88 2002/12/18 00:14:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -316,11 +316,11 @@ typedef struct EState
ExprContext *es_per_tuple_exprcontext;
/* Below is to re-evaluate plan qual in READ COMMITTED mode */
struct Plan *es_origPlan;
Pointer es_evalPlanQual;
bool *es_evTupleNull;
HeapTuple *es_evTuple;
bool es_useEvalPlan;
Plan *es_topPlan; /* link to top of plan tree */
struct evalPlanQual *es_evalPlanQual; /* chain of PlanQual states */
bool *es_evTupleNull; /* local array of EPQ status */
HeapTuple *es_evTuple; /* shared array of EPQ substitute tuples */
bool es_useEvalPlan; /* evaluating EPQ tuples? */
} EState;