From 300c0b356baeb02766eb7d33b477927648b65fb1 Mon Sep 17 00:00:00 2001
From: Amul Sul <sulamul@gmail.com>
Date: Fri, 18 Aug 2017 15:28:26 +0530
Subject: [PATCH 1/2] add-optional-second-hash-proc-v3

v3:
	Updated w.r.t Robert suggestion in
	message-id : CA%2BTgmoYPvoTMGJYkVBA%3D2j1o1wpZ-WZCYiS7_B-AqqZBkWT4HQ%40mail.gmail.com

v2:
	Extended remaining hash function.

v1:
	Patch by Robert Haas.
---
 doc/src/sgml/xindex.sgml                    |  11 +-
 src/backend/access/hash/hashfunc.c          | 372 +++++++++++++++++++++++++++-
 src/backend/access/hash/hashpage.c          |   2 +-
 src/backend/access/hash/hashutil.c          |   6 +-
 src/backend/access/hash/hashvalidate.c      |  42 +++-
 src/backend/commands/opclasscmds.c          |  34 ++-
 src/backend/utils/adt/acl.c                 |  15 ++
 src/backend/utils/adt/arrayfuncs.c          |  79 ++++++
 src/backend/utils/adt/date.c                |  21 ++
 src/backend/utils/adt/jsonb_op.c            |  42 ++++
 src/backend/utils/adt/jsonb_util.c          |  40 +++
 src/backend/utils/adt/mac.c                 |   9 +
 src/backend/utils/adt/mac8.c                |   9 +
 src/backend/utils/adt/network.c             |  10 +
 src/backend/utils/adt/numeric.c             |  59 +++++
 src/backend/utils/adt/pg_lsn.c              |   6 +
 src/backend/utils/adt/rangetypes.c          |  63 +++++
 src/backend/utils/adt/timestamp.c           |  19 ++
 src/backend/utils/adt/uuid.c                |   8 +
 src/backend/utils/adt/varchar.c             |  18 ++
 src/backend/utils/cache/lsyscache.c         |   8 +-
 src/backend/utils/cache/typcache.c          |  58 ++++-
 src/include/access/hash.h                   |  17 +-
 src/include/catalog/pg_amproc.h             |  36 +++
 src/include/catalog/pg_proc.h               |  54 ++++
 src/include/utils/jsonb.h                   |   2 +
 src/include/utils/typcache.h                |   4 +
 src/test/regress/expected/alter_generic.out |   4 +-
 28 files changed, 1008 insertions(+), 40 deletions(-)

diff --git a/doc/src/sgml/xindex.sgml b/doc/src/sgml/xindex.sgml
index 333a36c..0f3c46b 100644
--- a/doc/src/sgml/xindex.sgml
+++ b/doc/src/sgml/xindex.sgml
@@ -436,7 +436,8 @@
    </table>
 
   <para>
-   Hash indexes require one support function, shown in <xref
+   Hash indexes require one support function, and allow a second one to be
+   supplied at the operator class author's option, as shown in <xref
    linkend="xindex-hash-support-table">.
   </para>
 
@@ -451,9 +452,15 @@
      </thead>
      <tbody>
       <row>
-       <entry>Compute the hash value for a key</entry>
+       <entry>Compute the 32-bit hash value for a key</entry>
        <entry>1</entry>
       </row>
+      <row>
+       <entry>
+         Compute the 64-bit hash value for a key given a 64-bit salt
+       </entry>
+       <entry>2</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c
index a127f3f..998f9fe 100644
--- a/src/backend/access/hash/hashfunc.c
+++ b/src/backend/access/hash/hashfunc.c
@@ -47,18 +47,36 @@ hashchar(PG_FUNCTION_ARGS)
 }
 
 Datum
+hashcharextended(PG_FUNCTION_ARGS)
+{
+	return hash_uint32_extended((int32) PG_GETARG_CHAR(0), PG_GETARG_INT64(1));
+}
+
+Datum
 hashint2(PG_FUNCTION_ARGS)
 {
 	return hash_uint32((int32) PG_GETARG_INT16(0));
 }
 
 Datum
+hashint2extended(PG_FUNCTION_ARGS)
+{
+	return hash_uint32_extended((int32) PG_GETARG_INT16(0), PG_GETARG_INT64(1));
+}
+
+Datum
 hashint4(PG_FUNCTION_ARGS)
 {
 	return hash_uint32(PG_GETARG_INT32(0));
 }
 
 Datum
+hashint4extended(PG_FUNCTION_ARGS)
+{
+	return hash_uint32_extended(PG_GETARG_INT32(0), PG_GETARG_INT64(1));
+}
+
+Datum
 hashint8(PG_FUNCTION_ARGS)
 {
 	/*
@@ -79,18 +97,43 @@ hashint8(PG_FUNCTION_ARGS)
 }
 
 Datum
+hashint8extended(PG_FUNCTION_ARGS)
+{
+	/* Same approach as hashint8 */
+	int64		val = PG_GETARG_INT64(0);
+	uint32		lohalf = (uint32) val;
+	uint32		hihalf = (uint32) (val >> 32);
+
+	lohalf ^= (val >= 0) ? hihalf : ~hihalf;
+
+	return hash_uint32_extended(lohalf, PG_GETARG_INT64(1));
+}
+
+Datum
 hashoid(PG_FUNCTION_ARGS)
 {
 	return hash_uint32((uint32) PG_GETARG_OID(0));
 }
 
 Datum
+hashoidextended(PG_FUNCTION_ARGS)
+{
+	return hash_uint32_extended((uint32) PG_GETARG_OID(0), PG_GETARG_INT64(1));
+}
+
+Datum
 hashenum(PG_FUNCTION_ARGS)
 {
 	return hash_uint32((uint32) PG_GETARG_OID(0));
 }
 
 Datum
+hashenumextended(PG_FUNCTION_ARGS)
+{
+	return hash_uint32_extended((uint32) PG_GETARG_OID(0), PG_GETARG_INT64(1));
+}
+
+Datum
 hashfloat4(PG_FUNCTION_ARGS)
 {
 	float4		key = PG_GETARG_FLOAT4(0);
@@ -117,6 +160,21 @@ hashfloat4(PG_FUNCTION_ARGS)
 }
 
 Datum
+hashfloat4extended(PG_FUNCTION_ARGS)
+{
+	float4		key = PG_GETARG_FLOAT4(0);
+	float8		key8;
+
+	/* Same approach as hashfloat4 */
+	if (key == (float4) 0)
+		return UInt64GetDatum(0);
+	key8 = key;
+
+	return hash_any_extended((unsigned char *) &key8, sizeof(key8),
+							 PG_GETARG_INT64(1));
+}
+
+Datum
 hashfloat8(PG_FUNCTION_ARGS)
 {
 	float8		key = PG_GETARG_FLOAT8(0);
@@ -133,6 +191,19 @@ hashfloat8(PG_FUNCTION_ARGS)
 }
 
 Datum
+hashfloat8extended(PG_FUNCTION_ARGS)
+{
+	float8		key = PG_GETARG_FLOAT8(0);
+
+	/* Same approach as hashfloat8 */
+	if (key == (float8) 0)
+		return UInt64GetDatum(0);
+
+	return hash_any_extended((unsigned char *) &key, sizeof(key),
+							 PG_GETARG_INT64(1));
+}
+
+Datum
 hashoidvector(PG_FUNCTION_ARGS)
 {
 	oidvector  *key = (oidvector *) PG_GETARG_POINTER(0);
@@ -141,6 +212,16 @@ hashoidvector(PG_FUNCTION_ARGS)
 }
 
 Datum
+hashoidvectorextended(PG_FUNCTION_ARGS)
+{
+	oidvector  *key = (oidvector *) PG_GETARG_POINTER(0);
+
+	return hash_any_extended((unsigned char *) key->values,
+							 key->dim1 * sizeof(Oid),
+							 PG_GETARG_INT64(1));
+}
+
+Datum
 hashname(PG_FUNCTION_ARGS)
 {
 	char	   *key = NameStr(*PG_GETARG_NAME(0));
@@ -149,6 +230,15 @@ hashname(PG_FUNCTION_ARGS)
 }
 
 Datum
+hashnameextended(PG_FUNCTION_ARGS)
+{
+	char	   *key = NameStr(*PG_GETARG_NAME(0));
+
+	return hash_any_extended((unsigned char *) key, strlen(key),
+							 PG_GETARG_INT64(1));
+}
+
+Datum
 hashtext(PG_FUNCTION_ARGS)
 {
 	text	   *key = PG_GETARG_TEXT_PP(0);
@@ -168,6 +258,22 @@ hashtext(PG_FUNCTION_ARGS)
 	return result;
 }
 
