From a8dd50100915f4bc889bc923d749d0661a88b973 Mon Sep 17 00:00:00 2001
From: Andres Freund <andres@anarazel.de>
Date: Tue, 25 Jul 2017 17:37:17 -0700
Subject: [PATCH 1/2] Move interrupt checking from ExecProcNode() to executor
 nodes.

In a followup commit ExecProcNode(), and especially the large switch
it contains, will be replaced by a function pointer directly to the
correct node. The node functions will then get invoked by a thin
inline function wrapper. To avoid having to include miscadmin.h in
headers - CHECK_FOR_INTERRUPTS() - move the interrupt checks into the
individual executor routines.

While looking through all executor nodes, I noticed a number of
missing interrupt checks, add these too.

Author: Andres Freund
Reviewed-By: Tom Lane
Discussion:
    https://postgr.es/m/22833.1490390175@sss.pgh.pa.us
---
 src/backend/executor/execProcnode.c       | 2 --
 src/backend/executor/execScan.c           | 1 +
 src/backend/executor/nodeAgg.c            | 5 +++++
 src/backend/executor/nodeAppend.c         | 3 +++
 src/backend/executor/nodeBitmapHeapscan.c | 3 +++
 src/backend/executor/nodeCustom.c         | 3 +++
 src/backend/executor/nodeGather.c         | 4 ++++
 src/backend/executor/nodeGatherMerge.c    | 4 ++++
 src/backend/executor/nodeGroup.c          | 3 +++
 src/backend/executor/nodeHash.c           | 6 ++++++
 src/backend/executor/nodeHashjoin.c       | 9 ++-------
 src/backend/executor/nodeIndexonlyscan.c  | 3 +++
 src/backend/executor/nodeIndexscan.c      | 6 ++++++
 src/backend/executor/nodeLimit.c          | 3 +++
 src/backend/executor/nodeMaterial.c       | 2 ++
 src/backend/executor/nodeMergeAppend.c    | 4 +++-
 src/backend/executor/nodeMergejoin.c      | 3 +++
 src/backend/executor/nodeModifyTable.c    | 2 ++
 src/backend/executor/nodeNestloop.c       | 3 +++
 src/backend/executor/nodeProjectSet.c     | 3 +++
 src/backend/executor/nodeRecursiveunion.c | 2 ++
 src/backend/executor/nodeResult.c         | 3 +++
 src/backend/executor/nodeSetOp.c          | 5 +++++
 src/backend/executor/nodeSort.c           | 2 ++
 src/backend/executor/nodeSubplan.c        | 5 +++++
 src/backend/executor/nodeTableFuncscan.c  | 2 ++
 src/backend/executor/nodeTidscan.c        | 3 +++
 src/backend/executor/nodeUnique.c         | 3 +++
 src/backend/executor/nodeWindowAgg.c      | 5 +++++
 29 files changed, 92 insertions(+), 10 deletions(-)

diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index 294ad2cff9..20fd9f822e 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -399,8 +399,6 @@ ExecProcNode(PlanState *node)
 {
 	TupleTableSlot *result;
 
-	CHECK_FOR_INTERRUPTS();
-
 	if (node->chgParam != NULL) /* something changed */
 		ExecReScan(node);		/* let ReScan handle this */
 
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 4f131b3ee0..dc40f6b699 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -139,6 +139,7 @@ ExecScan(ScanState *node,
 	 */
 	if (!qual && !projInfo)
 	{
+		CHECK_FOR_INTERRUPTS();
 		ResetExprContext(econtext);
 		return ExecScanFetch(node, accessMtd, recheckMtd);
 	}
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index de9a18e71c..c1096ed8b0 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -677,6 +677,7 @@ fetch_input_tuple(AggState *aggstate)
 
 	if (aggstate->sort_in)
 	{
+		CHECK_FOR_INTERRUPTS();
 		if (!tuplesort_gettupleslot(aggstate->sort_in, true, false,
 									aggstate->sort_slot, NULL))
 			return NULL;
@@ -1414,6 +1415,8 @@ process_ordered_aggregate_multi(AggState *aggstate,
 	while (tuplesort_gettupleslot(pertrans->sortstates[aggstate->current_set],
 								  true, true, slot1, &newAbbrevVal))
 	{
+		CHECK_FOR_INTERRUPTS();
+
 		/*
 		 * Extract the first numTransInputs columns as datums to pass to the
 		 * transfn.  (This will help execTuplesMatch too, so we do it
@@ -2563,6 +2566,8 @@ agg_retrieve_hash_table(AggState *aggstate)
 		TupleTableSlot *hashslot = perhash->hashslot;
 		int			i;
 
+		CHECK_FOR_INTERRUPTS();
+
 		/*
 		 * Find the next entry in the hash table
 		 */
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index aae5e3fa63..58045e05e5 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -59,6 +59,7 @@
 
 #include "executor/execdebug.h"
 #include "executor/nodeAppend.h"
+#include "miscadmin.h"
 
 static bool exec_append_initialize_next(AppendState *appendstate);
 
@@ -204,6 +205,8 @@ ExecAppend(AppendState *node)
 		PlanState  *subnode;
 		TupleTableSlot *result;
 
+		CHECK_FOR_INTERRUPTS();
+
 		/*
 		 * figure out which subplan we are currently processing
 		 */
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 7e0ba030b7..cf109d5049 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -41,6 +41,7 @@
 #include "access/transam.h"
 #include "executor/execdebug.h"
 #include "executor/nodeBitmapHeapscan.h"
+#include "miscadmin.h"
 #include "pgstat.h"
 #include "storage/bufmgr.h"
 #include "storage/predicate.h"
@@ -192,6 +193,8 @@ BitmapHeapNext(BitmapHeapScanState *node)
 		Page		dp;
 		ItemId		lp;
 
+		CHECK_FOR_INTERRUPTS();
+
 		/*
 		 * Get next page of results if needed
 		 */
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 69e27047f1..e28e41df59 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -15,6 +15,7 @@
 #include "executor/nodeCustom.h"
 #include "nodes/execnodes.h"
 #include "nodes/plannodes.h"
+#include "miscadmin.h"
 #include "parser/parsetree.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
@@ -105,6 +106,8 @@ TupleTableSlot *
 ExecCustomScan(CustomScanState *node)
 {
 	Assert(node->methods->ExecCustomScan != NULL);
+	CHECK_FOR_INTERRUPTS();
+
 	return node->methods->ExecCustomScan(node);
 }
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index f83cd584d7..5dbe19c056 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -128,6 +128,8 @@ ExecGather(GatherState *node)
 	TupleTableSlot *slot;
 	ExprContext *econtext;
 
+	CHECK_FOR_INTERRUPTS();
+
 	/*
 	 * Initialize the parallel context and workers on first execution. We do
 	 * this on first execution rather than during node initialization, as it
@@ -247,6 +249,8 @@ gather_getnext(GatherState *gatherstate)
 
 	while (gatherstate->reader != NULL || gatherstate->need_to_scan_locally)
 	{
+		CHECK_FOR_INTERRUPTS();
+
 		if (gatherstate->reader != NULL)
 		{
 			MemoryContext oldContext;
diff --git a/src/backend/executor/nodeGatherMerge.c b/src/backend/executor/nodeGatherMerge.c
index 80ee1fc89b..0aff3798f7 100644
--- a/src/backend/executor/nodeGatherMerge.c
+++ b/src/backend/executor/nodeGatherMerge.c
@@ -164,6 +164,8 @@ ExecGatherMerge(GatherMergeState *node)
 	ExprContext *econtext;
 	int			i;
 
+	CHECK_FOR_INTERRUPTS();
+
 	/*
 	 * As with Gather, we don't launch workers until this node is actually
 	 * executed.
@@ -393,6 +395,8 @@ gather_merge_init(GatherMergeState *gm_state)
 reread:
 	for (i = 0; i < nreaders + 1; i++)
 	{
+		CHECK_FOR_INTERRUPTS();
+
 		if (!gm_state->gm_tuple_buffers[i].done &&
 			(TupIsNull(gm_state->gm_slots[i]) ||
 			 gm_state->gm_slots[i]->tts_isempty))
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index af9ba4905e..fc5e0e59bc 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -24,6 +24,7 @@
 
 #include "executor/executor.h"
 #include "executor/nodeGroup.h"
+#include "miscadmin.h"
 
 
 /*
@@ -40,6 +41,8 @@ ExecGroup(GroupState *node)
 	TupleTableSlot *firsttupleslot;
 	TupleTableSlot *outerslot;
 
+	CHECK_FOR_INTERRUPTS();
+
 	/*
 	 * get state info from node
 	 */
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 075f4ed11c..fbeb562489 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -810,6 +810,9 @@ ExecHashIncreaseNumBuckets(HashJoinTable hashtable)
 			idx += MAXALIGN(HJTUPLE_OVERHEAD +
 							HJTUPLE_MINTUPLE(hashTuple)->t_len);
 		}
+
+		/* allow this loop to be cancellable */
+		CHECK_FOR_INTERRUPTS();
 	}
 }
 
@@ -1192,6 +1195,9 @@ ExecScanHashTableForUnmatched(HashJoinState *hjstate, ExprContext *econtext)
 
 			hashTuple = hashTuple->next;
 		}
+
+		/* allow this loop to be cancellable */
+		CHECK_FOR_INTERRUPTS();
 	}
 
 	/*
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 668ed871e1..917c3b6cef 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -92,6 +92,8 @@ ExecHashJoin(HashJoinState *node)
 	 */
 	for (;;)
 	{
+		CHECK_FOR_INTERRUPTS();
+
 		switch (node->hj_JoinState)
 		{
 			case HJ_BUILD_HASHTABLE:
@@ -246,13 +248,6 @@ ExecHashJoin(HashJoinState *node)
 
 			case HJ_SCAN_BUCKET:
 
-				/*
-				 * We check for interrupts here because this corresponds to
-				 * where we'd fetch a row from a child plan node in other join
-				 * types.
-				 */
-				CHECK_FOR_INTERRUPTS();
-
 				/*
 				 * Scan the selected hash bucket for matches to current outer
 				 */
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 890e54416a..e2000764a4 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -34,6 +34,7 @@
 #include "executor/execdebug.h"
 #include "executor/nodeIndexonlyscan.h"
 #include "executor/nodeIndexscan.h"
+#include "miscadmin.h"
 #include "storage/bufmgr.h"
 #include "storage/predicate.h"
 #include "utils/memutils.h"
@@ -117,6 +118,8 @@ IndexOnlyNext(IndexOnlyScanState *node)
 	{
 		HeapTuple	tuple = NULL;
 
+		CHECK_FOR_INTERRUPTS();
+
 		/*
 		 * We can skip the heap fetch if the TID references a heap page on
 		 * which all tuples are known visible to everybody.  In any case,
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 75b10115f5..581a2092be 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -34,6 +34,7 @@
 #include "executor/execdebug.h"
 #include "executor/nodeIndexscan.h"
 #include "lib/pairingheap.h"
+#include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
 #include "utils/array.h"
@@ -131,6 +132,8 @@ IndexNext(IndexScanState *node)
 	 */
 	while ((tuple = index_getnext(scandesc, direction)) != NULL)
 	{
+		CHECK_FOR_INTERRUPTS();
+
 		/*
 		 * Store the scanned tuple in the scan tuple slot of the scan state.
 		 * Note: we pass 'false' because tuples returned by amgetnext are
@@ -233,6 +236,8 @@ IndexNextWithReorder(IndexScanState *node)
 
 	for (;;)
 	{
+		CHECK_FOR_INTERRUPTS();
+
 		/*
 		 * Check the reorder queue first.  If the topmost tuple in the queue
 		 * has an ORDER BY value smaller than (or equal to) the value last
@@ -299,6 +304,7 @@ next_indextuple:
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
+				CHECK_FOR_INTERRUPTS();
 				goto next_indextuple;
 			}
 		}
diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c
index abd060d75f..2ed3523257 100644
--- a/src/backend/executor/nodeLimit.c
+++ b/src/backend/executor/nodeLimit.c
@@ -23,6 +23,7 @@
 
 #include "executor/executor.h"
 #include "executor/nodeLimit.h"
+#include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 
 static void recompute_limits(LimitState *node);
@@ -43,6 +44,8 @@ ExecLimit(LimitState *node)
 	TupleTableSlot *slot;
 	PlanState  *outerPlan;
 
+	CHECK_FOR_INTERRUPTS();
+
 	/*
 	 * get information from the node
 	 */
diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c
index 32b7269cda..3342949590 100644
--- a/src/backend/executor/nodeMaterial.c
+++ b/src/backend/executor/nodeMaterial.c
@@ -45,6 +45,8 @@ ExecMaterial(MaterialState *node)
 	bool		eof_tuplestore;
 	TupleTableSlot *slot;
 
+	CHECK_FOR_INTERRUPTS();
+
 	/*
 	 * get state info from node
 	 */
diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c
index fef83dbdbd..d41def1350 100644
--- a/src/backend/executor/nodeMergeAppend.c
+++ b/src/backend/executor/nodeMergeAppend.c
@@ -40,8 +40,8 @@
 
 #include "executor/execdebug.h"
 #include "executor/nodeMergeAppend.h"
-
 #include "lib/binaryheap.h"
+#include "miscadmin.h"
 
 /*
  * We have one slot for each item in the heap array.  We use SlotNumber
@@ -175,6 +175,8 @@ ExecMergeAppend(MergeAppendState *node)
 	TupleTableSlot *result;
 	SlotNumber	i;
 
+	CHECK_FOR_INTERRUPTS();
+
 	if (!node->ms_initialized)
 	{
 		/*
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6a145ee33a..657af4692f 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -95,6 +95,7 @@
 #include "access/nbtree.h"
 #include "executor/execdebug.h"
 #include "executor/nodeMergejoin.h"
+#include "miscadmin.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 
@@ -634,6 +635,8 @@ ExecMergeJoin(MergeJoinState *node)
 	{
 		MJ_dump(node);
 
+		CHECK_FOR_INTERRUPTS();
+
 		/*
 		 * get the current state of the join and do things accordingly.
 		 */
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 77ba15dd90..637a582e1c 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1551,6 +1551,8 @@ ExecModifyTable(ModifyTableState *node)
 	HeapTupleData oldtupdata;
 	HeapTuple	oldtuple;
 
+	CHECK_FOR_INTERRUPTS();
+
 	/*
 	 * This should NOT get called during EvalPlanQual; we should have passed a
 	 * subplan tree to EvalPlanQual, instead.  Use a runtime test not just
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 0065fe601e..ec8862653e 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -23,6 +23,7 @@
 
 #include "executor/execdebug.h"
 #include "executor/nodeNestloop.h"
+#include "miscadmin.h"
 #include "utils/memutils.h"
 
 
@@ -95,6 +96,8 @@ ExecNestLoop(NestLoopState *node)
 
 	for (;;)
 	{
+		CHECK_FOR_INTERRUPTS();
+
 		/*
 		 * If we don't have an outer tuple, get the next one and reset the
 		 * inner scan.
diff --git a/src/backend/executor/nodeProjectSet.c b/src/backend/executor/nodeProjectSet.c
index 01048cc826..3b69c7adee 100644
--- a/src/backend/executor/nodeProjectSet.c
+++ b/src/backend/executor/nodeProjectSet.c
@@ -24,6 +24,7 @@
 
 #include "executor/executor.h"
 #include "executor/nodeProjectSet.h"
+#include "miscadmin.h"
 #include "nodes/nodeFuncs.h"
 #include "utils/memutils.h"
 
@@ -46,6 +47,8 @@ ExecProjectSet(ProjectSetState *node)
 	PlanState  *outerPlan;
 	ExprContext *econtext;
 
+	CHECK_FOR_INTERRUPTS();
+
 	econtext = node->ps.ps_ExprContext;
 
 	/*
diff --git a/src/backend/executor/nodeRecursiveunion.c b/src/backend/executor/nodeRecursiveunion.c
index fc1c00d68f..2802fffa2b 100644
--- a/src/backend/executor/nodeRecursiveunion.c
+++ b/src/backend/executor/nodeRecursiveunion.c
@@ -75,6 +75,8 @@ ExecRecursiveUnion(RecursiveUnionState *node)
 	TupleTableSlot *slot;
 	bool		isnew;
 
+	CHECK_FOR_INTERRUPTS();
+
 	/* 1. Evaluate non-recursive term */
 	if (!node->recursing)
 	{
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index a753a53419..f007f46784 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -47,6 +47,7 @@
 
 #include "executor/executor.h"
 #include "executor/nodeResult.h"
+#include "miscadmin.h"
 #include "utils/memutils.h"
 
 
@@ -70,6 +71,8 @@ ExecResult(ResultState *node)
 	PlanState  *outerPlan;
 	ExprContext *econtext;
 
+	CHECK_FOR_INTERRUPTS();
+
 	econtext = node->ps.ps_ExprContext;
 
 	/*
diff --git a/src/backend/executor/nodeSetOp.c b/src/backend/executor/nodeSetOp.c
index 9c7812e519..56c5643f17 100644
--- a/src/backend/executor/nodeSetOp.c
+++ b/src/backend/executor/nodeSetOp.c
@@ -47,6 +47,7 @@
 #include "access/htup_details.h"
 #include "executor/executor.h"
 #include "executor/nodeSetOp.h"
+#include "miscadmin.h"
 #include "utils/memutils.h"
 
 
@@ -185,6 +186,8 @@ ExecSetOp(SetOpState *node)
 	SetOp	   *plannode = (SetOp *) node->ps.plan;
 	TupleTableSlot *resultTupleSlot = node->ps.ps_ResultTupleSlot;
 
+	CHECK_FOR_INTERRUPTS();
+
 	/*
 	 * If the previously-returned tuple needs to be returned more than once,
 	 * keep returning it.
@@ -428,6 +431,8 @@ setop_retrieve_hash_table(SetOpState *setopstate)
 	 */
 	while (!setopstate->setop_done)
 	{
+		CHECK_FOR_INTERRUPTS();
+
 		/*
 		 * Find the next entry in the hash table
 		 */
diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c
index 924b458df8..799a4e9204 100644
--- a/src/backend/executor/nodeSort.c
+++ b/src/backend/executor/nodeSort.c
@@ -43,6 +43,8 @@ ExecSort(SortState *node)
 	Tuplesortstate *tuplesortstate;
 	TupleTableSlot *slot;
 
+	CHECK_FOR_INTERRUPTS();
+
 	/*
 	 * get state info from node
 	 */
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e8fa4c8547..fe10e809df 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -33,6 +33,7 @@
 #include "executor/executor.h"
 #include "executor/nodeSubplan.h"
 #include "nodes/makefuncs.h"
+#include "miscadmin.h"
 #include "optimizer/clauses.h"
 #include "utils/array.h"
 #include "utils/lsyscache.h"
@@ -65,6 +66,8 @@ ExecSubPlan(SubPlanState *node,
 {
 	SubPlan    *subplan = node->subplan;
 
+	CHECK_FOR_INTERRUPTS();
+
 	/* Set non-null as default */
 	*isNull = false;
 
@@ -618,6 +621,8 @@ findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot,
 	InitTupleHashIterator(hashtable, &hashiter);
 	while ((entry = ScanTupleHashTable(hashtable, &hashiter)) != NULL)
 	{
+		CHECK_FOR_INTERRUPTS();
+
 		ExecStoreMinimalTuple(entry->firstTuple, hashtable->tableslot, false);
 		if (!execTuplesUnequal(slot, hashtable->tableslot,
 							   numCols, keyColIdx,
diff --git a/src/backend/executor/nodeTableFuncscan.c b/src/backend/executor/nodeTableFuncscan.c
index bb016ec8f6..2859363fe2 100644
--- a/src/backend/executor/nodeTableFuncscan.c
+++ b/src/backend/executor/nodeTableFuncscan.c
@@ -440,6 +440,8 @@ tfuncLoadRows(TableFuncScanState *tstate, ExprContext *econtext)
 		ListCell   *cell = list_head(tstate->coldefexprs);
 		int			colno;
 
+		CHECK_FOR_INTERRUPTS();
+
 		ExecClearTuple(tstate->ss.ss_ScanTupleSlot);
 
 		/*
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 96af2d21d9..c122473bdf 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -26,6 +26,7 @@
 #include "catalog/pg_type.h"
 #include "executor/execdebug.h"
 #include "executor/nodeTidscan.h"
+#include "miscadmin.h"
 #include "optimizer/clauses.h"
 #include "storage/bufmgr.h"
 #include "utils/array.h"
@@ -400,6 +401,8 @@ TidNext(TidScanState *node)
 			node->tss_TidPtr--;
 		else
 			node->tss_TidPtr++;
+
+		CHECK_FOR_INTERRUPTS();
 	}
 
 	/*
diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c
index 28cc1e90f8..db78c88368 100644
--- a/src/backend/executor/nodeUnique.c
+++ b/src/backend/executor/nodeUnique.c
@@ -35,6 +35,7 @@
 
 #include "executor/executor.h"
 #include "executor/nodeUnique.h"
+#include "miscadmin.h"
 #include "utils/memutils.h"
 
 
@@ -50,6 +51,8 @@ ExecUnique(UniqueState *node)
 	TupleTableSlot *slot;
 	PlanState  *outerPlan;
 
+	CHECK_FOR_INTERRUPTS();
+
 	/*
 	 * get information from the node
 	 */
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 8f13fe0c73..9da35ac506 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1594,6 +1594,8 @@ ExecWindowAgg(WindowAggState *winstate)
 	int			i;
 	int			numfuncs;
 
+	CHECK_FOR_INTERRUPTS();
+
 	if (winstate->all_done)
 		return NULL;
 
@@ -2371,6 +2373,9 @@ window_gettupleslot(WindowObject winobj, int64 pos, TupleTableSlot *slot)
 	WindowAggState *winstate = winobj->winstate;
 	MemoryContext oldcontext;
 
+	/* often called repeatedly in a row */
+	CHECK_FOR_INTERRUPTS();
+
 	/* Don't allow passing -1 to spool_tuples here */
 	if (pos < 0)
 		return false;
-- 
2.13.1.392.g8d1b10321b.dirty

