From 350260795acbc5143a1449a49acc9d5d9d02e802 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 18 Dec 2002 00:14:47 +0000 Subject: [PATCH] Update EvalPlanQual() to work with new executor memory management method. It doesn't leak memory anymore ... --- src/backend/executor/execMain.c | 309 ++++++++++++++++++------------- src/backend/executor/execUtils.c | 4 +- src/include/nodes/execnodes.h | 12 +- 3 files changed, 186 insertions(+), 139 deletions(-) diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index f17fbcbb46..f184265c49 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.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; +} diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 6c2cece7b6..054ec70386 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -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; diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b57d7ac58c..a593957022 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -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;