Header And Logo

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

Data Structures | Defines | Typedefs | Functions

hash.h File Reference

#include "access/genam.h"
#include "access/itup.h"
#include "access/sdir.h"
#include "access/xlog.h"
#include "fmgr.h"
#include "storage/bufmgr.h"
#include "storage/lock.h"
#include "utils/relcache.h"
Include dependency graph for hash.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  HashPageOpaqueData
struct  HashScanOpaqueData
struct  HashMetaPageData

Defines

#define BUCKET_TO_BLKNO(metap, B)   ((BlockNumber) ((B) + ((B) ? (metap)->hashm_spares[_hash_log2((B)+1)-1] : 0)) + 1)
#define LH_UNUSED_PAGE   (0)
#define LH_OVERFLOW_PAGE   (1 << 0)
#define LH_BUCKET_PAGE   (1 << 1)
#define LH_BITMAP_PAGE   (1 << 2)
#define LH_META_PAGE   (1 << 3)
#define HASHO_PAGE_ID   0xFF80
#define HASH_METAPAGE   0
#define HASH_MAGIC   0x6440640
#define HASH_VERSION   2
#define HASH_MAX_SPLITPOINTS   32
#define HASH_MAX_BITMAPS   128
#define HashMaxItemSize(page)
#define HASH_MIN_FILLFACTOR   10
#define HASH_DEFAULT_FILLFACTOR   75
#define BYTE_TO_BIT   3
#define ALL_SET   ((uint32) ~0)
#define BMPGSZ_BYTE(metap)   ((metap)->hashm_bmsize)
#define BMPGSZ_BIT(metap)   ((metap)->hashm_bmsize << BYTE_TO_BIT)
#define BMPG_SHIFT(metap)   ((metap)->hashm_bmshift)
#define BMPG_MASK(metap)   (BMPGSZ_BIT(metap) - 1)
#define HashPageGetBitmap(page)   ((uint32 *) PageGetContents(page))
#define HashGetMaxBitmapSize(page)
#define HashPageGetMeta(page)   ((HashMetaPage) PageGetContents(page))
#define BITS_PER_MAP   32
#define CLRBIT(A, N)   ((A)[(N)/BITS_PER_MAP] &= ~(1<<((N)%BITS_PER_MAP)))
#define SETBIT(A, N)   ((A)[(N)/BITS_PER_MAP] |= (1<<((N)%BITS_PER_MAP)))
#define ISSET(A, N)   ((A)[(N)/BITS_PER_MAP] & (1<<((N)%BITS_PER_MAP)))
#define HASH_READ   BUFFER_LOCK_SHARE
#define HASH_WRITE   BUFFER_LOCK_EXCLUSIVE
#define HASH_NOLOCK   (-1)
#define HASH_SHARE   ShareLock
#define HASH_EXCLUSIVE   ExclusiveLock
#define HTEqualStrategyNumber   1
#define HTMaxStrategyNumber   1
#define HASHPROC   1

Typedefs

typedef uint32 Bucket
typedef struct HashPageOpaqueData HashPageOpaqueData
typedef HashPageOpaqueDataHashPageOpaque
typedef struct HashScanOpaqueData HashScanOpaqueData
typedef HashScanOpaqueDataHashScanOpaque
typedef struct HashMetaPageData HashMetaPageData
typedef HashMetaPageDataHashMetaPage
typedef struct HSpool HSpool

Functions

Datum hashbuild (PG_FUNCTION_ARGS)
Datum hashbuildempty (PG_FUNCTION_ARGS)
Datum hashinsert (PG_FUNCTION_ARGS)
Datum hashbeginscan (PG_FUNCTION_ARGS)
Datum hashgettuple (PG_FUNCTION_ARGS)
Datum hashgetbitmap (PG_FUNCTION_ARGS)
Datum hashrescan (PG_FUNCTION_ARGS)
Datum hashendscan (PG_FUNCTION_ARGS)
Datum hashmarkpos (PG_FUNCTION_ARGS)
Datum hashrestrpos (PG_FUNCTION_ARGS)
Datum hashbulkdelete (PG_FUNCTION_ARGS)
Datum hashvacuumcleanup (PG_FUNCTION_ARGS)
Datum hashoptions (PG_FUNCTION_ARGS)
Datum hashchar (PG_FUNCTION_ARGS)
Datum hashint2 (PG_FUNCTION_ARGS)
Datum hashint4 (PG_FUNCTION_ARGS)
Datum hashint8 (PG_FUNCTION_ARGS)
Datum hashoid (PG_FUNCTION_ARGS)
Datum hashenum (PG_FUNCTION_ARGS)
Datum hashfloat4 (PG_FUNCTION_ARGS)
Datum hashfloat8 (PG_FUNCTION_ARGS)
Datum hashoidvector (PG_FUNCTION_ARGS)
Datum hashint2vector (PG_FUNCTION_ARGS)
Datum hashname (PG_FUNCTION_ARGS)
Datum hashtext (PG_FUNCTION_ARGS)
Datum hashvarlena (PG_FUNCTION_ARGS)
Datum hash_any (register const unsigned char *k, register int keylen)
Datum hash_uint32 (uint32 k)
void _hash_doinsert (Relation rel, IndexTuple itup)
OffsetNumber _hash_pgaddtup (Relation rel, Buffer buf, Size itemsize, IndexTuple itup)
Buffer _hash_addovflpage (Relation rel, Buffer metabuf, Buffer buf)
BlockNumber _hash_freeovflpage (Relation rel, Buffer ovflbuf, BufferAccessStrategy bstrategy)
void _hash_initbitmap (Relation rel, HashMetaPage metap, BlockNumber blkno, ForkNumber forkNum)
void _hash_squeezebucket (Relation rel, Bucket bucket, BlockNumber bucket_blkno, BufferAccessStrategy bstrategy)
void _hash_getlock (Relation rel, BlockNumber whichlock, int access)
bool _hash_try_getlock (Relation rel, BlockNumber whichlock, int access)
void _hash_droplock (Relation rel, BlockNumber whichlock, int access)
Buffer _hash_getbuf (Relation rel, BlockNumber blkno, int access, int flags)
Buffer _hash_getinitbuf (Relation rel, BlockNumber blkno)
Buffer _hash_getnewbuf (Relation rel, BlockNumber blkno, ForkNumber forkNum)
Buffer _hash_getbuf_with_strategy (Relation rel, BlockNumber blkno, int access, int flags, BufferAccessStrategy bstrategy)
void _hash_relbuf (Relation rel, Buffer buf)
void _hash_dropbuf (Relation rel, Buffer buf)
void _hash_wrtbuf (Relation rel, Buffer buf)
void _hash_chgbufaccess (Relation rel, Buffer buf, int from_access, int to_access)
uint32 _hash_metapinit (Relation rel, double num_tuples, ForkNumber forkNum)
void _hash_pageinit (Page page, Size size)
void _hash_expandtable (Relation rel, Buffer metabuf)
void _hash_regscan (IndexScanDesc scan)
void _hash_dropscan (IndexScanDesc scan)
bool _hash_has_active_scan (Relation rel, Bucket bucket)
void ReleaseResources_hash (void)
bool _hash_next (IndexScanDesc scan, ScanDirection dir)
bool _hash_first (IndexScanDesc scan, ScanDirection dir)
bool _hash_step (IndexScanDesc scan, Buffer *bufP, ScanDirection dir)
HSpool_h_spoolinit (Relation heap, Relation index, uint32 num_buckets)
void _h_spooldestroy (HSpool *hspool)
void _h_spool (IndexTuple itup, HSpool *hspool)
void _h_indexbuild (HSpool *hspool)
bool _hash_checkqual (IndexScanDesc scan, IndexTuple itup)
uint32 _hash_datum2hashkey (Relation rel, Datum key)
uint32 _hash_datum2hashkey_type (Relation rel, Datum key, Oid keytype)
Bucket _hash_hashkey2bucket (uint32 hashkey, uint32 maxbucket, uint32 highmask, uint32 lowmask)
uint32 _hash_log2 (uint32 num)
void _hash_checkpage (Relation rel, Buffer buf, int flags)
uint32 _hash_get_indextuple_hashkey (IndexTuple itup)
IndexTuple _hash_form_tuple (Relation index, Datum *values, bool *isnull)
OffsetNumber _hash_binsearch (Page page, uint32 hash_value)
OffsetNumber _hash_binsearch_last (Page page, uint32 hash_value)
void hash_redo (XLogRecPtr lsn, XLogRecord *record)
void hash_desc (StringInfo buf, uint8 xl_info, char *rec)

Define Documentation

#define ALL_SET   ((uint32) ~0)

Definition at line 185 of file hash.h.

Referenced by _hash_getovflpage().

#define BITS_PER_MAP   32

Definition at line 212 of file hash.h.

Referenced by _hash_getovflpage().

#define BMPG_MASK (   metap  )     (BMPGSZ_BIT(metap) - 1)

Definition at line 197 of file hash.h.

Referenced by _hash_freeovflpage(), _hash_getovflpage(), and _hash_metapinit().

#define BMPG_SHIFT (   metap  )     ((metap)->hashm_bmshift)

Definition at line 196 of file hash.h.

Referenced by _hash_freeovflpage(), _hash_getovflpage(), and _hash_metapinit().

#define BMPGSZ_BIT (   metap  )     ((metap)->hashm_bmsize << BYTE_TO_BIT)

Definition at line 195 of file hash.h.

Referenced by _hash_getovflpage().

#define BMPGSZ_BYTE (   metap  )     ((metap)->hashm_bmsize)

Definition at line 194 of file hash.h.

Referenced by _hash_initbitmap().

#define BUCKET_TO_BLKNO (   metap,
  B 
)    ((BlockNumber) ((B) + ((B) ? (metap)->hashm_spares[_hash_log2((B)+1)-1] : 0)) + 1)
#define BYTE_TO_BIT   3

Definition at line 184 of file hash.h.

#define CLRBIT (   A,
  N 
)    ((A)[(N)/BITS_PER_MAP] &= ~(1<<((N)%BITS_PER_MAP)))

Definition at line 215 of file hash.h.

#define HASH_DEFAULT_FILLFACTOR   75

Definition at line 179 of file hash.h.

Referenced by _hash_metapinit().

#define HASH_EXCLUSIVE   ExclusiveLock

Definition at line 227 of file hash.h.

Referenced by _hash_expandtable(), and hashbulkdelete().

#define HASH_MAGIC   0x6440640

Definition at line 118 of file hash.h.

Referenced by _hash_checkpage().

#define HASH_MAX_BITMAPS   128

Definition at line 142 of file hash.h.

Referenced by _hash_initbitmap().

#define HASH_MAX_SPLITPOINTS   32

Definition at line 141 of file hash.h.

Referenced by _hash_metapinit().

#define HASH_METAPAGE   0
#define HASH_MIN_FILLFACTOR   10

Definition at line 178 of file hash.h.

#define HASH_NOLOCK   (-1)
#define HASH_READ   BUFFER_LOCK_SHARE
#define HASH_SHARE   ShareLock

Definition at line 226 of file hash.h.

Referenced by _hash_doinsert(), _hash_first(), hashendscan(), hashrescan(), and pgstat_hash_page().

#define HASH_VERSION   2

Definition at line 119 of file hash.h.

Referenced by _hash_checkpage().

#define HASH_WRITE   BUFFER_LOCK_EXCLUSIVE
#define HashGetMaxBitmapSize (   page  ) 
Value:

Definition at line 202 of file hash.h.

Referenced by _hash_metapinit().

#define HashMaxItemSize (   page  ) 
Value:

Definition at line 172 of file hash.h.

Referenced by _hash_doinsert().

#define HASHO_PAGE_ID   0xFF80

Definition at line 72 of file hash.h.

#define HashPageGetBitmap (   page  )     ((uint32 *) PageGetContents(page))

Definition at line 199 of file hash.h.

Referenced by _hash_freeovflpage(), _hash_getovflpage(), and _hash_initbitmap().

#define HashPageGetMeta (   page  )     ((HashMetaPage) PageGetContents(page))
#define HASHPROC   1
#define HTEqualStrategyNumber   1
#define HTMaxStrategyNumber   1

Definition at line 233 of file hash.h.

#define ISSET (   A,
  N 
)    ((A)[(N)/BITS_PER_MAP] & (1<<((N)%BITS_PER_MAP)))

Definition at line 217 of file hash.h.

Referenced by _hash_freeovflpage().

#define LH_BITMAP_PAGE   (1 << 2)

Definition at line 52 of file hash.h.

Referenced by _hash_freeovflpage(), _hash_getovflpage(), and pgstat_hash_page().

#define LH_BUCKET_PAGE   (1 << 1)
#define LH_META_PAGE   (1 << 3)
#define LH_OVERFLOW_PAGE   (1 << 0)
#define LH_UNUSED_PAGE   (0)

Definition at line 49 of file hash.h.

Referenced by pgstat_hash_page().

#define SETBIT (   A,
  N 
)    ((A)[(N)/BITS_PER_MAP] |= (1<<((N)%BITS_PER_MAP)))

Definition at line 216 of file hash.h.


Typedef Documentation

typedef uint32 Bucket

Definition at line 33 of file hash.h.

Definition at line 167 of file hash.h.

Definition at line 64 of file hash.h.

Definition at line 110 of file hash.h.

typedef struct HSpool HSpool

Definition at line 335 of file hash.h.


Function Documentation

void _h_indexbuild ( HSpool hspool  ) 

Definition at line 103 of file hashsort.c.

References _hash_doinsert(), HSpool::index, NULL, pfree(), HSpool::sortstate, tuplesort_getindextuple(), and tuplesort_performsort().

Referenced by hashbuild().

