]> granicus.if.org Git - postgresql/commitdiff
Introduce 64-bit hash functions with a 64-bit seed.
authorRobert Haas <rhaas@postgresql.org>
Fri, 1 Sep 2017 02:21:21 +0000 (22:21 -0400)
committerRobert Haas <rhaas@postgresql.org>
Fri, 1 Sep 2017 02:21:21 +0000 (22:21 -0400)
This will be useful for hash partitioning, which needs a way to seed
the hash functions to avoid problems such as a hash index on a hash
partitioned table clumping all values into a small portion of the
bucket space; it's also useful for anything that wants a 64-bit hash
value rather than a 32-bit hash value.

Just in case somebody wants a 64-bit hash value that is compatible
with the existing 32-bit hash values, make the low 32-bits of the
64-bit hash value match the 32-bit hash value when the seed is 0.

Robert Haas and Amul Sul

Discussion: http://postgr.es/m/CA+Tgmoafx2yoJuhCQQOL5CocEi-w_uG4S2xT0EtgiJnPGcHW3g@mail.gmail.com

33 files changed:
doc/src/sgml/xindex.sgml
src/backend/access/hash/hashfunc.c
src/backend/access/hash/hashpage.c
src/backend/access/hash/hashutil.c
src/backend/access/hash/hashvalidate.c
src/backend/commands/opclasscmds.c
src/backend/utils/adt/acl.c
src/backend/utils/adt/arrayfuncs.c
src/backend/utils/adt/date.c
src/backend/utils/adt/jsonb_op.c
src/backend/utils/adt/jsonb_util.c
src/backend/utils/adt/mac.c
src/backend/utils/adt/mac8.c
src/backend/utils/adt/network.c
src/backend/utils/adt/numeric.c
src/backend/utils/adt/pg_lsn.c
src/backend/utils/adt/rangetypes.c
src/backend/utils/adt/timestamp.c
src/backend/utils/adt/uuid.c
src/backend/utils/adt/varchar.c
src/backend/utils/cache/lsyscache.c
src/backend/utils/cache/typcache.c
src/include/access/hash.h
src/include/catalog/catversion.h
src/include/catalog/pg_amproc.h
src/include/catalog/pg_proc.h
src/include/fmgr.h
src/include/utils/jsonb.h
src/include/utils/typcache.h
src/test/regress/expected/alter_generic.out
src/test/regress/expected/hash_func.out [new file with mode: 0644]
src/test/regress/parallel_schedule
src/test/regress/sql/hash_func.sql [new file with mode: 0644]

index 333a36c4562f1876c86630df465e691ea0507480..745b4d5619a2d4f72a51dc74bc79f1e291f5f33b 100644 (file)
    </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>
 
      </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; if
+         the salt is 0, the low 32 bits will match the value that would
+         have been computed by function 1
+       </entry>
+       <entry>2</entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
index a127f3f8b1b71c7b48351c7304557068924e7353..413e6b6ca3f84d80490caf1aca78cd88278c99b3 100644 (file)
@@ -46,18 +46,36 @@ hashchar(PG_FUNCTION_ARGS)
        return hash_uint32((int32) PG_GETARG_CHAR(0));
 }
 
+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)
 {
@@ -78,18 +96,43 @@ hashint8(PG_FUNCTION_ARGS)
        return hash_uint32(lohalf);
 }
 
+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)
 {
@@ -116,6 +159,21 @@ hashfloat4(PG_FUNCTION_ARGS)
        return hash_any((unsigned char *) &key8, sizeof(key8));
 }
 
+Datum
+hashfloat4extended(PG_FUNCTION_ARGS)
+{
+       float4          key = PG_GETARG_FLOAT4(0);
+       uint64          seed = PG_GETARG_INT64(1);
+       float8          key8;
+
+       /* Same approach as hashfloat4 */
+       if (key == (float4) 0)
+               PG_RETURN_UINT64(seed);
+       key8 = key;
+
+       return hash_any_extended((unsigned char *) &key8, sizeof(key8), seed);
+}
+
 Datum
 hashfloat8(PG_FUNCTION_ARGS)
 {
@@ -132,6 +190,19 @@ hashfloat8(PG_FUNCTION_ARGS)
        return hash_any((unsigned char *) &key, sizeof(key));
 }
 
+Datum
+hashfloat8extended(PG_FUNCTION_ARGS)
+{
+       float8          key = PG_GETARG_FLOAT8(0);
+       uint64          seed = PG_GETARG_INT64(1);
+
+       /* Same approach as hashfloat8 */
+       if (key == (float8) 0)
+               PG_RETURN_UINT64(seed);
+
+       return hash_any_extended((unsigned char *) &key, sizeof(key), seed);
+}
+
 Datum
 hashoidvector(PG_FUNCTION_ARGS)
 {
@@ -140,6 +211,16 @@ hashoidvector(PG_FUNCTION_ARGS)
        return hash_any((unsigned char *) key->values, key->dim1 * sizeof(Oid));
 }
 
+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)
 {
@@ -148,6 +229,15 @@ hashname(PG_FUNCTION_ARGS)
        return hash_any((unsigned char *) key, strlen(key));
 }
 
