| +loop_top_fwd: | <> | +loop_top_bwd: | 
| + /* load items[] in ascending order */ | + /* load items[] in descending order */ | |
| + itemIndex = 0; | + itemIndex = MaxIndexTuplesPerPage; | |
| + | = | + | 
| + /* new page, locate starting position by binary search */ | + /* new page, locate starting position by binary search */ | |
| + offnum = _hash_binsearch(page, so->hashso_sk_hash); | <> | + offnum = _hash_binsearch_last(page, so->hashso_sk_hash); | 
| + | = | + | 
| + while (offnum <= maxoff) | <> | + while (offnum >= FirstOffsetNumber) | 
| + { | = | + { | 
| + Assert(offnum >= FirstOffsetNumber); | <> | + Assert(offnum <= maxoff); | 
| + itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum)); | = | + itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum)); | 
| + | + | |
| + /* | + /* | |
| + * skip the tuples that are moved by split operation | + * skip the tuples that are moved by split operation | |
| + * for the scan that has started when split was in | + * for the scan that has started when split was in | |
| + * progress. Also, skip the tuples that are marked | + * progress. Also, skip the tuples that are marked | |
| + * as dead. | + * as dead. | |
| + */ | + */ | |
| + if ((so->hashso_buc_populated && !so->hashso_buc_split && | + if ((so->hashso_buc_populated && !so->hashso_buc_split && | |
| + (itup->t_info & INDEX_MOVED_BY_SPLIT_MASK)) || | + (itup->t_info & INDEX_MOVED_BY_SPLIT_MASK)) || | |
| + (scan->ignore_killed_tuples && | + (scan->ignore_killed_tuples && | |
| + (ItemIdIsDead(PageGetItemId(page, offnum))))) | + (ItemIdIsDead(PageGetItemId(page, offnum))))) | |
| + { | + { | |
| + offnum = OffsetNumberNext(offnum); /* move forward */ | <> | + offnum = OffsetNumberPrev(offnum); /* move back */ | 
| + continue; | = | + continue; | 
| + } | + } | |
| + | + | |
| + if (so->hashso_sk_hash == _hash_get_indextuple_hashkey(itup) && | + if (so->hashso_sk_hash == _hash_get_indextuple_hashkey(itup) && | |
| + _hash_checkqual(scan, itup)) | + _hash_checkqual(scan, itup)) | |
| + { | + { | |
| -+ | + itemIndex--; | |
| + /* tuple is qualified, so remember it */ | = | + /* tuple is qualified, so remember it */ | 
| + _hash_saveitem(so, itemIndex, offnum, itup); | + _hash_saveitem(so, itemIndex, offnum, itup); | |
| + itemIndex++; | +- | |
| + } | = | + } | 
| + | + | |
| + offnum = OffsetNumberNext(offnum); | <> | + offnum = OffsetNumberPrev(offnum); | 
| + } | = | + } | 
| + | + | |
| + Assert(itemIndex <= MaxIndexTuplesPerPage); | <> | + Assert(itemIndex >= 0); | 
| + | = | + | 
| + if (itemIndex == 0) | <> | + if (itemIndex == MaxIndexTuplesPerPage) | 
| + { | = | + { | 
| + /* | + /* | |
| + * Could not find any matching tuples in the current page, move | + * Could not find any matching tuples in the current page, move | |
| + * to the next page. Before leaving the current page, also deal | <> | + * to the prev page. Before leaving the current page, also deal | 
| + * with any killed items. | = | + * with any killed items. | 
| + */ | + */ | |
| + if (so->numKilled > 0) | + if (so->numKilled > 0) | |
| + _hash_kill_items(scan); | + _hash_kill_items(scan); | |
| + | + | |
| + _hash_readnext(scan, &buf, &page, &opaque); | <> | + _hash_readprev(scan, &buf, &page, &opaque); | 
| + if (BufferIsValid(buf)) | = | + if (BufferIsValid(buf)) | 
| + { | + { | |
| + so->currPos.buf = buf; | + so->currPos.buf = buf; | |
| + so->currPos.currPage = BufferGetBlockNumber(buf); | + so->currPos.currPage = BufferGetBlockNumber(buf); | |
| + maxoff = PageGetMaxOffsetNumber(page); | + maxoff = PageGetMaxOffsetNumber(page); | |
| + offnum = _hash_binsearch(page, so->hashso_sk_hash); | <> | + offnum = _hash_binsearch_last(page, so->hashso_sk_hash); | 
| + goto loop_top_fwd; | + goto loop_top_bwd; | |
| + } | = | + } | 
| + else | + else | |
| + return false; | + return false; | |
| + } | + } | |
| + else | + else | |
| + { | + { | |
| + if (so->currPos.buf == so->hashso_bucket_buf || | + if (so->currPos.buf == so->hashso_bucket_buf || | |
| + so->currPos.buf == so->hashso_split_bucket_buf) | + so->currPos.buf == so->hashso_split_bucket_buf) | |
| + LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK); | + LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK); | |
| + else | + else | |
| + _hash_relbuf(rel, so->currPos.buf); | + _hash_relbuf(rel, so->currPos.buf); | |
| + | <> | |
| + so->currPos.nextPage = (opaque)->hasho_nextblkno; | + so->currPos.prevPage = (opaque)->hasho_prevblkno; | |
| + } | = | + } | 
| + | + | |
| + so->currPos.firstItem = 0; | <> | + so->currPos.firstItem = itemIndex; | 
| + so->currPos.lastItem = itemIndex - 1; | + so->currPos.lastItem = MaxIndexTuplesPerPage - 1; | |
| + so->currPos.itemIndex = 0; | + so->currPos.itemIndex = MaxIndexTuplesPerPage - 1; | |
| + } | = | + } |