{
    IndexTuple  itup;
    bool        should_free;

    tuplesort_performsort(hspool->sortstate);

    while ((itup = tuplesort_getindextuple(hspool->sortstate,
                                           true, &should_free)) != NULL)
    {
        _hash_doinsert(hspool->index, itup);
        if (should_free)
            pfree(itup);
    }
}

void _h_spool ( IndexTuple  itup,
HSpool hspool 
)

Definition at line 93 of file hashsort.c.

References HSpool::sortstate, and tuplesort_putindextuple().

Referenced by hashbuildCallback().

{
    tuplesort_putindextuple(hspool->sortstate, itup);
}

void _h_spooldestroy ( HSpool hspool  ) 

Definition at line 83 of file hashsort.c.

References pfree(), HSpool::sortstate, and tuplesort_end().

Referenced by hashbuild().

{
    tuplesort_end(hspool->sortstate);
    pfree(hspool);
}

HSpool* _h_spoolinit ( Relation  heap,
Relation  index,
uint32  num_buckets 
)

Definition at line 47 of file hashsort.c.

References _hash_log2(), HSpool::index, maintenance_work_mem, palloc0(), HSpool::sortstate, and tuplesort_begin_index_hash().

Referenced by hashbuild().

{
    HSpool     *hspool = (HSpool *) palloc0(sizeof(HSpool));
    uint32      hash_mask;

    hspool->index = index;

    /*
     * Determine the bitmask for hash code values.  Since there are currently
     * num_buckets buckets in the index, the appropriate mask can be computed
     * as follows.
     *
     * Note: at present, the passed-in num_buckets is always a power of 2, so
     * we could just compute num_buckets - 1.  We prefer not to assume that
     * here, though.
     */
    hash_mask = (((uint32) 1) << _hash_log2(num_buckets)) - 1;

    /*
     * We size the sort area as maintenance_work_mem rather than work_mem to
     * speed index creation.  This should be OK since a single backend can't
     * run multiple index creations in parallel.
     */
    hspool->sortstate = tuplesort_begin_index_hash(heap,
                                                   index,
                                                   hash_mask,
                                                   maintenance_work_mem,
                                                   false);

    return hspool;
}

Buffer _hash_addovflpage ( Relation  rel,
Buffer  metabuf,
Buffer  buf 
)

Definition at line 101 of file hashovfl.c.

References _hash_checkpage(), _hash_chgbufaccess(), _hash_getbuf(), _hash_getovflpage(), _hash_relbuf(), _hash_wrtbuf(), BlockNumberIsValid, BufferGetBlockNumber(), BufferGetPage, HASH_NOLOCK, HASH_WRITE, HashPageOpaqueData::hasho_bucket, HashPageOpaqueData::hasho_flag, HashPageOpaqueData::hasho_nextblkno, HashPageOpaqueData::hasho_page_id, HashPageOpaqueData::hasho_prevblkno, LH_BUCKET_PAGE, LH_OVERFLOW_PAGE, MarkBufferDirty(), and PageGetSpecialPointer.

Referenced by _hash_doinsert(), and _hash_splitbucket().

{
    Buffer      ovflbuf;
    Page        page;
    Page        ovflpage;
    HashPageOpaque pageopaque;
    HashPageOpaque ovflopaque;

    /* allocate and lock an empty overflow page */
    ovflbuf = _hash_getovflpage(rel, metabuf);

    /*
     * Write-lock the tail page.  It is okay to hold two buffer locks here
     * since there cannot be anyone else contending for access to ovflbuf.
     */
    _hash_chgbufaccess(rel, buf, HASH_NOLOCK, HASH_WRITE);

    /* probably redundant... */
    _hash_checkpage(rel, buf, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);

    /* loop to find current tail page, in case someone else inserted too */
    for (;;)
    {
        BlockNumber nextblkno;

        page = BufferGetPage(buf);
        pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
        nextblkno = pageopaque->hasho_nextblkno;

        if (!BlockNumberIsValid(nextblkno))
            break;

        /* we assume we do not need to write the unmodified page */
        _hash_relbuf(rel, buf);

        buf = _hash_getbuf(rel, nextblkno, HASH_WRITE, LH_OVERFLOW_PAGE);
    }

    /* now that we have correct backlink, initialize new overflow page */
    ovflpage = BufferGetPage(ovflbuf);
    ovflopaque = (HashPageOpaque) PageGetSpecialPointer(ovflpage);
    ovflopaque->hasho_prevblkno = BufferGetBlockNumber(buf);
    ovflopaque->hasho_nextblkno = InvalidBlockNumber;
    ovflopaque->hasho_bucket = pageopaque->hasho_bucket;
    ovflopaque->hasho_flag = LH_OVERFLOW_PAGE;
    ovflopaque->hasho_page_id = HASHO_PAGE_ID;

    MarkBufferDirty(ovflbuf);

    /* logically chain overflow page to previous page */
    pageopaque->hasho_nextblkno = BufferGetBlockNumber(ovflbuf);
    _hash_wrtbuf(rel, buf);

    return ovflbuf;
}

OffsetNumber _hash_binsearch ( Page  page,
uint32  hash_value 
)

Definition at line 287 of file hashutil.c.

References _hash_get_indextuple_hashkey(), Assert, OffsetNumberIsValid, PageGetItem, PageGetItemId, and PageGetMaxOffsetNumber.

Referenced by _hash_pgaddtup(), and _hash_step().

{
    OffsetNumber upper;
    OffsetNumber lower;

    /* Loop invariant: lower <= desired place <= upper */
    upper = PageGetMaxOffsetNumber(page) + 1;
    lower = FirstOffsetNumber;

    while (upper > lower)
    {
        OffsetNumber off;
        IndexTuple  itup;
        uint32      hashkey;

        off = (upper + lower) / 2;
        Assert(OffsetNumberIsValid(off));

        itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
        hashkey = _hash_get_indextuple_hashkey(itup);
        if (hashkey < hash_value)
            lower = off + 1;
        else
            upper = off;
    }

    return lower;
}

OffsetNumber _hash_binsearch_last ( Page  page,
uint32  hash_value 
)

Definition at line 325 of file hashutil.c.

References _hash_get_indextuple_hashkey(), Assert, FirstOffsetNumber, OffsetNumberIsValid, PageGetItem, PageGetItemId, and PageGetMaxOffsetNumber.

Referenced by _hash_step().

{
    OffsetNumber upper;
    OffsetNumber lower;

    /* Loop invariant: lower <= desired place <= upper */
    upper = PageGetMaxOffsetNumber(page);
    lower = FirstOffsetNumber - 1;

    while (upper > lower)
    {
        IndexTuple  itup;
        OffsetNumber off;
        uint32      hashkey;

        off = (upper + lower + 1) / 2;
        Assert(OffsetNumberIsValid(off));

        itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, off));
        hashkey = _hash_get_indextuple_hashkey(itup);
        if (hashkey > hash_value)
            upper = off - 1;
        else
            lower = off;
    }

    return lower;
}

void _hash_checkpage ( Relation  rel,
Buffer  buf,
int  flags 
)

Definition at line 156 of file hashutil.c.

References BufferGetBlockNumber(), BufferGetPage, ereport, errcode(), errhint(), errmsg(), ERROR, HASH_MAGIC, HASH_VERSION, HashMetaPageData::hashm_magic, HashMetaPageData::hashm_version, HashPageOpaqueData::hasho_flag, HashPageGetMeta, LH_META_PAGE, MAXALIGN, PageGetSpecialPointer, PageGetSpecialSize, PageIsNew, and RelationGetRelationName.

Referenced by _hash_addovflpage(), _hash_expandtable(), _hash_first(), _hash_freeovflpage(), _hash_getbuf(), _hash_getbuf_with_strategy(), _hash_getovflpage(), _hash_next(), _hash_pgaddtup(), and _hash_step().

{
    Page        page = BufferGetPage(buf);

    /*
     * ReadBuffer verifies that every newly-read page passes
     * PageHeaderIsValid, which means it either contains a reasonably sane
     * page header or is all-zero.  We have to defend against the all-zero
     * case, however.
     */
    if (PageIsNew(page))
        ereport(ERROR,
                (errcode(ERRCODE_INDEX_CORRUPTED),
             errmsg("index \"%s\" contains unexpected zero page at block %u",
                    RelationGetRelationName(rel),
                    BufferGetBlockNumber(buf)),
                 errhint("Please REINDEX it.")));

    /*
     * Additionally check that the special area looks sane.
     */
    if (PageGetSpecialSize(page) != MAXALIGN(sizeof(HashPageOpaqueData)))
        ereport(ERROR,
                (errcode(ERRCODE_INDEX_CORRUPTED),
                 errmsg("index \"%s\" contains corrupted page at block %u",
                        RelationGetRelationName(rel),
                        BufferGetBlockNumber(buf)),
                 errhint("Please REINDEX it.")));

    if (flags)
    {
        HashPageOpaque opaque = (HashPageOpaque) PageGetSpecialPointer(page);

        if ((opaque->hasho_flag & flags) == 0)
            ereport(ERROR,
                    (errcode(ERRCODE_INDEX_CORRUPTED),
                   errmsg("index \"%s\" contains corrupted page at block %u",
                          RelationGetRelationName(rel),
                          BufferGetBlockNumber(buf)),
                     errhint("Please REINDEX it.")));
    }

    /*
     * When checking the metapage, also verify magic number and version.
     */
    if (flags == LH_META_PAGE)
    {
        HashMetaPage metap = HashPageGetMeta(page);

        if (metap->hashm_magic != HASH_MAGIC)
            ereport(ERROR,
                    (errcode(ERRCODE_INDEX_CORRUPTED),
                     errmsg("index \"%s\" is not a hash index",
                            RelationGetRelationName(rel))));

        if (metap->hashm_version != HASH_VERSION)
            ereport(ERROR,
                    (errcode(ERRCODE_INDEX_CORRUPTED),
                     errmsg("index \"%s\" has wrong hash version",
                            RelationGetRelationName(rel)),
                     errhint("Please REINDEX it.")));
    }
}

bool _hash_checkqual ( IndexScanDesc  scan,
IndexTuple  itup 
)

Definition at line 28 of file hashutil.c.

References DatumGetBool, FunctionCall2Coll(), index_getattr, IndexScanDescData::indexRelation, IndexScanDescData::keyData, IndexScanDescData::numberOfKeys, RelationGetDescr, ScanKeyData::sk_argument, ScanKeyData::sk_attno, ScanKeyData::sk_collation, ScanKeyData::sk_flags, ScanKeyData::sk_func, and SK_ISNULL.

Referenced by _hash_step().

{
    /*
     * Currently, we can't check any of the scan conditions since we do not
     * have the original index entry value to supply to the sk_func. Always
     * return true; we expect that hashgettuple already set the recheck flag
     * to make the main indexscan code do it.
     */
#ifdef NOT_USED
    TupleDesc   tupdesc = RelationGetDescr(scan->indexRelation);
    ScanKey     key = scan->keyData;
    int         scanKeySize = scan->numberOfKeys;

    while (scanKeySize > 0)
    {
        Datum       datum;
        bool        isNull;
        Datum       test;

        datum = index_getattr(itup,
                              key->sk_attno,
                              tupdesc,
                              &isNull);

        /* assume sk_func is strict */
        if (isNull)
            return false;
        if (key->sk_flags & SK_ISNULL)
            return false;

        test = FunctionCall2Coll(&key->sk_func, key->sk_collation,
                                 datum, key->sk_argument);

        if (!DatumGetBool(test))
            return false;

        key++;
        scanKeySize--;
    }
#endif

    return true;
}

void _hash_chgbufaccess ( Relation  rel,
Buffer  buf,
int  from_access,
int  to_access 
)
uint32 _hash_datum2hashkey ( Relation  rel,
Datum  key 
)

Definition at line 79 of file hashutil.c.

References DatumGetUInt32, FunctionCall1Coll(), HASHPROC, index_getprocinfo(), and RelationData::rd_indcollation.

Referenced by _hash_first(), and _hash_form_tuple().

{
    FmgrInfo   *procinfo;
    Oid         collation;

    /* XXX assumes index has only one attribute */
    procinfo = index_getprocinfo(rel, 1, HASHPROC);
    collation = rel->rd_indcollation[0];

    return DatumGetUInt32(FunctionCall1Coll(procinfo, collation, key));
}

uint32 _hash_datum2hashkey_type ( Relation  rel,
Datum  key,
Oid  keytype 
)

Definition at line 99 of file hashutil.c.

References DatumGetUInt32, elog, ERROR, get_opfamily_proc(), HASHPROC, OidFunctionCall1Coll(), RelationData::rd_indcollation, RelationData::rd_opfamily, RegProcedureIsValid, and RelationGetRelationName.

Referenced by _hash_first().

{
    RegProcedure hash_proc;
    Oid         collation;

    /* XXX assumes index has only one attribute */
    hash_proc = get_opfamily_proc(rel->rd_opfamily[0],
                                  keytype,
                                  keytype,
                                  HASHPROC);
    if (!RegProcedureIsValid(hash_proc))
        elog(ERROR, "missing support function %d(%u,%u) for index \"%s\"",
             HASHPROC, keytype, keytype,
             RelationGetRelationName(rel));
    collation = rel->rd_indcollation[0];

    return DatumGetUInt32(OidFunctionCall1Coll(hash_proc, collation, key));
}

void _hash_doinsert ( Relation  rel,
IndexTuple  itup 
)

Definition at line 29 of file hashinsert.c.