+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)
 {
@@ -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 12-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 */
+       PG_RETURN_UINT64(((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 */
+       PG_RETURN_UINT64(((uint64) b << 32) | c);
+}
index 7b2906b0ca90476042706192133f3d67f5193c44..05798419fc3aa59b1357d70bed0474917bbe2ddc 100644 (file)
@@ -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
index 9b803af7c2554317d8496334882184242670e264..869cbc1081e699059cceca2d91b1fd24b767b46b 100644 (file)
@@ -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];
 
index 30b29cb100c87414ce7ddbd7cc39982f0ebb2a0c..8b633c273a5d0ea7f790562e8bc01b657e7a1bd5 100644 (file)
@@ -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;
 }
index a31b1acb9c6c4ac0fdfdd4469d0cf99be632583c..d23e6d6f250d12b6017257f44bfb2faddd3325e8 100644 (file)
@@ -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
index 2efb6c94e111cd97370a8f5ea2bae214a51bc09c..0c26e44d820507221e16a75f24dc05e73c72ccba 100644 (file)
@@ -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));
 }
 
+/*
+ * 64-bit hash function for aclitem.
+ *
+ * Similar to hash_aclitem, but accepts a seed and returns a uint64 value.
+ */
+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
index 34dadd6e19efd05e4192370b0f17dd09ff465093..522af7affc6e301bc7c594c6fd1b1db37ea45a5f 100644 (file)
@@ -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);
+
+       PG_RETURN_UINT64(result);
+}
+
 
 /*-----------------------------------------------------------------------------
  * array overlap/containment comparisons
index 7d89d7943818c51409f04c49405088dc44f3c610..34c0b52d5834d1b929c9b3540d0d64e2b6b87ff2 100644 (file)
@@ -1508,6 +1508,12 @@ time_hash(PG_FUNCTION_ARGS)
        return hashint8(fcinfo);
 }
 
+Datum
+time_hash_extended(PG_FUNCTION_ARGS)
+{
+       return hashint8extended(fcinfo);
+}
+
 Datum
 time_larger(PG_FUNCTION_ARGS)
 {
@@ -2213,6 +2219,21 @@ timetz_hash(PG_FUNCTION_ARGS)
        PG_RETURN_UINT32(thash);
 }
 
+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));
+       PG_RETURN_UINT64(thash);
+}
+
 Datum
 timetz_larger(PG_FUNCTION_ARGS)
 {
index d4c490e948a349065d79b9b40df33f4a4c615c94..c4a7dc3f13a5fdb2b24a61430182cf9f75c6da1c 100644 (file)
@@ -291,3 +291,46 @@ 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);
+       uint64          seed = PG_GETARG_INT64(1);
+       JsonbIterator *it;
+       JsonbValue      v;
+       JsonbIteratorToken r;
+       uint64          hash = 0;
+
+       if (JB_ROOT_COUNT(jb) == 0)
+               PG_RETURN_UINT64(seed);
+
+       it = JsonbIteratorInit(&jb->root);
+
+       while ((r = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
+       {
+               switch (r)
+               {
+                       /* Rotation is left to JsonbHashScalarValueExtended() */
+                       case WJB_BEGIN_ARRAY:
+                               hash ^= ((UINT64CONST(JB_FARRAY) << 32) | UINT64CONST(JB_FARRAY));
+                               break;
+                       case WJB_BEGIN_OBJECT:
+                               hash ^= ((UINT64CONST(JB_FOBJECT) << 32) | UINT64CONST(JB_FOBJECT));
+                               break;
+                       case WJB_KEY:
+                       case WJB_VALUE:
+                       case WJB_ELEM:
+                               JsonbHashScalarValueExtended(&v, &hash, seed);
+                               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);
+       PG_RETURN_UINT64(hash);
+}
index 4850569bb5e6204001d3a736023aa278abfffcbd..d425f3240385cac28518a60cf44e5ff3d2bc3280 100644 (file)
@@ -1249,6 +1249,49 @@ JsonbHashScalarValue(const JsonbValue *scalarVal, uint32 *hash)
        *hash ^= tmp;
 }
 
+/*
+ * 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 = seed + 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),
+                                                                                                        UInt64GetDatum(seed)));
+                       break;
+               case jbvBool:
+                       if (seed)
+                               tmp = DatumGetUInt64(DirectFunctionCall2(hashcharextended,
+                                                                                                                BoolGetDatum(scalarVal->val.boolean),
+                                                                                                                UInt64GetDatum(seed)));
+                       else
+                               tmp = scalarVal->val.boolean ? 0x02 : 0x04;
+
+                       break;
+               default:
+                       elog(ERROR, "invalid jsonb scalar type");
+                       break;
+       }
+
+       *hash = ROTATE_HIGH_AND_LOW_32BITS(*hash);
+       *hash ^= tmp;
+}
+
 /*
  * Are two scalar JsonbValues of the same type a and b equal?
  */
index d1c20c30865b385ff05ad4965393f8a59dc21101..60521cc21ff36c79508af48a81d6fa8727a5d178 100644 (file)
@@ -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.
  */
index 482d1fb5bf30039c4398f41fb45e75825480c303..0410b9888af4e8dcddfd2790afedd86e0f654426 100644 (file)
@@ -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.
  */
index 5573c340973832d4b2fdcce4f54cc80d8dfbf6b7..ec4ac20bb7bc2f9d430c4963c7ee959fdc68e6ad 100644 (file)
@@ -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.
  */
