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
This commit is contained in:
Andres Freund 2017-08-22 16:05:48 -07:00
parent 0052a0243d
commit 35ea75632a
3 changed files with 65 additions and 38 deletions

View 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

View 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 */

View 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,