diff --git a/contrib/pg_trgm/trgm_regexp.c b/contrib/pg_trgm/trgm_regexp.c
index 9f050533c59853d487590dca194cea425ce583e2..f7a73cb72c214f29d225670667bba907b4bf1c3d 100644
--- a/contrib/pg_trgm/trgm_regexp.c
+++ b/contrib/pg_trgm/trgm_regexp.c
@@ -915,11 +915,10 @@ transformGraph(TrgmNFA *trgmNFA)
 	hashCtl.keysize = sizeof(TrgmStateKey);
 	hashCtl.entrysize = sizeof(TrgmState);
 	hashCtl.hcxt = CurrentMemoryContext;
-	hashCtl.hash = tag_hash;
 	trgmNFA->states = hash_create("Trigram NFA",
 								  1024,
 								  &hashCtl,
-								  HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION);
+								  HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 	/* Create initial state: ambiguous prefix, NFA's initial state */
 	MemSet(&initkey, 0, sizeof(initkey));
diff --git a/contrib/postgres_fdw/connection.c b/contrib/postgres_fdw/connection.c
index 116be7ddcb77ce2509e8c4d538fc988d8f80b991..61216e5cc03ad8be517ffe0066be85ebe2a5ee0a 100644
--- a/contrib/postgres_fdw/connection.c
+++ b/contrib/postgres_fdw/connection.c
@@ -109,12 +109,11 @@ GetConnection(ForeignServer *server, UserMapping *user,
 		MemSet(&ctl, 0, sizeof(ctl));
 		ctl.keysize = sizeof(ConnCacheKey);
 		ctl.entrysize = sizeof(ConnCacheEntry);
-		ctl.hash = tag_hash;
 		/* allocate ConnectionHash in the cache context */
 		ctl.hcxt = CacheMemoryContext;
 		ConnectionHash = hash_create("postgres_fdw connections", 8,
 									 &ctl,
-								   HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+									 HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 		/*
 		 * Register some callback functions that manage connection cleanup.
diff --git a/src/backend/access/gist/gistbuild.c b/src/backend/access/gist/gistbuild.c
index 5acc986585a8e2d6aeac800f867fdf8899f4c5a9..09a9df4974b008019a70171e9a7d43d11c998aa9 100644
--- a/src/backend/access/gist/gistbuild.c
+++ b/src/backend/access/gist/gistbuild.c
@@ -1142,12 +1142,10 @@ gistInitParentMap(GISTBuildState *buildstate)
 	hashCtl.keysize = sizeof(BlockNumber);
 	hashCtl.entrysize = sizeof(ParentMapEntry);
 	hashCtl.hcxt = CurrentMemoryContext;
-	hashCtl.hash = oid_hash;
 	buildstate->parentMap = hash_create("gistbuild parent map",
 										1024,
 										&hashCtl,
-										HASH_ELEM | HASH_CONTEXT
-										| HASH_FUNCTION);
+									  HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 }
 
 static void
diff --git a/src/backend/access/gist/gistbuildbuffers.c b/src/backend/access/gist/gistbuildbuffers.c
index 577ea613b11f8a13eda9425b6dd6df7d491f8548..4937c38b4ef0df81199299934881d8cfbf9b141e 100644
--- a/src/backend/access/gist/gistbuildbuffers.c
+++ b/src/backend/access/gist/gistbuildbuffers.c
@@ -76,16 +76,14 @@ gistInitBuildBuffers(int pagesPerBuffer, int levelStep, int maxLevel)
 	 * nodeBuffersTab hash is association between index blocks and it's
 	 * buffers.
 	 */
+	memset(&hashCtl, 0, sizeof(hashCtl));
 	hashCtl.keysize = sizeof(BlockNumber);
 	hashCtl.entrysize = sizeof(GISTNodeBuffer);
 	hashCtl.hcxt = CurrentMemoryContext;
-	hashCtl.hash = tag_hash;
-	hashCtl.match = memcmp;
 	gfbb->nodeBuffersTab = hash_create("gistbuildbuffers",
 									   1024,
 									   &hashCtl,
-									   HASH_ELEM | HASH_CONTEXT
-									   | HASH_FUNCTION | HASH_COMPARE);
+									   HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 	gfbb->bufferEmptyingQueue = NIL;
 
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index 4b132b7d016891bc4faf74eb9203366bbbe1812a..97388657863b4ddfc403d9d88d9375fb82a92217 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -283,13 +283,12 @@ begin_heap_rewrite(Relation old_heap, Relation new_heap, TransactionId oldest_xm
 	hash_ctl.keysize = sizeof(TidHashKey);
 	hash_ctl.entrysize = sizeof(UnresolvedTupData);
 	hash_ctl.hcxt = state->rs_cxt;
-	hash_ctl.hash = tag_hash;
 
 	state->rs_unresolved_tups =
 		hash_create("Rewrite / Unresolved ctids",
 					128,		/* arbitrary initial size */
 					&hash_ctl,
-					HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+					HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 	hash_ctl.entrysize = sizeof(OldToNewMappingData);
 
@@ -297,7 +296,7 @@ begin_heap_rewrite(Relation old_heap, Relation new_heap, TransactionId oldest_xm
 		hash_create("Rewrite / Old to new tid map",
 					128,		/* arbitrary initial size */
 					&hash_ctl,
-					HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+					HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 	MemoryContextSwitchTo(old_cxt);
 
@@ -834,13 +833,12 @@ logical_begin_heap_rewrite(RewriteState state)
 	hash_ctl.keysize = sizeof(TransactionId);
 	hash_ctl.entrysize = sizeof(RewriteMappingFile);
 	hash_ctl.hcxt = state->rs_cxt;
-	hash_ctl.hash = tag_hash;
 
 	state->rs_logical_mappings =
 		hash_create("Logical rewrite mapping",
 					128,		/* arbitrary initial size */
 					&hash_ctl,
-					HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+					HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 }
 
 /*
diff --git a/src/backend/access/transam/xlogutils.c b/src/backend/access/transam/xlogutils.c
index ae323a0db87f6db005928147730346fafdfd2dc7..89b2ba5b08cea44e873a47af81f6f3054865b80b 100644
--- a/src/backend/access/transam/xlogutils.c
+++ b/src/backend/access/transam/xlogutils.c
@@ -107,12 +107,11 @@ log_invalid_page(RelFileNode node, ForkNumber forkno, BlockNumber blkno,
 		memset(&ctl, 0, sizeof(ctl));
 		ctl.keysize = sizeof(xl_invalid_page_key);
 		ctl.entrysize = sizeof(xl_invalid_page);
-		ctl.hash = tag_hash;
 
 		invalid_page_tab = hash_create("XLOG invalid-page table",
 									   100,
 									   &ctl,
-									   HASH_ELEM | HASH_FUNCTION);
+									   HASH_ELEM | HASH_BLOBS);
 	}
 
 	/* we currently assume xl_invalid_page_key contains no padding */
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index ba5b938863cb492632f9c45acb3c29b8d363165a..811e1d488040eebf767c44dfd368c9df00cb6c55 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -986,10 +986,9 @@ create_seq_hashtable(void)
 	memset(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(Oid);
 	ctl.entrysize = sizeof(SeqTableData);
-	ctl.hash = oid_hash;
 
 	seqhashtab = hash_create("Sequence values", 16, &ctl,
-							 HASH_ELEM | HASH_FUNCTION);
+							 HASH_ELEM | HASH_BLOBS);
 }
 
 /*
diff --git a/src/backend/nodes/tidbitmap.c b/src/backend/nodes/tidbitmap.c
index a880c81cf1c8fc3cb14b4b5d9b007b171c91b19c..3b074381f4ddf9a16dad9fa1ae6138773551ca20 100644
--- a/src/backend/nodes/tidbitmap.c
+++ b/src/backend/nodes/tidbitmap.c
@@ -221,12 +221,11 @@ tbm_create_pagetable(TIDBitmap *tbm)
 	MemSet(&hash_ctl, 0, sizeof(hash_ctl));
 	hash_ctl.keysize = sizeof(BlockNumber);
 	hash_ctl.entrysize = sizeof(PagetableEntry);
-	hash_ctl.hash = tag_hash;
 	hash_ctl.hcxt = tbm->mcxt;
 	tbm->pagetable = hash_create("TIDBitmap",
 								 128,	/* start small and extend */
 								 &hash_ctl,
-								 HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+								 HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 	/* If entry1 is valid, push it into the hashtable */
 	if (tbm->status == TBM_ONE_PAGE)
diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c
index 9f9a208d3e621baa750d773cd991d25bcbcb84f7..6daa0588a6cbf3700ee16a054a5ac35be815a718 100644
--- a/src/backend/optimizer/util/predtest.c
+++ b/src/backend/optimizer/util/predtest.c
@@ -1711,9 +1711,8 @@ lookup_proof_cache(Oid pred_op, Oid clause_op, bool refute_it)
 		MemSet(&ctl, 0, sizeof(ctl));
 		ctl.keysize = sizeof(OprProofCacheKey);
 		ctl.entrysize = sizeof(OprProofCacheEntry);
-		ctl.hash = tag_hash;
 		OprProofCacheHash = hash_create("Btree proof lookup cache", 256,
-										&ctl, HASH_ELEM | HASH_FUNCTION);
+										&ctl, HASH_ELEM | HASH_BLOBS);
 
 		/* Arrange to flush cache on pg_amop changes */
 		CacheRegisterSyscacheCallback(AMOPOPID,
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index b65b632f17e41a1f76a7d58b735c3a2a2efae3ce..11a6397a4d51eb1851999cbfbcd690a65154ab1d 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -1059,9 +1059,8 @@ find_oper_cache_entry(OprCacheKey *key)
 		MemSet(&ctl, 0, sizeof(ctl));
 		ctl.keysize = sizeof(OprCacheKey);
 		ctl.entrysize = sizeof(OprCacheEntry);
-		ctl.hash = tag_hash;
 		OprCacheHash = hash_create("Operator lookup cache", 256,
-								   &ctl, HASH_ELEM | HASH_FUNCTION);
+								   &ctl, HASH_ELEM | HASH_BLOBS);
 
 		/* Arrange to flush cache on pg_operator and pg_cast changes */
 		CacheRegisterSyscacheCallback(OPERNAMENSP,
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 1d6e3f35f94a67fec37b38c7991c2b40a4132dfa..675f985eadacc09004ee735ae822e060487821e3 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -922,10 +922,9 @@ rebuild_database_list(Oid newdb)
 	 */
 	hctl.keysize = sizeof(Oid);
 	hctl.entrysize = sizeof(avl_dbase);
-	hctl.hash = oid_hash;
 	hctl.hcxt = tmpcxt;
 	dbhash = hash_create("db hash", 20, &hctl,	/* magic number here FIXME */
-						 HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+						 HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 	/* start by inserting the new database */
 	score = 0;
@@ -1997,12 +1996,11 @@ do_autovacuum(void)
 	MemSet(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(Oid);
 	ctl.entrysize = sizeof(av_relation);
-	ctl.hash = oid_hash;
 
 	table_toast_map = hash_create("TOAST to main relid map",
 								  100,
 								  &ctl,
-								  HASH_ELEM | HASH_FUNCTION);
+								  HASH_ELEM | HASH_BLOBS);
 
 	/*
 	 * Scan pg_class to determine which tables to vacuum.
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 6c814ba0be8fc732de2ba3093d4e6b64843437de..8a79d9b23ad95083bc38b86dd7aa907bcabcfb68 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -1212,13 +1212,12 @@ CompactCheckpointerRequestQueue(void)
 	MemSet(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(CheckpointerRequest);
 	ctl.entrysize = sizeof(struct CheckpointerSlotMapping);
-	ctl.hash = tag_hash;
 	ctl.hcxt = CurrentMemoryContext;
 
 	htab = hash_create("CompactCheckpointerRequestQueue",
 					   CheckpointerShmem->num_requests,
 					   &ctl,
-					   HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+					   HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 	/*
 	 * The basic idea here is that a request can be skipped if it's followed
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index 6672b8bae51688df7e740a6e8df6267fc9f8f13d..17bfc0bd159c7c2b4064d139a066b1174501ab2c 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -1132,12 +1132,11 @@ pgstat_collect_oids(Oid catalogid)
 	memset(&hash_ctl, 0, sizeof(hash_ctl));
 	hash_ctl.keysize = sizeof(Oid);
 	hash_ctl.entrysize = sizeof(Oid);
-	hash_ctl.hash = oid_hash;
 	hash_ctl.hcxt = CurrentMemoryContext;
 	htab = hash_create("Temporary table of OIDs",
 					   PGSTAT_TAB_HASH_SIZE,
 					   &hash_ctl,
-					   HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+					   HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 	rel = heap_open(catalogid, AccessShareLock);
 	snapshot = RegisterSnapshot(GetLatestSnapshot());
@@ -1520,11 +1519,10 @@ pgstat_init_function_usage(FunctionCallInfoData *fcinfo,
 		memset(&hash_ctl, 0, sizeof(hash_ctl));
 		hash_ctl.keysize = sizeof(Oid);
 		hash_ctl.entrysize = sizeof(PgStat_BackendFunctionEntry);
-		hash_ctl.hash = oid_hash;
 		pgStatFunctions = hash_create("Function stat entries",
 									  PGSTAT_FUNCTION_HASH_SIZE,
 									  &hash_ctl,
-									  HASH_ELEM | HASH_FUNCTION);
+									  HASH_ELEM | HASH_BLOBS);
 	}
 
 	/* Get the stats entry for this function, create if necessary */
@@ -3483,19 +3481,17 @@ reset_dbentry_counters(PgStat_StatDBEntry *dbentry)
 	memset(&hash_ctl, 0, sizeof(hash_ctl));
 	hash_ctl.keysize = sizeof(Oid);
 	hash_ctl.entrysize = sizeof(PgStat_StatTabEntry);
-	hash_ctl.hash = oid_hash;
 	dbentry->tables = hash_create("Per-database table",
 								  PGSTAT_TAB_HASH_SIZE,
 								  &hash_ctl,
-								  HASH_ELEM | HASH_FUNCTION);
+								  HASH_ELEM | HASH_BLOBS);
 
 	hash_ctl.keysize = sizeof(Oid);
 	hash_ctl.entrysize = sizeof(PgStat_StatFuncEntry);
-	hash_ctl.hash = oid_hash;
 	dbentry->functions = hash_create("Per-database function",
 									 PGSTAT_FUNCTION_HASH_SIZE,
 									 &hash_ctl,
-									 HASH_ELEM | HASH_FUNCTION);
+									 HASH_ELEM | HASH_BLOBS);
 }
 
 /*
@@ -3902,10 +3898,9 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 	memset(&hash_ctl, 0, sizeof(hash_ctl));
 	hash_ctl.keysize = sizeof(Oid);
 	hash_ctl.entrysize = sizeof(PgStat_StatDBEntry);
-	hash_ctl.hash = oid_hash;
 	hash_ctl.hcxt = pgStatLocalContext;
 	dbhash = hash_create("Databases hash", PGSTAT_DB_HASH_SIZE, &hash_ctl,
-						 HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+						 HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 	/*
 	 * Clear out global and archiver statistics so they start from zero in
@@ -4026,21 +4021,19 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
 				memset(&hash_ctl, 0, sizeof(hash_ctl));
 				hash_ctl.keysize = sizeof(Oid);
 				hash_ctl.entrysize = sizeof(PgStat_StatTabEntry);
-				hash_ctl.hash = oid_hash;
 				hash_ctl.hcxt = pgStatLocalContext;
 				dbentry->tables = hash_create("Per-database table",
 											  PGSTAT_TAB_HASH_SIZE,
 											  &hash_ctl,
-								   HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+									  HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 				hash_ctl.keysize = sizeof(Oid);
 				hash_ctl.entrysize = sizeof(PgStat_StatFuncEntry);
-				hash_ctl.hash = oid_hash;
 				hash_ctl.hcxt = pgStatLocalContext;
 				dbentry->functions = hash_create("Per-database function",
 												 PGSTAT_FUNCTION_HASH_SIZE,
 												 &hash_ctl,
-								   HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+									  HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 				/*
 				 * If requested, read the data from the database-specific
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index cd132c16adf7872ed1803ab38e9483ad4159fd0e..799ec8430ce6dcd65226c978bcecf6e22c8d85df 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -245,11 +245,10 @@ ReorderBufferAllocate(void)
 
 	hash_ctl.keysize = sizeof(TransactionId);
 	hash_ctl.entrysize = sizeof(ReorderBufferTXNByIdEnt);
-	hash_ctl.hash = tag_hash;
 	hash_ctl.hcxt = buffer->context;
 
 	buffer->by_txn = hash_create("ReorderBufferByXid", 1000, &hash_ctl,
-								 HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+								 HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 	buffer->by_txn_last_xid = InvalidTransactionId;
 	buffer->by_txn_last_txn = NULL;
@@ -1111,7 +1110,6 @@ ReorderBufferBuildTupleCidHash(ReorderBuffer *rb, ReorderBufferTXN *txn)
 
 	hash_ctl.keysize = sizeof(ReorderBufferTupleCidKey);
 	hash_ctl.entrysize = sizeof(ReorderBufferTupleCidEnt);
-	hash_ctl.hash = tag_hash;
 	hash_ctl.hcxt = rb->context;
 
 	/*
@@ -1120,7 +1118,7 @@ ReorderBufferBuildTupleCidHash(ReorderBuffer *rb, ReorderBufferTXN *txn)
 	 */
 	txn->tuplecid_hash =
 		hash_create("ReorderBufferTupleCid", txn->ntuplecids, &hash_ctl,
-					HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+					HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 	dlist_foreach(iter, &txn->tuplecids)
 	{
@@ -2434,10 +2432,9 @@ ReorderBufferToastInitHash(ReorderBuffer *rb, ReorderBufferTXN *txn)
 	memset(&hash_ctl, 0, sizeof(hash_ctl));
 	hash_ctl.keysize = sizeof(Oid);
 	hash_ctl.entrysize = sizeof(ReorderBufferToastEnt);
-	hash_ctl.hash = tag_hash;
 	hash_ctl.hcxt = rb->context;
 	txn->toast_hash = hash_create("ReorderBufferToastHash", 5, &hash_ctl,
-								  HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+								  HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 }
 
 /*
diff --git a/src/backend/storage/buffer/buf_table.c b/src/backend/storage/buffer/buf_table.c
index 7a38f2f15099520c7384725030282841ba34c83f..0750b639db603cf58d95e666275798c4d7ffd3aa 100644
--- a/src/backend/storage/buffer/buf_table.c
+++ b/src/backend/storage/buffer/buf_table.c
@@ -59,13 +59,12 @@ InitBufTable(int size)
 	/* BufferTag maps to Buffer */
 	info.keysize = sizeof(BufferTag);
 	info.entrysize = sizeof(BufferLookupEnt);
-	info.hash = tag_hash;
 	info.num_partitions = NUM_BUFFER_PARTITIONS;
 
 	SharedBufHash = ShmemInitHash("Shared Buffer Lookup Table",
 								  size, size,
 								  &info,
-								  HASH_ELEM | HASH_FUNCTION | HASH_PARTITION);
+								  HASH_ELEM | HASH_BLOBS | HASH_PARTITION);
 }
 
 /*
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 9b62aecd9178fc37d99dc735d6304d308f15aa9f..1b99e216f125b82ad96c1409a72373fc0c7bd0f6 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -2063,10 +2063,9 @@ InitBufferPoolAccess(void)
 	MemSet(&hash_ctl, 0, sizeof(hash_ctl));
 	hash_ctl.keysize = sizeof(int32);
 	hash_ctl.entrysize = sizeof(PrivateRefCountArray);
-	hash_ctl.hash = oid_hash; /* a bit more efficient than tag_hash */
 
 	PrivateRefCountHash = hash_create("PrivateRefCount", 100, &hash_ctl,
-									  HASH_ELEM | HASH_FUNCTION);
+									  HASH_ELEM | HASH_BLOBS);
 }
 
 /*
diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c
index 6c81be4237709c1603ea0c6f05eab741264700ae..3768b0212674b7b09d506edc82f98dc8a16dc5ec 100644
--- a/src/backend/storage/buffer/localbuf.c
+++ b/src/backend/storage/buffer/localbuf.c
@@ -415,12 +415,11 @@ InitLocalBuffers(void)
 	MemSet(&info, 0, sizeof(info));
 	info.keysize = sizeof(BufferTag);
 	info.entrysize = sizeof(LocalBufferLookupEnt);
-	info.hash = tag_hash;
 
 	LocalBufHash = hash_create("Local Buffer Lookup Table",
 							   nbufs,
 							   &info,
-							   HASH_ELEM | HASH_FUNCTION);
+							   HASH_ELEM | HASH_BLOBS);
 
 	if (!LocalBufHash)
 		elog(ERROR, "could not initialize local buffer hash table");
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index cbe957475641e9e48f9c032e66ef9b6fbe1fa3a6..173c9db52e2d3504ceb7f1cb2854871815891e24 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -373,7 +373,6 @@ void
 InitLocks(void)
 {
 	HASHCTL		info;
-	int			hash_flags;
 	long		init_table_size,
 				max_table_size;
 	bool		found;
@@ -392,15 +391,13 @@ InitLocks(void)
 	MemSet(&info, 0, sizeof(info));
 	info.keysize = sizeof(LOCKTAG);
 	info.entrysize = sizeof(LOCK);
-	info.hash = tag_hash;
 	info.num_partitions = NUM_LOCK_PARTITIONS;
-	hash_flags = (HASH_ELEM | HASH_FUNCTION | HASH_PARTITION);
 
 	LockMethodLockHash = ShmemInitHash("LOCK hash",
 									   init_table_size,
 									   max_table_size,
 									   &info,
-									   hash_flags);
+									HASH_ELEM | HASH_BLOBS | HASH_PARTITION);
 
 	/* Assume an average of 2 holders per lock */
 	max_table_size *= 2;
@@ -414,13 +411,12 @@ InitLocks(void)
 	info.entrysize = sizeof(PROCLOCK);
 	info.hash = proclock_hash;
 	info.num_partitions = NUM_LOCK_PARTITIONS;
-	hash_flags = (HASH_ELEM | HASH_FUNCTION | HASH_PARTITION);
 
 	LockMethodProcLockHash = ShmemInitHash("PROCLOCK hash",
 										   init_table_size,
 										   max_table_size,
 										   &info,
-										   hash_flags);
+								 HASH_ELEM | HASH_FUNCTION | HASH_PARTITION);
 
 	/*
 	 * Allocate fast-path structures.
@@ -445,13 +441,11 @@ InitLocks(void)
 
 	info.keysize = sizeof(LOCALLOCKTAG);
 	info.entrysize = sizeof(LOCALLOCK);
-	info.hash = tag_hash;
-	hash_flags = (HASH_ELEM | HASH_FUNCTION);
 
 	LockMethodLocalHash = hash_create("LOCALLOCK hash",
 									  16,
 									  &info,
-									  hash_flags);
+									  HASH_ELEM | HASH_BLOBS);
 }
 
 
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index c9f86571fd339967373987685137404d1bfe69d9..26a550f50824271f26248e4e0d4f3ce4127239a5 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -167,10 +167,9 @@ init_lwlock_stats(void)
 	MemSet(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(lwlock_stats_key);
 	ctl.entrysize = sizeof(lwlock_stats);
-	ctl.hash = tag_hash;
 	ctl.hcxt = lwlock_stats_cxt;
 	lwlock_stats_htab = hash_create("lwlock stats", 16384, &ctl,
-									HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+									HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 	if (!exit_registered)
 	{
 		on_shmem_exit(print_lwlock_stats, 0);
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index f1261181c9ee0d461e9a3710b1d6afc430e08626..e783955a40afb8f1ccc24e088d6084a581caad69 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -286,7 +286,7 @@
  * the lock partition number from the hashcode.
  */
 #define PredicateLockTargetTagHashCode(predicatelocktargettag) \
