PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
hashsearch.c File Reference
#include "postgres.h"
#include "access/hash.h"
#include "access/relscan.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "storage/predicate.h"
#include "utils/rel.h"
Include dependency graph for hashsearch.c:

Go to the source code of this file.

Functions

static bool _hash_readpage (IndexScanDesc scan, Buffer *bufP, ScanDirection dir)
 
static int _hash_load_qualified_items (IndexScanDesc scan, Page page, OffsetNumber offnum, ScanDirection dir)
 
static void _hash_saveitem (HashScanOpaque so, int itemIndex, OffsetNumber offnum, IndexTuple itup)
 
static void _hash_readnext (IndexScanDesc scan, Buffer *bufp, Page *pagep, HashPageOpaque *opaquep)
 
bool _hash_next (IndexScanDesc scan, ScanDirection dir)
 
static void _hash_readprev (IndexScanDesc scan, Buffer *bufp, Page *pagep, HashPageOpaque *opaquep)
 
bool _hash_first (IndexScanDesc scan, ScanDirection dir)
 

Function Documentation

◆ _hash_first()

bool _hash_first ( IndexScanDesc  scan,
ScanDirection  dir 
)

Definition at line 288 of file hashsearch.c.

289{
290 Relation rel = scan->indexRelation;
292 ScanKey cur;
293 uint32 hashkey;
294 Bucket bucket;
295 Buffer buf;
296 Page page;
297 HashPageOpaque opaque;
298 HashScanPosItem *currItem;
299
301 if (scan->instrument)
302 scan->instrument->nsearches++;
303
304 /*
305 * We do not support hash scans with no index qualification, because we
306 * would have to read the whole index rather than just one bucket. That
307 * creates a whole raft of problems, since we haven't got a practical way
308 * to lock all the buckets against splits or compactions.
309 */
310 if (scan->numberOfKeys < 1)
312 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
313 errmsg("hash indexes do not support whole-index scans")));
314
315 /* There may be more than one index qual, but we hash only the first */
316 cur = &scan->keyData[0];
317
318 /* We support only single-column hash indexes */
319 Assert(cur->sk_attno == 1);
320 /* And there's only one operator strategy, too */
321 Assert(cur->sk_strategy == HTEqualStrategyNumber);
322
323 /*
324 * If the constant in the index qual is NULL, assume it cannot match any
325 * items in the index.
326 */
327 if (cur->sk_flags & SK_ISNULL)
328 return false;
329
330 /*
331 * Okay to compute the hash key. We want to do this before acquiring any
332 * locks, in case a user-defined hash function happens to be slow.
333 *
334 * If scankey operator is not a cross-type comparison, we can use the
335 * cached hash function; otherwise gotta look it up in the catalogs.
336 *
337 * We support the convention that sk_subtype == InvalidOid means the
338 * opclass input type; this is a hack to simplify life for ScanKeyInit().
339 */
340 if (cur->sk_subtype == rel->rd_opcintype[0] ||
341 cur->sk_subtype == InvalidOid)
342 hashkey = _hash_datum2hashkey(rel, cur->sk_argument);
343 else
344 hashkey = _hash_datum2hashkey_type(rel, cur->sk_argument,
345 cur->sk_subtype);
346
347 so->hashso_sk_hash = hashkey;
348
349 buf = _hash_getbucketbuf_from_hashkey(rel, hashkey, HASH_READ, NULL);
351 page = BufferGetPage(buf);
352 opaque = HashPageGetOpaque(page);
353 bucket = opaque->hasho_bucket;
354
356
357 /*
358 * If a bucket split is in progress, then while scanning the bucket being
359 * populated, we need to skip tuples that were copied from bucket being
360 * split. We also need to maintain a pin on the bucket being split to
361 * ensure that split-cleanup work done by vacuum doesn't remove tuples
362 * from it till this scan is done. We need to maintain a pin on the
363 * bucket being populated to ensure that vacuum doesn't squeeze that
364 * bucket till this scan is complete; otherwise, the ordering of tuples
365 * can't be maintained during forward and backward scans. Here, we have
366 * to be cautious about locking order: first, acquire the lock on bucket
367 * being split; then, release the lock on it but not the pin; then,
368 * acquire a lock on bucket being populated and again re-verify whether
369 * the bucket split is still in progress. Acquiring the lock on bucket
370 * being split first ensures that the vacuum waits for this scan to
371 * finish.
372 */
373 if (H_BUCKET_BEING_POPULATED(opaque))
374 {
375 BlockNumber old_blkno;
376 Buffer old_buf;
377
378 old_blkno = _hash_get_oldblock_from_newbucket(rel, bucket);
379
380 /*
381 * release the lock on new bucket and re-acquire it after acquiring
382 * the lock on old bucket.
383 */
385
386 old_buf = _hash_getbuf(rel, old_blkno, HASH_READ, LH_BUCKET_PAGE);
387
388 /*
389 * remember the split bucket buffer so as to use it later for
390 * scanning.
391 */
392 so->hashso_split_bucket_buf = old_buf;
394
396 page = BufferGetPage(buf);
397 opaque = HashPageGetOpaque(page);
398 Assert(opaque->hasho_bucket == bucket);
399
400 if (H_BUCKET_BEING_POPULATED(opaque))
401 so->hashso_buc_populated = true;
402 else
403 {
406 }
407 }
408
409 /* If a backwards scan is requested, move to the end of the chain */
411 {
412 /*
413 * Backward scans that start during split needs to start from end of
414 * bucket being split.
415 */
416 while (BlockNumberIsValid(opaque->hasho_nextblkno) ||
418 _hash_readnext(scan, &buf, &page, &opaque);
419 }
420
421 /* remember which buffer we have pinned, if any */
423 so->currPos.buf = buf;
424
425 /* Now find all the tuples satisfying the qualification from a page */
426 if (!_hash_readpage(scan, &buf, dir))
427 return false;
428
429 /* OK, itemIndex says what to return */
430 currItem = &so->currPos.items[so->currPos.itemIndex];
431 scan->xs_heaptid = currItem->heapTid;
432
433 /* if we're here, _hash_readpage found a valid tuples */
434 return true;
435}
uint32 BlockNumber
Definition: block.h:31
static bool BlockNumberIsValid(BlockNumber blockNumber)
Definition: block.h:71
int Buffer
Definition: buf.h:23
#define BufferIsInvalid(buffer)
Definition: buf.h:31
#define InvalidBuffer
Definition: buf.h:25
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:4231
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:5607
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:196
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:197
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:417
PageData * Page
Definition: bufpage.h:82
uint32_t uint32
Definition: c.h:502
struct cursor * cur
Definition: ecpg.c:29
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
#define HashPageGetOpaque(page)
Definition: hash.h:88
#define LH_BUCKET_PAGE
Definition: hash.h:55
#define HASH_READ
Definition: hash.h:339
#define H_BUCKET_BEING_POPULATED(opaque)
Definition: hash.h:92
uint32 Bucket
Definition: hash.h:35
HashScanOpaqueData * HashScanOpaque
Definition: hash.h:192
Assert(PointerIsAligned(start, uint64))
void _hash_dropbuf(Relation rel, Buffer buf)
Definition: hashpage.c:277
Buffer _hash_getbuf(Relation rel, BlockNumber blkno, int access, int flags)
Definition: hashpage.c:70
Buffer _hash_getbucketbuf_from_hashkey(Relation rel, uint32 hashkey, int access, HashMetaPage *cachedmetap)
Definition: hashpage.c:1559
static void _hash_readnext(IndexScanDesc scan, Buffer *bufp, Page *pagep, HashPageOpaque *opaquep)
Definition: hashsearch.c:131
static bool _hash_readpage(IndexScanDesc scan, Buffer *bufP, ScanDirection dir)
Definition: hashsearch.c:448
uint32 _hash_datum2hashkey(Relation rel, Datum key)
Definition: hashutil.c:82
BlockNumber _hash_get_oldblock_from_newbucket(Relation rel, Bucket new_bucket)
Definition: hashutil.c:422
uint32 _hash_datum2hashkey_type(Relation rel, Datum key, Oid keytype)
Definition: hashutil.c:102
static char * buf
Definition: pg_test_fsync.c:72
#define pgstat_count_index_scan(rel)
Definition: pgstat.h:694
#define InvalidOid
Definition: postgres_ext.h:35
void PredicateLockPage(Relation relation, BlockNumber blkno, Snapshot snapshot)
Definition: predicate.c:2599
#define ScanDirectionIsBackward(direction)
Definition: sdir.h:50
#define SK_ISNULL
Definition: skey.h:115
#define HTEqualStrategyNumber
Definition: stratnum.h:41
bool hashso_buc_split
Definition: hash.h:180
HashScanPosData currPos
Definition: hash.h:189
bool hashso_buc_populated
Definition: hash.h:174
Buffer hashso_split_bucket_buf
Definition: hash.h:171
Buffer hashso_bucket_buf
Definition: hash.h:164
uint32 hashso_sk_hash
Definition: hash.h:161
HashScanPosItem items[MaxIndexTuplesPerPage]
Definition: hash.h:127
Buffer buf
Definition: hash.h:111
int itemIndex
Definition: hash.h:125
struct ScanKeyData * keyData
Definition: relscan.h:141
struct IndexScanInstrumentation * instrument
Definition: relscan.h:159
Relation indexRelation
Definition: relscan.h:137
ItemPointerData xs_heaptid
Definition: relscan.h:172
struct SnapshotData * xs_snapshot
Definition: relscan.h:138
Oid * rd_opcintype
Definition: rel.h:208

References _hash_datum2hashkey(), _hash_datum2hashkey_type(), _hash_dropbuf(), _hash_get_oldblock_from_newbucket(), _hash_getbucketbuf_from_hashkey(), _hash_getbuf(), _hash_readnext(), _hash_readpage(), Assert(), BlockNumberIsValid(), buf, HashScanPosData::buf, BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferGetBlockNumber(), BufferGetPage(), BufferIsInvalid, cur, HashScanOpaqueData::currPos, ereport, errcode(), errmsg(), ERROR, H_BUCKET_BEING_POPULATED, HASH_READ, HashPageGetOpaque, HashScanOpaqueData::hashso_buc_populated, HashScanOpaqueData::hashso_buc_split, HashScanOpaqueData::hashso_bucket_buf, HashScanOpaqueData::hashso_sk_hash, HashScanOpaqueData::hashso_split_bucket_buf, HTEqualStrategyNumber, IndexScanDescData::indexRelation, IndexScanDescData::instrument, InvalidBuffer, InvalidOid, HashScanPosData::itemIndex, HashScanPosData::items, IndexScanDescData::keyData, LH_BUCKET_PAGE, LockBuffer(), IndexScanInstrumentation::nsearches, IndexScanDescData::numberOfKeys, IndexScanDescData::opaque, pgstat_count_index_scan, PredicateLockPage(), RelationData::rd_opcintype, ScanDirectionIsBackward, SK_ISNULL, IndexScanDescData::xs_heaptid, and IndexScanDescData::xs_snapshot.

