From 33e4e0a6e33814c5109418c1a193aba1fa935002 Mon Sep 17 00:00:00 2001 From: Peter Geoghegan 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