diff --git a/src/backend/executor/execGrouping.c b/src/backend/executor/execGrouping.c
index 100e7a1c375caa47a6b724a518bc872c3b5e83fc..d293bb7ff29a2e107f68f8ba98b6ec5c04710463 100644
--- a/src/backend/executor/execGrouping.c
+++ b/src/backend/executor/execGrouping.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execGrouping.c,v 1.7 2003/08/08 21:41:34 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execGrouping.c,v 1.8 2003/08/19 01:13:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,6 +23,13 @@
 #include "utils/syscache.h"
 
 
+static TupleHashTable CurTupleHashTable = NULL;
+
+static uint32 TupleHashTableHash(const void *key, Size keysize);
+static int	TupleHashTableMatch(const void *key1, const void *key2,
+								Size keysize);
+
+
 /*****************************************************************************
  *		Utility routines for grouping tuples together
  *****************************************************************************/
@@ -272,7 +279,7 @@ execTuplesHashPrepare(TupleDesc tupdesc,
  *	numCols, keyColIdx: identify the tuple fields to use as lookup key
  *	eqfunctions: equality comparison functions to use
  *	hashfunctions: datatype-specific hashing functions to use
- *	nbuckets: number of buckets to make
+ *	nbuckets: initial estimate of hashtable size
  *	entrysize: size of each entry (at least sizeof(TupleHashEntryData))
  *	tablecxt: memory context in which to store table and table entries
  *	tempcxt: short-lived context for evaluation hash and comparison functions
@@ -290,14 +297,13 @@ BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
 					MemoryContext tablecxt, MemoryContext tempcxt)
 {
 	TupleHashTable hashtable;
-	Size		tabsize;
+	HASHCTL		hash_ctl;
 
 	Assert(nbuckets > 0);
 	Assert(entrysize >= sizeof(TupleHashEntryData));
 
-	tabsize = sizeof(TupleHashTableData) +
-		(nbuckets - 1) *sizeof(TupleHashEntry);
-	hashtable = (TupleHashTable) MemoryContextAllocZero(tablecxt, tabsize);
+	hashtable = (TupleHashTable) MemoryContextAlloc(tablecxt,
+												sizeof(TupleHashTableData));
 
 	hashtable->numCols = numCols;
 	hashtable->keyColIdx = keyColIdx;
@@ -306,7 +312,20 @@ BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
 	hashtable->tablecxt = tablecxt;
 	hashtable->tempcxt = tempcxt;
 	hashtable->entrysize = entrysize;
-	hashtable->nbuckets = nbuckets;
+
+	MemSet(&hash_ctl, 0, sizeof(hash_ctl));
+	hash_ctl.keysize = sizeof(TupleHashEntryData);
+	hash_ctl.entrysize = entrysize;
+	hash_ctl.hash = TupleHashTableHash;
+	hash_ctl.match = TupleHashTableMatch;
+	hash_ctl.hcxt = tablecxt;
+	hashtable->hashtab = hash_create("TupleHashTable", (long) nbuckets,
+									 &hash_ctl,
+					HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_CONTEXT);
+	if (hashtable->hashtab == NULL)
+		ereport(ERROR,
+				(errcode(ERRCODE_OUT_OF_MEMORY),
+				 errmsg("out of memory")));
 
 	return hashtable;
 }