-	(tag_hash((predicatelocktargettag), sizeof(PREDICATELOCKTARGETTAG)))
+	get_hash_value(PredicateLockTargetHash, predicatelocktargettag)
 
 /*
  * Given a predicate lock tag, and the hash for its target,
@@ -1095,7 +1095,6 @@ void
 InitPredicateLocks(void)
 {
 	HASHCTL		info;
-	int			hash_flags;
 	long		max_table_size;
 	Size		requestSize;
 	bool		found;
@@ -1113,15 +1112,14 @@ InitPredicateLocks(void)
 	MemSet(&info, 0, sizeof(info));
 	info.keysize = sizeof(PREDICATELOCKTARGETTAG);
 	info.entrysize = sizeof(PREDICATELOCKTARGET);
-	info.hash = tag_hash;
 	info.num_partitions = NUM_PREDICATELOCK_PARTITIONS;
-	hash_flags = (HASH_ELEM | HASH_FUNCTION | HASH_PARTITION | HASH_FIXED_SIZE);
 
 	PredicateLockTargetHash = ShmemInitHash("PREDICATELOCKTARGET hash",
 											max_table_size,
 											max_table_size,
 											&info,
-											hash_flags);
+											HASH_ELEM | HASH_BLOBS |
+											HASH_PARTITION | HASH_FIXED_SIZE);
 
 	/* Assume an average of 2 xacts per target */
 	max_table_size *= 2;
