summaryrefslogtreecommitdiff
path: root/src/backend/utils/hash/dynahash.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/hash/dynahash.c')
-rw-r--r--src/backend/utils/hash/dynahash.c118
1 files changed, 53 insertions, 65 deletions
diff --git a/src/backend/utils/hash/dynahash.c b/src/backend/utils/hash/dynahash.c
index 85bd82244c..8f1af2b8fa 100644
--- a/src/backend/utils/hash/dynahash.c
+++ b/src/backend/utils/hash/dynahash.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/hash/dynahash.c,v 1.60 2005/05/16 00:19:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/hash/dynahash.c,v 1.61 2005/05/29 04:23:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -498,22 +498,22 @@ calc_bucket(HASHHDR *hctl, uint32 hash_val)
* action is one of:
* HASH_FIND: look up key in table
* HASH_ENTER: look up key in table, creating entry if not present
+ * HASH_ENTER_NULL: same, but return NULL if out of memory
* HASH_REMOVE: look up key in table, remove entry if present
- * HASH_FIND_SAVE: look up key in table, also save in static var
- * HASH_REMOVE_SAVED: remove entry saved by HASH_FIND_SAVE
*
* Return value is a pointer to the element found/entered/removed if any,
- * or NULL if no match was found. (NB: in the case of the REMOVE actions,
+ * or NULL if no match was found. (NB: in the case of the REMOVE action,
* the result is a dangling pointer that shouldn't be dereferenced!)
- * A NULL result for HASH_ENTER implies we ran out of memory.
+ *
+ * HASH_ENTER will normally ereport a generic "out of memory" error if
+ * it is unable to create a new entry. The HASH_ENTER_NULL operation is
+ * the same except it will return NULL if out of memory. Note that
+ * HASH_ENTER_NULL cannot be used with the default palloc-based allocator,
+ * since palloc internally ereports on out-of-memory.
*
* If foundPtr isn't NULL, then *foundPtr is set TRUE if we found an
* existing entry in the table, FALSE otherwise. This is needed in the
* HASH_ENTER case, but is redundant with the return value otherwise.
- *
- * The HASH_FIND_SAVE/HASH_REMOVE_SAVED interface is a hack to save one
- * table lookup in a find/process/remove scenario. Note that no other
- * addition or removal in the table can safely happen in between.
*----------
*/
void *
@@ -523,19 +523,15 @@ hash_search(HTAB *hashp,
bool *foundPtr)
{
HASHHDR *hctl = hashp->hctl;
- uint32 hashvalue = 0;
+ Size keysize = hctl->keysize;
+ uint32 hashvalue;
uint32 bucket;
long segment_num;
long segment_ndx;
HASHSEGMENT segp;
HASHBUCKET currBucket;
HASHBUCKET *prevBucketPtr;
-
- static struct State
- {
- HASHBUCKET currBucket;
- HASHBUCKET *prevBucketPtr;
- } saveState;
+ HashCompareFunc match;
#if HASH_STATISTICS
hash_accesses++;
@@ -543,54 +539,38 @@ hash_search(HTAB *hashp,
#endif
/*
- * Do the initial lookup (or recall result of prior lookup)
+ * Do the initial lookup
*/
- if (action == HASH_REMOVE_SAVED)
- {
- currBucket = saveState.currBucket;
- prevBucketPtr = saveState.prevBucketPtr;
+ hashvalue = hashp->hash(keyPtr, keysize);
+ bucket = calc_bucket(hctl, hashvalue);
- /*
- * Try to catch subsequent errors
- */
- Assert(currBucket);
- saveState.currBucket = NULL;
- }
- else
- {
- HashCompareFunc match;
- Size keysize = hctl->keysize;
+ segment_num = bucket >> hctl->sshift;
+ segment_ndx = MOD(bucket, hctl->ssize);
- hashvalue = hashp->hash(keyPtr, keysize);
- bucket = calc_bucket(hctl, hashvalue);
+ segp = hashp->dir[segment_num];
- segment_num = bucket >> hctl->sshift;
- segment_ndx = MOD(bucket, hctl->ssize);
+ if (segp == NULL)
+ hash_corrupted(hashp);
- segp = hashp->dir[segment_num];
+ prevBucketPtr = &segp[segment_ndx];
+ currBucket = *prevBucketPtr;
- if (segp == NULL)
- hash_corrupted(hashp);
+ /*
+ * Follow collision chain looking for matching key
+ */
+ match = hashp->match; /* save one fetch in inner loop */
- prevBucketPtr = &segp[segment_ndx];
+ while (currBucket != NULL)
+ {
+ if (currBucket->hashvalue == hashvalue &&
+ match(ELEMENTKEY(currBucket), keyPtr, keysize) == 0)
+ break;
+ prevBucketPtr = &(currBucket->link);
currBucket = *prevBucketPtr;
-
- /*
- * Follow collision chain looking for matching key
- */
- match = hashp->match; /* save one fetch in inner loop */
- while (currBucket != NULL)
- {
- if (currBucket->hashvalue == hashvalue &&
- match(ELEMENTKEY(currBucket), keyPtr, keysize) == 0)
- break;
- prevBucketPtr = &(currBucket->link);
- currBucket = *prevBucketPtr;
#if HASH_STATISTICS
- hash_collisions++;
- hctl->collisions++;
+ hash_collisions++;
+ hctl->collisions++;
#endif
- }
}
if (foundPtr)
@@ -606,17 +586,7 @@ hash_search(HTAB *hashp,
return (void *) ELEMENTKEY(currBucket);
return NULL;
- case HASH_FIND_SAVE:
- if (currBucket != NULL)
- {
- saveState.currBucket = currBucket;
- saveState.prevBucketPtr = prevBucketPtr;
- return (void *) ELEMENTKEY(currBucket);
- }
- return NULL;
-
case HASH_REMOVE:
- case HASH_REMOVE_SAVED:
if (currBucket != NULL)
{
Assert(hctl->nentries > 0);
@@ -638,6 +608,11 @@ hash_search(HTAB *hashp,
}
return NULL;
+ case HASH_ENTER_NULL:
+ /* ENTER_NULL does not work with palloc-based allocator */
+ Assert(hashp->alloc != DynaHashAlloc);
+ /* FALL THRU */
+
case HASH_ENTER:
/* Return existing element if found, else create one */
if (currBucket != NULL)
@@ -649,7 +624,20 @@ hash_search(HTAB *hashp,
{
/* no free elements. allocate another chunk of buckets */
if (!element_alloc(hashp, HASHELEMENT_ALLOC_INCR))
- return NULL; /* out of memory */
+ {
+ /* out of memory */
+ if (action == HASH_ENTER_NULL)
+ return NULL;
+ /* report a generic message */
+ if (hashp->isshared)
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("out of shared memory")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_OUT_OF_MEMORY),
+ errmsg("out of memory")));
+ }
currBucket = hctl->freeList;
Assert(currBucket != NULL);
}