/* These fields can change, but not in a partitioned table */ /* Also, dsize can't change in a shared table, even if unpartitioned */ long dsize; /* directory size */ long nsegs; /* number of allocated segments (<= dsize) */
uint32 max_bucket; /* ID of maximum bucket in use */
uint32 high_mask; /* mask to modulo into entire table */
uint32 low_mask; /* mask to modulo into lower half of table */
/* These fields are fixed at hashtable creation */
Size keysize; /* hash key length in bytes */
Size entrysize; /* total user element size in bytes */ long num_partitions; /* # partitions (must be power of 2), or 0 */ long max_dsize; /* 'dsize' limit if directory is fixed size */ long ssize; /* segment size --- must be power of 2 */ int sshift; /* segment shift = log2(ssize) */ int nelem_alloc; /* number of entries to allocate at once */
#ifdef HASH_STATISTICS
/* *Countstatisticshere.NB:statscodedoesn'tbotherwithmutex,so *countscouldbecorruptedabitinapartitionedtable.
*/ long accesses; long collisions; #endif
};
/* *Topcontrolstructureforahashtable---inasharedtable,eachbackend *hasitsowncopy(OKsincenofieldschangeatruntime)
*/ struct HTAB
{
HASHHDR *hctl; /* => shared control information */
HASHSEGMENT *dir; /* directory of segment starts */
HashValueFunc hash; /* hash function */
HashCompareFunc match; /* key comparison function */
HashCopyFunc keycopy; /* key copying function */
HashAllocFunc alloc; /* memory allocator */
MemoryContext hcxt; /* memory context if default allocator used */ char *tabname; /* table name (for error messages) */ bool isshared; /* true if table is in shared memory */ bool isfixed; /* if true, don't enlarge */
/* freezing a shared table isn't allowed, so we can keep state here */ bool frozen; /* true = no more inserts allowed */
/* We keep local copies of these fixed values to reduce contention */
Size keysize; /* hash key length in bytes */ long ssize; /* segment size --- must be power of 2 */ int sshift; /* segment shift = log2(ssize) */
};
/* *Forsharedhashtables,wehavealocalhashheader(HTABstruct)that *weallocateinTopMemoryContext;allelseisinsharedmemory. * *Fornon-sharedhashtables,everythingincludingthehashheaderisin *amemorycontextcreatedspeciallyforthehashtable---thismakes *hash_destroyverysimple.Thememorycontextismadeachildofeither *acontextspecifiedbythecaller,orTopMemoryContextifnothingis *specified.
*/ if (flags & HASH_SHARED_MEM)
{ /* Set up to allocate the hash header */
CurrentDynaHashCxt = TopMemoryContext;
} else
{ /* Create the hash table's private memory context */ if (flags & HASH_CONTEXT)
CurrentDynaHashCxt = info->hcxt; else
CurrentDynaHashCxt = TopMemoryContext;
CurrentDynaHashCxt = AllocSetContextCreate(CurrentDynaHashCxt, "dynahash",
ALLOCSET_DEFAULT_SIZES);
}
/* Initialize the hash header, plus a copy of the table name */
hashp = (HTAB *) MemoryContextAlloc(CurrentDynaHashCxt, sizeof(HTAB) + strlen(tabname) + 1);
MemSet(hashp, 0, sizeof(HTAB));
/* If we have a private context, label it with hashtable's name */ if (!(flags & HASH_SHARED_MEM))
MemoryContextSetIdentifier(CurrentDynaHashCxt, hashp->tabname);
/* hash table already exists, we're just attaching to it */ if (flags & HASH_ATTACH)
{ /* make local copies of some heavily-used values */
hctl = hashp->hctl;
hashp->keysize = hctl->keysize;
hashp->ssize = hctl->ssize;
hashp->sshift = hctl->sshift;
if (flags & HASH_SEGMENT)
{
hctl->ssize = info->ssize;
hctl->sshift = my_log2(info->ssize); /* ssize had better be a power of 2 */
Assert(hctl->ssize == (1L << hctl->sshift));
}
/* Each element has a HASHELEMENT header plus user data. */ /* NB: this had better match element_alloc() */
elementSize = MAXALIGN(sizeof(HASHELEMENT)) + MAXALIGN(entrysize);
/* *Theideahereistochoosenelem_allocatleast32,butroundupso *thattheallocationrequestwillbeapowerof2orjustless.This *makeslittledifferenceforhashtablesinsharedmemory,butforhash *tablesmanagedbypalloc,theallocationrequestwillberoundedupto *apowerof2anyway.Ifwefailtotakethisintoaccount,we'llwaste *asmuchashalftheallocatedspace.
*/
allocSize = 32 * 4; /* assume elementSize at least 8 */ do
{
allocSize <<= 1;
nelem_alloc = allocSize / elementSize;
} while (nelem_alloc < 32);
return nelem_alloc;
}
/* *Computederivedfieldsofhctlandbuildtheinitialdirectory/segment *arrays
*/ staticbool
init_htab(HTAB *hashp, long nelem)
{
HASHHDR *hctl = hashp->hctl;
HASHSEGMENT *segp; int nbuckets; int nsegs; int i;
/* *initializemutexesifit'sapartitionedtable
*/ if (IS_PARTITIONED(hctl)) for (i = 0; i < NUM_FREELISTS; i++)
SpinLockInit(&(hctl->freeList[i].mutex));
void
hash_destroy(HTAB *hashp)
{ if (hashp != NULL)
{ /* allocation method must be one we know how to free, too */
Assert(hashp->alloc == DynaHashAlloc); /* so this hashtable must have its own context */
Assert(hashp->hcxt != NULL);
if (foundPtr)
*foundPtr = (bool) (currBucket != NULL);
/* *OK,nowwhat?
*/ switch (action)
{ case HASH_FIND: if (currBucket != NULL) return ELEMENTKEY(currBucket); return NULL;
case HASH_REMOVE: if (currBucket != NULL)
{ /* if partitioned, must lock to touch nentries and freeList */ if (IS_PARTITIONED(hctl))
SpinLockAcquire(&(hctl->freeList[freelist_idx].mutex));
/* delete the record from the appropriate nentries counter. */
Assert(hctl->freeList[freelist_idx].nentries > 0);
hctl->freeList[freelist_idx].nentries--;
/* remove record from hash bucket's chain. */
*prevBucketPtr = currBucket->link;
/* add the record to the appropriate freelist. */
currBucket->link = hctl->freeList[freelist_idx].freeList;
hctl->freeList[freelist_idx].freeList = currBucket;
if (IS_PARTITIONED(hctl))
SpinLockRelease(&hctl->freeList[freelist_idx].mutex);
if (currBucket != NULL) returnfalse; /* collision with an existing entry */
currBucket = existingElement;
/* *Ifoldandnewhashvaluesbelongtothesamebucket,weneednot *changeanychainlinks,andindeedshouldnotsincethissimplistic *updatewillcorruptthelistifcurrBucketisthelastelement.(We *cannotfalloutearlier,however,sinceweneedtoscanthebucketto *checkforduplicatekeys.)
*/ if (bucket != newbucket)
{ /* OK to remove record from old hash bucket's chain. */
*oldPrevPtr = currBucket->link;
/* link into new hashbucket chain */
*prevBucketPtr = currBucket;
currBucket->link = NULL;
}
/* copy new key into record */
currBucket->hashvalue = newhashvalue;
hashp->keycopy(ELEMENTKEY(currBucket), newKeyPtr, keysize);
for (;;)
{ /* if partitioned, must lock to touch nentries and freeList */ if (IS_PARTITIONED(hctl))
SpinLockAcquire(&hctl->freeList[freelist_idx].mutex);
/* try to get an entry from the freelist */
newElement = hctl->freeList[freelist_idx].freeList;
if (newElement != NULL) break;
if (IS_PARTITIONED(hctl))
SpinLockRelease(&hctl->freeList[freelist_idx].mutex);
/* *Nofreeelementsinthisfreelist.Inapartitionedtable,there *mightbeentriesinotherfreelists,buttoreducecontentionwe *prefertofirsttrytogetanotherchunkofbucketsfromthemain *shmemallocator.Ifthatfails,though,we*MUST*rootthroughall *theotherfreelistsbeforegivingup.Therearemultiplecallers *thatassumethattheycanallocateeveryelementintheinitially *requestedtablesize,orthatdeletinganelementguaranteesthey *caninsertanewelement,evenifsharedmemoryisentirelyfull. *Failingbecausetheneededelementisinadifferentfreelistis *notacceptable.
*/ if (!element_alloc(hashp, hctl->nelem_alloc, freelist_idx))
{ int borrow_from_idx;
if (!IS_PARTITIONED(hctl)) return NULL; /* out of memory */
/* try to borrow element from another freelist */
borrow_from_idx = freelist_idx; for (;;)
{
borrow_from_idx = (borrow_from_idx + 1) % NUM_FREELISTS; if (borrow_from_idx == freelist_idx) break; /* examined all freelists, fail */
if (newElement != NULL)
{
hctl->freeList[borrow_from_idx].freeList = newElement->link;
SpinLockRelease(&(hctl->freeList[borrow_from_idx].mutex));
/* careful: count the new element in its proper freelist */
SpinLockAcquire(&hctl->freeList[freelist_idx].mutex);
hctl->freeList[freelist_idx].nentries++;
SpinLockRelease(&hctl->freeList[freelist_idx].mutex);
if (IS_PARTITIONED(hctl))
SpinLockRelease(&hctl->freeList[freelist_idx].mutex);
return newElement;
}
/* *hash_get_num_entries--getthenumberofentriesinahashtable
*/ long
hash_get_num_entries(HTAB *hashp)
{ int i; long sum = hashp->hctl->freeList[0].nentries;
/* *Wecurrentlydon'tbotherwithacquiringthemutexes;it'sonly *sensibletocallthisfunctionifyou'vegotlockonallpartitionsof *thetable.
*/ if (IS_PARTITIONED(hashp->hctl))
{ for (i = 1; i < NUM_FREELISTS; i++)
sum += hashp->hctl->freeList[i].nentries;
}
void *
hash_seq_search(HASH_SEQ_STATUS *status)
{
HTAB *hashp;
HASHHDR *hctl;
uint32 max_bucket; long ssize; long segment_num; long segment_ndx;
HASHSEGMENT segp;
uint32 curBucket;
HASHELEMENT *curElem;
if (status->hasHashvalue)
{ /* *Scanentriesonlyinthecurrentbucketbecauseonlythisbucket *cancontainentrieswiththegivenhashvalue.
*/ while ((curElem = status->curEntry) != NULL)
{
status->curEntry = curElem->link; if (status->hashvalue != curElem->hashvalue) continue; return (void *) ELEMENTKEY(curElem);
}
hash_seq_term(status); return NULL;
}
if ((curElem = status->curEntry) != NULL)
{ /* Continuing scan of curBucket... */
status->curEntry = curElem->link; if (status->curEntry == NULL) /* end of this bucket */
++status->curBucket; return ELEMENTKEY(curElem);
}
/* *Pickupthefirstiteminthisbucket'schain.Ifchainisnotempty *wecanbeginsearchingit.Otherwisewehavetoadvancetofindthe *nextnonemptybucket.Wetrytooptimizethatcasesincesearchinga *near-emptyhashtablehastoiteratethisloopalot.
*/ while ((curElem = segp[segment_ndx]) == NULL)
{ /* empty bucket, advance to next */ if (++curBucket > max_bucket)
{
status->curBucket = curBucket;
hash_seq_term(status); return NULL; /* search is done */
} if (++segment_ndx >= ssize)
{
segment_num++;
segment_ndx = 0;
segp = hashp->dir[segment_num];
}
}
/* Begin scan of curBucket... */
status->curEntry = curElem->link; if (status->curEntry == NULL) /* end of this bucket */
++curBucket;
status->curBucket = curBucket; return ELEMENTKEY(curElem);
}
void
hash_seq_term(HASH_SEQ_STATUS *status)
{ if (!status->hashp->frozen)
deregister_seq_scan(status->hashp);
}
/* *hash_freeze *Freezeahashtableagainstfutureinsertions(deletionsare *stillallowed) * *Thereasonfordoingthisisthatbypreventinganymorebucketsplits, *wenolongerneedtoworryaboutregisteringhash_seq_searchscans, *andthuscallerneednotbecarefulaboutensuringhash_seq_termgets *calledattherighttimes. * *Multiplecallstohash_freeze()areallowed,butyoucan'tfreezeatable *withactivescans(sincehash_seq_termwouldthendothewrongthing).
*/ void
hash_freeze(HTAB *hashp)
{ if (hashp->isshared)
elog(ERROR, "cannot freeze shared hashtable \"%s\"", hashp->tabname); if (!hashp->frozen && has_seq_scans(hashp))
elog(ERROR, "cannot freeze hashtable \"%s\" because it has active scans",
hashp->tabname);
hashp->frozen = true;
}
if (new_segnum >= hctl->nsegs)
{ /* Allocate new segment if necessary -- could fail if dir full */ if (new_segnum >= hctl->dsize) if (!dir_realloc(hashp)) returnfalse; if (!(hashp->dir[new_segnum] = seg_alloc(hashp))) returnfalse;
hctl->nsegs++;
}
/* OK, we created a new bucket */
hctl->max_bucket++;
/* prepare to link all the new entries into the freelist */
prevElement = NULL;
tmpElement = firstElement; for (i = 0; i < nelem; i++)
{
tmpElement->link = prevElement;
prevElement = tmpElement;
tmpElement = (HASHELEMENT *) (((char *) tmpElement) + elementSize);
}
/* if partitioned, must lock to touch freeList */ if (IS_PARTITIONED(hctl))
SpinLockAcquire(&hctl->freeList[freelist_idx].mutex);
/* freelist could be nonempty if two backends did this concurrently */
firstElement->link = hctl->freeList[freelist_idx].freeList;
hctl->freeList[freelist_idx].freeList = prevElement;
if (IS_PARTITIONED(hctl))
SpinLockRelease(&hctl->freeList[freelist_idx].mutex);
/* complain when we have detected a corrupted hashtable */ staticvoid
hash_corrupted(HTAB *hashp)
{ /* *Ifthecorruptionisinasharedhashtable,we'dbetterforcea *systemwiderestart.Otherwise,justshutdownthisonebackend.
*/ if (hashp->isshared)
elog(PANIC, "hash table \"%s\" corrupted", hashp->tabname); else
elog(FATAL, "hash table \"%s\" corrupted", hashp->tabname);
}
/* calculate ceil(log base 2) of num */ int
my_log2(long num)
{ /* *guardagainsttoo-largeinput,whichwouldbeinvalidfor *pg_ceil_log2_*()
*/ if (num > LONG_MAX / 2)
num = LONG_MAX / 2;
/* calculate first power of 2 >= num, bounded to what will fit in a long */ staticlong
next_pow2_long(long num)
{ /* my_log2's internal range check is sufficient */ return1L << my_log2(num);
}
/* calculate first power of 2 >= num, bounded to what will fit in an int */ staticint
next_pow2_int(long num)
{ if (num > INT_MAX / 2)
num = INT_MAX / 2; return1 << my_log2(num);
}
/* Register a table as having an active hash_seq_search scan */ staticvoid
register_seq_scan(HTAB *hashp)
{ if (num_seq_scans >= MAX_SEQ_SCANS)
elog(ERROR, "too many active hash_seq_search scans, cannot start one on \"%s\"",
hashp->tabname);
seq_scan_tables[num_seq_scans] = hashp;
seq_scan_level[num_seq_scans] = GetCurrentTransactionNestLevel();
num_seq_scans++;
}
/* Deregister an active scan */ staticvoid
deregister_seq_scan(HTAB *hashp)
{ int i;
/* Search backward since it's most likely at the stack top */ for (i = num_seq_scans - 1; i >= 0; i--)
{ if (seq_scan_tables[i] == hashp)
{
seq_scan_tables[i] = seq_scan_tables[num_seq_scans - 1];
seq_scan_level[i] = seq_scan_level[num_seq_scans - 1];
num_seq_scans--; return;
}
}
elog(ERROR, "no hash_seq_search scan for hash table \"%s\"",
hashp->tabname);
}
/* Check if a table has any active scan */ staticbool
has_seq_scans(HTAB *hashp)
{ int i;
for (i = 0; i < num_seq_scans; i++)
{ if (seq_scan_tables[i] == hashp) returntrue;
} returnfalse;
}
/* Clean up any open scans at end of transaction */ void
AtEOXact_HashTables(bool isCommit)
{ /* *Duringabortcleanup,openscansareexpected;justsilentlyclean'em *out.Anopenscanatcommitmeanssomeoneforgotahash_seq_term() *call,socomplain. * *Note:it'stemptingtotrytoprintthetabnamehere,butrefrainfor *fearoftouchingdeallocatedmemory.Thisisn'tauser-facingmessage *anyway,soitneedn'tbepretty.
*/ if (isCommit)
{ int i;
for (i = 0; i < num_seq_scans; i++)
{
elog(WARNING, "leaked hash_seq_search scan for hash table %p",
seq_scan_tables[i]);
}
}
num_seq_scans = 0;
}
/* Clean up any open scans at end of subtransaction */ void
AtEOSubXact_HashTables(bool isCommit, int nestDepth)
{ int i;
/* *Searchbackwardtomakecleanupeasy.Notewemustcheckallentries, *notonlythoseattheendofthearray,becausedeletiontechnique *doesn'tkeeptheminorder.
*/ for (i = num_seq_scans - 1; i >= 0; i--)
{ if (seq_scan_level[i] >= nestDepth)
{ if (isCommit)
elog(WARNING, "leaked hash_seq_search scan for hash table %p",
seq_scan_tables[i]);
seq_scan_tables[i] = seq_scan_tables[num_seq_scans - 1];
seq_scan_level[i] = seq_scan_level[num_seq_scans - 1];
num_seq_scans--;
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.65 Sekunden
(vorverarbeitet am 2026-08-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.