@ -66,6 +66,7 @@
# include "utils/builtins.h"
# include "utils/catcache.h"
# include "utils/fmgroids.h"
# include "utils/injection_point.h"
# include "utils/inval.h"
# include "utils/lsyscache.h"
# include "utils/memutils.h"
@ -77,6 +78,20 @@
/* The main type cache hashtable searched by lookup_type_cache */
static HTAB * TypeCacheHash = NULL ;
/*
* The mapping of relation ' s OID to the corresponding composite type OID .
* We ' re keeping the map entry when the corresponding typentry has something
* to clear i . e it has either TCFLAGS_HAVE_PG_TYPE_DATA , or
* TCFLAGS_OPERATOR_FLAGS , or tupdesc .
*/
static HTAB * RelIdToTypeIdCacheHash = NULL ;
typedef struct RelIdToTypeIdCacheEntry
{
Oid relid ; /* OID of the relation */
Oid composite_typid ; /* OID of the relation's composite type */
} RelIdToTypeIdCacheEntry ;
/* List of type cache entries for domain types */
static TypeCacheEntry * firstDomainTypeEntry = NULL ;
@ -208,6 +223,10 @@ typedef struct SharedTypmodTableEntry
dsa_pointer shared_tupdesc ;
} SharedTypmodTableEntry ;
static Oid * in_progress_list ;
static int in_progress_list_len ;
static int in_progress_list_maxlen ;
/*
* A comparator function for SharedRecordTableKey .
*/
@ -329,6 +348,8 @@ static void shared_record_typmod_registry_detach(dsm_segment *segment,
static TupleDesc find_or_make_matching_shared_tupledesc ( TupleDesc tupdesc ) ;
static dsa_pointer share_tupledesc ( dsa_area * area , TupleDesc tupdesc ,
uint32 typmod ) ;
static void insert_rel_type_cache_if_needed ( TypeCacheEntry * typentry ) ;
static void delete_rel_type_cache_if_needed ( TypeCacheEntry * typentry ) ;
/*
@ -366,11 +387,13 @@ lookup_type_cache(Oid type_id, int flags)
{
TypeCacheEntry * typentry ;
bool found ;
int in_progress_offset ;
if ( TypeCacheHash = = NULL )
{
/* First time through: initialize the hash table */
HASHCTL ctl ;
int allocsize ;
ctl . keysize = sizeof ( Oid ) ;
ctl . entrysize = sizeof ( TypeCacheEntry ) ;
@ -385,6 +408,13 @@ lookup_type_cache(Oid type_id, int flags)
TypeCacheHash = hash_create ( " Type information cache " , 64 ,
& ctl , HASH_ELEM | HASH_FUNCTION ) ;
Assert ( RelIdToTypeIdCacheHash = = NULL ) ;
ctl . keysize = sizeof ( Oid ) ;
ctl . entrysize = sizeof ( RelIdToTypeIdCacheEntry ) ;
RelIdToTypeIdCacheHash = hash_create ( " Map from relid to OID of cached composite type " , 64 ,
& ctl , HASH_ELEM | HASH_BLOBS ) ;
/* Also set up callbacks for SI invalidations */
CacheRegisterRelcacheCallback ( TypeCacheRelCallback , ( Datum ) 0 ) ;
CacheRegisterSyscacheCallback ( TYPEOID , TypeCacheTypCallback , ( Datum ) 0 ) ;
@ -394,8 +424,32 @@ lookup_type_cache(Oid type_id, int flags)
/* Also make sure CacheMemoryContext exists */
if ( ! CacheMemoryContext )
CreateCacheMemoryContext ( ) ;
/*
* reserve enough in_progress_list slots for many cases
*/
allocsize = 4 ;
in_progress_list =
MemoryContextAlloc ( CacheMemoryContext ,
allocsize * sizeof ( * in_progress_list ) ) ;
in_progress_list_maxlen = allocsize ;
}
Assert ( TypeCacheHash ! = NULL & & RelIdToTypeIdCacheHash ! = NULL ) ;
/* Register to catch invalidation messages */
if ( in_progress_list_len > = in_progress_list_maxlen )
{
int allocsize ;
allocsize = in_progress_list_maxlen * 2 ;
in_progress_list = repalloc ( in_progress_list ,
allocsize * sizeof ( * in_progress_list ) ) ;
in_progress_list_maxlen = allocsize ;
}
in_progress_offset = in_progress_list_len + + ;
in_progress_list [ in_progress_offset ] = type_id ;
/* Try to look up an existing entry */
typentry = ( TypeCacheEntry * ) hash_search ( TypeCacheHash ,
& type_id ,
@ -896,6 +950,13 @@ lookup_type_cache(Oid type_id, int flags)
load_domaintype_info ( typentry ) ;
}
INJECTION_POINT ( " typecache-before-rel-type-cache-insert " ) ;
Assert ( in_progress_offset + 1 = = in_progress_list_len ) ;
in_progress_list_len - - ;
insert_rel_type_cache_if_needed ( typentry ) ;
return typentry ;
}
@ -2290,6 +2351,53 @@ SharedRecordTypmodRegistryAttach(SharedRecordTypmodRegistry *registry)
CurrentSession - > shared_typmod_table = typmod_table ;
}
/*
* InvalidateCompositeTypeCacheEntry
* Invalidate particular TypeCacheEntry on Relcache inval callback
*
* Delete the cached tuple descriptor ( if any ) for the given composite
* type , and reset whatever info we have cached about the composite type ' s
* comparability .
*/
static void
InvalidateCompositeTypeCacheEntry ( TypeCacheEntry * typentry )
{
bool hadTupDescOrOpclass ;
Assert ( typentry - > typtype = = TYPTYPE_COMPOSITE & &
OidIsValid ( typentry - > typrelid ) ) ;
hadTupDescOrOpclass = ( typentry - > tupDesc ! = NULL ) | |
( typentry - > flags & TCFLAGS_OPERATOR_FLAGS ) ;
/* Delete tupdesc if we have it */
if ( typentry - > tupDesc ! = NULL )
{
/*
* Release our refcount and free the tupdesc if none remain . We can ' t
* use DecrTupleDescRefCount here because this reference is not logged
* by the current resource owner .
*/
Assert ( typentry - > tupDesc - > tdrefcount > 0 ) ;
if ( - - typentry - > tupDesc - > tdrefcount = = 0 )
FreeTupleDesc ( typentry - > tupDesc ) ;
typentry - > tupDesc = NULL ;
/*
* Also clear tupDesc_identifier , so that anyone watching it will
* realize that the tupdesc has changed .
*/
typentry - > tupDesc_identifier = 0 ;
}
/* Reset equality/comparison/hashing validity information */
typentry - > flags & = ~ TCFLAGS_OPERATOR_FLAGS ;
/* Call delete_rel_type_cache() if we actually cleared something */
if ( hadTupDescOrOpclass )
delete_rel_type_cache_if_needed ( typentry ) ;
}
/*
* TypeCacheRelCallback
* Relcache inval callback function
@ -2299,63 +2407,55 @@ SharedRecordTypmodRegistryAttach(SharedRecordTypmodRegistry *registry)
* whatever info we have cached about the composite type ' s comparability .
*
* This is called when a relcache invalidation event occurs for the given
* relid . We must scan the whole typcache hash since we don ' t know the
* type OID corresponding to the relid . We could do a direct search if this
* were a syscache - flush callback on pg_type , but then we would need all
* ALTER - TABLE - like commands that could modify a rowtype to issue syscache
* invals against the rel ' s pg_type OID . The extra SI signaling could very
* well cost more than we ' d save , since in most usages there are not very
* many entries in a backend ' s typcache . The risk of bugs - of - omission seems
* high , too .
*
* Another possibility , with only localized impact , is to maintain a second
* hashtable that indexes composite - type typcache entries by their typrelid .
* But it ' s still not clear it ' s worth the trouble .
* relid . We can ' t use syscache to find a type corresponding to the given
* relation because the code can be called outside of transaction . Thus , we
* use the RelIdToTypeIdCacheHash map to locate appropriate typcache entry .
*/
static void
TypeCacheRelCallback ( Datum arg , Oid relid )
{
HASH_SEQ_STATUS status ;
TypeCacheEntry * typentry ;
/* TypeCacheHash must exist, else this callback wouldn't be registered */
hash_seq_init ( & status , TypeCacheHash ) ;
while ( ( typentry = ( TypeCacheEntry * ) hash_seq_search ( & status ) ) ! = NULL )
/*
* RelIdToTypeIdCacheHash and TypeCacheHash should exist , otherwise this
* callback wouldn ' t be registered
*/
if ( OidIsValid ( relid ) )
{
if ( typentry - > typtype = = TYPTYPE_COMPOSITE )
RelIdToTypeIdCacheEntry * relentry ;
/*
* Find an RelIdToTypeIdCacheHash entry , which should exist as soon as
* corresponding typcache entry has something to clean .
*/
relentry = ( RelIdToTypeIdCacheEntry * ) hash_search ( RelIdToTypeIdCacheHash ,
& relid ,
HASH_FIND , NULL ) ;
if ( relentry ! = NULL )
{
/* Skip if no match, unless we're zapping all composite types */
if ( relid ! = typentry - > typrelid & & relid ! = InvalidOid )
continue ;
typentry = ( TypeCacheEntry * ) hash_search ( TypeCacheHash ,
& relentry - > composite_typid ,
HASH_FIND , NULL ) ;
/* Delete tupdesc if we have it */
if ( typentry - > tupDesc ! = NULL )
if ( typentry ! = NULL )
{
/*
* Release our refcount , and free the tupdesc if none remain .
* ( Can ' t use DecrTupleDescRefCount because this reference is
* not logged in current resource owner . )
*/
Assert ( typentry - > tupDesc - > tdrefcount > 0 ) ;
if ( - - typentry - > tupDesc - > tdrefcount = = 0 )
FreeTupleDesc ( typentry - > tupDesc ) ;
typentry - > tupDesc = NULL ;
Assert ( typentry - > typtype = = TYPTYPE_COMPOSITE ) ;
Assert ( relid = = typentry - > typrelid ) ;
/*
* Also clear tupDesc_identifier , so that anything watching
* that will realize that the tupdesc has possibly changed .
* ( Alternatively , we could specify that to detect possible
* tupdesc change , one must check for tupDesc ! = NULL as well
* as tupDesc_identifier being the same as what was previously
* seen . That seems error - prone . )
*/
typentry - > tupDesc_identifier = 0 ;
InvalidateCompositeTypeCacheEntry ( typentry ) ;
}
/* Reset equality/comparison/hashing validity information */
typentry - > flags & = ~ TCFLAGS_OPERATOR_FLAGS ;
}
else if ( typentry - > typtype = = TYPTYPE_DOMAIN )
/*
* Visit all the domain types sequentially . Typically , this shouldn ' t
* affect performance since domain types are less tended to bloat .
* Domain types are created manually , unlike composite types which are
* automatically created for every temporary table .
*/
for ( typentry = firstDomainTypeEntry ;
typentry ! = NULL ;
typentry = typentry - > nextDomain )
{
/*
* If it ' s domain over composite , reset flags . ( We don ' t bother
@ -2367,6 +2467,36 @@ TypeCacheRelCallback(Datum arg, Oid relid)
typentry - > flags & = ~ TCFLAGS_OPERATOR_FLAGS ;
}
}
else
{
HASH_SEQ_STATUS status ;
/*
* Relid is invalid . By convention , we need to reset all composite
* types in cache . Also , we should reset flags for domain types , and
* we loop over all entries in hash , so , do it in a single scan .
*/
hash_seq_init ( & status , TypeCacheHash ) ;
while ( ( typentry = ( TypeCacheEntry * ) hash_seq_search ( & status ) ) ! = NULL )
{
if ( typentry - > typtype = = TYPTYPE_COMPOSITE )
{
InvalidateCompositeTypeCacheEntry ( typentry ) ;
}
else if ( typentry - > typtype = = TYPTYPE_DOMAIN )
{
/*
* If it ' s domain over composite , reset flags . ( We don ' t
* bother trying to determine whether the specific base type
* needs a reset . ) Note that if we haven ' t determined whether
* the base type is composite , we don ' t need to reset
* anything .
*/
if ( typentry - > flags & TCFLAGS_DOMAIN_BASE_IS_COMPOSITE )
typentry - > flags & = ~ TCFLAGS_OPERATOR_FLAGS ;
}
}
}
}
/*
@ -2397,6 +2527,8 @@ TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue)
while ( ( typentry = ( TypeCacheEntry * ) hash_seq_search ( & status ) ) ! = NULL )
{
bool hadPgTypeData = ( typentry - > flags & TCFLAGS_HAVE_PG_TYPE_DATA ) ;
Assert ( hashvalue = = 0 | | typentry - > type_id_hash = = hashvalue ) ;
/*
@ -2406,6 +2538,13 @@ TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue)
*/
typentry - > flags & = ~ ( TCFLAGS_HAVE_PG_TYPE_DATA |
TCFLAGS_CHECKED_DOMAIN_CONSTRAINTS ) ;
/*
* Call delete_rel_type_cache ( ) if we cleaned
* TCFLAGS_HAVE_PG_TYPE_DATA flag previously .
*/
if ( hadPgTypeData )
delete_rel_type_cache_if_needed ( typentry ) ;
}
}
@ -2914,3 +3053,135 @@ shared_record_typmod_registry_detach(dsm_segment *segment, Datum datum)
}
CurrentSession - > shared_typmod_registry = NULL ;
}
/*
* Insert RelIdToTypeIdCacheHash entry if needed .
*/
static void
insert_rel_type_cache_if_needed ( TypeCacheEntry * typentry )
{
/* Immediately quit for non-composite types */
if ( typentry - > typtype ! = TYPTYPE_COMPOSITE )
return ;
/* typrelid should be given for composite types */
Assert ( OidIsValid ( typentry - > typrelid ) ) ;
/*
* Insert a RelIdToTypeIdCacheHash entry if the typentry have any
* information indicating it should be here .
*/
if ( ( typentry - > flags & TCFLAGS_HAVE_PG_TYPE_DATA ) | |
( typentry - > flags & TCFLAGS_OPERATOR_FLAGS ) | |
typentry - > tupDesc ! = NULL )
{
RelIdToTypeIdCacheEntry * relentry ;
bool found ;
relentry = ( RelIdToTypeIdCacheEntry * ) hash_search ( RelIdToTypeIdCacheHash ,
& typentry - > typrelid ,
HASH_ENTER , & found ) ;
relentry - > relid = typentry - > typrelid ;
relentry - > composite_typid = typentry - > type_id ;
}
}
/*
* Delete entry RelIdToTypeIdCacheHash if needed after resetting of the
* TCFLAGS_HAVE_PG_TYPE_DATA flag , or any of TCFLAGS_OPERATOR_FLAGS ,
* or tupDesc .
*/
static void
delete_rel_type_cache_if_needed ( TypeCacheEntry * typentry )
{
# ifdef USE_ASSERT_CHECKING
int i ;
bool is_in_progress = false ;
for ( i = 0 ; i < in_progress_list_len ; i + + )
{
if ( in_progress_list [ i ] = = typentry - > type_id )
{
is_in_progress = true ;
break ;
}
}
# endif
/* Immediately quit for non-composite types */
if ( typentry - > typtype ! = TYPTYPE_COMPOSITE )
return ;
/* typrelid should be given for composite types */
Assert ( OidIsValid ( typentry - > typrelid ) ) ;
/*
* Delete a RelIdToTypeIdCacheHash entry if the typentry doesn ' t have any
* information indicating entry should be still there .
*/
if ( ! ( typentry - > flags & TCFLAGS_HAVE_PG_TYPE_DATA ) & &
! ( typentry - > flags & TCFLAGS_OPERATOR_FLAGS ) & &
typentry - > tupDesc = = NULL )
{
bool found ;
( void ) hash_search ( RelIdToTypeIdCacheHash ,
& typentry - > typrelid ,
HASH_REMOVE , & found ) ;
Assert ( found | | is_in_progress ) ;
}
else
{
# ifdef USE_ASSERT_CHECKING
/*
* In assert - enabled builds otherwise check for RelIdToTypeIdCacheHash
* entry if it should exist .
*/
bool found ;
if ( ! is_in_progress )
{
( void ) hash_search ( RelIdToTypeIdCacheHash ,
& typentry - > typrelid ,
HASH_FIND , & found ) ;
Assert ( found ) ;
}
# endif
}
}
/*
* Add possibly missing RelIdToTypeId entries related to TypeCacheHash
* entries , marked as in - progress by lookup_type_cache ( ) . It may happen
* in case of an error or interruption during the lookup_type_cache ( ) call .
*/
static void
finalize_in_progress_typentries ( void )
{
int i ;
for ( i = 0 ; i < in_progress_list_len ; i + + )
{
TypeCacheEntry * typentry ;
typentry = ( TypeCacheEntry * ) hash_search ( TypeCacheHash ,
& in_progress_list [ i ] ,
HASH_FIND , NULL ) ;
if ( typentry )
insert_rel_type_cache_if_needed ( typentry ) ;
}
in_progress_list_len = 0 ;
}
void
AtEOXact_TypeCache ( void )
{
finalize_in_progress_typentries ( ) ;
}
void
AtEOSubXact_TypeCache ( void )
{
finalize_in_progress_typentries ( ) ;
}