summaryrefslogtreecommitdiff
path: root/src/backend/executor/execUtils.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2002-12-15 16:17:59 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2002-12-15 16:17:59 +0000
commit5bab36e9f6c3f3a9e14a89e1124179a339d2c3a1 (patch)
treea05154b129808efc7882599d96a1132051c2403b /src/backend/executor/execUtils.c
parent90b3a0b6fd3bc74804c01156491635e5d95091d9 (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.c345
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);
}