#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"
Go to the source code of this file.
#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) |
Definition at line 35 of file hash.h.
Referenced by _hash_doinsert(), _hash_expandtable(), _hash_first(), _hash_metapinit(), and hashbulkdelete().
#define CLRBIT | ( | A, | ||
N | ||||
) | ((A)[(N)/BITS_PER_MAP] &= ~(1<<((N)%BITS_PER_MAP))) |
#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 |
Definition at line 116 of file hash.h.
Referenced by _hash_doinsert(), _hash_first(), _hash_freeovflpage(), _hash_metapinit(), hashbulkdelete(), and pgstat_relation().
#define HASH_NOLOCK (-1) |
Definition at line 224 of file hash.h.
Referenced by _hash_addovflpage(), _hash_chgbufaccess(), _hash_doinsert(), _hash_expandtable(), _hash_first(), _hash_freeovflpage(), _hash_getbuf(), _hash_getbuf_with_strategy(), _hash_getovflpage(), _hash_metapinit(), _hash_splitbucket(), and hashgettuple().
#define HASH_READ BUFFER_LOCK_SHARE |
Definition at line 222 of file hash.h.
Referenced by _hash_doinsert(), _hash_expandtable(), _hash_first(), _hash_freeovflpage(), _hash_getovflpage(), _hash_readnext(), _hash_readprev(), hashbulkdelete(), hashgettuple(), and pgstat_hash_page().
#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 |
Definition at line 223 of file hash.h.
Referenced by _hash_addovflpage(), _hash_chgbufaccess(), _hash_doinsert(), _hash_expandtable(), _hash_freeovflpage(), _hash_getinitbuf(), _hash_getnewbuf(), _hash_getovflpage(), _hash_metapinit(), _hash_splitbucket(), _hash_squeezebucket(), and hashbulkdelete().
#define HashGetMaxBitmapSize | ( | page | ) |
(PageGetPageSize((Page) page) - \ (MAXALIGN(SizeOfPageHeaderData) + MAXALIGN(sizeof(HashPageOpaqueData))))
Definition at line 202 of file hash.h.
Referenced by _hash_metapinit().
#define HashMaxItemSize | ( | page | ) |
MAXALIGN_DOWN(PageGetPageSize(page) - \ SizeOfPageHeaderData - \ sizeof(ItemIdData) - \ MAXALIGN(sizeof(HashPageOpaqueData)))
Definition at line 172 of file hash.h.
Referenced by _hash_doinsert().
#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)) |
Definition at line 206 of file hash.h.
Referenced by _hash_checkpage(), _hash_doinsert(), _hash_expandtable(), _hash_first(), _hash_freeovflpage(), _hash_getovflpage(), _hash_metapinit(), and hashbulkdelete().
#define HASHPROC 1 |
Definition at line 240 of file hash.h.
Referenced by _hash_datum2hashkey(), _hash_datum2hashkey_type(), _hash_metapinit(), get_op_hash_functions(), and lookup_type_cache().
#define HTEqualStrategyNumber 1 |
Definition at line 232 of file hash.h.
Referenced by _hash_first(), get_compatible_hash_operators(), get_op_hash_functions(), and lookup_type_cache().
#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) |
Definition at line 51 of file hash.h.
Referenced by _hash_addovflpage(), _hash_doinsert(), _hash_first(), _hash_freeovflpage(), _hash_next(), _hash_pgaddtup(), _hash_readprev(), _hash_splitbucket(), _hash_squeezebucket(), _hash_step(), hashbulkdelete(), and pgstat_hash_page().
#define LH_META_PAGE (1 << 3) |
Definition at line 53 of file hash.h.
Referenced by _hash_checkpage(), _hash_doinsert(), _hash_expandtable(), _hash_first(), _hash_freeovflpage(), _hash_getovflpage(), hashbulkdelete(), and pgstat_hash_page().
#define LH_OVERFLOW_PAGE (1 << 0) |
Definition at line 50 of file hash.h.
Referenced by _hash_addovflpage(), _hash_doinsert(), _hash_first(), _hash_freeovflpage(), _hash_next(), _hash_pgaddtup(), _hash_readnext(), _hash_readprev(), _hash_splitbucket(), _hash_squeezebucket(), _hash_step(), hashbulkdelete(), and pgstat_hash_page().
#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))) |
typedef HashMetaPageData* HashMetaPage |
typedef struct HashMetaPageData HashMetaPageData |
typedef HashPageOpaqueData* HashPageOpaque |
typedef struct HashPageOpaqueData HashPageOpaqueData |
typedef HashScanOpaqueData* HashScanOpaque |
typedef struct HashScanOpaqueData HashScanOpaqueData |
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); }
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; }
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; }
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; }
Definition at line 297 of file hashpage.c.
References BUFFER_LOCK_UNLOCK, HASH_NOLOCK, HASH_WRITE, LockBuffer(), and MarkBufferDirty().
Referenced by _hash_addovflpage(), _hash_doinsert(), _hash_expandtable(), _hash_first(), _hash_freeovflpage(), _hash_getovflpage(), _hash_metapinit(), _hash_splitbucket(), and hashgettuple().
{ if (from_access == HASH_WRITE) MarkBufferDirty(buf); if (from_access != HASH_NOLOCK) LockBuffer(buf, BUFFER_LOCK_UNLOCK); if (to_access != HASH_NOLOCK) LockBuffer(buf, to_access); }
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)); }
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); }
Definition at line 260 of file hashpage.c.
References ReleaseBuffer().
Referenced by _hash_doinsert(), _hash_first(), hashendscan(), and hashrescan().
{ ReleaseBuffer(buf); }
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); }
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; }
Definition at line 135 of file hashscan.c.
References HashScanListData::hashsl_next, HashScanListData::hashsl_scan, HashScanOpaqueData::hashso_bucket, HashScanOpaqueData::hashso_bucket_valid, IndexScanDescData::indexRelation, IndexScanDescData::opaque, RelationData::rd_id, and RelationGetRelid.
Referenced by _hash_expandtable(), and hashbulkdelete().
{ Oid relid = RelationGetRelid(rel); HashScanList l; for (l = HashScans; l != NULL; l = l->hashsl_next) { if (relid == l->hashsl_scan->indexRelation->rd_id) { HashScanOpaque so = (HashScanOpaque) l->hashsl_scan->opaque; if (so->hashso_bucket_valid && so->hashso_bucket == bucket) return true; } } return false; }
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++; }
Definition at line 138 of file hashutil.c.
References i.
Referenced by _h_spoolinit(), _hash_expandtable(), and _hash_metapinit().
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; }
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 | ) |
Definition at line 93 of file hashscan.c.
References CurrentResourceOwner, HashScanListData::hashsl_next, HashScanListData::hashsl_owner, HashScanListData::hashsl_scan, MemoryContextAlloc(), and TopMemoryContext.
Referenced by hashbeginscan().
{ HashScanList new_el; new_el = (HashScanList) MemoryContextAlloc(TopMemoryContext, sizeof(HashScanListData)); new_el->hashsl_scan = scan; new_el->hashsl_owner = CurrentResourceOwner; new_el->hashsl_next = HashScans; HashScans = new_el; }
Definition at line 249 of file hashpage.c.
References UnlockReleaseBuffer().
Referenced by _hash_addovflpage(), _hash_doinsert(), _hash_freeovflpage(), _hash_getovflpage(), _hash_readnext(), _hash_readprev(), _hash_splitbucket(), _hash_squeezebucket(), hashbulkdelete(), and pgstat_hash_page().
{ UnlockReleaseBuffer(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; }
Definition at line 278 of file hashpage.c.
References MarkBufferDirty(), and UnlockReleaseBuffer().
Referenced by _hash_addovflpage(), _hash_doinsert(), _hash_freeovflpage(), _hash_getovflpage(), _hash_initbitmap(), _hash_metapinit(), _hash_splitbucket(), _hash_squeezebucket(), and hashbulkdelete().
{ MarkBufferDirty(buf); UnlockReleaseBuffer(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 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 | ) |
Definition at line 122 of file hash.c.
References _hash_metapinit(), INIT_FORKNUM, PG_GETARG_POINTER, and PG_RETURN_VOID.
{ Relation index = (Relation) PG_GETARG_POINTER(0); _hash_metapinit(index, 0, INIT_FORKNUM); PG_RETURN_VOID(); }
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 | ) |
Definition at line 451 of file hash.c.
References _hash_dropbuf(), _hash_droplock(), _hash_dropscan(), BufferIsValid, HASH_SHARE, HashScanOpaqueData::hashso_bucket_blkno, HashScanOpaqueData::hashso_curbuf, IndexScanDescData::indexRelation, IndexScanDescData::opaque, pfree(), PG_GETARG_POINTER, and PG_RETURN_VOID.
{ IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0); HashScanOpaque so = (HashScanOpaque) scan->opaque; Relation rel = scan->indexRelation; /* don't need scan registered anymore */ _hash_dropscan(scan); /* release any pin we still hold */ if (BufferIsValid(so->hashso_curbuf)) _hash_dropbuf(rel, so->hashso_curbuf); so->hashso_curbuf = InvalidBuffer; /* release lock on bucket, too */ if (so->hashso_bucket_blkno) _hash_droplock(rel, so->hashso_bucket_blkno, HASH_SHARE); so->hashso_bucket_blkno = 0; pfree(so); scan->opaque = NULL; PG_RETURN_VOID(); }
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.
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 | ) |
Definition at line 412 of file hash.c.
References _hash_dropbuf(), _hash_droplock(), BufferIsValid, HASH_SHARE, HashScanOpaqueData::hashso_bucket_blkno, HashScanOpaqueData::hashso_bucket_valid, HashScanOpaqueData::hashso_curbuf, HashScanOpaqueData::hashso_curpos, HashScanOpaqueData::hashso_heappos, IndexScanDescData::indexRelation, ItemPointerSetInvalid, IndexScanDescData::keyData, memmove, IndexScanDescData::numberOfKeys, IndexScanDescData::opaque, PG_GETARG_POINTER, and PG_RETURN_VOID.
{ IndexScanDesc scan = (IndexScanDesc) PG_GETARG_POINTER(0); ScanKey scankey = (ScanKey) PG_GETARG_POINTER(1); /* remaining arguments are ignored */ HashScanOpaque so = (HashScanOpaque) scan->opaque; Relation rel = scan->indexRelation; /* release any pin we still hold */ if (BufferIsValid(so->hashso_curbuf)) _hash_dropbuf(rel, so->hashso_curbuf); so->hashso_curbuf = InvalidBuffer; /* release lock on bucket, too */ if (so->hashso_bucket_blkno) _hash_droplock(rel, so->hashso_bucket_blkno, HASH_SHARE); so->hashso_bucket_blkno = 0; /* set position invalid (this will cause _hash_first call) */ ItemPointerSetInvalid(&(so->hashso_curpos)); ItemPointerSetInvalid(&(so->hashso_heappos)); /* Update scan key, if a new one is given */ if (scankey && scan->numberOfKeys > 0) { memmove(scan->keyData, scankey, scan->numberOfKeys * sizeof(ScanKeyData)); so->hashso_bucket_valid = false; } PG_RETURN_VOID(); }
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; } }