From 1f1e5f5a1a20ea2ed028a543688b31781956581b Mon Sep 17 00:00:00 2001
From: Alexander Korotkov <akorotkov@postgresql.org>
Date: Mon, 11 Dec 2023 17:49:05 +0200
Subject: [PATCH 2/2] Fix requiredOppositeDir bug

Instead of relying on _bt_first(), rely on the first matched item on the page.

Reported-by:
Bug:
Discussion:
Author:
Reviewed-by:
Tested-by:
Backpatch-through:
---
 src/backend/access/nbtree/nbtsearch.c | 13 +++++++------
 src/backend/access/nbtree/nbtutils.c  |  6 ++++--
 src/include/access/nbtree.h           |  2 +-
 3 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index 410180587e0..7bdd6bf11c4 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -1541,6 +1541,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum,
 	bool		continuescan;
 	int			indnatts;
 	bool		requiredMatchedByPrecheck;
+	bool		haveFirstMatch = false;
 
 	/*
 	 * We must have the buffer pinned and locked, but the usual macro can't be
@@ -1626,7 +1627,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum,
 		 * otherwise.
 		 */
 		(void) _bt_checkkeys(scan, itup, indnatts, dir,
-							 &requiredMatchedByPrecheck, false);
+							 &requiredMatchedByPrecheck, false, NULL);
 	}
 	else
 	{
@@ -1659,7 +1660,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum,
 			itup = (IndexTuple) PageGetItem(page, iid);
 
 			passes_quals = _bt_checkkeys(scan, itup, indnatts, dir,
-										 &continuescan, requiredMatchedByPrecheck);
+										 &continuescan, requiredMatchedByPrecheck, &haveFirstMatch);
 
 			/*
 			 * If the result of prechecking required keys was true, then in
@@ -1668,7 +1669,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum,
 			 */
 			Assert(!requiredMatchedByPrecheck ||
 				   passes_quals == _bt_checkkeys(scan, itup, indnatts, dir,
-												 &continuescan, false));
+												 &continuescan, false, NULL));
 			if (passes_quals)
 			{
 				/* tuple passes all scan key conditions */
@@ -1726,7 +1727,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum,
 			int			truncatt;
 
 			truncatt = BTreeTupleGetNAtts(itup, scan->indexRelation);
-			_bt_checkkeys(scan, itup, truncatt, dir, &continuescan, false);
+			_bt_checkkeys(scan, itup, truncatt, dir, &continuescan, false, NULL);
 		}
 
 		if (!continuescan)
@@ -1778,7 +1779,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum,
 			itup = (IndexTuple) PageGetItem(page, iid);
 
 			passes_quals = _bt_checkkeys(scan, itup, indnatts, dir,
-										 &continuescan, requiredMatchedByPrecheck);
+										 &continuescan, requiredMatchedByPrecheck, &haveFirstMatch);
 
 			/*
 			 * If the result of prechecking required keys was true, then in
@@ -1787,7 +1788,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum,
 			 */
 			Assert(!requiredMatchedByPrecheck ||
 				   passes_quals == _bt_checkkeys(scan, itup, indnatts, dir,
-												 &continuescan, false));
+												 &continuescan, false, NULL));
 			if (passes_quals && tuple_alive)
 			{
 				/* tuple passes all scan key conditions */
diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c
index 1510b97fbe1..0a8e94473ba 100644
--- a/src/backend/access/nbtree/nbtutils.c
+++ b/src/backend/access/nbtree/nbtutils.c
@@ -1378,7 +1378,7 @@ _bt_mark_scankey_required(ScanKey skey)
 bool
 _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple, int tupnatts,
 			  ScanDirection dir, bool *continuescan,
-			  bool requiredMatchedByPrecheck)
+			  bool requiredMatchedByPrecheck, bool *haveFirstMatch)
 {
 	TupleDesc	tupdesc;
 	BTScanOpaque so;
@@ -1411,7 +1411,7 @@ _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple, int tupnatts,
 			requiredSameDir = true;
 		else if (((key->sk_flags & SK_BT_REQFWD) && ScanDirectionIsBackward(dir)) ||
 				 ((key->sk_flags & SK_BT_REQBKWD) && ScanDirectionIsForward(dir)))
-			requiredOppositeDir = true;
+			requiredOppositeDir = haveFirstMatch && *haveFirstMatch;
 
 		/*
 		 * Is the key required for scanning for either forward or backward
@@ -1561,6 +1561,8 @@ _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple, int tupnatts,
 	}
 
 	/* If we get here, the tuple passes all index quals. */
+	if (haveFirstMatch)
+		*haveFirstMatch = true;
 	return true;
 }
 
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 2e33523b08a..7704a1dc709 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -1256,7 +1256,7 @@ extern void _bt_restore_array_keys(IndexScanDesc scan);
 extern void _bt_preprocess_keys(IndexScanDesc scan);
 extern bool _bt_checkkeys(IndexScanDesc scan, IndexTuple tuple,
 						  int tupnatts, ScanDirection dir, bool *continuescan,
-						  bool requiredMatchedByPrecheck);
+						  bool requiredMatchedByPrecheck, bool *haveFirstMatch);
 extern void _bt_killitems(IndexScanDesc scan);
 extern BTCycleId _bt_vacuum_cycleid(Relation rel);
 extern BTCycleId _bt_start_vacuum(Relation rel);
-- 
2.39.3 (Apple Git-145)