+Datum
+hashtextextended(PG_FUNCTION_ARGS)
+{
+	text	   *key = PG_GETARG_TEXT_PP(0);
+	Datum		result;
+
+	/* Same approach as hashtext */
+	result = hash_any_extended((unsigned char *) VARDATA_ANY(key),
+							   VARSIZE_ANY_EXHDR(key),
+							   PG_GETARG_INT64(1));
+
+	PG_FREE_IF_COPY(key, 0);
+
+	return result;
+}
+
 /*
  * hashvarlena() can be used for any varlena datatype in which there are
  * no non-significant bits, ie, distinct bitpatterns never compare as equal.
@@ -187,6 +293,21 @@ hashvarlena(PG_FUNCTION_ARGS)
 	return result;
 }
 
+Datum
+hashvarlenaextended(PG_FUNCTION_ARGS)
+{
+	struct varlena *key = PG_GETARG_VARLENA_PP(0);
+	Datum		result;
+
+	result = hash_any_extended((unsigned char *) VARDATA_ANY(key),
+							   VARSIZE_ANY_EXHDR(key),
+							   PG_GETARG_INT64(1));
+
+	PG_FREE_IF_COPY(key, 0);
+
+	return result;
+}
+
 /*
  * This hash function was written by Bob Jenkins
  * (bob_jenkins@burtleburtle.net), and superficially adapted
@@ -502,7 +623,227 @@ hash_any(register const unsigned char *k, register int keylen)
 }
 
 /*
- * hash_uint32() -- hash a 32-bit value
+ * hash_any_extended() -- hash into a 64-bit value, using an optional seed
+ *		k		: the key (the unaligned variable-length array of bytes)
+ *		len		: the length of the key, counting by bytes
+ *		seed	: a 64-bit seed (0 means no seed)
+ *
+ * Returns a uint64 value.  Otherwise similar to hash_any.
+ */
+Datum
+hash_any_extended(register const unsigned char *k, register int keylen,
+				  uint64 seed)
+{
+	register uint32 a,
+				b,
+				c,
+				len;
+
+	/* Set up the internal state */
+	len = keylen;
+	a = b = c = 0x9e3779b9 + len + 3923095;
+
+	/* If the seed is non-zero, use it to perturb the internal state. */
+	if (seed != 0)
+	{
+		/*
+		 * In essence, the seed is treated as part of the data being hashed,
+		 * but for simplicity, we pretend that it's padded with four bytes of
+		 * zeroes so that the seed constitutes a 4-byte chunk.
+		 */
+		a += (uint32) (seed >> 32);
+		b += (uint32) seed;
+		mix(a, b, c);
+	}
+
+	/* If the source pointer is word-aligned, we use word-wide fetches */
+	if (((uintptr_t) k & UINT32_ALIGN_MASK) == 0)
+	{
+		/* Code path for aligned source data */
+		register const uint32 *ka = (const uint32 *) k;
+
+		/* handle most of the key */
+		while (len >= 12)
+		{
+			a += ka[0];
+			b += ka[1];
+			c += ka[2];
+			mix(a, b, c);
+			ka += 3;
+			len -= 12;
+		}
+
+		/* handle the last 11 bytes */
+		k = (const unsigned char *) ka;
+#ifdef WORDS_BIGENDIAN
+		switch (len)
+		{
+			case 11:
+				c += ((uint32) k[10] << 8);
+				/* fall through */
+			case 10:
+				c += ((uint32) k[9] << 16);
+				/* fall through */
+			case 9:
+				c += ((uint32) k[8] << 24);
+				/* the lowest byte of c is reserved for the length */
+				/* fall through */
+			case 8:
+				b += ka[1];
+				a += ka[0];
+				break;
+			case 7:
+				b += ((uint32) k[6] << 8);
+				/* fall through */
+			case 6:
+				b += ((uint32) k[5] << 16);
+				/* fall through */
+			case 5:
+				b += ((uint32) k[4] << 24);
+				/* fall through */
+			case 4:
+				a += ka[0];
+				break;
+			case 3:
+				a += ((uint32) k[2] << 8);
+				/* fall through */
+			case 2:
+				a += ((uint32) k[1] << 16);
+				/* fall through */
+			case 1:
+				a += ((uint32) k[0] << 24);
+				/* case 0: nothing left to add */
+		}
+#else							/* !WORDS_BIGENDIAN */
+		switch (len)
+		{
+			case 11:
+				c += ((uint32) k[10] << 24);
+				/* fall through */
+			case 10:
+				c += ((uint32) k[9] << 16);
+				/* fall through */
+			case 9:
+				c += ((uint32) k[8] << 8);
+				/* the lowest byte of c is reserved for the length */
+				/* fall through */
+			case 8:
+				b += ka[1];
+				a += ka[0];
+				break;
+			case 7:
+				b += ((uint32) k[6] << 16);
+				/* fall through */
+			case 6:
+				b += ((uint32) k[5] << 8);
+				/* fall through */
+			case 5:
+				b += k[4];
+				/* fall through */
+			case 4:
+				a += ka[0];
+				break;
+			case 3:
+				a += ((uint32) k[2] << 16);
+				/* fall through */
+			case 2:
+				a += ((uint32) k[1] << 8);
+				/* fall through */
+			case 1:
+				a += k[0];
+				/* case 0: nothing left to add */
+		}
+#endif							/* WORDS_BIGENDIAN */
+	}
+	else
+	{
+		/* Code path for non-aligned source data */
+
+		/* handle most of the key */
+		while (len >= 12)
+		{
+#ifdef WORDS_BIGENDIAN
+			a += (k[3] + ((uint32) k[2] << 8) + ((uint32) k[1] << 16) + ((uint32) k[0] << 24));
+			b += (k[7] + ((uint32) k[6] << 8) + ((uint32) k[5] << 16) + ((uint32) k[4] << 24));
+			c += (k[11] + ((uint32) k[10] << 8) + ((uint32) k[9] << 16) + ((uint32) k[8] << 24));
+#else							/* !WORDS_BIGENDIAN */
+			a += (k[0] + ((uint32) k[1] << 8) + ((uint32) k[2] << 16) + ((uint32) k[3] << 24));
+			b += (k[4] + ((uint32) k[5] << 8) + ((uint32) k[6] << 16) + ((uint32) k[7] << 24));
+			c += (k[8] + ((uint32) k[9] << 8) + ((uint32) k[10] << 16) + ((uint32) k[11] << 24));
+#endif							/* WORDS_BIGENDIAN */
+			mix(a, b, c);
+			k += 12;
+			len -= 12;
+		}
+
+		/* handle the last 11 bytes */
+#ifdef WORDS_BIGENDIAN
+		switch (len)			/* all the case statements fall through */
+		{
+			case 11:
+				c += ((uint32) k[10] << 8);
+			case 10:
+				c += ((uint32) k[9] << 16);
+			case 9:
+				c += ((uint32) k[8] << 24);
+				/* the lowest byte of c is reserved for the length */
+			case 8:
+				b += k[7];
+			case 7:
+				b += ((uint32) k[6] << 8);
+			case 6:
+				b += ((uint32) k[5] << 16);
+			case 5:
+				b += ((uint32) k[4] << 24);
+			case 4:
+				a += k[3];
+			case 3:
+				a += ((uint32) k[2] << 8);
+			case 2:
+				a += ((uint32) k[1] << 16);
+			case 1:
+				a += ((uint32) k[0] << 24);
+				/* case 0: nothing left to add */
+		}
+#else							/* !WORDS_BIGENDIAN */
+		switch (len)			/* all the case statements fall through */
+		{
+			case 11:
+				c += ((uint32) k[10] << 24);
+			case 10:
+				c += ((uint32) k[9] << 16);
+			case 9:
+				c += ((uint32) k[8] << 8);
+				/* the lowest byte of c is reserved for the length */
+			case 8:
+				b += ((uint32) k[7] << 24);
+			case 7:
+				b += ((uint32) k[6] << 16);
+			case 6:
+				b += ((uint32) k[5] << 8);
+			case 5:
+				b += k[4];
+			case 4:
+				a += ((uint32) k[3] << 24);
+			case 3:
+				a += ((uint32) k[2] << 16);
+			case 2:
+				a += ((uint32) k[1] << 8);
+			case 1:
+				a += k[0];
+				/* case 0: nothing left to add */
+		}
+#endif							/* WORDS_BIGENDIAN */
+	}
+
+	final(a, b, c);
+
+	/* report the result */
+	return UInt64GetDatum(((uint64) b << 32) | c);
+}
+
+/*
+ * hash_uint32() -- hash a 32-bit value to a 32-bit value
  *
  * This has the same result as
  *		hash_any(&k, sizeof(uint32))
@@ -523,3 +864,32 @@ hash_uint32(uint32 k)
 	/* report the result */
 	return UInt32GetDatum(c);
 }
+
+/*
+ * hash_uint32_extended() -- hash a 32-bit value to a 64-bit value, with a seed
+ *
+ * Like hash_uint32, this is a convenience function.
+ */
+Datum
+hash_uint32_extended(uint32 k, uint64 seed)
+{
+	register uint32 a,
+				b,
+				c;
+
+	a = b = c = 0x9e3779b9 + (uint32) sizeof(uint32) + 3923095;
+
+	if (seed != 0)
+	{
+		a += (uint32) (seed >> 32);
+		b += (uint32) seed;
+		mix(a, b, c);
+	}
+
+	a += k;
+
+	final(a, b, c);
+
+	/* report the result */
+	return UInt64GetDatum(((uint64) b << 32) | c);
+}
diff --git a/src/backend/access/hash/hashpage.c b/src/backend/access/hash/hashpage.c
index 7b2906b..0579841 100644
--- a/src/backend/access/hash/hashpage.c
+++ b/src/backend/access/hash/hashpage.c
@@ -373,7 +373,7 @@ _hash_init(Relation rel, double num_tuples, ForkNumber forkNum)
 	if (ffactor < 10)
 		ffactor = 10;
 
