From bcfdc9df046c159e973149f2a82acc99d07d7b83 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 22 Feb 1999 06:16:57 +0000
Subject: [PATCH] Repair some pretty serious problems in dynahash.c and shared
 memory space allocation.  It's a wonder we have not seen bug reports
 traceable to this area ... it's quite clear that the routine dir_realloc()
 has never worked correctly, for example.

---
 src/backend/storage/buffer/buf_init.c |  35 ++----
 src/backend/storage/ipc/ipci.c        |  12 +-
 src/backend/storage/ipc/shmem.c       |  32 ++---
 src/backend/storage/lmgr/lock.c       |  38 ++----
 src/backend/storage/smgr/mm.c         |  31 ++---
 src/backend/utils/hash/dynahash.c     | 170 ++++++++++++++++----------
 src/include/utils/hsearch.h           |  33 ++---
 7 files changed, 178 insertions(+), 173 deletions(-)

diff --git a/src/backend/storage/buffer/buf_init.c b/src/backend/storage/buffer/buf_init.c
index 4dd1b0cefe3..13a55f32768 100644
--- a/src/backend/storage/buffer/buf_init.c
+++ b/src/backend/storage/buffer/buf_init.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/buffer/buf_init.c,v 1.21 1999/02/13 23:17:54 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/buffer/buf_init.c,v 1.22 1999/02/22 06:16:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,7 +33,6 @@
 #include "storage/lmgr.h"
 #include "miscadmin.h"
 #include "utils/builtins.h"
-#include "utils/dynahash.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
 #include "executor/execdebug.h" /* for NDirectFileRead */
@@ -261,21 +260,11 @@ int
 BufferShmemSize()
 {
 	int			size = 0;
-	int			nbuckets;
-	int			nsegs;
-	int			tmp;
-
-	nbuckets = 1 << (int) my_log2((NBuffers - 1) / DEF_FFACTOR + 1);
-	nsegs = 1 << (int) my_log2((nbuckets - 1) / DEF_SEGSIZE + 1);
-
-	/* size of shmem index table */
-	size += MAXALIGN(my_log2(SHMEM_INDEX_SIZE) * sizeof(void *));		/* HTAB->dir */
-	size += MAXALIGN(sizeof(HHDR));		/* HTAB->hctl */
-	size += MAXALIGN(DEF_SEGSIZE * sizeof(SEGMENT));
-	size += BUCKET_ALLOC_INCR *
-		(MAXALIGN(sizeof(BUCKET_INDEX)) +
-		 MAXALIGN(SHMEM_INDEX_KEYSIZE) +
-		 MAXALIGN(SHMEM_INDEX_DATASIZE));
+
+	/* size of shmem index hash table */
+	size += hash_estimate_size(SHMEM_INDEX_SIZE,
+							   SHMEM_INDEX_KEYSIZE,
+							   SHMEM_INDEX_DATASIZE);
 
 	/* size of buffer descriptors */
 	size += MAXALIGN((NBuffers + 1) * sizeof(BufferDesc));
@@ -284,17 +273,13 @@ BufferShmemSize()
 	size += NBuffers * MAXALIGN(BLCKSZ);
 
 	/* size of buffer hash table */
-	size += MAXALIGN(my_log2(NBuffers) * sizeof(void *));		/* HTAB->dir */
-	size += MAXALIGN(sizeof(HHDR));		/* HTAB->hctl */
-	size += nsegs * MAXALIGN(DEF_SEGSIZE * sizeof(SEGMENT));
-	tmp = (int) ceil((double) NBuffers / BUCKET_ALLOC_INCR);
-	size += tmp * BUCKET_ALLOC_INCR *
-		(MAXALIGN(sizeof(BUCKET_INDEX)) +
-		 MAXALIGN(sizeof(BufferTag)) +
-		 MAXALIGN(sizeof(Buffer)));
+	size += hash_estimate_size(NBuffers,
+							   sizeof(BufferTag),
+							   sizeof(Buffer));
 
 #ifdef BMTRACE
 	size += (BMT_LIMIT * sizeof(bmtrace)) + sizeof(long);
 #endif
+
 	return size;
 }
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 49259807b40..2f5d1d3a193 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipci.c,v 1.21 1999/02/21 01:41:44 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/ipc/ipci.c,v 1.22 1999/02/22 06:16:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -72,11 +72,19 @@ CreateSharedMemoryAndSemaphores(IPCKey key, int maxBackends)
 	 * ----------------
 	 */
 	CreateSpinlocks(IPCKeyGetSpinLockSemaphoreKey(key));