References _hash_addovflpage(), _hash_chgbufaccess(), _hash_dropbuf(), _hash_droplock(), _hash_expandtable(), _hash_get_indextuple_hashkey(), _hash_getbuf(), _hash_getlock(), _hash_hashkey2bucket(), _hash_pgaddtup(), _hash_relbuf(), _hash_wrtbuf(), Assert, BlockNumberIsValid, BUCKET_TO_BLKNO, buf, BufferGetPage, ereport, errcode(), errhint(), errmsg(), ERROR, HASH_METAPAGE, HASH_NOLOCK, HASH_READ, HASH_SHARE, HASH_WRITE, HashMaxItemSize, HashPageOpaqueData::hasho_bucket, HashPageOpaqueData::hasho_flag, HashPageOpaqueData::hasho_nextblkno, HashPageGetMeta, IndexTupleDSize, LH_BUCKET_PAGE, LH_META_PAGE, LH_OVERFLOW_PAGE, MAXALIGN, PageGetFreeSpace(), and PageGetSpecialPointer.

Referenced by _h_indexbuild(), hashbuildCallback(), and hashinsert().

{
    Buffer      buf;
    Buffer      metabuf;
    HashMetaPage metap;
    BlockNumber blkno;
    BlockNumber oldblkno = InvalidBlockNumber;
    bool        retry = false;
    Page        page;
    HashPageOpaque pageopaque;
    Size        itemsz;
    bool        do_expand;
    uint32      hashkey;
    Bucket      bucket;

    /*
     * Get the hash key for the item (it's stored in the index tuple itself).
     */
    hashkey = _hash_get_indextuple_hashkey(itup);

    /* compute item size too */
    itemsz = IndexTupleDSize(*itup);
    itemsz = MAXALIGN(itemsz);  /* be safe, PageAddItem will do this but we
                                 * need to be consistent */

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

    /*
     * Check whether the item can fit on a hash page at all. (Eventually, we
     * ought to try to apply TOAST methods if not.)  Note that at this point,
     * itemsz doesn't include the ItemId.
     *
     * XXX this is useless code if we are only storing hash keys.
     */
    if (itemsz > HashMaxItemSize((Page) metap))
        ereport(ERROR,
                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                 errmsg("index row size %lu exceeds hash maximum %lu",
                        (unsigned long) itemsz,
                        (unsigned long) HashMaxItemSize((Page) metap)),
            errhint("Values larger than a buffer page cannot be indexed.")));

    /*
     * 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;
    }

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

    /* Do the insertion */
    while (PageGetFreeSpace(page) < itemsz)
    {
        /*
         * no space on this page; check for an overflow page
         */
        BlockNumber nextblkno = pageopaque->hasho_nextblkno;

        if (BlockNumberIsValid(nextblkno))
        {
            /*
             * ovfl page exists; go get it.  if it doesn't have room, we'll
             * find out next pass through the loop test above.
             */
            _hash_relbuf(rel, buf);
            buf = _hash_getbuf(rel, nextblkno, HASH_WRITE, LH_OVERFLOW_PAGE);
            page = BufferGetPage(buf);
        }
        else
        {
            /*
             * we're at the end of the bucket chain and we haven't found a
             * page with enough room.  allocate a new overflow page.
             */

            /* release our write lock without modifying buffer */
            _hash_chgbufaccess(rel, buf, HASH_READ, HASH_NOLOCK);

            /* chain to a new overflow page */
            buf = _hash_addovflpage(rel, metabuf, buf);
            page = BufferGetPage(buf);

            /* should fit now, given test above */
            Assert(PageGetFreeSpace(page) >= itemsz);
        }
        pageopaque = (HashPageOpaque) PageGetSpecialPointer(page);
        Assert(pageopaque->hasho_flag == LH_OVERFLOW_PAGE);
        Assert(pageopaque->hasho_bucket == bucket);
    }

    /* found page with enough space, so add the item here */
    (void) _hash_pgaddtup(rel, buf, itemsz, itup);

    /* write and release the modified page */
    _hash_wrtbuf(rel, buf);

    /* We can drop the bucket lock now */
    _hash_droplock(rel, blkno, HASH_SHARE);

    /*
     * Write-lock the metapage so we can increment the tuple count. After
     * incrementing it, check to see if it's time for a split.
     */
    _hash_chgbufaccess(rel, metabuf, HASH_NOLOCK, HASH_WRITE);

    metap->hashm_ntuples += 1;

    /* Make sure this stays in sync with _hash_expandtable() */
    do_expand = metap->hashm_ntuples >
        (double) metap->hashm_ffactor * (metap->hashm_maxbucket + 1);

    /* Write out the metapage and drop lock, but keep pin */
    _hash_chgbufaccess(rel, metabuf, HASH_WRITE, HASH_NOLOCK);

    /* Attempt to split if a split is needed */
    if (do_expand)
        _hash_expandtable(rel, metabuf);

    /* Finally drop our pin on the metapage */
    _hash_dropbuf(rel, metabuf);
}

void _hash_dropbuf ( Relation  rel,
Buffer  buf 
)

Definition at line 260 of file hashpage.c.

References ReleaseBuffer().

Referenced by _hash_doinsert(), _hash_first(), hashendscan(), and hashrescan().

void _hash_droplock ( Relation  rel,
BlockNumber  whichlock,
int  access 
)

Definition at line 91 of file hashpage.c.

References UnlockPage(), and USELOCKING.

Referenced by _hash_doinsert(), _hash_expandtable(), _hash_first(), hashbulkdelete(), hashendscan(), hashrescan(), and pgstat_hash_page().

{
    if (USELOCKING(rel))
        UnlockPage(rel, whichlock, access);
}

void _hash_dropscan ( IndexScanDesc  scan  ) 

Definition at line 109 of file hashscan.c.

References elog, ERROR, HashScanListData::hashsl_next, HashScanListData::hashsl_scan, NULL, and pfree().

Referenced by hashendscan().

{
    HashScanList chk,
                last;

    last = NULL;
    for (chk = HashScans;
         chk != NULL && chk->hashsl_scan != scan;
         chk = chk->hashsl_next)
        last = chk;

    if (chk == NULL)
        elog(ERROR, "hash scan list trashed; cannot find 0x%p", (void *) scan);

    if (last == NULL)
        HashScans = chk->hashsl_next;
    else
        last->hashsl_next = chk->hashsl_next;

    pfree(chk);
}

void _hash_expandtable ( Relation  rel,
Buffer  metabuf 
)

Definition at line 497 of file hashpage.c.

References _hash_alloc_buckets(), _hash_checkpage(), _hash_chgbufaccess(), _hash_droplock(), _hash_has_active_scan(), _hash_log2(), _hash_splitbucket(), _hash_try_getlock(), Assert, BUCKET_TO_BLKNO, BufferGetPage, elog, END_CRIT_SECTION, ERROR, HASH_EXCLUSIVE, HASH_NOLOCK, HASH_READ, HASH_WRITE, HashMetaPageData::hashm_ffactor, HashMetaPageData::hashm_highmask, HashMetaPageData::hashm_lowmask, HashMetaPageData::hashm_maxbucket, HashMetaPageData::hashm_ntuples, HashMetaPageData::hashm_ovflpoint, HashMetaPageData::hashm_spares, HashPageGetMeta, LH_META_PAGE, and START_CRIT_SECTION.

Referenced by _hash_doinsert().

{
    HashMetaPage metap;
    Bucket      old_bucket;
    Bucket      new_bucket;
    uint32      spare_ndx;
    BlockNumber start_oblkno;
    BlockNumber start_nblkno;
    uint32      maxbucket;
    uint32      highmask;
    uint32      lowmask;

    /*
     * Write-lock the meta page.  It used to be necessary to acquire a
     * heavyweight lock to begin a split, but that is no longer required.
     */
    _hash_chgbufaccess(rel, metabuf, HASH_NOLOCK, HASH_WRITE);

    _hash_checkpage(rel, metabuf, LH_META_PAGE);
    metap = HashPageGetMeta(BufferGetPage(metabuf));

    /*
     * Check to see if split is still needed; someone else might have already
     * done one while we waited for the lock.
     *
     * Make sure this stays in sync with _hash_doinsert()
     */
    if (metap->hashm_ntuples <=
        (double) metap->hashm_ffactor * (metap->hashm_maxbucket + 1))
        goto fail;

    /*
     * Can't split anymore if maxbucket has reached its maximum possible
     * value.
     *
     * Ideally we'd allow bucket numbers up to UINT_MAX-1 (no higher because
     * the calculation maxbucket+1 mustn't overflow).  Currently we restrict
     * to half that because of overflow looping in _hash_log2() and
     * insufficient space in hashm_spares[].  It's moot anyway because an
     * index with 2^32 buckets would certainly overflow BlockNumber and hence
     * _hash_alloc_buckets() would fail, but if we supported buckets smaller
     * than a disk block then this would be an independent constraint.
     *
     * If you change this, see also the maximum initial number of buckets in
     * _hash_metapinit().
     */
    if (metap->hashm_maxbucket >= (uint32) 0x7FFFFFFE)
        goto fail;

    /*
     * Determine which bucket is to be split, and attempt to lock the old
     * bucket.  If we can't get the lock, give up.
     *
     * The lock protects us against other backends, but not against our own
     * backend.  Must check for active scans separately.
     */
    new_bucket = metap->hashm_maxbucket + 1;

    old_bucket = (new_bucket & metap->hashm_lowmask);

    start_oblkno = BUCKET_TO_BLKNO(metap, old_bucket);

    if (_hash_has_active_scan(rel, old_bucket))
        goto fail;

    if (!_hash_try_getlock(rel, start_oblkno, HASH_EXCLUSIVE))
        goto fail;

    /*
     * Likewise lock the new bucket (should never fail).
     *
     * Note: it is safe to compute the new bucket's blkno here, even though we
     * may still need to update the BUCKET_TO_BLKNO mapping.  This is because
     * the current value of hashm_spares[hashm_ovflpoint] correctly shows
     * where we are going to put a new splitpoint's worth of buckets.
     */
    start_nblkno = BUCKET_TO_BLKNO(metap, new_bucket);

    if (_hash_has_active_scan(rel, new_bucket))
        elog(ERROR, "scan in progress on supposedly new bucket");

    if (!_hash_try_getlock(rel, start_nblkno, HASH_EXCLUSIVE))
        elog(ERROR, "could not get lock on supposedly new bucket");

    /*
     * If the split point is increasing (hashm_maxbucket's log base 2
     * increases), we need to allocate a new batch of bucket pages.
     */
    spare_ndx = _hash_log2(new_bucket + 1);
    if (spare_ndx > metap->hashm_ovflpoint)
    {
        Assert(spare_ndx == metap->hashm_ovflpoint + 1);

        /*
         * The number of buckets in the new splitpoint is equal to the total
         * number already in existence, i.e. new_bucket.  Currently this maps
         * one-to-one to blocks required, but someday we may need a more
         * complicated calculation here.
         */
        if (!_hash_alloc_buckets(rel, start_nblkno, new_bucket))
        {
            /* can't split due to BlockNumber overflow */
            _hash_droplock(rel, start_oblkno, HASH_EXCLUSIVE);
            _hash_droplock(rel, start_nblkno, HASH_EXCLUSIVE);
            goto fail;
        }
    }

    /*
     * Okay to proceed with split.  Update the metapage bucket mapping info.
     *
     * Since we are scribbling on the metapage data right in the shared
     * buffer, any failure in this next little bit leaves us with a big
     * problem: the metapage is effectively corrupt but could get written back
     * to disk.  We don't really expect any failure, but just to be sure,
     * establish a critical section.
     */
    START_CRIT_SECTION();

    metap->hashm_maxbucket = new_bucket;

    if (new_bucket > metap->hashm_highmask)
    {
        /* Starting a new doubling */
        metap->hashm_lowmask = metap->hashm_highmask;
        metap->hashm_highmask = new_bucket | metap->hashm_lowmask;
    }

    /*
     * If the split point is increasing (hashm_maxbucket's log base 2
     * increases), we need to adjust the hashm_spares[] array and
     * hashm_ovflpoint so that future overflow pages will be created beyond
     * this new batch of bucket pages.
     */
    if (spare_ndx > metap->hashm_ovflpoint)
    {
        metap->hashm_spares[spare_ndx] = metap->hashm_spares[metap->hashm_ovflpoint];
        metap->hashm_ovflpoint = spare_ndx;
    }

    /* Done mucking with metapage */
    END_CRIT_SECTION();

    /*
     * Copy bucket mapping info now; this saves re-accessing the meta page
     * inside _hash_splitbucket's inner loop.  Note that once we drop the
     * split lock, other splits could begin, so these values might be out of
     * date before _hash_splitbucket finishes.  That's okay, since all it
     * needs is to tell which of these two buckets to map hashkeys into.
     */
    maxbucket = metap->hashm_maxbucket;
    highmask = metap->hashm_highmask;
    lowmask = metap->hashm_lowmask;

    /* Write out the metapage and drop lock, but keep pin */
    _hash_chgbufaccess(rel, metabuf, HASH_WRITE, HASH_NOLOCK);

    /* Relocate records to the new bucket */
    _hash_splitbucket(rel, metabuf, old_bucket, new_bucket,
                      start_oblkno, start_nblkno,
                      maxbucket, highmask, lowmask);

    /* Release bucket locks, allowing others to access them */
    _hash_droplock(rel, start_oblkno, HASH_EXCLUSIVE);
    _hash_droplock(rel, start_nblkno, HASH_EXCLUSIVE);

    return;

    /* Here if decide not to split or fail to acquire old bucket lock */
fail:

    /* We didn't write the metapage, so just drop lock */
    _hash_chgbufaccess(rel, metabuf, HASH_READ, HASH_NOLOCK);
}

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;
}

IndexTuple _hash_form_tuple ( Relation  index,
Datum values,
bool isnull 
)

