From f7902c69704598e9dbbc1ee72d42bc9ca6c98a21 Mon Sep 17 00:00:00 2001
From: Amit Langote <amitlan@postgresql.org>
Date: Thu, 28 Sep 2023 16:56:29 +0900
Subject: [PATCH v49 1/8] Assorted tightening in various ExecEnd()* routines

This includes adding NULLness checks on pointers before cleaning them
in up.  Many ExecEnd*() routines already perform this check, but a few
instances remain.  These NULLness checks might seem redundant as
things stand since the ExecEnd*() routines operate under the
assumption that their matching ExecInit* routine would have fully
executed, ensuring pointers are set. However, a forthcoming patch will
modify ExecInit* routines to sometimes exit early, potentially leaving
some pointers in an undetermined state, so it will become crucial to
have these NULLness checks in place.

This also adds a guard at the begigging of EvalPlanQualEnd() to return
early if the EPQState does not appear to have been initialized.  That
case can happen if the corresponding ExecInit*() routine returned
early without calling EvalPlanQualInit().

While at it, this commit ensures that pointers are consistently set
to NULL after cleanup in all ExecEnd*() routines.

Finally, for enhanced consistency, the format of NULLness checks has
been standardized to "if (pointer != NULL)", replacing the previous
"if (pointer)" style.

Reviewed-by: Robert Haas
Discussion: https://postgr.es/m/CA+HiwqFGkMSge6TgC9KQzde0ohpAycLQuV7ooitEEpbKB0O_mg@mail.gmail.com
---
 src/backend/executor/execMain.c            |  4 ++
 src/backend/executor/nodeAgg.c             | 27 +++++++++----
 src/backend/executor/nodeAppend.c          |  3 ++
 src/backend/executor/nodeBitmapAnd.c       |  4 +-
 src/backend/executor/nodeBitmapHeapscan.c  | 47 +++++++++++++++-------
 src/backend/executor/nodeBitmapIndexscan.c | 23 +++++------
 src/backend/executor/nodeBitmapOr.c        |  4 +-
 src/backend/executor/nodeForeignscan.c     | 17 ++++----
 src/backend/executor/nodeGather.c          |  1 +
 src/backend/executor/nodeGatherMerge.c     |  1 +
 src/backend/executor/nodeGroup.c           |  6 +--
 src/backend/executor/nodeHash.c            |  6 +--
 src/backend/executor/nodeHashjoin.c        |  4 +-
 src/backend/executor/nodeIncrementalSort.c | 13 +++++-
 src/backend/executor/nodeIndexonlyscan.c   | 25 ++++++------
 src/backend/executor/nodeIndexscan.c       | 23 +++++------
 src/backend/executor/nodeLimit.c           |  1 +
 src/backend/executor/nodeLockRows.c        |  1 +
 src/backend/executor/nodeMaterial.c        |  5 ++-
 src/backend/executor/nodeMemoize.c         |  8 +++-
 src/backend/executor/nodeMergeAppend.c     |  3 ++
 src/backend/executor/nodeMergejoin.c       |  2 +
 src/backend/executor/nodeModifyTable.c     | 11 ++++-
 src/backend/executor/nodeNestloop.c        |  2 +
 src/backend/executor/nodeProjectSet.c      |  1 +
 src/backend/executor/nodeRecursiveunion.c  | 24 +++++++++--
 src/backend/executor/nodeResult.c          |  1 +
 src/backend/executor/nodeSamplescan.c      |  7 +++-
 src/backend/executor/nodeSeqscan.c         | 16 +++-----
 src/backend/executor/nodeSetOp.c           |  6 ++-
 src/backend/executor/nodeSort.c            |  5 ++-
 src/backend/executor/nodeSubqueryscan.c    |  1 +
 src/backend/executor/nodeTableFuncscan.c   |  4 +-
 src/backend/executor/nodeTidrangescan.c    | 12 ++++--
 src/backend/executor/nodeTidscan.c         |  8 +++-
 src/backend/executor/nodeUnique.c          |  1 +
 src/backend/executor/nodeWindowAgg.c       | 41 ++++++++++++++-----
 37 files changed, 248 insertions(+), 120 deletions(-)

diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 4c5a7bbf62..f7f18d3054 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -3010,6 +3010,10 @@ EvalPlanQualEnd(EPQState *epqstate)
 	MemoryContext oldcontext;
 	ListCell   *l;
 
+	/* Nothing to do if no EvalPlanQualInit() was done to begin with. */
+	if (epqstate->parentestate == NULL)
+		return;
+
 	rtsize = epqstate->parentestate->es_range_table_size;
 
 	/*
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index f154f28902..af22b1676f 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -4304,7 +4304,6 @@ GetAggInitVal(Datum textInitVal, Oid transtype)
 void
 ExecEndAgg(AggState *node)
 {
-	PlanState  *outerPlan;
 	int			transno;
 	int			numGroupingSets = Max(node->maxsets, 1);
 	int			setno;
@@ -4314,7 +4313,7 @@ ExecEndAgg(AggState *node)
 	 * worker back into shared memory so that it can be picked up by the main
 	 * process to report in EXPLAIN ANALYZE.
 	 */
-	if (node->shared_info && IsParallelWorker())
+	if (node->shared_info != NULL && IsParallelWorker())
 	{
 		AggregateInstrumentation *si;
 
@@ -4327,10 +4326,16 @@ ExecEndAgg(AggState *node)
 
 	/* Make sure we have closed any open tuplesorts */
 
-	if (node->sort_in)
+	if (node->sort_in != NULL)
+	{
 		tuplesort_end(node->sort_in);
-	if (node->sort_out)
+		node->sort_in = NULL;
+	}
+	if (node->sort_out != NULL)
+	{
 		tuplesort_end(node->sort_out);
+		node->sort_out = NULL;
+	}
 
 	hashagg_reset_spill_state(node);
 
@@ -4346,19 +4351,25 @@ ExecEndAgg(AggState *node)
 
 		for (setno = 0; setno < numGroupingSets; setno++)
 		{
-			if (pertrans->sortstates[setno])
+			if (pertrans->sortstates[setno] != NULL)
 				tuplesort_end(pertrans->sortstates[setno]);
 		}
 	}
 
 	/* And ensure any agg shutdown callbacks have been called */
 	for (setno = 0; setno < numGroupingSets; setno++)
+	{
 		ReScanExprContext(node->aggcontexts[setno]);
-	if (node->hashcontext)
+		node->aggcontexts[setno] = NULL;
+	}
+	if (node->hashcontext != NULL)
+	{
 		ReScanExprContext(node->hashcontext);
+		node->hashcontext = NULL;
+	}
 
-	outerPlan = outerPlanState(node);
-	ExecEndNode(outerPlan);
+	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 }
 
 void
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index 609df6b9e6..a2af221e05 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -399,7 +399,10 @@ ExecEndAppend(AppendState *node)
 	 * shut down each of the subscans
 	 */
 	for (i = 0; i < nplans; i++)
+	{
 		ExecEndNode(appendplans[i]);
+		appendplans[i] = NULL;
+	}
 }
 
 void
