Update EvalPlanQual() to work with new executor memory management method.
authorTom Lane
Wed, 18 Dec 2002 00:14:47 +0000 (00:14 +0000)
committerTom Lane
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;