diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index a35b93b..00fc926 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -5245,7 +5245,7 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata,
 			HeapTuple	tup;
 			Datum		values[INDEX_MAX_KEYS];
 			bool		isnull[INDEX_MAX_KEYS];
-			SnapshotData SnapshotDirty;
+			SnapshotData SnapshotNonVacuumable;
 
 			estate = CreateExecutorState();
 			econtext = GetPerTupleExprContext(estate);
@@ -5268,7 +5268,7 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata,
 			slot = MakeSingleTupleTableSlot(RelationGetDescr(heapRel));
 			econtext->ecxt_scantuple = slot;
 			get_typlenbyval(vardata->atttype, &typLen, &typByVal);
-			InitDirtySnapshot(SnapshotDirty);
+			InitNonVacuumableSnapshot(SnapshotNonVacuumable, heapRel);
 
 			/* set up an IS NOT NULL scan key so that we ignore nulls */
 			ScanKeyEntryInitialize(&scankeys[0],
@@ -5290,17 +5290,17 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata,
 				 * active snapshot, which is the best approximation we've got
 				 * to what the query will see when executed.  But that won't
 				 * be exact if a new snap is taken before running the query,
-				 * and it can be very expensive if a lot of uncommitted rows
-				 * exist at the end of the index (because we'll laboriously
-				 * fetch each one and reject it).  What seems like a good
-				 * compromise is to use SnapshotDirty.  That will accept
-				 * uncommitted rows, and thus avoid fetching multiple heap
-				 * tuples in this scenario.  On the other hand, it will reject
-				 * known-dead rows, and thus not give a bogus answer when the
-				 * extreme value has been deleted; that case motivates not
-				 * using SnapshotAny here.
+				 * and it can be very expensive if a lot of uncommitted or dead
+				 * rows exist at the beginning or end of the index (because we'll
+				 * laboriously fetch each one and reject it).  What seems like a
+				 * good compromise is to use SnapshotNonVacuumable.  That will
+				 * accept uncommitted rows and recently dead rows, and thus avoid
+				 * fetching multiple heap tuples in this scenario.  On the other
+				 * hand, it will reject known-dead rows, and thus not give a bogus
+				 * answer when the extreme value has been deleted; that case
+				 * motivates not using SnapshotAny here.
 				 */
-				index_scan = index_beginscan(heapRel, indexRel, &SnapshotDirty,
+				index_scan = index_beginscan(heapRel, indexRel, &SnapshotNonVacuumable,
 											 1, 0);
 				index_rescan(index_scan, scankeys, 1, NULL, 0);
 
@@ -5332,7 +5332,7 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata,
 			/* If max is requested, and we didn't find the index is empty */
 			if (max && have_data)
 			{
-				index_scan = index_beginscan(heapRel, indexRel, &SnapshotDirty,
+				index_scan = index_beginscan(heapRel, indexRel, &SnapshotNonVacuumable,
 											 1, 0);
 				index_rescan(index_scan, scankeys, 1, NULL, 0);
 
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
index 519f3b6..3254426 100644
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/utils/time/tqual.c
@@ -49,6 +49,9 @@
  *		  visible unless part of interrupted vacuum, used for TOAST
  *	 HeapTupleSatisfiesAny()
  *		  all tuples are visible
+ *	 HeapTupleSatisfiesNonVacuumable()
+ *		  all non-vacuumable tuples are visible, i.e all tuples
+ *		  except known dead tuples
  *
  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
@@ -1392,6 +1395,24 @@ HeapTupleSatisfiesVacuum(HeapTuple htup, TransactionId OldestXmin,
 	return HEAPTUPLE_DEAD;
 }
 
+
+/*
+ * HeapTupleSatisfiesNonVacuumable
+ *  False if tuple not visible to any transactionn, i.e. dead.
+ *  True in all other cases.
+ *
+ *  Use result of HeapTupleSatisfiesVacuum and return
+ *  corresponding boolean.
+ */
+bool
+HeapTupleSatisfiesNonVacuumable(HeapTuple htup, Snapshot snapshot,
+				Buffer buffer)
+{
+	return HeapTupleSatisfiesVacuum(htup, snapshot->xmin, buffer)
+			!= HEAPTUPLE_DEAD;
+}
+
+
 /*
  * HeapTupleIsSurelyDead
  *
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index f39f21b..2e2b050 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -16,7 +16,9 @@
 #define TQUAL_H
 
 #include "utils/snapshot.h"
+#include "utils/snapmgr.h"
 #include "access/xlogdefs.h"
+#include "catalog/catalog.h"
 
 
 /* Static variables representing various special snapshot semantics */
@@ -68,6 +70,8 @@ extern bool HeapTupleSatisfiesDirty(HeapTuple htup,
 						Snapshot snapshot, Buffer buffer);
 extern bool HeapTupleSatisfiesHistoricMVCC(HeapTuple htup,
 							   Snapshot snapshot, Buffer buffer);
+extern bool HeapTupleSatisfiesNonVacuumable(HeapTuple htup,
+						Snapshot snapshot, Buffer buffer);
 
 /* Special "satisfies" routines with different APIs */
 extern HTSU_Result HeapTupleSatisfiesUpdate(HeapTuple htup,
@@ -101,6 +105,26 @@ extern bool ResolveCminCmaxDuringDecoding(struct HTAB *tuplecid_data,
 	((snapshotdata).satisfies = HeapTupleSatisfiesDirty)
 
 /*
+ * Similarly, some initialization is required for SnapshotNonVacuumable.
+ * We need to set xmin horizon for this relation. If it's a proper
+ * catalog relation or a user defined, additional, catalog relation, we
+ * need to use the horizon that includes slots, otherwise the data-only
+ * horizon can be used. Note that the toast relation of user defined
+ * relations are *not* considered catalog relations.
+ */
+#define InitNonVacuumableSnapshot(snapshotdata, relation)  \
+	do { \
+		if (IsCatalogRelation(relation) || \
+			RelationIsAccessibleInLogicalDecoding(relation)) \
+			(snapshotdata).xmin = RecentGlobalXmin; \
+		else \
+			(snapshotdata).xmin = \
+				TransactionIdLimitedForOldSnapshots(RecentGlobalDataXmin, \
+								relation); \
+		(snapshotdata).satisfies = HeapTupleSatisfiesNonVacuumable; \
+	} while(0)
+
+/*
  * Similarly, some initialization is required for SnapshotToast.  We need
  * to set lsn and whenTaken correctly to support snapshot_too_old.
  */
