From 33e4e0a6e33814c5109418c1a193aba1fa935002 Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@bowt.ie>
Date: Sun, 26 Jul 2026 14:05:19 -0400
Subject: [PATCH v1] Unset SK_SEARCHARRAY when inequality arrays degenerate.

Preprocessing reliably transforms an inequality SK_SEARCHARRAY scan key
into a simple scalar comparison against the array's most restrictive
element.  The transformed scan key nevertheless retained its
SK_SEARCHARRAY marking, forcing code that deals with the scan's arrays
at runtime to test the equality strategy alongside the flag (there is
never a backing array for a transformed inequality key).

Unset SK_SEARCHARRAY when _bt_preprocess_array_keys performs the
transformation, establishing the invariant that SK_SEARCHARRAY keys
seen after that point always use the equality strategy.  Simplify the
places that tested both the flag and the strategy, retaining assertions
that verify the invariant instead.
---
 src/backend/access/nbtree/nbtpreprocesskeys.c | 27 ++++++++--------
 src/backend/access/nbtree/nbtreadpage.c       | 31 +++++++++----------
 2 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/src/backend/access/nbtree/nbtpreprocesskeys.c b/src/backend/access/nbtree/nbtpreprocesskeys.c
index 39c0a5d61..43bee38d9 100644
--- a/src/backend/access/nbtree/nbtpreprocesskeys.c
+++ b/src/backend/access/nbtree/nbtpreprocesskeys.c
@@ -286,9 +286,11 @@ _bt_preprocess_keys(IndexScanDesc scan)
 			 * (we'll miss out on the single value array transformation, but
 			 * that's not nearly as important when there's only one scan key)
 			 */
-			Assert(so->keyData[0].sk_flags & SK_SEARCHARRAY);
+			Assert(so->keyData[0].sk_strategy == BTEqualStrategyNumber ||
+				   !(so->keyData[0].sk_flags & SK_SEARCHARRAY));
 			Assert(so->keyData[0].sk_strategy != BTEqualStrategyNumber ||
-				   (so->arrayKeys[0].scan_key == 0 &&
+				   ((so->keyData[0].sk_flags & SK_SEARCHARRAY) &&
+					so->arrayKeys[0].scan_key == 0 &&
 					!(so->keyData[0].sk_flags & SK_BT_SKIP) &&
 					OidIsValid(so->orderProcs[0].fn_oid)));
 		}
@@ -493,10 +495,10 @@ _bt_preprocess_keys(IndexScanDesc scan)
 		/* check strategy this key's operator corresponds to */
 		j = inkey->sk_strategy - 1;
 
-		if (inkey->sk_strategy == BTEqualStrategyNumber &&
-			(inkey->sk_flags & SK_SEARCHARRAY))
+		if (inkey->sk_flags & SK_SEARCHARRAY)
 		{
-			/* must track how input scan keys map to arrays */
+			/* must track how input = scan keys map to arrays */
+			Assert(inkey->sk_strategy == BTEqualStrategyNumber);
 			Assert(arrayKeyData);
 			arrayidx++;
 		}
