--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -35,6 +35,37 @@
 	bool	   *hasMatchKey;
 } pendingPosition;
 
+#define GP_SEEN_INITIAL		1024
+#define GP_SEEN_MAX			(16 * 1024 * 1024)
+
+typedef struct
+{
+	ItemPointerData *tids;		/* sorted, de-duplicated */
+	Size		n;				/* number of used slots */
+	Size		capacity;		/* allocated slots */
+	bool		recheckAll;		/* true if the tracker overflowed */
+} GinPendingSeen;
+
+static int
+ginItemPointerCmp(const void *a, const void *b)
+{
+	return ginCompareItemPointers((const ItemPointer) a, (const ItemPointer) b);
+}
+
+static bool
+ginPendingSeenCheck(GinPendingSeen *seen, ItemPointer tid)
+{
+	ItemPointerData *found;
+
+	if (seen->recheckAll)
+		return true;
+	if (seen->n == 0)
+		return false;
+	found = (ItemPointerData *) bsearch(tid, seen->tids, seen->n,
+										sizeof(ItemPointerData),
+										ginItemPointerCmp);
+	return found != NULL;
+}
 
 /*
  * Goes to the next page if current offset is outside of bounds
@@ -1447,12 +1478,10 @@
 	return true;
 }
 
-
 /*
  * Functions for scanning the pending list
  */
 
-
 /*
  * Get ItemPointer of next heap row to be checked from pending list.
  * Returns false if there are no more. On pages with several heap rows
@@ -1832,7 +1861,8 @@
  * Collect all matched rows from pending list into bitmap.
  */
 static void
-scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids)
+scanPendingInsert(IndexScanDesc scan, TIDBitmap *tbm, int64 *ntids,
+				  GinPendingSeen *seen)
 {
 	GinScanOpaque so = (GinScanOpaque) scan->opaque;
 	MemoryContext oldCtx;
@@ -1878,6 +1908,40 @@
 	 */
 	while (scanGetCandidate(scan, &pos))
 	{
+		if (!seen->recheckAll)
+		{
+			if (seen->n == seen->capacity)
+			{
+				Size		newcap = seen->capacity ? seen->capacity * 2 : GP_SEEN_INITIAL;
+
+				if (newcap > GP_SEEN_MAX ||
+					newcap * sizeof(ItemPointerData) > MaxAllocSize)
+				{
+					seen->recheckAll = true;
+					if (seen->tids)
+						pfree(seen->tids);
+					seen->tids = NULL;
+					seen->n = seen->capacity = 0;
+				}
+				else
+				{
+					ItemPointerData *newtids = (ItemPointerData *)
+						palloc(newcap * sizeof(ItemPointerData));
+
+					if (seen->tids)
+					{
+						memcpy(newtids, seen->tids,
+							   seen->n * sizeof(ItemPointerData));
+						pfree(seen->tids);
+					}
+					seen->tids = newtids;
+					seen->capacity = newcap;
+				}
+			}
+			if (!seen->recheckAll)
+				seen->tids[seen->n++] = pos.item;
+		}
+
 		/*
 		 * Check entries in tuple and set up entryRes array.
 		 *
@@ -1921,7 +1985,6 @@
 	pfree(pos.hasMatchKey);
 }
 
-
 #define GinIsVoidRes(s)		( ((GinScanOpaque) scan->opaque)->isVoidRes )
 
 int64
@@ -1931,6 +1994,7 @@
 	int64		ntids;
 	ItemPointerData iptr;
 	bool		recheck;
+	GinPendingSeen seen;
 
 	/*
 	 * Set up the scan keys, and check for unsatisfiable query.
@@ -1943,6 +2007,10 @@
 		return 0;
 
 	ntids = 0;
+	seen.tids = NULL;
+	seen.n = 0;
+	seen.capacity = 0;
+	seen.recheckAll = false;
 
 	/*
 	 * First, scan the pending list and collect any matching entries into the
@@ -1954,7 +2022,23 @@
 	 * to scan the main index before the pending list, since concurrent
 	 * cleanup could then make us miss entries entirely.
 	 */
-	scanPendingInsert(scan, tbm, &ntids);
+	scanPendingInsert(scan, tbm, &ntids, &seen);
+
+	if (seen.n > 1)
+	{
+		Size		out;
+
+		qsort(seen.tids, seen.n, sizeof(ItemPointerData), ginItemPointerCmp);
+		out = 1;
+		for (Size i = 1; i < seen.n; i++)
+		{
+			if (!ItemPointerEquals(&seen.tids[out - 1], &seen.tids[i]))
+			{
+				seen.tids[out++] = seen.tids[i];
+			}
+		}
+		seen.n = out;
+	}
 
 	/*
 	 * Now scan the main index.
@@ -1971,9 +2055,16 @@
 		if (ItemPointerIsLossyPage(&iptr))
 			tbm_add_page(tbm, ItemPointerGetBlockNumber(&iptr));
 		else
+		{
+			if (seen.recheckAll || ginPendingSeenCheck(&seen, &iptr))
+				recheck = true;
 			tbm_add_tuples(tbm, &iptr, 1, recheck);
+		}
 		ntids++;
 	}
 
+	if (seen.tids)
+		pfree(seen.tids);
+
 	return ntids;
 }