index 3e5614ece30d4f012e554084e83ab387ef10bd03..22d5898927cec5dc8700d9a76ae72df45b1a8e62 100644 (file)
@@ -2230,6 +2230,66 @@ 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);
+       uint64          seed = PG_GETARG_INT64(1);
+       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))
+               PG_RETURN_UINT64(seed);
+
+       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)
+               PG_RETURN_UINT64(seed - 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),
+                                                                  seed);
+
+       result = digit_hash ^ weight;
+
+       PG_RETURN_DATUM(result);
+}
+
 
 /* ----------------------------------------------------------------------
  *
index aefbb876808a619b726e7eeaf3bc7649f6113ce2..7ad30a260a9cc45cd8225e67c7ca2aa88022572f 100644 (file)
@@ -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.
index 09a4f14a179c4c2eeccbbb284fe1d4b65ffc8ed4..d7ba271317a19d8b8a2e420d084e174817a0007c 100644 (file)
@@ -1280,6 +1280,69 @@ hash_range(PG_FUNCTION_ARGS)
        PG_RETURN_INT32(result);
 }
 
+/*
+ * 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;
+
+       /* Merge hashes of flags and bounds */
+       result = hash_uint32_extended((uint32) flags, seed);
+       result ^= lower_hash;
+       result = ROTATE_HIGH_AND_LOW_32BITS(result);
+       result ^= upper_hash;
+
+       PG_RETURN_UINT64(result);
+}
+
 /*
  *----------------------------------------------------------
  * CANONICAL FUNCTIONS
index 6fa126d295ba8b82edd6606865ff585248794e6b..b11d452fc8a024a9dcfab74e2ace6f5985637d5c 100644 (file)
@@ -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
index 5f15c8e6196c984e0ddec5044a88a101782a395c..f73c695878d1bb3e6e061e48ba450e5188221759 100644 (file)
@@ -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));
+}
index cbc62b00be26be3622d11dd09639a857df4dd7fa..2df6f2ccb002f6a46517df0999ca6f522a9e7ebc 100644 (file)
@@ -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
index 82763f8013dd160307a01c21877285e648f184d2..b7a14dc87e14801029c82e690087c43f4843c481 100644 (file)
@@ -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 */
index 691d4987b160889ae743e6fce08af1ef18429e6c..2e633f08c515166d206a0feabd5227ac39f0ca0f 100644 (file)
@@ -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
index 72fce3038c02381c1addfe59ccfb51a193ca70e1..c06dcb214f0812e26e11fd8171dc6780c0d9269b 100644 (file)
@@ -38,6 +38,17 @@ typedef uint32 Bucket;
 #define BUCKET_TO_BLKNO(metap,B) \
                ((BlockNumber) ((B) + ((B) ? (metap)->hashm_spares[_hash_spareindex((B)+1)-1] : 0)) + 1)
 
+/*
+ * Rotate the high 32 bits and the low 32 bits separately.  The standard
+ * hash function sometimes rotates the low 32 bits by one bit when
+ * combining elements.  We want extended hash functions to be compatible with
+ * that algorithm when the seed is 0, so we can't just do a normal rotation.
+ * This works, though.
+ */
+#define ROTATE_HIGH_AND_LOW_32BITS(v) \
+       ((((v) << 1) & UINT64CONST(0xfffffffefffffffe)) | \
+       (((v) >> 31) & UINT64CONST(0x100000001)))
+
 /*
  * Special space for hash index pages.
  *
@@ -289,12 +300,20 @@ 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.
+ *
+ * When the salt is 0, the low 32 bits of the value returned by the extended
+ * hash procedure should match the value that would have been returned by the
+ * standard hash procedure.
  */
-#define HASHPROC               1
-#define HASHNProcs             1
+#define HASHSTANDARD_PROC              1
+#define HASHEXTENDED_PROC              2
+#define HASHNProcs                             2
 
 
 /* public routines */
@@ -322,7 +341,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 */
 
index 0dafd6bf2a4e7a3b7dc4f755f81fef544cd4498c..6525da970d2c1c38d1fb7bdd9c2fcd911293b7e4 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201707211
+#define CATALOG_VERSION_NO     201708311
 
 #endif
index 7d245b12710ee0bd289cb04791414cbe9092e921..fb6a829c90ce74a0a5084d229fdf4bc12e0f9860 100644 (file)
@@ -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 */
index 8b33b4e0ea70a00412923f03327f9ff3bf6b129f..d820b56aa1b72da43deb9e0a0e53e373a58b66e8 100644 (file)
@@ -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_ ));
index 0216965bfc1ebed329eac84259d365dbe011918f..b604a5c16245f0f4d3cccd9de899c270df3b854e 100644 (file)
@@ -325,6 +325,7 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena *datum);
 #define PG_RETURN_FLOAT4(x)  return Float4GetDatum(x)
 #define PG_RETURN_FLOAT8(x)  return Float8GetDatum(x)
 #define PG_RETURN_INT64(x)      return Int64GetDatum(x)
+#define PG_RETURN_UINT64(x)  return UInt64GetDatum(x)
 /* RETURN macros for other pass-by-ref types will typically look like this: */
 #define PG_RETURN_BYTEA_P(x)   PG_RETURN_POINTER(x)
 #define PG_RETURN_TEXT_P(x)    PG_RETURN_POINTER(x)