@@ -327,19 +346,93 @@ TupleHashEntry
 LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
 					 bool *isnew)
 {
-	int			numCols = hashtable->numCols;
-	AttrNumber *keyColIdx = hashtable->keyColIdx;
 	HeapTuple	tuple = slot->val;
 	TupleDesc	tupdesc = slot->ttc_tupleDescriptor;
-	uint32		hashkey = 0;
-	int			i;
-	int			bucketno;
 	TupleHashEntry entry;
 	MemoryContext oldContext;
+	TupleHashTable saveCurHT;
+	bool		found;
 
-	/* Need to run the hash function in short-lived context */
+	/* Need to run the hash functions in short-lived context */
 	oldContext = MemoryContextSwitchTo(hashtable->tempcxt);
 
+	/*
+	 * Set up data needed by hash and match functions
+	 *
+	 * We save and restore CurTupleHashTable just in case someone manages
+	 * to invoke this code re-entrantly.
+	 */
+	hashtable->tupdesc = tupdesc;
+	saveCurHT = CurTupleHashTable;
+	CurTupleHashTable = hashtable;
+
+	/* Search the hash table */
+	entry = (TupleHashEntry) hash_search(hashtable->hashtab,
+										 &tuple,
+										 isnew ? HASH_ENTER : HASH_FIND,
+										 &found);
+
+	if (isnew)
+	{
+		if (found)
+		{
+			/* found pre-existing entry */
+			*isnew = false;
+		}
+		else
+		{
+			/* created new entry ... we hope */
+			if (entry == NULL)
+				ereport(ERROR,
+						(errcode(ERRCODE_OUT_OF_MEMORY),
+						 errmsg("out of memory")));
+
+			/*
+			 * Zero any caller-requested space in the entry.  (This zaps
+			 * the "key data" dynahash.c copied into the new entry, but
+			 * we don't care since we're about to overwrite it anyway.)
+			 */
+			MemSet(entry, 0, hashtable->entrysize);
+
+			/* Copy the first tuple into the table context */
+			MemoryContextSwitchTo(hashtable->tablecxt);
+			entry->firstTuple = heap_copytuple(tuple);
+
+			*isnew = true;
+		}
+	}
+
+	CurTupleHashTable = saveCurHT;
+
+	MemoryContextSwitchTo(oldContext);
+
+	return entry;
+}
+
+/*
+ * Compute the hash value for a tuple
+ *
+ * The passed-in key is a pointer to a HeapTuple pointer -- this is either
+ * the firstTuple field of a TupleHashEntry struct, or the key value passed
+ * to hash_search.  We ignore the keysize.
+ *
+ * CurTupleHashTable must be set before calling this, since dynahash.c
+ * doesn't provide any API that would let us get at the hashtable otherwise.
+ *
+ * Also, the caller must select an appropriate memory context for running
+ * the hash functions.  (dynahash.c doesn't change CurrentMemoryContext.)
+ */
+static uint32
+TupleHashTableHash(const void *key, Size keysize)
+{
+	HeapTuple	tuple = *(const HeapTuple *) key;
+	TupleHashTable hashtable = CurTupleHashTable;
+	int			numCols = hashtable->numCols;
+	AttrNumber *keyColIdx = hashtable->keyColIdx;
+	TupleDesc	tupdesc = hashtable->tupdesc;
+	uint32		hashkey = 0;
+	int			i;
+
 	for (i = 0; i < numCols; i++)
 	{
 		AttrNumber	att = keyColIdx[i];
@@ -360,72 +453,36 @@ LookupTupleHashEntry(TupleHashTable hashtable, TupleTableSlot *slot,
 			hashkey ^= hkey;
 		}
 	}
-	bucketno = hashkey % (uint32) hashtable->nbuckets;
-
-	for (entry = hashtable->buckets[bucketno];
-		 entry != NULL;
-		 entry = entry->next)
-	{
-		/* Quick check using hashkey */
-		if (entry->hashkey != hashkey)
-			continue;
-		if (execTuplesMatch(entry->firstTuple,
-							tuple,
-							tupdesc,
-							numCols, keyColIdx,
-							hashtable->eqfunctions,
-							hashtable->tempcxt))
-		{
-			if (isnew)
-				*isnew = false;
-			MemoryContextSwitchTo(oldContext);
-			return entry;
-		}
-	}
-
-	/* Not there, so build a new one if requested */
-	if (isnew)
-	{
-		MemoryContextSwitchTo(hashtable->tablecxt);
-
-		entry = (TupleHashEntry) palloc0(hashtable->entrysize);
-
-		entry->hashkey = hashkey;
-		entry->firstTuple = heap_copytuple(tuple);
-
-		entry->next = hashtable->buckets[bucketno];
-		hashtable->buckets[bucketno] = entry;
-
-		*isnew = true;
-	}
-
-	MemoryContextSwitchTo(oldContext);
 
-	return entry;
+	return hashkey;
 }
 
 /*
- * Walk through all the entries of a hash table, in no special order.
- * Returns NULL when no more entries remain.
+ * See whether two tuples (presumably of the same hash value) match
+ *
+ * As above, the passed pointers are pointers to HeapTuple pointers.
  *
- * Iterator state must be initialized with ResetTupleHashIterator() macro.
+ * CurTupleHashTable must be set before calling this, since dynahash.c
+ * doesn't provide any API that would let us get at the hashtable otherwise.
+ *
+ * Also, the caller must select an appropriate memory context for running
+ * the compare functions.  (dynahash.c doesn't change CurrentMemoryContext.)
  */
