hashoom.patch
application/octet-stream
Filename: hashoom.patch
Type: application/octet-stream
Part: 0
Message:
Re: hash_search and out of memory
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/utils/hash/dynahash.c | 84 | 58 |
diff --git a/src/backend/utils/hash/dynahash.c b/src/backend/utils/hash/dynahash.c
index 6d0e0f5..210e1f2 100644
--- a/src/backend/utils/hash/dynahash.c
+++ b/src/backend/utils/hash/dynahash.c
@@ -195,6 +195,9 @@ static HASHSEGMENT seg_alloc(HTAB *hashp);
static bool element_alloc(HTAB *hashp, int nelem);
static bool dir_realloc(HTAB *hashp);
static bool expand_table(HTAB *hashp);
+static HASHBUCKET get_matched_bucket(HTAB *hashp,
+ const void *keyPtr, uint32 hashvalue,
+ HASHBUCKET **prevBucketPtr);
static HASHBUCKET get_hash_entry(HTAB *hashp);
static void hdefault(HTAB *hashp);
static int choose_nelem_alloc(Size entrysize);
@@ -806,54 +809,15 @@ hash_search_with_hash_value(HTAB *hashp,
bool *foundPtr)
{
HASHHDR *hctl = hashp->hctl;
- Size keysize;
- uint32 bucket;
- long segment_num;
- long segment_ndx;
- HASHSEGMENT segp;
HASHBUCKET currBucket;
HASHBUCKET *prevBucketPtr;
- HashCompareFunc match;
#if HASH_STATISTICS
hash_accesses++;
hctl->accesses++;
#endif
- /*
- * Do the initial lookup
- */
- bucket = calc_bucket(hctl, hashvalue);
-
- segment_num = bucket >> hashp->sshift;
- segment_ndx = MOD(bucket, hashp->ssize);
-
- segp = hashp->dir[segment_num];
-
- if (segp == NULL)
- hash_corrupted(hashp);
-
- prevBucketPtr = &segp[segment_ndx];
- currBucket = *prevBucketPtr;
-
- /*
- * Follow collision chain looking for matching key
- */
- match = hashp->match; /* save one fetch in inner loop */
- keysize = hashp->keysize; /* ditto */
-
- 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++;
-#endif
- }
+ currBucket = get_matched_bucket(hashp, keyPtr, hashvalue, &prevBucketPtr);
if (foundPtr)
*foundPtr = (bool) (currBucket != NULL);
@@ -915,6 +879,31 @@ hash_search_with_hash_value(HTAB *hashp,
elog(ERROR, "cannot insert into frozen hashtable \"%s\"",
hashp->tabname);
+ /*
+ * Check if it is time to split a bucket. Can't split if running
+ * in partitioned mode, nor if table is the subject of any active
+ * hash_seq_search scans. Strange order of these tests is to try
+ * to check cheaper conditions first.
+ */
+ if (!IS_PARTITIONED(hctl) &&
+ (hctl->nentries + 1) / (long) (hctl->max_bucket + 1) >= hctl->ffactor &&
+ !has_seq_scans(hashp))
+ {
+ /*
+ * We do this before allocating a hash entry, because some
+ * long-lived hash tables like relcache could run into
+ * trouble in transaction abort clean up if expand_table
+ * errors out after we allocate an entry that has not been
+ * filled out.
+ */
+ expand_table(hashp);
+
+ /*
+ * Calculate bucket to find the spot to insert the new entry.
+ */
+ get_matched_bucket(hashp, keyPtr, hashvalue, &prevBucketPtr);
+ }
+
currBucket = get_hash_entry(hashp);
if (currBucket == NULL)
{
@@ -938,27 +927,10 @@ hash_search_with_hash_value(HTAB *hashp,
/* copy key into record */
currBucket->hashvalue = hashvalue;
- hashp->keycopy(ELEMENTKEY(currBucket), keyPtr, keysize);
+ hashp->keycopy(ELEMENTKEY(currBucket), keyPtr, hashp->keysize);
/* caller is expected to fill the data field on return */
- /*
- * Check if it is time to split a bucket. Can't split if running
- * in partitioned mode, nor if table is the subject of any active
- * hash_seq_search scans. Strange order of these tests is to try
- * to check cheaper conditions first.
- */
- if (!IS_PARTITIONED(hctl) &&
- hctl->nentries / (long) (hctl->max_bucket + 1) >= hctl->ffactor &&
- !has_seq_scans(hashp))
- {
- /*
- * NOTE: failure to expand table is not a fatal error, it just
- * means we have to run at higher fill factor than we wanted.
- */
- expand_table(hashp);
- }
-
return (void *) ELEMENTKEY(currBucket);
}
@@ -968,6 +940,60 @@ hash_search_with_hash_value(HTAB *hashp,
}
/*
+ * Get the entry that matches key, with the previous entry in the same bucket.
+ */
+static HASHBUCKET
+get_matched_bucket(HTAB *hashp, const void *keyPtr, uint32 hashvalue,
+ HASHBUCKET **prevBucketPtr)
+{
+ HASHHDR *hctl = hashp->hctl;
+ Size keysize;
+ uint32 bucket;
+ long segment_num;
+ long segment_ndx;
+ HASHSEGMENT segp;
+ HASHBUCKET currBucket;
+ HashCompareFunc match;
+
+ /*
+ * Do the initial lookup
+ */
+ bucket = calc_bucket(hctl, hashvalue);
+
+ segment_num = bucket >> hashp->sshift;
+ segment_ndx = MOD(bucket, hashp->ssize);
+
+ segp = hashp->dir[segment_num];
+
+ if (segp == NULL)
+ hash_corrupted(hashp);
+
+ *prevBucketPtr = &segp[segment_ndx];
+ currBucket = **prevBucketPtr;
+
+ /*
+ * Follow collision chain looking for matching key
+ */
+ match = hashp->match; /* save one fetch in inner loop */
+ keysize = hashp->keysize; /* ditto */
+
+ 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++;
+#endif
+ }
+
+ return currBucket;
+}
+
+/*
* create a new entry if possible
*/
static HASHBUCKET