-	procid = index_getprocid(rel, 1, HASHPROC);
+	procid = index_getprocid(rel, 1, HASHSTANDARD_PROC);
 
 	/*
 	 * We initialize the metapage, the first N bucket pages, and the first
diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c
index 9b803af..869cbc1 100644
--- a/src/backend/access/hash/hashutil.c
+++ b/src/backend/access/hash/hashutil.c
@@ -85,7 +85,7 @@ _hash_datum2hashkey(Relation rel, Datum key)
 	Oid			collation;
 
 	/* XXX assumes index has only one attribute */
-	procinfo = index_getprocinfo(rel, 1, HASHPROC);
+	procinfo = index_getprocinfo(rel, 1, HASHSTANDARD_PROC);
 	collation = rel->rd_indcollation[0];
 
 	return DatumGetUInt32(FunctionCall1Coll(procinfo, collation, key));
@@ -108,10 +108,10 @@ _hash_datum2hashkey_type(Relation rel, Datum key, Oid keytype)
 	hash_proc = get_opfamily_proc(rel->rd_opfamily[0],
 								  keytype,
 								  keytype,
-								  HASHPROC);
+								  HASHSTANDARD_PROC);
 	if (!RegProcedureIsValid(hash_proc))
 		elog(ERROR, "missing support function %d(%u,%u) for index \"%s\"",
-			 HASHPROC, keytype, keytype,
+			 HASHSTANDARD_PROC, keytype, keytype,
 			 RelationGetRelationName(rel));
 	collation = rel->rd_indcollation[0];
 
diff --git a/src/backend/access/hash/hashvalidate.c b/src/backend/access/hash/hashvalidate.c
index 30b29cb..8b633c2 100644
--- a/src/backend/access/hash/hashvalidate.c
+++ b/src/backend/access/hash/hashvalidate.c
@@ -29,7 +29,7 @@
 #include "utils/syscache.h"
 
 
-static bool check_hash_func_signature(Oid funcid, Oid restype, Oid argtype);
+static bool check_hash_func_signature(Oid funcid, int16 amprocnum, Oid argtype);
 
 
 /*
@@ -105,8 +105,9 @@ hashvalidate(Oid opclassoid)
 		/* Check procedure numbers and function signatures */
 		switch (procform->amprocnum)
 		{
-			case HASHPROC:
-				if (!check_hash_func_signature(procform->amproc, INT4OID,
+			case HASHSTANDARD_PROC:
+			case HASHEXTENDED_PROC:
+				if (!check_hash_func_signature(procform->amproc, procform->amprocnum,
 											   procform->amproclefttype))
 				{
 					ereport(INFO,
@@ -264,19 +265,37 @@ hashvalidate(Oid opclassoid)
  * hacks in the core hash opclass definitions.
  */
 static bool
-check_hash_func_signature(Oid funcid, Oid restype, Oid argtype)
+check_hash_func_signature(Oid funcid, int16 amprocnum, Oid argtype)
 {
 	bool		result = true;
+	Oid			restype;
+	int16		nargs;
 	HeapTuple	tp;
 	Form_pg_proc procform;
 
+	switch (amprocnum)
+	{
+		case HASHSTANDARD_PROC:
+			restype = INT4OID;
+			nargs = 1;
+			break;
+
+		case HASHEXTENDED_PROC:
+			restype = INT8OID;
+			nargs = 2;
+			break;
+
+		default:
+			elog(ERROR, "invalid amprocnum");
+	}
+
 	tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
 	if (!HeapTupleIsValid(tp))
 		elog(ERROR, "cache lookup failed for function %u", funcid);
 	procform = (Form_pg_proc) GETSTRUCT(tp);
 
 	if (procform->prorettype != restype || procform->proretset ||
-		procform->pronargs != 1)
+		procform->pronargs != nargs)
 		result = false;
 
 	if (!IsBinaryCoercible(argtype, procform->proargtypes.values[0]))
@@ -290,24 +309,29 @@ check_hash_func_signature(Oid funcid, Oid restype, Oid argtype)
 		 * identity, not just its input type, because hashvarlena() takes
 		 * INTERNAL and allowing any such function seems too scary.
 		 */
-		if (funcid == F_HASHINT4 &&
+		if ((funcid == F_HASHINT4 || funcid == F_HASHINT4EXTENDED) &&
 			(argtype == DATEOID ||
 			 argtype == ABSTIMEOID || argtype == RELTIMEOID ||
 			 argtype == XIDOID || argtype == CIDOID))
 			 /* okay, allowed use of hashint4() */ ;
-		else if (funcid == F_TIMESTAMP_HASH &&
+		else if ((funcid == F_TIMESTAMP_HASH ||
+				  funcid == F_TIMESTAMP_HASH_EXTENDED) &&
 				 argtype == TIMESTAMPTZOID)
 			 /* okay, allowed use of timestamp_hash() */ ;
-		else if (funcid == F_HASHCHAR &&
+		else if ((funcid == F_HASHCHAR || funcid == F_HASHCHAREXTENDED) &&
 				 argtype == BOOLOID)
 			 /* okay, allowed use of hashchar() */ ;
-		else if (funcid == F_HASHVARLENA &&
+		else if ((funcid == F_HASHVARLENA || funcid == F_HASHVARLENAEXTENDED) &&
 				 argtype == BYTEAOID)
 			 /* okay, allowed use of hashvarlena() */ ;
 		else
 			result = false;
 	}
 
+	/* If function takes a second argument, it must be for a 64-bit salt. */
+	if (nargs == 2 && procform->proargtypes.values[1] != INT8OID)
+		result = false;
+
 	ReleaseSysCache(tp);
 	return result;
 }
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index a31b1ac..d23e6d6 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -18,6 +18,7 @@
 #include <limits.h>
 
 #include "access/genam.h"
+#include "access/hash.h"
 #include "access/heapam.h"
 #include "access/nbtree.h"
 #include "access/htup_details.h"
@@ -1129,7 +1130,8 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
 	/*
 	 * btree comparison procs must be 2-arg procs returning int4, while btree
 	 * sortsupport procs must take internal and return void.  hash support
-	 * procs must be 1-arg procs returning int4.  Otherwise we don't know.
+	 * proc 1 must be a 1-arg proc returning int4, while proc 2 must be a
+	 * 2-arg proc returning int8.  Otherwise we don't know.
 	 */
 	if (amoid == BTREE_AM_OID)
 	{
@@ -1172,14 +1174,28 @@ assignProcTypes(OpFamilyMember *member, Oid amoid, Oid typeoid)
 	}
 	else if (amoid == HASH_AM_OID)
 	{
-		if (procform->pronargs != 1)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-					 errmsg("hash procedures must have one argument")));
-		if (procform->prorettype != INT4OID)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-					 errmsg("hash procedures must return integer")));
+		if (member->number == HASHSTANDARD_PROC)
+		{
+			if (procform->pronargs != 1)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+						 errmsg("hash procedure 1 must have one argument")));
+			if (procform->prorettype != INT4OID)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+						 errmsg("hash procedure 1 must return integer")));
+		}
+		else if (member->number == HASHEXTENDED_PROC)
+		{
+			if (procform->pronargs != 2)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+						 errmsg("hash procedure 2 must have two arguments")));
+			if (procform->prorettype != INT8OID)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+						 errmsg("hash procedure 2 must return bigint")));
+		}
 
 		/*
 		 * If lefttype/righttype isn't specified, use the proc's input type
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 2efb6c9..917491e 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -16,6 +16,7 @@
 
 #include <ctype.h>
 
+#include "access/hash.h"
 #include "access/htup_details.h"
 #include "catalog/catalog.h"
 #include "catalog/namespace.h"
@@ -717,6 +718,20 @@ hash_aclitem(PG_FUNCTION_ARGS)
 	PG_RETURN_UINT32((uint32) (a->ai_privs + a->ai_grantee + a->ai_grantor));
 }
 
+/*
+ * Hash aclitems with a 64-bit seed, if the seed is non-zero.
+ *
+ * Returns a uint64 value.  Otherwise similar to hash_aclitem.
+ */
+Datum
+hash_aclitem_extended(PG_FUNCTION_ARGS)
+{
+	AclItem    *a = PG_GETARG_ACLITEM_P(0);
+	uint64		seed = PG_GETARG_INT64(1);
+	uint32		sum = (uint32) (a->ai_privs + a->ai_grantee + a->ai_grantor);
+
+	return (seed == 0) ? UInt64GetDatum(sum) : hash_uint32_extended(sum, seed);
+}
 
 /*
  * acldefault()  --- create an ACL describing default access permissions
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 34dadd6..b08614e 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -20,6 +20,7 @@
 #endif
 #include <math.h>
 
+#include "access/hash.h"
 #include "access/htup_details.h"
 #include "catalog/pg_type.h"
 #include "funcapi.h"
@@ -4020,6 +4021,84 @@ hash_array(PG_FUNCTION_ARGS)
 	PG_RETURN_UINT32(result);
 }
 
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_array.
+ */
+Datum
+hash_array_extended(PG_FUNCTION_ARGS)
+{
+	AnyArrayType *array = PG_GETARG_ANY_ARRAY(0);
+	uint64		seed = PG_GETARG_INT64(1);
+	int			ndims = AARR_NDIM(array);
+	int		   *dims = AARR_DIMS(array);
+	Oid			element_type = AARR_ELEMTYPE(array);
+	uint64		result = 1;
+	int			nitems;
+	TypeCacheEntry *typentry;
+	int			typlen;
+	bool		typbyval;
+	char		typalign;
+	int			i;
+	array_iter	iter;
+	FunctionCallInfoData locfcinfo;
+
+	typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+	if (typentry == NULL ||
+		typentry->type_id != element_type)
+	{
+		typentry = lookup_type_cache(element_type,
+									 TYPECACHE_HASH_EXTENDED_PROC_FINFO);
+		if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_FUNCTION),
+					 errmsg("could not identify an extended hash function for type %s",
+							format_type_be(element_type))));
+		fcinfo->flinfo->fn_extra = (void *) typentry;
+	}
+	typlen = typentry->typlen;
+	typbyval = typentry->typbyval;
+	typalign = typentry->typalign;
+
+	InitFunctionCallInfoData(locfcinfo, &typentry->hash_extended_proc_finfo, 2,
+							 InvalidOid, NULL, NULL);
+
+	/* Loop over source data */
+	nitems = ArrayGetNItems(ndims, dims);
+	array_iter_setup(&iter, array);
+
+	for (i = 0; i < nitems; i++)
+	{
+		Datum		elt;
+		bool		isnull;
+		uint64		elthash;
+
+		/* Get element, checking for NULL */
+		elt = array_iter_next(&iter, &isnull, i, typlen, typbyval, typalign);
+
+		if (isnull)
+		{
+			elthash = 0;
+		}
+		else
+		{
+			/* Apply the hash function */
+			locfcinfo.arg[0] = elt;
+			locfcinfo.arg[1] = seed;
+			locfcinfo.argnull[0] = false;
+			locfcinfo.argnull[1] = false;
+			locfcinfo.isnull = false;
+			elthash = DatumGetUInt64(FunctionCallInvoke(&locfcinfo));
+		}
+
+		result = (result << 5) - result + elthash;
+	}
+
+	AARR_FREE_IF_COPY(array, 0);
+
+	return UInt64GetDatum(result);
+}
+
 
 /*-----------------------------------------------------------------------------
  * array overlap/containment comparisons
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 7d89d79..06934ed 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -1509,6 +1509,12 @@ time_hash(PG_FUNCTION_ARGS)
 }
 
 Datum
+time_hash_extended(PG_FUNCTION_ARGS)
+{
+	return hashint8extended(fcinfo);
+}
+
+Datum
 time_larger(PG_FUNCTION_ARGS)
 {
 	TimeADT		time1 = PG_GETARG_TIMEADT(0);
@@ -2214,6 +2220,21 @@ timetz_hash(PG_FUNCTION_ARGS)
 }
 
 Datum
+timetz_hash_extended(PG_FUNCTION_ARGS)
+{
+	TimeTzADT  *key = PG_GETARG_TIMETZADT_P(0);
+	uint64		seed = PG_GETARG_DATUM(1);
+	uint64		thash;
+
+	/* Same approach as timetz_hash */
+	thash = DatumGetUInt64(DirectFunctionCall2(hashint8extended,
+											   Int64GetDatumFast(key->time),
+											   seed));
+	thash ^= DatumGetUInt64(hash_uint32_extended(key->zone, seed));
+	return UInt64GetDatum(thash);
+}
+
+Datum
 timetz_larger(PG_FUNCTION_ARGS)
 {
 	TimeTzADT  *time1 = PG_GETARG_TIMETZADT_P(0);
diff --git a/src/backend/utils/adt/jsonb_op.c b/src/backend/utils/adt/jsonb_op.c
index d4c490e..4a0d147 100644
--- a/src/backend/utils/adt/jsonb_op.c
+++ b/src/backend/utils/adt/jsonb_op.c
@@ -291,3 +291,45 @@ jsonb_hash(PG_FUNCTION_ARGS)
 	PG_FREE_IF_COPY(jb, 0);
 	PG_RETURN_INT32(hash);
 }
