diff --git a/src/backend/utils/hash/dynahash.c b/src/backend/utils/hash/dynahash.c
index 933533b754958cf1c8d0fa9252438a102920292c..28ee6a15dee9bf66fc5acc497b2f1ba57a4715a1 100644
--- a/src/backend/utils/hash/dynahash.c
+++ b/src/backend/utils/hash/dynahash.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/hash/dynahash.c,v 1.22 1999/05/25 16:12:28 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/hash/dynahash.c,v 1.23 1999/05/31 17:01:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,25 +52,23 @@
 #include	   "utils/memutils.h"
 
 /*
- * Fast arithmetic, relying on powers of 2,
- * and on pre-processor concatenation property
+ * Fast MOD arithmetic, assuming that y is a power of 2 !
  */
 
 #define MOD(x,y)			   ((x) & ((y)-1))
 
-/*
- * external routines
- */
-
 /*
  * Private function prototypes
  */
 static long *DynaHashAlloc(unsigned int size);
 static void DynaHashFree(Pointer ptr);
-static uint32 call_hash(HTAB *hashp, char *k, int len);
+static uint32 call_hash(HTAB *hashp, char *k);
 static SEG_OFFSET seg_alloc(HTAB *hashp);
 static int	bucket_alloc(HTAB *hashp);
 static int	dir_realloc(HTAB *hashp);
+static int	expand_table(HTAB *hashp);
+static int	hdefault(HTAB *hashp);
+static int	init_htab(HTAB *hashp, int nelem);
 
 typedef long *((*dhalloc_ptr) ());
 
@@ -118,15 +116,6 @@ DynaHashFree(Pointer ptr)
 
 #endif	 /* FRONTEND */
 
-/* ----------------
- * Internal routines
- * ----------------
- */
-
-static int	expand_table(HTAB *hashp);
-static int	hdefault(HTAB *hashp);
-static int	init_htab(HTAB *hashp, int nelem);
-
 
 /*
  * pointer access macros.  Shared memory implementation cannot
@@ -220,6 +209,8 @@ hash_create(int nelem, HASHCTL *info, int flags)
 	{
 		hctl->ssize = info->ssize;
 		hctl->sshift = my_log2(info->ssize);
+		/* ssize had better be a power of 2 */
+		Assert(hctl->ssize == (1L << hctl->sshift));
 	}
 	if (flags & HASH_FFACTOR)
 		hctl->ffactor = info->ffactor;
@@ -412,6 +403,8 @@ hash_estimate_size(long num_entries, long keysize, long datasize)
  * XXX this sure looks thoroughly broken to me --- tgl 2/99.
  * It's freeing every entry individually --- but they weren't
  * allocated individually, see bucket_alloc!!  Why doesn't it crash?
+ * ANSWER: it probably does crash, but is never invoked in normal
+ * operations...
  */
 
 void
@@ -479,14 +472,13 @@ hash_stats(char *where, HTAB *hashp)
 /*******************************SEARCH ROUTINES *****************************/
 
 static uint32