Definition at line 254 of file hashutil.c.

References _hash_datum2hashkey(), Assert, index_form_tuple(), tupleDesc::natts, RelationGetDescr, and UInt32GetDatum.

Referenced by hashbuildCallback(), and hashinsert().

{
    IndexTuple  itup;
    uint32      hashkey;
    Datum       hashkeydatum;
    TupleDesc   hashdesc;

    if (isnull[0])
        hashkeydatum = (Datum) 0;
    else
    {
        hashkey = _hash_datum2hashkey(index, values[0]);
        hashkeydatum = UInt32GetDatum(hashkey);
    }
    hashdesc = RelationGetDescr(index);
    Assert(hashdesc->natts == 1);
    itup = index_form_tuple(hashdesc, &hashkeydatum, isnull);
    return itup;
}

BlockNumber _hash_freeovflpage ( Relation  rel,
Buffer  ovflbuf,
BufferAccessStrategy  bstrategy 
)

Definition at line 377 of file hashovfl.c.

References _hash_checkpage(), _hash_chgbufaccess(), _hash_getbuf(), _hash_getbuf_with_strategy(), _hash_relbuf(), _hash_wrtbuf(), Assert, blkno_to_bitno(), BlockNumberIsValid, BMPG_MASK, BMPG_SHIFT, BufferGetBlockNumber(), BufferGetPage, BufferGetPageSize, CLRBIT, elog, ERROR, HASH_METAPAGE, HASH_NOLOCK, HASH_READ, HASH_WRITE, HashMetaPageData::hashm_firstfree, HashMetaPageData::hashm_mapp, HashMetaPageData::hashm_nmaps, HashPageOpaqueData::hasho_bucket, HashPageOpaqueData::hasho_nextblkno, HashPageOpaqueData::hasho_prevblkno, HashPageGetBitmap, HashPageGetMeta, ISSET, LH_BITMAP_PAGE, LH_BUCKET_PAGE, LH_META_PAGE, LH_OVERFLOW_PAGE, MemSet, and PageGetSpecialPointer.

Referenced by _hash_squeezebucket().

{
    HashMetaPage metap;
    Buffer      metabuf;
    Buffer      mapbuf;
    BlockNumber ovflblkno;
    BlockNumber prevblkno;
    BlockNumber blkno;
    BlockNumber nextblkno;
    HashPageOpaque ovflopaque;
    Page        ovflpage;
    Page        mappage;
    uint32     *freep;
    uint32      ovflbitno;
    int32       bitmappage,
                bitmapbit;
    Bucket bucket PG_USED_FOR_ASSERTS_ONLY;

    /* Get information from the doomed page */
    _hash_checkpage(rel, ovflbuf, LH_OVERFLOW_PAGE);
    ovflblkno = BufferGetBlockNumber(ovflbuf);
    ovflpage = BufferGetPage(ovflbuf);
    ovflopaque = (HashPageOpaque) PageGetSpecialPointer(ovflpage);
    nextblkno = ovflopaque->hasho_nextblkno;
    prevblkno = ovflopaque->hasho_prevblkno;
    bucket = ovflopaque->hasho_bucket;

    /*
     * Zero the page for debugging's sake; then write and release it. (Note:
     * if we failed to zero the page here, we'd have problems with the Assert
     * in _hash_pageinit() when the page is reused.)
     */
    MemSet(ovflpage, 0, BufferGetPageSize(ovflbuf));
    _hash_wrtbuf(rel, ovflbuf);

    /*
     * Fix up the bucket chain.  this is a doubly-linked list, so we must fix
     * up the bucket chain members behind and ahead of the overflow page being
     * deleted.  No concurrency issues since we hold exclusive lock on the
     * entire bucket.
     */
    if (BlockNumberIsValid(prevblkno))
    {
        Buffer      prevbuf = _hash_getbuf_with_strategy(rel,
                                                         prevblkno,
                                                         HASH_WRITE,
                                           LH_BUCKET_PAGE | LH_OVERFLOW_PAGE,
                                                         bstrategy);
        Page        prevpage = BufferGetPage(prevbuf);
        HashPageOpaque prevopaque = (HashPageOpaque) PageGetSpecialPointer(prevpage);

        Assert(prevopaque->hasho_bucket == bucket);
        prevopaque->hasho_nextblkno = nextblkno;
        _hash_wrtbuf(rel, prevbuf);
    }
    if (BlockNumberIsValid(nextblkno))
    {
        Buffer      nextbuf = _hash_getbuf_with_strategy(rel,
                                                         nextblkno,
                                                         HASH_WRITE,
                                                         LH_OVERFLOW_PAGE,
                                                         bstrategy);
        Page        nextpage = BufferGetPage(nextbuf);
        HashPageOpaque nextopaque = (HashPageOpaque) PageGetSpecialPointer(nextpage);

        Assert(nextopaque->hasho_bucket == bucket);
        nextopaque->hasho_prevblkno = prevblkno;
        _hash_wrtbuf(rel, nextbuf);
    }

    /* Note: bstrategy is intentionally not used for metapage and bitmap */

    /* Read the metapage so we can determine which bitmap page to use */
    metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
    metap = HashPageGetMeta(BufferGetPage(metabuf));

    /* Identify which bit to set */
    ovflbitno = blkno_to_bitno(metap, ovflblkno);

    bitmappage = ovflbitno >> BMPG_SHIFT(metap);
    bitmapbit = ovflbitno & BMPG_MASK(metap);

    if (bitmappage >= metap->hashm_nmaps)
        elog(ERROR, "invalid overflow bit number %u", ovflbitno);
    blkno = metap->hashm_mapp[bitmappage];

    /* Release metapage lock while we access the bitmap page */
    _hash_chgbufaccess(rel, metabuf, HASH_READ, HASH_NOLOCK);

    /* Clear the bitmap bit to indicate that this overflow page is free */
    mapbuf = _hash_getbuf(rel, blkno, HASH_WRITE, LH_BITMAP_PAGE);
    mappage = BufferGetPage(mapbuf);
    freep = HashPageGetBitmap(mappage);
    Assert(ISSET(freep, bitmapbit));
    CLRBIT(freep, bitmapbit);
    _hash_wrtbuf(rel, mapbuf);

    /* Get write-lock on metapage to update firstfree */
    _hash_chgbufaccess(rel, metabuf, HASH_NOLOCK, HASH_WRITE);

    /* if this is now the first free page, update hashm_firstfree */
    if (ovflbitno < metap->hashm_firstfree)
    {
        metap->hashm_firstfree = ovflbitno;
        _hash_wrtbuf(rel, metabuf);
    }
    else
    {
        /* no need to change metapage */
        _hash_relbuf(rel, metabuf);
    }

    return nextblkno;
}

uint32 _hash_get_indextuple_hashkey ( IndexTuple  itup  ) 

Definition at line 238 of file hashutil.c.

References IndexInfoFindDataOffset, and IndexTupleData::t_info.

Referenced by _hash_binsearch(), _hash_binsearch_last(), _hash_doinsert(), _hash_pgaddtup(), _hash_splitbucket(), and _hash_step().

{
    char       *attp;

    /*
     * We assume the hash key is the first attribute and can't be null, so
     * this can be done crudely but very very cheaply ...
     */
    attp = (char *) itup + IndexInfoFindDataOffset(itup->t_info);
    return *((uint32 *) attp);
}

Buffer _hash_getbuf ( Relation  rel,
BlockNumber  blkno,
int  access,
int  flags 
)

Definition at line 115 of file hashpage.c.

References _hash_checkpage(), buf, elog, ERROR, HASH_NOLOCK, LockBuffer(), P_NEW, and ReadBuffer().

Referenced by _hash_addovflpage(), _hash_doinsert(), _hash_first(), _hash_freeovflpage(), _hash_getovflpage(), _hash_readnext(), _hash_readprev(), _hash_splitbucket(), and hashbulkdelete().

{
    Buffer      buf;

    if (blkno == P_NEW)
        elog(ERROR, "hash AM does not use P_NEW");

    buf = ReadBuffer(rel, blkno);

    if (access != HASH_NOLOCK)
        LockBuffer(buf, access);

    /* ref count and lock type are correct */

    _hash_checkpage(rel, buf, flags);

    return buf;
}

Buffer _hash_getbuf_with_strategy ( Relation  rel,
BlockNumber  blkno,
int  access,
int  flags,
BufferAccessStrategy  bstrategy 
)

Definition at line 222 of file hashpage.c.

References _hash_checkpage(), buf, elog, ERROR, HASH_NOLOCK, LockBuffer(), MAIN_FORKNUM, P_NEW, RBM_NORMAL, and ReadBufferExtended().

Referenced by _hash_freeovflpage(), _hash_squeezebucket(), hashbulkdelete(), and pgstat_hash_page().

{
    Buffer      buf;

    if (blkno == P_NEW)
        elog(ERROR, "hash AM does not use P_NEW");

    buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, bstrategy);

    if (access != HASH_NOLOCK)
        LockBuffer(buf, access);

    /* ref count and lock type are correct */

    _hash_checkpage(rel, buf, flags);

    return buf;
}

Buffer _hash_getinitbuf ( Relation  rel,
BlockNumber  blkno 
)

Definition at line 151 of file hashpage.c.

References _hash_pageinit(), buf, BufferGetPage, BufferGetPageSize, elog, ERROR, HASH_WRITE, LockBuffer(), MAIN_FORKNUM, NULL, P_NEW, RBM_ZERO, and ReadBufferExtended().

Referenced by _hash_getovflpage().

{
    Buffer      buf;

    if (blkno == P_NEW)
        elog(ERROR, "hash AM does not use P_NEW");

    buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_ZERO, NULL);

    LockBuffer(buf, HASH_WRITE);

    /* ref count and lock type are correct */

    /* initialize the page */
    _hash_pageinit(BufferGetPage(buf), BufferGetPageSize(buf));

    return buf;
}

void _hash_getlock ( Relation  rel,
BlockNumber  whichlock,
int  access 
)

Definition at line 67 of file hashpage.c.

References LockPage(), and USELOCKING.

Referenced by _hash_doinsert(), _hash_first(), hashbulkdelete(), and pgstat_hash_page().

{
    if (USELOCKING(rel))
        LockPage(rel, whichlock, access);
}

Buffer _hash_getnewbuf ( Relation  rel,
BlockNumber  blkno,
ForkNumber  forkNum 
)

Definition at line 183 of file hashpage.c.

References _hash_pageinit(), buf, BufferGetBlockNumber(), BufferGetPage, BufferGetPageSize, elog, ERROR, HASH_WRITE, LockBuffer(), NULL, P_NEW, RBM_NORMAL, RBM_ZERO, ReadBufferExtended(), RelationGetNumberOfBlocksInFork(), and RelationGetRelationName.

Referenced by _hash_getovflpage(), _hash_initbitmap(), _hash_metapinit(), and _hash_splitbucket().

{
    BlockNumber nblocks = RelationGetNumberOfBlocksInFork(rel, forkNum);
    Buffer      buf;

    if (blkno == P_NEW)
        elog(ERROR, "hash AM does not use P_NEW");
    if (blkno > nblocks)
        elog(ERROR, "access to noncontiguous page in hash index \"%s\"",
             RelationGetRelationName(rel));

    /* smgr insists we use P_NEW to extend the relation */
    if (blkno == nblocks)
    {
        buf = ReadBufferExtended(rel, forkNum, P_NEW, RBM_NORMAL, NULL);
        if (BufferGetBlockNumber(buf) != blkno)
            elog(ERROR, "unexpected hash relation size: %u, should be %u",
                 BufferGetBlockNumber(buf), blkno);
    }
    else
        buf = ReadBufferExtended(rel, forkNum, blkno, RBM_ZERO, NULL);

    LockBuffer(buf, HASH_WRITE);

    /* ref count and lock type are correct */

    /* initialize the page */
    _hash_pageinit(BufferGetPage(buf), BufferGetPageSize(buf));

    return buf;
}

bool _hash_has_active_scan ( Relation  rel,
Bucket  bucket 
)
Bucket _hash_hashkey2bucket ( uint32  hashkey,
uint32  maxbucket,
uint32  highmask,
uint32  lowmask 
)

Definition at line 122 of file hashutil.c.

Referenced by _hash_doinsert(), _hash_first(), and _hash_splitbucket().

{
    Bucket      bucket;

    bucket = hashkey & highmask;
    if (bucket > maxbucket)
        bucket = bucket & lowmask;

    return bucket;
}

void _hash_initbitmap ( Relation  rel,
HashMetaPage  metap,
BlockNumber  blkno,
ForkNumber  forkNum 
)

Definition at line 505 of file hashovfl.c.

References _hash_getnewbuf(), _hash_wrtbuf(), BMPGSZ_BYTE, buf, BufferGetPage, ereport, errcode(), errmsg(), ERROR, HASH_MAX_BITMAPS, HashMetaPageData::hashm_mapp, HashMetaPageData::hashm_nmaps, HashPageOpaqueData::hasho_bucket, HashPageOpaqueData::hasho_flag, HashPageOpaqueData::hasho_nextblkno, HashPageOpaqueData::hasho_page_id, HashPageOpaqueData::hasho_prevblkno, HashPageGetBitmap, MemSet, PageGetSpecialPointer, and RelationGetRelationName.

Referenced by _hash_getovflpage(), and _hash_metapinit().