@@ -1143,13 +1141,13 @@ InitPredicateLocks(void)
 	info.entrysize = sizeof(PREDICATELOCK);
 	info.hash = predicatelock_hash;
 	info.num_partitions = NUM_PREDICATELOCK_PARTITIONS;
-	hash_flags = (HASH_ELEM | HASH_FUNCTION | HASH_PARTITION | HASH_FIXED_SIZE);
 
 	PredicateLockHash = ShmemInitHash("PREDICATELOCK hash",
 									  max_table_size,
 									  max_table_size,
 									  &info,
-									  hash_flags);
+									  HASH_ELEM | HASH_FUNCTION |
+									  HASH_PARTITION | HASH_FIXED_SIZE);
 
 	/*
 	 * Compute size for serializable transaction hashtable. Note these
@@ -1224,14 +1222,13 @@ InitPredicateLocks(void)
 	MemSet(&info, 0, sizeof(info));
 	info.keysize = sizeof(SERIALIZABLEXIDTAG);
 	info.entrysize = sizeof(SERIALIZABLEXID);
-	info.hash = tag_hash;
-	hash_flags = (HASH_ELEM | HASH_FUNCTION | HASH_FIXED_SIZE);
 
 	SerializableXidHash = ShmemInitHash("SERIALIZABLEXID hash",
 										max_table_size,
 										max_table_size,
 										&info,
-										hash_flags);
+										HASH_ELEM | HASH_BLOBS |
+										HASH_FIXED_SIZE);
 
 	/*
 	 * Allocate space for tracking rw-conflicts in lists attached to the
@@ -1793,11 +1790,10 @@ GetSerializableTransactionSnapshotInt(Snapshot snapshot,
 	MemSet(&hash_ctl, 0, sizeof(hash_ctl));
 	hash_ctl.keysize = sizeof(PREDICATELOCKTARGETTAG);
 	hash_ctl.entrysize = sizeof(LOCALPREDICATELOCK);
-	hash_ctl.hash = tag_hash;
 	LocalPredicateLockHash = hash_create("Local predicate lock",
 										 max_predicate_locks_per_xact,
 										 &hash_ctl,
-										 HASH_ELEM | HASH_FUNCTION);
+										 HASH_ELEM | HASH_BLOBS);
 
 	return snapshot;
 }
diff --git a/src/backend/storage/smgr/md.c b/src/backend/storage/smgr/md.c
index 167d61c1afecc655860d3f49aeeb6d67fc003a62..53d9188e18b443bdf127fb3b01ca4ac568a4086c 100644
--- a/src/backend/storage/smgr/md.c
+++ b/src/backend/storage/smgr/md.c
@@ -229,12 +229,11 @@ mdinit(void)
 		MemSet(&hash_ctl, 0, sizeof(hash_ctl));
 		hash_ctl.keysize = sizeof(RelFileNode);
 		hash_ctl.entrysize = sizeof(PendingOperationEntry);
-		hash_ctl.hash = tag_hash;
 		hash_ctl.hcxt = pendingOpsCxt;
 		pendingOpsTable = hash_create("Pending Ops Table",
 									  100L,
 									  &hash_ctl,
-								   HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+									  HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 		pendingUnlinks = NIL;
 	}
 }
diff --git a/src/backend/storage/smgr/smgr.c b/src/backend/storage/smgr/smgr.c
index d16f5592983af31096eea1a3c6ba699926619145..87be47709fcc15e23cbbffe38df7413f89a34ed4 100644
--- a/src/backend/storage/smgr/smgr.c
+++ b/src/backend/storage/smgr/smgr.c
@@ -146,9 +146,8 @@ smgropen(RelFileNode rnode, BackendId backend)
 		MemSet(&ctl, 0, sizeof(ctl));
 		ctl.keysize = sizeof(RelFileNodeBackend);
 		ctl.entrysize = sizeof(SMgrRelationData);
-		ctl.hash = tag_hash;
 		SMgrRelationHash = hash_create("smgr relation table", 400,
-									   &ctl, HASH_ELEM | HASH_FUNCTION);
+									   &ctl, HASH_ELEM | HASH_BLOBS);
 		first_unowned_reln = NULL;
 	}
 
diff --git a/src/backend/utils/adt/array_typanalyze.c b/src/backend/utils/adt/array_typanalyze.c
index 4d7e9c311fb610ce36a87531c3dbe8dc03c297d3..3653888f39900e83c7e92860d4445cf4ac1ab4a6 100644
--- a/src/backend/utils/adt/array_typanalyze.c
+++ b/src/backend/utils/adt/array_typanalyze.c
@@ -290,12 +290,11 @@ compute_array_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
 	MemSet(&count_hash_ctl, 0, sizeof(count_hash_ctl));
 	count_hash_ctl.keysize = sizeof(int);
 	count_hash_ctl.entrysize = sizeof(DECountItem);
-	count_hash_ctl.hash = tag_hash;
 	count_hash_ctl.hcxt = CurrentMemoryContext;
 	count_tab = hash_create("Array distinct element count table",
 							64,
 							&count_hash_ctl,
-							HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+							HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 	/* Initialize counters. */
 	b_current = 1;
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 94bb5a47bb730aff35ddd8408b497186fbeabeab..1b6874303d57d0569a321b1750dcf9fd93583ace 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -875,9 +875,8 @@ lookup_collation_cache(Oid collation, bool set_flags)
 		memset(&ctl, 0, sizeof(ctl));
 		ctl.keysize = sizeof(Oid);
 		ctl.entrysize = sizeof(collation_cache_entry);
