From 235d5175ea81e3413358227ffb7f1ba3db1fccd3 Mon Sep 17 00:00:00 2001
From: Jakub Wartak <jakub.wartak@enterprisedb.com>
Date: Mon, 21 Nov 2022 16:52:34 +0100
Subject: [PATCH] Damage control for planner's get_actual_variable_endpoint()
 runaway

Limit planner's get_actual_variable_endpoint to 100 pages.
Discussion: https://www.postgresql.org/message-id/flat/CAKZiRmznOwi0oaV%3D4PHOCM4ygcH4MgSvt8%3D5cu_vNCfc8FSUug%40mail.gmail.com
---
 src/backend/utils/adt/selfuncs.c | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index e0aeaa6909..6d8ead4c06 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -6161,6 +6161,9 @@ get_actual_variable_endpoint(Relation heapRel,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	MemoryContext oldcontext;
+	int 		visited_pages = 0;
+	BlockNumber	last_block = InvalidBlockNumber;
+
 
 	/*
 	 * We use the index-only-scan machinery for this.  With mostly-static
@@ -6213,14 +6216,26 @@ get_actual_variable_endpoint(Relation heapRel,
 	/* Fetch first/next tuple in specified direction */
 	while ((tid = index_getnext_tid(index_scan, indexscandir)) != NULL)
 	{
+		BlockNumber block = ItemPointerGetBlockNumber(tid);
 		if (!VM_ALL_VISIBLE(heapRel,
-							ItemPointerGetBlockNumber(tid),
+							block,
 							&vmbuffer))
 		{
 			/* Rats, we have to visit the heap to check visibility */
 			if (!index_fetch_heap(index_scan, tableslot))
 				continue;		/* no visible tuple, try next index entry */
 
+			{
+				CHECK_FOR_INTERRUPTS();
+				if (block != last_block)
+					visited_pages++;
+#define VISITED_PAGES_LIMIT 100
+				if (visited_pages > VISITED_PAGES_LIMIT)
+					break;
+				else
+					continue; /* no visible tuple, try next index entry */
+			}
+
 			/* We don't actually need the heap tuple for anything */
 			ExecClearTuple(tableslot);
 
-- 
2.30.2