-TupleHashEntry
-ScanTupleHashTable(TupleHashTable hashtable, TupleHashIterator *state)
+static int
+TupleHashTableMatch(const void *key1, const void *key2, Size keysize)
 {
-	TupleHashEntry entry;
-
-	entry = state->next_entry;
-	while (entry == NULL)
-	{
-		if (state->next_bucket >= hashtable->nbuckets)
-		{
-			/* No more entries in hashtable, so done */
-			return NULL;
-		}
-		entry = hashtable->buckets[state->next_bucket++];
-	}
-	state->next_entry = entry->next;
-
-	return entry;
+	HeapTuple	tuple1 = *(const HeapTuple *) key1;
+	HeapTuple	tuple2 = *(const HeapTuple *) key2;
+	TupleHashTable hashtable = CurTupleHashTable;
+
+	if (execTuplesMatch(tuple1,
+						tuple2,
+						hashtable->tupdesc,
+						hashtable->numCols,
+						hashtable->keyColIdx,
+						hashtable->eqfunctions,
+						hashtable->tempcxt))
+		return 0;
+	else
+		return 1;
 }
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index d8fb9a9565da6919d8a9adf07ebbc3d5fe564da9..d9adb09dafbe05ae10adf618cb63a969c478c667 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -45,7 +45,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.115 2003/08/08 21:41:41 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.116 2003/08/19 01:13:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -905,7 +905,7 @@ agg_fill_hash_table(AggState *aggstate)
 
 	aggstate->table_filled = true;
 	/* Initialize to walk the hash table */
-	ResetTupleHashIterator(&aggstate->hashiter);
+	ResetTupleHashIterator(aggstate->hashtable, &aggstate->hashiter);
 }
 
 /*
@@ -920,7 +920,6 @@ agg_retrieve_hash_table(AggState *aggstate)
 	bool	   *aggnulls;
 	AggStatePerAgg peragg;
 	AggStatePerGroup pergroup;
-	TupleHashTable hashtable;
 	AggHashEntry entry;
 	TupleTableSlot *firstSlot;
 	TupleTableSlot *resultSlot;
@@ -935,7 +934,6 @@ agg_retrieve_hash_table(AggState *aggstate)
 	aggnulls = econtext->ecxt_aggnulls;
 	projInfo = aggstate->ss.ps.ps_ProjInfo;
 	peragg = aggstate->peragg;
-	hashtable = aggstate->hashtable;
 	firstSlot = aggstate->ss.ss_ScanTupleSlot;
 
 	/*
@@ -950,8 +948,7 @@ agg_retrieve_hash_table(AggState *aggstate)
 		/*
 		 * Find the next entry in the hash table
 		 */
-		entry = (AggHashEntry) ScanTupleHashTable(hashtable,
-												  &aggstate->hashiter);
+		entry = (AggHashEntry) ScanTupleHashTable(&aggstate->hashiter);
 		if (entry == NULL)
 		{
 			/* No more entries in hashtable, so done */
@@ -1440,7 +1437,7 @@ ExecReScanAgg(AggState *node, ExprContext *exprCtxt)
 		 */
 		if (((PlanState *) node)->lefttree->chgParam == NULL)
 		{
-			ResetTupleHashIterator(&node->hashiter);
+			ResetTupleHashIterator(node->hashtable, &node->hashiter);
 			return;
 		}
 	}
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 7530be263f32b0d286e8bfb5f9a43d368ae6550c..23b0cd3bf3d6c9499698cadfeea1b78dc33ab2c3 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.54 2003/08/08 21:41:42 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.55 2003/08/19 01:13:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -627,8 +627,8 @@ findPartialMatch(TupleHashTable hashtable, TupleTableSlot *slot)
 	TupleHashIterator hashiter;
 	TupleHashEntry entry;
 
