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

Lines changed: 27 additions & 0 deletions
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

Lines changed: 36 additions & 38 deletions
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

Lines changed: 2 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy