Header And Logo

PostgreSQL
| The world's most advanced open source database.

Functions

hashsearch.c File Reference

#include "postgres.h"
#include "access/hash.h"
#include "access/relscan.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "utils/rel.h"
Include dependency graph for hashsearch.c:

Go to the source code of this file.

Functions

bool _hash_next (IndexScanDesc scan, ScanDirection dir)
static void _hash_readnext (Relation rel, Buffer *bufp, Page *pagep, HashPageOpaque *opaquep)
static void _hash_readprev (Relation rel, Buffer *bufp, Page *pagep, HashPageOpaque *opaquep)
bool _hash_first (IndexScanDesc scan, ScanDirection dir)
bool _hash_step (IndexScanDesc scan, Buffer *bufP, ScanDirection dir)

Function Documentation

bool _hash_first ( IndexScanDesc  scan,
ScanDirection  dir 
)

Definition at line 120 of file hashsearch.c.

References _hash_checkpage(), _hash_chgbufaccess(), _hash_datum2hashkey(), _hash_datum2hashkey_type(), _hash_dropbuf(), _hash_droplock(), _hash_getbuf(), _hash_getlock(), _hash_hashkey2bucket(), _hash_readnext(), _hash_step(), Assert, BlockNumberIsValid, BUCKET_TO_BLKNO, buf, BufferGetPage, cur, ereport, errcode(), errmsg(), ERROR, HASH_METAPAGE, HASH_NOLOCK, HASH_READ, HASH_SHARE, HashPageGetMeta, HashScanOpaqueData::hashso_bucket, HashScanOpaqueData::hashso_bucket_blkno, HashScanOpaqueData::hashso_bucket_valid, HashScanOpaqueData::hashso_curpos, HashScanOpaqueData::hashso_heappos, HashScanOpaqueData::hashso_sk_hash, HTEqualStrategyNumber, IndexScanDescData::indexRelation, InvalidBuffer, InvalidOid, ItemPointerGetOffsetNumber, ItemPointerSetInvalid, IndexScanDescData::keyData, LH_BUCKET_PAGE, LH_META_PAGE, LH_OVERFLOW_PAGE, IndexScanDescData::numberOfKeys, IndexScanDescData::opaque, PageGetItem, PageGetItemId, PageGetSpecialPointer, pgstat_count_index_scan, RelationData::rd_opcintype, ScanDirectionIsBackward, and SK_ISNULL.

Referenced by hashgetbitmap(), and hashgettuple().