-call_hash(HTAB *hashp, char *k, int len)
+call_hash(HTAB *hashp, char *k)
 {
+	HHDR	   *hctl = hashp->hctl;
 	long		hash_val,
 				bucket;
-	HHDR	   *hctl;
 
-	hctl = hashp->hctl;
-	hash_val = hashp->hash(k, len);
+	hash_val = hashp->hash(k, (int) hctl->keysize);
 
 	bucket = hash_val & hctl->high_mask;
 	if (bucket > hctl->max_bucket)
@@ -550,9 +542,9 @@ hash_search(HTAB *hashp,
 	}
 	else
 	{
-		bucket = call_hash(hashp, keyPtr, hctl->keysize);
+		bucket = call_hash(hashp, keyPtr);
 		segment_num = bucket >> hctl->sshift;
-		segment_ndx = bucket & (hctl->ssize - 1);
+		segment_ndx = MOD(bucket, hctl->ssize);
 
 		segp = GET_SEG(hashp, segment_num);
 
@@ -598,8 +590,10 @@ hash_search(HTAB *hashp,
 				Assert(hctl->nkeys > 0);
 				hctl->nkeys--;
 
-				/* add the bucket to the freelist for this table.  */
+				/* remove record from hash bucket's chain. */
 				*prevIndexPtr = curr->next;
+
+				/* add the record to the freelist for this table.  */
 				curr->next = hctl->freeBucketIndex;
 				hctl->freeBucketIndex = currIndex;
 
@@ -639,7 +633,6 @@ hash_search(HTAB *hashp,
 	currIndex = hctl->freeBucketIndex;
 	if (currIndex == INVALID_INDEX)
 	{
-
 		/* no free elements.  allocate another chunk of buckets */
 		if (!bucket_alloc(hashp))
 			return NULL;
@@ -722,7 +715,7 @@ hash_seq(HTAB *hashp)
 		 * initialize the search within this bucket.
 		 */
 		segment_num = curBucket >> hctl->sshift;
-		segment_ndx = curBucket & (hctl->ssize - 1);
+		segment_ndx = MOD(curBucket, hctl->ssize);
 
 		/*
 		 * first find the right segment in the table directory.
@@ -751,6 +744,10 @@ hash_seq(HTAB *hashp)
 
 
 /********************************* UTILITIES ************************/
+
+/*
+ * Expand the table by adding one more hash bucket.
+ */
 static int
 expand_table(HTAB *hashp)
 {
@@ -790,19 +787,31 @@ expand_table(HTAB *hashp)
 		hctl->nsegs++;
 	}
 
-	/* OK, we got a new bucket */
+	/* OK, we created a new bucket */
 	hctl->max_bucket++;
-	old_bucket = (hctl->max_bucket & hctl->low_mask);
 
+	/*
+	 * *Before* changing masks, find old bucket corresponding to same hash
+	 * values; values in that bucket may need to be relocated to new bucket.
+	 * Note that new_bucket is certainly larger than low_mask at this point,
+	 * so we can skip the first step of the regular hash mask calc.
+	 */
+	old_bucket = (new_bucket & hctl->low_mask);
+
+	/*
+	 * If we crossed a power of 2, readjust masks.
+	 */
 	if (new_bucket > hctl->high_mask)
 	{
-		/* Starting a new doubling */
 		hctl->low_mask = hctl->high_mask;
 		hctl->high_mask = new_bucket | hctl->low_mask;
 	}
 
 	/*
-	 * Relocate records to the new bucket
+	 * Relocate records to the new bucket.  NOTE: because of the way the
+	 * hash masking is done in call_hash, only one old bucket can need to
+	 * be split at this point.  With a different way of reducing the hash
+	 * value, that might not be true!
 	 */
 	old_segnum = old_bucket >> hctl->sshift;
 	old_segndx = MOD(old_bucket, hctl->ssize);
@@ -816,12 +825,9 @@ expand_table(HTAB *hashp)
 		 chainIndex != INVALID_INDEX;
 		 chainIndex = nextIndex)
 	{
-
 		chain = GET_BUCKET(hashp, chainIndex);
 		nextIndex = chain->next;
-		if (call_hash(hashp,
-					  (char *) &(chain->key),
-					  hctl->keysize) == old_bucket)
+		if (call_hash(hashp, (char *) &(chain->key)) == old_bucket)
 		{
 			*old = chainIndex;
 			old = &chain->next;
@@ -831,8 +837,10 @@ expand_table(HTAB *hashp)
 			*newbi = chainIndex;
 			newbi = &chain->next;
 		}
-		chain->next = INVALID_INDEX;
 	}
+	/* don't forget to terminate the rebuilt hash chains... */
+	*old = INVALID_INDEX;
+	*newbi = INVALID_INDEX;
 	return 1;
 }
 
@@ -907,15 +915,13 @@ bucket_alloc(HTAB *hashp)
 	/* make sure its aligned correctly */
 	bucketSize = MAXALIGN(bucketSize);
 
-	/*
-	 * tmpIndex is the shmem offset into the first bucket of the array.
-	 */
 	tmpBucket = (ELEMENT *)
 		hashp->alloc((unsigned long) BUCKET_ALLOC_INCR * bucketSize);
 
 	if (!tmpBucket)
 		return 0;
 
+	/* tmpIndex is the shmem offset into the first bucket of the array */
 	tmpIndex = MAKE_HASHOFFSET(hashp, tmpBucket);
 
 	/* set the freebucket list to point to the first bucket */