diff options
author | Tom Lane | 2002-12-15 16:17:59 +0000 |
---|---|---|
committer | Tom Lane | 2002-12-15 16:17:59 +0000 |
commit | 5bab36e9f6c3f3a9e14a89e1124179a339d2c3a1 (patch) | |
tree | a05154b129808efc7882599d96a1132051c2403b /src/backend/executor | |
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')
24 files changed, 563 insertions, 366 deletions
diff --git a/src/backend/executor/README b/src/backend/executor/README index d9b0ea1275a..ebdbe2d9d0c 100644 --- a/src/backend/executor/README +++ b/src/backend/executor/README @@ -1,4 +1,4 @@ -$Header: /cvsroot/pgsql/src/backend/executor/README,v 1.2 2002/12/05 15:50:30 tgl Exp $ +$Header: /cvsroot/pgsql/src/backend/executor/README,v 1.3 2002/12/15 16:17:45 tgl Exp $ The Postgres Executor --------------------- @@ -60,6 +60,83 @@ ExprState nodes. (Actually, there are also List nodes, which are used as "glue" in all four kinds of tree.) +Memory Management +----------------- + +A "per query" memory context is created during CreateExecutorState(); +all storage allocated during an executor invocation is allocated in that +context or a child context. This allows easy reclamation of storage +during executor shutdown --- rather than messing with retail pfree's and +probable storage leaks, we just destroy the memory context. + +In particular, the plan state trees and expression state trees described +in the previous section are allocated in the per-query memory context. + +To avoid intra-query memory leaks, most processing while a query runs +is done in "per tuple" memory contexts, which are so-called because they +are typically reset to empty once per tuple. Per-tuple contexts are usually +associated with ExprContexts, and commonly each PlanState node has its own +ExprContext to evaluate its qual and targetlist expressions in. + + +Query Processing Control Flow +----------------------------- + +This is a sketch of control flow for full query processing: + + CreateQueryDesc + + ExecutorStart + CreateExecutorState + creates per-query context + switch to per-query context to run ExecInitNode + ExecInitNode --- recursively scans plan tree + CreateExprContext + creates per-tuple context + ExecInitExpr + + ExecutorRun + ExecProcNode --- recursively called in per-query context + ExecEvalExpr --- called in per-tuple context + ResetExprContext --- to free memory + + ExecutorEnd + ExecEndNode --- recursively releases resources + FreeExecutorState + frees per-query context and child contexts + + FreeQueryDesc + +Per above comments, it's not really critical for ExecEndNode to free any +memory; it'll all go away in FreeExecutorState anyway. However, we do need to +be careful to close relations, drop buffer pins, etc, so we do need to scan +the plan state tree to find these sorts of resources. + + +The executor can also be used to evaluate simple expressions without any Plan +tree ("simple" meaning "no aggregates and no sub-selects", though such might +be hidden inside function calls). This case has a flow of control like + + CreateExecutorState + creates per-query context + + CreateExprContext -- or use GetPerTupleExprContext(estate) + creates per-tuple context + + ExecPrepareExpr + switch to per-query context to run ExecInitExpr + ExecInitExpr + + Repeatedly do: + ExecEvalExprSwitchContext + ExecEvalExpr --- called in per-tuple context + ResetExprContext --- to free memory + + FreeExecutorState + frees per-query context, as well as ExprContext + (a separate FreeExprContext call is not necessary) + + EvalPlanQual (READ COMMITTED update checking) --------------------------------------------- diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index bac7398825e..a25f2f2e296 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,7 +26,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.192 2002/12/13 19:45:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.193 2002/12/15 16:17:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,7 +40,6 @@ #include "executor/execdebug.h" #include "executor/execdefs.h" #include "miscadmin.h" -#include "optimizer/planmain.h" #include "optimizer/var.h" #include "parser/parsetree.h" #include "utils/acl.h" @@ -53,7 +52,6 @@ static void initResultRelInfo(ResultRelInfo *resultRelInfo, Index resultRelationIndex, List *rangeTable, CmdType operation); -static void EndPlan(PlanState *planstate, EState *estate); static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate, CmdType operation, long numberTuples, @@ -86,27 +84,31 @@ static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation); * field of the QueryDesc is filled in to describe the tuples that will be * returned, and the internal fields (estate and planstate) are set up. * - * XXX this will change soon: - * NB: the CurrentMemoryContext when this is called must be the context - * to be used as the per-query context for the query plan. ExecutorRun() - * and ExecutorEnd() must be called in this same memory context. + * NB: the CurrentMemoryContext when this is called will become the parent + * of the per-query context used for this Executor invocation. * ---------------------------------------------------------------- */ void ExecutorStart(QueryDesc *queryDesc) { EState *estate; + MemoryContext oldcontext; /* sanity checks: queryDesc must not be started already */ Assert(queryDesc != NULL); Assert(queryDesc->estate == NULL); /* - * Build EState, fill with parameters from queryDesc + * Build EState, switch into per-query memory context for startup. */ estate = CreateExecutorState(); queryDesc->estate = estate; + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + /* + * Fill in parameters, if any, from queryDesc + */ estate->es_param_list_info = queryDesc->params; if (queryDesc->plantree->nParamExec > 0) @@ -128,6 +130,8 @@ ExecutorStart(QueryDesc *queryDesc) * Initialize the plan state tree */ InitPlan(queryDesc); + + MemoryContextSwitchTo(oldcontext); } /* ---------------------------------------------------------------- @@ -152,23 +156,30 @@ TupleTableSlot * ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count) { - CmdType operation; EState *estate; + CmdType operation; CommandDest dest; DestReceiver *destfunc; TupleTableSlot *result; + MemoryContext oldcontext; + + /* sanity checks */ + Assert(queryDesc != NULL); + + estate = queryDesc->estate; + + Assert(estate != NULL); /* - * sanity checks + * Switch into per-query memory context */ - Assert(queryDesc != NULL); + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); /* * extract information from the query descriptor and the query * feature. */ operation = queryDesc->operation; - estate = queryDesc->estate; dest = queryDesc->dest; /* @@ -199,6 +210,8 @@ ExecutorRun(QueryDesc *queryDesc, */ (*destfunc->cleanup) (destfunc); + MemoryContextSwitchTo(oldcontext); + return result; } @@ -213,72 +226,37 @@ void ExecutorEnd(QueryDesc *queryDesc) { EState *estate; + MemoryContext oldcontext; /* sanity checks */ Assert(queryDesc != NULL); estate = queryDesc->estate; - EndPlan(queryDesc->planstate, estate); - - if (estate->es_snapshot != NULL) - { - if (estate->es_snapshot->xcnt > 0) - pfree(estate->es_snapshot->xip); - pfree(estate->es_snapshot); - estate->es_snapshot = NULL; - } - - if (estate->es_param_exec_vals != NULL) - { - pfree(estate->es_param_exec_vals); - estate->es_param_exec_vals = NULL; - } -} - - -/* - * CreateExecutorState - */ -EState * -CreateExecutorState(void) -{ - EState *state; + Assert(estate != NULL); /* - * create a new executor state + * Switch into per-query memory context to run ExecEndPlan */ - state = makeNode(EState); + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + ExecEndPlan(queryDesc->planstate, estate); /* - * initialize the Executor State structure + * Must switch out of context before destroying it */ - state->es_direction = ForwardScanDirection; - state->es_range_table = NIL; - - state->es_result_relations = NULL; - state->es_num_result_relations = 0; - state->es_result_relation_info = NULL; - - state->es_junkFilter = NULL; - - state->es_into_relation_descriptor = NULL; - - state->es_param_list_info = NULL; - state->es_param_exec_vals = NULL; - - state->es_tupleTable = NULL; - - state->es_query_cxt = CurrentMemoryContext; - - state->es_instrument = false; - - state->es_per_tuple_exprcontext = NULL; + MemoryContextSwitchTo(oldcontext); /* - * return the executor state structure + * Release EState and per-query memory context. This should release + * everything the executor has allocated. */ - return state; + FreeExecutorState(estate); + + /* Reset queryDesc fields that no longer point to anything */ + queryDesc->tupDesc = NULL; + queryDesc->estate = NULL; + queryDesc->planstate = NULL; } @@ -794,13 +772,13 @@ initResultRelInfo(ResultRelInfo *resultRelInfo, } /* ---------------------------------------------------------------- - * EndPlan + * ExecEndPlan * * Cleans up the query plan -- closes files and frees up storage * ---------------------------------------------------------------- */ -static void -EndPlan(PlanState *planstate, EState *estate) +void +ExecEndPlan(PlanState *planstate, EState *estate) { ResultRelInfo *resultRelInfo; int i; @@ -1542,9 +1520,8 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, for (i = 0; i < ncheck; i++) { qual = (List *) stringToNode(check[i].ccbin); - fix_opfuncids((Node *) qual); resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecInitExpr((Expr *) qual, NULL); + ExecPrepareExpr((Expr *) qual, estate); } MemoryContextSwitchTo(oldContext); } diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index a3f79c3ac80..971773b1212 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.120 2002/12/14 00:17:50 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.121 2002/12/15 16:17:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,6 +40,7 @@ #include "executor/functions.h" #include "executor/nodeSubplan.h" #include "miscadmin.h" +#include "optimizer/planmain.h" #include "parser/parse_expr.h" #include "utils/acl.h" #include "utils/array.h" @@ -1896,9 +1897,11 @@ ExecEvalExprSwitchContext(ExprState *expression, * cleanup work can register a shutdown callback in the ExprContext. * * 'node' is the root of the expression tree to examine - * 'parent' is the PlanState node that owns the expression, - * or NULL if we are preparing an expression that is not associated - * with a plan. (If so, it can't have aggs or subplans.) + * 'parent' is the PlanState node that owns the expression. + * + * 'parent' may be NULL if we are preparing an expression that is not + * associated with a plan tree. (If so, it can't have aggs or subplans.) + * This case should usually come through ExecPrepareExpr, not directly here. */ ExprState * ExecInitExpr(Expr *node, PlanState *parent) @@ -2017,6 +2020,7 @@ ExecInitExpr(Expr *node, PlanState *parent) * parent->subPlan. The subplans will be initialized later. */ parent->subPlan = lcons(sstate, parent->subPlan); + sstate->sub_estate = NULL; sstate->planstate = NULL; sstate->oper = (List *) @@ -2149,6 +2153,7 @@ ExecInitExprInitPlan(SubPlan *node, PlanState *parent) elog(ERROR, "ExecInitExpr: SubPlan not expected here"); /* The subplan's state will be initialized later */ + sstate->sub_estate = NULL; sstate->planstate = NULL; sstate->oper = (List *) ExecInitExpr((Expr *) node->oper, parent); @@ -2159,6 +2164,33 @@ ExecInitExprInitPlan(SubPlan *node, PlanState *parent) return sstate; } +/* + * ExecPrepareExpr --- initialize for expression execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we apply + * fix_opfuncids() to the passed expression tree to be sure it is ready + * to run. (In ordinary Plan trees the planner will have fixed opfuncids, + * but callers outside the executor will not have done this.) + */ +ExprState * +ExecPrepareExpr(Expr *node, EState *estate) +{ + ExprState *result; + MemoryContext oldcontext; + + fix_opfuncids((Node *) node); + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + result = ExecInitExpr(node, NULL); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject 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); } diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 328aea5f079..d3ccf0dd905 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.61 2002/12/05 15:50:32 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.62 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -284,7 +284,8 @@ postquel_end(execution_state *es) if (es->qd->operation != CMD_UTILITY) ExecutorEnd(es->qd); - pfree(es->qd); + FreeQueryDesc(es->qd); + es->qd = NULL; es->status = F_EXEC_DONE; diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index e6ba3887630..769e88a8397 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -45,7 +45,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.100 2002/12/13 19:45:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.101 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1392,8 +1392,6 @@ ExecEndAgg(AggState *node) tuplesort_end(peraggstate->sortstate); } - ExecFreeProjectionInfo(&node->ss.ps); - /* * Free both the expr contexts. */ @@ -1401,18 +1399,13 @@ ExecEndAgg(AggState *node) node->ss.ps.ps_ExprContext = node->tmpcontext; ExecFreeExprContext(&node->ss.ps); + /* clean up tuple table */ + ExecClearTuple(node->ss.ss_ScanTupleSlot); + MemoryContextDelete(node->aggcontext); outerPlan = outerPlanState(node); ExecEndNode(outerPlan); - - /* clean up tuple table */ - ExecClearTuple(node->ss.ss_ScanTupleSlot); - if (node->grp_firstTuple != NULL) - { - heap_freetuple(node->grp_firstTuple); - node->grp_firstTuple = NULL; - } } void diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index d0bf78631da..78f66523d51 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.15 2002/12/13 19:45:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.16 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -273,9 +273,8 @@ void ExecEndFunctionScan(FunctionScanState *node) { /* - * Free the projection info and the scan attribute info + * Free the exprcontext */ - ExecFreeProjectionInfo(&node->ss.ps); ExecFreeExprContext(&node->ss.ps); /* diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index 8bb72ba3438..58f6c1b34e9 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -15,7 +15,7 @@ * locate group boundaries. * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.52 2002/12/13 19:45:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.53 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -218,19 +218,13 @@ ExecEndGroup(GroupState *node) { PlanState *outerPlan; - ExecFreeProjectionInfo(&node->ss.ps); ExecFreeExprContext(&node->ss.ps); - outerPlan = outerPlanState(node); - ExecEndNode(outerPlan); - /* clean up tuple table */ ExecClearTuple(node->ss.ss_ScanTupleSlot); - if (node->grp_firstTuple != NULL) - { - heap_freetuple(node->grp_firstTuple); - node->grp_firstTuple = NULL; - } + + outerPlan = outerPlanState(node); + ExecEndNode(outerPlan); } void diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 45ba826317d..efdd3b3cabb 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.70 2002/12/13 19:45:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.71 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -179,10 +179,8 @@ ExecEndHash(HashState *node) PlanState *outerPlan; /* - * free projection info. no need to free result type info because - * that came from the outer plan... + * free exprcontext */ - ExecFreeProjectionInfo(&node->ps); ExecFreeExprContext(&node->ps); /* diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 07de8703812..8f899b577d7 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.44 2002/12/13 19:45:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.45 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -456,24 +456,22 @@ ExecEndHashJoin(HashJoinState *node) } /* - * Free the projection info and the scan attribute info + * Free the exprcontext */ - ExecFreeProjectionInfo(&node->js.ps); ExecFreeExprContext(&node->js.ps); /* - * clean up subtrees - */ - ExecEndNode(outerPlanState(node)); - ExecEndNode(innerPlanState(node)); - - /* * clean out the tuple table */ ExecClearTuple(node->js.ps.ps_ResultTupleSlot); ExecClearTuple(node->hj_OuterTupleSlot); ExecClearTuple(node->hj_HashTupleSlot); + /* + * clean up subtrees + */ + ExecEndNode(outerPlanState(node)); + ExecEndNode(innerPlanState(node)); } /* ---------------------------------------------------------------- diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 1e36e93113d..7b96723844b 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.74 2002/12/13 19:45:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.75 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -399,45 +399,39 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt) /* ---------------------------------------------------------------- * ExecEndIndexScan - * - * old comments - * Releases any storage allocated through C routines. - * Returns nothing. * ---------------------------------------------------------------- */ void ExecEndIndexScan(IndexScanState *node) { - ExprState ***runtimeKeyInfo; - ScanKey *scanKeys; - int *numScanKeys; int numIndices; - Relation relation; RelationPtr indexRelationDescs; IndexScanDescPtr indexScanDescs; + Relation relation; int i; - runtimeKeyInfo = node->iss_RuntimeKeyInfo; - /* * extract information from the node */ numIndices = node->iss_NumIndices; - scanKeys = node->iss_ScanKeys; - numScanKeys = node->iss_NumScanKeys; indexRelationDescs = node->iss_RelationDescs; indexScanDescs = node->iss_ScanDescs; relation = node->ss.ss_currentRelation; /* - * Free the projection info and the scan attribute info + * Free the exprcontext(s) */ - ExecFreeProjectionInfo(&node->ss.ps); ExecFreeExprContext(&node->ss.ps); if (node->iss_RuntimeContext) FreeExprContext(node->iss_RuntimeContext); /* + * clear out tuple table slots + */ + ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); + ExecClearTuple(node->ss.ss_ScanTupleSlot); + + /* * close the index relations */ for (i = 0; i < numIndices; i++) @@ -458,36 +452,6 @@ ExecEndIndexScan(IndexScanState *node) * locking, however.) */ heap_close(relation, NoLock); - - /* - * free the scan keys used in scanning the indices - */ - for (i = 0; i < numIndices; i++) - { - if (scanKeys[i] != NULL) - pfree(scanKeys[i]); - } - pfree(scanKeys); - pfree(numScanKeys); - - if (runtimeKeyInfo) - { - for (i = 0; i < numIndices; i++) - { - if (runtimeKeyInfo[i] != NULL) - pfree(runtimeKeyInfo[i]); - } - pfree(runtimeKeyInfo); - } - - /* - * clear out tuple table slots - */ - ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); - ExecClearTuple(node->ss.ss_ScanTupleSlot); - pfree(node->iss_RelationDescs); - pfree(node->iss_ScanDescs); - pfree(node); } /* ---------------------------------------------------------------- diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c index 6abd83de8aa..39d09331ce2 100644 --- a/src/backend/executor/nodeLimit.c +++ b/src/backend/executor/nodeLimit.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeLimit.c,v 1.13 2002/12/13 19:45:54 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeLimit.c,v 1.14 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -349,10 +349,10 @@ ExecEndLimit(LimitState *node) { ExecFreeExprContext(&node->ps); - ExecEndNode(outerPlanState(node)); - /* clean up tuple table */ ExecClearTuple(node->ps.ps_ResultTupleSlot); + + ExecEndNode(outerPlanState(node)); } diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c index cf7ca89f4a9..a1725901a7c 100644 --- a/src/backend/executor/nodeMaterial.c +++ b/src/backend/executor/nodeMaterial.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.39 2002/12/05 15:50:33 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.40 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -197,16 +197,16 @@ ExecEndMaterial(MaterialState *node) ExecClearTuple(node->ss.ss_ScanTupleSlot); /* - * shut down the subplan - */ - ExecEndNode(outerPlanState(node)); - - /* * Release tuplestore resources */ if (node->tuplestorestate != NULL) tuplestore_end((Tuplestorestate *) node->tuplestorestate); node->tuplestorestate = NULL; + + /* + * shut down the subplan + */ + ExecEndNode(outerPlanState(node)); } /* ---------------------------------------------------------------- diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 1bb5878d819..af6cd8d6f3f 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.54 2002/12/13 19:45:54 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.55 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1551,23 +1551,22 @@ ExecEndMergeJoin(MergeJoinState *node) "ending node processing"); /* - * Free the projection info and the scan attribute info + * Free the exprcontext */ - ExecFreeProjectionInfo(&node->js.ps); ExecFreeExprContext(&node->js.ps); /* - * shut down the subplans - */ - ExecEndNode(innerPlanState(node)); - ExecEndNode(outerPlanState(node)); - - /* * clean out the tuple table */ ExecClearTuple(node->js.ps.ps_ResultTupleSlot); ExecClearTuple(node->mj_MarkedTupleSlot); + /* + * shut down the subplans + */ + ExecEndNode(innerPlanState(node)); + ExecEndNode(outerPlanState(node)); + MJ1_printf("ExecEndMergeJoin: %s\n", "node processing ended"); } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 452ed7d70c3..917a7011cbf 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.28 2002/12/13 19:45:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.29 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -364,21 +364,20 @@ ExecEndNestLoop(NestLoopState *node) "ending node processing"); /* - * Free the projection info + * Free the exprcontext */ - ExecFreeProjectionInfo(&node->js.ps); ExecFreeExprContext(&node->js.ps); /* - * close down subplans + * clean out the tuple table */ - ExecEndNode(outerPlanState(node)); - ExecEndNode(innerPlanState(node)); + ExecClearTuple(node->js.ps.ps_ResultTupleSlot); /* - * clean out the tuple table + * close down subplans */ - ExecClearTuple(node->js.ps.ps_ResultTupleSlot); + ExecEndNode(outerPlanState(node)); + ExecEndNode(innerPlanState(node)); NL1_printf("ExecEndNestLoop: %s\n", "node processing ended"); diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 3f2c9927e01..9ea75eb3ce7 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -34,7 +34,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.23 2002/12/13 19:45:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.24 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -248,9 +248,8 @@ void ExecEndResult(ResultState *node) { /* - * Free the projection info + * Free the exprcontext */ - ExecFreeProjectionInfo(&node->ps); ExecFreeExprContext(&node->ps); /* diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 6a7393795b2..6628a9eecbe 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.40 2002/12/13 19:45:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.41 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -264,23 +264,22 @@ ExecEndSeqScan(SeqScanState *node) scanDesc = node->ss_currentScanDesc; /* - * Free the projection info and the scan attribute info + * Free the exprcontext */ - ExecFreeProjectionInfo(&node->ps); ExecFreeExprContext(&node->ps); /* - * close heap scan - */ - heap_endscan(scanDesc); - - /* * clean out the tuple table */ ExecClearTuple(node->ps.ps_ResultTupleSlot); ExecClearTuple(node->ss_ScanTupleSlot); /* + * close heap scan + */ + heap_endscan(scanDesc); + + /* * close the heap relation. * * Currently, we do not release the AccessShareLock acquired by diff --git a/src/backend/executor/nodeSetOp.c b/src/backend/executor/nodeSetOp.c index a81a4a29d91..965a2a6466a 100644 --- a/src/backend/executor/nodeSetOp.c +++ b/src/backend/executor/nodeSetOp.c @@ -21,7 +21,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSetOp.c,v 1.7 2002/12/05 15:50:33 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSetOp.c,v 1.8 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -301,9 +301,9 @@ ExecEndSetOp(SetOpState *node) ExecClearTuple(node->ps.ps_ResultTupleSlot); node->ps.ps_OuterTupleSlot = NULL; - ExecEndNode(outerPlanState(node)); - MemoryContextDelete(node->tempContext); + + ExecEndNode(outerPlanState(node)); } diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c index e1b4db7a51d..a37583241fb 100644 --- a/src/backend/executor/nodeSort.c +++ b/src/backend/executor/nodeSort.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.41 2002/12/05 15:50:33 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.42 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -285,17 +285,17 @@ ExecEndSort(SortState *node) ExecClearTuple(node->ss.ss_ScanTupleSlot); /* - * shut down the subplan - */ - ExecEndNode(outerPlanState(node)); - - /* * Release tuplesort resources */ if (node->tuplesortstate != NULL) tuplesort_end((Tuplesortstate *) node->tuplesortstate); node->tuplesortstate = NULL; + /* + * shut down the subplan + */ + ExecEndNode(outerPlanState(node)); + SO1_printf("ExecEndSort: %s\n", "sort node shutdown"); } diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index 3a2ca974aee..2f5ab52e213 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.38 2002/12/14 00:17:50 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.39 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -47,9 +47,10 @@ ExecSubPlan(SubPlanState *node, /* * We are probably in a short-lived expression-evaluation context. - * Switch to longer-lived per-query context. + * Switch to the child plan's per-query context for manipulating its + * chgParam, calling ExecProcNode on it, etc. */ - oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); + oldcontext = MemoryContextSwitchTo(node->sub_estate->es_query_cxt); if (subplan->setParam != NIL) elog(ERROR, "ExecSubPlan: can't set parent params from subquery"); @@ -132,10 +133,13 @@ ExecSubPlan(SubPlanState *node, * ExecProcNode() call. node->curTuple keeps track of the * copied tuple for eventual freeing. */ + MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tup = heap_copytuple(tup); if (node->curTuple) heap_freetuple(node->curTuple); node->curTuple = tup; + MemoryContextSwitchTo(node->sub_estate->es_query_cxt); + result = heap_getattr(tup, col, tdesc, isNull); /* keep scanning subplan to make sure there's only one tuple */ continue; @@ -295,6 +299,7 @@ ExecInitSubPlan(SubPlanState *node, EState *estate) { SubPlan *subplan = (SubPlan *) node->xprstate.expr; EState *sp_estate; + MemoryContext oldcontext; /* * Do access checking on the rangetable entries in the subquery. @@ -303,15 +308,23 @@ ExecInitSubPlan(SubPlanState *node, EState *estate) ExecCheckRTPerms(subplan->rtable, CMD_SELECT); /* - * initialize state + * initialize my state */ node->needShutdown = false; node->curTuple = NULL; /* * create an EState for the subplan + * + * The subquery needs its own EState because it has its own rangetable. + * It shares our Param ID space, however. XXX if rangetable access were + * done differently, the subquery could share our EState, which would + * eliminate some thrashing about in this module... */ sp_estate = CreateExecutorState(); + node->sub_estate = sp_estate; + + oldcontext = MemoryContextSwitchTo(sp_estate->es_query_cxt); sp_estate->es_range_table = subplan->rtable; sp_estate->es_param_list_info = estate->es_param_list_info; @@ -322,12 +335,14 @@ ExecInitSubPlan(SubPlanState *node, EState *estate) sp_estate->es_instrument = estate->es_instrument; /* - * Start up the subplan + * Start up the subplan (this is a very cut-down form of InitPlan()) */ node->planstate = ExecInitNode(subplan->plan, sp_estate); node->needShutdown = true; /* now we need to shutdown the subplan */ + MemoryContextSwitchTo(oldcontext); + /* * If this plan is un-correlated or undirect correlated one and want * to set params for parent plan then prepare parameters. @@ -376,10 +391,9 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) bool found = false; /* - * We are probably in a short-lived expression-evaluation context. - * Switch to longer-lived per-query context. + * Must switch to child query's per-query memory context. */ - oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); + oldcontext = MemoryContextSwitchTo(node->sub_estate->es_query_cxt); if (subLinkType == ANY_SUBLINK || subLinkType == ALL_SUBLINK) @@ -415,15 +429,18 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) found = true; /* - * We need to copy the subplan's tuple in case any of the params - * are pass-by-ref type --- the pointers stored in the param - * structs will point at this copied tuple! node->curTuple keeps - * track of the copied tuple for eventual freeing. + * We need to copy the subplan's tuple into our own context, + * in case any of the params are pass-by-ref type --- the pointers + * stored in the param structs will point at this copied tuple! + * node->curTuple keeps track of the copied tuple for eventual + * freeing. */ + MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tup = heap_copytuple(tup); if (node->curTuple) heap_freetuple(node->curTuple); node->curTuple = tup; + MemoryContextSwitchTo(node->sub_estate->es_query_cxt); foreach(lst, subplan->setParam) { @@ -460,7 +477,10 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) if (planstate->plan->extParam == NULL) /* un-correlated ... */ { - ExecEndNode(planstate); + ExecEndPlan(planstate, node->sub_estate); + /* mustn't free context while still in it... */ + MemoryContextSwitchTo(oldcontext); + FreeExecutorState(node->sub_estate); node->needShutdown = false; } @@ -476,7 +496,12 @@ ExecEndSubPlan(SubPlanState *node) { if (node->needShutdown) { - ExecEndNode(node->planstate); + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(node->sub_estate->es_query_cxt); + ExecEndPlan(node->planstate, node->sub_estate); + MemoryContextSwitchTo(oldcontext); + FreeExecutorState(node->sub_estate); node->needShutdown = false; } if (node->curTuple) diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 68291ba6e34..4466ef34219 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -12,7 +12,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.15 2002/12/13 19:45:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.16 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,7 @@ SubqueryNext(SubqueryScanState *node) EState *estate; ScanDirection direction; TupleTableSlot *slot; + MemoryContext oldcontext; /* * get information from the estate and scan state @@ -66,12 +67,17 @@ SubqueryNext(SubqueryScanState *node) */ /* - * get the next tuple from the sub-query + * Get the next tuple from the sub-query. We have to be careful to + * run it in its appropriate memory context. */ node->sss_SubEState->es_direction = direction; + oldcontext = MemoryContextSwitchTo(node->sss_SubEState->es_query_cxt); + slot = ExecProcNode(node->subplan); + MemoryContextSwitchTo(oldcontext); + node->ss.ss_ScanTupleSlot = slot; return slot; @@ -106,6 +112,7 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate) SubqueryScanState *subquerystate; RangeTblEntry *rte; EState *sp_estate; + MemoryContext oldcontext; /* * SubqueryScan should not have any "normal" children. @@ -152,9 +159,17 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate) rte = rt_fetch(node->scan.scanrelid, estate->es_range_table); Assert(rte->rtekind == RTE_SUBQUERY); + /* + * The subquery needs its own EState because it has its own rangetable. + * It shares our Param ID space, however. XXX if rangetable access were + * done differently, the subquery could share our EState, which would + * eliminate some thrashing about in this module... + */ sp_estate = CreateExecutorState(); subquerystate->sss_SubEState = sp_estate; + oldcontext = MemoryContextSwitchTo(sp_estate->es_query_cxt); + sp_estate->es_range_table = rte->subquery->rtable; sp_estate->es_param_list_info = estate->es_param_list_info; sp_estate->es_param_exec_vals = estate->es_param_exec_vals; @@ -163,8 +178,13 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate) sp_estate->es_snapshot = estate->es_snapshot; sp_estate->es_instrument = estate->es_instrument; + /* + * Start up the subplan (this is a very cut-down form of InitPlan()) + */ subquerystate->subplan = ExecInitNode(node->subplan, sp_estate); + MemoryContextSwitchTo(oldcontext); + subquerystate->ss.ss_ScanTupleSlot = NULL; subquerystate->ss.ps.ps_TupFromTlist = false; @@ -197,10 +217,11 @@ ExecCountSlotsSubqueryScan(SubqueryScan *node) void ExecEndSubqueryScan(SubqueryScanState *node) { + MemoryContext oldcontext; + /* - * Free the projection info and the scan attribute info + * Free the exprcontext */ - ExecFreeProjectionInfo(&node->ss.ps); ExecFreeExprContext(&node->ss.ps); /* @@ -211,15 +232,13 @@ ExecEndSubqueryScan(SubqueryScanState *node) /* * close down subquery */ - ExecEndNode(node->subplan); + oldcontext = MemoryContextSwitchTo(node->sss_SubEState->es_query_cxt); - /* - * clean up subquery's tuple table - */ - node->ss.ss_ScanTupleSlot = NULL; - ExecDropTupleTable(node->sss_SubEState->es_tupleTable, true); + ExecEndPlan(node->subplan, node->sss_SubEState); - /* XXX we seem to be leaking the sub-EState... */ + MemoryContextSwitchTo(oldcontext); + + FreeExecutorState(node->sss_SubEState); } /* ---------------------------------------------------------------- @@ -232,12 +251,17 @@ void ExecSubqueryReScan(SubqueryScanState *node, ExprContext *exprCtxt) { EState *estate; + MemoryContext oldcontext; estate = node->ss.ps.state; + oldcontext = MemoryContextSwitchTo(node->sss_SubEState->es_query_cxt); + /* * ExecReScan doesn't know about my subplan, so I have to do - * changed-parameter signaling myself. + * changed-parameter signaling myself. This is just as well, + * because the subplan has its own memory context in which its + * chgParam lists live. */ if (node->ss.ps.chgParam != NULL) SetChangedParamList(node->subplan, node->ss.ps.chgParam); @@ -249,5 +273,7 @@ ExecSubqueryReScan(SubqueryScanState *node, ExprContext *exprCtxt) if (node->subplan->chgParam == NULL) ExecReScan(node->subplan, NULL); + MemoryContextSwitchTo(oldcontext); + node->ss.ss_ScanTupleSlot = NULL; } diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index ba2793407ce..7e35bc07cd7 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.29 2002/12/13 19:45:56 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.30 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -278,19 +278,8 @@ void ExecEndTidScan(TidScanState *node) { /* - * extract information from the node + * Free the exprcontext */ - if (node && node->tss_TidList) - pfree(node->tss_TidList); - - /* - * Free the projection info and the scan attribute info - * - * Note: we don't ExecFreeResultType(scanstate) because the rule manager - * depends on the tupType returned by ExecMain(). So for now, this is - * freed at end-transaction time. -cim 6/2/91 - */ - ExecFreeProjectionInfo(&node->ss.ps); ExecFreeExprContext(&node->ss.ps); /* diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c index 88b08061f99..415594f92c4 100644 --- a/src/backend/executor/nodeUnique.c +++ b/src/backend/executor/nodeUnique.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.35 2002/12/05 15:50:34 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.36 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -194,15 +194,10 @@ ExecEndUnique(UniqueState *node) { /* clean up tuple table */ ExecClearTuple(node->ps.ps_ResultTupleSlot); - if (node->priorTuple != NULL) - { - heap_freetuple(node->priorTuple); - node->priorTuple = NULL; - } - - ExecEndNode(outerPlanState(node)); MemoryContextDelete(node->tempContext); + + ExecEndNode(outerPlanState(node)); } diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 147becf6ed8..cde9ab6ff6e 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.79 2002/12/05 15:50:34 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.80 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1287,23 +1287,23 @@ _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount) elog(FATAL, "SPI_select: # of processed tuples check failed"); } - ExecutorEnd(queryDesc); - -#ifdef SPI_EXECUTOR_STATS - if (ShowExecutorStats) - ShowUsage("SPI EXECUTOR STATS"); -#endif - if (dest == SPI) { SPI_processed = _SPI_current->processed; SPI_lastoid = save_lastoid; SPI_tuptable = _SPI_current->tuptable; } - queryDesc->dest = dest; - return res; + ExecutorEnd(queryDesc); + + FreeQueryDesc(queryDesc); + +#ifdef SPI_EXECUTOR_STATS + if (ShowExecutorStats) + ShowUsage("SPI EXECUTOR STATS"); +#endif + return res; } /* |