From 9035f48eb107e454012e37fa62d67cee6d1d5f8f Mon Sep 17 00:00:00 2001
From: Maxime Schoemans <maxime.schoemans@enterprisedb.com>
Date: Thu, 18 Dec 2025 15:34:26 +0100
Subject: [PATCH v1] Perform ScanKey evaluation in slot form

---
 src/backend/access/heap/heapam.c | 33 +++++++++++++++++++-------------
 src/include/access/valid.h       |  9 +++++++--
 2 files changed, 27 insertions(+), 15 deletions(-)

diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 6daf4a87dec..97fc583d8c4 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -31,6 +31,7 @@
  */
 #include "postgres.h"
 
+#include "access/genam.h"
 #include "access/heapam.h"
 #include "access/heaptoast.h"
 #include "access/hio.h"
@@ -43,6 +44,7 @@
 #include "catalog/pg_database.h"
 #include "catalog/pg_database_d.h"
 #include "commands/vacuum.h"
+#include "executor/tuptable.h"
 #include "pgstat.h"
 #include "port/pg_bitutils.h"
 #include "storage/lmgr.h"
@@ -911,7 +913,8 @@ static void
 heapgettup(HeapScanDesc scan,
 		   ScanDirection dir,
 		   int nkeys,
-		   ScanKey key)
+		   ScanKey key,
+		   TupleTableSlot *slot)
 {
 	HeapTuple	tuple = &(scan->rs_ctup);
 	Page		page;
@@ -975,10 +978,13 @@ continue_page:
 			if (!visible)
 				continue;
 
+			if (slot)
+				ExecStoreBufferHeapTuple(&scan->rs_ctup, slot, scan->rs_cbuf);
+
 			/* skip any tuples that don't match the scan key */
 			if (key != NULL &&
 				!HeapKeyTest(tuple, RelationGetDescr(scan->rs_base.rs_rd),
-							 nkeys, key))
+							 nkeys, key, slot))
 				continue;
 
 			LockBuffer(scan->rs_cbuf, BUFFER_LOCK_UNLOCK);
@@ -1021,7 +1027,8 @@ static void
 heapgettup_pagemode(HeapScanDesc scan,
 					ScanDirection dir,
 					int nkeys,
-					ScanKey key)
+					ScanKey key,
+					TupleTableSlot *slot)
 {
 	HeapTuple	tuple = &(scan->rs_ctup);
 	Page		page;
@@ -1083,10 +1090,13 @@ continue_page:
 			tuple->t_len = ItemIdGetLength(lpp);
 			ItemPointerSetOffsetNumber(&tuple->t_self, lineoff);
 
+			if (slot)
+				ExecStoreBufferHeapTuple(&scan->rs_ctup, slot, scan->rs_cbuf);
+
 			/* skip any tuples that don't match the scan key */
 			if (key != NULL &&
 				!HeapKeyTest(tuple, RelationGetDescr(scan->rs_base.rs_rd),
-							 nkeys, key))
+							 nkeys, key, slot))
 				continue;
 
 			scan->rs_cindex = lineindex;
@@ -1388,10 +1398,10 @@ heap_getnext(TableScanDesc sscan, ScanDirection direction)
 
 	if (scan->rs_base.rs_flags & SO_ALLOW_PAGEMODE)
 		heapgettup_pagemode(scan, direction,
-							scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
+							scan->rs_base.rs_nkeys, scan->rs_base.rs_key, NULL);
 	else
 		heapgettup(scan, direction,
-				   scan->rs_base.rs_nkeys, scan->rs_base.rs_key);
+				   scan->rs_base.rs_nkeys, scan->rs_base.rs_key, NULL);
 
 	if (scan->rs_ctup.t_data == NULL)
 		return NULL;
@@ -1414,9 +1424,9 @@ heap_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *s
 	/* Note: no locking manipulations needed */
 
 	if (sscan->rs_flags & SO_ALLOW_PAGEMODE)
-		heapgettup_pagemode(scan, direction, sscan->rs_nkeys, sscan->rs_key);
+		heapgettup_pagemode(scan, direction, sscan->rs_nkeys, sscan->rs_key, slot);
 	else
-		heapgettup(scan, direction, sscan->rs_nkeys, sscan->rs_key);
+		heapgettup(scan, direction, sscan->rs_nkeys, sscan->rs_key, slot);
 
 	if (scan->rs_ctup.t_data == NULL)
 	{
@@ -1431,8 +1441,6 @@ heap_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *s
 
 	pgstat_count_heap_getnext(scan->rs_base.rs_rd);
 
-	ExecStoreBufferHeapTuple(&scan->rs_ctup, slot,
-							 scan->rs_cbuf);
 	return true;
 }
 
@@ -1521,9 +1529,9 @@ heap_getnextslot_tidrange(TableScanDesc sscan, ScanDirection direction,
 	for (;;)
 	{
 		if (sscan->rs_flags & SO_ALLOW_PAGEMODE)
-			heapgettup_pagemode(scan, direction, sscan->rs_nkeys, sscan->rs_key);
+			heapgettup_pagemode(scan, direction, sscan->rs_nkeys, sscan->rs_key, slot);
 		else
-			heapgettup(scan, direction, sscan->rs_nkeys, sscan->rs_key);
+			heapgettup(scan, direction, sscan->rs_nkeys, sscan->rs_key, slot);
 
 		if (scan->rs_ctup.t_data == NULL)
 		{
@@ -1579,7 +1587,6 @@ heap_getnextslot_tidrange(TableScanDesc sscan, ScanDirection direction,
 	 */
 	pgstat_count_heap_getnext(scan->rs_base.rs_rd);
 
-	ExecStoreBufferHeapTuple(&scan->rs_ctup, slot, scan->rs_cbuf);
 	return true;
 }
 
diff --git a/src/include/access/valid.h b/src/include/access/valid.h
index 8b33089dac4..2f272294784 100644
--- a/src/include/access/valid.h
+++ b/src/include/access/valid.h
@@ -18,6 +18,7 @@
 #include "access/htup_details.h"
 #include "access/skey.h"
 #include "access/tupdesc.h"
+#include "executor/tuptable.h"
 
 /*
  *		HeapKeyTest
@@ -25,7 +26,8 @@
  *		Test a heap tuple to see if it satisfies a scan key.
  */
 static inline bool
-HeapKeyTest(HeapTuple tuple, TupleDesc tupdesc, int nkeys, ScanKey keys)
+HeapKeyTest(HeapTuple tuple, TupleDesc tupdesc, int nkeys, ScanKey keys,
+	TupleTableSlot *slot)
 {
 	int			cur_nkeys = nkeys;
 	ScanKey		cur_key = keys;
@@ -39,7 +41,10 @@ HeapKeyTest(HeapTuple tuple, TupleDesc tupdesc, int nkeys, ScanKey keys)
 		if (cur_key->sk_flags & SK_ISNULL)
 			return false;
 
-		atp = heap_getattr(tuple, cur_key->sk_attno, tupdesc, &isnull);
+		if (slot)
+			atp = slot_getattr(slot, cur_key->sk_attno, &isnull);
+		else
+			atp = heap_getattr(tuple, cur_key->sk_attno, tupdesc, &isnull);
 
 		if (isnull)
 			return false;
-- 
2.50.1 (Apple Git-155)