{
    Buffer      buf;
    Page        pg;
    HashPageOpaque op;
    uint32     *freep;

    /*
     * It is okay to write-lock the new bitmap page while holding metapage
     * write lock, because no one else could be contending for the new page.
     * Also, the metapage lock makes it safe to extend the index using
     * _hash_getnewbuf.
     *
     * There is some loss of concurrency in possibly doing I/O for the new
     * page while holding the metapage lock, but this path is taken so seldom
     * that it's not worth worrying about.
     */
    buf = _hash_getnewbuf(rel, blkno, forkNum);
    pg = BufferGetPage(buf);

    /* initialize the page's special space */
    op = (HashPageOpaque) PageGetSpecialPointer(pg);
    op->hasho_prevblkno = InvalidBlockNumber;
    op->hasho_nextblkno = InvalidBlockNumber;
    op->hasho_bucket = -1;
    op->hasho_flag = LH_BITMAP_PAGE;
    op->hasho_page_id = HASHO_PAGE_ID;

    /* set all of the bits to 1 */
    freep = HashPageGetBitmap(pg);
    MemSet(freep, 0xFF, BMPGSZ_BYTE(metap));

    /* write out the new bitmap page (releasing write lock and pin) */
    _hash_wrtbuf(rel, buf);

    /* add the new bitmap page to the metapage's list of bitmaps */
    /* metapage already has a write lock */
    if (metap->hashm_nmaps >= HASH_MAX_BITMAPS)
        ereport(ERROR,
                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                 errmsg("out of overflow pages in hash index \"%s\"",
                        RelationGetRelationName(rel))));

    metap->hashm_mapp[metap->hashm_nmaps] = blkno;

    metap->hashm_nmaps++;
}

uint32 _hash_log2 ( uint32  num  ) 

Definition at line 138 of file hashutil.c.

References i.

Referenced by _h_spoolinit(), _hash_expandtable(), and _hash_metapinit().

{
    uint32      i,
                limit;

    limit = 1;
    for (i = 0; limit < num; limit <<= 1, i++)
        ;
    return i;
}

uint32 _hash_metapinit ( Relation  rel,
double  num_tuples,
ForkNumber  forkNum 
)

Definition at line 324 of file hashpage.c.

References _hash_chgbufaccess(), _hash_getnewbuf(), _hash_initbitmap(), _hash_log2(), _hash_wrtbuf(), Assert, BMPG_MASK, BMPG_SHIFT, BUCKET_TO_BLKNO, buf, BufferGetPage, CHECK_FOR_INTERRUPTS, elog, ERROR, HASH_DEFAULT_FILLFACTOR, HASH_MAX_SPLITPOINTS, HASH_METAPAGE, HASH_NOLOCK, HASH_WRITE, HashGetMaxBitmapSize, HashMetaPageData::hashm_bmshift, HashMetaPageData::hashm_bmsize, HashMetaPageData::hashm_bsize, HashMetaPageData::hashm_ffactor, HashMetaPageData::hashm_firstfree, HashMetaPageData::hashm_highmask, HashMetaPageData::hashm_lowmask, HashMetaPageData::hashm_magic, HashMetaPageData::hashm_mapp, HashMetaPageData::hashm_maxbucket, HashMetaPageData::hashm_nmaps, HashMetaPageData::hashm_ntuples, HashMetaPageData::hashm_ovflpoint, HashMetaPageData::hashm_procid, HashMetaPageData::hashm_spares, HashMetaPageData::hashm_version, HashPageOpaqueData::hasho_bucket, HashPageOpaqueData::hasho_flag, HashPageOpaqueData::hasho_nextblkno, HashPageOpaqueData::hasho_page_id, HashPageOpaqueData::hasho_prevblkno, HashPageGetMeta, HASHPROC, i, index_getprocid(), MAXALIGN, MemSet, PageGetSpecialPointer, RelationGetNumberOfBlocksInFork(), RelationGetRelationName, and RelationGetTargetPageUsage.

Referenced by hashbuild(), and hashbuildempty().

{
    HashMetaPage metap;
    HashPageOpaque pageopaque;
    Buffer      metabuf;
    Buffer      buf;
    Page        pg;
    int32       data_width;
    int32       item_width;
    int32       ffactor;
    double      dnumbuckets;
    uint32      num_buckets;
    uint32      log2_num_buckets;
    uint32      i;

    /* safety check */
    if (RelationGetNumberOfBlocksInFork(rel, forkNum) != 0)
        elog(ERROR, "cannot initialize non-empty hash index \"%s\"",
             RelationGetRelationName(rel));

    /*
     * Determine the target fill factor (in tuples per bucket) for this index.
     * The idea is to make the fill factor correspond to pages about as full
     * as the user-settable fillfactor parameter says.  We can compute it
     * exactly since the index datatype (i.e. uint32 hash key) is fixed-width.
     */
    data_width = sizeof(uint32);
    item_width = MAXALIGN(sizeof(IndexTupleData)) + MAXALIGN(data_width) +
        sizeof(ItemIdData);     /* include the line pointer */
    ffactor = RelationGetTargetPageUsage(rel, HASH_DEFAULT_FILLFACTOR) / item_width;
    /* keep to a sane range */
    if (ffactor < 10)
        ffactor = 10;

    /*
     * Choose the number of initial bucket pages to match the fill factor
     * given the estimated number of tuples.  We round up the result to the
     * next power of 2, however, and always force at least 2 bucket pages. The
     * upper limit is determined by considerations explained in
     * _hash_expandtable().
     */
    dnumbuckets = num_tuples / ffactor;
    if (dnumbuckets <= 2.0)
        num_buckets = 2;
    else if (dnumbuckets >= (double) 0x40000000)
        num_buckets = 0x40000000;
    else
        num_buckets = ((uint32) 1) << _hash_log2((uint32) dnumbuckets);

    log2_num_buckets = _hash_log2(num_buckets);
    Assert(num_buckets == (((uint32) 1) << log2_num_buckets));
    Assert(log2_num_buckets < HASH_MAX_SPLITPOINTS);

    /*
     * We initialize the metapage, the first N bucket pages, and the first
     * bitmap page in sequence, using _hash_getnewbuf to cause smgrextend()
     * calls to occur.  This ensures that the smgr level has the right idea of
     * the physical index length.
     */
    metabuf = _hash_getnewbuf(rel, HASH_METAPAGE, forkNum);
    pg = BufferGetPage(metabuf);

    pageopaque = (HashPageOpaque) PageGetSpecialPointer(pg);
    pageopaque->hasho_prevblkno = InvalidBlockNumber;
    pageopaque->hasho_nextblkno = InvalidBlockNumber;
    pageopaque->hasho_bucket = -1;
    pageopaque->hasho_flag = LH_META_PAGE;
    pageopaque->hasho_page_id = HASHO_PAGE_ID;

    metap = HashPageGetMeta(pg);

    metap->hashm_magic = HASH_MAGIC;
    metap->hashm_version = HASH_VERSION;
    metap->hashm_ntuples = 0;
    metap->hashm_nmaps = 0;
    metap->hashm_ffactor = ffactor;
    metap->hashm_bsize = HashGetMaxBitmapSize(pg);
    /* find largest bitmap array size that will fit in page size */
    for (i = _hash_log2(metap->hashm_bsize); i > 0; --i)
    {
        if ((1 << i) <= metap->hashm_bsize)
            break;
    }
    Assert(i > 0);
    metap->hashm_bmsize = 1 << i;
    metap->hashm_bmshift = i + BYTE_TO_BIT;
    Assert((1 << BMPG_SHIFT(metap)) == (BMPG_MASK(metap) + 1));

    /*
     * Label the index with its primary hash support function's OID.  This is
     * pretty useless for normal operation (in fact, hashm_procid is not used
     * anywhere), but it might be handy for forensic purposes so we keep it.
     */
    metap->hashm_procid = index_getprocid(rel, 1, HASHPROC);

    /*
     * We initialize the index with N buckets, 0 .. N-1, occupying physical
     * blocks 1 to N.  The first freespace bitmap page is in block N+1. Since
     * N is a power of 2, we can set the masks this way:
     */
    metap->hashm_maxbucket = metap->hashm_lowmask = num_buckets - 1;
    metap->hashm_highmask = (num_buckets << 1) - 1;

    MemSet(metap->hashm_spares, 0, sizeof(metap->hashm_spares));
    MemSet(metap->hashm_mapp, 0, sizeof(metap->hashm_mapp));

    /* Set up mapping for one spare page after the initial splitpoints */
    metap->hashm_spares[log2_num_buckets] = 1;
    metap->hashm_ovflpoint = log2_num_buckets;
    metap->hashm_firstfree = 0;

    /*
     * Release buffer lock on the metapage while we initialize buckets.
     * Otherwise, we'll be in interrupt holdoff and the CHECK_FOR_INTERRUPTS
     * won't accomplish anything.  It's a bad idea to hold buffer locks for
     * long intervals in any case, since that can block the bgwriter.
     */
    _hash_chgbufaccess(rel, metabuf, HASH_WRITE, HASH_NOLOCK);

    /*
     * Initialize the first N buckets
     */
    for (i = 0; i < num_buckets; i++)
    {
        /* Allow interrupts, in case N is huge */
        CHECK_FOR_INTERRUPTS();

        buf = _hash_getnewbuf(rel, BUCKET_TO_BLKNO(metap, i), forkNum);
        pg = BufferGetPage(buf);
        pageopaque = (HashPageOpaque) PageGetSpecialPointer(pg);
        pageopaque->hasho_prevblkno = InvalidBlockNumber;
        pageopaque->hasho_nextblkno = InvalidBlockNumber;
        pageopaque->hasho_bucket = i;
        pageopaque->hasho_flag = LH_BUCKET_PAGE;
        pageopaque->hasho_page_id = HASHO_PAGE_ID;
        _hash_wrtbuf(rel, buf);
    }

    /* Now reacquire buffer lock on metapage */
    _hash_chgbufaccess(rel, metabuf, HASH_NOLOCK, HASH_WRITE);

    /*
     * Initialize first bitmap page
     */
    _hash_initbitmap(rel, metap, num_buckets + 1, forkNum);

    /* all done */
    _hash_wrtbuf(rel, metabuf);

    return num_buckets;
}

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;
}

void _hash_pageinit ( Page  page,
Size  size 
)

Definition at line 480 of file hashpage.c.

References Assert, PageInit(), and PageIsNew.

Referenced by _hash_getinitbuf(), and _hash_getnewbuf().

{
    Assert(PageIsNew(page));
    PageInit(page, size, sizeof(HashPageOpaqueData));
}

OffsetNumber _hash_pgaddtup ( Relation  rel,
Buffer  buf,
Size  itemsize,
IndexTuple  itup 
)

Definition at line 203 of file hashinsert.c.

References _hash_binsearch(), _hash_checkpage(), _hash_get_indextuple_hashkey(), BufferGetPage, elog, ERROR, InvalidOffsetNumber, LH_BUCKET_PAGE, LH_OVERFLOW_PAGE, PageAddItem(), and RelationGetRelationName.

Referenced by _hash_doinsert(), _hash_splitbucket(), and _hash_squeezebucket().

{
    OffsetNumber itup_off;
    Page        page;
    uint32      hashkey;

    _hash_checkpage(rel, buf, LH_BUCKET_PAGE | LH_OVERFLOW_PAGE);
    page = BufferGetPage(buf);

    /* Find where to insert the tuple (preserving page's hashkey ordering) */
    hashkey = _hash_get_indextuple_hashkey(itup);
    itup_off = _hash_binsearch(page, hashkey);

    if (PageAddItem(page, (Item) itup, itemsize, itup_off, false, false)
        == InvalidOffsetNumber)
        elog(ERROR, "failed to add index item to \"%s\"",
             RelationGetRelationName(rel));

    return itup_off;
}

void _hash_regscan ( IndexScanDesc  scan  ) 
void _hash_relbuf ( Relation  rel,
Buffer  buf 
)
void _hash_squeezebucket ( Relation  rel,
Bucket  bucket,
BlockNumber  bucket_blkno,
BufferAccessStrategy  bstrategy 
)

Definition at line 580 of file hashovfl.c.

References _hash_freeovflpage(), _hash_getbuf_with_strategy(), _hash_pgaddtup(), _hash_relbuf(), _hash_wrtbuf(), Assert, BlockNumberIsValid, BufferGetPage, FirstOffsetNumber, HASH_WRITE, HashPageOpaqueData::hasho_bucket, HashPageOpaqueData::hasho_nextblkno, HashPageOpaqueData::hasho_prevblkno, IndexTupleDSize, InvalidBuffer, LH_BUCKET_PAGE, LH_OVERFLOW_PAGE, MAXALIGN, OffsetNumberNext, PageGetFreeSpace(), PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageGetSpecialPointer, PageIndexMultiDelete(), and PageIsEmpty.

Referenced by _hash_splitbucket(), and hashbulkdelete().

