diff options
-rw-r--r-- | src/backend/access/nbtree/nbtsearch.c | 8 | ||||
-rw-r--r-- | src/backend/access/nbtree/nbtutils.c | 42 |
2 files changed, 39 insertions, 11 deletions
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c index 77264ddeecb..fe9a3886913 100644 --- a/src/backend/access/nbtree/nbtsearch.c +++ b/src/backend/access/nbtree/nbtsearch.c @@ -1790,9 +1790,13 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum, IndexTuple itup = (IndexTuple) PageGetItem(page, iid); int truncatt; - truncatt = BTreeTupleGetNAtts(itup, rel); + /* Reset arrays, per _bt_set_startikey contract */ + if (pstate.forcenonrequired) + _bt_start_array_keys(scan, dir); pstate.forcenonrequired = false; pstate.startikey = 0; /* _bt_set_startikey ignores P_HIKEY */ + + truncatt = BTreeTupleGetNAtts(itup, rel); _bt_checkkeys(scan, &pstate, arrayKeys, itup, truncatt); } @@ -1879,8 +1883,10 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum, pstate.offnum = offnum; if (arrayKeys && offnum == minoff && pstate.forcenonrequired) { + /* Reset arrays, per _bt_set_startikey contract */ pstate.forcenonrequired = false; pstate.startikey = 0; + _bt_start_array_keys(scan, dir); } passes_quals = _bt_checkkeys(scan, &pstate, arrayKeys, itup, indnatts); diff --git a/src/backend/access/nbtree/nbtutils.c b/src/backend/access/nbtree/nbtutils.c index c580c2bf527..1a15dfcb7d3 100644 --- a/src/backend/access/nbtree/nbtutils.c +++ b/src/backend/access/nbtree/nbtutils.c @@ -2489,13 +2489,14 @@ _bt_oppodir_checkkeys(IndexScanDesc scan, ScanDirection dir, * primscan's first page would mislead _bt_advance_array_keys, which expects * pstate.nskipadvances to be representative of every first page's key space.) * - * Caller must reset startikey and forcenonrequired ahead of the _bt_checkkeys - * call for pstate.finaltup iff we set forcenonrequired=true. This will give - * _bt_checkkeys the opportunity to call _bt_advance_array_keys once more, - * with sktrig_required=true, to advance the arrays that were ignored during - * checks of all of the page's prior tuples. Caller doesn't need to do this - * on the rightmost/leftmost page in the index (where pstate.finaltup isn't - * set), since forcenonrequired won't be set here by us in the first place. + * Caller must call _bt_start_array_keys and reset startikey/forcenonrequired + * ahead of the finaltup _bt_checkkeys call when we set forcenonrequired=true. + * This will give _bt_checkkeys the opportunity to call _bt_advance_array_keys + * with sktrig_required=true, restoring the invariant that the scan's required + * arrays always track the scan's progress through the index's key space. + * Caller won't need to do this on the rightmost/leftmost page in the index + * (where pstate.finaltup isn't ever set), since forcenonrequired will never + * be set here in the first place. */ void _bt_set_startikey(IndexScanDesc scan, BTReadPageState *pstate) @@ -2556,10 +2557,31 @@ _bt_set_startikey(IndexScanDesc scan, BTReadPageState *pstate) if (key->sk_flags & SK_ROW_HEADER) { /* - * Can't let pstate.startikey get set to an ikey beyond a - * RowCompare inequality + * RowCompare inequality. + * + * Only the first subkey from a RowCompare can ever be marked + * required (that happens when the row header is marked required). + * There is no simple, general way for us to transitively deduce + * whether or not every tuple on the page satisfies a RowCompare + * key based only on firsttup and lasttup -- so we just give up. */ - break; /* unsafe */ + if (!start_past_saop_eq && !so->skipScan) + break; /* unsafe to go further */ + + /* + * We have to be even more careful with RowCompares that come + * after an array: we assume it's unsafe to even bypass the array. + * Calling _bt_start_array_keys to recover the scan's arrays + * following use of forcenonrequired mode isn't compatible with + * _bt_check_rowcompare's continuescan=false behavior with NULL + * row compare members. _bt_advance_array_keys must not make a + * decision on the basis of a key not being satisfied in the + * opposite-to-scan direction until the scan reaches a leaf page + * where the same key begins to be satisfied in scan direction. + * The _bt_first !used_all_subkeys behavior makes this limitation + * hard to work around some other way. + */ + return; /* completely unsafe to set pstate.startikey */ } if (key->sk_strategy != BTEqualStrategyNumber) { |