-		ctl.hash = oid_hash;
 		collation_cache = hash_create("Collation cache", 100, &ctl,
-									  HASH_ELEM | HASH_FUNCTION);
+									  HASH_ELEM | HASH_BLOBS);
 	}
 
 	cache_entry = hash_search(collation_cache, &collation, HASH_ENTER, &found);
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 2f0230384f42a0f3d9c2397f7b8fd2ad3070edd6..5c75390ce4911b8c2b2f899f4c41ab994446a302 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -3326,10 +3326,9 @@ ri_InitHashTables(void)
 	memset(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(Oid);
 	ctl.entrysize = sizeof(RI_ConstraintInfo);
-	ctl.hash = oid_hash;
 	ri_constraint_cache = hash_create("RI constraint cache",
 									  RI_INIT_CONSTRAINTHASHSIZE,
-									  &ctl, HASH_ELEM | HASH_FUNCTION);
+									  &ctl, HASH_ELEM | HASH_BLOBS);
 
 	/* Arrange to flush cache on pg_constraint changes */
 	CacheRegisterSyscacheCallback(CONSTROID,
@@ -3339,18 +3338,16 @@ ri_InitHashTables(void)
 	memset(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(RI_QueryKey);
 	ctl.entrysize = sizeof(RI_QueryHashEntry);
-	ctl.hash = tag_hash;
 	ri_query_cache = hash_create("RI query cache",
 								 RI_INIT_QUERYHASHSIZE,
-								 &ctl, HASH_ELEM | HASH_FUNCTION);
+								 &ctl, HASH_ELEM | HASH_BLOBS);
 
 	memset(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(RI_CompareKey);
 	ctl.entrysize = sizeof(RI_CompareHashEntry);
-	ctl.hash = tag_hash;
 	ri_compare_cache = hash_create("RI compare cache",
 								   RI_INIT_QUERYHASHSIZE,
-								   &ctl, HASH_ELEM | HASH_FUNCTION);
+								   &ctl, HASH_ELEM | HASH_BLOBS);
 }
 
 
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c
index 5fcf0dd7c75bf7d283846f7bdd24535bcfa203a2..75e7ae60caca9493c8410c175266bd3ad5e96913 100644
--- a/src/backend/utils/cache/attoptcache.c
+++ b/src/backend/utils/cache/attoptcache.c
@@ -82,10 +82,9 @@ InitializeAttoptCache(void)
 	MemSet(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(AttoptCacheKey);
 	ctl.entrysize = sizeof(AttoptCacheEntry);
-	ctl.hash = tag_hash;
 	AttoptCacheHash =
 		hash_create("Attopt cache", 256, &ctl,
-					HASH_ELEM | HASH_FUNCTION);
+					HASH_ELEM | HASH_BLOBS);
 
 	/* Make sure we've initialized CacheMemoryContext. */
 	if (!CacheMemoryContext)