diff --git a/src/backend/executor/nodeBitmapAnd.c b/src/backend/executor/nodeBitmapAnd.c
index 4c5eb2b23b..4abb0609a0 100644
--- a/src/backend/executor/nodeBitmapAnd.c
+++ b/src/backend/executor/nodeBitmapAnd.c
@@ -192,8 +192,8 @@ ExecEndBitmapAnd(BitmapAndState *node)
 	 */
 	for (i = 0; i < nplans; i++)
 	{
-		if (bitmapplans[i])
-			ExecEndNode(bitmapplans[i]);
+		ExecEndNode(bitmapplans[i]);
+		bitmapplans[i] = NULL;
 	}
 }
 
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 2db0acfc76..d3f58c22f9 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -648,40 +648,59 @@ ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
 void
 ExecEndBitmapHeapScan(BitmapHeapScanState *node)
 {
-	TableScanDesc scanDesc;
-
-	/*
-	 * extract information from the node
-	 */
-	scanDesc = node->ss.ss_currentScanDesc;
-
 	/*
 	 * close down subplans
 	 */
 	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 
 	/*
 	 * release bitmaps and buffers if any
 	 */
-	if (node->tbmiterator)
+	if (node->tbmiterator != NULL)
+	{
 		tbm_end_iterate(node->tbmiterator);
-	if (node->prefetch_iterator)
+		node->tbmiterator = NULL;
+	}
+	if (node->prefetch_iterator != NULL)
+	{
 		tbm_end_iterate(node->prefetch_iterator);
-	if (node->tbm)
+		node->prefetch_iterator = NULL;
+	}
+	if (node->tbm != NULL)
+	{
 		tbm_free(node->tbm);
-	if (node->shared_tbmiterator)
+		node->tbm = NULL;
+	}
+	if (node->shared_tbmiterator != NULL)
+	{
 		tbm_end_shared_iterate(node->shared_tbmiterator);
-	if (node->shared_prefetch_iterator)
+		node->shared_tbmiterator = NULL;
+	}
+	if (node->shared_prefetch_iterator != NULL)
+	{
 		tbm_end_shared_iterate(node->shared_prefetch_iterator);
+		node->shared_prefetch_iterator = NULL;
+	}
 	if (node->vmbuffer != InvalidBuffer)
+	{
 		ReleaseBuffer(node->vmbuffer);
+		node->vmbuffer = InvalidBuffer;
+	}
 	if (node->pvmbuffer != InvalidBuffer)
+	{
 		ReleaseBuffer(node->pvmbuffer);
+		node->pvmbuffer = InvalidBuffer;
+	}
 
 	/*
-	 * close heap scan
+	 * close heap scan (no-op if we didn't start it)
 	 */
-	table_endscan(scanDesc);
+	if (node->ss.ss_currentScanDesc != NULL)
+	{
+		table_endscan(node->ss.ss_currentScanDesc);
+		node->ss.ss_currentScanDesc = NULL;
+	}
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeBitmapIndexscan.c b/src/backend/executor/nodeBitmapIndexscan.c
index 7cf8532bc9..488f11a3ff 100644
--- a/src/backend/executor/nodeBitmapIndexscan.c
+++ b/src/backend/executor/nodeBitmapIndexscan.c
@@ -175,22 +175,21 @@ ExecReScanBitmapIndexScan(BitmapIndexScanState *node)
 void
 ExecEndBitmapIndexScan(BitmapIndexScanState *node)
 {
-	Relation	indexRelationDesc;
-	IndexScanDesc indexScanDesc;
-
-	/*
-	 * extract information from the node
-	 */
-	indexRelationDesc = node->biss_RelationDesc;
-	indexScanDesc = node->biss_ScanDesc;
+	/* close the scan (no-op if we didn't start it) */
+	if (node->biss_ScanDesc != NULL)
+	{
+		index_endscan(node->biss_ScanDesc);
+		node->biss_ScanDesc = NULL;
+	}
 
 	/*
 	 * close the index relation (no-op if we didn't open it)
 	 */
-	if (indexScanDesc)
-		index_endscan(indexScanDesc);
-	if (indexRelationDesc)
-		index_close(indexRelationDesc, NoLock);
+	if (node->biss_RelationDesc != NULL)
+	{
+		index_close(node->biss_RelationDesc, NoLock);
+		node->biss_RelationDesc = NULL;
+	}
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeBitmapOr.c b/src/backend/executor/nodeBitmapOr.c
index 0bf8af9652..ace18593aa 100644
--- a/src/backend/executor/nodeBitmapOr.c
+++ b/src/backend/executor/nodeBitmapOr.c
@@ -210,8 +210,8 @@ ExecEndBitmapOr(BitmapOrState *node)
 	 */
 	for (i = 0; i < nplans; i++)
 	{
-		if (bitmapplans[i])
-			ExecEndNode(bitmapplans[i]);
+		ExecEndNode(bitmapplans[i]);
+		bitmapplans[i] = NULL;
 	}
 }
 
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 73913ebb18..3aba28285a 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -301,17 +301,20 @@ ExecEndForeignScan(ForeignScanState *node)
 	EState	   *estate = node->ss.ps.state;
 
 	/* Let the FDW shut down */
-	if (plan->operation != CMD_SELECT)
+	if (node->fdwroutine != NULL)
 	{
-		if (estate->es_epq_active == NULL)
-			node->fdwroutine->EndDirectModify(node);
+		if (plan->operation != CMD_SELECT)
+		{
+			if (estate->es_epq_active == NULL)
+				node->fdwroutine->EndDirectModify(node);
+		}
+		else
+			node->fdwroutine->EndForeignScan(node);
 	}
-	else
-		node->fdwroutine->EndForeignScan(node);
 
 	/* Shut down any outer plan. */
-	if (outerPlanState(node))
-		ExecEndNode(outerPlanState(node));
+	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index bb2500a469..1a3c8abdad 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -249,6 +249,7 @@ void
 ExecEndGather(GatherState *node)
 {
 	ExecEndNode(outerPlanState(node));	/* let children clean up first */
+	outerPlanState(node) = NULL;
 	ExecShutdownGather(node);
 }
 
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index 7a71a58509..c6fb45fee0 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -289,6 +289,7 @@ void
 ExecEndGatherMerge(GatherMergeState *node)
 {
 	ExecEndNode(outerPlanState(node));	/* let children clean up first */
+	outerPlanState(node) = NULL;
 	ExecShutdownGatherMerge(node);
 }
 
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index 8c650f0e46..6dfe5a1d23 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -226,10 +226,8 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 void
 ExecEndGroup(GroupState *node)
 {
-	PlanState  *outerPlan;
-
-	outerPlan = outerPlanState(node);
-	ExecEndNode(outerPlan);
+	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 }
 
 void
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index e72f0986c2..88ba336882 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -413,13 +413,11 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 void
 ExecEndHash(HashState *node)
 {
-	PlanState  *outerPlan;
-
 	/*
 	 * shut down the subplan
 	 */
-	outerPlan = outerPlanState(node);
-	ExecEndNode(outerPlan);
+	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 }
 
 
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 25a2d78f15..03dd931527 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -861,7 +861,7 @@ ExecEndHashJoin(HashJoinState *node)
 	/*
 	 * Free hash table
 	 */
-	if (node->hj_HashTable)
+	if (node->hj_HashTable != NULL)
 	{
 		ExecHashTableDestroy(node->hj_HashTable);
 		node->hj_HashTable = NULL;
@@ -871,7 +871,9 @@ ExecEndHashJoin(HashJoinState *node)
 	 * clean up subtrees
 	 */
 	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 	ExecEndNode(innerPlanState(node));
+	innerPlanState(node) = NULL;
 }
 
 /*
diff --git a/src/backend/executor/nodeIncrementalSort.c b/src/backend/executor/nodeIncrementalSort.c
index cd094a190c..28a0e81cb3 100644
--- a/src/backend/executor/nodeIncrementalSort.c
+++ b/src/backend/executor/nodeIncrementalSort.c
@@ -1079,8 +1079,16 @@ ExecEndIncrementalSort(IncrementalSortState *node)
 {
 	SO_printf("ExecEndIncrementalSort: shutting down sort node\n");
 
-	ExecDropSingleTupleTableSlot(node->group_pivot);
-	ExecDropSingleTupleTableSlot(node->transfer_tuple);
+	if (node->group_pivot != NULL)
+	{
+		ExecDropSingleTupleTableSlot(node->group_pivot);
+		node->group_pivot = NULL;
+	}
+	if (node->transfer_tuple != NULL)
+	{
+		ExecDropSingleTupleTableSlot(node->transfer_tuple);
+		node->transfer_tuple = NULL;
+	}
 
 	/*
 	 * Release tuplesort resources.
@@ -1100,6 +1108,7 @@ ExecEndIncrementalSort(IncrementalSortState *node)
 	 * Shut down the subplan.
 	 */
 	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 
 	SO_printf("ExecEndIncrementalSort: sort node shutdown\n");
 }
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index f1db35665c..1f3843abe9 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -364,15 +364,6 @@ ExecReScanIndexOnlyScan(IndexOnlyScanState *node)
 void
 ExecEndIndexOnlyScan(IndexOnlyScanState *node)
 {
-	Relation	indexRelationDesc;
-	IndexScanDesc indexScanDesc;
-
-	/*
-	 * extract information from the node
-	 */
-	indexRelationDesc = node->ioss_RelationDesc;
-	indexScanDesc = node->ioss_ScanDesc;
-
 	/* Release VM buffer pin, if any. */
 	if (node->ioss_VMBuffer != InvalidBuffer)
 	{
@@ -380,13 +371,21 @@ ExecEndIndexOnlyScan(IndexOnlyScanState *node)
 		node->ioss_VMBuffer = InvalidBuffer;
 	}
 
+	/* close the scan (no-op if we didn't start it) */
+	if (node->ioss_ScanDesc != NULL)
+	{
+		index_endscan(node->ioss_ScanDesc);
+		node->ioss_ScanDesc = NULL;
+	}
+
 	/*
 	 * close the index relation (no-op if we didn't open it)
 	 */
-	if (indexScanDesc)
-		index_endscan(indexScanDesc);
-	if (indexRelationDesc)
-		index_close(indexRelationDesc, NoLock);
+	if (node->ioss_RelationDesc != NULL)
+	{
+		index_close(node->ioss_RelationDesc, NoLock);
+		node->ioss_RelationDesc = NULL;
+	}
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 14b9c00217..32e1714f15 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -785,22 +785,21 @@ ExecIndexAdvanceArrayKeys(IndexArrayKeyInfo *arrayKeys, int numArrayKeys)
 void
 ExecEndIndexScan(IndexScanState *node)
 {
-	Relation	indexRelationDesc;
-	IndexScanDesc indexScanDesc;
-
-	/*
-	 * extract information from the node
-	 */
-	indexRelationDesc = node->iss_RelationDesc;
-	indexScanDesc = node->iss_ScanDesc;
+	/* close the scan (no-op if we didn't start it) */
+	if (node->iss_ScanDesc != NULL)
+	{
+		index_endscan(node->iss_ScanDesc);
+		node->iss_ScanDesc = NULL;
+	}
 
 	/*
 	 * close the index relation (no-op if we didn't open it)
 	 */
-	if (indexScanDesc)
-		index_endscan(indexScanDesc);
-	if (indexRelationDesc)
-		index_close(indexRelationDesc, NoLock);
+	if (node->iss_RelationDesc != NULL)
+	{
+		index_close(node->iss_RelationDesc, NoLock);
+		node->iss_RelationDesc = NULL;
+	}
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c
index 5654158e3e..a97bac9f6d 100644
--- a/src/backend/executor/nodeLimit.c
+++ b/src/backend/executor/nodeLimit.c
@@ -535,6 +535,7 @@ void
 ExecEndLimit(LimitState *node)
 {
 	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 }
 
 
diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c
index e459971d32..26fbe95c57 100644
--- a/src/backend/executor/nodeLockRows.c
+++ b/src/backend/executor/nodeLockRows.c
@@ -387,6 +387,7 @@ ExecEndLockRows(LockRowsState *node)
 	/* We may have shut down EPQ already, but no harm in another call */
 	EvalPlanQualEnd(&node->lr_epqstate);
 	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 }
 
 
diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c
index 753ea28915..03c514900b 100644
--- a/src/backend/executor/nodeMaterial.c
+++ b/src/backend/executor/nodeMaterial.c
@@ -243,13 +243,16 @@ ExecEndMaterial(MaterialState *node)
 	 * Release tuplestore resources
 	 */
 	if (node->tuplestorestate != NULL)
+	{
 		tuplestore_end(node->tuplestorestate);
-	node->tuplestorestate = NULL;
+		node->tuplestorestate = NULL;
+	}
 
 	/*
 	 * shut down the subplan
 	 */
 	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeMemoize.c b/src/backend/executor/nodeMemoize.c
index 1085b3c79d..9b13e2e552 100644
--- a/src/backend/executor/nodeMemoize.c
+++ b/src/backend/executor/nodeMemoize.c
@@ -1062,6 +1062,7 @@ ExecEndMemoize(MemoizeState *node)
 {
 #ifdef USE_ASSERT_CHECKING
 	/* Validate the memory accounting code is correct in assert builds. */
+	if (node->hashtable != NULL)
 	{
 		int			count;
 		uint64		mem = 0;
@@ -1108,12 +1109,17 @@ ExecEndMemoize(MemoizeState *node)
 	}
 
 	/* Remove the cache context */
-	MemoryContextDelete(node->tableContext);
+	if (node->tableContext != NULL)
+	{
+		MemoryContextDelete(node->tableContext);
+		node->tableContext = NULL;
+	}
 
 	/*
 	 * shut down the subplan
 	 */
 	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 }
 
 void
diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c
index 21b5726e6e..0a42a04b19 100644
--- a/src/backend/executor/nodeMergeAppend.c
+++ b/src/backend/executor/nodeMergeAppend.c
@@ -333,7 +333,10 @@ ExecEndMergeAppend(MergeAppendState *node)
 	 * shut down each of the subscans
 	 */
 	for (i = 0; i < nplans; i++)
+	{
 		ExecEndNode(mergeplans[i]);
+		mergeplans[i] = NULL;
+	}
 }
 
 void
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 3cdab77dfc..4b181621f9 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -1647,7 +1647,9 @@ ExecEndMergeJoin(MergeJoinState *node)
 	 * shut down the subplans
 	 */
 	ExecEndNode(innerPlanState(node));
