From 35ea75632a56ca8ef22aa8fed03b9dabb9c8c575 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Tue, 22 Aug 2017 16:05:48 -0700 Subject: [PATCH] Refactor typcache.c's record typmod hash table. 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 | 27 +++++++++++ src/backend/utils/cache/typcache.c | 74 ++++++++++++++--------------- src/include/access/tupdesc.h | 2 + 3 files changed, 65 insertions(+), 38 deletions(-) diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 75b191ba2a..4436c86361 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -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 diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c index 20567a394b..691d4987b1 100644 --- a/src/backend/utils/cache/typcache.c +++ b/src/backend/utils/cache/typcache.c @@ -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 */ diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h index 39fd59686a..989fe738bb 100644 --- a/src/include/access/tupdesc.h +++ b/src/include/access/tupdesc.h @@ -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,