+
+Datum
+jsonb_hash_extended(PG_FUNCTION_ARGS)
+{
+	Jsonb	   *jb = PG_GETARG_JSONB(0);
+	JsonbIterator *it;
+	JsonbValue	v;
+	JsonbIteratorToken r;
+	uint64		hash = 0;
+
+	if (JB_ROOT_COUNT(jb) == 0)
+		return UInt64GetDatum(0);
+
+	it = JsonbIteratorInit(&jb->root);
+
+	while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
+	{
+		switch (r)
+		{
+				/* Rotation is left to JsonbHashScalarValue() */
+			case WJB_BEGIN_ARRAY:
+				hash ^= JB_FARRAY;
+				break;
+			case WJB_BEGIN_OBJECT:
+				hash ^= JB_FOBJECT;
+				break;
+			case WJB_KEY:
+			case WJB_VALUE:
+			case WJB_ELEM:
+				JsonbHashScalarValueExtended(&v, &hash, PG_GETARG_INT64(1));
+				break;
+			case WJB_END_ARRAY:
+			case WJB_END_OBJECT:
+				break;
+			default:
+				elog(ERROR, "invalid JsonbIteratorNext rc: %d", (int) r);
+		}
+	}
+
+	PG_FREE_IF_COPY(jb, 0);
+	return UInt64GetDatum(hash);
+}
diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c
index 4850569..60986dd 100644
--- a/src/backend/utils/adt/jsonb_util.c
+++ b/src/backend/utils/adt/jsonb_util.c
@@ -1250,6 +1250,46 @@ JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash)
 }
 
 /*
+ * Hash a value to a 64-bit value, with a seed. Otherwise, similar to
+ * JsonbHashScalarValue.
+ */
+void
+JsonbHashScalarValueExtended(const JsonbValue *scalarVal, uint64 *hash,
+							 uint64 seed)
+{
+	uint64		tmp;
+
+	switch (scalarVal->type)
+	{
+		case jbvNull:
+			tmp = 0x01;
+			break;
+		case jbvString:
+			tmp = DatumGetUInt64(hash_any_extended((const unsigned char *) scalarVal->val.string.val,
+												   scalarVal->val.string.len,
+												   seed));
+			break;
+		case jbvNumeric:
+			tmp = DatumGetUInt64(DirectFunctionCall2(hash_numeric_extended,
+													 NumericGetDatum(scalarVal->val.numeric),
+													 seed));
+			break;
+		case jbvBool:
+			tmp = DatumGetUInt64(DirectFunctionCall2(hashcharextended,
+													 BoolGetDatum(scalarVal->val.boolean),
+													 seed));
+
+			break;
+		default:
+			elog(ERROR, "invalid jsonb scalar type");
+			break;
+	}
+
+	*hash = (*hash << 1) | (*hash >> 63);
+	*hash ^= tmp;
+}
+
+/*
  * Are two scalar JsonbValues of the same type a and b equal?
  */
 static bool
diff --git a/src/backend/utils/adt/mac.c b/src/backend/utils/adt/mac.c
index d1c20c3..60521cc 100644
--- a/src/backend/utils/adt/mac.c
+++ b/src/backend/utils/adt/mac.c
@@ -271,6 +271,15 @@ hashmacaddr(PG_FUNCTION_ARGS)
 	return hash_any((unsigned char *) key, sizeof(macaddr));
 }
 
+Datum
+hashmacaddrextended(PG_FUNCTION_ARGS)
+{
+	macaddr    *key = PG_GETARG_MACADDR_P(0);
+
+	return hash_any_extended((unsigned char *) key, sizeof(macaddr),
+							 PG_GETARG_INT64(1));
+}
+
 /*
  * Arithmetic functions: bitwise NOT, AND, OR.
  */
diff --git a/src/backend/utils/adt/mac8.c b/src/backend/utils/adt/mac8.c
index 482d1fb..0410b98 100644
--- a/src/backend/utils/adt/mac8.c
+++ b/src/backend/utils/adt/mac8.c
@@ -407,6 +407,15 @@ hashmacaddr8(PG_FUNCTION_ARGS)
 	return hash_any((unsigned char *) key, sizeof(macaddr8));
 }
 
+Datum
+hashmacaddr8extended(PG_FUNCTION_ARGS)
+{
+	macaddr8   *key = PG_GETARG_MACADDR8_P(0);
+
+	return hash_any_extended((unsigned char *) key, sizeof(macaddr8),
+							 PG_GETARG_INT64(1));
+}
+
 /*
  * Arithmetic functions: bitwise NOT, AND, OR.
  */
diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c
index 5573c34..ec4ac20 100644
--- a/src/backend/utils/adt/network.c
+++ b/src/backend/utils/adt/network.c
@@ -486,6 +486,16 @@ hashinet(PG_FUNCTION_ARGS)
 	return hash_any((unsigned char *) VARDATA_ANY(addr), addrsize + 2);
 }
 
+Datum
+hashinetextended(PG_FUNCTION_ARGS)
+{
+	inet	   *addr = PG_GETARG_INET_PP(0);
+	int			addrsize = ip_addrsize(addr);
+
+	return hash_any_extended((unsigned char *) VARDATA_ANY(addr), addrsize + 2,
+							 PG_GETARG_INT64(1));
+}
+
 /*
  *	Boolean network-inclusion tests.
  */
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 3e5614e..59f6041 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -2230,6 +2230,65 @@ hash_numeric(PG_FUNCTION_ARGS)
 	PG_RETURN_DATUM(result);
 }
 
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_numeric.
+ */
+Datum
+hash_numeric_extended(PG_FUNCTION_ARGS)
+{
+	Numeric		key = PG_GETARG_NUMERIC(0);
+	Datum		digit_hash;
+	Datum		result;
+	int			weight;
+	int			start_offset;
+	int			end_offset;
+	int			i;
+	int			hash_len;
+	NumericDigit *digits;
+
+	if (NUMERIC_IS_NAN(key))
+		return UInt64GetDatum(0);
+
+	weight = NUMERIC_WEIGHT(key);
+	start_offset = 0;
+	end_offset = 0;
+
+	digits = NUMERIC_DIGITS(key);
+	for (i = 0; i < NUMERIC_NDIGITS(key); i++)
+	{
+		if (digits[i] != (NumericDigit) 0)
+			break;
+
+		start_offset++;
+
+		weight--;
+	}
+
+	if (NUMERIC_NDIGITS(key) == start_offset)
+		return UInt64GetDatum(-1);
+
+	for (i = NUMERIC_NDIGITS(key) - 1; i >= 0; i--)
+	{
+		if (digits[i] != (NumericDigit) 0)
+			break;
+
+		end_offset++;
+	}
+
+	Assert(start_offset + end_offset < NUMERIC_NDIGITS(key));
+
+	hash_len = NUMERIC_NDIGITS(key) - start_offset - end_offset;
+	digit_hash = hash_any_extended((unsigned char *) (NUMERIC_DIGITS(key)
+													  + start_offset),
+								   hash_len * sizeof(NumericDigit),
+								   PG_GETARG_INT64(1));
+
+	result = digit_hash ^ weight;
+
+	PG_RETURN_DATUM(result);
+}
+
 
 /* ----------------------------------------------------------------------
  *
diff --git a/src/backend/utils/adt/pg_lsn.c b/src/backend/utils/adt/pg_lsn.c
index aefbb87..7ad30a2 100644
--- a/src/backend/utils/adt/pg_lsn.c
+++ b/src/backend/utils/adt/pg_lsn.c
@@ -179,6 +179,12 @@ pg_lsn_hash(PG_FUNCTION_ARGS)
 	return hashint8(fcinfo);
 }
 
+Datum
+pg_lsn_hash_extended(PG_FUNCTION_ARGS)
+{
+	return hashint8extended(fcinfo);
+}
+
 
 /*----------------------------------------------------------
  *	Arithmetic operators on PostgreSQL LSNs.
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 09a4f14..66cad0d 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -1281,6 +1281,69 @@ hash_range(PG_FUNCTION_ARGS)
 }
 
 /*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+	RangeType  *r = PG_GETARG_RANGE(0);
+	uint64		seed = PG_GETARG_INT64(1);
+	uint64		result;
+	TypeCacheEntry *typcache;
+	TypeCacheEntry *scache;
+	RangeBound	lower;
+	RangeBound	upper;
+	bool		empty;
+	char		flags;
+	uint64		lower_hash;
+	uint64		upper_hash;
+
+	check_stack_depth();
+
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+	range_deserialize(typcache, r, &lower, &upper, &empty);
+	flags = range_get_flags(r);
+
+	scache = typcache->rngelemtype;
+	if (!OidIsValid(scache->hash_extended_proc_finfo.fn_oid))
+	{
+		scache = lookup_type_cache(scache->type_id,
+								   TYPECACHE_HASH_EXTENDED_PROC_FINFO);
+		if (!OidIsValid(scache->hash_extended_proc_finfo.fn_oid))
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_FUNCTION),
+					 errmsg("could not identify a hash function for type %s",
+							format_type_be(scache->type_id))));
+	}
+
+	if (RANGE_HAS_LBOUND(flags))
+		lower_hash = DatumGetUInt64(FunctionCall2Coll(&scache->hash_extended_proc_finfo,
+													  typcache->rng_collation,
+													  lower.val,
+													  seed));
+	else
+		lower_hash = 0;
+
+	if (RANGE_HAS_UBOUND(flags))
+		upper_hash = DatumGetUInt64(FunctionCall2Coll(&scache->hash_extended_proc_finfo,
+													  typcache->rng_collation,
+													  upper.val,
+													  seed));
+	else
+		upper_hash = 0;
+
+	/* Same approach as hash_range */
+	result = hash_uint32_extended((uint32) flags, seed);
+	result ^= lower_hash;
+	result = (result << 1) | (result >> 63);
+	result ^= upper_hash;
+
+	return UInt64GetDatum(result);
+}
+
+/*
  *----------------------------------------------------------
  * CANONICAL FUNCTIONS
  *
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index 6fa126d..b11d452 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -2113,6 +2113,11 @@ timestamp_hash(PG_FUNCTION_ARGS)
 	return hashint8(fcinfo);
 }
 
+Datum
+timestamp_hash_extended(PG_FUNCTION_ARGS)
+{
+	return hashint8extended(fcinfo);
+}
 
 /*
  * Cross-type comparison functions for timestamp vs timestamptz
@@ -2419,6 +2424,20 @@ interval_hash(PG_FUNCTION_ARGS)
 	return DirectFunctionCall1(hashint8, Int64GetDatumFast(span64));
 }
 
+Datum
+interval_hash_extended(PG_FUNCTION_ARGS)
+{
+	Interval   *interval = PG_GETARG_INTERVAL_P(0);
+	INT128		span = interval_cmp_value(interval);
+	int64		span64;
+
+	/* Same approach as interval_hash */
+	span64 = int128_to_int64(span);
+
+	return DirectFunctionCall2(hashint8extended, Int64GetDatumFast(span64),
+							   PG_GETARG_DATUM(1));
+}
+
 /* overlaps_timestamp() --- implements the SQL OVERLAPS operator.
  *
  * Algorithm is per SQL spec.  This is much harder than you'd think
diff --git a/src/backend/utils/adt/uuid.c b/src/backend/utils/adt/uuid.c
index 5f15c8e..f73c695 100644
--- a/src/backend/utils/adt/uuid.c
+++ b/src/backend/utils/adt/uuid.c
@@ -408,3 +408,11 @@ uuid_hash(PG_FUNCTION_ARGS)
 
 	return hash_any(key->data, UUID_LEN);
 }
+
+Datum
+uuid_hash_extended(PG_FUNCTION_ARGS)
+{
+	pg_uuid_t  *key = PG_GETARG_UUID_P(0);
+
+	return hash_any_extended(key->data, UUID_LEN, PG_GETARG_INT64(1));
+}
diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
index cbc62b0..2df6f2c 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -947,6 +947,24 @@ hashbpchar(PG_FUNCTION_ARGS)
 	return result;
 }
 
+Datum
+hashbpcharextended(PG_FUNCTION_ARGS)
+{
+	BpChar	   *key = PG_GETARG_BPCHAR_PP(0);
+	char	   *keydata;
+	int			keylen;
+	Datum		result;
+
+	keydata = VARDATA_ANY(key);
+	keylen = bcTruelen(key);
+
+	result = hash_any_extended((unsigned char *) keydata, keylen,
+							   PG_GETARG_INT64(1));
+
+	PG_FREE_IF_COPY(key, 0);
+
+	return result;
+}
 
 /*
  * The following operators support character-by-character comparison
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 82763f8..b7a14dc 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -490,8 +490,8 @@ get_compatible_hash_operators(Oid opno,
 
 /*
  * get_op_hash_functions
- *		Get the OID(s) of hash support function(s) compatible with the given
- *		operator, operating on its LHS and/or RHS datatype as required.
+ *		Get the OID(s) of the standard hash support function(s) compatible with
+ *		the given operator, operating on its LHS and/or RHS datatype as required.
  *
  * A function for the LHS type is sought and returned into *lhs_procno if
  * lhs_procno isn't NULL.  Similarly, a function for the RHS type is sought
@@ -542,7 +542,7 @@ get_op_hash_functions(Oid opno,
 				*lhs_procno = get_opfamily_proc(aform->amopfamily,
 												aform->amoplefttype,
 												aform->amoplefttype,
-												HASHPROC);
+												HASHSTANDARD_PROC);
 				if (!OidIsValid(*lhs_procno))
 					continue;
 				/* Matching LHS found, done if caller doesn't want RHS */
