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