diff --git a/src/backend/utils/cache/evtcache.c b/src/backend/utils/cache/evtcache.c
index b9d442cdfc93045058e6e33d25d591a9e543d21e..01051b08a0dd1fe1818ddef5f77f451a5b874cb1 100644
--- a/src/backend/utils/cache/evtcache.c
+++ b/src/backend/utils/cache/evtcache.c
@@ -123,10 +123,9 @@ BuildEventTriggerCache(void)
 	MemSet(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(EventTriggerEvent);
 	ctl.entrysize = sizeof(EventTriggerCacheEntry);
-	ctl.hash = tag_hash;
 	ctl.hcxt = EventTriggerCacheContext;
 	cache = hash_create("Event Trigger Cache", 32, &ctl,
-						HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+						HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 	/*
 	 * Prepare to scan pg_event_trigger in name order.
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 79244e5686512a0de81d8609e5cd5d7b5015503c..c2e574da4827ca57e31e0f24b1f883cd5f42bb0f 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -1409,9 +1409,8 @@ LookupOpclassInfo(Oid operatorClassOid,
 		MemSet(&ctl, 0, sizeof(ctl));
 		ctl.keysize = sizeof(Oid);
 		ctl.entrysize = sizeof(OpClassCacheEnt);
-		ctl.hash = oid_hash;
 		OpClassCache = hash_create("Operator class cache", 64,
-								   &ctl, HASH_ELEM | HASH_FUNCTION);
+								   &ctl, HASH_ELEM | HASH_BLOBS);
 
 		/* Also make sure CacheMemoryContext exists */
 		if (!CacheMemoryContext)
@@ -3140,9 +3139,8 @@ RelationCacheInitialize(void)
 	MemSet(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(Oid);
 	ctl.entrysize = sizeof(RelIdCacheEnt);
-	ctl.hash = oid_hash;
 	RelationIdCache = hash_create("Relcache by OID", INITRELCACHESIZE,
-								  &ctl, HASH_ELEM | HASH_FUNCTION);
+								  &ctl, HASH_ELEM | HASH_BLOBS);
 
 	/*
 	 * relation mapper needs to be initialized too
diff --git a/src/backend/utils/cache/relfilenodemap.c b/src/backend/utils/cache/relfilenodemap.c
index 1e8429c64c335c9607c164d91e829439f1f1006a..87813fa52cb4d16a01c2b467f36cc4ebf3a4e358 100644
--- a/src/backend/utils/cache/relfilenodemap.c
+++ b/src/backend/utils/cache/relfilenodemap.c
@@ -115,7 +115,6 @@ InitializeRelfilenodeMap(void)
 	MemSet(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(RelfilenodeMapKey);
 	ctl.entrysize = sizeof(RelfilenodeMapEntry);
-	ctl.hash = tag_hash;
 	ctl.hcxt = CacheMemoryContext;
 
 	/*
@@ -125,7 +124,7 @@ InitializeRelfilenodeMap(void)
 	 */
 	RelfilenodeMapHash =
 		hash_create("RelfilenodeMap cache", 1024, &ctl,
-					HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+					HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 	/* Watch for invalidation events. */
 	CacheRegisterRelcacheCallback(RelfilenodeMapInvalidateCallback,
diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c
index 24e8679e2547c95c4b55b5530383b503e28a52f3..6cbafaf691987bbe71ca47c60a5e43a334172dcf 100644
--- a/src/backend/utils/cache/spccache.c
+++ b/src/backend/utils/cache/spccache.c
@@ -81,10 +81,9 @@ InitializeTableSpaceCache(void)
 	MemSet(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(Oid);
 	ctl.entrysize = sizeof(TableSpaceCacheEntry);
-	ctl.hash = oid_hash;
 	TableSpaceCacheHash =
 		hash_create("TableSpace cache", 16, &ctl,
-					HASH_ELEM | HASH_FUNCTION);
+					HASH_ELEM | HASH_BLOBS);
 
 	/* Make sure we've initialized CacheMemoryContext. */
 	if (!CacheMemoryContext)
diff --git a/src/backend/utils/cache/ts_cache.c b/src/backend/utils/cache/ts_cache.c
index 5ff1461f7d818de3643fbadd13312ed76c8d2e59..46da30357bb5e238d7dcf923c1e9600eba702916 100644
--- a/src/backend/utils/cache/ts_cache.c
+++ b/src/backend/utils/cache/ts_cache.c
@@ -120,9 +120,8 @@ lookup_ts_parser_cache(Oid prsId)
 		MemSet(&ctl, 0, sizeof(ctl));
 		ctl.keysize = sizeof(Oid);
 		ctl.entrysize = sizeof(TSParserCacheEntry);
-		ctl.hash = oid_hash;
 		TSParserCacheHash = hash_create("Tsearch parser cache", 4,
-										&ctl, HASH_ELEM | HASH_FUNCTION);
+										&ctl, HASH_ELEM | HASH_BLOBS);
 		/* Flush cache on pg_ts_parser changes */
 		CacheRegisterSyscacheCallback(TSPARSEROID, InvalidateTSCacheCallBack,
 									  PointerGetDatum(TSParserCacheHash));
@@ -219,9 +218,8 @@ lookup_ts_dictionary_cache(Oid dictId)
 		MemSet(&ctl, 0, sizeof(ctl));
 		ctl.keysize = sizeof(Oid);
 		ctl.entrysize = sizeof(TSDictionaryCacheEntry);
-		ctl.hash = oid_hash;
 		TSDictionaryCacheHash = hash_create("Tsearch dictionary cache", 8,
-											&ctl, HASH_ELEM | HASH_FUNCTION);
+											&ctl, HASH_ELEM | HASH_BLOBS);
 		/* Flush cache on pg_ts_dict and pg_ts_template changes */
 		CacheRegisterSyscacheCallback(TSDICTOID, InvalidateTSCacheCallBack,
 									  PointerGetDatum(TSDictionaryCacheHash));
@@ -368,9 +366,8 @@ init_ts_config_cache(void)
 	MemSet(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(Oid);
 	ctl.entrysize = sizeof(TSConfigCacheEntry);
-	ctl.hash = oid_hash;
 	TSConfigCacheHash = hash_create("Tsearch configuration cache", 16,
-									&ctl, HASH_ELEM | HASH_FUNCTION);
+									&ctl, HASH_ELEM | HASH_BLOBS);
 	/* Flush cache on pg_ts_config and pg_ts_config_map changes */
 	CacheRegisterSyscacheCallback(TSCONFIGOID, InvalidateTSCacheCallBack,
 								  PointerGetDatum(TSConfigCacheHash));
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 29aec3cf743c9fdc131484a0faf262e9ebbf3341..08e17b92d22db0f2726e782880e8b96fccf24db5 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -166,9 +166,8 @@ lookup_type_cache(Oid type_id, int flags)
 		MemSet(&ctl, 0, sizeof(ctl));
 		ctl.keysize = sizeof(Oid);
 		ctl.entrysize = sizeof(TypeCacheEntry);
-		ctl.hash = oid_hash;
 		TypeCacheHash = hash_create("Type information cache", 64,
-									&ctl, HASH_ELEM | HASH_FUNCTION);
+									&ctl, HASH_ELEM | HASH_BLOBS);
 
 		/* Also set up callbacks for SI invalidations */
 		CacheRegisterRelcacheCallback(TypeCacheRelCallback, (Datum) 0);
@@ -846,9 +845,8 @@ assign_record_type_typmod(TupleDesc tupDesc)
 		MemSet(&ctl, 0, sizeof(ctl));
 		ctl.keysize = REC_HASH_KEYS * sizeof(Oid);
 		ctl.entrysize = sizeof(RecordCacheEntry);
-		ctl.hash = tag_hash;
 		RecordCacheHash = hash_create("Record information cache", 64,
-									  &ctl, HASH_ELEM | HASH_FUNCTION);
+									  &ctl, HASH_ELEM | HASH_BLOBS);
 
 		/* Also make sure CacheMemoryContext exists */
 		if (!CacheMemoryContext)
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index a95be056dae434c75caee1499f040dd9c15cdf2e..e81c855315239d024beb965c036e6961983367e7 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -540,11 +540,10 @@ record_C_func(HeapTuple procedureTuple,
 		MemSet(&hash_ctl, 0, sizeof(hash_ctl));
 		hash_ctl.keysize = sizeof(Oid);
 		hash_ctl.entrysize = sizeof(CFuncHashTabEntry);
-		hash_ctl.hash = oid_hash;
 		CFuncHash = hash_create("CFuncHash",
 								100,
 								&hash_ctl,
-								HASH_ELEM | HASH_FUNCTION);
+								HASH_ELEM | HASH_BLOBS);
 	}
 
 	entry = (CFuncHashTabEntry *)
