@ -250,6 +250,7 @@ static void RelationDestroyRelation(Relation relation, bool remember_tupdesc);
static void RelationClearRelation ( Relation relation , bool rebuild ) ;
static void RelationReloadIndexInfo ( Relation relation ) ;
static void RelationReloadNailed ( Relation relation ) ;
static void RelationFlushRelation ( Relation relation ) ;
static void RememberToFreeTupleDescAtEOX ( TupleDesc td ) ;
static void AtEOXact_cleanup ( Relation relation , bool isCommit ) ;
@ -286,7 +287,7 @@ static void IndexSupportInitialize(oidvector *indclass,
static OpClassCacheEnt * LookupOpclassInfo ( Oid operatorClassOid ,
StrategyNumber numSupport ) ;
static void RelationCacheInitFileRemoveInDir ( const char * tblspcpath ) ;
static void unlink_initfile ( const char * initfilename ) ;
static void unlink_initfile ( const char * initfilename , int elevel ) ;
static bool equalPartitionDescs ( PartitionKey key , PartitionDesc partdesc1 ,
PartitionDesc partdesc2 ) ;
@ -1931,7 +1932,16 @@ RelationIdGetRelation(Oid relationId)
RelationReloadIndexInfo ( rd ) ;
else
RelationClearRelation ( rd , true ) ;
Assert ( rd - > rd_isvalid ) ;
/*
* Normally entries need to be valid here , but before the relcache
* has been initialized , not enough infrastructure exists to
* perform pg_class lookups . The structure of such entries doesn ' t
* change , but we still want to update the rd_rel entry . So
* rd_isvalid = false is left in place for a later lookup .
*/
Assert ( rd - > rd_isvalid | |
( rd - > rd_isnailed & & ! criticalRelcachesBuilt ) ) ;
}
return rd ;
}
@ -2135,6 +2145,81 @@ RelationReloadIndexInfo(Relation relation)
relation - > rd_isvalid = true ;
}
/*
* RelationReloadNailed - reload minimal information for nailed relations .
*
* The structure of a nailed relation can never change ( which is good , because
* we rely on knowing their structure to be able to read catalog content ) . But
* some parts , e . g . pg_class . relfrozenxid , are still important to have
* accurate content for . Therefore those need to be reloaded after the arrival
* of invalidations .
*/
static void
RelationReloadNailed ( Relation relation )
{
Assert ( relation - > rd_isnailed ) ;
/*
* Redo RelationInitPhysicalAddr in case it is a mapped relation whose
* mapping changed .
*/
RelationInitPhysicalAddr ( relation ) ;
/* flag as needing to be revalidated */
relation - > rd_isvalid = false ;
/*
* Can only reread catalog contents if in a transaction . If the relation
* is currently open ( not counting the nailed refcount ) , do so
* immediately . Otherwise we ' ve already marked the entry as possibly
* invalid , and it ' ll be fixed when next opened .
*/
if ( ! IsTransactionState ( ) | | relation - > rd_refcnt < = 1 )
return ;
if ( relation - > rd_rel - > relkind = = RELKIND_INDEX )
{
/*
* If it ' s a nailed - but - not - mapped index , then we need to re - read the
* pg_class row to see if its relfilenode changed .
*/
RelationReloadIndexInfo ( relation ) ;
}
else
{
/*
* Reload a non - index entry . We can ' t easily do so if relcaches
* aren ' t yet built , but that ' s fine because at that stage the
* attributes that need to be current ( like relfrozenxid ) aren ' t yet
* accessed . To ensure the entry will later be revalidated , we leave
* it in invalid state , but allow use ( cf . RelationIdGetRelation ( ) ) .
*/
if ( criticalRelcachesBuilt )
{
HeapTuple pg_class_tuple ;
Form_pg_class relp ;
/*
* NB : Mark the entry as valid before starting to scan , to avoid
* self - recursion when re - building pg_class .
*/
relation - > rd_isvalid = true ;
pg_class_tuple = ScanPgRelation ( RelationGetRelid ( relation ) ,
true , false ) ;
relp = ( Form_pg_class ) GETSTRUCT ( pg_class_tuple ) ;
memcpy ( relation - > rd_rel , relp , CLASS_TUPLE_SIZE ) ;
heap_freetuple ( pg_class_tuple ) ;
/*
* Again mark as valid , to protect against concurrently arriving
* invalidations .
*/
relation - > rd_isvalid = true ;
}
}
}
/*
* RelationDestroyRelation
*
@ -2250,27 +2335,12 @@ RelationClearRelation(Relation relation, bool rebuild)
RelationCloseSmgr ( relation ) ;
/*
* Never , never ever blow away a nailed - in system relation , because we ' d
* be unable to recover . However , we must redo RelationInitPhysicalAddr
* in case it is a mapped relation whose mapping changed .
*
* If it ' s a nailed - but - not - mapped index , then we need to re - read the
* pg_class row to see if its relfilenode changed . We do that immediately
* if we ' re inside a valid transaction and the relation is open ( not
* counting the nailed refcount ) . Otherwise just mark the entry as
* possibly invalid , and it ' ll be fixed when next opened .
* Treat nailed - in system relations separately , they always need to be
* accessible , so we can ' t blow them away .
*/
if ( relation - > rd_isnailed )
{
RelationInitPhysicalAddr ( relation ) ;
if ( relation - > rd_rel - > relkind = = RELKIND_INDEX | |
relation - > rd_rel - > relkind = = RELKIND_PARTITIONED_INDEX )
{
relation - > rd_isvalid = false ; /* needs to be revalidated */
if ( relation - > rd_refcnt > 1 & & IsTransactionState ( ) )
RelationReloadIndexInfo ( relation ) ;
}
RelationReloadNailed ( relation ) ;
return ;
}
@ -5907,24 +5977,26 @@ write_item(const void *data, Size len, FILE *fp)
/*
* Determine whether a given relation ( identified by OID ) is one of the ones
* we should store in the local relcache init file .
* we should store in a relcache init file .
*
* We must cache all nailed rels , and for efficiency we should cache every rel
* that supports a syscache . The former set is almost but not quite a subset
* of the latter . Currently , we must special - case TriggerRelidNameIndexId ,
* which RelationCacheInitializePhase3 chooses to nail for efficiency reasons ,
* but which does not support any syscache .
*
* Note : this function is currently never called for shared rels . If it were ,
* we ' d probably also need a special case for DatabaseNameIndexId , which is
* critical but does not support a syscache .
* of the latter . The special cases are relations where
* RelationCacheInitializePhase2 / 3 chooses to nail for efficiency reasons , but
* which do not support any syscache .
*/
bool
RelationIdIsInInitFile ( Oid relationId )
{
if ( relationId = = TriggerRelidNameIndexId )
if ( relationId = = SharedSecLabelRelationId | |
relationId = = TriggerRelidNameIndexId | |
relationId = = DatabaseNameIndexId | |
relationId = = SharedSecLabelObjectIndexId )
{
/* If this Assert fails, we don't need this special case anymore. */
/*
* If this Assert fails , we don ' t need the applicable special case
* anymore .
*/
Assert ( ! RelationSupportsSysCache ( relationId ) ) ;
return true ;
}
@ -5994,38 +6066,30 @@ RelationHasUnloggedIndex(Relation rel)
* We take the lock and do the unlink in RelationCacheInitFilePreInvalidate ,
* then release the lock in RelationCacheInitFilePostInvalidate . Caller must
* send any pending SI messages between those calls .
*
* Notice this deals only with the local init file , not the shared init file .
* The reason is that there can never be a " significant " change to the
* relcache entry of a shared relation ; the most that could happen is
* updates of noncritical fields such as relpages / reltuples . So , while
* it ' s worth updating the shared init file from time to time , it can never
* be invalid enough to make it necessary to remove it .
*/
void
RelationCacheInitFilePreInvalidate ( void )
{
char initfilename [ MAXPGPATH ] ;
char localinitfname [ MAXPGPATH ] ;
char sharedinitfname [ MAXPGPATH ] ;
snprintf ( initfilename , sizeof ( initfilename ) , " %s/%s " ,
DatabasePath , RELCACHE_INIT_FILENAME ) ;
if ( DatabasePath )
snprintf ( localinitfname , sizeof ( localinitfname ) , " %s/%s " ,
DatabasePath , RELCACHE_INIT_FILENAME ) ;
snprintf ( sharedinitfname , sizeof ( sharedinitfname ) , " global/%s " ,
RELCACHE_INIT_FILENAME ) ;
LWLockAcquire ( RelCacheInitLock , LW_EXCLUSIVE ) ;
if ( unlink ( initfilename ) < 0 )
{
/*
* The file might not be there if no backend has been started since
* the last removal . But complain about failures other than ENOENT .
* Fortunately , it ' s not too late to abort the transaction if we can ' t
* get rid of the would - be - obsolete init file .
*/
if ( errno ! = ENOENT )
ereport ( ERROR ,
( errcode_for_file_access ( ) ,
errmsg ( " could not remove cache file \" %s \" : %m " ,
initfilename ) ) ) ;
}
/*
* The files might not be there if no backend has been started since the
* last removal . But complain about failures other than ENOENT with
* ERROR . Fortunately , it ' s not too late to abort the transaction if we
* can ' t get rid of the would - be - obsolete init file .
*/
if ( DatabasePath )
unlink_initfile ( localinitfname , ERROR ) ;
unlink_initfile ( sharedinitfname , ERROR ) ;
}
void
@ -6051,13 +6115,9 @@ RelationCacheInitFileRemove(void)
struct dirent * de ;
char path [ MAXPGPATH + 10 + sizeof ( TABLESPACE_VERSION_DIRECTORY ) ] ;
/*
* We zap the shared cache file too . In theory it can ' t get out of sync
* enough to be a problem , but in data - corruption cases , who knows . . .
*/
snprintf ( path , sizeof ( path ) , " global/%s " ,
RELCACHE_INIT_FILENAME ) ;
unlink_initfile ( path ) ;
unlink_initfile ( path , LOG ) ;
/* Scan everything in the default tablespace */
RelationCacheInitFileRemoveInDir ( " base " ) ;
@ -6097,7 +6157,7 @@ RelationCacheInitFileRemoveInDir(const char *tblspcpath)
/* Try to remove the init file in each database */
snprintf ( initfilename , sizeof ( initfilename ) , " %s/%s/%s " ,
tblspcpath , de - > d_name , RELCACHE_INIT_FILENAME ) ;
unlink_initfile ( initfilename ) ;
unlink_initfile ( initfilename , LOG ) ;
}
}
@ -6105,12 +6165,15 @@ RelationCacheInitFileRemoveInDir(const char *tblspcpath)
}
static void
unlink_initfile ( const char * initfilename )
unlink_initfile ( const char * initfilename , int elevel )
{
if ( unlink ( initfilename ) < 0 )
{
/* It might not be there, but log any error other than ENOENT */
if ( errno ! = ENOENT )
elog ( LOG , " could not remove cache file \" %s \" : %m " , initfilename ) ;
ereport ( elevel ,
( errcode_for_file_access ( ) ,
errmsg ( " could not remove cache file \" %s \" : %m " ,
initfilename ) ) ) ;
}
}