+	innerPlanState(node) = NULL;
 	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 
 	MJ1_printf("ExecEndMergeJoin: %s\n",
 			   "node processing ended");
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index b16fbe9e22..bc82fb033a 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -4447,7 +4447,9 @@ ExecEndModifyTable(ModifyTableState *node)
 		for (j = 0; j < resultRelInfo->ri_NumSlotsInitialized; j++)
 		{
 			ExecDropSingleTupleTableSlot(resultRelInfo->ri_Slots[j]);
+			resultRelInfo->ri_Slots[j] = NULL;
 			ExecDropSingleTupleTableSlot(resultRelInfo->ri_PlanSlots[j]);
+			resultRelInfo->ri_PlanSlots[j] = NULL;
 		}
 	}
 
@@ -4455,12 +4457,16 @@ ExecEndModifyTable(ModifyTableState *node)
 	 * Close all the partitioned tables, leaf partitions, and their indices
 	 * and release the slot used for tuple routing, if set.
 	 */
-	if (node->mt_partition_tuple_routing)
+	if (node->mt_partition_tuple_routing != NULL)
 	{
 		ExecCleanupTupleRouting(node, node->mt_partition_tuple_routing);
+		node->mt_partition_tuple_routing = NULL;
 
-		if (node->mt_root_tuple_slot)
+		if (node->mt_root_tuple_slot != NULL)
+		{
 			ExecDropSingleTupleTableSlot(node->mt_root_tuple_slot);
+			node->mt_root_tuple_slot = NULL;
+		}
 	}
 
 	/*
@@ -4472,6 +4478,7 @@ ExecEndModifyTable(ModifyTableState *node)
 	 * shut down subplan
 	 */
 	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 }
 
 void
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index ebd1406843..1211d871ea 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -368,7 +368,9 @@ ExecEndNestLoop(NestLoopState *node)
 	 * close down subplans
 	 */
 	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 	ExecEndNode(innerPlanState(node));