{
    BlockNumber wblkno;
    BlockNumber rblkno;
    Buffer      wbuf;
    Buffer      rbuf;
    Page        wpage;
    Page        rpage;
    HashPageOpaque wopaque;
    HashPageOpaque ropaque;
    bool        wbuf_dirty;

    /*
     * start squeezing into the base bucket page.
     */
    wblkno = bucket_blkno;
    wbuf = _hash_getbuf_with_strategy(rel,
                                      wblkno,
                                      HASH_WRITE,
                                      LH_BUCKET_PAGE,
                                      bstrategy);
    wpage = BufferGetPage(wbuf);
    wopaque = (HashPageOpaque) PageGetSpecialPointer(wpage);

    /*
     * if there aren't any overflow pages, there's nothing to squeeze.
     */
    if (!BlockNumberIsValid(wopaque->hasho_nextblkno))
    {
        _hash_relbuf(rel, wbuf);
        return;
    }

    /*
     * Find the last page in the bucket chain by starting at the base bucket
     * page and working forward.  Note: we assume that a hash bucket chain is
     * usually smaller than the buffer ring being used by VACUUM, else using
     * the access strategy here would be counterproductive.
     */
    rbuf = InvalidBuffer;
    ropaque = wopaque;
    do
    {
        rblkno = ropaque->hasho_nextblkno;
        if (rbuf != InvalidBuffer)
            _hash_relbuf(rel, rbuf);
        rbuf = _hash_getbuf_with_strategy(rel,
                                          rblkno,
                                          HASH_WRITE,
                                          LH_OVERFLOW_PAGE,
                                          bstrategy);
        rpage = BufferGetPage(rbuf);
        ropaque = (HashPageOpaque) PageGetSpecialPointer(rpage);
        Assert(ropaque->hasho_bucket == bucket);
    } while (BlockNumberIsValid(ropaque->hasho_nextblkno));

    /*
     * squeeze the tuples.
     */
    wbuf_dirty = false;
    for (;;)
    {
        OffsetNumber roffnum;
        OffsetNumber maxroffnum;
        OffsetNumber deletable[MaxOffsetNumber];
        int         ndeletable = 0;

        /* Scan each tuple in "read" page */
        maxroffnum = PageGetMaxOffsetNumber(rpage);
        for (roffnum = FirstOffsetNumber;
             roffnum <= maxroffnum;
             roffnum = OffsetNumberNext(roffnum))
        {
            IndexTuple  itup;
            Size        itemsz;

            itup = (IndexTuple) PageGetItem(rpage,
                                            PageGetItemId(rpage, roffnum));
            itemsz = IndexTupleDSize(*itup);
            itemsz = MAXALIGN(itemsz);

            /*
             * Walk up the bucket chain, looking for a page big enough for
             * this item.  Exit if we reach the read page.
             */
            while (PageGetFreeSpace(wpage) < itemsz)
            {
                Assert(!PageIsEmpty(wpage));

                wblkno = wopaque->hasho_nextblkno;
                Assert(BlockNumberIsValid(wblkno));

                if (wbuf_dirty)
                    _hash_wrtbuf(rel, wbuf);
                else
                    _hash_relbuf(rel, wbuf);

                /* nothing more to do if we reached the read page */
                if (rblkno == wblkno)
                {
                    if (ndeletable > 0)
                    {
                        /* Delete tuples we already moved off read page */
                        PageIndexMultiDelete(rpage, deletable, ndeletable);
                        _hash_wrtbuf(rel, rbuf);
                    }
                    else
                        _hash_relbuf(rel, rbuf);
                    return;
                }

                wbuf = _hash_getbuf_with_strategy(rel,
                                                  wblkno,
                                                  HASH_WRITE,
                                                  LH_OVERFLOW_PAGE,
                                                  bstrategy);
                wpage = BufferGetPage(wbuf);
                wopaque = (HashPageOpaque) PageGetSpecialPointer(wpage);
                Assert(wopaque->hasho_bucket == bucket);
                wbuf_dirty = false;
            }

            /*
             * we have found room so insert on the "write" page, being careful
             * to preserve hashkey ordering.  (If we insert many tuples into
             * the same "write" page it would be worth qsort'ing instead of
             * doing repeated _hash_pgaddtup.)
             */
            (void) _hash_pgaddtup(rel, wbuf, itemsz, itup);
            wbuf_dirty = true;

            /* remember tuple for deletion from "read" page */
            deletable[ndeletable++] = roffnum;
        }

        /*
         * If we reach here, there are no live tuples on the "read" page ---
         * it was empty when we got to it, or we moved them all.  So we can
         * just free the page without bothering with deleting tuples
         * individually.  Then advance to the previous "read" page.
         *
         * Tricky point here: if our read and write pages are adjacent in the
         * bucket chain, our write lock on wbuf will conflict with
         * _hash_freeovflpage's attempt to update the sibling links of the
         * removed page.  However, in that case we are done anyway, so we can
         * simply drop the write lock before calling _hash_freeovflpage.
         */
        rblkno = ropaque->hasho_prevblkno;
        Assert(BlockNumberIsValid(rblkno));

        /* are we freeing the page adjacent to wbuf? */
        if (rblkno == wblkno)
        {
            /* yes, so release wbuf lock first */
            if (wbuf_dirty)
                _hash_wrtbuf(rel, wbuf);
            else
                _hash_relbuf(rel, wbuf);
            /* free this overflow page (releases rbuf) */
            _hash_freeovflpage(rel, rbuf, bstrategy);
            /* done */
            return;
        }

        /* free this overflow page, then get the previous one */
        _hash_freeovflpage(rel, rbuf, bstrategy);

        rbuf = _hash_getbuf_with_strategy(rel,
                                          rblkno,
                                          HASH_WRITE,
                                          LH_OVERFLOW_PAGE,
                                          bstrategy);
        rpage = BufferGetPage(rbuf);
        ropaque = (HashPageOpaque) PageGetSpecialPointer(rpage);
        Assert(ropaque->hasho_bucket == bucket);
    }

    /* NOTREACHED */
}

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;
}

bool _hash_try_getlock ( Relation  rel,
BlockNumber  whichlock,
int  access 
)

Definition at line 79 of file hashpage.c.

References ConditionalLockPage(), and USELOCKING.

Referenced by _hash_expandtable().

{
    if (USELOCKING(rel))
        return ConditionalLockPage(rel, whichlock, access);
    else
        return true;
}

void _hash_wrtbuf ( Relation  rel,
Buffer  buf 
)
Datum hash_any ( register const unsigned char *  k,
register int  keylen 
)

Definition at line 305 of file hashfunc.c.

References mix, UINT32_ALIGN_MASK, and UInt32GetDatum.

Referenced by AppendJumble(), bms_hash_value(), citext_hash(), hash_numeric(), hashbpchar(), hashfloat4(), hashfloat8(), hashinet(), hashint2vector(), hashmacaddr(), hashname(), hashoidvector(), hashtext(), hashvarlena(), hstore_hash(), lexeme_hash(), pgss_hash_string(), pgss_post_parse_analyze(), sepgsql_avc_hash(), string_hash(), tag_hash(), and uuid_hash().

{
    register uint32 a,
                b,
                c,
                len;

    /* Set up the internal state */
    len = keylen;
    a = b = c = 0x9e3779b9 + len + 3923095;

    /* If the source pointer is word-aligned, we use word-wide fetches */
    if (((intptr_t) k & UINT32_ALIGN_MASK) == 0)
    {
        /* Code path for aligned source data */
        register const uint32 *ka = (const uint32 *) k;

        /* handle most of the key */
        while (len >= 12)
        {
            a += ka[0];
            b += ka[1];
            c += ka[2];
            mix(a, b, c);
            ka += 3;
            len -= 12;
        }

        /* handle the last 11 bytes */
        k = (const unsigned char *) ka;
#ifdef WORDS_BIGENDIAN
        switch (len)
        {
            case 11:
                c += ((uint32) k[10] << 8);
                /* fall through */
            case 10:
                c += ((uint32) k[9] << 16);
                /* fall through */
            case 9:
                c += ((uint32) k[8] << 24);
                /* the lowest byte of c is reserved for the length */
                /* fall through */
            case 8:
                b += ka[1];
                a += ka[0];
                break;
            case 7:
                b += ((uint32) k[6] << 8);
                /* fall through */
            case 6:
                b += ((uint32) k[5] << 16);
                /* fall through */
            case 5:
                b += ((uint32) k[4] << 24);
                /* fall through */
            case 4:
                a += ka[0];
                break;
            case 3:
                a += ((uint32) k[2] << 8);
                /* fall through */
            case 2:
                a += ((uint32) k[1] << 16);
                /* fall through */
            case 1:
                a += ((uint32) k[0] << 24);
                /* case 0: nothing left to add */
        }
#else                           /* !WORDS_BIGENDIAN */
        switch (len)
        {
            case 11:
                c += ((uint32) k[10] << 24);
                /* fall through */
            case 10:
                c += ((uint32) k[9] << 16);
                /* fall through */
            case 9:
                c += ((uint32) k[8] << 8);
                /* the lowest byte of c is reserved for the length */
                /* fall through */
            case 8:
                b += ka[1];
                a += ka[0];
                break;
            case 7:
                b += ((uint32) k[6] << 16);
                /* fall through */
            case 6:
                b += ((uint32) k[5] << 8);
                /* fall through */
            case 5:
                b += k[4];
                /* fall through */
            case 4:
                a += ka[0];
                break;
            case 3:
                a += ((uint32) k[2] << 16);
                /* fall through */
            case 2:
                a += ((uint32) k[1] << 8);
                /* fall through */
            case 1:
                a += k[0];
                /* case 0: nothing left to add */
        }
#endif   /* WORDS_BIGENDIAN */
    }
    else
    {
        /* Code path for non-aligned source data */

        /* handle most of the key */
        while (len >= 12)
        {
#ifdef WORDS_BIGENDIAN
            a += (k[3] + ((uint32) k[2] << 8) + ((uint32) k[1] << 16) + ((uint32) k[0] << 24));
            b += (k[7] + ((uint32) k[6] << 8) + ((uint32) k[5] << 16) + ((uint32) k[4] << 24));
            c += (k[11] + ((uint32) k[10] << 8) + ((uint32) k[9] << 16) + ((uint32) k[8] << 24));
#else                           /* !WORDS_BIGENDIAN */
            a += (k[0] + ((uint32) k[1] << 8) + ((uint32) k[2] << 16) + ((uint32) k[3] << 24));
            b += (k[4] + ((uint32) k[5] << 8) + ((uint32) k[6] << 16) + ((uint32) k[7] << 24));
            c += (k[8] + ((uint32) k[9] << 8) + ((uint32) k[10] << 16) + ((uint32) k[11] << 24));
#endif   /* WORDS_BIGENDIAN */
            mix(a, b, c);
            k += 12;
            len -= 12;
        }

        /* handle the last 11 bytes */
#ifdef WORDS_BIGENDIAN
        switch (len)            /* all the case statements fall through */
        {
            case 11:
                c += ((uint32) k[10] << 8);
            case 10:
                c += ((uint32) k[9] << 16);
            case 9:
                c += ((uint32) k[8] << 24);
                /* the lowest byte of c is reserved for the length */
            case 8:
                b += k[7];
            case 7:
                b += ((uint32) k[6] << 8);
            case 6:
                b += ((uint32) k[5] << 16);
            case 5:
                b += ((uint32) k[4] << 24);
            case 4:
                a += k[3];
            case 3:
                a += ((uint32) k[2] << 8);
            case 2:
                a += ((uint32) k[1] << 16);
            case 1:
                a += ((uint32) k[0] << 24);
                /* case 0: nothing left to add */
        }
#else                           /* !WORDS_BIGENDIAN */
        switch (len)            /* all the case statements fall through */
        {
            case 11:
                c += ((uint32) k[10] << 24);
            case 10:
                c += ((uint32) k[9] << 16);
            case 9:
                c += ((uint32) k[8] << 8);
                /* the lowest byte of c is reserved for the length */
            case 8:
                b += ((uint32) k[7] << 24);
            case 7:
                b += ((uint32) k[6] << 16);
            case 6:
                b += ((uint32) k[5] << 8);
            case 5:
                b += k[4];
            case 4:
                a += ((uint32) k[3] << 24);
            case 3:
                a += ((uint32) k[2] << 16);
            case 2:
                a += ((uint32) k[1] << 8);
            case 1:
                a += k[0];
                /* case 0: nothing left to add */
        }
#endif   /* WORDS_BIGENDIAN */
    }

    final(a, b, c);

    /* report the result */
    return UInt32GetDatum(c);
}

void hash_desc ( StringInfo  buf,
uint8  xl_info,
char *  rec 
)

Definition at line 20 of file hashdesc.c.

{
}

void hash_redo ( XLogRecPtr  lsn,
XLogRecord record 
)

Definition at line 709 of file hash.c.

References elog, and PANIC.

{
    elog(PANIC, "hash_redo: unimplemented");
}

Datum hash_uint32 ( uint32  k  ) 

Definition at line 510 of file hashfunc.c.

References UInt32GetDatum.

Referenced by hash_range(), hashchar(), hashenum(), hashint2(), hashint4(), hashint8(), hashoid(), oid_hash(), pgss_hash_fn(), and timetz_hash().

{
    register uint32 a,
                b,
                c;

    a = b = c = 0x9e3779b9 + (uint32) sizeof(uint32) + 3923095;
    a += k;

    final(a, b, c);

    /* report the result */
    return UInt32GetDatum(c);
}

Datum hashbeginscan ( PG_FUNCTION_ARGS   ) 

Definition at line 379 of file hash.c.

References _hash_regscan(), Assert, HashScanOpaqueData::hashso_bucket_blkno, HashScanOpaqueData::hashso_bucket_valid, HashScanOpaqueData::hashso_curbuf, HashScanOpaqueData::hashso_curpos, HashScanOpaqueData::hashso_heappos, ItemPointerSetInvalid, IndexScanDescData::opaque, palloc(), PG_GETARG_INT32, PG_GETARG_POINTER, PG_RETURN_POINTER, and RelationGetIndexScan().

{
    Relation    rel = (Relation) PG_GETARG_POINTER(0);
    int         nkeys = PG_GETARG_INT32(1);
    int         norderbys = PG_GETARG_INT32(2);
    IndexScanDesc scan;
    HashScanOpaque so;

    /* no order by operators allowed */
    Assert(norderbys == 0);

    scan = RelationGetIndexScan(rel, nkeys, norderbys);

    so = (HashScanOpaque) palloc(sizeof(HashScanOpaqueData));
    so->hashso_bucket_valid = false;
    so->hashso_bucket_blkno = 0;
    so->hashso_curbuf = InvalidBuffer;
    /* set position invalid (this will cause _hash_first call) */
    ItemPointerSetInvalid(&(so->hashso_curpos));
    ItemPointerSetInvalid(&(so->hashso_heappos));

    scan->opaque = so;

    /* register scan in case we change pages it's using */
    _hash_regscan(scan);

    PG_RETURN_POINTER(scan);
}

