Skip to content

Commit 35ea756

Browse files
committed
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
1 parent 0052a02 commit 35ea756

File tree

3 files changed

+65
-38
lines changed

3 files changed

+65
-38
lines changed

src/backend/access/common/tupdesc.c

+27
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@
1919

2020
#include "postgres.h"
2121

22+
#include "access/hash.h"
2223
#include "access/htup_details.h"
2324
#include "catalog/pg_collation.h"
2425
#include "catalog/pg_type.h"
2526
#include "miscadmin.h"
2627
#include "parser/parse_type.h"
2728
#include "utils/acl.h"
2829
#include "utils/builtins.h"
30+
#include "utils/hashutils.h"
2931
#include "utils/resowner_private.h"
3032
#include "utils/syscache.h"
3133

@@ -443,6 +445,31 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
443445
return true;
444446
}
445447

448+
/*
449+
* hashTupleDesc
450+
* Compute a hash value for a tuple descriptor.
451+
*
452+
* If two tuple descriptors would be considered equal by equalTupleDescs()
453+
* then their hash value will be equal according to this function.
454+
*
455+
* Note that currently contents of constraint are not hashed - it'd be a bit
456+
* painful to do so, and conflicts just due to constraints are unlikely.
457+
*/
458+
uint32
459+
hashTupleDesc(TupleDesc desc)
460+
{
461+
uint32 s;
462+
int i;
463+
464+
s = hash_combine(0, hash_uint32(desc->natts));
465+
s = hash_combine(s, hash_uint32(desc->tdtypeid));
466+
s = hash_combine(s, hash_uint32(desc->tdhasoid));
467+
for (i = 0; i < desc->natts; ++i)
468+
s = hash_combine(s, hash_uint32(TupleDescAttr(desc, i)->atttypid));
469+
470+
return s;
471+
}
472+
446473
/*
447474
* TupleDescInitEntry
448475
* This function initializes a single attribute structure in

src/backend/utils/cache/typcache.c

+36-38
Original file line numberDiff line numberDiff line change
@@ -133,19 +133,12 @@ typedef struct TypeCacheEnumData
133133
*
134134
* Stored record types are remembered in a linear array of TupleDescs,
135135
* which can be indexed quickly with the assigned typmod. There is also
136-
* a hash table to speed searches for matching TupleDescs. The hash key
137-
* uses just the first N columns' type OIDs, and so we may have multiple
138-
* entries with the same hash key.
136+
* a hash table to speed searches for matching TupleDescs.
139137
*/
140-
#define REC_HASH_KEYS 16 /* use this many columns in hash key */
141138

142139
typedef struct RecordCacheEntry
143140
{
144-
/* the hash lookup key MUST BE FIRST */
145-
Oid hashkey[REC_HASH_KEYS]; /* column type IDs, zero-filled */
146-
147-
/* list of TupleDescs for record types with this hashkey */
148-
List *tupdescs;
141+
TupleDesc tupdesc;
149142
} RecordCacheEntry;
150143

151144
static HTAB *RecordCacheHash = NULL;
@@ -1297,6 +1290,28 @@ lookup_rowtype_tupdesc_copy(Oid type_id, int32 typmod)
12971290
return CreateTupleDescCopyConstr(tmp);
12981291
}
12991292

1293+
/*
1294+
* Hash function for the hash table of RecordCacheEntry.
1295+
*/
1296+
static uint32
1297+
record_type_typmod_hash(const void *data, size_t size)
1298+
{
1299+
RecordCacheEntry *entry = (RecordCacheEntry *) data;
1300+
1301+
return hashTupleDesc(entry->tupdesc);
1302+
}
1303+
1304+
/*
1305+
* Match function for the hash table of RecordCacheEntry.
1306+
*/
1307+
static int
1308+
record_type_typmod_compare(const void *a, const void *b, size_t size)
1309+
{
1310+
RecordCacheEntry *left = (RecordCacheEntry *) a;
1311+
RecordCacheEntry *right = (RecordCacheEntry *) b;
1312+
1313+
return equalTupleDescs(left->tupdesc, right->tupdesc) ? 0 : 1;
1314+
}
13001315