+	innerPlanState(node) = NULL;
 
 	NL1_printf("ExecEndNestLoop: %s\n",
 			   "node processing ended");
diff --git a/src/backend/executor/nodeProjectSet.c b/src/backend/executor/nodeProjectSet.c
index aee26d3813..514669558c 100644
--- a/src/backend/executor/nodeProjectSet.c
+++ b/src/backend/executor/nodeProjectSet.c
@@ -332,6 +332,7 @@ ExecEndProjectSet(ProjectSetState *node)
 	 * shut down subplans
 	 */
 	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 }
 
 void
diff --git a/src/backend/executor/nodeRecursiveunion.c b/src/backend/executor/nodeRecursiveunion.c
index 3207643156..08637a64db 100644
--- a/src/backend/executor/nodeRecursiveunion.c
+++ b/src/backend/executor/nodeRecursiveunion.c
@@ -272,20 +272,36 @@ void
 ExecEndRecursiveUnion(RecursiveUnionState *node)
 {
 	/* Release tuplestores */
-	tuplestore_end(node->working_table);
-	tuplestore_end(node->intermediate_table);
+	if (node->working_table != NULL)
+	{
+		tuplestore_end(node->working_table);
+		node->working_table = NULL;
+	}
+	if (node->intermediate_table != NULL)
+	{
+		tuplestore_end(node->intermediate_table);
+		node->intermediate_table = NULL;
+	}
 
 	/* free subsidiary stuff including hashtable */
-	if (node->tempContext)
+	if (node->tempContext != NULL)
+	{
 		MemoryContextDelete(node->tempContext);
-	if (node->tableContext)
+		node->tempContext = NULL;
+	}
+	if (node->tableContext != NULL)
+	{
 		MemoryContextDelete(node->tableContext);
+		node->tableContext = NULL;
+	}
 
 	/*
 	 * close down subplans
 	 */
 	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 	ExecEndNode(innerPlanState(node));
+	innerPlanState(node) = NULL;
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index e9f5732f33..f15902e840 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -244,6 +244,7 @@ ExecEndResult(ResultState *node)
 	 * shut down subplans
 	 */
 	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 }
 
 void
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 41c1ea37ad..a6813559e6 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -185,14 +185,17 @@ ExecEndSampleScan(SampleScanState *node)
 	/*
 	 * Tell sampling function that we finished the scan.
 	 */