@@ -564,7 +564,7 @@ get_op_hash_functions(Oid opno,
 				*rhs_procno = get_opfamily_proc(aform->amopfamily,
 												aform->amoprighttype,
 												aform->amoprighttype,
-												HASHPROC);
+												HASHSTANDARD_PROC);
 				if (!OidIsValid(*rhs_procno))
 				{
 					/* Forget any LHS function from this opfamily */
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 691d498..2e633f0 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -90,6 +90,7 @@ static TypeCacheEntry *firstDomainTypeEntry = NULL;
 #define TCFLAGS_HAVE_FIELD_EQUALITY			0x1000
 #define TCFLAGS_HAVE_FIELD_COMPARE			0x2000
 #define TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS	0x4000
+#define TCFLAGS_CHECKED_HASH_EXTENDED_PROC	0x8000
 
 /*
  * Data stored about a domain type's constraints.  Note that we do not create
@@ -307,6 +308,8 @@ lookup_type_cache(Oid type_id, int flags)
 		flags |= TYPECACHE_HASH_OPFAMILY;
 
 	if ((flags & (TYPECACHE_HASH_PROC | TYPECACHE_HASH_PROC_FINFO |
+				  TYPECACHE_HASH_EXTENDED_PROC |
+				  TYPECACHE_HASH_EXTENDED_PROC_FINFO |
 				  TYPECACHE_HASH_OPFAMILY)) &&
 		!(typentry->flags & TCFLAGS_CHECKED_HASH_OPCLASS))
 	{
@@ -329,6 +332,7 @@ lookup_type_cache(Oid type_id, int flags)
 		 * decision is still good.
 		 */
 		typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC);
+		typentry->flags &= ~(TCFLAGS_CHECKED_HASH_EXTENDED_PROC);
 		typentry->flags |= TCFLAGS_CHECKED_HASH_OPCLASS;
 	}
 
@@ -372,11 +376,12 @@ lookup_type_cache(Oid type_id, int flags)
 		typentry->eq_opr = eq_opr;
 
 		/*
-		 * Reset info about hash function whenever we pick up new info about
-		 * equality operator.  This is so we can ensure that the hash function
-		 * matches the operator.
+		 * Reset info about hash functions whenever we pick up new info about
+		 * equality operator.  This is so we can ensure that the hash functions
+		 * match the operator.
 		 */
 		typentry->flags &= ~(TCFLAGS_CHECKED_HASH_PROC);
+		typentry->flags &= ~(TCFLAGS_CHECKED_HASH_EXTENDED_PROC);
 		typentry->flags |= TCFLAGS_CHECKED_EQ_OPR;
 	}
 	if ((flags & TYPECACHE_LT_OPR) &&
@@ -467,7 +472,7 @@ lookup_type_cache(Oid type_id, int flags)
 			hash_proc = get_opfamily_proc(typentry->hash_opf,
 										  typentry->hash_opintype,
 										  typentry->hash_opintype,
-										  HASHPROC);
+										  HASHSTANDARD_PROC);
 
 		/*
 		 * As above, make sure hash_array will succeed.  We don't currently
@@ -485,6 +490,43 @@ lookup_type_cache(Oid type_id, int flags)
 		typentry->hash_proc = hash_proc;
 		typentry->flags |= TCFLAGS_CHECKED_HASH_PROC;
 	}
+	if ((flags & (TYPECACHE_HASH_EXTENDED_PROC |
+				  TYPECACHE_HASH_EXTENDED_PROC_FINFO)) &&
+		!(typentry->flags & TCFLAGS_CHECKED_HASH_EXTENDED_PROC))
+	{
+		Oid			hash_extended_proc = InvalidOid;
+
+		/*
+		 * We insist that the eq_opr, if one has been determined, match the
+		 * hash opclass; else report there is no hash function.
+		 */
+		if (typentry->hash_opf != InvalidOid &&
+			(!OidIsValid(typentry->eq_opr) ||
+			 typentry->eq_opr == get_opfamily_member(typentry->hash_opf,
+													 typentry->hash_opintype,
+													 typentry->hash_opintype,
+													 HTEqualStrategyNumber)))
+			hash_extended_proc = get_opfamily_proc(typentry->hash_opf,
+												   typentry->hash_opintype,
+												   typentry->hash_opintype,
+												   HASHEXTENDED_PROC);
+
+		/*
+		 * As above, make sure hash_array_extended will succeed.  We don't
+		 * currently support hashing for composite types, but when we do,
+		 * we'll need more logic here to check that case too.
+		 */
+		if (hash_extended_proc == F_HASH_ARRAY_EXTENDED &&
+			!array_element_has_hashing(typentry))
+			hash_extended_proc = InvalidOid;
+
+		/* Force update of hash_proc_finfo only if we're changing state */
+		if (typentry->hash_extended_proc != hash_extended_proc)
+			typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
+
+		typentry->hash_extended_proc = hash_extended_proc;
+		typentry->flags |= TCFLAGS_CHECKED_HASH_EXTENDED_PROC;
+	}
 
 	/*
 	 * Set up fmgr lookup info as requested
@@ -523,6 +565,14 @@ lookup_type_cache(Oid type_id, int flags)
 		fmgr_info_cxt(typentry->hash_proc, &typentry->hash_proc_finfo,
 					  CacheMemoryContext);
 	}
+	if ((flags & TYPECACHE_HASH_EXTENDED_PROC_FINFO) &&
+		typentry->hash_extended_proc_finfo.fn_oid == InvalidOid &&
+		typentry->hash_extended_proc != InvalidOid)
+	{
+		fmgr_info_cxt(typentry->hash_extended_proc,
+					  &typentry->hash_extended_proc_finfo,
+					  CacheMemoryContext);
+	}
 
 	/*
 	 * If it's a composite type (row type), get tupdesc if requested
diff --git a/src/include/access/hash.h b/src/include/access/hash.h
index 72fce30..d89398d 100644
--- a/src/include/access/hash.h
+++ b/src/include/access/hash.h
@@ -289,12 +289,16 @@ typedef HashMetaPageData *HashMetaPage;
 #define HTMaxStrategyNumber				1
 
 /*
- *	When a new operator class is declared, we require that the user supply
- *	us with an amproc procudure for hashing a key of the new type.
- *	Since we only have one such proc in amproc, it's number 1.
+ * When a new operator class is declared, we require that the user supply
+ * us with an amproc procudure for hashing a key of the new type, returning
+ * a 32-bit hash value.  We call this the "standard" hash procedure.  We
+ * also allow an optional "extended" hash procedure which accepts a salt and
+ * returns a 64-bit hash value.  This is highly recommended but, for reasons
+ * of backward compatibility, optional.
  */
-#define HASHPROC		1
-#define HASHNProcs		1
+#define HASHSTANDARD_PROC		1
+#define HASHEXTENDED_PROC		2
+#define HASHNProcs				2
 
 
 /* public routines */
@@ -322,7 +326,10 @@ extern bytea *hashoptions(Datum reloptions, bool validate);
 extern bool hashvalidate(Oid opclassoid);
 
 extern Datum hash_any(register const unsigned char *k, register int keylen);
+extern Datum hash_any_extended(register const unsigned char *k,
+				  register int keylen, uint64 seed);
 extern Datum hash_uint32(uint32 k);
+extern Datum hash_uint32_extended(uint32 k, uint64 seed);
 
 /* private routines */
 
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
index 7d245b1..fb6a829 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -153,41 +153,77 @@ DATA(insert (	4033   3802 3802 1 4044 ));
 
 /* hash */
 DATA(insert (	427   1042 1042 1 1080 ));
+DATA(insert (	427   1042 1042 2 972 ));
 DATA(insert (	431   18 18 1 454 ));
+DATA(insert (	431   18 18 2 446 ));
 DATA(insert (	435   1082 1082 1 450 ));
+DATA(insert (	435   1082 1082 2 425 ));
 DATA(insert (	627   2277 2277 1 626 ));
+DATA(insert (	627   2277 2277 2 782 ));
 DATA(insert (	1971   700 700 1 451 ));
+DATA(insert (	1971   700 700 2 443 ));
 DATA(insert (	1971   701 701 1 452 ));
+DATA(insert (	1971   701 701 2 444 ));
 DATA(insert (	1975   869 869 1 422 ));
+DATA(insert (	1975   869 869 2 779 ));
 DATA(insert (	1977   21 21 1 449 ));
+DATA(insert (	1977   21 21 2 441 ));
 DATA(insert (	1977   23 23 1 450 ));
+DATA(insert (	1977   23 23 2 425 ));
 DATA(insert (	1977   20 20 1 949 ));
+DATA(insert (	1977   20 20 2 442 ));
 DATA(insert (	1983   1186 1186 1 1697 ));
+DATA(insert (	1983   1186 1186 2 3418 ));
 DATA(insert (	1985   829 829 1 399 ));
+DATA(insert (	1985   829 829 2 778 ));
 DATA(insert (	1987   19 19 1 455 ));
+DATA(insert (	1987   19 19 2 447 ));
 DATA(insert (	1990   26 26 1 453 ));
