7777/* The main type cache hashtable searched by lookup_type_cache */
7878static HTAB * TypeCacheHash = NULL ;
7979
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+
8094/* List of type cache entries for domain types */
8195static TypeCacheEntry * firstDomainTypeEntry = NULL ;
8296
@@ -329,6 +343,8 @@ static void shared_record_typmod_registry_detach(dsm_segment *segment,
329343static TupleDesc find_or_make_matching_shared_tupledesc (TupleDesc tupdesc );
330344static dsa_pointer share_tupledesc (dsa_area * area , TupleDesc tupdesc ,
331345 uint32 typmod );
346+ static void insert_rel_type_cache_if_needed (TypeCacheEntry * typentry );
347+ static void delete_rel_type_cache_if_needed (TypeCacheEntry * typentry );
332348
333349
334350/*
@@ -376,6 +392,13 @@ lookup_type_cache(Oid type_id, int flags)
376392 TypeCacheHash = hash_create ("Type information cache" , 64 ,
377393 & ctl , HASH_ELEM | HASH_FUNCTION );
378394
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+
379402 /* Also set up callbacks for SI invalidations */
380403 CacheRegisterRelcacheCallback (TypeCacheRelCallback , (Datum ) 0 );
381404 CacheRegisterSyscacheCallback (TYPEOID , TypeCacheTypCallback , (Datum ) 0 );
@@ -387,6 +410,8 @@ lookup_type_cache(Oid type_id, int flags)
387410 CreateCacheMemoryContext ();
388411 }
389412
413+ Assert (TypeCacheHash != NULL && RelIdToTypeIdCacheHash != NULL );
414+
390415 /* Try to look up an existing entry */
391416 typentry = (TypeCacheEntry * ) hash_search (TypeCacheHash ,
392417 & type_id ,
@@ -438,6 +463,7 @@ lookup_type_cache(Oid type_id, int flags)
438463 typentry -> typelem = typtup -> typelem ;
439464 typentry -> typcollation = typtup -> typcollation ;
440465 typentry -> flags |= TCFLAGS_HAVE_PG_TYPE_DATA ;
466+ insert_rel_type_cache_if_needed (typentry );
441467
442468 /* If it's a domain, immediately thread it into the domain cache list */
443469 if (typentry -> typtype == TYPTYPE_DOMAIN )
@@ -483,6 +509,7 @@ lookup_type_cache(Oid type_id, int flags)
483509 typentry -> typelem = typtup -> typelem ;
484510 typentry -> typcollation = typtup -> typcollation ;
485511 typentry -> flags |= TCFLAGS_HAVE_PG_TYPE_DATA ;
512+ insert_rel_type_cache_if_needed (typentry );
486513
487514 ReleaseSysCache (tp );
488515 }
@@ -2281,6 +2308,53 @@ SharedRecordTypmodRegistryAttach(SharedRecordTypmodRegistry *registry)
22812308 CurrentSession -> shared_typmod_table = typmod_table ;
22822309}
22832310
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+
22842358/*
22852359 * TypeCacheRelCallback
22862360 * Relcache inval callback function
@@ -2290,63 +2364,55 @@ SharedRecordTypmodRegistryAttach(SharedRecordTypmodRegistry *registry)
22902364 * whatever info we have cached about the composite type's comparability.
22912365 *
22922366 * 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.
23052370 */
23062371static void
23072372TypeCacheRelCallback (Datum arg , Oid relid )
23082373{
2309- HASH_SEQ_STATUS status ;
23102374 TypeCacheEntry * typentry ;
23112375
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 ))
23152381 {
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 )
23172393 {
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 ) ;
23212397
2322- /* Delete tupdesc if we have it */
2323- if (typentry -> tupDesc != NULL )
2398+ if (typentry != NULL )
23242399 {
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 );
23342402
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 );
23442404 }
2345-
2346- /* Reset equality/comparison/hashing validity information */
2347- typentry -> flags &= ~TCFLAGS_OPERATOR_FLAGS ;
23482405 }
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 )
23502416 {
23512417 /*
23522418 * If it's domain over composite, reset flags. (We don't bother
@@ -2358,6 +2424,36 @@ TypeCacheRelCallback(Datum arg, Oid relid)
23582424 typentry -> flags &= ~TCFLAGS_OPERATOR_FLAGS ;
23592425 }
23602426 }
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+ }
23612457}
23622458
23632459/*
@@ -2388,6 +2484,8 @@ TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue)
23882484
23892485 while ((typentry = (TypeCacheEntry * ) hash_seq_search (& status )) != NULL )
23902486 {
2487+ bool hadPgTypeData = (typentry -> flags & TCFLAGS_HAVE_PG_TYPE_DATA );
2488+
23912489 Assert (hashvalue == 0 || typentry -> type_id_hash == hashvalue );
23922490
23932491 /*
@@ -2397,6 +2495,13 @@ TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue)
23972495 */
23982496 typentry -> flags &= ~(TCFLAGS_HAVE_PG_TYPE_DATA |
23992497 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 );
24002505 }
24012506}
24022507
@@ -2905,3 +3010,85 @@ shared_record_typmod_registry_detach(dsm_segment *segment, Datum datum)
29053010 }
29063011 CurrentSession -> shared_typmod_registry = NULL ;
29073012}
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