-	ResetTupleHashIterator(&hashiter);
-	while ((entry = ScanTupleHashTable(hashtable, &hashiter)) != NULL)
+	ResetTupleHashIterator(hashtable, &hashiter);
+	while ((entry = ScanTupleHashTable(&hashiter)) != NULL)
 	{
 		if (!execTuplesUnequal(entry->firstTuple,
 							   tuple,
diff --git a/src/backend/utils/hash/dynahash.c b/src/backend/utils/hash/dynahash.c
index 7090d000587c7fb704106f023bddedc4b405db51..c6f9b0236975f153b3cdea846313e73161d6a1e2 100644
--- a/src/backend/utils/hash/dynahash.c
+++ b/src/backend/utils/hash/dynahash.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/hash/dynahash.c,v 1.47 2003/08/04 02:40:06 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/hash/dynahash.c,v 1.48 2003/08/19 01:13:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,7 +44,6 @@
 
 #include "postgres.h"
 
-
 #include "utils/dynahash.h"
 #include "utils/hsearch.h"
 #include "utils/memutils.h"
@@ -63,7 +62,6 @@
  * Private function prototypes
  */
 static void *DynaHashAlloc(Size size);
-static uint32 call_hash(HTAB *hashp, void *k);
 static HASHSEGMENT seg_alloc(HTAB *hashp);
 static bool element_alloc(HTAB *hashp);
 static bool dir_realloc(HTAB *hashp);
@@ -133,6 +131,19 @@ hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
 	else
 		hashp->hash = string_hash;		/* default hash function */
 
+	/*
+	 * If you don't specify a match function, it defaults to strncmp() if
+	 * you used string_hash (either explicitly or by default) and to
+	 * memcmp() otherwise.  (Prior to PostgreSQL 7.4, memcmp() was always
+	 * used.)
+	 */
+	if (flags & HASH_COMPARE)
+		hashp->match = info->match;
+	else if (hashp->hash == string_hash)
+		hashp->match = (HashCompareFunc) strncmp;
+	else
+		hashp->match = memcmp;
+
 	if (flags & HASH_SHARED_MEM)
 	{
 		/*
@@ -155,7 +166,7 @@ hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
 		hashp->hctl = NULL;
 		hashp->dir = NULL;
 		hashp->alloc = MEM_ALLOC;
-		hashp->hcxt = DynaHashCxt;
+		hashp->hcxt = CurrentDynaHashCxt;
 		hashp->isshared = false;
 	}
 
@@ -207,26 +218,13 @@ hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
 		hashp->alloc = info->alloc;
 	else
 	{
-		if (flags & HASH_CONTEXT)
-		{
-			/* hash table structures live in child of given context */
-			CurrentDynaHashCxt = AllocSetContextCreate(info->hcxt,
-													   "DynaHashTable",
-												ALLOCSET_DEFAULT_MINSIZE,
-											   ALLOCSET_DEFAULT_INITSIZE,
-											   ALLOCSET_DEFAULT_MAXSIZE);
-			hashp->hcxt = CurrentDynaHashCxt;
-		}
-		else
-		{
-			/* hash table structures live in child of DynaHashCxt */
-			CurrentDynaHashCxt = AllocSetContextCreate(DynaHashCxt,
-													   "DynaHashTable",
-												ALLOCSET_DEFAULT_MINSIZE,
-											   ALLOCSET_DEFAULT_INITSIZE,
-											   ALLOCSET_DEFAULT_MAXSIZE);
-			hashp->hcxt = CurrentDynaHashCxt;
-		}
+		/* remaining hash table structures live in child of given context */
+		hashp->hcxt = AllocSetContextCreate(CurrentDynaHashCxt,
+											"DynaHashTable",
+											ALLOCSET_DEFAULT_MINSIZE,
+											ALLOCSET_DEFAULT_INITSIZE,
+											ALLOCSET_DEFAULT_MAXSIZE);
+		CurrentDynaHashCxt = hashp->hcxt;
 	}
 
 	if (!init_htab(hashp, nelem))
@@ -351,7 +349,7 @@ init_htab(HTAB *hashp, long nelem)
  * NB: assumes that all hash structure parameters have default values!
  */
 long
-hash_estimate_size(long num_entries, long entrysize)
+hash_estimate_size(long num_entries, Size entrysize)
 {
 	long		size = 0;
 	long		nBuckets,
@@ -447,7 +445,6 @@ void
 hash_stats(const char *where, HTAB *hashp)
 {
 #if HASH_STATISTICS
-
 	fprintf(stderr, "%s: this HTAB -- accesses %ld collisions %ld\n",
 			where, hashp->hctl->accesses, hashp->hctl->collisions);
 
@@ -459,19 +456,16 @@ hash_stats(const char *where, HTAB *hashp)
 	fprintf(stderr, "hash_stats: total expansions %ld\n",
 			hash_expansions);
 #endif
-
 }
 
 /*******************************SEARCH ROUTINES *****************************/
 
-static uint32
-call_hash(HTAB *hashp, void *k)
-{
-	HASHHDR    *hctl = hashp->hctl;
-	uint32		hash_val,
-				bucket;
 
-	hash_val = hashp->hash(k, (int) hctl->keysize);
+/* Convert a hash value to a bucket number */
+static inline uint32
+calc_bucket(HASHHDR *hctl, uint32 hash_val)
+{
+	uint32		bucket;
 
 	bucket = hash_val & hctl->high_mask;
 	if (bucket > hctl->max_bucket)
@@ -506,11 +500,12 @@ call_hash(HTAB *hashp, void *k)
  */
 void *
 hash_search(HTAB *hashp,
-			void *keyPtr,
+			const void *keyPtr,
 			HASHACTION action,
 			bool *foundPtr)
 {
 	HASHHDR    *hctl = hashp->hctl;
+	uint32		hashvalue = 0;
 	uint32		bucket;
 	long		segment_num;
 	long		segment_ndx;
@@ -545,7 +540,12 @@ hash_search(HTAB *hashp,
 	}
 	else
 	{
-		bucket = call_hash(hashp, keyPtr);
+		HashCompareFunc match;
+		Size		keysize = hctl->keysize;
+
+		hashvalue = hashp->hash(keyPtr, keysize);
+		bucket = calc_bucket(hctl, hashvalue);
+
 		segment_num = bucket >> hctl->sshift;
 		segment_ndx = MOD(bucket, hctl->ssize);
 
@@ -560,9 +560,11 @@ hash_search(HTAB *hashp,
 		/*
 		 * Follow collision chain looking for matching key
 		 */
+		match = hashp->match;	/* save one fetch in inner loop */
 		while (currBucket != NULL)
 		{
-			if (memcmp(ELEMENTKEY(currBucket), keyPtr, hctl->keysize) == 0)
+			if (currBucket->hashvalue == hashvalue &&
+				match(ELEMENTKEY(currBucket), keyPtr, keysize) == 0)
 				break;
 			prevBucketPtr = &(currBucket->link);
 			currBucket = *prevBucketPtr;
@@ -641,6 +643,7 @@ hash_search(HTAB *hashp,
 			currBucket->link = NULL;
 
 			/* copy key into record */
+			currBucket->hashvalue = hashvalue;
 			memcpy(ELEMENTKEY(currBucket), keyPtr, hctl->keysize);
 
 			/* caller is expected to fill the data field on return */
@@ -802,7 +805,7 @@ expand_table(HTAB *hashp)
 
 	/*
 	 * 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
+	 * hash masking is done in calc_bucket, 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!
 	 */
@@ -820,8 +823,7 @@ expand_table(HTAB *hashp)
 		 currElement = nextElement)
 	{
 		nextElement = currElement->link;
-		if ((long) call_hash(hashp, (void *) ELEMENTKEY(currElement))
-			== old_bucket)
+		if ((long) calc_bucket(hctl, currElement->hashvalue) == old_bucket)
 		{
 			*oldlink = currElement;
 			oldlink = &currElement->link;
diff --git a/src/backend/utils/hash/hashfn.c b/src/backend/utils/hash/hashfn.c
index 835bd007a9746a0ea6ea689f42a1f4778f9a8fd1..3f7a0089075fa1243afe30500dc33940e9d1d211 100644
--- a/src/backend/utils/hash/hashfn.c
+++ b/src/backend/utils/hash/hashfn.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/hash/hashfn.c,v 1.18 2003/08/04 02:40:06 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/hash/hashfn.c,v 1.19 2003/08/19 01:13:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,24 +22,21 @@
 /*
  * string_hash: hash function for keys that are null-terminated strings.
  *
- * NOTE: since dynahash.c backs this up with a fixed-length memcmp(),
- * the key must actually be zero-padded to the specified maximum length
- * to work correctly.  However, if it is known that nothing after the
- * first zero byte is interesting, this is the right hash function to use.
- *
  * NOTE: this is the default hash function if none is specified.
  */
 uint32
-string_hash(void *key, int keysize)
+string_hash(const void *key, Size keysize)
 {
-	return DatumGetUInt32(hash_any((unsigned char *) key, strlen((char *) key)));
+	return DatumGetUInt32(hash_any((const unsigned char *) key,
+								   (int) strlen((const char *) key)));
 }
 
 /*
  * tag_hash: hash function for fixed-size tag values
  */
 uint32
-tag_hash(void *key, int keysize)
+tag_hash(const void *key, Size keysize)
 {
-	return DatumGetUInt32(hash_any((unsigned char *) key, keysize));
+	return DatumGetUInt32(hash_any((const unsigned char *) key,
+								   (int) keysize));
 }
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index af2f123d2d6f329e30cf9c1895e2a53227dd9095..88449034feec56d90ff0c0294c8443fab3c9b6e8 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: executor.h,v 1.99 2003/08/08 21:42:44 momjian Exp $
+ * $Id: executor.h,v 1.100 2003/08/19 01:13:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -71,8 +71,6 @@ extern TupleHashTable BuildTupleHashTable(int numCols, AttrNumber *keyColIdx,
 extern TupleHashEntry LookupTupleHashEntry(TupleHashTable hashtable,
 					 TupleTableSlot *slot,
 					 bool *isnew);
-extern TupleHashEntry ScanTupleHashTable(TupleHashTable hashtable,
-				   TupleHashIterator *state);
 
 /*
  * prototypes from functions in execJunk.c
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 3f163b8fdaa9aafa574638c4293c4fb3fe59f7fc..8d180009bfd97cb3f2ce34a123fee8de312f9934 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.103 2003/08/08 21:42:47 momjian Exp $
+ * $Id: execnodes.h,v 1.104 2003/08/19 01:13:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,6 +21,7 @@
 #include "nodes/bitmapset.h"
 #include "nodes/params.h"
 #include "nodes/plannodes.h"
+#include "utils/hsearch.h"
 #include "utils/tuplestore.h"
 
 
@@ -344,14 +345,14 @@ typedef struct TupleHashTableData *TupleHashTable;
 
 typedef struct TupleHashEntryData
 {
-	TupleHashEntry next;		/* next entry in same hash bucket */
-	uint32		hashkey;		/* exact hash key of this entry */
+	/* firstTuple must be the first field in this struct! */
 	HeapTuple	firstTuple;		/* copy of first tuple in this group */
 	/* there may be additional data beyond the end of this struct */
 } TupleHashEntryData;			/* VARIABLE LENGTH STRUCT */
 
 typedef struct TupleHashTableData
 {
+	HTAB	   *hashtab;		/* underlying dynahash table */
 	int			numCols;		/* number of columns in lookup key */
 	AttrNumber *keyColIdx;		/* attr numbers of key columns */
 	FmgrInfo   *eqfunctions;	/* lookup data for comparison functions */
@@ -359,19 +360,15 @@ typedef struct TupleHashTableData
 	MemoryContext tablecxt;		/* memory context containing table */
 	MemoryContext tempcxt;		/* context for function evaluations */
 	Size		entrysize;		/* actual size to make each hash entry */
-	int			nbuckets;		/* number of buckets in hash table */
-	TupleHashEntry buckets[1];	/* VARIABLE LENGTH ARRAY */
-} TupleHashTableData;			/* VARIABLE LENGTH STRUCT */
+	TupleDesc	tupdesc;		/* tuple descriptor */
+} TupleHashTableData;
 
-typedef struct
-{
-	TupleHashEntry next_entry;	/* next entry in current chain */
-	int			next_bucket;	/* next chain */
-} TupleHashIterator;
+typedef HASH_SEQ_STATUS TupleHashIterator;
 
-#define ResetTupleHashIterator(iter) \
-	((iter)->next_entry = NULL, \
-	 (iter)->next_bucket = 0)
+#define ResetTupleHashIterator(htable, iter) \
+	hash_seq_init(iter, (htable)->hashtab)
+#define ScanTupleHashTable(iter) \
+	((TupleHashEntry) hash_seq_search(iter))
 
 
 /* ----------------------------------------------------------------
diff --git a/src/include/utils/hsearch.h b/src/include/utils/hsearch.h
index 905268badc6199e7cee00cf20bf93553daa4bf81..05d26e9a15092b95965fe1fe774a0bdd0812b8a8 100644
--- a/src/include/utils/hsearch.h
+++ b/src/include/utils/hsearch.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: hsearch.h,v 1.28 2003/08/04 02:40:15 momjian Exp $
+ * $Id: hsearch.h,v 1.29 2003/08/19 01:13:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,6 +15,23 @@
 #define HSEARCH_H
 
 
+/*
+ * Hash and comparison functions must have these signatures.  Comparison
+ * functions return zero for match, nonzero for no match.  (The comparison
+ * function definition is designed to allow memcmp() and strncmp() to be
+ * used directly as key comparison functions.)
+ */
+typedef uint32 (*HashValueFunc) (const void *key, Size keysize);
+typedef int (*HashCompareFunc) (const void *key1, const void *key2,
+								Size keysize);
+
+/*
+ * Space allocation function for a hashtable --- designed to match malloc().
+ * Note: there is no free function API; can't destroy a hashtable unless you
+ * use the default allocator.
+ */
+typedef void *(*HashAllocFunc) (Size request);
+
 /*
  * Constants
  *
@@ -44,6 +61,7 @@
 typedef struct HASHELEMENT
 {
 	struct HASHELEMENT *link;	/* link to next entry in same bucket */
+	uint32	hashvalue;			/* hash function result for this entry */
 } HASHELEMENT;
 
 /* A hash bucket is a linked list of HASHELEMENTs */
@@ -64,8 +82,8 @@ typedef struct HASHHDR
 	long		ffactor;		/* Fill factor */
 	long		nentries;		/* Number of entries in hash table */
 	long		nsegs;			/* Number of allocated segments */
-	long		keysize;		/* hash key length in bytes */
-	long		entrysize;		/* total user element size in bytes */
+	Size		keysize;		/* hash key length in bytes */
+	Size		entrysize;		/* total user element size in bytes */
 	long		max_dsize;		/* 'dsize' limit if directory is fixed
 								 * size */
 	HASHELEMENT *freeList;		/* linked list of free elements */
@@ -83,8 +101,9 @@ typedef struct HTAB
 {
 	HASHHDR    *hctl;			/* shared control information */
 	HASHSEGMENT *dir;			/* directory of segment starts */
-	uint32		(*hash) (void *key, int keysize);		/* Hash Function */
-	void	   *(*alloc) (Size);	/* memory allocator */
+	HashValueFunc hash;			/* hash function */
+	HashCompareFunc match;		/* key comparison function */
+	HashAllocFunc alloc;		/* memory allocator */
 	MemoryContext hcxt;			/* memory context if default allocator
 								 * used */
 	char	   *tabname;		/* table name (for error messages) */
@@ -97,28 +116,30 @@ typedef struct HASHCTL
 {
 	long		ssize;			/* Segment Size */
 	long		dsize;			/* (initial) Directory Size */
-	long		ffactor;		/* Fill factor */
-	uint32		(*hash) (void *key, int keysize);		/* Hash Function */
-	long		keysize;		/* hash key length in bytes */
-	long		entrysize;		/* total user element size in bytes */
 	long		max_dsize;		/* limit to dsize if directory size is
 								 * limited */
-	void	   *(*alloc) (Size);	/* memory allocation function */
+	long		ffactor;		/* Fill factor */
+	Size		keysize;		/* hash key length in bytes */
+	Size		entrysize;		/* total user element size in bytes */
+	HashValueFunc hash;			/* hash function */
+	HashCompareFunc match;		/* key comparison function */
+	HashAllocFunc alloc;		/* memory allocator */
 	HASHSEGMENT *dir;			/* directory of segment starts */
 	HASHHDR    *hctl;			/* location of header in shared mem */
 	MemoryContext hcxt;			/* memory context to use for allocations */
 } HASHCTL;
 
 /* Flags to indicate which parameters are supplied */
-#define HASH_SEGMENT	0x002	/* Setting segment size */
-#define HASH_DIRSIZE	0x004	/* Setting directory size */
-#define HASH_FFACTOR	0x008	/* Setting fill factor */
+#define HASH_SEGMENT	0x002	/* Set segment size */
+#define HASH_DIRSIZE	0x004	/* Set directory size */
+#define HASH_FFACTOR	0x008	/* Set fill factor */
 #define HASH_FUNCTION	0x010	/* Set user defined hash function */
-#define HASH_ELEM		0x020	/* Setting key/entry size */
-#define HASH_SHARED_MEM 0x040	/* Setting shared mem const */
+#define HASH_ELEM		0x020	/* Set key/entry size */
+#define HASH_SHARED_MEM 0x040	/* Set shared mem const */
 #define HASH_ATTACH		0x080	/* Do not initialize hctl */
-#define HASH_ALLOC		0x100	/* Setting memory allocator */
-#define HASH_CONTEXT	0x200	/* Setting explicit memory context */
+#define HASH_ALLOC		0x100	/* Set memory allocator */
+#define HASH_CONTEXT	0x200	/* Set explicit memory context */
+#define HASH_COMPARE	0x400	/* Set user defined comparison function */
 
 
 /* max_dsize value to indicate expansible directory */
@@ -151,17 +172,17 @@ extern HTAB *hash_create(const char *tabname, long nelem,
 			HASHCTL *info, int flags);
 extern void hash_destroy(HTAB *hashp);
 extern void hash_stats(const char *where, HTAB *hashp);
-extern void *hash_search(HTAB *hashp, void *keyPtr, HASHACTION action,
+extern void *hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action,
 			bool *foundPtr);
 extern void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp);
 extern void *hash_seq_search(HASH_SEQ_STATUS *status);
-extern long hash_estimate_size(long num_entries, long entrysize);
+extern long hash_estimate_size(long num_entries, Size entrysize);
 extern long hash_select_dirsize(long num_entries);
 
 /*
  * prototypes for functions in hashfn.c
  */
-extern uint32 string_hash(void *key, int keysize);
-extern uint32 tag_hash(void *key, int keysize);
+extern uint32 string_hash(const void *key, Size keysize);
+extern uint32 tag_hash(const void *key, Size keysize);
 
 #endif   /* HSEARCH_H */
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index 04b52738e6e9236b14b30c975463f7958fef3d5f..bc6b9e4d85d9ba425df987a24a2507a6138d05ce 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -350,183 +350,183 @@ select f3, myaggp01a(*) from t group by f3;
  f3 | myaggp01a 
 ----+-----------
  b  | {}
- a  | {}
  c  | {}
+ a  | {}
 (3 rows)
 
 select f3, myaggp03a(*) from t group by f3;
  f3 | myaggp03a 
 ----+-----------
  b  | {}
- a  | {}
  c  | {}
+ a  | {}
 (3 rows)
 
 select f3, myaggp03b(*) from t group by f3;
  f3 | myaggp03b 
 ----+-----------
  b  | {}
- a  | {}
  c  | {}
+ a  | {}
 (3 rows)
 
 select f3, myaggp05a(f1) from t group by f3;
  f3 | myaggp05a 
 ----+-----------
  b  | {1,2,3}
- a  | {1,2,3}
  c  | {1,2}
+ a  | {1,2,3}
 (3 rows)
 
 select f3, myaggp06a(f1) from t group by f3;
  f3 | myaggp06a 
 ----+-----------
  b  | {}
- a  | {}
  c  | {}
+ a  | {}
 (3 rows)
 
 select f3, myaggp08a(f1) from t group by f3;
  f3 | myaggp08a 
 ----+-----------
  b  | {}
- a  | {}
  c  | {}
+ a  | {}
 (3 rows)
 
 select f3, myaggp09a(f1) from t group by f3;
  f3 | myaggp09a 
 ----+-----------
  b  | {}
- a  | {}
  c  | {}
+ a  | {}
 (3 rows)
 
 select f3, myaggp09b(f1) from t group by f3;
  f3 | myaggp09b 
 ----+-----------
  b  | {}
- a  | {}
  c  | {}
+ a  | {}
 (3 rows)
 
 select f3, myaggp10a(f1) from t group by f3;
  f3 | myaggp10a 
 ----+-----------
  b  | {1,2,3}
- a  | {1,2,3}
  c  | {1,2}
+ a  | {1,2,3}
 (3 rows)
 
 select f3, myaggp10b(f1) from t group by f3;
  f3 | myaggp10b 
 ----+-----------
  b  | {1,2,3}
- a  | {1,2,3}
  c  | {1,2}
+ a  | {1,2,3}
 (3 rows)
 
 select f3, myaggp20a(f1) from t group by f3;
  f3 | myaggp20a 
 ----+-----------
  b  | {1,2,3}
- a  | {1,2,3}
  c  | {1,2}
+ a  | {1,2,3}
 (3 rows)
 
 select f3, myaggp20b(f1) from t group by f3;
  f3 | myaggp20b 
 ----+-----------
  b  | {1,2,3}
- a  | {1,2,3}
  c  | {1,2}
+ a  | {1,2,3}
 (3 rows)
 
 select f3, myaggn01a(*) from t group by f3;
  f3 | myaggn01a 
 ----+-----------
  b  | {}
- a  | {}
  c  | {}
+ a  | {}
 (3 rows)
 
 select f3, myaggn01b(*) from t group by f3;
  f3 | myaggn01b 
 ----+-----------
  b  | {}
- a  | {}
  c  | {}
+ a  | {}
 (3 rows)
 
 select f3, myaggn03a(*) from t group by f3;
  f3 | myaggn03a 
 ----+-----------
  b  | {}
- a  | {}
  c  | {}
+ a  | {}
 (3 rows)
 
 select f3, myaggn05a(f1) from t group by f3;
  f3 | myaggn05a 
 ----+-----------
  b  | {1,2,3}
- a  | {1,2,3}
  c  | {1,2}
+ a  | {1,2,3}
 (3 rows)
 
 select f3, myaggn05b(f1) from t group by f3;
  f3 | myaggn05b 
 ----+-----------
  b  | {1,2,3}
- a  | {1,2,3}
  c  | {1,2}
+ a  | {1,2,3}
 (3 rows)
 
 select f3, myaggn06a(f1) from t group by f3;
  f3 | myaggn06a 
 ----+-----------
  b  | {}
- a  | {}
  c  | {}
+ a  | {}
 (3 rows)
 
 select f3, myaggn06b(f1) from t group by f3;
  f3 | myaggn06b 
 ----+-----------
  b  | {}
- a  | {}
  c  | {}
+ a  | {}
 (3 rows)
 
 select f3, myaggn08a(f1) from t group by f3;
  f3 | myaggn08a 
 ----+-----------
  b  | {}
- a  | {}
  c  | {}
+ a  | {}
 (3 rows)
 
 select f3, myaggn08b(f1) from t group by f3;
  f3 | myaggn08b 
 ----+-----------
  b  | {}
- a  | {}
  c  | {}
+ a  | {}
 (3 rows)
 
 select f3, myaggn09a(f1) from t group by f3;
  f3 | myaggn09a 
 ----+-----------
  b  | {}
- a  | {}
  c  | {}
+ a  | {}
 (3 rows)
 
 select f3, myaggn10a(f1) from t group by f3;
  f3 | myaggn10a 
 ----+-----------
  b  | {1,2,3}
- a  | {1,2,3}
  c  | {1,2}
+ a  | {1,2,3}
 (3 rows)