-	if (node->tsmroutine->EndSampleScan)
+	if (node->tsmroutine != NULL && node->tsmroutine->EndSampleScan)
 		node->tsmroutine->EndSampleScan(node);
 
 	/*
-	 * close heap scan
+	 * close heap scan (no-op if we didn't start it)
 	 */
 	if (node->ss.ss_currentScanDesc)
+	{
 		table_endscan(node->ss.ss_currentScanDesc);
+		node->ss.ss_currentScanDesc = NULL;
+	}
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 49a5933aff..911266da07 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -183,18 +183,14 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 void
 ExecEndSeqScan(SeqScanState *node)
 {
-	TableScanDesc scanDesc;
-
-	/*
-	 * get information from node
-	 */
-	scanDesc = node->ss.ss_currentScanDesc;
-
 	/*
-	 * close heap scan
+	 * close heap scan (no-op if we didn't start it)
 	 */
-	if (scanDesc != NULL)
-		table_endscan(scanDesc);
+	if (node->ss.ss_currentScanDesc != NULL)
+	{
+		table_endscan(node->ss.ss_currentScanDesc);
+		node->ss.ss_currentScanDesc = NULL;
+	}
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeSetOp.c b/src/backend/executor/nodeSetOp.c
index 5a84969cf8..92bd2da8e0 100644
--- a/src/backend/executor/nodeSetOp.c
+++ b/src/backend/executor/nodeSetOp.c
@@ -583,10 +583,14 @@ void
 ExecEndSetOp(SetOpState *node)
 {
 	/* free subsidiary stuff including hashtable */
-	if (node->tableContext)
+	if (node->tableContext != NULL)
+	{
 		MemoryContextDelete(node->tableContext);
+		node->tableContext = NULL;
+	}
 
 	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 }
 
 
diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c
index eea7f2ae15..c8a35b64a8 100644
--- a/src/backend/executor/nodeSort.c
+++ b/src/backend/executor/nodeSort.c
@@ -307,13 +307,16 @@ ExecEndSort(SortState *node)
 	 * Release tuplesort resources
 	 */
 	if (node->tuplesortstate != NULL)
+	{
 		tuplesort_end((Tuplesortstate *) node->tuplesortstate);
-	node->tuplesortstate = NULL;
+		node->tuplesortstate = NULL;
+	}
 
 	/*
 	 * shut down the subplan
 	 */
 	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 
 	SO1_printf("ExecEndSort: %s\n",
 			   "sort node shutdown");
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 1ee6295660..91d7ae82ce 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -171,6 +171,7 @@ ExecEndSubqueryScan(SubqueryScanState *node)
 	 * close down subquery
 	 */
 	ExecEndNode(node->subplan);
+	node->subplan = NULL;
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeTableFuncscan.c b/src/backend/executor/nodeTableFuncscan.c
index a60dcd4943..80ed4b26a8 100644
--- a/src/backend/executor/nodeTableFuncscan.c
+++ b/src/backend/executor/nodeTableFuncscan.c
@@ -217,8 +217,10 @@ ExecEndTableFuncScan(TableFuncScanState *node)
 	 * Release tuplestore resources
 	 */
 	if (node->tupstore != NULL)
+	{
 		tuplestore_end(node->tupstore);
-	node->tupstore = NULL;
+		node->tupstore = NULL;
+	}
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeTidrangescan.c b/src/backend/executor/nodeTidrangescan.c
index 6f97c35daa..d6b4ed2e42 100644
--- a/src/backend/executor/nodeTidrangescan.c
+++ b/src/backend/executor/nodeTidrangescan.c
@@ -327,10 +327,14 @@ ExecReScanTidRangeScan(TidRangeScanState *node)
 void
 ExecEndTidRangeScan(TidRangeScanState *node)
 {
-	TableScanDesc scan = node->ss.ss_currentScanDesc;
-
-	if (scan != NULL)
-		table_endscan(scan);
+	/*
+	 * close heap scan (no-op if we didn't start it)
+	 */
+	if (node->ss.ss_currentScanDesc != NULL)
+	{
+		table_endscan(node->ss.ss_currentScanDesc);
+		node->ss.ss_currentScanDesc = NULL;
+	}
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 15055077d0..74ec6afdcc 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -470,8 +470,14 @@ ExecReScanTidScan(TidScanState *node)
 void
 ExecEndTidScan(TidScanState *node)
 {
-	if (node->ss.ss_currentScanDesc)
+	/*
+	 * close heap scan (no-op if we didn't start it)
+	 */
+	if (node->ss.ss_currentScanDesc != NULL)
+	{
 		table_endscan(node->ss.ss_currentScanDesc);
+		node->ss.ss_currentScanDesc = NULL;
+	}
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c
index 01f951197c..13c556326a 100644
--- a/src/backend/executor/nodeUnique.c
+++ b/src/backend/executor/nodeUnique.c
@@ -169,6 +169,7 @@ void
 ExecEndUnique(UniqueState *node)
 {
 	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 }
 
 
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 3258305f57..0253c47448 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1351,11 +1351,14 @@ release_partition(WindowAggState *winstate)
 	 * any aggregate temp data).  We don't rely on retail pfree because some
 	 * aggregates might have allocated data we don't have direct pointers to.
 	 */
-	MemoryContextReset(winstate->partcontext);
-	MemoryContextReset(winstate->aggcontext);
+	if (winstate->partcontext != NULL)
+		MemoryContextReset(winstate->partcontext);
+	if (winstate->aggcontext != NULL)
+		MemoryContextReset(winstate->aggcontext);
 	for (i = 0; i < winstate->numaggs; i++)
 	{
-		if (winstate->peragg[i].aggcontext != winstate->aggcontext)
+		if (winstate->peragg[i].aggcontext != NULL &&
+			winstate->peragg[i].aggcontext != winstate->aggcontext)
 			MemoryContextReset(winstate->peragg[i].aggcontext);
 	}
 
@@ -2681,24 +2684,40 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 void
 ExecEndWindowAgg(WindowAggState *node)
 {
-	PlanState  *outerPlan;
 	int			i;
 
 	release_partition(node);
 
 	for (i = 0; i < node->numaggs; i++)
 	{
-		if (node->peragg[i].aggcontext != node->aggcontext)
+		if (node->peragg[i].aggcontext != NULL &&
+			node->peragg[i].aggcontext != node->aggcontext)
 			MemoryContextDelete(node->peragg[i].aggcontext);
 	}
-	MemoryContextDelete(node->partcontext);
-	MemoryContextDelete(node->aggcontext);
+	if (node->partcontext != NULL)
+	{
+		MemoryContextDelete(node->partcontext);
+		node->partcontext = NULL;
+	}
+	if (node->aggcontext != NULL)
+	{
+		MemoryContextDelete(node->aggcontext);
+		node->aggcontext = NULL;
+	}
 
-	pfree(node->perfunc);
-	pfree(node->peragg);
+	if (node->perfunc != NULL)
+	{
+		pfree(node->perfunc);
+		node->perfunc = NULL;
+	}
+	if (node->peragg != NULL)
+	{
+		pfree(node->peragg);
+		node->peragg = NULL;
+	}
 
-	outerPlan = outerPlanState(node);
-	ExecEndNode(outerPlan);
+	ExecEndNode(outerPlanState(node));
+	outerPlanState(node) = NULL;
 }
 
 /* -----------------
-- 
2.35.3