@@ -975,10 +977,8 @@ _bt_compare_scankey_args(IndexScanDesc scan, ScanKey op,
 		bool		leftarray,
 					rightarray;
 
-		leftarray = ((leftarg->sk_flags & SK_SEARCHARRAY) &&
-					 leftarg->sk_strategy == BTEqualStrategyNumber);
-		rightarray = ((rightarg->sk_flags & SK_SEARCHARRAY) &&
-					  rightarg->sk_strategy == BTEqualStrategyNumber);
+		leftarray = (leftarg->sk_flags & SK_SEARCHARRAY) != 0;
+		rightarray = (rightarg->sk_flags & SK_SEARCHARRAY) != 0;
 
 		/*
 		 * _bt_preprocess_array_keys is responsible for merging together array
@@ -1102,10 +1102,9 @@ _bt_compare_array_scankey_args(IndexScanDesc scan, ScanKey arraysk, ScanKey skey
 	Assert(!(arraysk->sk_flags & (SK_ISNULL | SK_ROW_HEADER | SK_ROW_MEMBER)));
 	Assert((arraysk->sk_flags & SK_SEARCHARRAY) &&
 		   arraysk->sk_strategy == BTEqualStrategyNumber);
-	/* don't expect to have to deal with NULLs/row comparison scan keys */
-	Assert(!(skey->sk_flags & (SK_ISNULL | SK_ROW_HEADER | SK_ROW_MEMBER)));
-	Assert(!(skey->sk_flags & SK_SEARCHARRAY) ||
-		   skey->sk_strategy != BTEqualStrategyNumber);
+	/* don't expect to have to deal with array/IS NULL/row compare skey */
+	Assert(!(skey->sk_flags &
+			 (SK_SEARCHARRAY | SK_ISNULL | SK_ROW_HEADER | SK_ROW_MEMBER)));
 
 	/*
 	 * Just call the appropriate helper function based on whether it's a SAOP
@@ -2078,6 +2077,7 @@ _bt_preprocess_array_keys(IndexScanDesc scan, int *new_numberOfKeys)
 					_bt_find_extreme_element(scan, cur, elemtype,
 											 BTGreaterStrategyNumber,
 											 elem_values, num_nonnulls);
+				cur->sk_flags &= ~SK_SEARCHARRAY;
 				numArrayKeyData++;	/* keep this transformed scan key */
 				continue;
 			case BTEqualStrategyNumber:
@@ -2089,6 +2089,7 @@ _bt_preprocess_array_keys(IndexScanDesc scan, int *new_numberOfKeys)
 					_bt_find_extreme_element(scan, cur, elemtype,
 											 BTLessStrategyNumber,
 											 elem_values, num_nonnulls);
+				cur->sk_flags &= ~SK_SEARCHARRAY;
 				numArrayKeyData++;	/* keep this transformed scan key */
 				continue;
 			default:
diff --git a/src/backend/access/nbtree/nbtreadpage.c b/src/backend/access/nbtree/nbtreadpage.c
index 448a51412..003e67cc3 100644
--- a/src/backend/access/nbtree/nbtreadpage.c
+++ b/src/backend/access/nbtree/nbtreadpage.c
@@ -548,6 +548,7 @@ _bt_start_array_keys(IndexScanDesc scan, ScanDirection dir)
 		ScanKey		skey = &so->keyData[array->scan_key];
 
 		Assert(skey->sk_flags & SK_SEARCHARRAY);
+		Assert(skey->sk_strategy == BTEqualStrategyNumber);
 
 		_bt_array_set_low_or_high(rel, skey, array,
 								  ScanDirectionIsForward(dir));
@@ -1501,14 +1502,12 @@ _bt_check_compare(IndexScanDesc scan, ScanDirection dir,
 				*continuescan = false;
 
 			/*
-			 * If this is a non-required equality-type array key, the tuple
-			 * needs to be checked against every possible array key.  Handle
-			 * this by "advancing" the scan key's array to a matching value
-			 * (if we're successful then the tuple might match the qual).
+			 * If this is a non-required array key, the tuple needs to be
+			 * checked against every possible array key.  Handle this by
+			 * "advancing" the scan key's array to a matching value (if we're
+			 * successful then the tuple might match the qual).
 			 */
-			else if (advancenonrequired &&
-					 key->sk_strategy == BTEqualStrategyNumber &&
-					 (key->sk_flags & SK_SEARCHARRAY))
+			else if (advancenonrequired && (key->sk_flags & SK_SEARCHARRAY))
 				return _bt_advance_array_keys(scan, NULL, tuple, tupnatts,
 											  tupdesc, *ikey, false);
 
@@ -2242,16 +2241,14 @@ _bt_advance_array_keys(IndexScanDesc scan, BTReadPageState *pstate,
 		int32		result;
 		int			set_elem = 0;
 
-		if (cur->sk_strategy == BTEqualStrategyNumber)
+		if (cur->sk_flags & SK_SEARCHARRAY)
 		{
 			/* Manage array state */
-			if (cur->sk_flags & SK_SEARCHARRAY)
-			{
-				array = &so->arrayKeys[arrayidx++];
-				Assert(array->scan_key == ikey);
-			}
+			Assert(cur->sk_strategy == BTEqualStrategyNumber);
+			array = &so->arrayKeys[arrayidx++];
+			Assert(array->scan_key == ikey);
 		}
-		else
+		else if (cur->sk_strategy != BTEqualStrategyNumber)
 		{
 			/*
 			 * Are any inequalities required in the opposite direction only
@@ -3683,10 +3680,12 @@ _bt_verify_keys_with_arraykeys(IndexScanDesc scan)
 		ScanKey		cur = so->keyData + ikey;
 		BTArrayKeyInfo *array;
 
-		if (cur->sk_strategy != BTEqualStrategyNumber ||
-			!(cur->sk_flags & SK_SEARCHARRAY))
+		if (!(cur->sk_flags & SK_SEARCHARRAY))
 			continue;
 
+		if (cur->sk_strategy != BTEqualStrategyNumber)
+			return false;
+
 		array = &so->arrayKeys[arrayidx++];
 		if (array->scan_key != ikey)
 			return false;
-- 
2.53.0

