77
77
/* The main type cache hashtable searched by lookup_type_cache */
78
78
static HTAB * TypeCacheHash = NULL ;
79
79
80
+ /*
81
+ * The mapping of relation's OID to the corresponding composite type OID.
82
+ * We're keeping the map entry when the corresponding typentry has something
83
+ * to clear i.e it has either TCFLAGS_HAVE_PG_TYPE_DATA, or
84
+ * TCFLAGS_OPERATOR_FLAGS, or tupdesc.
85
+ */
86
+ static HTAB * RelIdToTypeIdCacheHash = NULL ;
87
+
88
+ typedef struct RelIdToTypeIdCacheEntry
89
+ {
90
+ Oid relid ; /* OID of the relation */
91
+ Oid composite_typid ; /* OID of the relation's composite type */
92
+ } RelIdToTypeIdCacheEntry ;
93
+
80
94
/* List of type cache entries for domain types */
81
95
static TypeCacheEntry * firstDomainTypeEntry = NULL ;
82
96
@@ -329,6 +343,8 @@ static void shared_record_typmod_registry_detach(dsm_segment *segment,
329
343
static TupleDesc find_or_make_matching_shared_tupledesc (TupleDesc tupdesc );
330
344
static dsa_pointer share_tupledesc (dsa_area * area , TupleDesc tupdesc ,
331
345
uint32 typmod );
346
+ static void insert_rel_type_cache_if_needed (TypeCacheEntry * typentry );
347
+ static void delete_rel_type_cache_if_needed (TypeCacheEntry * typentry );
332
348
333
349
334
350
/*
@@ -376,6 +392,13 @@ lookup_type_cache(Oid type_id, int flags)
376
392
TypeCacheHash = hash_create ("Type information cache" , 64 ,
377
393
& ctl , HASH_ELEM | HASH_FUNCTION );
378
394
395
+ Assert (RelIdToTypeIdCacheHash == NULL );
396
+
397
+ ctl .keysize = sizeof (Oid );
398
+ ctl .entrysize = sizeof (RelIdToTypeIdCacheEntry );
399
+ RelIdToTypeIdCacheHash = hash_create ("Map from relid to OID of cached composite type" , 64 ,
400
+ & ctl , HASH_ELEM | HASH_BLOBS );
401
+
379
402
/* Also set up callbacks for SI invalidations */
380
403
CacheRegisterRelcacheCallback (TypeCacheRelCallback , (Datum ) 0 );
381
404
CacheRegisterSyscacheCallback (TYPEOID , TypeCacheTypCallback , (Datum ) 0 );
@@ -387,6 +410,8 @@ lookup_type_cache(Oid type_id, int flags)
387
410
CreateCacheMemoryContext ();
388
411
}
389
412
413
+ Assert (TypeCacheHash != NULL && RelIdToTypeIdCacheHash != NULL );
414
+
390
415
/* Try to look up an existing entry */
391
416
typentry = (TypeCacheEntry * ) hash_search (TypeCacheHash ,
392
417
& type_id ,
@@ -438,6 +463,7 @@ lookup_type_cache(Oid type_id, int flags)
438
463
typentry -> typelem = typtup -> typelem ;
439
464
typentry -> typcollation = typtup -> typcollation ;
440
465
typentry -> flags |= TCFLAGS_HAVE_PG_TYPE_DATA ;
466
+ insert_rel_type_cache_if_needed (typentry );
441
467
442
468
/* If it's a domain, immediately thread it into the domain cache list */
443
469
if (typentry -> typtype == TYPTYPE_DOMAIN )
@@ -483,6 +509,7 @@ lookup_type_cache(Oid type_id, int flags)
483
509
typentry -> typelem = typtup -> typelem ;
484
510
typentry -> typcollation = typtup -> typcollation ;
485
511
typentry -> flags |= TCFLAGS_HAVE_PG_TYPE_DATA ;
512
+ insert_rel_type_cache_if_needed (typentry );
486
513
487
514
ReleaseSysCache (tp );
488
515
}
@@ -2281,6 +2308,53 @@ SharedRecordTypmodRegistryAttach(SharedRecordTypmodRegistry *registry)
2281
2308
CurrentSession -> shared_typmod_table = typmod_table ;
2282
2309
}
2283
2310
2311
+ /*
2312
+ * InvalidateCompositeTypeCacheEntry
2313
+ * Invalidate particular TypeCacheEntry on Relcache inval callback
2314
+ *
2315
+ * Delete the cached tuple descriptor (if any) for the given composite
2316
+ * type, and reset whatever info we have cached about the composite type's
2317
+ * comparability.
2318
+ */
2319
+ static void
2320
+ InvalidateCompositeTypeCacheEntry (TypeCacheEntry * typentry )
2321
+ {
2322
+ bool hadTupDescOrOpclass ;
2323
+
2324
+ Assert (typentry -> typtype == TYPTYPE_COMPOSITE &&
2325
+ OidIsValid (typentry -> typrelid ));
2326
+
2327
+ hadTupDescOrOpclass = (typentry -> tupDesc != NULL ) ||
2328
+ (typentry -> flags & TCFLAGS_OPERATOR_FLAGS );
2329
+
2330
+ /* Delete tupdesc if we have it */
2331
+ if (typentry -> tupDesc != NULL )
2332
+ {
2333
+ /*
2334
+ * Release our refcount and free the tupdesc if none remain. We can't
2335
+ * use DecrTupleDescRefCount here because this reference is not logged
2336
+ * by the current resource owner.
2337
+ */
2338
+ Assert (typentry -> tupDesc -> tdrefcount > 0 );
2339
+ if (-- typentry -> tupDesc -> tdrefcount == 0 )
2340
+ FreeTupleDesc (typentry -> tupDesc );
2341
+ typentry -> tupDesc = NULL ;
2342
+
2343
+ /*
2344
+ * Also clear tupDesc_identifier, so that anyone watching it will
2345
+ * realize that the tupdesc has changed.
2346
+ */
2347
+ typentry -> tupDesc_identifier = 0 ;
2348
+ }
2349
+
2350
+ /* Reset equality/comparison/hashing validity information */
2351
+ typentry -> flags &= ~TCFLAGS_OPERATOR_FLAGS ;
2352
+
2353
+ /* Call check_delete_rel_type_cache() if we actually cleared something */
2354
+ if (hadTupDescOrOpclass )
2355
+ delete_rel_type_cache_if_needed (typentry );
2356
+ }
2357
+
2284
2358
/*
2285
2359
* TypeCacheRelCallback
2286
2360
* Relcache inval callback function
@@ -2290,63 +2364,55 @@ SharedRecordTypmodRegistryAttach(SharedRecordTypmodRegistry *registry)
2290
2364
* whatever info we have cached about the composite type's comparability.
2291
2365
*
2292
2366
* This is called when a relcache invalidation event occurs for the given
2293
- * relid. We must scan the whole typcache hash since we don't know the
2294
- * type OID corresponding to the relid. We could do a direct search if this
2295
- * were a syscache-flush callback on pg_type, but then we would need all
2296
- * ALTER-TABLE-like commands that could modify a rowtype to issue syscache
2297
- * invals against the rel's pg_type OID. The extra SI signaling could very
2298
- * well cost more than we'd save, since in most usages there are not very
2299
- * many entries in a backend's typcache. The risk of bugs-of-omission seems
2300
- * high, too.
2301
- *
2302
- * Another possibility, with only localized impact, is to maintain a second
2303
- * hashtable that indexes composite-type typcache entries by their typrelid.
2304
- * But it's still not clear it's worth the trouble.
2367
+ * relid. We can't use syscache to find a type corresponding to the given
2368
+ * relation because the code can be called outside of transaction. Thus we use
2369
+ * the RelIdToTypeIdCacheHash map to locate appropriate typcache entry.
2305
2370
*/
2306
2371
static void
2307
2372
TypeCacheRelCallback (Datum arg , Oid relid )
2308
2373
{
2309
- HASH_SEQ_STATUS status ;
2310
2374
TypeCacheEntry * typentry ;
2311
2375
2312
- /* TypeCacheHash must exist, else this callback wouldn't be registered */
2313
- hash_seq_init (& status , TypeCacheHash );
2314
- while ((typentry = (TypeCacheEntry * ) hash_seq_search (& status )) != NULL )
2376
+ /*
2377
+ * RelIdToTypeIdCacheHash and TypeCacheHash should exist, otherwise this
2378
+ * callback wouldn't be registered
2379
+ */
2380
+ if (OidIsValid (relid ))
2315
2381
{
2316
- if (typentry -> typtype == TYPTYPE_COMPOSITE )
2382
+ RelIdToTypeIdCacheEntry * relentry ;
2383
+
2384
+ /*
2385
+ * Find an RelIdToTypeIdCacheHash entry, which should exist as soon as
2386
+ * corresponding typcache entry has something to clean.
2387
+ */
2388
+ relentry = (RelIdToTypeIdCacheEntry * ) hash_search (RelIdToTypeIdCacheHash ,
2389
+ & relid ,
2390
+ HASH_FIND , NULL );
2391
+
2392
+ if (relentry != NULL )
2317
2393
{
2318
- /* Skip if no match, unless we're zapping all composite types */
2319
- if ( relid != typentry -> typrelid && relid != InvalidOid )
2320
- continue ;
2394
+ typentry = ( TypeCacheEntry * ) hash_search ( TypeCacheHash ,
2395
+ & relentry -> composite_typid ,
2396
+ HASH_FIND , NULL ) ;
2321
2397
2322
- /* Delete tupdesc if we have it */
2323
- if (typentry -> tupDesc != NULL )
2398
+ if (typentry != NULL )
2324
2399
{
2325
- /*
2326
- * Release our refcount, and free the tupdesc if none remain.
2327
- * (Can't use DecrTupleDescRefCount because this reference is
2328
- * not logged in current resource owner.)
2329
- */
2330
- Assert (typentry -> tupDesc -> tdrefcount > 0 );
2331
- if (-- typentry -> tupDesc -> tdrefcount == 0 )
2332
- FreeTupleDesc (typentry -> tupDesc );
2333
- typentry -> tupDesc = NULL ;
2400
+ Assert (typentry -> typtype == TYPTYPE_COMPOSITE );
2401
+ Assert (relid == typentry -> typrelid );
2334
2402
2335
- /*
2336
- * Also clear tupDesc_identifier, so that anything watching
2337
- * that will realize that the tupdesc has possibly changed.
2338
- * (Alternatively, we could specify that to detect possible
2339
- * tupdesc change, one must check for tupDesc != NULL as well
2340
- * as tupDesc_identifier being the same as what was previously
2341
- * seen. That seems error-prone.)
2342
- */
2343
- typentry -> tupDesc_identifier = 0 ;
2403
+ InvalidateCompositeTypeCacheEntry (typentry );
2344
2404
}
2345
-
2346
- /* Reset equality/comparison/hashing validity information */
2347
- typentry -> flags &= ~TCFLAGS_OPERATOR_FLAGS ;
2348
2405
}
2349
- else if (typentry -> typtype == TYPTYPE_DOMAIN )
2406
+
2407
+ /*
2408
+ * Visit all the domain types sequentially. Typically this shouldn't
2409
+ * affect performance since domain types are less tended to bloat.
2410
+ * Domain types are created manually, unlike composite types which are
2411
+ * automatically created for every temporary table.
2412
+ */
2413
+ for (typentry = firstDomainTypeEntry ;
2414
+ typentry != NULL ;
2415
+ typentry = typentry -> nextDomain )
2350
2416
{
2351
2417
/*
2352
2418
* If it's domain over composite, reset flags. (We don't bother
@@ -2358,6 +2424,36 @@ TypeCacheRelCallback(Datum arg, Oid relid)
2358
2424
typentry -> flags &= ~TCFLAGS_OPERATOR_FLAGS ;
2359
2425
}
2360
2426
}
2427
+ else
2428
+ {
2429
+ HASH_SEQ_STATUS status ;
2430
+
2431
+ /*
2432
+ * Relid is invalid. By convention we need to reset all composite
2433
+ * types in cache. Also, we should reset flags for domain types, and
2434
+ * we loop over all entries in hash, so, do it in a single scan.
2435
+ */
2436
+ hash_seq_init (& status , TypeCacheHash );
2437
+ while ((typentry = (TypeCacheEntry * ) hash_seq_search (& status )) != NULL )
2438
+ {
2439
+ if (typentry -> typtype == TYPTYPE_COMPOSITE )
2440
+ {
2441
+ InvalidateCompositeTypeCacheEntry (typentry );
2442
+ }
2443
+ else if (typentry -> typtype == TYPTYPE_DOMAIN )
2444
+ {
2445
+ /*
2446
+ * If it's domain over composite, reset flags. (We don't
2447
+ * bother trying to determine whether the specific base type
2448
+ * needs a reset.) Note that if we haven't determined whether
2449
+ * the base type is composite, we don't need to reset
2450
+ * anything.
2451
+ */
2452
+ if (typentry -> flags & TCFLAGS_DOMAIN_BASE_IS_COMPOSITE )
2453
+ typentry -> flags &= ~TCFLAGS_OPERATOR_FLAGS ;
2454
+ }
2455
+ }
2456
+ }
2361
2457
}
2362
2458
2363
2459
/*
@@ -2388,6 +2484,8 @@ TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue)
2388
2484
2389
2485
while ((typentry = (TypeCacheEntry * ) hash_seq_search (& status )) != NULL )
2390
2486
{
2487
+ bool hadPgTypeData = (typentry -> flags & TCFLAGS_HAVE_PG_TYPE_DATA );
2488
+
2391
2489
Assert (hashvalue == 0 || typentry -> type_id_hash == hashvalue );
2392
2490
2393
2491
/*
@@ -2397,6 +2495,13 @@ TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue)
2397
2495
*/
2398
2496
typentry -> flags &= ~(TCFLAGS_HAVE_PG_TYPE_DATA |
2399
2497
TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS );
2498
+
2499
+ /*
2500
+ * Call check_delete_rel_type_cache() if we cleaned
2501
+ * TCFLAGS_HAVE_PG_TYPE_DATA flag previously.
2502
+ */
2503
+ if (hadPgTypeData )
2504
+ delete_rel_type_cache_if_needed (typentry );
2400
2505
}
2401
2506
}
2402
2507
@@ -2905,3 +3010,85 @@ shared_record_typmod_registry_detach(dsm_segment *segment, Datum datum)
2905
3010
}
2906
3011
CurrentSession -> shared_typmod_registry = NULL ;
2907
3012
}
3013
+
3014
+ /*
3015
+ * Insert RelIdToTypeIdCacheHash entry after setting of the
3016
+ * TCFLAGS_HAVE_PG_TYPE_DATA flag if needed.
3017
+ */
3018
+ static void
3019
+ insert_rel_type_cache_if_needed (TypeCacheEntry * typentry )
3020
+ {
3021
+ Assert (typentry -> flags & TCFLAGS_HAVE_PG_TYPE_DATA );
3022
+
3023
+ /* Immediately quit for non-composite types */
3024
+ if (typentry -> typtype != TYPTYPE_COMPOSITE )
3025
+ return ;
3026
+
3027
+ /* typrelid should be given for composite types */
3028
+ Assert (OidIsValid (typentry -> typrelid ));
3029
+
3030
+ /*
3031
+ * Insert a RelIdToTypeIdCacheHash entry if the typentry doesn't have any
3032
+ * information indicating there should be such an entry already.
3033
+ */
3034
+ if (!(typentry -> flags & TCFLAGS_OPERATOR_FLAGS ) &&
3035
+ typentry -> tupDesc == NULL )
3036
+ {
3037
+ RelIdToTypeIdCacheEntry * relentry ;
3038
+ bool found ;
3039
+
3040
+ relentry = (RelIdToTypeIdCacheEntry * ) hash_search (RelIdToTypeIdCacheHash ,
3041
+ & typentry -> typrelid ,
3042
+ HASH_ENTER , & found );
3043
+ Assert (!found );
3044
+ relentry -> relid = typentry -> typrelid ;
3045
+ relentry -> composite_typid = typentry -> type_id ;
3046
+ }
3047
+ }
3048
+
3049
+ /*
3050
+ * Delete entry RelIdToTypeIdCacheHash if needed after resetting of the
3051
+ * TCFLAGS_HAVE_PG_TYPE_DATA flag, or any of TCFLAGS_OPERATOR_FLAGS flags,
3052
+ * or tupDesc.
3053
+ */
3054
+ static void
3055
+ delete_rel_type_cache_if_needed (TypeCacheEntry * typentry )
3056
+ {
3057
+ /* Immediately quit for non-composite types */
3058
+ if (typentry -> typtype != TYPTYPE_COMPOSITE )
3059
+ return ;
3060
+
3061
+ /* typrelid should be given for composite types */
3062
+ Assert (OidIsValid (typentry -> typrelid ));
3063
+
3064
+ /*
3065
+ * Delete a RelIdToTypeIdCacheHash entry if the typentry doesn't have any
3066
+ * information indicating entry should be still there.
3067
+ */
3068
+ if (!(typentry -> flags & TCFLAGS_HAVE_PG_TYPE_DATA ) &&
3069
+ !(typentry -> flags & TCFLAGS_OPERATOR_FLAGS ) &&
3070
+ typentry -> tupDesc == NULL )
3071
+ {
3072
+ bool found ;
3073
+
3074
+ (void ) hash_search (RelIdToTypeIdCacheHash ,
3075
+ & typentry -> typrelid ,
3076
+ HASH_REMOVE , & found );
3077
+ Assert (found );
3078
+ }
3079
+ else
3080
+ {
3081
+ #ifdef USE_ASSERT_CHECKING
3082
+ /*
3083
+ * In assert-enabled builds otherwise check for RelIdToTypeIdCacheHash
3084
+ * entry if it should exist.
3085
+ */
3086
+ bool found ;
3087
+
3088
+ (void ) hash_search (RelIdToTypeIdCacheHash ,
3089
+ & typentry -> typrelid ,
3090
+ HASH_FIND , & found );
3091
+ Assert (found );
3092
+ #endif
3093
+ }
3094
+ }
0 commit comments