diff --git a/src/backend/utils/hash/dynahash.c b/src/backend/utils/hash/dynahash.c
index 2b99e4b5218c0319d653f4124f0872462aa4ca7c..5f6d6cbbde5ac215df8a216b0f80d273d7de18cd 100644
--- a/src/backend/utils/hash/dynahash.c
+++ b/src/backend/utils/hash/dynahash.c
@@ -26,6 +26,20 @@
  * in local memory, we typically use palloc() which will throw error on
  * failure.  The code in this file has to cope with both cases.
  *
+ * dynahash.c provides support for these types of lookup keys:
+ *
+ * 1. Null-terminated C strings (truncated if necessary to fit in keysize),
+ * compared as though by strcmp().  This is the default behavior.
+ *
+ * 2. Arbitrary binary data of size keysize, compared as though by memcmp().
+ * (Caller must ensure there are no undefined padding bits in the keys!)
+ * This is selected by specifying HASH_BLOBS flag to hash_create.
+ *
+ * 3. More complex key behavior can be selected by specifying user-supplied
+ * hashing, comparison, and/or key-copying functions.  At least a hashing
+ * function must be supplied; comparison defaults to memcmp() and key copying
+ * to memcpy() when a user-defined hashing function is selected.
+ *
  * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
@@ -305,15 +319,32 @@ hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
 	hashp->tabname = (char *) (hashp + 1);
 	strcpy(hashp->tabname, tabname);
 
