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,