-	size = BufferShmemSize() + LockShmemSize(maxBackends);
 
+	/*
+	 * Size of the primary shared-memory block is estimated via
+	 * moderately-accurate estimates for the big hogs, plus 100K for
+	 * the stuff that's too small to bother with estimating.
+	 * Then we add 10% for a safety margin.
+	 */
+	size = BufferShmemSize() + LockShmemSize(maxBackends);
 #ifdef STABLE_MEMORY_STORAGE
 	size += MMShmemSize();
 #endif
+	size += 100000;
+	size += size / 10;
 
 	if (DebugLvl > 1)
 	{
diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c
index afc32068980..3653e844ab6 100644
--- a/src/backend/storage/ipc/shmem.c
+++ b/src/backend/storage/ipc/shmem.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/ipc/shmem.c,v 1.36 1999/02/13 23:18:13 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/ipc/shmem.c,v 1.37 1999/02/22 06:16:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -65,7 +65,6 @@
 #include "storage/shmem.h"
 #include "storage/spin.h"
 #include "storage/proc.h"
-#include "utils/dynahash.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
 #include "access/xact.h"
@@ -215,7 +214,7 @@ InitShmem(unsigned int key, unsigned int size)
 	/* create OR attach to the shared memory shmem index */
 	info.keysize = SHMEM_INDEX_KEYSIZE;
 	info.datasize = SHMEM_INDEX_DATASIZE;
-	hash_flags = (HASH_ELEM);
+	hash_flags = HASH_ELEM;
 
 	/* This will acquire the shmem index lock, but not release it. */
 	ShmemIndex = ShmemInitHash("ShmemIndex",
@@ -340,8 +339,8 @@ ShmemIsValid(unsigned long addr)
  */
 HTAB *
 ShmemInitHash(char *name,		/* table string name for shmem index */
-			  long init_size,	/* initial size */
-			  long max_size,	/* max size of the table */
+			  long init_size,	/* initial table size */
+			  long max_size,	/* max size of the table (NOT USED) */
 			  HASHCTL *infoP,	/* info about key and bucket size */
 			  int hash_flags)	/* info about infoP */
 {
@@ -349,17 +348,20 @@ ShmemInitHash(char *name,		/* table string name for shmem index */
 	long	   *location;
 
 	/*
-	 * shared memory hash tables have a fixed max size so that the control
-	 * structures don't try to grow.  The segbase is for calculating
-	 * pointer values.	The shared memory allocator must be specified.
+	 * Hash tables allocated in shared memory have a fixed directory;
+	 * it can't grow or other backends wouldn't be able to find it.
+	 * The segbase is for calculating pointer values.
+	 * The shared memory allocator must be specified too.
 	 */
+	infoP->dsize = infoP->max_dsize = DEF_DIRSIZE;
 	infoP->segbase = (long *) ShmemBase;
 	infoP->alloc = ShmemAlloc;
-	infoP->max_size = max_size;
-	hash_flags |= HASH_SHARED_MEM;
+	hash_flags |= HASH_SHARED_MEM | HASH_DIRSIZE;
 
 	/* look it up in the shmem index */
-	location = ShmemInitStruct(name, my_log2(max_size) + sizeof(HHDR), &found);
+	location = ShmemInitStruct(name,
+							   sizeof(HHDR) + DEF_DIRSIZE * sizeof(SEG_OFFSET),
+							   &found);
 
 	/*
 	 * shmem index is corrupted.	Let someone else give the error
@@ -375,13 +377,11 @@ ShmemInitHash(char *name,		/* table string name for shmem index */
 	if (found)
 		hash_flags |= HASH_ATTACH;
 
-	/* these structures were allocated or bound in ShmemInitStruct */
-	/* control information and parameters */
+	/* Now provide the header and directory pointers */
 	infoP->hctl = (long *) location;
-	/* directory for hash lookup */
-	infoP->dir = (long *) (location + sizeof(HHDR));
+	infoP->dir = (long *) (((char*) location) + sizeof(HHDR));
 
-	return hash_create(init_size, infoP, hash_flags);;
+	return hash_create(init_size, infoP, hash_flags);
 }
 
 /*
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index 58bd8d04491..ed9a5b31313 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.44 1999/02/21 03:49:22 scrappy Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.45 1999/02/22 06:16:52 tgl Exp $
  *
  * NOTES
  *	  Outside modules can create a lock table and acquire/release
@@ -42,7 +42,6 @@
 #include "storage/spin.h"
 #include "storage/proc.h"
 #include "storage/lock.h"
-#include "utils/dynahash.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
 #include "utils/palloc.h"
@@ -1481,36 +1480,21 @@ int
 LockShmemSize(int maxBackends)
 {
 	int			size = 0;
-	int			nLockEnts = NLOCKENTS(maxBackends);
-	int			nLockBuckets,
-				nLockSegs;
-	int			nXidBuckets,
-				nXidSegs;
-
-	nLockBuckets = 1 << (int) my_log2((nLockEnts - 1) / DEF_FFACTOR + 1);
-	nLockSegs = 1 << (int) my_log2((nLockBuckets - 1) / DEF_SEGSIZE + 1);
-
-	nXidBuckets = 1 << (int) my_log2((NLOCKS_PER_XACT - 1) / DEF_FFACTOR + 1);
-	nXidSegs = 1 << (int) my_log2((nLockBuckets - 1) / DEF_SEGSIZE + 1);
 
+	size += MAXALIGN(sizeof(PROC_HDR)); /* ProcGlobal */
 	size += MAXALIGN(maxBackends * sizeof(PROC)); /* each MyProc */
 	size += MAXALIGN(maxBackends * sizeof(LOCKMETHODCTL));		/* each
 																 * lockMethodTable->ctl */
-	size += MAXALIGN(sizeof(PROC_HDR)); /* ProcGlobal */
 
-	size += MAXALIGN(my_log2(nLockEnts) * sizeof(void *));
-	size += MAXALIGN(sizeof(HHDR));
-	size += nLockSegs * MAXALIGN(DEF_SEGSIZE * sizeof(SEGMENT));
-	size += nLockEnts *			/* XXX not multiple of BUCKET_ALLOC_INCR? */
-		(MAXALIGN(sizeof(BUCKET_INDEX)) +
-		 MAXALIGN(sizeof(LOCK)));		/* contains hash key */
-
-	size += MAXALIGN(my_log2(maxBackends) * sizeof(void *));
-	size += MAXALIGN(sizeof(HHDR));
-	size += nXidSegs * MAXALIGN(DEF_SEGSIZE * sizeof(SEGMENT));
-	size += maxBackends *		/* XXX not multiple of BUCKET_ALLOC_INCR? */
-		(MAXALIGN(sizeof(BUCKET_INDEX)) +
-		 MAXALIGN(sizeof(XIDLookupEnt)));		/* contains hash key */
+	/* lockHash table */
+	size += hash_estimate_size(NLOCKENTS(maxBackends),
+							   sizeof(LOCKTAG),
+							   sizeof(LOCK));
+
+	/* xidHash table */
+	size += hash_estimate_size(maxBackends,
+							   XID_TAGSIZE,
+							   sizeof(XIDLookupEnt));
 
 	return size;
 }
diff --git a/src/backend/storage/smgr/mm.c b/src/backend/storage/smgr/mm.c
index 4ade36df00a..603ee590638 100644
--- a/src/backend/storage/smgr/mm.c
+++ b/src/backend/storage/smgr/mm.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/smgr/Attic/mm.c,v 1.13 1999/02/13 23:18:36 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/smgr/Attic/mm.c,v 1.14 1999/02/22 06:16:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,7 +25,6 @@
 #include "storage/shmem.h"
 #include "storage/spin.h"
 
-#include "utils/dynahash.h"
 #include "utils/hsearch.h"
 #include "utils/rel.h"
 #include "utils/memutils.h"
@@ -565,36 +564,20 @@ int
 MMShmemSize()
 {
 	int			size = 0;
-	int			nbuckets;
-	int			nsegs;
-	int			tmp;
 
 	/*
 	 * first compute space occupied by the (dbid,relid,blkno) hash table
 	 */
-
-	nbuckets = 1 << (int) my_log2((MMNBUFFERS - 1) / DEF_FFACTOR + 1);
-	nsegs = 1 << (int) my_log2((nbuckets - 1) / DEF_SEGSIZE + 1);
-
-	size += MAXALIGN(my_log2(MMNBUFFERS) * sizeof(void *));
-	size += MAXALIGN(sizeof(HHDR));
-	size += nsegs * MAXALIGN(DEF_SEGSIZE * sizeof(SEGMENT));
-	tmp = (int) ceil((double) MMNBUFFERS / BUCKET_ALLOC_INCR);
-	size += tmp * BUCKET_ALLOC_INCR *
-		(MAXALIGN(sizeof(BUCKET_INDEX)) +
-		 MAXALIGN(sizeof(MMHashEntry)));		/* contains hash key */
+	size += hash_estimate_size(MMNBUFFERS,
+				   0, /* MMHashEntry includes key */
+				   sizeof(MMHashEntry));
 
 	/*
 	 * now do the same for the rel hash table
 	 */
-
-	size += MAXALIGN(my_log2(MMNRELATIONS) * sizeof(void *));
-	size += MAXALIGN(sizeof(HHDR));
-	size += nsegs * MAXALIGN(DEF_SEGSIZE * sizeof(SEGMENT));
-	tmp = (int) ceil((double) MMNRELATIONS / BUCKET_ALLOC_INCR);
-	size += tmp * BUCKET_ALLOC_INCR *
-		(MAXALIGN(sizeof(BUCKET_INDEX)) +
-		 MAXALIGN(sizeof(MMRelHashEntry)));		/* contains hash key */
+	size += hash_estimate_size(MMNRELATIONS,
+				   0, /* MMRelHashEntry includes key */
+				   sizeof(MMRelHashEntry));
 
 	/*
 	 * finally, add in the memory block we use directly
diff --git a/src/backend/utils/hash/dynahash.c b/src/backend/utils/hash/dynahash.c
index f41b322189e..dcf4e4cce55 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.18 1999/02/13 23:19:55 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/hash/dynahash.c,v 1.19 1999/02/22 06:16:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -174,7 +174,9 @@ hash_create(int nelem, HASHCTL *info, int flags)
 
 	if (flags & HASH_SHARED_MEM)
 	{
-		/* ctl structure is preallocated for shared memory tables */
+		/* ctl structure is preallocated for shared memory tables.
+		 * Note that HASH_DIRSIZE had better be set as well.
+		 */
 
 		hashp->hctl = (HHDR *) info->hctl;
 		hashp->segbase = (char *) info->segbase;
@@ -190,6 +192,7 @@ hash_create(int nelem, HASHCTL *info, int flags)
 	{
 		/* setup hash table defaults */
 
+		hashp->hctl = NULL;
 		hashp->alloc = (dhalloc_ptr) MEM_ALLOC;
 		hashp->dir = NULL;
 		hashp->segbase = NULL;
@@ -210,11 +213,6 @@ hash_create(int nelem, HASHCTL *info, int flags)
 	hctl->accesses = hctl->collisions = 0;
 #endif
 
-	if (flags & HASH_BUCKET)
-	{
-		hctl->bsize = info->bsize;
-		hctl->bshift = my_log2(info->bsize);
-	}
 	if (flags & HASH_SEGMENT)
 	{
 		hctl->ssize = info->ssize;
@@ -224,13 +222,12 @@ hash_create(int nelem, HASHCTL *info, int flags)
 		hctl->ffactor = info->ffactor;
 
 	/*
-	 * SHM hash tables have fixed maximum size (allocate a maximal sized
-	 * directory).
+	 * SHM hash tables have fixed directory size passed by the caller.
 	 */
 	if (flags & HASH_DIRSIZE)
 	{
-		hctl->max_dsize = my_log2(info->max_size);
-		hctl->dsize = my_log2(info->dsize);
+		hctl->max_dsize = info->max_dsize;
+		hctl->dsize = info->dsize;
 	}
 
 	/*
@@ -254,8 +251,8 @@ hash_create(int nelem, HASHCTL *info, int flags)
 }
 
 /*
-  Allocate and initialize an HTAB structure
-  */
+ * Set default HHDR parameters.
+ */
 static int
 hdefault(HTAB *hashp)
 {
@@ -264,8 +261,6 @@ hdefault(HTAB *hashp)
 	MemSet(hashp->hctl, 0, sizeof(HHDR));
 
 	hctl = hashp->hctl;
-	hctl->bsize = DEF_BUCKET_SIZE;
-	hctl->bshift = DEF_BUCKET_SHIFT;
 	hctl->ssize = DEF_SEGSIZE;
 	hctl->sshift = DEF_SEGSIZE_SHIFT;
 	hctl->dsize = DEF_DIRSIZE;
@@ -295,38 +290,41 @@ init_htab(HTAB *hashp, int nelem)
 	SEG_OFFSET *segp;
 	int			nbuckets;
 	int			nsegs;
-	int			l2;
 	HHDR	   *hctl;
 
 	hctl = hashp->hctl;
 
 	/*
-	 * Divide number of elements by the fill factor and determine a
+	 * Divide number of elements by the fill factor to determine a
 	 * desired number of buckets.  Allocate space for the next greater
 	 * power of two number of buckets
 	 */
 	nelem = (nelem - 1) / hctl->ffactor + 1;
 
-	l2 = my_log2(nelem);
-	nbuckets = 1 << l2;
+	nbuckets = 1 << my_log2(nelem);
 
 	hctl->max_bucket = hctl->low_mask = nbuckets - 1;
 	hctl->high_mask = (nbuckets << 1) - 1;
 
+	/*
+	 * Figure number of directory segments needed, round up to a power of 2
+	 */
 	nsegs = (nbuckets - 1) / hctl->ssize + 1;
 	nsegs = 1 << my_log2(nsegs);
 
-	if (nsegs > hctl->dsize)
-		hctl->dsize = nsegs;
-
-	/* Use two low order bits of points ???? */
-
 	/*
-	 * if ( !(hctl->mem = bit_alloc ( nbuckets )) ) return(-1); if (
-	 * !(hctl->mod = bit_alloc ( nbuckets )) ) return(-1);
+	 * Make sure directory is big enough.
+	 * If pre-allocated directory is too small, choke (caller screwed up).
 	 */
+	if (nsegs > hctl->dsize)
+	{
+		if (!(hashp->dir))
+			hctl->dsize = nsegs;
+		else
+			return -1;
+	}
 
-	/* allocate a directory */
+	/* Allocate a directory */
 	if (!(hashp->dir))
 	{
 		hashp->dir = (SEG_OFFSET *) hashp->alloc(hctl->dsize * sizeof(SEG_OFFSET));
@@ -346,11 +344,9 @@ init_htab(HTAB *hashp, int nelem)
 	}
 
 #if HASH_DEBUG
-	fprintf(stderr, "%s\n%s%x\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%x\n%s%x\n%s%d\n%s%d\n",
+	fprintf(stderr, "%s\n%s%x\n%s%d\n%s%d\n%s%d\n%s%d\n%s%d\n%s%x\n%s%x\n%s%d\n%s%d\n",
 			"init_htab:",
 			"TABLE POINTER   ", hashp,
-			"BUCKET SIZE     ", hctl->bsize,
-			"BUCKET SHIFT    ", hctl->bshift,
 			"DIRECTORY SIZE  ", hctl->dsize,
 			"SEGMENT SIZE    ", hctl->ssize,
 			"SEGMENT SHIFT   ", hctl->sshift,
@@ -364,8 +360,51 @@ init_htab(HTAB *hashp, int nelem)
 	return 0;
 }
 
+/*
+ * Estimate the space needed for a hashtable containing the given number
+ * of entries of given size.
+ * NOTE: this is used to estimate the footprint of hashtables in shared
+ * memory; therefore it does not count HTAB which is in local memory.
+ * NB: assumes that all hash structure parameters have default values!
+ */
+long
+hash_estimate_size(long num_entries, long keysize, long datasize)
+{
+	long	size = 0;
+	long	nBuckets,
+			nSegments,
+			nRecordAllocs,
+			recordSize;
+
+	/* estimate number of buckets wanted */
+	nBuckets = 1L << my_log2((num_entries - 1) / DEF_FFACTOR + 1);
+	/* # of segments needed for nBuckets */
+	nSegments = (nBuckets - 1) / DEF_SEGSIZE + 1;
+
+	/* fixed control info */
+	size += MAXALIGN(sizeof(HHDR));	/* but not HTAB, per above */
+	/* directory, assumed to be of size DEF_DIRSIZE */
+	size += MAXALIGN(DEF_DIRSIZE * sizeof(SEG_OFFSET));
+	/* segments */
+	size += nSegments * MAXALIGN(DEF_SEGSIZE * sizeof(BUCKET_INDEX));
+	/* records --- allocated in groups of BUCKET_ALLOC_INCR */
+	recordSize = sizeof(BUCKET_INDEX) + keysize + datasize;
+	recordSize = MAXALIGN(recordSize);
+	nRecordAllocs = (num_entries - 1) / BUCKET_ALLOC_INCR + 1;
+	size += nRecordAllocs * BUCKET_ALLOC_INCR * recordSize;
+
+	return size;
+}
+
+
 /********************** DESTROY ROUTINES ************************/
 
+/*
+ * 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?
+ */
+
 void
 hash_destroy(HTAB *hashp)
 {
@@ -602,7 +641,7 @@ hash_search(HTAB *hashp,
 	/* link into chain */
 	*prevIndexPtr = currIndex;
 
-	/* copy key and data */
+	/* copy key into record */
 	destAddr = (char *) &(curr->key);
 	memmove(destAddr, keyPtr, hctl->keysize);
 	curr->next = INVALID_INDEX;
@@ -617,13 +656,10 @@ hash_search(HTAB *hashp,
 	 */
 	if (++hctl->nkeys / (hctl->max_bucket + 1) > hctl->ffactor)
 	{
-
-		/*
-		 * fprintf(stderr,"expanding on '%s'\n",keyPtr);
-		 * hash_stats("expanded table",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.
 		 */
-		if (!expand_table(hashp))
-			return NULL;
+		expand_table(hashp);
 	}
 	return &(curr->key);
 }
@@ -724,23 +760,25 @@ expand_table(HTAB *hashp)
 #endif
 
 	hctl = hashp->hctl;
-	new_bucket = ++hctl->max_bucket;
-	old_bucket = (hctl->max_bucket & hctl->low_mask);
 
+	new_bucket = hctl->max_bucket + 1;
 	new_segnum = new_bucket >> hctl->sshift;
 	new_segndx = MOD(new_bucket, hctl->ssize);
 
 	if (new_segnum >= hctl->nsegs)
 	{
-
-		/* Allocate new segment if necessary */
+		/* Allocate new segment if necessary -- could fail if dir full */
 		if (new_segnum >= hctl->dsize)
-			dir_realloc(hashp);
+			if (! dir_realloc(hashp))
+				return 0;
 		if (!(hashp->dir[new_segnum] = seg_alloc(hashp)))
 			return 0;
 		hctl->nsegs++;
 	}
 
+	/* OK, we got a new bucket */
+	hctl->max_bucket++;
+	old_bucket = (hctl->max_bucket & hctl->low_mask);
 
 	if (new_bucket > hctl->high_mask)
 	{
@@ -789,31 +827,32 @@ static int
 dir_realloc(HTAB *hashp)
 {
 	char	   *p;
-	char	  **p_ptr;
+	char	   *old_p;
+	long		new_dsize;
 	long		old_dirsize;
 	long		new_dirsize;
 
-
 	if (hashp->hctl->max_dsize != NO_MAX_DSIZE)
 		return 0;
 
 	/* Reallocate directory */
-	old_dirsize = hashp->hctl->dsize * sizeof(SEGMENT *);
-	new_dirsize = old_dirsize << 1;
+	new_dsize = hashp->hctl->dsize << 1;
+	old_dirsize = hashp->hctl->dsize * sizeof(SEG_OFFSET);
+	new_dirsize = new_dsize * sizeof(SEG_OFFSET);
 
-	p_ptr = (char **) hashp->dir;
+	old_p = (char *) hashp->dir;
 	p = (char *) hashp->alloc((unsigned long) new_dirsize);
+
 	if (p != NULL)
 	{
-		memmove(p, *p_ptr, old_dirsize);
-		MemSet(*p_ptr + old_dirsize, 0, new_dirsize - old_dirsize);
-		free((char *) *p_ptr);
-		*p_ptr = p;
-		hashp->hctl->dsize = new_dirsize;
+		memmove(p, old_p, old_dirsize);
+		MemSet(p + old_dirsize, 0, new_dirsize - old_dirsize);
+		MEM_FREE((char *) old_p);
+		hashp->dir = (SEG_OFFSET *) p;
+		hashp->hctl->dsize = new_dsize;
 		return 1;
 	}
 	return 0;
-
 }
 
 
@@ -823,15 +862,14 @@ seg_alloc(HTAB *hashp)
 	SEGMENT		segp;
 	SEG_OFFSET	segOffset;
 
-
 	segp = (SEGMENT) hashp->alloc((unsigned long)
-								  sizeof(SEGMENT) * hashp->hctl->ssize);
+								  sizeof(BUCKET_INDEX) * hashp->hctl->ssize);
 
 	if (!segp)
 		return 0;
 
 	MemSet((char *) segp, 0,
-		   (long) sizeof(SEGMENT) * hashp->hctl->ssize);
+		   (long) sizeof(BUCKET_INDEX) * hashp->hctl->ssize);
 
 	segOffset = MAKE_HASHOFFSET(hashp, segp);
 	return segOffset;
@@ -849,10 +887,11 @@ bucket_alloc(HTAB *hashp)
 	BUCKET_INDEX tmpIndex,
 				lastIndex;
 
+	/* Each bucket has a BUCKET_INDEX header plus user data. */
 	bucketSize = sizeof(BUCKET_INDEX) + hashp->hctl->keysize + hashp->hctl->datasize;
 
 	/* make sure its aligned correctly */
-	bucketSize += sizeof(long *) - (bucketSize % sizeof(long *));
+	bucketSize = MAXALIGN(bucketSize);
 
 	/*
 	 * tmpIndex is the shmem offset into the first bucket of the array.
@@ -869,8 +908,10 @@ bucket_alloc(HTAB *hashp)
 	lastIndex = hashp->hctl->freeBucketIndex;
 	hashp->hctl->freeBucketIndex = tmpIndex;
 
-	/* initialize each bucket to point to the one behind it */
-	for (i = 0; i < (BUCKET_ALLOC_INCR - 1); i++)
+	/* initialize each bucket to point to the one behind it.
+	 * NOTE: loop sets last bucket incorrectly; we fix below.
+	 */
+	for (i = 0; i < BUCKET_ALLOC_INCR; i++)
 	{
 		tmpBucket = GET_BUCKET(hashp, tmpIndex);
 		tmpIndex += bucketSize;
@@ -879,20 +920,21 @@ bucket_alloc(HTAB *hashp)
 
 	/*
 	 * the last bucket points to the old freelist head (which is probably
-	 * invalid or we wouldnt be here)
+	 * invalid or we wouldn't be here)
 	 */
 	tmpBucket->next = lastIndex;
 
 	return 1;
 }
 
-/* calculate the log base 2 of num */
+/* calculate ceil(log base 2) of num */
 int
 my_log2(long num)
 {
-	int			i = 1;
-	int			limit;
+	int			i;
+	long		limit;
 
-	for (i = 0, limit = 1; limit < num; limit = 2 * limit, i++);
+	for (i = 0, limit = 1; limit < num; i++, limit <<= 1)
+		;
 	return i;
 }
diff --git a/src/include/utils/hsearch.h b/src/include/utils/hsearch.h
index 50b8b3095c6..4e5676eb887 100644
--- a/src/include/utils/hsearch.h
+++ b/src/include/utils/hsearch.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: hsearch.h,v 1.10 1999/02/13 23:22:22 momjian Exp $
+ * $Id: hsearch.h,v 1.11 1999/02/22 06:16:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,16 +16,23 @@
 
 /*
  * Constants
+ *
+ * A hash table has a top-level "directory", each of whose entries points
+ * to a "segment" of ssize bucket headers.  The maximum number of hash
+ * buckets is thus dsize * ssize (but dsize may be expansible).  Of course,
+ * the number of records in the table can be larger, but we don't want a
+ * whole lot of records per bucket or performance goes down.
+ *
+ * In a hash table allocated in shared memory, the directory cannot be
+ * expanded because it must stay at a fixed address.
  */
-#define DEF_BUCKET_SIZE		   256
-#define DEF_BUCKET_SHIFT	   8/* log2(BUCKET) */
 #define DEF_SEGSIZE			   256
-#define DEF_SEGSIZE_SHIFT			   8		/* log2(SEGSIZE)  */
+#define DEF_SEGSIZE_SHIFT	   8			/* log2(SEGSIZE)  */
 #define DEF_DIRSIZE			   256
-#define PRIME1				   37
+#define DEF_FFACTOR			   1			/* default fill factor */
+
+#define PRIME1				   37			/* for the hash function */
 #define PRIME2				   1048583
-#define DEF_FFACTOR			   1
-#define SPLTMAX				   8
 
 
 /*
@@ -46,10 +53,8 @@ typedef unsigned long SEG_OFFSET;
 
 typedef struct hashhdr
 {
-	long		bsize;			/* Bucket/Page Size */
-	long		bshift;			/* Bucket shift */
 	long		dsize;			/* Directory Size */
-	long		ssize;			/* Segment Size */
+	long		ssize;			/* Segment Size --- must be power of 2 */
 	long		sshift;			/* Segment shift */
 	long		max_bucket;		/* ID of Maximum bucket in use */
 	long		high_mask;		/* Mask to modulo into entire table */
@@ -59,8 +64,7 @@ typedef struct hashhdr
 	long		nsegs;			/* Number of allocated segments */
 	long		keysize;		/* hash key length in bytes */
 	long		datasize;		/* elem data length in bytes */
-	long		max_dsize;		/* 'dsize' limit if directory is fixed
-								 * size */
+	long		max_dsize;		/* 'dsize' limit if directory is fixed size */
 	BUCKET_INDEX freeBucketIndex;
 	/* index of first free bucket */
 #ifdef HASH_STATISTICS
@@ -83,14 +87,13 @@ typedef struct htab
 
 typedef struct hashctl
 {
-	long		bsize;			/* Bucket Size */
 	long		ssize;			/* Segment Size */
 	long		dsize;			/* Dirsize Size */
 	long		ffactor;		/* Fill factor */
 	long		(*hash) ();		/* Hash Function */
 	long		keysize;		/* hash key length in bytes */
 	long		datasize;		/* elem data length in bytes */
-	long		max_size;		/* limit to dsize if directory size is
+	long		max_dsize;		/* limit to dsize if directory size is
 								 * limited */
 	long	   *segbase;		/* base for calculating bucket + seg ptrs */
 	long	   *(*alloc) ();	/* memory allocation function */
@@ -100,7 +103,6 @@ typedef struct hashctl
 } HASHCTL;
 
 /* Flags to indicate action for hctl */
-#define HASH_BUCKET		0x001	/* Setting bucket size */
 #define HASH_SEGMENT	0x002	/* Setting segment size */
 #define HASH_DIRSIZE	0x004	/* Setting directory size */
 #define HASH_FFACTOR	0x008	/* Setting fill factor */
@@ -136,6 +138,7 @@ extern void hash_stats(char *where, HTAB *hashp);
 extern long *hash_search(HTAB *hashp, char *keyPtr, HASHACTION action,
 			bool *foundPtr);
 extern long *hash_seq(HTAB *hashp);
+extern long hash_estimate_size(long num_entries, long keysize, long datasize);
 
 /*
  * prototypes from functions in hashfn.c
-- 
GitLab