From 350260795acbc5143a1449a49acc9d5d9d02e802 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
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 f17fbcbb466..f184265c491 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 6c2cece7b6e..054ec703866 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 b57d7ac58cc..a593957022c 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;
 
 
-- 
GitLab