+DATA(insert (	1990   26 26 2 445 ));
 DATA(insert (	1992   30 30 1 457 ));
+DATA(insert (	1992   30 30 2 776 ));
 DATA(insert (	1995   25 25 1 400 ));
+DATA(insert (	1995   25 25 2 448));
 DATA(insert (	1997   1083 1083 1 1688 ));
+DATA(insert (	1997   1083 1083 2 3409 ));
 DATA(insert (	1998   1700 1700 1 432 ));
+DATA(insert (	1998   1700 1700 2 780 ));
 DATA(insert (	1999   1184 1184 1 2039 ));
+DATA(insert (	1999   1184 1184 2 3411 ));
 DATA(insert (	2001   1266 1266 1 1696 ));
+DATA(insert (	2001   1266 1266 2 3410 ));
 DATA(insert (	2040   1114 1114 1 2039 ));
+DATA(insert (	2040   1114 1114 2 3411 ));
 DATA(insert (	2222   16 16 1 454 ));
+DATA(insert (	2222   16 16 2 446 ));
 DATA(insert (	2223   17 17 1 456 ));
+DATA(insert (	2223   17 17 2 772 ));
 DATA(insert (	2225   28 28 1 450 ));
+DATA(insert (	2225   28 28 2 425));
 DATA(insert (	2226   29 29 1 450 ));
+DATA(insert (	2226   29 29 2 425 ));
 DATA(insert (	2227   702 702 1 450 ));
+DATA(insert (	2227   702 702 2 425 ));
 DATA(insert (	2228   703 703 1 450 ));
+DATA(insert (	2228   703 703 2 425 ));
 DATA(insert (	2229   25 25 1 400 ));
+DATA(insert (	2229   25 25 2 448 ));
 DATA(insert (	2231   1042 1042 1 1080 ));
+DATA(insert (	2231   1042 1042 2 972 ));
 DATA(insert (	2235   1033 1033 1 329 ));
+DATA(insert (	2235   1033 1033 2 777 ));
 DATA(insert (	2969   2950 2950 1 2963 ));
+DATA(insert (	2969   2950 2950 2 3412 ));
 DATA(insert (	3254   3220 3220 1 3252 ));
+DATA(insert (	3254   3220 3220 2 3413 ));
 DATA(insert (	3372   774 774 1 328 ));
+DATA(insert (	3372   774 774 2 781 ));
 DATA(insert (	3523   3500 3500 1 3515 ));
+DATA(insert (	3523   3500 3500 2 3414 ));
 DATA(insert (	3903   3831 3831 1 3902 ));
+DATA(insert (	3903   3831 3831 2 3417 ));
 DATA(insert (	4034   3802 3802 1 4045 ));
