Refactor typcache.c's record typmod hash table.
authorAndres Freund <andres@anarazel.de>
Tue, 22 Aug 2017 23:05:48 +0000 (16:05 -0700)
committerAndres Freund <andres@anarazel.de>
Tue, 22 Aug 2017 23:11:54 +0000 (16:11 -0700)
Previously, tuple descriptors were stored in chains keyed by a fixed size
array of OIDs.  That meant there were effectively two levels of collision
chain -- one inside and one outside the hash table.  Instead, let dynahash.c
look after conflicts for us by supplying a proper hash and equal function
pair.

This is a nice cleanup on its own, but also simplifies followup
changes allowing blessed TupleDescs to be shared between backends
participating in parallel query.

Author: Thomas Munro
Reviewed-By: Andres Freund
Discussion: https://postgr.es/m/CAEepm%3D34GVhOL%2BarUx56yx7OPk7%3DqpGsv3CpO54feqjAwQKm5g%40mail.gmail.com

src/backend/access/common/tupdesc.c
src/backend/utils/cache/typcache.c
src/include/access/tupdesc.h

index 75b191ba2aa96bbe87eb51bf3ba41ddf1cee2427..4436c8636170a2489b7eda7e99039b588ce1ac2b 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "postgres.h"
 
+#include "access/hash.h"
 #include "access/htup_details.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_type.h"
@@ -26,6 +27,7 @@
 #include "parser/parse_type.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/hashutils.h"
 #include "utils/resowner_private.h"
 #include "utils/syscache.h"
 
@@ -443,6 +445,31 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
        return true;
 }
 
+/*
+ * hashTupleDesc
+ *             Compute a hash value for a tuple descriptor.
+ *
+ * If two tuple descriptors would be considered equal by equalTupleDescs()
+ * then their hash value will be equal according to this function.
+ *
+ * Note that currently contents of constraint are not hashed - it'd be a bit
+ * painful to do so, and conflicts just due to constraints are unlikely.
+ */
+uint32
+hashTupleDesc(TupleDesc desc)
+{
+       uint32          s;
+       int                     i;
+
+       s = hash_combine(0, hash_uint32(desc->natts));
+       s = hash_combine(s, hash_uint32(desc->tdtypeid));
+       s = hash_combine(s, hash_uint32(desc->tdhasoid));
+       for (i = 0; i < desc->natts; ++i)
+               s = hash_combine(s, hash_uint32(TupleDescAttr(desc, i)->atttypid));
+
+       return s;
+}
+
 /*
  * TupleDescInitEntry
  *             This function initializes a single attribute structure in
index 20567a394b2109bd93f69b4305a82c7ac6b1b48b..691d4987b160889ae743e6fce08af1ef18429e6c 100644 (file)
@@ -133,19 +133,12 @@ typedef struct TypeCacheEnumData
  *
  * Stored record types are remembered in a linear array of TupleDescs,
  * which can be indexed quickly with the assigned typmod.  There is also
- * a hash table to speed searches for matching TupleDescs.  The hash key
- * uses just the first N columns' type OIDs, and so we may have multiple
- * entries with the same hash key.
+ * a hash table to speed searches for matching TupleDescs.
  */
-#define REC_HASH_KEYS  16              /* use this many columns in hash key */
 
 typedef struct RecordCacheEntry
 {
-       /* the hash lookup key MUST BE FIRST */
-       Oid                     hashkey[REC_HASH_KEYS]; /* column type IDs, zero-filled */
-
-       /* list of TupleDescs for record types with this hashkey */
-       List       *tupdescs;
+       TupleDesc       tupdesc;
 } RecordCacheEntry;
 
 static HTAB *RecordCacheHash = NULL;
@@ -1297,6 +1290,28 @@ lookup_rowtype_tupdesc_copy(Oid type_id, int32 typmod)
        return CreateTupleDescCopyConstr(tmp);
 }
 