+	/*
+	 * Select the appropriate hash function (see comments at head of file).
+	 */
 	if (flags & HASH_FUNCTION)
 		hashp->hash = info->hash;
+	else if (flags & HASH_BLOBS)
+	{
+		/* We can optimize hashing for common key sizes */
+		Assert(flags & HASH_ELEM);
+		if (info->keysize == sizeof(uint32))
+			hashp->hash = uint32_hash;
+		else
+			hashp->hash = tag_hash;
+	}
 	else
 		hashp->hash = string_hash;		/* default hash function */
 
 	/*
 	 * If you don't specify a match function, it defaults to string_compare if
 	 * you used string_hash (either explicitly or by default) and to memcmp
-	 * otherwise.  (Prior to PostgreSQL 7.4, memcmp was always used.)
+	 * otherwise.
+	 *
+	 * Note: explicitly specifying string_hash is deprecated, because this
+	 * might not work for callers in loadable modules on some platforms due to
+	 * referencing a trampoline instead of the string_hash function proper.
+	 * Just let it default, eh?
 	 */
 	if (flags & HASH_COMPARE)
 		hashp->match = info->match;
@@ -332,6 +363,7 @@ hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
 	else
 		hashp->keycopy = memcpy;
 
+	/* And select the entry allocation function, too. */
 	if (flags & HASH_ALLOC)
 		hashp->alloc = info->alloc;
 	else
diff --git a/src/backend/utils/hash/hashfn.c b/src/backend/utils/hash/hashfn.c
index a12f98f2f2584be961b3c0880492c8e4523bba58..adb0bd9564cdbb42aa89eef0fcc389c3267446ee 100644
--- a/src/backend/utils/hash/hashfn.c
+++ b/src/backend/utils/hash/hashfn.c
@@ -55,15 +55,15 @@ tag_hash(const void *key, Size keysize)
 }
 
 /*
- * oid_hash: hash function for keys that are OIDs
+ * uint32_hash: hash function for keys that are uint32 or int32
  *
  * (tag_hash works for this case too, but is slower)
  */
 uint32
-oid_hash(const void *key, Size keysize)
+uint32_hash(const void *key, Size keysize)
 {
-	Assert(keysize == sizeof(Oid));
-	return DatumGetUInt32(hash_uint32((uint32) *((const Oid *) key)));
+	Assert(keysize == sizeof(uint32));
+	return DatumGetUInt32(hash_uint32(*((const uint32 *) key)));
 }
 
 /*
diff --git a/src/backend/utils/time/combocid.c b/src/backend/utils/time/combocid.c
index a70c7542c92d624114beeae6499a5db2abb7bb2d..ea7a905db4a42fa3b418c1337cdd223f37faa6f8 100644
--- a/src/backend/utils/time/combocid.c
+++ b/src/backend/utils/time/combocid.c
@@ -218,13 +218,12 @@ GetComboCommandId(CommandId cmin, CommandId cmax)
 		memset(&hash_ctl, 0, sizeof(hash_ctl));
 		hash_ctl.keysize = sizeof(ComboCidKeyData);
 		hash_ctl.entrysize = sizeof(ComboCidEntryData);
-		hash_ctl.hash = tag_hash;
 		hash_ctl.hcxt = TopTransactionContext;
 
 		comboHash = hash_create("Combo CIDs",
 								CCID_HASH_SIZE,
 								&hash_ctl,
-								HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
+								HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
 		comboCids = (ComboCidKeyData *)
 			MemoryContextAlloc(TopTransactionContext,
diff --git a/src/include/utils/hsearch.h b/src/include/utils/hsearch.h
index 77974a193b2798d0d3c5f215dcaef35857db8899..dfdf658ddf0dcacbca98885073798eb4be0da03b 100644
--- a/src/include/utils/hsearch.h
+++ b/src/include/utils/hsearch.h
@@ -31,7 +31,7 @@ typedef int (*HashCompareFunc) (const void *key1, const void *key2,
 
 /*
  * Key copying functions must have this signature.  The return value is not
- * used.  (The definition is set up to allow memcpy() and strncpy() to be
+ * used.  (The definition is set up to allow memcpy() and strlcpy() to be
  * used directly.)
  */
 typedef void *(*HashCopyFunc) (void *dest, const void *src, Size keysize);
@@ -80,19 +80,20 @@ typedef struct HASHCTL
 } HASHCTL;
 
 /* Flags to indicate which parameters are supplied */
-#define HASH_PARTITION	0x001	/* Hashtable is used w/partitioned locking */
-#define HASH_SEGMENT	0x002	/* Set segment size */
-#define HASH_DIRSIZE	0x004	/* Set directory size (initial and max) */
-#define HASH_FFACTOR	0x008	/* Set fill factor */
-#define HASH_FUNCTION	0x010	/* Set user defined hash function */
-#define HASH_ELEM		0x020	/* Set keysize and entrysize */
-#define HASH_SHARED_MEM 0x040	/* Hashtable is in shared memory */
-#define HASH_ATTACH		0x080	/* Do not initialize hctl */
-#define HASH_ALLOC		0x100	/* Set memory allocator */
-#define HASH_CONTEXT	0x200	/* Set memory allocation context */
-#define HASH_COMPARE	0x400	/* Set user defined comparison function */
-#define HASH_KEYCOPY	0x800	/* Set user defined key-copying function */
-#define HASH_FIXED_SIZE 0x1000	/* Initial size is a hard limit */
+#define HASH_PARTITION	0x0001	/* Hashtable is used w/partitioned locking */
+#define HASH_SEGMENT	0x0002	/* Set segment size */
+#define HASH_DIRSIZE	0x0004	/* Set directory size (initial and max) */
+#define HASH_FFACTOR	0x0008	/* Set fill factor */
+#define HASH_ELEM		0x0010	/* Set keysize and entrysize */
+#define HASH_BLOBS		0x0020	/* Select support functions for binary keys */
+#define HASH_FUNCTION	0x0040	/* Set user defined hash function */
+#define HASH_COMPARE	0x0080	/* Set user defined comparison function */
+#define HASH_KEYCOPY	0x0100	/* Set user defined key-copying function */
+#define HASH_ALLOC		0x0200	/* Set memory allocator */
+#define HASH_CONTEXT	0x0400	/* Set memory allocation context */
+#define HASH_SHARED_MEM 0x0800	/* Hashtable is in shared memory */
+#define HASH_ATTACH		0x1000	/* Do not initialize hctl */
+#define HASH_FIXED_SIZE 0x2000	/* Initial size is a hard limit */
 
 
 /* max_dsize value to indicate expansible directory */