+DATA(insert (	4034   3802 3802 2 3416));
 
 
 /* gist */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 8b33b4e..d820b56 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -668,36 +668,68 @@ DESCR("convert char(n) to name");
 
 DATA(insert OID = 449 (  hashint2		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "21" _null_ _null_ _null_ _null_ _null_ hashint2 _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 441 (  hashint2extended  PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "21 20" _null_ _null_ _null_ _null_ _null_ hashint2extended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 450 (  hashint4		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "23" _null_ _null_ _null_ _null_ _null_ hashint4 _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 425 (  hashint4extended  PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "23 20" _null_ _null_ _null_ _null_ _null_ hashint4extended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 949 (  hashint8		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "20" _null_ _null_ _null_ _null_ _null_ hashint8 _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 442 (  hashint8extended  PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "20 20" _null_ _null_ _null_ _null_ _null_ hashint8extended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 451 (  hashfloat4		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "700" _null_ _null_ _null_ _null_ _null_ hashfloat4 _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 443 (  hashfloat4extended	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "700 20" _null_ _null_ _null_ _null_ _null_ hashfloat4extended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 452 (  hashfloat8		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "701" _null_ _null_ _null_ _null_ _null_ hashfloat8 _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 444 (  hashfloat8extended PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "701 20" _null_ _null_ _null_ _null_ _null_ hashfloat8extended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 453 (  hashoid		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "26" _null_ _null_ _null_ _null_ _null_ hashoid _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 445 (  hashoidextended   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "26 20" _null_ _null_ _null_ _null_ _null_ hashoidextended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 454 (  hashchar		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "18" _null_ _null_ _null_ _null_ _null_ hashchar _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 446 (  hashcharextended  PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "18 20" _null_ _null_ _null_ _null_ _null_ hashcharextended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 455 (  hashname		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "19" _null_ _null_ _null_ _null_ _null_ hashname _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 447 (  hashnameextended  PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "19 20" _null_ _null_ _null_ _null_ _null_ hashnameextended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 400 (  hashtext		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "25" _null_ _null_ _null_ _null_ _null_ hashtext _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 448 (  hashtextextended  PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "25 20" _null_ _null_ _null_ _null_ _null_ hashtextextended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 456 (  hashvarlena	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "2281" _null_ _null_ _null_ _null_ _null_ hashvarlena _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 772 (  hashvarlenaextended PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "2281 20" _null_ _null_ _null_ _null_ _null_ hashvarlenaextended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 457 (  hashoidvector	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "30" _null_ _null_ _null_ _null_ _null_ hashoidvector _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 776 (  hashoidvectorextended PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "30 20" _null_ _null_ _null_ _null_ _null_ hashoidvectorextended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 329 (  hash_aclitem	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "1033" _null_ _null_ _null_ _null_ _null_	hash_aclitem _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 777 (  hash_aclitem_extended PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "1033 20" _null_ _null_ _null_ _null_ _null_ hash_aclitem_extended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 399 (  hashmacaddr	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "829" _null_ _null_ _null_ _null_ _null_ hashmacaddr _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 778 (  hashmacaddrextended PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "829 20" _null_ _null_ _null_ _null_ _null_ hashmacaddrextended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 422 (  hashinet		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "869" _null_ _null_ _null_ _null_ _null_ hashinet _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 779 (  hashinetextended  PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "869 20" _null_ _null_ _null_ _null_ _null_ hashinetextended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 432 (  hash_numeric	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "1700" _null_ _null_ _null_ _null_ _null_ hash_numeric _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 780 (  hash_numeric_extended PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "1700 20" _null_ _null_ _null_ _null_ _null_ hash_numeric_extended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 328 (  hashmacaddr8	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "774" _null_ _null_ _null_ _null_ _null_ hashmacaddr8 _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 781 (  hashmacaddr8extended PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "774 20" _null_ _null_ _null_ _null_ _null_ hashmacaddr8extended _null_ _null_ _null_ ));
+DESCR("hash");
 
 DATA(insert OID = 438 (  num_nulls		   PGNSP PGUID 12 1 0 2276 0 f f f f f f i s 1 0 23 "2276" "{2276}" "{v}" _null_ _null_ _null_ pg_num_nulls _null_ _null_ _null_ ));
 DESCR("count the number of NULL arguments");
@@ -747,6 +779,8 @@ DESCR("convert float8 to int8");
 
 DATA(insert OID = 626 (  hash_array		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "2277" _null_ _null_ _null_ _null_ _null_ hash_array _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 782 (  hash_array_extended PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "2277 20" _null_ _null_ _null_ _null_ _null_ hash_array_extended _null_ _null_ _null_ ));
+DESCR("hash");
 
 DATA(insert OID = 652 (  float4			   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 700 "20" _null_ _null_ _null_ _null_ _null_ i8tof _null_ _null_ _null_ ));
 DESCR("convert int8 to float4");
@@ -1155,6 +1189,8 @@ DATA(insert OID = 3328 ( bpchar_sortsupport PGNSP PGUID 12 1 0 0 0 f f f f t f i
 DESCR("sort support");
 DATA(insert OID = 1080 (  hashbpchar	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "1042" _null_ _null_ _null_ _null_ _null_	hashbpchar _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 972  (  hashbpcharextended PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "1042 20" _null_ _null_ _null_ _null_ _null_	hashbpcharextended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 1081 (  format_type	   PGNSP PGUID 12 1 0 0 0 f f f f f f s s 2 0 25 "26 23" _null_ _null_ _null_ _null_ _null_ format_type _null_ _null_ _null_ ));
 DESCR("format a type oid and atttypmod to canonical SQL");
 DATA(insert OID = 1084 (  date_in		   PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 1082 "2275" _null_ _null_ _null_ _null_ _null_ date_in _null_ _null_ _null_ ));
@@ -2286,10 +2322,16 @@ DESCR("less-equal-greater");
 
 DATA(insert OID = 1688 (  time_hash			PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "1083" _null_ _null_ _null_ _null_ _null_ time_hash _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 3409 (  time_hash_extended	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "1083 20" _null_ _null_ _null_ _null_ _null_ time_hash_extended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 1696 (  timetz_hash		PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "1266" _null_ _null_ _null_ _null_ _null_ timetz_hash _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 3410 (  timetz_hash_extended PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "1266 20" _null_ _null_ _null_ _null_ _null_ timetz_hash_extended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 1697 (  interval_hash		PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "1186" _null_ _null_ _null_ _null_ _null_ interval_hash _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 3418 (  interval_hash_extended PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "1186 20" _null_ _null_ _null_ _null_ _null_ interval_hash_extended _null_ _null_ _null_ ));
+DESCR("hash");
 
 
 /* OID's 1700 - 1799 NUMERIC data type */
@@ -3078,6 +3120,8 @@ DATA(insert OID = 2038 (  timezone			PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0
 DESCR("adjust time with time zone to new zone");
 DATA(insert OID = 2039 (  timestamp_hash	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "1114" _null_ _null_ _null_ _null_ _null_ timestamp_hash _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 3411 (  timestamp_hash_extended PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "1114 20" _null_ _null_ _null_ _null_ _null_ timestamp_hash_extended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 2041 ( overlaps			PGNSP PGUID 12 1 0 0 0 f f f f f f i s 4 0 16 "1114 1114 1114 1114" _null_ _null_ _null_ _null_ _null_	overlaps_timestamp _null_ _null_ _null_ ));
 DESCR("intervals overlap?");
 DATA(insert OID = 2042 ( overlaps			PGNSP PGUID 14 1 0 0 0 f f f f f f i s 4 0 16 "1114 1186 1114 1186" _null_ _null_ _null_ _null_ _null_ "select ($1, ($1 + $2)) overlaps ($3, ($3 + $4))" _null_ _null_ _null_ ));
@@ -4543,6 +4587,8 @@ DATA(insert OID = 2962 (  uuid_send		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1
 DESCR("I/O");
 DATA(insert OID = 2963 (  uuid_hash		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "2950" _null_ _null_ _null_ _null_ _null_ uuid_hash _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 3412 (  uuid_hash_extended PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "2950 20" _null_ _null_ _null_ _null_ _null_ uuid_hash_extended _null_ _null_ _null_ ));
+DESCR("hash");
 
 /* pg_lsn */
 DATA(insert OID = 3229 (  pg_lsn_in		PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 3220 "2275" _null_ _null_ _null_ _null_ _null_ pg_lsn_in _null_ _null_ _null_ ));
@@ -4564,6 +4610,8 @@ DATA(insert OID = 3251 (  pg_lsn_cmp	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0
 DESCR("less-equal-greater");
 DATA(insert OID = 3252 (  pg_lsn_hash	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "3220" _null_ _null_ _null_ _null_ _null_ pg_lsn_hash _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 3413 (  pg_lsn_hash_extended PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "3220 20" _null_ _null_ _null_ _null_ _null_ pg_lsn_hash_extended _null_ _null_ _null_ ));
+DESCR("hash");
 
 /* enum related procs */
 DATA(insert OID = 3504 (  anyenum_in	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 3500 "2275" _null_ _null_ _null_ _null_ _null_ anyenum_in _null_ _null_ _null_ ));
@@ -4584,6 +4632,8 @@ DATA(insert OID = 3514 (  enum_cmp		PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 2
 DESCR("less-equal-greater");
 DATA(insert OID = 3515 (  hashenum		PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "3500" _null_ _null_ _null_ _null_ _null_ hashenum _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 3414 (  hashenumextended PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "3500 20" _null_ _null_ _null_ _null_ _null_ hashenumextended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 3524 (  enum_smaller	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 3500 "3500 3500" _null_ _null_ _null_ _null_ _null_ enum_smaller _null_ _null_ _null_ ));
 DESCR("smaller of two");
 DATA(insert OID = 3525 (  enum_larger	PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 3500 "3500 3500" _null_ _null_ _null_ _null_ _null_ enum_larger _null_ _null_ _null_ ));
@@ -4981,6 +5031,8 @@ DATA(insert OID = 4044 (  jsonb_cmp		   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2
 DESCR("less-equal-greater");
 DATA(insert OID = 4045 (  jsonb_hash	   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "3802" _null_ _null_ _null_ _null_ _null_ jsonb_hash _null_ _null_ _null_ ));
 DESCR("hash");
+DATA(insert OID = 3416 (  jsonb_hash_extended PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "3802 20" _null_ _null_ _null_ _null_ _null_ jsonb_hash_extended _null_ _null_ _null_ ));
+DESCR("hash");
 DATA(insert OID = 4046 (  jsonb_contains   PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "3802 3802" _null_ _null_ _null_ _null_ _null_ jsonb_contains _null_ _null_ _null_ ));
 DATA(insert OID = 4047 (  jsonb_exists	 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "3802 25" _null_ _null_ _null_ _null_ _null_ jsonb_exists _null_ _null_ _null_ ));
 DATA(insert OID = 4048 (  jsonb_exists_any	 PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 16 "3802 1009" _null_ _null_ _null_ _null_ _null_ jsonb_exists_any _null_ _null_ _null_ ));
@@ -5171,6 +5223,8 @@ DATA(insert OID = 3881 (  range_gist_same		PGNSP PGUID 12 1 0 0 0 f f f f t f i
 DESCR("GiST support");
 DATA(insert OID = 3902 (  hash_range			PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 23 "3831" _null_ _null_ _null_ _null_ _null_ hash_range _null_ _null_ _null_ ));
 DESCR("hash a range");
+DATA(insert OID = 3417 (  hash_range_extended PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 20 "3831 20" _null_ _null_ _null_ _null_ _null_ hash_range_extended _null_ _null_ _null_ ));
+DESCR("hash a range");
 DATA(insert OID = 3916 (  range_typanalyze		PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 16 "2281" _null_ _null_ _null_ _null_ _null_ range_typanalyze _null_ _null_ _null_ ));
 DESCR("range typanalyze");
 DATA(insert OID = 3169 (  rangesel				PGNSP PGUID 12 1 0 0 0 f f f f t f s s 4 0 701 "2281 26 2281 23" _null_ _null_ _null_ _null_ _null_ rangesel _null_ _null_ _null_ ));
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index ea9dd17..24f4916 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -370,6 +370,8 @@ extern Jsonb *JsonbValueToJsonb(JsonbValue *val);
 extern bool JsonbDeepContains(JsonbIterator **val,
 				  JsonbIterator **mContained);
 extern void JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash);
+extern void JsonbHashScalarValueExtended(const JsonbValue *scalarVal,
+							 uint64 *hash, uint64 seed);
 
 /* jsonb.c support functions */
 extern char *JsonbToCString(StringInfo out, JsonbContainer *in,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index c12631d..b4f7592 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -56,6 +56,7 @@ typedef struct TypeCacheEntry
 	Oid			gt_opr;			/* the greater-than operator */
 	Oid			cmp_proc;		/* the btree comparison function */
 	Oid			hash_proc;		/* the hash calculation function */
+	Oid			hash_extended_proc; /* the extended hash calculation function */
 
 	/*
 	 * Pre-set-up fmgr call info for the equality operator, the btree
@@ -67,6 +68,7 @@ typedef struct TypeCacheEntry
 	FmgrInfo	eq_opr_finfo;
 	FmgrInfo	cmp_proc_finfo;
 	FmgrInfo	hash_proc_finfo;
+	FmgrInfo	hash_extended_proc_finfo;
 
 	/*
 	 * Tuple descriptor if it's a composite type (row type).  NULL if not
@@ -120,6 +122,8 @@ typedef struct TypeCacheEntry
 #define TYPECACHE_HASH_OPFAMILY		0x0400
 #define TYPECACHE_RANGE_INFO		0x0800
 #define TYPECACHE_DOMAIN_INFO		0x1000
+#define TYPECACHE_HASH_EXTENDED_PROC		0x2000
+#define TYPECACHE_HASH_EXTENDED_PROC_FINFO	0x4000
 
 /*
  * Callers wishing to maintain a long-lived reference to a domain's constraint
diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out
index 9f6ad4d..767c09b 100644
--- a/src/test/regress/expected/alter_generic.out
+++ b/src/test/regress/expected/alter_generic.out
@@ -421,7 +421,7 @@ BEGIN TRANSACTION;
 CREATE OPERATOR FAMILY alt_opf13 USING hash;
 CREATE FUNCTION fn_opf13  (int4) RETURNS BIGINT AS 'SELECT NULL::BIGINT;' LANGUAGE SQL;
 ALTER OPERATOR FAMILY alt_opf13 USING hash ADD FUNCTION 1 fn_opf13(int4);
-ERROR:  hash procedures must return integer
+ERROR:  hash procedure 1 must return integer
 DROP OPERATOR FAMILY alt_opf13 USING hash;
 ERROR:  current transaction is aborted, commands ignored until end of transaction block
 ROLLBACK;
@@ -439,7 +439,7 @@ BEGIN TRANSACTION;
 CREATE OPERATOR FAMILY alt_opf15 USING hash;
 CREATE FUNCTION fn_opf15 (int4, int2) RETURNS BIGINT AS 'SELECT NULL::BIGINT;' LANGUAGE SQL;
 ALTER OPERATOR FAMILY alt_opf15 USING hash ADD FUNCTION 1 fn_opf15(int4, int2);
-ERROR:  hash procedures must have one argument
+ERROR:  hash procedure 1 must have one argument
 DROP OPERATOR FAMILY alt_opf15 USING hash;
 ERROR:  current transaction is aborted, commands ignored until end of transaction block
 ROLLBACK;
-- 
2.6.2

