]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Update EvalPlanQual() to work with new executor memory management method.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 18 Dec 2002 00:14:47 +0000 (00:14 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 18 Dec 2002 00:14:47 +0000 (00:14 +0000)
It doesn't leak memory anymore ...

src/backend/executor/execMain.c
src/backend/executor/execUtils.c
src/include/nodes/execnodes.h

index f17fbcbb4661b7e89ec2cf8a29799f867734d93e..f184265c49185943be8e6ddd9d289dbccd382f9e 100644 (file)
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.194 2002/12/15 21:01:34 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.195 2002/12/18 00:14:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #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;
 }
index 6c2cece7b6eb629f8e9aed43590a5a6bd980addf..054ec703866e7454edb26687a8482131ce4b9358 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.93 2002/12/15 16:17:46 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.94 2002/12/18 00:14:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -204,7 +204,7 @@ CreateExecutorState(void)
 
        estate->es_per_tuple_exprcontext = NULL;
 
-       estate->es_origPlan = NULL;
+       estate->es_topPlan = NULL;
        estate->es_evalPlanQual = NULL;
        estate->es_evTupleNull = NULL;
        estate->es_evTuple = NULL;
index b57d7ac58ccd6b46a3191e311e9134dada0fec1a..a593957022cca4b35b80e52cdd9d8cfa6c138375 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.87 2002/12/15 21:01:34 tgl Exp $
+ * $Id: execnodes.h,v 1.88 2002/12/18 00:14:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -316,11 +316,11 @@ typedef struct EState
        ExprContext *es_per_tuple_exprcontext;
 
        /* Below is to re-evaluate plan qual in READ COMMITTED mode */
-       struct Plan *es_origPlan;
-       Pointer         es_evalPlanQual;
-       bool       *es_evTupleNull;
-       HeapTuple  *es_evTuple;
-       bool            es_useEvalPlan;
+       Plan       *es_topPlan;         /* link to top of plan tree */
+       struct evalPlanQual *es_evalPlanQual; /* chain of PlanQual states */
+       bool       *es_evTupleNull;     /* local array of EPQ status */
+       HeapTuple  *es_evTuple;         /* shared array of EPQ substitute tuples */
+       bool            es_useEvalPlan; /* evaluating EPQ tuples? */
 } EState;