Datum hashbuild ( PG_FUNCTION_ARGS   ) 

Definition at line 50 of file hash.c.

References _h_indexbuild(), _h_spooldestroy(), _h_spoolinit(), _hash_metapinit(), elog, ERROR, estimate_rel_size(), hashbuildCallback(), IndexBuildResult::heap_tuples, IndexBuildResult::index_tuples, IndexBuildHeapScan(), HashBuildState::indtuples, MAIN_FORKNUM, NBuffers, NULL, palloc(), PG_GETARG_POINTER, PG_RETURN_POINTER, RelationGetNumberOfBlocks, RelationGetRelationName, and HashBuildState::spool.

{
    Relation    heap = (Relation) PG_GETARG_POINTER(0);
    Relation    index = (Relation) PG_GETARG_POINTER(1);
    IndexInfo  *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
    IndexBuildResult *result;
    BlockNumber relpages;
    double      reltuples;
    double      allvisfrac;
    uint32      num_buckets;
    HashBuildState buildstate;

    /*
     * We expect to be called exactly once for any index relation. If that's
     * not the case, big trouble's what we have.
     */
    if (RelationGetNumberOfBlocks(index) != 0)
        elog(ERROR, "index \"%s\" already contains data",
             RelationGetRelationName(index));

    /* Estimate the number of rows currently present in the table */
    estimate_rel_size(heap, NULL, &relpages, &reltuples, &allvisfrac);

    /* Initialize the hash index metadata page and initial buckets */
    num_buckets = _hash_metapinit(index, reltuples, MAIN_FORKNUM);

    /*
     * If we just insert the tuples into the index in scan order, then
     * (assuming their hash codes are pretty random) there will be no locality
     * of access to the index, and if the index is bigger than available RAM
     * then we'll thrash horribly.  To prevent that scenario, we can sort the
     * tuples by (expected) bucket number.  However, such a sort is useless
     * overhead when the index does fit in RAM.  We choose to sort if the
     * initial index size exceeds NBuffers.
     *
     * NOTE: this test will need adjustment if a bucket is ever different from
     * one page.
     */
    if (num_buckets >= (uint32) NBuffers)
        buildstate.spool = _h_spoolinit(heap, index, num_buckets);
    else
        buildstate.spool = NULL;

    /* prepare to build the index */
    buildstate.indtuples = 0;

    /* do the heap scan */
    reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
                                   hashbuildCallback, (void *) &buildstate);

    if (buildstate.spool)
    {
        /* sort the tuples and insert them into the index */
        _h_indexbuild(buildstate.spool);
        _h_spooldestroy(buildstate.spool);
    }

    /*
     * Return statistics
     */
    result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));

    result->heap_tuples = reltuples;
    result->index_tuples = buildstate.indtuples;

    PG_RETURN_POINTER(result);
}

Datum hashbuildempty ( PG_FUNCTION_ARGS   ) 
Datum hashbulkdelete ( PG_FUNCTION_ARGS   ) 

Definition at line 504 of file hash.c.

References _hash_droplock(), _hash_getbuf(), _hash_getbuf_with_strategy(), _hash_getlock(), _hash_has_active_scan(), _hash_relbuf(), _hash_squeezebucket(), _hash_wrtbuf(), Assert, GistBDItem::blkno, BlockNumberIsValid, BUCKET_TO_BLKNO, buf, BufferGetPage, callback(), elog, ERROR, IndexBulkDeleteResult::estimated_count, FirstOffsetNumber, HASH_EXCLUSIVE, HASH_METAPAGE, HASH_READ, HASH_WRITE, HashMetaPageData::hashm_maxbucket, HashMetaPageData::hashm_ntuples, HashPageOpaqueData::hasho_bucket, HashPageOpaqueData::hasho_nextblkno, HashPageGetMeta, IndexVacuumInfo::index, LH_BUCKET_PAGE, LH_META_PAGE, LH_OVERFLOW_PAGE, NULL, IndexBulkDeleteResult::num_index_tuples, OffsetNumberNext, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageGetSpecialPointer, PageIndexMultiDelete(), palloc0(), PG_GETARG_POINTER, PG_RETURN_POINTER, IndexVacuumInfo::strategy, IndexTupleData::t_tid, IndexBulkDeleteResult::tuples_removed, and vacuum_delay_point().

{
    IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
    IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
    IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
    void       *callback_state = (void *) PG_GETARG_POINTER(3);
    Relation    rel = info->index;
    double      tuples_removed;
    double      num_index_tuples;
    double      orig_ntuples;
    Bucket      orig_maxbucket;
    Bucket      cur_maxbucket;
    Bucket      cur_bucket;
    Buffer      metabuf;
    HashMetaPage metap;
    HashMetaPageData local_metapage;

    tuples_removed = 0;
    num_index_tuples = 0;

    /*
     * Read the metapage to fetch original bucket and tuple counts.  Also, we
     * keep a copy of the last-seen metapage so that we can use its
     * hashm_spares[] values to compute bucket page addresses.  This is a bit
     * hokey but perfectly safe, since the interesting entries in the spares
     * array cannot change under us; and it beats rereading the metapage for
     * each bucket.
     */
    metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
    metap = HashPageGetMeta(BufferGetPage(metabuf));
    orig_maxbucket = metap->hashm_maxbucket;
    orig_ntuples = metap->hashm_ntuples;
    memcpy(&local_metapage, metap, sizeof(local_metapage));
    _hash_relbuf(rel, metabuf);

    /* Scan the buckets that we know exist */
    cur_bucket = 0;
    cur_maxbucket = orig_maxbucket;

loop_top:
    while (cur_bucket <= cur_maxbucket)
    {
        BlockNumber bucket_blkno;
        BlockNumber blkno;
        bool        bucket_dirty = false;

        /* Get address of bucket's start page */
        bucket_blkno = BUCKET_TO_BLKNO(&local_metapage, cur_bucket);

        /* Exclusive-lock the bucket so we can shrink it */
        _hash_getlock(rel, bucket_blkno, HASH_EXCLUSIVE);

        /* Shouldn't have any active scans locally, either */
        if (_hash_has_active_scan(rel, cur_bucket))
            elog(ERROR, "hash index has active scan during VACUUM");

        /* Scan each page in bucket */
        blkno = bucket_blkno;
        while (BlockNumberIsValid(blkno))
        {
            Buffer      buf;
            Page        page;
            HashPageOpaque opaque;
            OffsetNumber offno;
            OffsetNumber maxoffno;
            OffsetNumber deletable[MaxOffsetNumber];
            int         ndeletable = 0;

            vacuum_delay_point();

            buf = _hash_getbuf_with_strategy(rel, blkno, HASH_WRITE,
                                           LH_BUCKET_PAGE | LH_OVERFLOW_PAGE,
                                             info->strategy);
            page = BufferGetPage(buf);
            opaque = (HashPageOpaque) PageGetSpecialPointer(page);
            Assert(opaque->hasho_bucket == cur_bucket);

            /* Scan each tuple in page */
            maxoffno = PageGetMaxOffsetNumber(page);
            for (offno = FirstOffsetNumber;
                 offno <= maxoffno;
                 offno = OffsetNumberNext(offno))
            {
                IndexTuple  itup;
                ItemPointer htup;

                itup = (IndexTuple) PageGetItem(page,
                                                PageGetItemId(page, offno));
                htup = &(itup->t_tid);
                if (callback(htup, callback_state))
                {
                    /* mark the item for deletion */
                    deletable[ndeletable++] = offno;
                    tuples_removed += 1;
                }
                else
                    num_index_tuples += 1;
            }

            /*
             * Apply deletions and write page if needed, advance to next page.
             */
            blkno = opaque->hasho_nextblkno;

            if (ndeletable > 0)
            {
                PageIndexMultiDelete(page, deletable, ndeletable);
                _hash_wrtbuf(rel, buf);
                bucket_dirty = true;
            }
            else
                _hash_relbuf(rel, buf);
        }

        /* If we deleted anything, try to compact free space */
        if (bucket_dirty)
            _hash_squeezebucket(rel, cur_bucket, bucket_blkno,
                                info->strategy);

        /* Release bucket lock */
        _hash_droplock(rel, bucket_blkno, HASH_EXCLUSIVE);

        /* Advance to next bucket */
        cur_bucket++;
    }

    /* Write-lock metapage and check for split since we started */
    metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_WRITE, LH_META_PAGE);
    metap = HashPageGetMeta(BufferGetPage(metabuf));

    if (cur_maxbucket != metap->hashm_maxbucket)
    {
        /* There's been a split, so process the additional bucket(s) */
        cur_maxbucket = metap->hashm_maxbucket;
        memcpy(&local_metapage, metap, sizeof(local_metapage));
        _hash_relbuf(rel, metabuf);
        goto loop_top;
    }

    /* Okay, we're really done.  Update tuple count in metapage. */

    if (orig_maxbucket == metap->hashm_maxbucket &&
        orig_ntuples == metap->hashm_ntuples)
    {
        /*
         * No one has split or inserted anything since start of scan, so
         * believe our count as gospel.
         */
        metap->hashm_ntuples = num_index_tuples;
    }
    else
    {
        /*
         * Otherwise, our count is untrustworthy since we may have
         * double-scanned tuples in split buckets.  Proceed by dead-reckoning.
         * (Note: we still return estimated_count = false, because using this
         * count is better than not updating reltuples at all.)
         */
        if (metap->hashm_ntuples > tuples_removed)
            metap->hashm_ntuples -= tuples_removed;
        else
            metap->hashm_ntuples = 0;
        num_index_tuples = metap->hashm_ntuples;
    }

    _hash_wrtbuf(rel, metabuf);

    /* return statistics */
    if (stats == NULL)
        stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
    stats->estimated_count = false;
    stats->num_index_tuples = num_index_tuples;
    stats->tuples_removed += tuples_removed;
    /* hashvacuumcleanup will fill in num_pages */

    PG_RETURN_POINTER(stats);
}

Datum hashchar ( PG_FUNCTION_ARGS   ) 

Definition at line 34 of file hashfunc.c.

References hash_uint32(), and PG_GETARG_CHAR.

{
    return hash_uint32((int32) PG_GETARG_CHAR(0));
}

Datum hashendscan ( PG_FUNCTION_ARGS   ) 
Datum hashenum ( PG_FUNCTION_ARGS   ) 

Definition at line 78 of file hashfunc.c.

References hash_uint32(), and PG_GETARG_OID.

{
    return hash_uint32((uint32) PG_GETARG_OID(0));
}

Datum hashfloat4 ( PG_FUNCTION_ARGS   ) 

Definition at line 84 of file hashfunc.c.

References hash_any(), PG_GETARG_FLOAT4, and PG_RETURN_UINT32.

{
    float4      key = PG_GETARG_FLOAT4(0);
    float8      key8;

    /*
     * On IEEE-float machines, minus zero and zero have different bit patterns
     * but should compare as equal.  We must ensure that they have the same
     * hash value, which is most reliably done this way:
     */
    if (key == (float4) 0)
        PG_RETURN_UINT32(0);

    /*
     * To support cross-type hashing of float8 and float4, we want to return
     * the same hash value hashfloat8 would produce for an equal float8 value.
     * So, widen the value to float8 and hash that.  (We must do this rather
     * than have hashfloat8 try to narrow its value to float4; that could fail
     * on overflow.)
     */
    key8 = key;

    return hash_any((unsigned char *) &key8, sizeof(key8));
}

Datum hashfloat8 ( PG_FUNCTION_ARGS   ) 

Definition at line 110 of file hashfunc.c.

References hash_any(), PG_GETARG_FLOAT8, and PG_RETURN_UINT32.

Referenced by interval_hash(), time_hash(), timestamp_hash(), and timetz_hash().

{
    float8      key = PG_GETARG_FLOAT8(0);

    /*
     * On IEEE-float machines, minus zero and zero have different bit patterns
     * but should compare as equal.  We must ensure that they have the same
     * hash value, which is most reliably done this way:
     */
    if (key == (float8) 0)
        PG_RETURN_UINT32(0);

    return hash_any((unsigned char *) &key, sizeof(key));
}

Datum hashgetbitmap ( PG_FUNCTION_ARGS   ) 

Definition at line 331 of file hash.c.

References _hash_first(), _hash_next(), BufferGetPage, ForwardScanDirection, HashScanOpaqueData::hashso_curbuf, HashScanOpaqueData::hashso_curpos, HashScanOpaqueData::hashso_heappos, IndexScanDescData::ignore_killed_tuples, ItemIdIsDead, ItemPointerGetOffsetNumber, IndexScanDescData::opaque, PageGetItemId, PG_GETARG_POINTER, PG_RETURN_INT64, and tbm_add_tuples().

{
    IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
    TIDBitmap  *tbm = (TIDBitmap *) PG_GETARG_POINTER(1);
    HashScanOpaque so = (HashScanOpaque) scan->opaque;
    bool        res;
    int64       ntids = 0;

    res = _hash_first(scan, ForwardScanDirection);

    while (res)
    {
        bool        add_tuple;

        /*
         * Skip killed tuples if asked to.
         */
        if (scan->ignore_killed_tuples)
        {
            Page        page;
            OffsetNumber offnum;

            offnum = ItemPointerGetOffsetNumber(&(so->hashso_curpos));
            page = BufferGetPage(so->hashso_curbuf);
            add_tuple = !ItemIdIsDead(PageGetItemId(page, offnum));
        }
        else
            add_tuple = true;

        /* Save tuple ID, and continue scanning */
        if (add_tuple)
        {
            /* Note we mark the tuple ID as requiring recheck */
            tbm_add_tuples(tbm, &(so->hashso_heappos), 1, true);
            ntids++;
        }

        res = _hash_next(scan, ForwardScanDirection);
    }

    PG_RETURN_INT64(ntids);
}

