mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-10-01 22:31:18 +02:00
Fix hash_update_hash_key() to handle same-bucket case correctly.
Original coding would corrupt the hashtable if the item being updated was at the end of its bucket chain and the new hash key hashed to that same bucket. Diagnosis and fix by Heikki Linnakangas.
This commit is contained in:
parent
3f4b1749a8
commit
1b794d3f32
@ -1022,6 +1022,7 @@ hash_update_hash_key(HTAB *hashp,
|
|||||||
uint32 newhashvalue;
|
uint32 newhashvalue;
|
||||||
Size keysize;
|
Size keysize;
|
||||||
uint32 bucket;
|
uint32 bucket;
|
||||||
|
uint32 newbucket;
|
||||||
long segment_num;
|
long segment_num;
|
||||||
long segment_ndx;
|
long segment_ndx;
|
||||||
HASHSEGMENT segp;
|
HASHSEGMENT segp;
|
||||||
@ -1078,10 +1079,10 @@ hash_update_hash_key(HTAB *hashp,
|
|||||||
*/
|
*/
|
||||||
newhashvalue = hashp->hash(newKeyPtr, hashp->keysize);
|
newhashvalue = hashp->hash(newKeyPtr, hashp->keysize);
|
||||||
|
|
||||||
bucket = calc_bucket(hctl, newhashvalue);
|
newbucket = calc_bucket(hctl, newhashvalue);
|
||||||
|
|
||||||
segment_num = bucket >> hashp->sshift;
|
segment_num = newbucket >> hashp->sshift;
|
||||||
segment_ndx = MOD(bucket, hashp->ssize);
|
segment_ndx = MOD(newbucket, hashp->ssize);
|
||||||
|
|
||||||
segp = hashp->dir[segment_num];
|
segp = hashp->dir[segment_num];
|
||||||
|
|
||||||
@ -1115,12 +1116,22 @@ hash_update_hash_key(HTAB *hashp,
|
|||||||
|
|
||||||
currBucket = existingElement;
|
currBucket = existingElement;
|
||||||
|
|
||||||
/* OK to remove record from old hash bucket's chain. */
|
/*
|
||||||
*oldPrevPtr = currBucket->link;
|
* If old and new hash values belong to the same bucket, we need not
|
||||||
|
* change any chain links, and indeed should not since this simplistic
|
||||||
|
* update will corrupt the list if currBucket is the last element. (We
|
||||||
|
* cannot fall out earlier, however, since we need to scan the bucket to
|
||||||
|
* check for duplicate keys.)
|
||||||
|
*/
|
||||||
|
if (bucket != newbucket)
|
||||||
|
{
|
||||||
|
/* OK to remove record from old hash bucket's chain. */
|
||||||
|
*oldPrevPtr = currBucket->link;
|
||||||
|
|
||||||
/* link into new hashbucket chain */
|
/* link into new hashbucket chain */
|
||||||
*prevBucketPtr = currBucket;
|
*prevBucketPtr = currBucket;
|
||||||
currBucket->link = NULL;
|
currBucket->link = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* copy new key into record */
|
/* copy new key into record */
|
||||||
currBucket->hashvalue = newhashvalue;
|
currBucket->hashvalue = newhashvalue;
|
||||||
|
Loading…
Reference in New Issue
Block a user