+/*
+ * Hash function for the hash table of RecordCacheEntry.
+ */
+static uint32
+record_type_typmod_hash(const void *data, size_t size)
+{
+       RecordCacheEntry *entry = (RecordCacheEntry *) data;
+
+       return hashTupleDesc(entry->tupdesc);
+}
+
+/*
+ * Match function for the hash table of RecordCacheEntry.
+ */
+static int
+record_type_typmod_compare(const void *a, const void *b, size_t size)
+{
+       RecordCacheEntry *left = (RecordCacheEntry *) a;
+       RecordCacheEntry *right = (RecordCacheEntry *) b;
+
+       return equalTupleDescs(left->tupdesc, right->tupdesc) ? 0 : 1;
+}
 
 /*
  * assign_record_type_typmod
@@ -1310,10 +1325,7 @@ assign_record_type_typmod(TupleDesc tupDesc)
 {
        RecordCacheEntry *recentry;
        TupleDesc       entDesc;
-       Oid                     hashkey[REC_HASH_KEYS];
        bool            found;
-       int                     i;
-       ListCell   *l;
        int32           newtypmod;
        MemoryContext oldcxt;
 
@@ -1325,45 +1337,31 @@ assign_record_type_typmod(TupleDesc tupDesc)
                HASHCTL         ctl;
 
                MemSet(&ctl, 0, sizeof(ctl));
-               ctl.keysize = REC_HASH_KEYS * sizeof(Oid);
+               ctl.keysize = sizeof(TupleDesc);        /* just the pointer */
                ctl.entrysize = sizeof(RecordCacheEntry);
+               ctl.hash = record_type_typmod_hash;
+               ctl.match = record_type_typmod_compare;
                RecordCacheHash = hash_create("Record information cache", 64,
-                                                                         &ctl, HASH_ELEM | HASH_BLOBS);
+                                                                         &ctl,
+                                                                         HASH_ELEM | HASH_FUNCTION | HASH_COMPARE);
 
                /* Also make sure CacheMemoryContext exists */
                if (!CacheMemoryContext)
                        CreateCacheMemoryContext();
        }
 
-       /* Find or create a hashtable entry for this hash class */
-       MemSet(hashkey, 0, sizeof(hashkey));
-       for (i = 0; i < tupDesc->natts; i++)
-       {
-               if (i >= REC_HASH_KEYS)
-                       break;
-               hashkey[i] = TupleDescAttr(tupDesc, i)->atttypid;
-       }
+       /* Find or create a hashtable entry for this tuple descriptor */
        recentry = (RecordCacheEntry *) hash_search(RecordCacheHash,
-                                                                                               (void *) hashkey,
+                                                                                               (void *) &tupDesc,
                                                                                                HASH_ENTER, &found);
-       if (!found)
+       if (found && recentry->tupdesc != NULL)
        {
-               /* New entry ... hash_search initialized only the hash key */
-               recentry->tupdescs = NIL;
-       }
-
-       /* Look for existing record cache entry */
-       foreach(l, recentry->tupdescs)
-       {
-               entDesc = (TupleDesc) lfirst(l);
-               if (equalTupleDescs(tupDesc, entDesc))
-               {
-                       tupDesc->tdtypmod = entDesc->tdtypmod;
-                       return;
-               }
+               tupDesc->tdtypmod = recentry->tupdesc->tdtypmod;
+               return;
        }
 
        /* Not present, so need to manufacture an entry */
+       recentry->tupdesc = NULL;
        oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
 
        if (RecordCacheArray == NULL)
@@ -1382,7 +1380,7 @@ assign_record_type_typmod(TupleDesc tupDesc)
 
        /* if fail in subrs, no damage except possibly some wasted memory... */
        entDesc = CreateTupleDescCopy(tupDesc);
-       recentry->tupdescs = lcons(entDesc, recentry->tupdescs);
+       recentry->tupdesc = entDesc;
        /* mark it as a reference-counted tupdesc */
        entDesc->tdrefcount = 1;
        /* now it's safe to advance NextRecordTypmod */
index 39fd59686a7145d28c614c83c9857610862cd9ec..989fe738bbe7675632383e6330f3ab15f37c6954 100644 (file)
@@ -114,6 +114,8 @@ extern void DecrTupleDescRefCount(TupleDesc tupdesc);
 
 extern bool equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2);
 
+extern uint32 hashTupleDesc(TupleDesc tupdesc);
+
 extern void TupleDescInitEntry(TupleDesc desc,
                                   AttrNumber attributeNumber,
                                   const char *attributeName,