13011316
/*
13021317
* assign_record_type_typmod
@@ -1310,10 +1325,7 @@ assign_record_type_typmod(TupleDesc tupDesc)
13101325
{
13111326
RecordCacheEntry *recentry;
13121327
TupleDesc entDesc;
1313-
Oid hashkey[REC_HASH_KEYS];
13141328
bool found;
1315-
int i;
1316-
ListCell *l;
13171329
int32 newtypmod;
13181330
MemoryContext oldcxt;
13191331

@@ -1325,45 +1337,31 @@ assign_record_type_typmod(TupleDesc tupDesc)
13251337
HASHCTL ctl;
13261338

13271339
MemSet(&ctl, 0, sizeof(ctl));
1328-
ctl.keysize = REC_HASH_KEYS * sizeof(Oid);
1340+
ctl.keysize = sizeof(TupleDesc); /* just the pointer */
13291341
ctl.entrysize = sizeof(RecordCacheEntry);
1342+
ctl.hash = record_type_typmod_hash;
1343+
ctl.match = record_type_typmod_compare;
13301344
RecordCacheHash = hash_create("Record information cache", 64,
1331-
&ctl, HASH_ELEM | HASH_BLOBS);
1345+
&ctl,
1346+
HASH_ELEM | HASH_FUNCTION | HASH_COMPARE);
13321347

13331348
/* Also make sure CacheMemoryContext exists */
13341349
if (!CacheMemoryContext)
13351350
CreateCacheMemoryContext();
13361351
}
13371352

1338-
/* Find or create a hashtable entry for this hash class */
1339-
MemSet(hashkey, 0, sizeof(hashkey));
1340-
for (i = 0; i < tupDesc->natts; i++)
1341-
{
1342-
if (i >= REC_HASH_KEYS)
1343-
break;
1344-
hashkey[i] = TupleDescAttr(tupDesc, i)->atttypid;
1345-
}
1353+
/* Find or create a hashtable entry for this tuple descriptor */
13461354
recentry = (RecordCacheEntry *) hash_search(RecordCacheHash,
1347-
(void *) hashkey,
1355+
(void *) &tupDesc,
13481356
HASH_ENTER, &found);
1349-
if (!found)
1357+
if (found && recentry->tupdesc != NULL)
13501358
{
1351-
/* New entry ... hash_search initialized only the hash key */
1352-
recentry->tupdescs = NIL;
1353-
}
1354-
1355-
/* Look for existing record cache entry */
1356-
foreach(l, recentry->tupdescs)
1357-
{
1358-
entDesc = (TupleDesc) lfirst(l);
1359-
if (equalTupleDescs(tupDesc, entDesc))
1360-
{
1361-
tupDesc->tdtypmod = entDesc->tdtypmod;
1362-
return;
1363-
}
1359+
tupDesc->tdtypmod = recentry->tupdesc->tdtypmod;
1360+
return;
13641361
}
13651362

13661363
/* Not present, so need to manufacture an entry */
1364+
recentry->tupdesc = NULL;
13671365
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
13681366

13691367
if (RecordCacheArray == NULL)
@@ -1382,7 +1380,7 @@ assign_record_type_typmod(TupleDesc tupDesc)
13821380

13831381
/* if fail in subrs, no damage except possibly some wasted memory... */
13841382
entDesc = CreateTupleDescCopy(tupDesc);
1385-
recentry->tupdescs = lcons(entDesc, recentry->tupdescs);
1383+
recentry->tupdesc = entDesc;
13861384
/* mark it as a reference-counted tupdesc */
13871385
entDesc->tdrefcount = 1;
13881386
/* now it's safe to advance NextRecordTypmod */

src/include/access/tupdesc.h

+2
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ extern void DecrTupleDescRefCount(TupleDesc tupdesc);
114114

115115
extern bool equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2);
116116

117+
extern uint32 hashTupleDesc(TupleDesc tupdesc);
118+
117119
extern void TupleDescInitEntry(TupleDesc desc,
118120
AttrNumber attributeNumber,
119121
const char *attributeName,

0 commit comments

Comments
 (0)