=== Applying patches on top of PostgreSQL commit ID 5092aae431e3e1a20324ea3a42a181c63f703d0d === /etc/rc.d/jail: WARNING: Per-jail configuration via jail_* variables is obsolete. Please consider migrating to /etc/jail.conf. Sat Oct 4 06:39:19 UTC 2025 On branch cf/5973 nothing to commit, working tree clean === using 'git am' to apply patch ./v1-0001-Clarify-comment-in-_bt_set_startikey.patch === Applying: Clarify comment in _bt_set_startikey Using index info to reconstruct a base tree... M src/backend/access/nbtree/nbtutils.c Falling back to patching base and 3-way merge... Auto-merging src/backend/access/nbtree/nbtutils.c CONFLICT (content): Merge conflict in src/backend/access/nbtree/nbtutils.c error: Failed to merge in the changes. hint: Use 'git am --show-current-patch=diff' to see the failed patch Patch failed at 0001 Clarify comment in _bt_set_startikey When you have resolved this problem, run "git am --continue". If you prefer to skip this patch, run "git am --skip" instead. To restore the original branch and stop patching, run "git am --abort". Unstaged changes after reset: M src/backend/access/nbtree/nbtutils.c === using patch(1) to apply patch ./v1-0001-Clarify-comment-in-_bt_set_startikey.patch === patching file src/backend/access/nbtree/nbtutils.c Hunk #1 FAILED at 2432. 1 out of 1 hunk FAILED -- saving rejects to file src/backend/access/nbtree/nbtutils.c.rej Removing src/backend/access/nbtree/nbtutils.c.rej === using 'git apply' to apply patch ./v1-0001-Clarify-comment-in-_bt_set_startikey.patch === Applied patch to 'src/backend/access/nbtree/nbtutils.c' with conflicts. U src/backend/access/nbtree/nbtutils.c diff --cc src/backend/access/nbtree/nbtutils.c index 41b4fbd1c37,83bb4b80491..00000000000 --- a/src/backend/access/nbtree/nbtutils.c +++ b/src/backend/access/nbtree/nbtutils.c @@@ -2436,106 -2432,21 +2436,120 @@@ _bt_set_startikey(IndexScanDesc scan, B int32 result; /* ++<<<<<<< ours + * Determine if it's safe to set pstate.startikey to an offset to a + * key that comes after this key, by examining this key + */ ++======= + * Determine whether we can set pstate.startikey to a later key offset, + * ensuring that all earlier scan keys are satisfied by every tuple on + * the current page. This is done by comparing the key against the first + * and last tuples on the page. + */ + + if (!(key->sk_flags & (SK_BT_REQFWD | SK_BT_REQBKWD))) + { + /* Scan key isn't marked required (corner case) */ + break; /* unsafe */ + } ++>>>>>>> theirs if (key->sk_flags & SK_ROW_HEADER) { - /* RowCompare inequalities currently aren't supported */ - break; /* "unsafe" */ + /* RowCompare inequality (header key) */ + ScanKey subkey = (ScanKey) DatumGetPointer(key->sk_argument); + bool satisfied = false; + + for (;;) + { + int cmpresult; + bool firstsatisfies = false; + + if (subkey->sk_attno > firstchangingattnum) /* >, not >= */ + break; /* unsafe, preceding attr has multiple + * distinct values */ + + if (subkey->sk_flags & SK_ISNULL) + break; /* unsafe, unsatisfiable NULL subkey arg */ + + firstdatum = index_getattr(firsttup, subkey->sk_attno, + tupdesc, &firstnull); + lastdatum = index_getattr(lasttup, subkey->sk_attno, + tupdesc, &lastnull); + + if (firstnull || lastnull) + break; /* unsafe, NULL value won't satisfy subkey */ + + /* + * Compare the first tuple's datum for this row compare member + */ + cmpresult = DatumGetInt32(FunctionCall2Coll(&subkey->sk_func, + subkey->sk_collation, + firstdatum, + subkey->sk_argument)); + if (subkey->sk_flags & SK_BT_DESC) + INVERT_COMPARE_RESULT(cmpresult); + + if (cmpresult != 0 || (subkey->sk_flags & SK_ROW_END)) + { + firstsatisfies = _bt_rowcompare_cmpresult(subkey, + cmpresult); + if (!firstsatisfies) + { + /* Unsafe, firstdatum does not satisfy subkey */ + break; + } + } + + /* + * Compare the last tuple's datum for this row compare member + */ + cmpresult = DatumGetInt32(FunctionCall2Coll(&subkey->sk_func, + subkey->sk_collation, + lastdatum, + subkey->sk_argument)); + if (subkey->sk_flags & SK_BT_DESC) + INVERT_COMPARE_RESULT(cmpresult); + + if (cmpresult != 0 || (subkey->sk_flags & SK_ROW_END)) + { + if (!firstsatisfies) + { + /* + * It's only safe to set startikey beyond the row + * compare header key when both firsttup and lasttup + * satisfy the key as a whole based on the same + * deciding subkey/attribute. That can't happen now. + */ + break; /* unsafe */ + } + + satisfied = _bt_rowcompare_cmpresult(subkey, cmpresult); + break; /* safe iff 'satisfied' is true */ + } + + /* Move on to next row member/subkey */ + if (subkey->sk_flags & SK_ROW_END) + break; /* defensive */ + subkey++; + + /* + * We deliberately don't check if the next subkey has the same + * strategy as this iteration's subkey (which happens when + * subkeys for both ASC and DESC columns are used together), + * nor if any subkey is marked required. This is safe because + * in general all prior index attributes must have only one + * distinct value (across all of the tuples on the page) in + * order for us to even consider any subkey's attribute. + */ + } + + if (satisfied) + { + /* Safe, row compare satisfied by every tuple on page */ + continue; + } + + break; /* unsafe */ } if (key->sk_strategy != BTEqualStrategyNumber) {