Datum hashgettuple ( PG_FUNCTION_ARGS   ) 

Definition at line 218 of file hash.c.

References _hash_chgbufaccess(), _hash_first(), _hash_next(), Assert, buf, BufferGetPage, BufferIsValid, elog, ERROR, HASH_NOLOCK, HASH_READ, HashScanOpaqueData::hashso_curbuf, HashScanOpaqueData::hashso_curpos, HashScanOpaqueData::hashso_heappos, IndexScanDescData::ignore_killed_tuples, IndexScanDescData::indexRelation, ItemIdIsDead, ItemIdMarkDead, ItemPointerEquals(), ItemPointerGetOffsetNumber, ItemPointerIsValid, ItemPointerSetOffsetNumber, IndexScanDescData::kill_prior_tuple, MarkBufferDirtyHint(), OffsetNumberNext, IndexScanDescData::opaque, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PG_GETARG_INT32, PG_GETARG_POINTER, PG_RETURN_BOOL, RelationGetRelationName, HeapTupleData::t_self, IndexTupleData::t_tid, IndexScanDescData::xs_ctup, and IndexScanDescData::xs_recheck.

{
    IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0);
    ScanDirection dir = (ScanDirection) PG_GETARG_INT32(1);
    HashScanOpaque so = (HashScanOpaque) scan->opaque;
    Relation    rel = scan->indexRelation;
    Buffer      buf;
    Page        page;
    OffsetNumber offnum;
    ItemPointer current;
    bool        res;

    /* Hash indexes are always lossy since we store only the hash code */
    scan->xs_recheck = true;

    /*
     * We hold pin but not lock on current buffer while outside the hash AM.
     * Reacquire the read lock here.
     */
    if (BufferIsValid(so->hashso_curbuf))
        _hash_chgbufaccess(rel, so->hashso_curbuf, HASH_NOLOCK, HASH_READ);

    /*
     * If we've already initialized this scan, we can just advance it in the
     * appropriate direction.  If we haven't done so yet, we call a routine to
     * get the first item in the scan.
     */
    current = &(so->hashso_curpos);
    if (ItemPointerIsValid(current))
    {
        /*
         * An insertion into the current index page could have happened while
         * we didn't have read lock on it.  Re-find our position by looking
         * for the TID we previously returned.  (Because we hold share lock on
         * the bucket, no deletions or splits could have occurred; therefore
         * we can expect that the TID still exists in the current index page,
         * at an offset >= where we were.)
         */
        OffsetNumber maxoffnum;

        buf = so->hashso_curbuf;
        Assert(BufferIsValid(buf));
        page = BufferGetPage(buf);
        maxoffnum = PageGetMaxOffsetNumber(page);
        for (offnum = ItemPointerGetOffsetNumber(current);
             offnum <= maxoffnum;
             offnum = OffsetNumberNext(offnum))
        {
            IndexTuple  itup;

            itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum));
            if (ItemPointerEquals(&(so->hashso_heappos), &(itup->t_tid)))
                break;
        }
        if (offnum > maxoffnum)
            elog(ERROR, "failed to re-find scan position within index \"%s\"",
                 RelationGetRelationName(rel));
        ItemPointerSetOffsetNumber(current, offnum);

        /*
         * Check to see if we should kill the previously-fetched tuple.
         */
        if (scan->kill_prior_tuple)
        {
            /*
             * Yes, so mark it by setting the LP_DEAD state in the item flags.
             */
            ItemIdMarkDead(PageGetItemId(page, offnum));

            /*
             * Since this can be redone later if needed, mark as a hint.
             */
            MarkBufferDirtyHint(buf);
        }

        /*
         * Now continue the scan.
         */
        res = _hash_next(scan, dir);
    }
    else
        res = _hash_first(scan, dir);

    /*
     * Skip killed tuples if asked to.
     */
    if (scan->ignore_killed_tuples)
    {
        while (res)
        {
            offnum = ItemPointerGetOffsetNumber(current);
            page = BufferGetPage(so->hashso_curbuf);
            if (!ItemIdIsDead(PageGetItemId(page, offnum)))
                break;
            res = _hash_next(scan, dir);
        }
    }

    /* Release read lock on current buffer, but keep it pinned */
    if (BufferIsValid(so->hashso_curbuf))
        _hash_chgbufaccess(rel, so->hashso_curbuf, HASH_READ, HASH_NOLOCK);

    /* Return current heap TID on success */
    scan->xs_ctup.t_self = so->hashso_heappos;

    PG_RETURN_BOOL(res);
}

Datum hashinsert ( PG_FUNCTION_ARGS   ) 

Definition at line 174 of file hash.c.

References _hash_doinsert(), _hash_form_tuple(), IndexTupleHasNulls, pfree(), PG_GETARG_INT32, PG_GETARG_POINTER, PG_RETURN_BOOL, IndexTupleData::t_tid, and values.

{
    Relation    rel = (Relation) PG_GETARG_POINTER(0);
    Datum      *values = (Datum *) PG_GETARG_POINTER(1);
    bool       *isnull = (bool *) PG_GETARG_POINTER(2);
    ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);

#ifdef NOT_USED
    Relation    heapRel = (Relation) PG_GETARG_POINTER(4);
    IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
#endif
    IndexTuple  itup;

    /* generate an index tuple */
    itup = _hash_form_tuple(rel, values, isnull);
    itup->t_tid = *ht_ctid;

    /*
     * If the single index key is null, we don't insert it into the index.
     * Hash tables support scans on '='. Relational algebra says that A = B
     * returns null if either A or B is null.  This means that no
     * qualification used in an index scan could ever return true on a null
     * attribute.  It also means that indices can't be used by ISNULL or
     * NOTNULL scans, but that's an artifact of the strategy map architecture
     * chosen in 1986, not of the way nulls are handled here.
     */
    if (IndexTupleHasNulls(itup))
    {
        pfree(itup);
        PG_RETURN_BOOL(false);
    }

    _hash_doinsert(rel, itup);

    pfree(itup);

    PG_RETURN_BOOL(false);
}

Datum hashint2 ( PG_FUNCTION_ARGS   ) 

Definition at line 40 of file hashfunc.c.

References hash_uint32(), and PG_GETARG_INT16.

{
    return hash_uint32((int32) PG_GETARG_INT16(0));
}

Datum hashint2vector ( PG_FUNCTION_ARGS   ) 

Definition at line 134 of file hashfunc.c.

References int2vector::dim1, hash_any(), PG_GETARG_POINTER, and int2vector::values.

{
    int2vector *key = (int2vector *) PG_GETARG_POINTER(0);

    return hash_any((unsigned char *) key->values, key->dim1 * sizeof(int16));
}

Datum hashint4 ( PG_FUNCTION_ARGS   ) 

Definition at line 46 of file hashfunc.c.

References hash_uint32(), and PG_GETARG_INT32.

{
    return hash_uint32(PG_GETARG_INT32(0));
}

Datum hashint8 ( PG_FUNCTION_ARGS   ) 

Definition at line 52 of file hashfunc.c.

References hash_uint32(), PG_GETARG_INT64, and val.

Referenced by interval_hash(), time_hash(), timestamp_hash(), and timetz_hash().

{
    /*
     * The idea here is to produce a hash value compatible with the values
     * produced by hashint4 and hashint2 for logically equal inputs; this is
     * necessary to support cross-type hash joins across these input types.
     * Since all three types are signed, we can xor the high half of the int8
     * value if the sign is positive, or the complement of the high half when
     * the sign is negative.
     */
    int64       val = PG_GETARG_INT64(0);
    uint32      lohalf = (uint32) val;
    uint32      hihalf = (uint32) (val >> 32);

    lohalf ^= (val >= 0) ? hihalf : ~hihalf;

    return hash_uint32(lohalf);
}

Datum hashmarkpos ( PG_FUNCTION_ARGS   ) 

Definition at line 480 of file hash.c.

References elog, ERROR, and PG_RETURN_VOID.

{
    elog(ERROR, "hash does not support mark/restore");
    PG_RETURN_VOID();
}

Datum hashname ( PG_FUNCTION_ARGS   ) 

Definition at line 142 of file hashfunc.c.

References Assert, hash_any(), NAMEDATALEN, NameStr, and PG_GETARG_NAME.

{
    char       *key = NameStr(*PG_GETARG_NAME(0));
    int         keylen = strlen(key);

    Assert(keylen < NAMEDATALEN);       /* else it's not truncated correctly */

    return hash_any((unsigned char *) key, keylen);
}

Datum hashoid ( PG_FUNCTION_ARGS   ) 

Definition at line 72 of file hashfunc.c.

References hash_uint32(), and PG_GETARG_OID.

{
    return hash_uint32((uint32) PG_GETARG_OID(0));
}

Datum hashoidvector ( PG_FUNCTION_ARGS   ) 

Definition at line 126 of file hashfunc.c.

References oidvector::dim1, hash_any(), PG_GETARG_POINTER, and oidvector::values.

{
    oidvector  *key = (oidvector *) PG_GETARG_POINTER(0);

    return hash_any((unsigned char *) key->values, key->dim1 * sizeof(Oid));
}

Datum hashoptions ( PG_FUNCTION_ARGS   ) 

Definition at line 221 of file hashutil.c.

References default_reloptions(), PG_GETARG_BOOL, PG_GETARG_DATUM, PG_RETURN_BYTEA_P, PG_RETURN_NULL, and RELOPT_KIND_HASH.

{
    Datum       reloptions = PG_GETARG_DATUM(0);
    bool        validate = PG_GETARG_BOOL(1);
    bytea      *result;

    result = default_reloptions(reloptions, validate, RELOPT_KIND_HASH);

    if (result)
        PG_RETURN_BYTEA_P(result);
    PG_RETURN_NULL();
}

Datum hashrescan ( PG_FUNCTION_ARGS   ) 
Datum hashrestrpos ( PG_FUNCTION_ARGS   ) 

Definition at line 490 of file hash.c.

References elog, ERROR, and PG_RETURN_VOID.

{
    elog(ERROR, "hash does not support mark/restore");
    PG_RETURN_VOID();
}

Datum hashtext ( PG_FUNCTION_ARGS   ) 

Definition at line 153 of file hashfunc.c.

References hash_any(), PG_FREE_IF_COPY, PG_GETARG_TEXT_PP, VARDATA_ANY, and VARSIZE_ANY_EXHDR.

{
    text       *key = PG_GETARG_TEXT_PP(0);
    Datum       result;

    /*
     * Note: this is currently identical in behavior to hashvarlena, but keep
     * it as a separate function in case we someday want to do something
     * different in non-C locales.  (See also hashbpchar, if so.)
     */
    result = hash_any((unsigned char *) VARDATA_ANY(key),
                      VARSIZE_ANY_EXHDR(key));

    /* Avoid leaking memory for toasted inputs */
    PG_FREE_IF_COPY(key, 0);

    return result;
}

Datum hashvacuumcleanup ( PG_FUNCTION_ARGS   ) 

Definition at line 688 of file hash.c.

References IndexVacuumInfo::index, NULL, IndexBulkDeleteResult::num_pages, PG_GETARG_POINTER, PG_RETURN_POINTER, and RelationGetNumberOfBlocks.

{
    IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
    IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
    Relation    rel = info->index;
    BlockNumber num_pages;

    /* If hashbulkdelete wasn't called, return NULL signifying no change */
    /* Note: this covers the analyze_only case too */
    if (stats == NULL)
        PG_RETURN_POINTER(NULL);

    /* update statistics */
    num_pages = RelationGetNumberOfBlocks(rel);
    stats->num_pages = num_pages;

    PG_RETURN_POINTER(stats);
}

Datum hashvarlena ( PG_FUNCTION_ARGS   ) 

Definition at line 177 of file hashfunc.c.

References hash_any(), PG_FREE_IF_COPY, PG_GETARG_VARLENA_PP, VARDATA_ANY, and VARSIZE_ANY_EXHDR.

{
    struct varlena *key = PG_GETARG_VARLENA_PP(0);
    Datum       result;

    result = hash_any((unsigned char *) VARDATA_ANY(key),
                      VARSIZE_ANY_EXHDR(key));

    /* Avoid leaking memory for toasted inputs */
    PG_FREE_IF_COPY(key, 0);

    return result;
}

void ReleaseResources_hash ( void   ) 

Definition at line 53 of file hashscan.c.

References CurrentResourceOwner, HashScanListData::hashsl_next, HashScanListData::hashsl_owner, NULL, and pfree().

Referenced by ResourceOwnerReleaseInternal().

{
    HashScanList l;
    HashScanList prev;
    HashScanList next;

    /*
     * Release all HashScanList items belonging to the current ResourceOwner.
     * Note that we do not release the underlying IndexScanDesc; that's in
     * executor memory and will go away on its own (in fact quite possibly has
     * gone away already, so we mustn't try to touch it here).
     *
     * Note: this should be a no-op during normal query shutdown. However, in
     * an abort situation ExecutorEnd is not called and so there may be open
     * index scans to clean up.
     */
    prev = NULL;

    for (l = HashScans; l != NULL; l = next)
    {
        next = l->hashsl_next;
        if (l->hashsl_owner == CurrentResourceOwner)
        {
            if (prev == NULL)
                HashScans = next;
            else
                prev->hashsl_next = next;

            pfree(l);
            /* prev does not change */
        }
        else
            prev = l;
    }
}