index ea9dd17540dddf7bd179f603cef7c8e7fb1e5bae..24f491663bb2ec7ec0078a5263ccc247316746a5 100644 (file)
@@ -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,
index c12631dafe23dd8cadf03eb9faa298a2aafdcffc..b4f75921625bd7343e713fb6273a517ee1c1afc6 100644 (file)
@@ -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
index 9f6ad4de33be71fc1432456051c7546288b130cb..767c09bec5e114933a6ce9e5e37ae212891dcb2a 100644 (file)
@@ -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;
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
new file mode 100644 (file)
index 0000000..da0948e
--- /dev/null
@@ -0,0 +1,300 @@
+--
+-- Test hash functions
+--
+-- When the salt is 0, the extended hash function should produce a result
+-- whose low 32 bits match the standard hash function.  When the salt is
+-- not 0, we should get a different result.
+--
+SELECT v as value, hashint2(v)::bit(32) as standard,
+       hashint2extended(v, 0)::bit(32) as extended0,
+       hashint2extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (0::int2), (1::int2), (17::int2), (42::int2)) x(v)
+WHERE  hashint2(v)::bit(32) != hashint2extended(v, 0)::bit(32)
+       OR hashint2(v)::bit(32) = hashint2extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, hashint4(v)::bit(32) as standard,
+          hashint4extended(v, 0)::bit(32) as extended0,
+          hashint4extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (0), (1), (17), (42), (550273), (207112489)) x(v)
+WHERE  hashint4(v)::bit(32) != hashint4extended(v, 0)::bit(32)
+       OR hashint4(v)::bit(32) = hashint4extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, hashint8(v)::bit(32) as standard,
+          hashint8extended(v, 0)::bit(32) as extended0,
+          hashint8extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (0), (1), (17), (42), (550273), (207112489)) x(v)
+WHERE  hashint8(v)::bit(32) != hashint8extended(v, 0)::bit(32)
+       OR hashint8(v)::bit(32) = hashint8extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, hashfloat4(v)::bit(32) as standard,
+          hashfloat4extended(v, 0)::bit(32) as extended0,
+          hashfloat4extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (0), (1), (17), (42), (550273), (207112489)) x(v)
+WHERE  hashfloat4(v)::bit(32) != hashfloat4extended(v, 0)::bit(32)
+       OR hashfloat4(v)::bit(32) = hashfloat4extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, hashfloat8(v)::bit(32) as standard,
+          hashfloat8extended(v, 0)::bit(32) as extended0,
+          hashfloat8extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (0), (1), (17), (42), (550273), (207112489)) x(v)
+WHERE  hashfloat8(v)::bit(32) != hashfloat8extended(v, 0)::bit(32)
+       OR hashfloat8(v)::bit(32) = hashfloat8extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, hashoid(v)::bit(32) as standard,
+          hashoidextended(v, 0)::bit(32) as extended0,
+          hashoidextended(v, 1)::bit(32) as extended1
+FROM   (VALUES (0), (1), (17), (42), (550273), (207112489)) x(v)
+WHERE  hashoid(v)::bit(32) != hashoidextended(v, 0)::bit(32)
+       OR hashoid(v)::bit(32) = hashoidextended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, hashchar(v)::bit(32) as standard,
+          hashcharextended(v, 0)::bit(32) as extended0,
+          hashcharextended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::"char"), ('1'), ('x'), ('X'), ('p'), ('N')) x(v)
+WHERE  hashchar(v)::bit(32) != hashcharextended(v, 0)::bit(32)
+       OR hashchar(v)::bit(32) = hashcharextended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, hashname(v)::bit(32) as standard,
+          hashnameextended(v, 0)::bit(32) as extended0,
+          hashnameextended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL), ('PostgreSQL'), ('eIpUEtqmY89'), ('AXKEJBTK'),
+       ('muop28x03'), ('yi3nm0d73')) x(v)
+WHERE  hashname(v)::bit(32) != hashnameextended(v, 0)::bit(32)
+       OR hashname(v)::bit(32) = hashnameextended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, hashtext(v)::bit(32) as standard,
+          hashtextextended(v, 0)::bit(32) as extended0,
+          hashtextextended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL), ('PostgreSQL'), ('eIpUEtqmY89'), ('AXKEJBTK'),
+       ('muop28x03'), ('yi3nm0d73')) x(v)
+WHERE  hashtext(v)::bit(32) != hashtextextended(v, 0)::bit(32)
+       OR hashtext(v)::bit(32) = hashtextextended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, hashoidvector(v)::bit(32) as standard,
+          hashoidvectorextended(v, 0)::bit(32) as extended0,
+          hashoidvectorextended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::oidvector), ('0 1 2 3 4'), ('17 18 19 20'),
+        ('42 43 42 45'), ('550273 550273 570274'),
+        ('207112489 207112499 21512 2155 372325 1363252')) x(v)
+WHERE  hashoidvector(v)::bit(32) != hashoidvectorextended(v, 0)::bit(32)
+       OR hashoidvector(v)::bit(32) = hashoidvectorextended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, hash_aclitem(v)::bit(32) as standard,
+          hash_aclitem_extended(v, 0)::bit(32) as extended0,
+          hash_aclitem_extended(v, 1)::bit(32) as extended1
+FROM   (SELECT DISTINCT(relacl[1]) FROM pg_class LIMIT 10) x(v)
+WHERE  hash_aclitem(v)::bit(32) != hash_aclitem_extended(v, 0)::bit(32)
+       OR hash_aclitem(v)::bit(32) = hash_aclitem_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, hashmacaddr(v)::bit(32) as standard,
+          hashmacaddrextended(v, 0)::bit(32) as extended0,
+          hashmacaddrextended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::macaddr), ('08:00:2b:01:02:04'), ('08:00:2b:01:02:04'),
+               ('e2:7f:51:3e:70:49'), ('d6:a9:4a:78:1c:d5'),
+        ('ea:29:b1:5e:1f:a5')) x(v)
+WHERE  hashmacaddr(v)::bit(32) != hashmacaddrextended(v, 0)::bit(32)
+       OR hashmacaddr(v)::bit(32) = hashmacaddrextended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, hashinet(v)::bit(32) as standard,
+          hashinetextended(v, 0)::bit(32) as extended0,
+          hashinetextended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::inet), ('192.168.100.128/25'), ('192.168.100.0/8'),
+               ('172.168.10.126/16'), ('172.18.103.126/24'), ('192.188.13.16/32')) x(v)
+WHERE  hashinet(v)::bit(32) != hashinetextended(v, 0)::bit(32)
+       OR hashinet(v)::bit(32) = hashinetextended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, hash_numeric(v)::bit(32) as standard,
+          hash_numeric_extended(v, 0)::bit(32) as extended0,
+          hash_numeric_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (0), (1.149484958), (17.149484958), (42.149484958),
+        (149484958.550273), (2071124898672)) x(v)
+WHERE  hash_numeric(v)::bit(32) != hash_numeric_extended(v, 0)::bit(32)
+       OR hash_numeric(v)::bit(32) = hash_numeric_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, hashmacaddr8(v)::bit(32) as standard,
+          hashmacaddr8extended(v, 0)::bit(32) as extended0,
+          hashmacaddr8extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::macaddr8), ('08:00:2b:01:02:04:36:49'),
+        ('08:00:2b:01:02:04:f0:e8'), ('e2:7f:51:3e:70:49:16:29'),
+        ('d6:a9:4a:78:1c:d5:47:32'), ('ea:29:b1:5e:1f:a5')) x(v)
+WHERE  hashmacaddr8(v)::bit(32) != hashmacaddr8extended(v, 0)::bit(32)
+       OR hashmacaddr8(v)::bit(32) = hashmacaddr8extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, hash_array(v)::bit(32) as standard,
+          hash_array_extended(v, 0)::bit(32) as extended0,
+          hash_array_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES ('{0}'::int4[]), ('{0,1,2,3,4}'), ('{17,18,19,20}'),
+        ('{42,34,65,98}'), ('{550273,590027, 870273}'),
+        ('{207112489, 807112489}')) x(v)
+WHERE  hash_array(v)::bit(32) != hash_array_extended(v, 0)::bit(32)
+       OR hash_array(v)::bit(32) = hash_array_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, hashbpchar(v)::bit(32) as standard,
+          hashbpcharextended(v, 0)::bit(32) as extended0,
+          hashbpcharextended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL), ('PostgreSQL'), ('eIpUEtqmY89'), ('AXKEJBTK'),
+       ('muop28x03'), ('yi3nm0d73')) x(v)
+WHERE  hashbpchar(v)::bit(32) != hashbpcharextended(v, 0)::bit(32)
+       OR hashbpchar(v)::bit(32) = hashbpcharextended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, time_hash(v)::bit(32) as standard,
+          time_hash_extended(v, 0)::bit(32) as extended0,
+          time_hash_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::time), ('11:09:59'), ('1:09:59'), ('11:59:59'),
+        ('7:9:59'), ('5:15:59')) x(v)
+WHERE  time_hash(v)::bit(32) != time_hash_extended(v, 0)::bit(32)
+       OR time_hash(v)::bit(32) = time_hash_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, timetz_hash(v)::bit(32) as standard,
+          timetz_hash_extended(v, 0)::bit(32) as extended0,
+          timetz_hash_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::timetz), ('00:11:52.518762-07'), ('00:11:52.51762-08'),
+               ('00:11:52.62-01'), ('00:11:52.62+01'), ('11:59:59+04')) x(v)
+WHERE  timetz_hash(v)::bit(32) != timetz_hash_extended(v, 0)::bit(32)
+       OR timetz_hash(v)::bit(32) = timetz_hash_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, interval_hash(v)::bit(32) as standard,
+          interval_hash_extended(v, 0)::bit(32) as extended0,
+          interval_hash_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::interval),
+        ('5 month 7 day 46 minutes'), ('1 year 7 day 46 minutes'),
+               ('1 year 7 month 20 day 46 minutes'), ('5 month'),
+               ('17 year 11 month 7 day 9 hours 46 minutes 5 seconds')) x(v)
+WHERE  interval_hash(v)::bit(32) != interval_hash_extended(v, 0)::bit(32)
+       OR interval_hash(v)::bit(32) = interval_hash_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, timestamp_hash(v)::bit(32) as standard,
+          timestamp_hash_extended(v, 0)::bit(32) as extended0,
+          timestamp_hash_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::timestamp), ('2017-08-22 00:09:59.518762'),
+        ('2015-08-20 00:11:52.51762-08'),
+               ('2017-05-22 00:11:52.62-01'),
+        ('2013-08-22 00:11:52.62+01'), ('2013-08-22 11:59:59+04')) x(v)
+WHERE  timestamp_hash(v)::bit(32) != timestamp_hash_extended(v, 0)::bit(32)
+       OR timestamp_hash(v)::bit(32) = timestamp_hash_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, uuid_hash(v)::bit(32) as standard,
+          uuid_hash_extended(v, 0)::bit(32) as extended0,
+          uuid_hash_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::uuid), ('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'),
+               ('5a9ba4ac-8d6f-11e7-bb31-be2e44b06b34'),
+        ('99c6705c-d939-461c-a3c9-1690ad64ed7b'),
+               ('7deed3ca-8d6f-11e7-bb31-be2e44b06b34'),
+        ('9ad46d4f-6f2a-4edd-aadb-745993928e1e')) x(v)
+WHERE  uuid_hash(v)::bit(32) != uuid_hash_extended(v, 0)::bit(32)
+       OR uuid_hash(v)::bit(32) = uuid_hash_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, pg_lsn_hash(v)::bit(32) as standard,
+          pg_lsn_hash_extended(v, 0)::bit(32) as extended0,
+          pg_lsn_hash_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::pg_lsn), ('16/B374D84'), ('30/B374D84'),
+               ('255/B374D84'), ('25/B379D90'), ('900/F37FD90')) x(v)
+WHERE  pg_lsn_hash(v)::bit(32) != pg_lsn_hash_extended(v, 0)::bit(32)
+       OR pg_lsn_hash(v)::bit(32) = pg_lsn_hash_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
+SELECT v as value, hashenum(v)::bit(32) as standard,
+          hashenumextended(v, 0)::bit(32) as extended0,
+          hashenumextended(v, 1)::bit(32) as extended1
+FROM   (VALUES ('sad'::mood), ('ok'), ('happy')) x(v)
+WHERE  hashenum(v)::bit(32) != hashenumextended(v, 0)::bit(32)
+       OR hashenum(v)::bit(32) = hashenumextended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+DROP TYPE mood;
+SELECT v as value, jsonb_hash(v)::bit(32) as standard,
+          jsonb_hash_extended(v, 0)::bit(32) as extended0,
+          jsonb_hash_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::jsonb),
+    ('{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'),
+       ('{"foo": [true, "bar"], "tags": {"e": 1, "f": null}}'),
+    ('{"g": {"h": "value"}}')) x(v)
+WHERE  jsonb_hash(v)::bit(32) != jsonb_hash_extended(v, 0)::bit(32)
+       OR jsonb_hash(v)::bit(32) = jsonb_hash_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
+SELECT v as value, hash_range(v)::bit(32) as standard,
+          hash_range_extended(v, 0)::bit(32) as extended0,
+          hash_range_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (int4range(10, 20)), (int4range(23, 43)),
+         (int4range(5675, 550273)),
+                (int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
+WHERE  hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
+       OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
index eefdeeacaef6fef26265705c33c4c81c1d04a260..2fd3f2b1b1c049b8e169a64d93be911abd2b93b6 100644 (file)
@@ -60,7 +60,7 @@ test: create_index create_view
 # ----------
 # Another group of parallel tests
 # ----------
-test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes create_am
+test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views rolenames roleattributes create_am hash_func
 
 # ----------
 # sanity_check does a vacuum, affecting the sort order of SELECT *
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
new file mode 100644 (file)
index 0000000..b7ce8b2
--- /dev/null
@@ -0,0 +1,222 @@
+--
+-- Test hash functions
+--
+-- When the salt is 0, the extended hash function should produce a result
+-- whose low 32 bits match the standard hash function.  When the salt is
+-- not 0, we should get a different result.
+--
+
+SELECT v as value, hashint2(v)::bit(32) as standard,
+       hashint2extended(v, 0)::bit(32) as extended0,
+       hashint2extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (0::int2), (1::int2), (17::int2), (42::int2)) x(v)
+WHERE  hashint2(v)::bit(32) != hashint2extended(v, 0)::bit(32)
+       OR hashint2(v)::bit(32) = hashint2extended(v, 1)::bit(32);
+
+SELECT v as value, hashint4(v)::bit(32) as standard,
+          hashint4extended(v, 0)::bit(32) as extended0,
+          hashint4extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (0), (1), (17), (42), (550273), (207112489)) x(v)
+WHERE  hashint4(v)::bit(32) != hashint4extended(v, 0)::bit(32)
+       OR hashint4(v)::bit(32) = hashint4extended(v, 1)::bit(32);
+
+SELECT v as value, hashint8(v)::bit(32) as standard,
+          hashint8extended(v, 0)::bit(32) as extended0,
+          hashint8extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (0), (1), (17), (42), (550273), (207112489)) x(v)
+WHERE  hashint8(v)::bit(32) != hashint8extended(v, 0)::bit(32)
+       OR hashint8(v)::bit(32) = hashint8extended(v, 1)::bit(32);
+
+SELECT v as value, hashfloat4(v)::bit(32) as standard,
+          hashfloat4extended(v, 0)::bit(32) as extended0,
+          hashfloat4extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (0), (1), (17), (42), (550273), (207112489)) x(v)
+WHERE  hashfloat4(v)::bit(32) != hashfloat4extended(v, 0)::bit(32)
+       OR hashfloat4(v)::bit(32) = hashfloat4extended(v, 1)::bit(32);
+
+SELECT v as value, hashfloat8(v)::bit(32) as standard,
+          hashfloat8extended(v, 0)::bit(32) as extended0,
+          hashfloat8extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (0), (1), (17), (42), (550273), (207112489)) x(v)
+WHERE  hashfloat8(v)::bit(32) != hashfloat8extended(v, 0)::bit(32)
+       OR hashfloat8(v)::bit(32) = hashfloat8extended(v, 1)::bit(32);
+
+SELECT v as value, hashoid(v)::bit(32) as standard,
+          hashoidextended(v, 0)::bit(32) as extended0,
+          hashoidextended(v, 1)::bit(32) as extended1
+FROM   (VALUES (0), (1), (17), (42), (550273), (207112489)) x(v)
+WHERE  hashoid(v)::bit(32) != hashoidextended(v, 0)::bit(32)
+       OR hashoid(v)::bit(32) = hashoidextended(v, 1)::bit(32);
+
+SELECT v as value, hashchar(v)::bit(32) as standard,
+          hashcharextended(v, 0)::bit(32) as extended0,
+          hashcharextended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::"char"), ('1'), ('x'), ('X'), ('p'), ('N')) x(v)
+WHERE  hashchar(v)::bit(32) != hashcharextended(v, 0)::bit(32)
+       OR hashchar(v)::bit(32) = hashcharextended(v, 1)::bit(32);
+
+SELECT v as value, hashname(v)::bit(32) as standard,
+          hashnameextended(v, 0)::bit(32) as extended0,
+          hashnameextended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL), ('PostgreSQL'), ('eIpUEtqmY89'), ('AXKEJBTK'),
+       ('muop28x03'), ('yi3nm0d73')) x(v)
+WHERE  hashname(v)::bit(32) != hashnameextended(v, 0)::bit(32)
+       OR hashname(v)::bit(32) = hashnameextended(v, 1)::bit(32);
+
+SELECT v as value, hashtext(v)::bit(32) as standard,
+          hashtextextended(v, 0)::bit(32) as extended0,
+          hashtextextended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL), ('PostgreSQL'), ('eIpUEtqmY89'), ('AXKEJBTK'),
+       ('muop28x03'), ('yi3nm0d73')) x(v)
+WHERE  hashtext(v)::bit(32) != hashtextextended(v, 0)::bit(32)
+       OR hashtext(v)::bit(32) = hashtextextended(v, 1)::bit(32);
+
+SELECT v as value, hashoidvector(v)::bit(32) as standard,
+          hashoidvectorextended(v, 0)::bit(32) as extended0,
+          hashoidvectorextended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::oidvector), ('0 1 2 3 4'), ('17 18 19 20'),
+        ('42 43 42 45'), ('550273 550273 570274'),
+        ('207112489 207112499 21512 2155 372325 1363252')) x(v)
+WHERE  hashoidvector(v)::bit(32) != hashoidvectorextended(v, 0)::bit(32)
+       OR hashoidvector(v)::bit(32) = hashoidvectorextended(v, 1)::bit(32);
+
+SELECT v as value, hash_aclitem(v)::bit(32) as standard,
+          hash_aclitem_extended(v, 0)::bit(32) as extended0,
+          hash_aclitem_extended(v, 1)::bit(32) as extended1
+FROM   (SELECT DISTINCT(relacl[1]) FROM pg_class LIMIT 10) x(v)
+WHERE  hash_aclitem(v)::bit(32) != hash_aclitem_extended(v, 0)::bit(32)
+       OR hash_aclitem(v)::bit(32) = hash_aclitem_extended(v, 1)::bit(32);
+
+SELECT v as value, hashmacaddr(v)::bit(32) as standard,
+          hashmacaddrextended(v, 0)::bit(32) as extended0,
+          hashmacaddrextended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::macaddr), ('08:00:2b:01:02:04'), ('08:00:2b:01:02:04'),
+               ('e2:7f:51:3e:70:49'), ('d6:a9:4a:78:1c:d5'),
+        ('ea:29:b1:5e:1f:a5')) x(v)
+WHERE  hashmacaddr(v)::bit(32) != hashmacaddrextended(v, 0)::bit(32)
+       OR hashmacaddr(v)::bit(32) = hashmacaddrextended(v, 1)::bit(32);
+
+SELECT v as value, hashinet(v)::bit(32) as standard,
+          hashinetextended(v, 0)::bit(32) as extended0,
+          hashinetextended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::inet), ('192.168.100.128/25'), ('192.168.100.0/8'),
+               ('172.168.10.126/16'), ('172.18.103.126/24'), ('192.188.13.16/32')) x(v)
+WHERE  hashinet(v)::bit(32) != hashinetextended(v, 0)::bit(32)
+       OR hashinet(v)::bit(32) = hashinetextended(v, 1)::bit(32);
+
+SELECT v as value, hash_numeric(v)::bit(32) as standard,
+          hash_numeric_extended(v, 0)::bit(32) as extended0,
+          hash_numeric_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (0), (1.149484958), (17.149484958), (42.149484958),
+        (149484958.550273), (2071124898672)) x(v)
+WHERE  hash_numeric(v)::bit(32) != hash_numeric_extended(v, 0)::bit(32)
+       OR hash_numeric(v)::bit(32) = hash_numeric_extended(v, 1)::bit(32);
+
+SELECT v as value, hashmacaddr8(v)::bit(32) as standard,
+          hashmacaddr8extended(v, 0)::bit(32) as extended0,
+          hashmacaddr8extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::macaddr8), ('08:00:2b:01:02:04:36:49'),
+        ('08:00:2b:01:02:04:f0:e8'), ('e2:7f:51:3e:70:49:16:29'),
+        ('d6:a9:4a:78:1c:d5:47:32'), ('ea:29:b1:5e:1f:a5')) x(v)
+WHERE  hashmacaddr8(v)::bit(32) != hashmacaddr8extended(v, 0)::bit(32)
+       OR hashmacaddr8(v)::bit(32) = hashmacaddr8extended(v, 1)::bit(32);
+
+SELECT v as value, hash_array(v)::bit(32) as standard,
+          hash_array_extended(v, 0)::bit(32) as extended0,
+          hash_array_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES ('{0}'::int4[]), ('{0,1,2,3,4}'), ('{17,18,19,20}'),
+        ('{42,34,65,98}'), ('{550273,590027, 870273}'),
+        ('{207112489, 807112489}')) x(v)
+WHERE  hash_array(v)::bit(32) != hash_array_extended(v, 0)::bit(32)
+       OR hash_array(v)::bit(32) = hash_array_extended(v, 1)::bit(32);
+
+SELECT v as value, hashbpchar(v)::bit(32) as standard,
+          hashbpcharextended(v, 0)::bit(32) as extended0,
+          hashbpcharextended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL), ('PostgreSQL'), ('eIpUEtqmY89'), ('AXKEJBTK'),
+       ('muop28x03'), ('yi3nm0d73')) x(v)
+WHERE  hashbpchar(v)::bit(32) != hashbpcharextended(v, 0)::bit(32)
+       OR hashbpchar(v)::bit(32) = hashbpcharextended(v, 1)::bit(32);
+
+SELECT v as value, time_hash(v)::bit(32) as standard,
+          time_hash_extended(v, 0)::bit(32) as extended0,
+          time_hash_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::time), ('11:09:59'), ('1:09:59'), ('11:59:59'),
+        ('7:9:59'), ('5:15:59')) x(v)
+WHERE  time_hash(v)::bit(32) != time_hash_extended(v, 0)::bit(32)
+       OR time_hash(v)::bit(32) = time_hash_extended(v, 1)::bit(32);
+
+SELECT v as value, timetz_hash(v)::bit(32) as standard,
+          timetz_hash_extended(v, 0)::bit(32) as extended0,
+          timetz_hash_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::timetz), ('00:11:52.518762-07'), ('00:11:52.51762-08'),
+               ('00:11:52.62-01'), ('00:11:52.62+01'), ('11:59:59+04')) x(v)
+WHERE  timetz_hash(v)::bit(32) != timetz_hash_extended(v, 0)::bit(32)
+       OR timetz_hash(v)::bit(32) = timetz_hash_extended(v, 1)::bit(32);
+
+SELECT v as value, interval_hash(v)::bit(32) as standard,
+          interval_hash_extended(v, 0)::bit(32) as extended0,
+          interval_hash_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::interval),
+        ('5 month 7 day 46 minutes'), ('1 year 7 day 46 minutes'),
+               ('1 year 7 month 20 day 46 minutes'), ('5 month'),
+               ('17 year 11 month 7 day 9 hours 46 minutes 5 seconds')) x(v)
+WHERE  interval_hash(v)::bit(32) != interval_hash_extended(v, 0)::bit(32)
+       OR interval_hash(v)::bit(32) = interval_hash_extended(v, 1)::bit(32);
+
+SELECT v as value, timestamp_hash(v)::bit(32) as standard,
+          timestamp_hash_extended(v, 0)::bit(32) as extended0,
+          timestamp_hash_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::timestamp), ('2017-08-22 00:09:59.518762'),
+        ('2015-08-20 00:11:52.51762-08'),
+               ('2017-05-22 00:11:52.62-01'),
+        ('2013-08-22 00:11:52.62+01'), ('2013-08-22 11:59:59+04')) x(v)
+WHERE  timestamp_hash(v)::bit(32) != timestamp_hash_extended(v, 0)::bit(32)
+       OR timestamp_hash(v)::bit(32) = timestamp_hash_extended(v, 1)::bit(32);
+
+SELECT v as value, uuid_hash(v)::bit(32) as standard,
+          uuid_hash_extended(v, 0)::bit(32) as extended0,
+          uuid_hash_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::uuid), ('a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'),
+               ('5a9ba4ac-8d6f-11e7-bb31-be2e44b06b34'),
+        ('99c6705c-d939-461c-a3c9-1690ad64ed7b'),
+               ('7deed3ca-8d6f-11e7-bb31-be2e44b06b34'),
+        ('9ad46d4f-6f2a-4edd-aadb-745993928e1e')) x(v)
+WHERE  uuid_hash(v)::bit(32) != uuid_hash_extended(v, 0)::bit(32)
+       OR uuid_hash(v)::bit(32) = uuid_hash_extended(v, 1)::bit(32);
+
+SELECT v as value, pg_lsn_hash(v)::bit(32) as standard,
+          pg_lsn_hash_extended(v, 0)::bit(32) as extended0,
+          pg_lsn_hash_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::pg_lsn), ('16/B374D84'), ('30/B374D84'),
+               ('255/B374D84'), ('25/B379D90'), ('900/F37FD90')) x(v)
+WHERE  pg_lsn_hash(v)::bit(32) != pg_lsn_hash_extended(v, 0)::bit(32)
+       OR pg_lsn_hash(v)::bit(32) = pg_lsn_hash_extended(v, 1)::bit(32);
+
+CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
+SELECT v as value, hashenum(v)::bit(32) as standard,
+          hashenumextended(v, 0)::bit(32) as extended0,
+          hashenumextended(v, 1)::bit(32) as extended1
+FROM   (VALUES ('sad'::mood), ('ok'), ('happy')) x(v)
+WHERE  hashenum(v)::bit(32) != hashenumextended(v, 0)::bit(32)
+       OR hashenum(v)::bit(32) = hashenumextended(v, 1)::bit(32);
+DROP TYPE mood;
+
+SELECT v as value, jsonb_hash(v)::bit(32) as standard,
+          jsonb_hash_extended(v, 0)::bit(32) as extended0,
+          jsonb_hash_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (NULL::jsonb),
+    ('{"a": "aaa bbb ddd ccc", "b": ["eee fff ggg"], "c": {"d": "hhh iii"}}'),
+       ('{"foo": [true, "bar"], "tags": {"e": 1, "f": null}}'),
+    ('{"g": {"h": "value"}}')) x(v)
+WHERE  jsonb_hash(v)::bit(32) != jsonb_hash_extended(v, 0)::bit(32)
+       OR jsonb_hash(v)::bit(32) = jsonb_hash_extended(v, 1)::bit(32);
+
+SELECT v as value, hash_range(v)::bit(32) as standard,
+          hash_range_extended(v, 0)::bit(32) as extended0,
+          hash_range_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES (int4range(10, 20)), (int4range(23, 43)),
+         (int4range(5675, 550273)),
+                (int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
+WHERE  hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
+       OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);