diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index d7e3f09..b51cbed 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -5232,8 +5232,6 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata,
 								   InvalidOid,	/* no reg proc for this */
 								   (Datum) 0);	/* constant */
 
-			have_data = true;
-
 			/* If min is requested ... */
 			if (min)
 			{
@@ -5262,15 +5260,33 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata,
 				 * get_actual_variable_range() call will not have to visit
 				 * that heap entry.  In this way we avoid repetitive work when
 				 * this function is used a lot during planning.
+				 *
+				 * However, all is not perfect, because if the index was
+				 * recently created it may have entries pointing to broken HOT
+				 * chains that contain tuples that don't match the index entry
+				 * but aren't yet known-dead either.  We need to ignore such
+				 * tuples, since they might not be representative of the index
+				 * extremal values, indeed could even contain NULLs.  We
+				 * approximate this by ignoring any hot-updated tuples we see
+				 * and continuing to scan.
 				 */
 				index_scan = index_beginscan(heapRel, indexRel,
 											 &SnapshotNonVacuumable,
 											 1, 0);
 				index_rescan(index_scan, scankeys, 1, NULL, 0);
 
-				/* Fetch first tuple in sortop's direction */
-				if (index_getnext_slot(index_scan, indexscandir, slot))
+				/* Fetch first/next tuple in sortop's direction */
+				while (index_getnext_slot(index_scan, indexscandir, slot))
 				{
+					/* Ignore hot-updated tuples, per comment above */
+					if (TTS_IS_BUFFERTUPLE(slot))
+					{
+						BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+						if (HeapTupleHeaderIsHotUpdated(bslot->base.tupdata.t_data))
+							continue;
+					}
+
 					/* Extract the index column values from the slot */
 					FormIndexDatum(indexInfo, slot, estate,
 								   values, isnull);
@@ -5284,24 +5300,40 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata,
 					MemoryContextSwitchTo(oldcontext);
 					*min = datumCopy(values[0], typByVal, typLen);
 					MemoryContextSwitchTo(tmpcontext);
+					have_data = true;
+					break;
 				}
-				else
-					have_data = false;
 
 				index_endscan(index_scan);
 			}
+			else
+			{
+				/* If min not requested, assume index has data */
+				have_data = true;
+			}
 
 			/* If max is requested, and we didn't find the index is empty */
 			if (max && have_data)
 			{
+				have_data = false;
+
 				index_scan = index_beginscan(heapRel, indexRel,
 											 &SnapshotNonVacuumable,
 											 1, 0);
 				index_rescan(index_scan, scankeys, 1, NULL, 0);
 
-				/* Fetch first tuple in reverse direction */
-				if (index_getnext_slot(index_scan, -indexscandir, slot))
+				/* Fetch first/next tuple in reverse direction */
+				while (index_getnext_slot(index_scan, -indexscandir, slot))
 				{
+					/* Ignore hot-updated tuples, per comment above */
+					if (TTS_IS_BUFFERTUPLE(slot))
+					{
+						BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
+						if (HeapTupleHeaderIsHotUpdated(bslot->base.tupdata.t_data))
+							continue;
+					}
+
 					/* Extract the index column values from the slot */
 					FormIndexDatum(indexInfo, slot, estate,
 								   values, isnull);
@@ -5315,9 +5347,9 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata,
 					MemoryContextSwitchTo(oldcontext);
 					*max = datumCopy(values[0], typByVal, typLen);
 					MemoryContextSwitchTo(tmpcontext);
+					have_data = true;
+					break;
 				}
-				else
-					have_data = false;
 
 				index_endscan(index_scan);
 			}