@@ -143,11 +144,17 @@ extern void AtEOSubXact_HashTables(bool isCommit, int nestDepth);
 
 /*
  * prototypes for functions in hashfn.c
+ *
+ * Note: It is deprecated for callers of hash_create to explicitly specify
+ * string_hash, tag_hash, uint32_hash, or oid_hash.  Just set HASH_BLOBS or
+ * not.  Use HASH_FUNCTION only when you want something other than those.
  */
 extern uint32 string_hash(const void *key, Size keysize);
 extern uint32 tag_hash(const void *key, Size keysize);
-extern uint32 oid_hash(const void *key, Size keysize);
+extern uint32 uint32_hash(const void *key, Size keysize);
 extern uint32 bitmap_hash(const void *key, Size keysize);
 extern int	bitmap_match(const void *key1, const void *key2, Size keysize);
 
+#define oid_hash uint32_hash	/* Remove me eventually */
+
 #endif   /* HSEARCH_H */
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 56295715a18b56fdf8281fda8d6a9252ac994c63..492c1ef4d24b93a47fc01396e94f3ab6f30a3f42 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -456,20 +456,18 @@ _PG_init(void)
 	memset(&hash_ctl, 0, sizeof(hash_ctl));
 	hash_ctl.keysize = sizeof(Oid);
 	hash_ctl.entrysize = sizeof(plperl_interp_desc);
-	hash_ctl.hash = oid_hash;
 	plperl_interp_hash = hash_create("PL/Perl interpreters",
 									 8,
 									 &hash_ctl,
-									 HASH_ELEM | HASH_FUNCTION);
+									 HASH_ELEM | HASH_BLOBS);
 
 	memset(&hash_ctl, 0, sizeof(hash_ctl));
 	hash_ctl.keysize = sizeof(plperl_proc_key);
 	hash_ctl.entrysize = sizeof(plperl_proc_ptr);
-	hash_ctl.hash = tag_hash;
 	plperl_proc_hash = hash_create("PL/Perl procedures",
 								   32,
 								   &hash_ctl,
-								   HASH_ELEM | HASH_FUNCTION);
+								   HASH_ELEM | HASH_BLOBS);
 
 	/*
 	 * Save the default opmask.
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index d2fd685c35dc18a8c59408674d94b18cfc9fd725..24dc371d62316cc3b661f2a5368e9ef750a98241 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -2519,11 +2519,10 @@ plpgsql_HashTableInit(void)
 	memset(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(PLpgSQL_func_hashkey);
 	ctl.entrysize = sizeof(plpgsql_HashEnt);
-	ctl.hash = tag_hash;
 	plpgsql_HashTable = hash_create("PLpgSQL function cache",
 									FUNCS_PER_USER,
 									&ctl,
-									HASH_ELEM | HASH_FUNCTION);
+									HASH_ELEM | HASH_BLOBS);
 }
 
 static PLpgSQL_function *
diff --git a/src/pl/plpython/plpy_plpymodule.c b/src/pl/plpython/plpy_plpymodule.c
index 37ea2a490d963eecdbebd07889ad9a4e0aa0f7c1..b8dc891e3730795eb8add6704d6a83d58002277e 100644
--- a/src/pl/plpython/plpy_plpymodule.c
+++ b/src/pl/plpython/plpy_plpymodule.c
@@ -226,9 +226,8 @@ PLy_add_exceptions(PyObject *plpy)
 	memset(&hash_ctl, 0, sizeof(hash_ctl));
 	hash_ctl.keysize = sizeof(int);
 	hash_ctl.entrysize = sizeof(PLyExceptionEntry);
-	hash_ctl.hash = tag_hash;
 	PLy_spi_exceptions = hash_create("SPI exceptions", 256,
-									 &hash_ctl, HASH_ELEM | HASH_FUNCTION);
+									 &hash_ctl, HASH_ELEM | HASH_BLOBS);
 
 	PLy_generate_spi_exceptions(excmod, PLy_exc_spi_error);
 }
diff --git a/src/pl/plpython/plpy_procedure.c b/src/pl/plpython/plpy_procedure.c
index fad80b242e75a9a11a2e8214d6b08695bfc30216..bd483012d35a73e52343a0597262f3695e007b68 100644
--- a/src/pl/plpython/plpy_procedure.c
+++ b/src/pl/plpython/plpy_procedure.c
@@ -39,9 +39,8 @@ init_procedure_caches(void)
 	memset(&hash_ctl, 0, sizeof(hash_ctl));
 	hash_ctl.keysize = sizeof(PLyProcedureKey);
 	hash_ctl.entrysize = sizeof(PLyProcedureEntry);
-	hash_ctl.hash = tag_hash;
 	PLy_procedure_cache = hash_create("PL/Python procedures", 32, &hash_ctl,
-									  HASH_ELEM | HASH_FUNCTION);
+									  HASH_ELEM | HASH_BLOBS);
 }
 
 /*
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index a53cca4f27518082a9474f5831dbf464ab6e312d..8f9804696e5bd1f2bd1f040aa24b1f3b8846e6a9 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -378,11 +378,10 @@ _PG_init(void)
 	memset(&hash_ctl, 0, sizeof(hash_ctl));
 	hash_ctl.keysize = sizeof(Oid);
 	hash_ctl.entrysize = sizeof(pltcl_interp_desc);
-	hash_ctl.hash = oid_hash;
 	pltcl_interp_htab = hash_create("PL/Tcl interpreters",
 									8,
 									&hash_ctl,
-									HASH_ELEM | HASH_FUNCTION);
+									HASH_ELEM | HASH_BLOBS);
 
 	/************************************************************
 	 * Create the hash table for function lookup
@@ -390,11 +389,10 @@ _PG_init(void)
 	memset(&hash_ctl, 0, sizeof(hash_ctl));
 	hash_ctl.keysize = sizeof(pltcl_proc_key);
 	hash_ctl.entrysize = sizeof(pltcl_proc_ptr);
-	hash_ctl.hash = tag_hash;
 	pltcl_proc_htab = hash_create("PL/Tcl functions",
 								  100,
 								  &hash_ctl,
-								  HASH_ELEM | HASH_FUNCTION);
+								  HASH_ELEM | HASH_BLOBS);
 
 	pltcl_pm_init_done = true;
 }