Referenced by hashgetbitmap(), and hashgettuple().

◆ _hash_load_qualified_items()

static int _hash_load_qualified_items ( IndexScanDesc  scan,
Page  page,
OffsetNumber  offnum,
ScanDirection  dir 
)
static

Definition at line 604 of file hashsearch.c.

606{
608 IndexTuple itup;
609 int itemIndex;
610 OffsetNumber maxoff;
611
612 maxoff = PageGetMaxOffsetNumber(page);
613
614 if (ScanDirectionIsForward(dir))
615 {
616 /* load items[] in ascending order */
617 itemIndex = 0;
618
619 while (offnum <= maxoff)
620 {
621 Assert(offnum >= FirstOffsetNumber);
622 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
623
624 /*
625 * skip the tuples that are moved by split operation for the scan
626 * that has started when split was in progress. Also, skip the
627 * tuples that are marked as dead.
628 */
629 if ((so->hashso_buc_populated && !so->hashso_buc_split &&
630 (itup->t_info & INDEX_MOVED_BY_SPLIT_MASK)) ||
631 (scan->ignore_killed_tuples &&
632 (ItemIdIsDead(PageGetItemId(page, offnum)))))
633 {
634 offnum = OffsetNumberNext(offnum); /* move forward */
635 continue;
636 }
637
639 _hash_checkqual(scan, itup))
640 {
641 /* tuple is qualified, so remember it */
642 _hash_saveitem(so, itemIndex, offnum, itup);
643 itemIndex++;
644 }
645 else
646 {
647 /*
648 * No more matching tuples exist in this page. so, exit while
649 * loop.
650 */
651 break;
652 }
653
654 offnum = OffsetNumberNext(offnum);
655 }
656
657 Assert(itemIndex <= MaxIndexTuplesPerPage);
658 return itemIndex;
659 }
660 else
661 {
662 /* load items[] in descending order */
663 itemIndex = MaxIndexTuplesPerPage;
664
665 while (offnum >= FirstOffsetNumber)
666 {
667 Assert(offnum <= maxoff);
668 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
669
670 /*
671 * skip the tuples that are moved by split operation for the scan
672 * that has started when split was in progress. Also, skip the
673 * tuples that are marked as dead.
674 */
675 if ((so->hashso_buc_populated && !so->hashso_buc_split &&
676 (itup->t_info & INDEX_MOVED_BY_SPLIT_MASK)) ||
677 (scan->ignore_killed_tuples &&
678 (ItemIdIsDead(PageGetItemId(page, offnum)))))
679 {
680 offnum = OffsetNumberPrev(offnum); /* move back */
681 continue;
682 }
683
685 _hash_checkqual(scan, itup))
686 {
687 itemIndex--;
688 /* tuple is qualified, so remember it */
689 _hash_saveitem(so, itemIndex, offnum, itup);
690 }
691 else
692 {
693 /*
694 * No more matching tuples exist in this page. so, exit while
695 * loop.
696 */
697 break;
698 }
699
700 offnum = OffsetNumberPrev(offnum);
701 }
702
703 Assert(itemIndex >= 0);
704 return itemIndex;
705 }
706}
static Item PageGetItem(const PageData *page, const ItemIdData *itemId)
Definition: bufpage.h:354
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:244
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition: bufpage.h:372
#define INDEX_MOVED_BY_SPLIT_MASK
Definition: hash.h:293
static void _hash_saveitem(HashScanOpaque so, int itemIndex, OffsetNumber offnum, IndexTuple itup)
Definition: hashsearch.c:710
uint32 _hash_get_indextuple_hashkey(IndexTuple itup)
Definition: hashutil.c:291
bool _hash_checkqual(IndexScanDesc scan, IndexTuple itup)
Definition: hashutil.c:31
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
IndexTupleData * IndexTuple
Definition: itup.h:53
#define MaxIndexTuplesPerPage
Definition: itup.h:181
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
uint16 OffsetNumber
Definition: off.h:24
#define FirstOffsetNumber
Definition: off.h:27
#define OffsetNumberPrev(offsetNumber)
Definition: off.h:54
#define ScanDirectionIsForward(direction)
Definition: sdir.h:64
bool ignore_killed_tuples
Definition: relscan.h:148