{
    Relation    rel = scan->indexRelation;
    HashScanOpaque so = (HashScanOpaque) scan->opaque;
    ScanKey     cur;
    uint32      hashkey;
    Bucket      bucket;
    BlockNumber blkno;
    BlockNumber oldblkno = InvalidBuffer;
    bool        retry = false;
    Buffer      buf;
    Buffer      metabuf;
    Page        page;
    HashPageOpaque opaque;
    HashMetaPage metap;
    IndexTuple  itup;
    ItemPointer current;
    OffsetNumber offnum;

    pgstat_count_index_scan(rel);

    current = &(so->hashso_curpos);
    ItemPointerSetInvalid(current);

    /*
     * We do not support hash scans with no index qualification, because we
     * would have to read the whole index rather than just one bucket. That
     * creates a whole raft of problems, since we haven't got a practical way
     * to lock all the buckets against splits or compactions.
     */
    if (scan->numberOfKeys < 1)
        ereport(ERROR,
                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                 errmsg("hash indexes do not support whole-index scans")));

    /* There may be more than one index qual, but we hash only the first */
    cur = &scan->keyData[0];

    /* We support only single-column hash indexes */
    Assert(cur->sk_attno == 1);
    /* And there's only one operator strategy, too */
    Assert(cur->sk_strategy == HTEqualStrategyNumber);

    /*
     * If the constant in the index qual is NULL, assume it cannot match any
     * items in the index.
     */
    if (cur->sk_flags & SK_ISNULL)
        return false;

    /*
     * Okay to compute the hash key.  We want to do this before acquiring any
     * locks, in case a user-defined hash function happens to be slow.
     *
     * If scankey operator is not a cross-type comparison, we can use the
     * cached hash function; otherwise gotta look it up in the catalogs.
     *
     * We support the convention that sk_subtype == InvalidOid means the
     * opclass input type; this is a hack to simplify life for ScanKeyInit().
     */
    if (cur->sk_subtype == rel->rd_opcintype[0] ||
        cur->sk_subtype == InvalidOid)
        hashkey = _hash_datum2hashkey(rel, cur->sk_argument);
    else
        hashkey = _hash_datum2hashkey_type(rel, cur->sk_argument,
                                           cur->sk_subtype);

    so->hashso_sk_hash = hashkey;

    /* Read the metapage */
    metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
    metap = HashPageGetMeta(BufferGetPage(metabuf));

    /*
     * Loop until we get a lock on the correct target bucket.
     */
    for (;;)
    {
        /*
         * Compute the target bucket number, and convert to block number.
         */
        bucket = _hash_hashkey2bucket(hashkey,
                                      metap->hashm_maxbucket,
                                      metap->hashm_highmask,
                                      metap->hashm_lowmask);

        blkno = BUCKET_TO_BLKNO(metap, bucket);

        /* Release metapage lock, but keep pin. */
        _hash_chgbufaccess(rel, metabuf, HASH_READ, HASH_NOLOCK);

        /*
         * If the previous iteration of this loop locked what is still the
         * correct target bucket, we are done.  Otherwise, drop any old lock
         * and lock what now appears to be the correct bucket.
         */
        if (retry)
        {
            if (oldblkno == blkno)
                break;
            _hash_droplock(rel, oldblkno, HASH_SHARE);
        }
        _hash_getlock(rel, blkno, HASH_SHARE);

        /*
         * Reacquire metapage lock and check that no bucket split has taken
         * place while we were awaiting the bucket lock.
         */
        _hash_chgbufaccess(rel, metabuf, HASH_NOLOCK, HASH_READ);
        oldblkno = blkno;
        retry = true;
    }

    /* done with the metapage */
    _hash_dropbuf(rel, metabuf);

    /* Update scan opaque state to show we have lock on the bucket */
    so->hashso_bucket = bucket;
    so->hashso_bucket_valid = true;
    so->hashso_bucket_blkno = blkno;

    /* Fetch the primary bucket page for the bucket */
    buf = _hash_getbuf(rel, blkno, HASH_READ, LH_BUCKET_PAGE);
    page = BufferGetPage(buf);
    opaque = (HashPageOpaque) PageGetSpecialPointer(page);
    Assert(opaque->hasho_bucket == bucket);

    /* If a backwards scan is requested, move to the end of the chain */
    if (ScanDirectionIsBackward(dir))
    {
        while (BlockNumberIsValid(opaque->hasho_nextblkno))
            _hash_readnext(rel, &buf, &page, &opaque);
    }

    /* Now find the first tuple satisfying the qualification */
    if (!_hash_step(scan, &buf, dir))
        return false;

    /* if we're here, _hash_step found a valid tuple */
    offnum = ItemPointerGetOffsetNumber(current);
    _hash_checkpage(rel, buf, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
    page = BufferGetPage(buf);
    itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
    so->hashso_heappos = itup->t_tid;

    return true;
}

bool _hash_next ( IndexScanDesc  scan,
ScanDirection  dir 
)

Definition at line 34 of file hashsearch.c.

References _hash_checkpage(), _hash_step(), Assert, buf, BufferGetPage, BufferIsValid, HashScanOpaqueData::hashso_curbuf, HashScanOpaqueData::hashso_curpos, HashScanOpaqueData::hashso_heappos, IndexScanDescData::indexRelation, ItemPointerGetOffsetNumber, LH_BUCKET_PAGE, LH_OVERFLOW_PAGE, IndexScanDescData::opaque, PageGetItem, and PageGetItemId.

Referenced by hashgetbitmap(), and hashgettuple().

