mirror of
https://github.com/postgres/postgres.git
synced 2025-07-09 22:41:56 +03:00
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:
74
src/backend/utils/cache/typcache.c
vendored
74
src/backend/utils/cache/typcache.c
vendored
@ -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 */
|
||||
|
Reference in New Issue
Block a user