diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2002-12-15 16:17:59 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2002-12-15 16:17:59 +0000 |
commit | 5bab36e9f6c3f3a9e14a89e1124179a339d2c3a1 (patch) | |
tree | a05154b129808efc7882599d96a1132051c2403b /src/backend/executor/execUtils.c | |
parent | 90b3a0b6fd3bc74804c01156491635e5d95091d9 (diff) |
Revise executor APIs so that all per-query state structure is built in
a per-query memory context created by CreateExecutorState --- and destroyed
by FreeExecutorState. This provides a final solution to the longstanding
problem of memory leaked by various ExecEndNode calls.
Diffstat (limited to 'src/backend/executor/execUtils.c')
-rw-r--r-- | src/backend/executor/execUtils.c | 345 |
1 files changed, 239 insertions, 106 deletions
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 36997a49103..6c2cece7b6e 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -8,13 +8,20 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.92 2002/12/13 19:45:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.93 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES + * CreateExecutorState Create/delete executor working state + * FreeExecutorState + * CreateExprContext + * FreeExprContext + * * ExecAssignExprContext Common code for plan node init routines. + * ExecAssignResultType + * etc * * ExecOpenIndices \ * ExecCloseIndices | referenced by InitPlan, EndPlan, @@ -26,7 +33,6 @@ * NOTES * This file has traditionally been the place to stick misc. * executor support stuff that doesn't really go anyplace else. - * */ #include "postgres.h" @@ -64,6 +70,7 @@ extern int NIndexTupleProcessed; /* have to be defined in the static void ShutdownExprContext(ExprContext *econtext); + /* ---------------------------------------------------------------- * statistic functions * ---------------------------------------------------------------- @@ -124,137 +131,264 @@ DisplayTupleCount(FILE *statfp) } #endif + /* ---------------------------------------------------------------- - * miscellaneous node-init support functions + * Executor state and memory management functions * ---------------------------------------------------------------- */ /* ---------------- - * ExecAssignExprContext + * CreateExecutorState * - * This initializes the ExprContext field. It is only necessary - * to do this for nodes which use ExecQual or ExecProject - * because those routines depend on econtext. Other nodes that - * don't have to evaluate expressions don't need to do this. + * Create and initialize an EState node, which is the root of + * working storage for an entire Executor invocation. * - * Note: we assume CurrentMemoryContext is the correct per-query context. - * This should be true during plan node initialization. + * Principally, this creates the per-query memory context that will be + * used to hold all working data that lives till the end of the query. + * Note that the per-query context will become a child of the caller's + * CurrentMemoryContext. * ---------------- */ -void -ExecAssignExprContext(EState *estate, PlanState *planstate) +EState * +CreateExecutorState(void) { - ExprContext *econtext = makeNode(ExprContext); + EState *estate; + MemoryContext qcontext; + MemoryContext oldcontext; - econtext->ecxt_scantuple = NULL; - econtext->ecxt_innertuple = NULL; - econtext->ecxt_outertuple = NULL; - econtext->ecxt_per_query_memory = CurrentMemoryContext; + /* + * Create the per-query context for this Executor run. + */ + qcontext = AllocSetContextCreate(CurrentMemoryContext, + "ExecutorState", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); /* - * Create working memory for expression evaluation in this context. + * Make the EState node within the per-query context. This way, + * we don't need a separate pfree() operation for it at shutdown. */ - econtext->ecxt_per_tuple_memory = - AllocSetContextCreate(CurrentMemoryContext, - "PlanExprContext", - ALLOCSET_DEFAULT_MINSIZE, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - econtext->ecxt_param_exec_vals = estate->es_param_exec_vals; - econtext->ecxt_param_list_info = estate->es_param_list_info; - econtext->ecxt_aggvalues = NULL; - econtext->ecxt_aggnulls = NULL; - econtext->ecxt_callbacks = NULL; + oldcontext = MemoryContextSwitchTo(qcontext); + + estate = makeNode(EState); + + /* + * Initialize all fields of the Executor State structure + */ + estate->es_direction = ForwardScanDirection; + estate->es_snapshot = SnapshotNow; + estate->es_range_table = NIL; + + estate->es_result_relations = NULL; + estate->es_num_result_relations = 0; + estate->es_result_relation_info = NULL; + + estate->es_junkFilter = NULL; + estate->es_into_relation_descriptor = NULL; + + estate->es_param_list_info = NULL; + estate->es_param_exec_vals = NULL; + + estate->es_query_cxt = qcontext; + + estate->es_tupleTable = NULL; - planstate->ps_ExprContext = econtext; + estate->es_processed = 0; + estate->es_lastoid = InvalidOid; + estate->es_rowMark = NIL; + + estate->es_instrument = false; + + estate->es_exprcontexts = NIL; + + estate->es_per_tuple_exprcontext = NULL; + + estate->es_origPlan = NULL; + estate->es_evalPlanQual = NULL; + estate->es_evTupleNull = NULL; + estate->es_evTuple = NULL; + estate->es_useEvalPlan = false; + + /* + * Return the executor state structure + */ + MemoryContextSwitchTo(oldcontext); + + return estate; +} + +/* ---------------- + * FreeExecutorState + * + * Release an EState along with all remaining working storage. + * + * Note: this is not responsible for releasing non-memory resources, + * such as open relations or buffer pins. But it will shut down any + * still-active ExprContexts within the EState. That is sufficient + * cleanup for situations where the EState has only been used for expression + * evaluation, and not to run a complete Plan. + * + * This can be called in any memory context ... so long as it's not one + * of the ones to be freed. + * ---------------- + */ +void +FreeExecutorState(EState *estate) +{ + /* + * Shut down and free any remaining ExprContexts. We do this + * explicitly to ensure that any remaining shutdown callbacks get + * called (since they might need to release resources that aren't + * simply memory within the per-query memory context). + */ + while (estate->es_exprcontexts) + { + FreeExprContext((ExprContext *) lfirst(estate->es_exprcontexts)); + /* FreeExprContext removed the list link for us */ + } + /* + * Free the per-query memory context, thereby releasing all working + * memory, including the EState node itself. + */ + MemoryContextDelete(estate->es_query_cxt); } /* ---------------- - * MakeExprContext + * CreateExprContext + * + * Create a context for expression evaluation within an EState. + * + * An executor run may require multiple ExprContexts (we usually make one + * for each Plan node, and a separate one for per-output-tuple processing + * such as constraint checking). Each ExprContext has its own "per-tuple" + * memory context. * - * Build an expression context for use outside normal plan-node cases. - * A fake scan-tuple slot can be supplied (pass NULL if not needed). - * A memory context sufficiently long-lived to use as fcache context - * must be supplied as well. + * Note we make no assumption about the caller's memory context. * ---------------- */ ExprContext * -MakeExprContext(TupleTableSlot *slot, - MemoryContext queryContext) +CreateExprContext(EState *estate) { - ExprContext *econtext = makeNode(ExprContext); + ExprContext *econtext; + MemoryContext oldcontext; - econtext->ecxt_scantuple = slot; + /* Create the ExprContext node within the per-query memory context */ + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + econtext = makeNode(ExprContext); + + /* Initialize fields of ExprContext */ + econtext->ecxt_scantuple = NULL; econtext->ecxt_innertuple = NULL; econtext->ecxt_outertuple = NULL; - econtext->ecxt_per_query_memory = queryContext; + + econtext->ecxt_per_query_memory = estate->es_query_cxt; /* - * We make the temporary context a child of current working context, - * not of the specified queryContext. This seems reasonable but I'm - * not totally sure about it... - * - * Expression contexts made via this routine typically don't live long - * enough to get reset, so specify a minsize of 0. That avoids - * alloc'ing any memory in the common case where expr eval doesn't use - * any. + * Create working memory for expression evaluation in this context. */ econtext->ecxt_per_tuple_memory = - AllocSetContextCreate(CurrentMemoryContext, - "TempExprContext", - 0, + AllocSetContextCreate(estate->es_query_cxt, + "ExprContext", + ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); - econtext->ecxt_param_exec_vals = NULL; - econtext->ecxt_param_list_info = NULL; + + econtext->ecxt_param_exec_vals = estate->es_param_exec_vals; + econtext->ecxt_param_list_info = estate->es_param_list_info; + econtext->ecxt_aggvalues = NULL; econtext->ecxt_aggnulls = NULL; + + econtext->domainValue_datum = (Datum) 0; + econtext->domainValue_isNull = true; + + econtext->ecxt_estate = estate; + econtext->ecxt_callbacks = NULL; + /* + * Link the ExprContext into the EState to ensure it is shut down + * when the EState is freed. Because we use lcons(), shutdowns will + * occur in reverse order of creation, which may not be essential + * but can't hurt. + */ + estate->es_exprcontexts = lcons(econtext, estate->es_exprcontexts); + + MemoryContextSwitchTo(oldcontext); + return econtext; } -/* - * Free an ExprContext made by MakeExprContext, including the temporary - * context used for expression evaluation. Note this will cause any - * pass-by-reference expression result to go away! +/* ---------------- + * FreeExprContext + * + * Free an expression context, including calling any remaining + * shutdown callbacks. + * + * Since we free the temporary context used for expression evaluation, + * any previously computed pass-by-reference expression result will go away! + * + * Note we make no assumption about the caller's memory context. + * ---------------- */ void FreeExprContext(ExprContext *econtext) { + EState *estate; + /* Call any registered callbacks */ ShutdownExprContext(econtext); /* And clean up the memory used */ MemoryContextDelete(econtext->ecxt_per_tuple_memory); + /* Unlink self from owning EState */ + estate = econtext->ecxt_estate; + estate->es_exprcontexts = lremove(econtext, estate->es_exprcontexts); + /* And delete the ExprContext node */ pfree(econtext); } /* * Build a per-output-tuple ExprContext for an EState. * - * This is normally invoked via GetPerTupleExprContext() macro. + * This is normally invoked via GetPerTupleExprContext() macro, + * not directly. */ ExprContext * MakePerTupleExprContext(EState *estate) { if (estate->es_per_tuple_exprcontext == NULL) - { - MemoryContext oldContext; + estate->es_per_tuple_exprcontext = CreateExprContext(estate); - oldContext = MemoryContextSwitchTo(estate->es_query_cxt); - estate->es_per_tuple_exprcontext = - MakeExprContext(NULL, estate->es_query_cxt); - MemoryContextSwitchTo(oldContext); - } return estate->es_per_tuple_exprcontext; } + /* ---------------------------------------------------------------- - * Result slot tuple type and ProjectionInfo support + * miscellaneous node-init support functions + * + * Note: all of these are expected to be called with CurrentMemoryContext + * equal to the per-query memory context. * ---------------------------------------------------------------- */ /* ---------------- + * ExecAssignExprContext + * + * This initializes the ps_ExprContext field. It is only necessary + * to do this for nodes which use ExecQual or ExecProject + * because those routines require an econtext. Other nodes that + * don't have to evaluate expressions don't need to do this. + * ---------------- + */ +void +ExecAssignExprContext(EState *estate, PlanState *planstate) +{ + planstate->ps_ExprContext = CreateExprContext(estate); +} + +/* ---------------- * ExecAssignResultType * ---------------- */ @@ -368,34 +502,12 @@ ExecAssignProjectionInfo(PlanState *planstate) /* ---------------- - * ExecFreeProjectionInfo - * ---------------- - */ -void -ExecFreeProjectionInfo(PlanState *planstate) -{ - ProjectionInfo *projInfo; - - /* - * get projection info. if NULL then this node has none so we just - * return. - */ - projInfo = planstate->ps_ProjInfo; - if (projInfo == NULL) - return; - - /* - * clean up memory used. - */ - if (projInfo->pi_tupValue != NULL) - pfree(projInfo->pi_tupValue); - - pfree(projInfo); - planstate->ps_ProjInfo = NULL; -} - -/* ---------------- * ExecFreeExprContext + * + * A plan node's ExprContext should be freed explicitly during ExecEndNode + * because there may be shutdown callbacks to call. (Other resources made + * by the above routines, such as projection info, don't need to be freed + * explicitly because they're just memory in the per-query memory context.) * ---------------- */ void @@ -411,16 +523,8 @@ ExecFreeExprContext(PlanState *planstate) if (econtext == NULL) return; - /* - * clean up any registered callbacks - */ - ShutdownExprContext(econtext); + FreeExprContext(econtext); - /* - * clean up memory used. - */ - MemoryContextDelete(econtext->ecxt_per_tuple_memory); - pfree(econtext); planstate->ps_ExprContext = NULL; } @@ -612,7 +716,8 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo) } /* - * XXX should free indexInfo array here too. + * XXX should free indexInfo array here too? Currently we assume that + * such stuff will be cleaned up automatically in FreeExecutorState. */ } @@ -674,16 +779,31 @@ ExecInsertIndexTuples(TupleTableSlot *slot, for (i = 0; i < numIndices; i++) { IndexInfo *indexInfo; - List *predicate; InsertIndexResult result; if (relationDescs[i] == NULL) continue; indexInfo = indexInfoArray[i]; - predicate = indexInfo->ii_PredicateState; - if (predicate != NIL) + + /* Check for partial index */ + if (indexInfo->ii_Predicate != NIL) { + List *predicate; + + /* + * If predicate state not set up yet, create it (in the + * estate's per-query context) + */ + predicate = indexInfo->ii_PredicateState; + if (predicate == NIL) + { + predicate = (List *) + ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, + estate); + indexInfo->ii_PredicateState = predicate; + } + /* Skip this index-update if the predicate isn't satisfied */ if (!ExecQual(predicate, econtext, false)) continue; @@ -811,6 +931,17 @@ static void ShutdownExprContext(ExprContext *econtext) { ExprContext_CB *ecxt_callback; + MemoryContext oldcontext; + + /* Fast path in normal case where there's nothing to do. */ + if (econtext->ecxt_callbacks == NULL) + return; + + /* + * Call the callbacks in econtext's per-tuple context. This ensures + * that any memory they might leak will get cleaned up. + */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); /* * Call each callback function in reverse registration order. @@ -821,4 +952,6 @@ ShutdownExprContext(ExprContext *econtext) (*ecxt_callback->function) (ecxt_callback->arg); pfree(ecxt_callback); } + + MemoryContextSwitchTo(oldcontext); } |