{
    Relation    rel = scan->indexRelation;
    HashScanOpaque so = (HashScanOpaque) scan->opaque;
    Buffer      buf;
    Page        page;
    OffsetNumber offnum;
    ItemPointer current;
    IndexTuple  itup;

    /* we still have the buffer pinned and read-locked */
    buf = so->hashso_curbuf;
    Assert(BufferIsValid(buf));

    /*
     * step to next valid tuple.
     */
    if (!_hash_step(scan, &buf, dir))
        return false;

    /* if we're here, _hash_step found a valid tuple */
    current = &(so->hashso_curpos);
    offnum = ItemPointerGetOffsetNumber(current);
    _hash_checkpage(rel, buf, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
    page = BufferGetPage(buf);
    itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
    so->hashso_heappos = itup->t_tid;

    return true;
}

static void _hash_readnext ( Relation  rel,
Buffer bufp,
Page pagep,
HashPageOpaque opaquep 
) [static]

Definition at line 69 of file hashsearch.c.

References _hash_getbuf(), _hash_relbuf(), BlockNumberIsValid, BufferGetPage, CHECK_FOR_INTERRUPTS, HASH_READ, LH_OVERFLOW_PAGE, and PageGetSpecialPointer.

Referenced by _hash_first(), and _hash_step().

{
    BlockNumber blkno;

    blkno = (*opaquep)->hasho_nextblkno;
    _hash_relbuf(rel, *bufp);
    *bufp = InvalidBuffer;
    /* check for interrupts while we're not holding any buffer lock */
    CHECK_FOR_INTERRUPTS();
    if (BlockNumberIsValid(blkno))
    {
        *bufp = _hash_getbuf(rel, blkno, HASH_READ, LH_OVERFLOW_PAGE);
        *pagep = BufferGetPage(*bufp);
        *opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
    }
}

static void _hash_readprev ( Relation  rel,
Buffer bufp,
Page pagep,
HashPageOpaque opaquep 
) [static]

Definition at line 91 of file hashsearch.c.

References _hash_getbuf(), _hash_relbuf(), BlockNumberIsValid, BufferGetPage, CHECK_FOR_INTERRUPTS, HASH_READ, LH_BUCKET_PAGE, LH_OVERFLOW_PAGE, and PageGetSpecialPointer.

Referenced by _hash_step().

{
    BlockNumber blkno;

    blkno = (*opaquep)->hasho_prevblkno;
    _hash_relbuf(rel, *bufp);
    *bufp = InvalidBuffer;
    /* check for interrupts while we're not holding any buffer lock */
    CHECK_FOR_INTERRUPTS();
    if (BlockNumberIsValid(blkno))
    {
        *bufp = _hash_getbuf(rel, blkno, HASH_READ,
                             LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
        *pagep = BufferGetPage(*bufp);
        *opaquep = (HashPageOpaque) PageGetSpecialPointer(*pagep);
    }
}

bool _hash_step ( IndexScanDesc  scan,
Buffer bufP,
ScanDirection  dir 
)

Definition at line 280 of file hashsearch.c.

References _hash_binsearch(), _hash_binsearch_last(), _hash_checkpage(), _hash_checkqual(), _hash_get_indextuple_hashkey(), _hash_readnext(), _hash_readprev(), Assert, BackwardScanDirection, buf, BufferGetBlockNumber(), BufferGetPage, BufferIsValid, FirstOffsetNumber, ForwardScanDirection, HashScanOpaqueData::hashso_curbuf, HashScanOpaqueData::hashso_curpos, HashScanOpaqueData::hashso_sk_hash, IndexScanDescData::indexRelation, InvalidOffsetNumber, ItemPointerGetOffsetNumber, ItemPointerIsValid, ItemPointerSet, ItemPointerSetInvalid, LH_BUCKET_PAGE, LH_OVERFLOW_PAGE, NULL, OffsetNumberNext, OffsetNumberPrev, IndexScanDescData::opaque, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, and PageGetSpecialPointer.

Referenced by _hash_first(), and _hash_next().

{
    Relation    rel = scan->indexRelation;
    HashScanOpaque so = (HashScanOpaque) scan->opaque;
    ItemPointer current;
    Buffer      buf;
    Page        page;
    HashPageOpaque opaque;
    OffsetNumber maxoff;
    OffsetNumber offnum;
    BlockNumber blkno;
    IndexTuple  itup;

    current = &(so->hashso_curpos);

    buf = *bufP;
    _hash_checkpage(rel, buf, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
    page = BufferGetPage(buf);
    opaque = (HashPageOpaque) PageGetSpecialPointer(page);

    /*
     * If _hash_step is called from _hash_first, current will not be valid, so
     * we can't dereference it.  However, in that case, we presumably want to
     * start at the beginning/end of the page...
     */
    maxoff = PageGetMaxOffsetNumber(page);
    if (ItemPointerIsValid(current))
        offnum = ItemPointerGetOffsetNumber(current);
    else
        offnum = InvalidOffsetNumber;

    /*
     * 'offnum' now points to the last tuple we examined (if any).
     *
     * continue to step through tuples until: 1) we get to the end of the
     * bucket chain or 2) we find a valid tuple.
     */
    do
    {
        switch (dir)
        {
            case ForwardScanDirection:
                if (offnum != InvalidOffsetNumber)
                    offnum = OffsetNumberNext(offnum);  /* move forward */
                else
                {
                    /* new page, locate starting position by binary search */
                    offnum = _hash_binsearch(page, so->hashso_sk_hash);
                }

                for (;;)
                {
                    /*
                     * check if we're still in the range of items with the
                     * target hash key
                     */
                    if (offnum <= maxoff)
                    {
                        Assert(offnum >= FirstOffsetNumber);
                        itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
                        if (so->hashso_sk_hash == _hash_get_indextuple_hashkey(itup))
                            break;      /* yes, so exit for-loop */
                    }

                    /*
                     * ran off the end of this page, try the next
                     */
                    _hash_readnext(rel, &buf, &page, &opaque);
                    if (BufferIsValid(buf))
                    {
                        maxoff = PageGetMaxOffsetNumber(page);
                        offnum = _hash_binsearch(page, so->hashso_sk_hash);
                    }
                    else
                    {
                        /* end of bucket */
                        itup = NULL;
                        break;  /* exit for-loop */
                    }
                }
                break;

            case BackwardScanDirection:
                if (offnum != InvalidOffsetNumber)
                    offnum = OffsetNumberPrev(offnum);  /* move back */
                else
                {
                    /* new page, locate starting position by binary search */
                    offnum = _hash_binsearch_last(page, so->hashso_sk_hash);
                }

                for (;;)
                {
                    /*
                     * check if we're still in the range of items with the
                     * target hash key
                     */
                    if (offnum >= FirstOffsetNumber)
                    {
                        Assert(offnum <= maxoff);
                        itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
                        if (so->hashso_sk_hash == _hash_get_indextuple_hashkey(itup))
                            break;      /* yes, so exit for-loop */
                    }

                    /*
                     * ran off the end of this page, try the next
                     */
                    _hash_readprev(rel, &buf, &page, &opaque);
                    if (BufferIsValid(buf))
                    {
                        maxoff = PageGetMaxOffsetNumber(page);
                        offnum = _hash_binsearch_last(page, so->hashso_sk_hash);
                    }
                    else
                    {
                        /* end of bucket */
                        itup = NULL;
                        break;  /* exit for-loop */
                    }
                }
                break;

            default:
                /* NoMovementScanDirection */
                /* this should not be reached */
                itup = NULL;
                break;
        }

        if (itup == NULL)
        {
            /* we ran off the end of the bucket without finding a match */
            *bufP = so->hashso_curbuf = InvalidBuffer;
            ItemPointerSetInvalid(current);
            return false;
        }

        /* check the tuple quals, loop around if not met */
    } while (!_hash_checkqual(scan, itup));

    /* if we made it to here, we've found a valid tuple */
    blkno = BufferGetBlockNumber(buf);
    *bufP = so->hashso_curbuf = buf;
    ItemPointerSet(current, blkno, offnum);
    return true;
}