References _hash_checkqual(), _hash_get_indextuple_hashkey(), _hash_saveitem(), Assert(), FirstOffsetNumber, HashScanOpaqueData::hashso_buc_populated, HashScanOpaqueData::hashso_buc_split, HashScanOpaqueData::hashso_sk_hash, IndexScanDescData::ignore_killed_tuples, INDEX_MOVED_BY_SPLIT_MASK, ItemIdIsDead, MaxIndexTuplesPerPage, OffsetNumberNext, OffsetNumberPrev, IndexScanDescData::opaque, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), and ScanDirectionIsForward.

Referenced by _hash_readpage().

◆ _hash_next()

bool _hash_next ( IndexScanDesc  scan,
ScanDirection  dir 
)

Definition at line 48 of file hashsearch.c.

49{
50 Relation rel = scan->indexRelation;
52 HashScanPosItem *currItem;
53 BlockNumber blkno;
54 Buffer buf;
55 bool end_of_scan = false;
56
57 /*
58 * Advance to the next tuple on the current page; or if done, try to read
59 * data from the next or previous page based on the scan direction. Before
60 * moving to the next or previous page make sure that we deal with all the
61 * killed items.
62 */
64 {
65 if (++so->currPos.itemIndex > so->currPos.lastItem)
66 {
67 if (so->numKilled > 0)
68 _hash_kill_items(scan);
69
70 blkno = so->currPos.nextPage;
71 if (BlockNumberIsValid(blkno))
72 {
74 if (!_hash_readpage(scan, &buf, dir))
75 end_of_scan = true;
76 }
77 else
78 end_of_scan = true;
79 }
80 }
81 else
82 {
83 if (--so->currPos.itemIndex < so->currPos.firstItem)
84 {
85 if (so->numKilled > 0)
86 _hash_kill_items(scan);
87
88 blkno = so->currPos.prevPage;
89 if (BlockNumberIsValid(blkno))
90 {
91 buf = _hash_getbuf(rel, blkno, HASH_READ,
93
94 /*
95 * We always maintain the pin on bucket page for whole scan
96 * operation, so releasing the additional pin we have acquired
97 * here.
98 */
99 if (buf == so->hashso_bucket_buf ||
101 _hash_dropbuf(rel, buf);
102
103 if (!_hash_readpage(scan, &buf, dir))
104 end_of_scan = true;
105 }
106 else
107 end_of_scan = true;
108 }
109 }
110
111 if (end_of_scan)
112 {
113 _hash_dropscanbuf(rel, so);
115 return false;
116 }
117
118 /* OK, itemIndex says what to return */
119 currItem = &so->currPos.items[so->currPos.itemIndex];
120 scan->xs_heaptid = currItem->heapTid;
121
122 return true;
123}
#define HashScanPosInvalidate(scanpos)
Definition: hash.h:144
#define LH_OVERFLOW_PAGE
Definition: hash.h:54
void _hash_dropscanbuf(Relation rel, HashScanOpaque so)
Definition: hashpage.c:289
void _hash_kill_items(IndexScanDesc scan)
Definition: hashutil.c:536
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
BlockNumber prevPage
Definition: hash.h:114
BlockNumber nextPage
Definition: hash.h:113
int lastItem
Definition: hash.h:124
int firstItem
Definition: hash.h:123

References _hash_dropbuf(), _hash_dropscanbuf(), _hash_getbuf(), _hash_kill_items(), _hash_readpage(), BlockNumberIsValid(), buf, HashScanOpaqueData::currPos, HashScanPosData::firstItem, HASH_READ, HashScanPosInvalidate, HashScanOpaqueData::hashso_bucket_buf, HashScanOpaqueData::hashso_split_bucket_buf, if(), IndexScanDescData::indexRelation, HashScanPosData::itemIndex, HashScanPosData::items, HashScanPosData::lastItem, LH_BUCKET_PAGE, LH_OVERFLOW_PAGE, HashScanPosData::nextPage, HashScanOpaqueData::numKilled, IndexScanDescData::opaque, HashScanPosData::prevPage, ScanDirectionIsForward, and IndexScanDescData::xs_heaptid.

Referenced by hashgetbitmap(), and hashgettuple().

◆ _hash_readnext()

static void _hash_readnext ( IndexScanDesc  scan,
Buffer bufp,
Page pagep,
HashPageOpaque opaquep 
)
static

Definition at line 131 of file hashsearch.c.

133{
134 BlockNumber blkno;
135 Relation rel = scan->indexRelation;
137 bool block_found = false;
138
139 blkno = (*opaquep)->hasho_nextblkno;
140
141 /*
142 * Retain the pin on primary bucket page till the end of scan. Refer the
143 * comments in _hash_first to know the reason of retaining pin.
144 */
145 if (*bufp == so->hashso_bucket_buf || *bufp == so->hashso_split_bucket_buf)
147 else
148 _hash_relbuf(rel, *bufp);
149
150 *bufp = InvalidBuffer;
151 /* check for interrupts while we're not holding any buffer lock */
153 if (BlockNumberIsValid(blkno))
154 {
155 *bufp = _hash_getbuf(rel, blkno, HASH_READ, LH_OVERFLOW_PAGE);
156 block_found = true;
157 }
158 else if (so->hashso_buc_populated && !so->hashso_buc_split)
159 {
160 /*
161 * end of bucket, scan bucket being split if there was a split in
162 * progress at the start of scan.
163 */
164 *bufp = so->hashso_split_bucket_buf;
165
166 /*
167 * buffer for bucket being split must be valid as we acquire the pin
168 * on it before the start of scan and retain it till end of scan.
169 */
170 Assert(BufferIsValid(*bufp));
171
174
175 /*
176 * setting hashso_buc_split to true indicates that we are scanning
177 * bucket being split.
178 */
179 so->hashso_buc_split = true;
180
181 block_found = true;
182 }
183
184 if (block_found)
185 {
186 *pagep = BufferGetPage(*bufp);
187 *opaquep = HashPageGetOpaque(*pagep);
188 }
189}
static bool BufferIsValid(Buffer bufnum)
Definition: bufmgr.h:368
void _hash_relbuf(Relation rel, Buffer buf)
Definition: hashpage.c:266
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:123

References _hash_getbuf(), _hash_relbuf(), Assert(), BlockNumberIsValid(), BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferGetBlockNumber(), BufferGetPage(), BufferIsValid(), CHECK_FOR_INTERRUPTS, HASH_READ, HashPageGetOpaque, HashScanOpaqueData::hashso_buc_populated, HashScanOpaqueData::hashso_buc_split, HashScanOpaqueData::hashso_bucket_buf, HashScanOpaqueData::hashso_split_bucket_buf, if(), IndexScanDescData::indexRelation, InvalidBuffer, LH_OVERFLOW_PAGE, LockBuffer(), IndexScanDescData::opaque, PredicateLockPage(), and IndexScanDescData::xs_snapshot.

Referenced by _hash_first(), _hash_readpage(), and _hash_readprev().

◆ _hash_readpage()

static bool _hash_readpage ( IndexScanDesc  scan,
Buffer bufP,
ScanDirection  dir 
)
static

Definition at line 448 of file hashsearch.c.

449{
450 Relation rel = scan->indexRelation;
452 Buffer buf;
453 Page page;
454 HashPageOpaque opaque;
455 OffsetNumber offnum;
456 uint16 itemIndex;
457
458 buf = *bufP;
461 page = BufferGetPage(buf);
462 opaque = HashPageGetOpaque(page);
463
464 so->currPos.buf = buf;
466
467 if (ScanDirectionIsForward(dir))
468 {
469 BlockNumber prev_blkno = InvalidBlockNumber;
470
471 for (;;)
472 {
473 /* new page, locate starting position by binary search */
474 offnum = _hash_binsearch(page, so->hashso_sk_hash);
475
476 itemIndex = _hash_load_qualified_items(scan, page, offnum, dir);
477
478 if (itemIndex != 0)
479 break;
480
481 /*
482 * Could not find any matching tuples in the current page, move to
483 * the next page. Before leaving the current page, deal with any
484 * killed items.
485 */
486 if (so->numKilled > 0)
487 _hash_kill_items(scan);
488
489 /*
490 * If this is a primary bucket page, hasho_prevblkno is not a real
491 * block number.
492 */
493 if (so->currPos.buf == so->hashso_bucket_buf ||
495 prev_blkno = InvalidBlockNumber;
496 else
497 prev_blkno = opaque->hasho_prevblkno;
498
499 _hash_readnext(scan, &buf, &page, &opaque);
500 if (BufferIsValid(buf))
501 {
502 so->currPos.buf = buf;
504 }
505 else
506 {
507 /*
508 * Remember next and previous block numbers for scrollable
509 * cursors to know the start position and return false
510 * indicating that no more matching tuples were found. Also,
511 * don't reset currPage or lsn, because we expect
512 * _hash_kill_items to be called for the old page after this
513 * function returns.
514 */
515 so->currPos.prevPage = prev_blkno;
517 so->currPos.buf = buf;
518 return false;
519 }
520 }
521
522 so->currPos.firstItem = 0;
523 so->currPos.lastItem = itemIndex - 1;
524 so->currPos.itemIndex = 0;
525 }
526 else
527 {
528 BlockNumber next_blkno = InvalidBlockNumber;
529
530 for (;;)
531 {
532 /* new page, locate starting position by binary search */
533 offnum = _hash_binsearch_last(page, so->hashso_sk_hash);
534
535 itemIndex = _hash_load_qualified_items(scan, page, offnum, dir);
536
537 if (itemIndex != MaxIndexTuplesPerPage)
538 break;
539
540 /*
541 * Could not find any matching tuples in the current page, move to
542 * the previous page. Before leaving the current page, deal with
543 * any killed items.
544 */
545 if (so->numKilled > 0)
546 _hash_kill_items(scan);
547
548 if (so->currPos.buf == so->hashso_bucket_buf ||
550 next_blkno = opaque->hasho_nextblkno;
551
552 _hash_readprev(scan, &buf, &page, &opaque);
553 if (BufferIsValid(buf))
554 {
555 so->currPos.buf = buf;
557 }
558 else
559 {
560 /*
561 * Remember next and previous block numbers for scrollable
562 * cursors to know the start position and return false
563 * indicating that no more matching tuples were found. Also,
564 * don't reset currPage or lsn, because we expect
565 * _hash_kill_items to be called for the old page after this
566 * function returns.
567 */
569 so->currPos.nextPage = next_blkno;
570 so->currPos.buf = buf;
571 return false;
572 }
573 }
574
575 so->currPos.firstItem = itemIndex;
578 }
579
580 if (so->currPos.buf == so->hashso_bucket_buf ||
582 {
584 so->currPos.nextPage = opaque->hasho_nextblkno;
586 }
587 else
588 {
589 so->currPos.prevPage = opaque->hasho_prevblkno;
590 so->currPos.nextPage = opaque->hasho_nextblkno;
591 _hash_relbuf(rel, so->currPos.buf);
593 }
594
596 return true;
597}
#define InvalidBlockNumber
Definition: block.h:33
uint16_t uint16
Definition: c.h:501
static void _hash_readprev(IndexScanDesc scan, Buffer *bufp, Page *pagep, HashPageOpaque *opaquep)
Definition: hashsearch.c:197
static int _hash_load_qualified_items(IndexScanDesc scan, Page page, OffsetNumber offnum, ScanDirection dir)
Definition: hashsearch.c:604
OffsetNumber _hash_binsearch(Page page, uint32 hash_value)
Definition: hashutil.c:350
OffsetNumber _hash_binsearch_last(Page page, uint32 hash_value)
Definition: hashutil.c:388
void _hash_checkpage(Relation rel, Buffer buf, int flags)
Definition: hashutil.c:210
BlockNumber currPage
Definition: hash.h:112

References _hash_binsearch(), _hash_binsearch_last(), _hash_checkpage(), _hash_kill_items(), _hash_load_qualified_items(), _hash_readnext(), _hash_readprev(), _hash_relbuf(), Assert(), buf, HashScanPosData::buf, BUFFER_LOCK_UNLOCK, BufferGetBlockNumber(), BufferGetPage(), BufferIsValid(), HashScanPosData::currPage, HashScanOpaqueData::currPos, HashScanPosData::firstItem, HashPageGetOpaque, HashScanOpaqueData::hashso_bucket_buf, HashScanOpaqueData::hashso_sk_hash, HashScanOpaqueData::hashso_split_bucket_buf, IndexScanDescData::indexRelation, InvalidBlockNumber, InvalidBuffer, HashScanPosData::itemIndex, HashScanPosData::lastItem, LH_BUCKET_PAGE, LH_OVERFLOW_PAGE, LockBuffer(), MaxIndexTuplesPerPage, HashScanPosData::nextPage, HashScanOpaqueData::numKilled, IndexScanDescData::opaque, HashScanPosData::prevPage, and ScanDirectionIsForward.

Referenced by _hash_first(), and _hash_next().

◆ _hash_readprev()

static void _hash_readprev ( IndexScanDesc  scan,
Buffer bufp,
Page pagep,
HashPageOpaque opaquep 
)
static

Definition at line 197 of file hashsearch.c.

199{
200 BlockNumber blkno;
201 Relation rel = scan->indexRelation;
203 bool haveprevblk;
204
205 blkno = (*opaquep)->hasho_prevblkno;
206
207 /*
208 * Retain the pin on primary bucket page till the end of scan. Refer the
209 * comments in _hash_first to know the reason of retaining pin.
210 */
211 if (*bufp == so->hashso_bucket_buf || *bufp == so->hashso_split_bucket_buf)
212 {
214 haveprevblk = false;
215 }
216 else
217 {
218 _hash_relbuf(rel, *bufp);
219 haveprevblk = true;
220 }
221
222 *bufp = InvalidBuffer;
223 /* check for interrupts while we're not holding any buffer lock */
225
226 if (haveprevblk)
227 {
229 *bufp = _hash_getbuf(rel, blkno, HASH_READ,
231 *pagep = BufferGetPage(*bufp);
232 *opaquep = HashPageGetOpaque(*pagep);
233
234 /*
235 * We always maintain the pin on bucket page for whole scan operation,
236 * so releasing the additional pin we have acquired here.
237 */
238 if (*bufp == so->hashso_bucket_buf || *bufp == so->hashso_split_bucket_buf)
239 _hash_dropbuf(rel, *bufp);
240 }
241 else if (so->hashso_buc_populated && so->hashso_buc_split)
242 {
243 /*
244 * end of bucket, scan bucket being populated if there was a split in
245 * progress at the start of scan.
246 */
247 *bufp = so->hashso_bucket_buf;
248
249 /*
250 * buffer for bucket being populated must be valid as we acquire the
251 * pin on it before the start of scan and retain it till end of scan.
252 */
253 Assert(BufferIsValid(*bufp));
254
256 *pagep = BufferGetPage(*bufp);
257 *opaquep = HashPageGetOpaque(*pagep);
258
259 /* move to the end of bucket chain */
260 while (BlockNumberIsValid((*opaquep)->hasho_nextblkno))
261 _hash_readnext(scan, bufp, pagep, opaquep);
262
263 /*
264 * setting hashso_buc_split to false indicates that we are scanning
265 * bucket being populated.
266 */
267 so->hashso_buc_split = false;
268 }
269}

References _hash_dropbuf(), _hash_getbuf(), _hash_readnext(), _hash_relbuf(), Assert(), BlockNumberIsValid(), BUFFER_LOCK_SHARE, BUFFER_LOCK_UNLOCK, BufferGetPage(), BufferIsValid(), CHECK_FOR_INTERRUPTS, HASH_READ, HashPageGetOpaque, HashScanOpaqueData::hashso_buc_populated, HashScanOpaqueData::hashso_buc_split, HashScanOpaqueData::hashso_bucket_buf, HashScanOpaqueData::hashso_split_bucket_buf, if(), IndexScanDescData::indexRelation, InvalidBuffer, LH_BUCKET_PAGE, LH_OVERFLOW_PAGE, LockBuffer(), and IndexScanDescData::opaque.

Referenced by _hash_readpage().

◆ _hash_saveitem()

static void _hash_saveitem ( HashScanOpaque  so,
int  itemIndex,
OffsetNumber  offnum,
IndexTuple  itup 
)
inlinestatic

Definition at line 710 of file hashsearch.c.

712{
713 HashScanPosItem *currItem = &so->currPos.items[itemIndex];
714
715 currItem->heapTid = itup->t_tid;
716 currItem->indexOffset = offnum;
717}
ItemPointerData heapTid
Definition: hash.h:105
OffsetNumber indexOffset
Definition: hash.h:106
ItemPointerData t_tid
Definition: itup.h:37

References HashScanOpaqueData::currPos, HashScanPosItem::heapTid, HashScanPosItem::indexOffset, HashScanPosData::items, and IndexTupleData::t_tid.

Referenced by _hash_load_qualified_items().